2025-12-02 6:52 PM - last edited on 2025-12-03 12:35 AM by Andrew Neil
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.
2025-12-03 12:38 AM
Have you used an analyser on the I2C bus to verify what's actually happening on the wires ?