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.

Community
  • 1
  • 1
Stroodlemuffin
  • 31
  • 1
  • 1
  • 5
  • Possibly a silly question. Do you have pullups on SDA and SDL? Are they high before you run this code? – DoxyLover Nov 04 '14 at 20:18
  • Hey there DoxyLover, tried this and didn't work. Don't believe they are set high before I ran this code. – Stroodlemuffin Nov 04 '14 at 22:15
  • What do you have exactly connected to the I2C lines? What is the slave device? Are you aware that you must have pull-up resistors connected between each I2C line and Vcc? – DoxyLover Nov 04 '14 at 22:37
  • So right now I don't have it connected to any other device at all; it's simply a master with a separate pull for each of the SCL and SDA lines. And yes, I am aware that I will eventually need a pullup for each device I have it connected to. Right now I don't even have a start signal going out (I should be able to see the raw data as is with my current code or at least the start signal). – Stroodlemuffin Nov 04 '14 at 22:52
  • Actually, no, you don't need separate pull-ups for each device. Just one each for SCL and SDA. The fact that these lines are low before you start initializing the I2C may be an important clue. The default mode of these pins appears to be driven (output) low where I'd expect them to be open-drain (and thus, pulled high by the resistors). Unfortunately, I an not familiar with the STM32 series so I really cannot help you further. – DoxyLover Nov 05 '14 at 00:13
  • Yeah, it's got to be something very specific to the STM32 series, more specifically the STM32F0 series. Thanks for your help though, I'll keep at it and see if I can't find a solution on my own. – Stroodlemuffin Nov 05 '14 at 03:06
  • 1
    You're missing `GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_Pin_9); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_Pin_10);`. I'm not sure if it matters but you might need to do it **before** calling `GPIO_Init`. – barak manos Nov 05 '14 at 05:04
  • Added those lines of code; see my edited response above for an update on the situation. – Stroodlemuffin Nov 05 '14 at 15:51

5 Answers5

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.

Andy Brown
  • 11,766
  • 2
  • 42
  • 61
  • So I found some other code with all the timing calculations done and inserted this line into my I2C bus setup: `I2C_InitStructure.I2C_Timing = 0x50330309;//400kHz` However, I'm still getting nothing on both the SCL and SDA lines. I only have one master device right now with the pullups on it, but I'll keep in mind that I only require one pullup per bus for future reference. – Stroodlemuffin Nov 06 '14 at 15:11
  • Now take a look at your transmission logic, referring to how ST do it in their example code. The basic pseudocode sequence to transmit the address is: I2C_TransferHandling() -> wait for TXIS -> I2C_SendData(address) -> wait for TCR. – Andy Brown Nov 06 '14 at 15:52
  • Theoretically speaking, if I were to skip the waiting for TXIS and TCR bits, would my bus not just transfer data regardless of any sort of acknowledgement from the slave? As of now, for simplicity sake, I only have a master on the entire bus and am simply just trying to see if the bus will even output something. Would the code I have above in my main loop not be substantial just to get something that I can scope in? – Stroodlemuffin Nov 06 '14 at 16:11
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.

Freddie Chopin
  • 8,440
  • 2
  • 28
  • 58
  • If you are referring to my comment, then the word "must" does not appear there, and so you can't say that the comment is misleading (in fact, you should probably give this comment some credit instead, because your answer is essentially the same). – barak manos Nov 05 '14 at 08:45
  • Ok, I may have misread "might" for "must" - it's early... (; Anyway - your comment doesn't explain the issue and the solution, and probably (I don't use SPL as that's crap) the code is wrong - you should give AF source instead of "GPIO_Pin_9" - there are some macros like AF_SOURCE_I2C or sth like that. – Freddie Chopin Nov 05 '14 at 09:08
  • Added those lines of code; see my edited response above for an update on the situation. – Stroodlemuffin Nov 05 '14 at 19:06
  • @Freddie Chopin I tried that first thing when DoxyLover suggested it yesterday; still getting nothing on the output. – Stroodlemuffin Nov 05 '14 at 23:53
  • @Stroodlemuffin - you can always ditch SPL (it's crap anyway) and configure I2C using registers. It's like 10 lines of code (maybe 20 if you include GPIOs) and you'll find numerous examples online. On the other hand, you'll find such examples for SPL too. There are also "official" examples in the package with SPL - have you tried looking there, running the examples and/or comparing them with your code? STM32F0xx_StdPeriph_Lib_V1.3.1/Projects/STM32F0xx_StdPeriph_Examples/I2C/: - I2C_EEPROM - I2C_TSENSOR - I2C_TwoBoards - I2C_WakeUpFromStop – Freddie Chopin Nov 06 '14 at 08:04
  • @Freddie Chopin - I've pretty much gone through every code sample with SPL in it, but if you really think that using the registers directly might solve my problem then I'll give it a try (don't understand why SPL itself would be the one breaking my code, but I'm running out of ideas here). I'll try looking through that code and will update here later. **Edit:** Just for reference this is the location Freddie was talking about: http://www.stm32-spl-doc-online.esy.es/STM32F0xx_StdPeriph_Lib_V1.3.1/stm32f0xx_stdperiph_lib_um_html/dir_e7f28d9a57c7d7ff89e077e1a2afb92a.html – Stroodlemuffin Nov 06 '14 at 15:14
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.

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);
dikokob
  • 85
  • 3
  • 12
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.