How to initialize I2C on STM32F0?

3

Recently I've been trying to get the I2C bus working on the STM32F030F4P6 MCU, but with little luck.

I'm using an STM32F0 module and have found plenty of resources for the STM32F1 module I2C initialization, but nothing specific about the STM32F0 initialization/transfer process.

Here's my initialization code:

void i2c_init(uint8_t ownAddress)
{
GPIO_InitTypeDef  GPIO_InitStructure;
I2C_InitTypeDef  I2C_InitStructure;

// Enable GPIOA clocks

RCC_APB2PeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

// Configure I2C1 clock and GPIO

GPIO_StructInit(&GPIO_InitStructure);


/* I2C1 clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

/* I2C1 SDA and SCL configuration */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_Init(GPIOA, &GPIO_InitStructure);

/* I2C1 Reset */

RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);


/* Configure I2C1                */

I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
I2C_StructInit(&I2C_InitStructure);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
//I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = ownAddress;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
//I2C_InitStructure.I2C_ClockSpeed = ClockSpeed;


I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE); 

}

In order to test to see if my setup was correct I designed some I2C transmission code that would transfer data in a never ending loop. Here's the code for that:

while(1)
{
I2C_SlaveAddressConfig(I2C1, RegName);
I2C_GenerateSTART(I2C1, ENABLE);
I2C_NumberOfBytesConfig(I2C1, 8);
I2C_SendData(I2C1,0b00000000);
I2C_GenerateSTOP(I2C1, ENABLE);
}

Where:

RegName = 0x75

SDA = GPIO_PIN_10 on GPIOA

SCL = GPIO_PIN_9 on GPIOA

I2C = I2C1

ownAdrress = 0x68

When I scope the I2C lines after I start this code I get a floating voltage around 160mV. When I step through the code every one of the I2C function calls happen and complete, so that's why I was thinking that it had something more so to do with my initialization of the pins themselves.

My problem is very similar to this thread, but was never answered:

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fSTM32F0%20I2C%20code%20doesn%27t%20work&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=1342

Edit 1: Here's my latest code; still not working (Edit 2: Updated to what I currently have; moved things into more correct locations):

void i2c_init(uint8_t ownAddress)
{

    /* TypeDefs for GPIOA and I2C1 */

    GPIO_InitTypeDef  GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure;

    /* Enable GPIOA clocks and I2C1 clock enable */

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    /* Configure I2C1 clock and GPIO */

    GPIO_StructInit(&GPIO_InitStructure);

    /* I2C1 SDA and SCL configuration */

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //UP
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;

    /* GPIO AF Configuration -> GPIO_AF_1: USART2, CEC, Tim3, USART1, USART2,EVENTOUT, I2C1, I2C2, TIM15 */

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* I2C1 Reset */

    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);


    //I2C_DeInit(I2C1);

    /* Configure I2C1 */

    I2C_StructInit(&I2C_InitStructure);

    I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_OwnAddress1 = ownAddress;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

    I2C_Init(I2C1, &I2C_InitStructure);
    I2C_Cmd(I2C1, ENABLE);
    //I2C_AcknowledgeConfig(I2C1, ENABLE);

}

As you can see I added the two lines GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1); and GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1); where I found that GPIO_AF_1 had the alternate function of I2C1 from the STM32F0 Standard Peripheral bibliotheek.

Any other ideas? I've been playing around with the clocks to see if that changed anything and have been adding snippets of other people's code just to see if that has any effect on the output of my device.

Edit 3: I have tried pulling both the SDA and SCL lines up to VCC with a 1kohm resistor as instructed by this guide: https://electronics.stackexchange.com/questions/57121/i2c-pullup-resistor-calculations

->(3.3V-0.4)/3mA ~= 1kohm

Edit 4: After going through my code line by line I tried outputting various flag bit registers. Specifically these registers: isr = I2C1->ISR;, cr1 = I2C1->CR1;, and cr2 = I2C1->CR2;

The flag I get after initiating the I2C transfer handling with I2C_TransferHandling(I2C1, 0x02, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Write); was 0x8001 which can be deciphered down to two errors:

#define I2C_ISR_BUSY ((uint32_t)0x00008000) /*!< Bus busy */

and

#define I2C_ISR_TXE ((uint32_t)0x00000001) /*!< Transmit data register empty */

I've found some work arounds at this link here (remove the space after https: to go to the link -> stack overflow won't let me post more than 1 link for some reason): https: //my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex_mx_stm32%2FSTM32L151RB%20I2C%20Busy%20flag%20always%20set&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=690 that I'm looking to implement and will report back as soon as I try them out.

arm
stm32
i2c
asked on Stack Overflow Nov 4, 2014 by Stroodlemuffin • edited Apr 13, 2017 by Community

5 Answers

1

I have I2C working as master and slave on the F0. The main issue I can see with your code is that in master mode you absolutely must set the I2C_Timing initialisation structure member. See RM0091 for sample values that correspond to the frequency you want to generate on SCL. In slave mode the clock is recovered from the master so the timing member doesn't appear to be used.

As the others have said, external pullups to Vcc on SCL and SDA are not optional and must be present once per bus not per peripheral as you incorrectly stated in a comment. You were right to use the calculator to choose suitable values because the internal pullups in the STM32 at about 30-50K are far too weak for use as I2C pullups.

answered on Stack Overflow Nov 6, 2014 by Andy Brown
0

The problem is that you don't configure proper AF (Alternate Function) source for I2C pins. After reset registers that configure AF are all 0, and I2C's function is 1, so your I2C peripheral is in fact disconnected from the GPIOs.

Technically it makes no difference when you configure that, but it's best to do that before GPIO configuration to minimize any unwanted transitions on the pins.

Edit: You MUST have the pullups on the I2C pins - without them you have "low" level there, and I2C peripheral detects that as bus error, which obviously prevents it from working properly. Either connect external resistors, or at least enable internal pullups instead of "no pullups/no pulldowns" configuration.

Moreover - it's just not possible for an "open drain" pin to work properly without pullup.

answered on Stack Overflow Nov 5, 2014 by Freddie Chopin • edited Nov 5, 2014 by Freddie Chopin
0

The Core of Cortex M0 is different then Core of Cortex M3, STM32f030f4 has AHB and APB1 (bridged) only. The C code of STM32f1xx never be run under STM32f0xx. Use STM32F0xx_Snippets_Package for solve your key problems.

answered on Stack Overflow Jun 15, 2015 by Marcinlas102
0

I notice that you are mapping the pins to AF_1 and that is for the UART function.

GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);

You need to map them to AF_4 to use the I2C function.

GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_4);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_4);
answered on Stack Overflow Nov 18, 2015 by Russ Troxel • edited Jan 28, 2016 by Boitumelo Dikoko
0

I just noticed this too:

RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);

So you have disabled the clock after you enabled it. I don't see where you have re-enabled it later.

answered on Stack Overflow Nov 18, 2015 by Russ Troxel

User contributions licensed under CC BY-SA 3.0