Skip to main content
Associate
December 3, 2025
Solved

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

  • December 3, 2025
  • 3 replies
  • 318 views

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.

Best answer by TDK

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.

3 replies

Andrew Neil
Super User
December 3, 2025

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
December 3, 2025

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
December 3, 2025

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
December 3, 2025

No i didn't use analyser to verify.

TDK
TDKBest answer
Super User
December 3, 2025

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
December 5, 2025

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.