Skip to main content
Nickos
Associate
March 3, 2023
Solved

How to configure I2C on STM32L4 Discovery kit IoT node using register

Hello everyone, I have a B-L475E-IOT01A board and I'm trying to configure I2C to communicate with a digital barometer (LPS22HB). But I have a problem, I can't get the SCL signal to be generated. I appreciate your help in solving my problem

#include <stdio.h>
#include <stm32l4xx.h>
 
void system_clock_msi_init(void)
{
 RCC->CR |= RCC_CR_MSION; // MSI clock enable
 RCC->CFGR |= RCC_CFGR_SW_MSI; // MSI oscillator selection as system clock
 while((RCC->CR & RCC_CR_MSIRDY) == 0);
 RCC->CR &= RCC_CR_MSIRANGE_Pos; 
 RCC->CR |= RCC_CR_MSIRANGE_7; // MSI clock ranges 7 around 8 MHz
 RCC->CR |= RCC_CR_MSIRGSEL; // Internal Multi Speed oscillator (MSI) range selection
 while((RCC->CR & RCC_CR_MSIRDY) == 0);
}
 
void clock_enable_init(void)
{
 RCC->APB1ENR1 |= RCC_APB1ENR1_I2C2EN; // enable I2C Clock
 RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN; // enable GPIOB Clock
 RCC->CCIPR |= RCC_CCIPR_I2C2SEL_0 ; // System clock (SYSCLK) selected as I2C2 clock
}
 
void GPIO_I2C_Init(void)
{
 GPIOB->MODER = 0xFF0FFEBF; // Reset MODER B
 GPIOB->MODER |= GPIO_MODER_MODE10_1 | GPIO_MODER_MODE11_1; // Alternate function mode
 GPIOB->OTYPER |= GPIO_OTYPER_OT10 | GPIO_OTYPER_OT11; // Output open-drain
 GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED10_Msk | GPIO_OSPEEDR_OSPEED11_Msk; // Very high speed
 GPIOB->PUPDR |= GPIO_PUPDR_PUPD10_0 | GPIO_PUPDR_PUPD11_0; // pull-up
 GPIOB->AFR[0] |= GPIO_AFRL_AFSEL4_2;
}
 
void I2C_Config (void)
{ 
 I2C2->CR1 |= I2C_CR1_PE; // Peripheral enable
 while((I2C2->CR1 & I2C_CR1_PE) == 0);
 I2C2->CR1 &= I2C_CR1_PE_Pos; // Peripheral not enable, Reset the I2C
 while((I2C2->CR1 & I2C_CR1_PE) == 1);
 
 I2C2->CR1 &= I2C_CR1_ANFOFF_Pos; // Analog noise filter ON 
 I2C2->CR1 &= I2C_CR1_DNF_Pos; // Digital noise filter OFF
 
 I2C2->TIMINGR |= 0x0 << I2C_TIMINGR_PRESC_Pos;
 I2C2->TIMINGR |= 0x9 << I2C_TIMINGR_SCLL_Pos;
 I2C2->TIMINGR |= 0x3 << I2C_TIMINGR_SCLH_Pos;
 I2C2->TIMINGR |= 0x1 << I2C_TIMINGR_SDADEL_Pos;
 I2C2->TIMINGR |= 0x3 << I2C_TIMINGR_SCLDEL_Pos;
 
 I2C2->CR1 |= I2C_CR1_NOSTRETCH; // Clock stretching disable 
 
 I2C2->CR1 |= I2C_CR1_PE; // Peripheral enable
 while((I2C2->CR1 & I2C_CR1_PE) == 0);
}
 
void I2C_Master_Init(void)
{
 I2C2->CR2 &= ~I2C_CR2_ADD10; // 0: The master operates in 7-bit addressing mode
 I2C2->CR2 |= 0xBA << I2C_CR2_SADD_Pos; // slave address 1011101b 
 I2C2->CR2 &= ~I2C_CR2_RD_WRN; // 0: Master requests a write transfer
 I2C2->CR2 |= 0x1 << I2C_CR2_NBYTES_Pos; // The number of bytes is 1
}
 
int main(void) 
{
 system_clock_msi_init();
 clock_enable_init();
 GPIO_I2C_Init();
 I2C_Config();
 I2C_Master_Init();
 
 while(1)
 { 
 I2C2->CR2 |= I2C_CR2_START; // Generate START
 while (!(I2C2->ISR & I2C_ISR_BUSY));
 I2C2->TXDR = 0xBA;
 while(!(I2C2->ISR & I2C_ISR_TXIS));
 }
 return 0;
}

    This topic has been closed for replies.
    Foued_KH
    Foued_KHBest answer
    ST Employee
    March 6, 2023

    Hello @Nickos​ ,

    First of all ,for the Start you need to enable the ACK :

    1. Send the START condition 

    2. Wait for the SB ( Bit 0 in SR1) to set. This indicates that the start condition is generated

    I2C1->CR1 |= (1<<10); // Enable the ACK
    I2C1->CR1 |= (1<<8); // Generate START
    while (!(I2C1->SR1 & (1<<0))); // Wait fror SB bit to set

    Then you should send the address :

    I2C1->DR = Address; // send the address
    while (!(I2C1->SR1 & (1<<1))); // wait for ADDR bit to set
    uint8_t temp = I2C1->SR1 | I2C1->SR2; // read SR1 and SR2 to clear the ADDR bit

    And finally for the Write :

    1. Wait for the TXE (bit 7 in SR1) to set. This indicates that the DR is empty

    2. Send the DATA to the DR Register

    3. Wait for the BTF (bit 2 in SR1) to set. This indicates the end of LAST DATA transmission

    while (!(I2C1->SR1 & (1<<7))); // wait for TXE bit to set
    I2C1->DR = data;
    while (!(I2C1->SR1 & (1<<2))); // wait for BTF bit to set

    Foued.

    To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

    2 replies

    Foued_KH
    Foued_KHBest answer
    ST Employee
    March 6, 2023

    Hello @Nickos​ ,

    First of all ,for the Start you need to enable the ACK :

    1. Send the START condition 

    2. Wait for the SB ( Bit 0 in SR1) to set. This indicates that the start condition is generated

    I2C1->CR1 |= (1<<10); // Enable the ACK
    I2C1->CR1 |= (1<<8); // Generate START
    while (!(I2C1->SR1 & (1<<0))); // Wait fror SB bit to set

    Then you should send the address :

    I2C1->DR = Address; // send the address
    while (!(I2C1->SR1 & (1<<1))); // wait for ADDR bit to set
    uint8_t temp = I2C1->SR1 | I2C1->SR2; // read SR1 and SR2 to clear the ADDR bit

    And finally for the Write :

    1. Wait for the TXE (bit 7 in SR1) to set. This indicates that the DR is empty

    2. Send the DATA to the DR Register

    3. Wait for the BTF (bit 2 in SR1) to set. This indicates the end of LAST DATA transmission

    while (!(I2C1->SR1 & (1<<7))); // wait for TXE bit to set
    I2C1->DR = data;
    while (!(I2C1->SR1 & (1<<2))); // wait for BTF bit to set

    Foued.

    To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
    Nickos
    NickosAuthor
    Associate
    March 29, 2023

    Thank you for your response. @KHALSI_Foued it helped me.

    Here's what I got

    #include <stdio.h>
    #include <stm32l4xx.h>
     
    #define WHO_AM_I 0x0F
    #define Addres 0xBA
    uint8_t data_read;
     
     
     
    void system_clock_msi_init(void)
    {
     RCC->CR |= RCC_CR_MSION; // MSI clock enable
     RCC->CFGR |= RCC_CFGR_SW_MSI; 
     while((RCC->CR & RCC_CR_MSIRDY) == 0);
     RCC->CR &= RCC_CR_MSIRANGE_Pos; 
     //RCC->CR |= RCC_CR_MSIRANGE_7; 
     RCC->CR |= RCC_CR_MSIRANGE_8;
     //RCC->CR |= RCC_CR_MSIRANGE_11;
     RCC->CR |= RCC_CR_MSIRGSEL;
     while((RCC->CR & RCC_CR_MSIRDY) == 0);
    }
     
    void clock_enable_init(void)
    {
     RCC->CCIPR |= RCC_CCIPR_I2C2SEL_0 ;
     RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
     RCC->APB1ENR1 |= RCC_APB1ENR1_I2C2EN;
    }
     
     
    void GPIO_I2C_Init(void)
    {
     GPIOB->MODER &= ~GPIO_MODER_MODE10_Msk 
     & ~GPIO_MODER_MODE11_Msk; 
     GPIOB->MODER |= GPIO_MODER_MODE10_1 
     | GPIO_MODER_MODE11_1; 
     GPIOB->OTYPER |= GPIO_OTYPER_OT10 
     | GPIO_OTYPER_OT11;
     GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED10_Msk 
     | GPIO_OSPEEDR_OSPEED11_Msk; 
     GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD10_Msk 
     & ~GPIO_PUPDR_PUPD11_Msk; 
     GPIOB->AFR[1] |= GPIO_AFRH_AFSEL10_2 
     | GPIO_AFRH_AFSEL11_2; 
    }
     
     
    void I2C_Intit_Seting(void)
    {
     /* Software reset *//*
     I2C2->CR1 |= I2C_CR1_PE; // Peripheral enable
     while((I2C2->CR1 & I2C_CR1_PE) == 0);
     I2C2->CR1 &= I2C_CR1_PE_Pos; // Peripheral not enable, Reset the I2C
     while((I2C2->CR1 & I2C_CR1_PE) == 1);
     */
     
     I2C2->CR1 |= I2C_CR1_DNF; // Digital noise filter ON
     I2C2->CR1 |= I2C_CR1_ANFOFF; // Analog noise filter ON 
     
     /* Timings settings for fI2CCLK = 16 MHz mode 10 kHz*/
     I2C2->TIMINGR |= 0x3 << I2C_TIMINGR_PRESC_Pos;
     I2C2->TIMINGR |= 0xC7 << I2C_TIMINGR_SCLL_Pos; 
     I2C2->TIMINGR |= 0xC3 << I2C_TIMINGR_SCLH_Pos;
     I2C2->TIMINGR |= 0x2 << I2C_TIMINGR_SDADEL_Pos;
     I2C2->TIMINGR |= 0x4 << I2C_TIMINGR_SCLDEL_Pos;
     
     /* Timings settings for fI2CCLK 16 MHz mode 100 kHz*//*
     I2C2->TIMINGR |= 0x13 << I2C_TIMINGR_SCLL_Pos; 
     I2C2->TIMINGR |= 0xF << I2C_TIMINGR_SCLH_Pos;
     I2C2->TIMINGR |= 0x2 << I2C_TIMINGR_SDADEL_Pos;
     I2C2->TIMINGR |= 0x4 << I2C_TIMINGR_SCLDEL_Pos;
     I2C2->TIMINGR |= 0xB << I2C_TIMINGR_PRESC_Pos;
     */
     
     I2C2->CR1 &= ~I2C_CR1_NOSTRETCH; 
     I2C2->CR1 |= I2C_CR1_PE; // Peripheral enable
     while((I2C2->CR1 & I2C_CR1_PE) == 0);
    }
     
    void Master_init(void)
    {
     I2C_Intit_Seting();
     /*Enable interrupts*/
     I2C2->CR1 |= I2C_CR1_ERRIE; 
     I2C2->CR1 |= I2C_CR1_TCIE;
     I2C2->CR1 |= I2C_CR1_STOPIE; 
     I2C2->CR1 |= I2C_CR1_NACKIE; 
     I2C2->CR1 |= I2C_CR1_TXIE; 
     I2C2->CR1 |= I2C_CR1_RXIE; 
    }
     
     
    void I2C_Master_transmit(uint8_t SADD, uint8_t Size)
    {
     I2C2->CR2 &= ~I2C_CR2_ADD10; 
     I2C2->CR2 |= Addres << I2C_CR2_SADD_Pos;
     I2C2->CR2 &= ~I2C_CR2_RD_WRN;
     I2C2->CR2 |= Size << I2C_CR2_NBYTES_Pos; 
     I2C2->CR2 &= ~I2C_CR2_AUTOEND_Msk;
     I2C2->CR2 |= I2C_CR2_START;
     
     while(!(I2C2->ISR & (I2C_ISR_TXIS & ~I2C_ISR_NACKF)));
     while(Size > 0)
     {
     I2C2->TXDR = WHO_AM_I;
     Size--;
     } 
     while(!(I2C2->ISR & I2C_ISR_TC));
    }
     
    void I2C_Master_receiver(uint8_t SADD, uint8_t Size)
    {
     I2C2->CR2 |= Addres << I2C_CR2_SADD_Pos;
     I2C2->CR2 |= 0x1 << I2C_CR2_NBYTES_Pos; 
     I2C2->CR2 &= ~I2C_CR2_AUTOEND_Msk;
     I2C2->CR2 |= I2C_CR2_RD_WRN;
     I2C2->CR2 |= I2C_CR2_START;
     while(!(I2C2->ISR & I2C_ISR_RXNE));
     while(Size > 0)
     {
     data_read = I2C2->RXDR;
     Size--;
     }
     while(!(I2C2->ISR & I2C_ISR_TC));
     I2C2->CR2 |= I2C_CR2_STOP;
     while(!(I2C2->ISR & I2C_ISR_STOPF));
     I2C2->ICR |= I2C_ICR_STOPCF;
    }
     
     
    int main(void)
    {
     system_clock_msi_init();
     clock_enable_init();
     GPIO_I2C_Init();
     Master_init();
     
     while(1)
     {
     I2C_Master_transmit(Addres,1);
     I2C_Master_receiver(Addres,1);
     }
     return 0;
    }