cancel
Showing results for 
Search instead for 
Did you mean: 

Facing difficulties in interfacing STM32L433 MCU to VCNL4040 (I2C-based sensor).

LalinPrasadC
Associate II

Hi, I’m Lalin Prasad. I’m currently learning bare-metal programming using he STM32L433 MCU (STM32L433RCT6PU).
While working with I²C communication, I interfaced a sensor successfully, but I’m not getting the correct output values.

This is the UART terminal output — the proximity (PROX) value is always 0:

UART OK
I2C OK
390
CONF1 written
CONF2 written
VCNL Ready
PROX = 0
PROX = 0
PROX = 0

Please help me understand why the proximity reading is always 0.

/*The below mentioned code is main.c*/

#include "timer.h"
#include "i2c.h"
#include "uart.h"

uint16_t value = 0;

int main(void)
{
    set_clock();
    uart_init();
    uart_write("UART OK\r\n");

    i2c_init();
    uart_write("I2C OK\r\n");

    uint16_t id=i2c_read(0x0C);
    uart_write_int(id);
    uart_write("\r\n");

    // Bit 0 = 1 → PS enable
    i2c_write(0x03, 0x0001);
    uart_write("CONF1 written\r\n");

    // 2) PS_CONF2 (0x04) — LED current = 10 mA
    i2c_write(0x04, 0x0008);
    uart_write("CONF2 written\r\n");

    // delay for sensor startup
    for(volatile int d=0; d<200000; d++);

    uart_write("VCNL Ready\r\n");

    while(1)
    {
        value = i2c_read(0x08);   // Proximity result LSB/MSB
        uart_write("PROX = ");
       uart_write_int(value);
        uart_write("\r\n");

        for(volatile int d=0; d<200000; d++);
    }
}

/*The below mentioned code is i2c.c*/

/*
 * i2c.c
 *
 *  Created on: Nov 20, 2025
 *      Author: lalin
 */


#include "i2c.h"

#define GPIOAEN	(1U<<0)
#define I2CEN	(1U<<21)

void i2c_init()
{
	RCC->AHB2ENR|=GPIOAEN;

	GPIOA->MODER&=~(15U<<18);
	GPIOA->MODER|=((1U<<19)|(1U<<21));

	GPIOA->AFR[1]&=~(15U<<4);
	GPIOA->AFR[1]&=~(15U<<8);
	GPIOA->AFR[1]|=(4U<<4);
	GPIOA->AFR[1]|=(4U<<8);

	GPIOA->OTYPER|=(1U<<9);
	GPIOA->OTYPER|=(1U<<10);


	GPIOA->OSPEEDR |= (3U<<18) | (3U<<20);  // Very high speed

	GPIOA->PUPDR &= ~(3U<<18);
	GPIOA->PUPDR &= ~(3U<<20);
	GPIOA->PUPDR|=(1U<<18);
	GPIOA->PUPDR|=(1U<<20);

	I2C1->CR1&=~(1U<<0); //For reseting

/***************************/
	RCC->APB1ENR1|=I2CEN;

	I2C1->CR1 &=~(1U<<12);//Analog filter enabled
	I2C1->CR1 &=~(15U<<8);//Keeping DNF to 0

	I2C1->TIMINGR=0;
	I2C1->TIMINGR|=(0x13<<0);
	I2C1->TIMINGR|=(0xF<<8);
	I2C1->TIMINGR|=(0x2<<16);
	I2C1->TIMINGR|=(0x4<<20);
	I2C1->TIMINGR|=(0x3<<28);

	I2C1->CR1 &=~(1U<<17);//Enable clock stretching

	I2C1->CR1|=(1U<<0);
}


void i2c_write(uint8_t reg, uint16_t value)
{
	I2C1->ICR = 0x3F;

	    // WRITE: 3 bytes -> reg, LSB, MSB
	    I2C1->CR2 = (0x60 << 1) | (0 << 10) | (3 << 16) | (1 << 25) | (1 << 13);

	    while(!(I2C1->ISR & (1 << 1)));
	    I2C1->TXDR = reg;

	    while(!(I2C1->ISR & (1 << 1)));
	    I2C1->TXDR = (value & 0xFF); // LSB

	    while(!(I2C1->ISR & (1 << 1)));
	    I2C1->TXDR = (value >> 8);   // MSB

	    while(!(I2C1->ISR & (1 << 5))); // STOP
	    I2C1->ICR = (1 << 5);
}




uint16_t i2c_read(uint8_t reg)
{
	uint8_t low, high;

	    I2C1->ICR = 0x3F;

	    // STEP 1: Send register address (NO AUTOEND)
	    I2C1->CR2 = (0x60<<1) | (0<<10) | (1<<16) | (0<<25) | (1<<13);

	    while(!(I2C1->ISR & (1<<1)));
	    I2C1->TXDR = reg;

	    while(!(I2C1->ISR & (1<<6))); // TC

	    // STEP 2: Repeated-start read 2 bytes
	    I2C1->CR2 = (0x60<<1) | (1<<10) | (2<<16) | (1<<25) | (1<<13);

	    while(!(I2C1->ISR & (1<<2)));
	    low = I2C1->RXDR;

	    while(!(I2C1->ISR & (1<<2)));
	    high = I2C1->RXDR;

	    return (high << 8) | low;
}


/*The below mentioned code is uart.c*/
/*
 * uart.c
 *
 *  Created on: Oct 8, 2025
 *      Author: lalin
 */

#include "uart.h"

#define SYS_CLK		16000000UL
#define UART2_EN	(1U<<17)
#define GPIOAEN		(1U<<0)

static void baud_rate(USART_TypeDef *USART,uint32_t baud_rate,uint32_t clock);
static int compute_baud(uint32_t clock, uint32_t baud_rate);

void uart_init()
{
	RCC->AHB2ENR|=GPIOAEN;

	GPIOA->MODER |=(1U<<5);
	GPIOA->MODER &=~(1U<<4);

	GPIOA->AFR[0] &=~(1U<<11);
	GPIOA->AFR[0] |=(1U<<10);
	GPIOA->AFR[0] |=(1U<<9);
	GPIOA->AFR[0] |=(1U<<8);

	RCC->APB1ENR1|=UART2_EN;

	baud_rate(USART2,115200,SYS_CLK);

	USART2->CR1=(1U<<3);
	USART2->CR1|=(1U<<0);

}

void uart_write(const char *val)
{
	while(*val)
	{
		while(!(USART2->ISR & (1U<<7))){}
		USART2->TDR=(*val++);
	}
}


void uart_write_int(uint32_t num)
{
    char buf[12];
    int i = 0;

    if(num == 0)
    {
        uart_write("0");
        return;
    }

    // Convert number to string in reverse order
    while(num > 0)
    {
        buf[i++] = (num % 10) + '0';
        num /= 10;
    }

    buf[i] = '\0';

    // Reverse the buffer in-place
    for(int j = 0; j < i/2; j++)
    {
        char temp = buf[j];
        buf[j] = buf[i-1-j];
        buf[i-1-j] = temp;
    }

    // Send the number using uart_write()
    uart_write(buf);
}


void uart_write_hex(uint16_t v)
{
    char buf[6];
    char *hex = "0123456789ABCDEF";

    buf[0] = hex[(v >> 12) & 0xF];
    buf[1] = hex[(v >> 8) & 0xF];
    buf[2] = hex[(v >> 4) & 0xF];
    buf[3] = hex[(v >> 0) & 0xF];
    buf[4] = 0;

    uart_write(buf);
}



static void baud_rate(USART_TypeDef *USART,uint32_t baud_rate,uint32_t clock)
{
	USART->BRR=compute_baud(clock,baud_rate);
}


static int compute_baud(uint32_t clock, uint32_t baud_rate)
{
	return ((clock+(baud_rate/2))/baud_rate);
}


/*The below mentioned code is timer.c*/

/*
 * timer.c
 *
 *  Created on: Oct 12, 2025
 *      Author: lalin
 */


#include "timer.h"
#include <math.h>

#define MSI_EN		(1U<<0)
#define SYS_RDY		(3U<<2)

void set_clock()
{

	RCC->CR|=(1U<<8);
	while(!(RCC->CR & (1U<<10))){}

	RCC->CFGR&=~(3U<<0);
	RCC->CFGR|=(1U<<0);

	while(!(RCC->CFGR & (1U<<2))){}
}


void delay(uint8_t value)
{
	RCC->APB1ENR1|=(1U<<4);

	TIM6->PSC=15999;
	TIM6->ARR=((value*1000)-1);

	TIM6->EGR|=(1U<<0);
	TIM6->CR1|=(1U<<0);

	TIM6->CNT=0;

	TIM6->SR &= ~(1U << 0);

		while(!(TIM6->SR & (1U<<0))){}
		TIM6->SR &=~(1U<<0);
}


void pwm()
{
	RCC->AHB2ENR|=(1U<<1);
	RCC->APB1ENR1|=(1U<<0);

	GPIOB->MODER|=(1U<<21);
	GPIOB->MODER&=~(1U<<20);

	GPIOB->AFR[1]&=~(15U<<8);
	GPIOB->AFR[1]|=(1U<<8);

	TIM2->PSC=15;
	TIM2->ARR=999;

	TIM2->CCR3=0;//INITIAL DUTY CYCLE

	TIM2->CCMR2&=~(7U<<4);
	TIM2->CCMR2|=(6U<<4);

	TIM2->CCMR2|=(1U<<3);

	TIM2->CR1|=(1U<<7);


	TIM2->CCER&=~(1U<<9);
	TIM2->CCER|=(1U<<8);

	TIM2->EGR|=(1U<<0);

	TIM2->CR1 |= (1U << 0);   // Enable TIM2 counter


	while(1)
	{
	for(int i=0;i<(999+1);i+=50)
	{
		TIM2->CCR3=i;
		delay(1);
	}

	for(int j=999;j>0;j-=50)
	{
		TIM2->CCR3=j;
		delay(1);
	}

}
}


I have also atached the datasheets along with this text.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Super User

There's no reason to doubt the information that is coming back. Doesn't seem like a STM32 problem, probably a sensor configuration problem. I recommend reading back the registers you are writing to verify they have been written correctly.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

7 REPLIES 7
Andrew Neil
Super User

Have you used an analyser on the I2C bus to verify what's actually happening on the wires ?

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

Thank you for the reply Andrew.

I used debugger and uart print to check the flow of program.

It's not getting stuck anywhere.

As i said the read function is able to read the Device ID but not the output data of the sensor.

But have you used an analyser on the I2C bus to verify what's actually happening on the wires ?

Does what's happening on the wires correspond to the VCNL4040 datasheet ?

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.
LalinPrasadC
Associate II

No i didn't use analyser to verify.

TDK
Super User

There's no reason to doubt the information that is coming back. Doesn't seem like a STM32 problem, probably a sensor configuration problem. I recommend reading back the registers you are writing to verify they have been written correctly.

If you feel a post has answered your question, please click "Accept as Solution".

You mean that the code which i wrote is correct but i need to check the registers?
I have attached the VCNL4040 data sheet along with my message, kindly check that if possible.
In that datasheet it has been mentioned that 0x09 and 0x08 is for PS_output and i have tried both but still 0.

Thank you so much Andrew and TDK for your responses.
As we discussed it is the register issue.
I was writing wrong values to the registers.