Skip to main content
Associate
Dec 3, 2025 2:52 AM UTC
Solved

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

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.

TDK
TDKBest answer
Dec 3, 2025 6:31 PM UTC

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""."

7 replies

Andrew Neil
Super User
Dec 3, 2025 8:38 AM UTC

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.
Associate
Dec 3, 2025 2:38 PM UTC

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.

Andrew Neil
Super User
Dec 3, 2025 2:41 PM UTC

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.
Associate
Dec 3, 2025 6:24 PM UTC

No i didn't use analyser to verify.

TDK
TDKBest answer
Dec 3, 2025 6:31 PM UTC

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""."
Associate
Dec 5, 2025 10:02 AM UTC

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.