cancel
Showing results for 
Search instead for 
Did you mean: 

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

LalinPrasadC
Visitor

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.

(Virus scan in progress ...)
(Virus scan in progress ...)
(Virus scan in progress ...)
1 REPLY 1
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.