2016-06-23 11:11 AM
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-eabi5.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
/**
******************************************************************************
* @file main.c
* @author Ac6
* @version V1.0
* @date 06/17/2016
* @brief I2C set up to SMT32F411 goal to connect to Accelerometer
*
*
******************************************************************************
*/
#include ''stm32f4xx.h''
//#include ''stm32f411e_discovery.h''
#define ACCEL_ADDR 0x32
#define xWrite 0x1
#define xRead 0x0
void
I2C1_PortInit(){
// Set port to use I2C1
RCC->CR |= 0x00000001;
// enable HSION (High speed internal oscillator)
while
((RCC->CR & RCC_CR_HSIRDY) == 0);
//Wait for HSI Ready RCC->CFGR = RCC_CFGR_SW_HSI;
while
((RCC->CFGR & RCC_CFGR_SWS) != 0) ;
//wait for HSI used as system clock
RCC->CFGR |= 0x00009000;
I2C1->CR1 &= ~I2C_CR1_PE;
RCC->APB1ENR |= 0x00200000;
// enable APB1 peripheral clock for I2C1 per section 6.3.11
RCC->AHB1ENR |= 0x00000002;
// enable clock for GPIO PortB (PB6/SCL and PB9/SDA pins)
GPIOB->OSPEEDR |= 0x00082000;
// set port speed to fast for ports PB6 and PB9 (50Mhz)
GPIOB->OTYPER |= 0x00000240;
// I2C needs to be open drain (drives GND/0V only) for PB6 and PB9
GPIOB->PUPDR |= 0x00041000;
// enable pull up resistors to PB6 and PB9
GPIOB-> AFR[1] |= 0x00000040;
// Assign I2C1_SDA to Alternate Function per table-9 of 32F411xE Datasheet
GPIOB-> AFR[0] |= 0x04000000;
// Assign I2C1_SCL to Alternate Function per table-9 of 32F411xE Datasheet
GPIOB->MODER |= 0x00042000;
// Set Alternate Function to PB6 and PB9, which function defined below by GPIOB_AFR
//Configure I2C Port 1 settings
I2C1->CR1 &= 0xFFFE;
// Must Disable the I2C peripheral to be able to change CCR and TRISE
I2C1->CR2 = 0x0008;
// Program the peripheral input clock per APB1 Peripheral Clock of 8Mhz => b1000. Disable Interrupts, no DMA
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
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.
Therefore the TRISE[5:0] bits must be programmed with 09h.(1000 ns / 125 ns = 8 + 1) */
I2C1->OAR1 |= 0x4000;
// Set I2C1 address not needed for master
I2C1->OAR2 |= 0x0000;
I2C1->CR1 |= 0x0001;
// Enable Peripheral
// Set START bit to generate START condition
}
void
I2C1_Start(uint8_t I2C_Write_Slave_Address, uint8_t Slave_Internal_Address ){
volatile int32_t Busy_Flag=1;
volatile int32_t AK_Status=0;
// following description in pg 469 of reference manual
// while (Busy_Flag == 1){ // check if I2C Busy Flag is set (I2Cx_SR2.Bit2=1:Busy, SR2.Bit2=0:OK to transmit)
// Busy_Flag = I2C1->SR2 & 0x2; // Update Busy flag bit
// }
I2C1->CR1 |= 0x0100;
// generate START condition I2Cx_CR1.Bit8=1:Start CR1.Bit8=0: No Start
I2C1->SR1;
//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)
// AK_Status = I2C1->SR1 & 0x1; // Update SB Start status
// }
//I2C1->CR1 |= 0x0400; //enable ack
I2C1->DR = I2C_Write_Slave_Address;
// send Slave Address with WRITE (R/W=0). I2Cx_DR register with Address
//AK_Status = 0;
// while (AK_Status==0){ // check for (EV6) ACK (ADDR=1, cleared by reading SR1 register followed by reading SR2)
// AK_Status = I2C1->SR1 & 0x2; // Keep checking for ADRR bit to be 1
// }
I2C1->SR1;
I2C1->SR2;
// Read SR2 after ADRR read at SR1 to clear the ADRR
I2C1->SR1 &=~(0x0020);
I2C1->DR = Slave_Internal_Address;
// Prepare slave to read specific internal address
AK_Status = 0;
while
(AK_Status == 0 ){
AK_Status = I2C1->SR1 & 0x80;
// Checking for TxE =1 to happen after ACK received
}
}
void
I2C1_Write(uint8_t I2C_Data_Out){
uint8_t AK_Status=0;
I2C1->DR = I2C_Data_Out;
// Prepare slave to read specific internal address
while
(AK_Status == 0 ){
AK_Status = (I2C1->SR1 & 0x80);
AK_Status = AK_Status || ((I2C1->SR1 & I2C_SR1_BTF));
// Checking for TxE =1 to happen after ACK received)
}
}
void
I2C1_Stop(){
I2C1->CR1 |= 0x0200;
}
uint8_t I2C1_Read_Last(){
I2C1->CR1 &= ~(0x0400);
// same as 0xF7FFF to clear ACK
I2C1->CR1 |= 0x0200;
// same as 0xF7FFF to clear ACK
uint8_t I2C_Data_In = I2C1->DR;
return
I2C_Data_In;
}
int
main(
void
)
{
volatile uint8_t Acc_Data_in;
// Define Buffer for data expected per frame of I2C communication
uint8_t Acc_Config_Out;
// Define Buffer for data expected per frame of I2C communication
uint8_t Acc_Internal_Reg;
// SystemInit(); // Sets correct clock cycles APB1 Peripherals to 8Mhz, APB1 clock to 16Mhz
I2C1_PortInit();
// Initialize the I2C Port
//Read Accelerometer
Acc_Internal_Reg = 0x20;
Acc_Config_Out = 0xF7;
//CTRL_REG1_A (20h) defaults to power down and all axis disable. Enable all axis and enable data rate
I2C1_Start(ACCEL_ADDR, Acc_Internal_Reg);
I2C1_Write(Acc_Config_Out);
I2C1_Stop();
// Acc_Data_in = I2C1_Read_Last(); // Stop the transmission
while
(1) {
I2C1_Start(ACCEL_ADDR, Acc_Internal_Reg);
}
}
#discovery #stm32f4 #i2c