AnsweredAssumed Answered

I2C start condition not working

Question asked by DiodeX on Jun 23, 2016
I am working on getting familiar with the STM32F411 using ST evaluation board STM32F411E-DISCOVERY.

I have written the following code using Eclipse + Ac6 STM32 + gcc (arm-none-eabi 5.3 2016q1)

I am able to program the board with another program I created to control PWM on the LEDs (PD12-PD15). I can see the PWM signal on the o-scope moving as programmed

When I switch to the I2C driver that I am writing, I cannot get a clock signal out on the I2C SCL nor a START signal on the I2C SDA. I have made lots of different changes but never get  good results. Not sure what else I should change so perhaps someone here can point me in the right direction. Is almost as if I am not enabling the clock correctly. PB6=SCL and PB9=SDA

001./**
002.  ******************************************************************************
003.  * @file    main.c
004.  * @author  Ac6
005.  * @version V1.0
006.  * @date    06/17/2016
007.  * @brief   I2C set up to SMT32F411 goal to connect to Accelerometer
008.  *
009.  *
010.  ******************************************************************************
011.*/
012. 
013.#include "stm32f4xx.h"
014.//#include "stm32f411e_discovery.h"
015. 
016.#define ACCEL_ADDR 0x32
017.#define xWrite  0x1
018.#define xRead   0x0
019. 
020.void I2C1_PortInit(){
021.    // Set port to use I2C1
022.    RCC->CR         |= 0x00000001;  // enable HSION (High speed internal oscillator)
023.    while ((RCC->CR & RCC_CR_HSIRDY) == 0);     //Wait for HSI Ready RCC->CFGR = RCC_CFGR_SW_HSI;
024.    while ((RCC->CFGR & RCC_CFGR_SWS) != 0) ;   //wait for HSI used as system clock
025.    RCC->CFGR       |= 0x00009000;
026. 
027.    I2C1->CR1       &= ~I2C_CR1_PE;
028.    RCC->APB1ENR    |= 0x00200000;  // enable APB1 peripheral clock for I2C1 per section 6.3.11
029.    RCC->AHB1ENR    |= 0x00000002;  // enable clock for GPIO PortB (PB6/SCL and PB9/SDA pins)
030. 
031.    GPIOB->OSPEEDR  |= 0x00082000;  // set port speed to fast for ports PB6 and PB9 (50Mhz)
032.    GPIOB->OTYPER   |= 0x00000240;  // I2C needs to be open drain (drives GND/0V only) for PB6 and PB9
033.    GPIOB->PUPDR    |= 0x00041000;  // enable pull up resistors to PB6 and PB9
034.    GPIOB-> AFR[1]  |= 0x00000040;  // Assign I2C1_SDA to Alternate Function per table-9 of 32F411xE Datasheet
035.    GPIOB-> AFR[0]  |= 0x04000000;  // Assign I2C1_SCL to Alternate Function per table-9 of 32F411xE Datasheet
036.    GPIOB->MODER    |= 0x00042000;  // Set Alternate Function to PB6 and PB9, which function defined below by GPIOB_AFR
037.    //Configure I2C Port 1 settings
038. 
039. 
040. 
041. 
042. 
043.    I2C1->CR1       &= 0xFFFE;      // Must Disable the I2C peripheral to be able to change CCR and TRISE
044.    I2C1->CR2       = 0x0008;       // Program the peripheral input clock per APB1 Peripheral Clock of 8Mhz  => b1000. Disable Interrupts, no DMA
045.    I2C1->CCR       = 0x0028;       // Clock Standard mode (SM=100khz=> 10,000ns) Thigh = 5000ns = Tlow (50% duty cycle). Peripheral clk=8Mhz (APB1) then 1/8mhz= 125nsec 5000ns/125ns = 40 =>0x28= CCR
046.    I2C1->TRISE     = 0x0009;       /* Configure the rise time register: If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to 0x08 and TPCLK1 = 125 ns.
047.                                       Therefore the TRISE[5:0] bits must be programmed with 09h.(1000 ns / 125 ns = 8 + 1) */
048.    I2C1->OAR1      |= 0x4000;      // Set I2C1 address not needed for master
049.    I2C1->OAR2      |= 0x0000;
050.    I2C1->CR1       |= 0x0001;      // Enable Peripheral
051.                                    // Set START bit to generate START condition
052.}
053. 
054. 
055. 
056.void I2C1_Start(uint8_t I2C_Write_Slave_Address, uint8_t Slave_Internal_Address ){
057.    volatile int32_t Busy_Flag=1;
058.    volatile int32_t AK_Status=0;
059. 
060.    // following description in pg 469 of reference manual
061.//  while (Busy_Flag == 1){                 // check if I2C Busy Flag is set (I2Cx_SR2.Bit2=1:Busy,  SR2.Bit2=0:OK to transmit)
062.//      Busy_Flag = I2C1->SR2 & 0x2;    // Update Busy flag bit
063.//  }
064. 
065.    I2C1->CR1 |= 0x0100;                // generate START condition I2Cx_CR1.Bit8=1:Start    CR1.Bit8=0: No Start
066.    I2C1->SR1;
067.    //while (AK_Status == 0){               // check for (EV5) START condition was good(SB=1, cleared by reading SR1 register followed by writing DR register with Address)
068.//      AK_Status = I2C1->SR1 & 0x1;    // Update SB Start status
069.//  }
070.    //I2C1->CR1 |= 0x0400;              //enable ack
071. 
072.    I2C1->DR = I2C_Write_Slave_Address; // send Slave Address with WRITE (R/W=0). I2Cx_DR register with Address
073. 
074.    //AK_Status = 0;
075.    //  while (AK_Status==0){                   // check for (EV6) ACK (ADDR=1, cleared by reading SR1 register followed by reading SR2)
076.    //  AK_Status = I2C1->SR1 & 0x2;        // Keep checking for ADRR bit to be  1
077.    //  }
078.    I2C1->SR1;
079.    I2C1->SR2;                  // Read SR2 after ADRR read at SR1 to clear the ADRR
080. 
081.    I2C1->SR1 &=~(0x0020);
082. 
083.    I2C1->DR = Slave_Internal_Address;      // Prepare slave to read specific internal address
084. 
085.    AK_Status = 0;
086.    while (AK_Status == 0 ){
087.        AK_Status = I2C1->SR1 & 0x80;       // Checking for TxE =1 to happen after ACK received
088.    }
089. 
090.}
091. 
092.void I2C1_Write(uint8_t I2C_Data_Out){
093.    uint8_t AK_Status=0;
094.    I2C1->DR = I2C_Data_Out;                    // Prepare slave to read specific internal address
095.    while (AK_Status == 0 ){
096.        AK_Status = (I2C1->SR1 & 0x80);
097.        AK_Status = AK_Status || ((I2C1->SR1 & I2C_SR1_BTF));       // Checking for TxE =1 to happen after ACK received)
098.    }
099.}
100. 
101.void I2C1_Stop(){
102.    I2C1->CR1 |= 0x0200;
103.}
104. 
105.uint8_t I2C1_Read_Last(){
106. 
107.    I2C1->CR1 &= ~(0x0400);  // same as 0xF7FFF to clear ACK
108.    I2C1->CR1 |= 0x0200;        // same as 0xF7FFF to clear ACK
109.    uint8_t I2C_Data_In = I2C1->DR;
110.    return I2C_Data_In;
111.}
112. 
113.int main(void)
114.{
115.    volatile uint8_t Acc_Data_in;   // Define Buffer for data expected per frame of I2C communication
116.    uint8_t Acc_Config_Out;         // Define Buffer for data expected per frame of I2C communication
117.    uint8_t Acc_Internal_Reg;
118. 
119.//  SystemInit();               // Sets correct clock cycles APB1 Peripherals to 8Mhz, APB1 clock to 16Mhz
120.    I2C1_PortInit();            // Initialize the I2C Port
121. 
122.    //Read Accelerometer
123.    Acc_Internal_Reg = 0x20;
124.    Acc_Config_Out = 0xF7;      //CTRL_REG1_A (20h) defaults to power down and all axis disable. Enable all axis and enable data rate
125. 
126.    I2C1_Start(ACCEL_ADDR, Acc_Internal_Reg);
127.    I2C1_Write(Acc_Config_Out);
128.    I2C1_Stop();
129.//  Acc_Data_in = I2C1_Read_Last();                         // Stop the transmission
130. 
131.    while(1) {
132. 
133.        I2C1_Start(ACCEL_ADDR, Acc_Internal_Reg);
134.    }
135.}

Outcomes