Library 09- I2C for STM32F4

I2C or Inter-Integrated Circuit is a multimaster serial single ended bus. This protocol is commonly used with RTC modules, temperature sensors, EEPROMs, IO expanders and more.

I2C protocol uses 2 wires:

  • SCL: Serial Clock, clock for serial synchronization
  • SDA: Serial data, bidirection line for receving and transmitting
  • Both wires need external pull up resistor, from about 4k7 to 47k, if you don’t use pull up resistors in MCU. In our case, you don’t need external pull ups, because library uses internal in STM32F4.

More about I2C is described here.

STM32F4 has up to 3 I2Cs, every of them has (as always) at least 2 pins pack for each I2C. Pins used for each I2C are described in table below:

I2C pins
PinS PACK 1 PINS PACK 2 PINS PACK 3
I2Cx SCL SDA SCL SDA SCL SDA APB
I2C1 PB6 PB7 PB8 PB9 PB6 PB9 1
I2C2 PB10 PB11 PF1 PF0 PH4 PH5 1
I2C3 PA8 PC9 PH7 PH8 1

Library

Features

  • Master mode
  • 7 bit slave address
  • Up to 127 different slaves on 1 I2C bus
  • Read/Write single byte
  • Read/Write multiple bytes from slave
  • Version 1.1 – August 09, 2014
    • Checks if device is connected to I2C bus
  • Version 1.2 – August 14, 2014
    • If you connect more devices on one I2C with different max SCL speed, low speed will be always selected.
    • Added some additional pins for I2C
  • Version 1.3 – December 22, 2014
    • Added option to read multi bytes from device without setting register from where
  • Version 1.4 – March 08, 2015
    • Added support for new GPIO settings

Dependencies

  • CMSIS
    • STM32F4xx
    • STM32F4xx RCC
    • STM32F4xx GPIO
    • STM32F4xx I2C
  • TM
    • TM GPIO
    • defines.h
    • attributes.h

Initialization

When you use library, you have to initialize it first. You can do this with

or

Now, you are able to read/write data with slave.

Read

 Write

Functions and enumerations

Example

Project available on Github, download library below.

Icon
TM STM32F4 I2C Library

I2C library for all I2C peripherals

tilz0R

Owner of this site. Also electronic enthusiasts, web developer, 3D printer fan, handball player and more. Big fan of STM32F4 devices. In anticipation of the new Discovery board for STM32F7 lines.

You may also like...

Read before commenting!

Before you make a new comment, make sure you agree with things listed below:

  • - Read post to make sure if it is already posted what you are asking for,
  • - Make sure you have the latest version of libraries used in your project,
  • - Make a clean and grammatically correct written message,
  • - Report as many details as possible, including what have you done so far,
  • - Do NOT post any code here. Use Pastebin,
  • - Do NOT post any error codes here. Use Pastebin,
  • - Specify STM32Fxxx family and used Discovery/EVAL/Nucleo or custom made board,
  • - Make sure your clock is set correct for PLL,
  • - If you are using my HAL drivers, please check this post how to start.
Comment will be deleted on breaking these rules without notification!
  • BenG

    Thank you so much!
    Using your libraries and tutorials I finally succeeded in reading out a 9 DoF Sensor stick! I was searching for a solution for over 2 weeks and with your site I got it working within 2 days!
    The only problem I have now is that for each axis of a sensor I receive 2 1 byte hexadec numbers (MSB and LSB) I do not know how I can convert them to the corresponding values in ASCII (so I can read and understand them in my terminal). Do you maybe have any clue?

    Thanks again!
    Greetings
    Ben

    • tilz0R

      If you want numbers to be printed to your terminal, than use sprintf function to convert.

      • BenG

        Thanks it works! 😉

  • محــمد مهــدی سلطانی

    Thanks a’lot for these libraries but I can’t use the I2C 2 Pin pack 1
    It return’s 0x00 …
    what is the problem?

    I have the stm32f407 discovery board…

    • Strange.

      Because it works for me just as expected.

  • Benedikt

    Thanks for your library. But if i use readmulti i always get Timeout…
    For what you use this: I2Cx->SR2; ? And why this:
    if (i == (count – 1)) {
    //Last byte
    data[i] = TM_I2C_ReadNack(I2Cx);
    } else {
    data[i] = TM_I2C_ReadAck(I2Cx);
    }

    • Sr2 is status register. Does this works great with read only? Not multi. And yes, last byte in read multi is read without ack from MCU.

      • Benedikt

        Thanks for your reply. Reading is working, I’ve got a little mistake there. But why no acknowledg for the last bit? how can I check that writing is working?

        • Write single byte and then read single byte. Make sure they are the same.

          • Benedikt

            I already tested this. I always get 0x69. Any Ideas?

  • Janice

    Could this I2C library be used to control ultrasonic sensors slave, for example SRF08?

  • Greg

    Hi Mejerle,
    can I read TMP112 I2C temperature sensor without any problem using this library (multibyte read) ?
    Here is the datasheet http://www.ti.com/lit/ds/symlink/tmp112.pdf
    Temperature is stored as two bytes (starting from address 0x00)
    Kindly please confirm that I can read temp from this sensor using your library (at least in theory).
    Thanks in advance,

  • Greg

    Hi Mejerle,
    I’m trying to read temp from Si7021 but I’m doing something wrong, I do not know how to use your I2C lib to read from that sensor, could you help me with that? some code snippets would be great here. Thank you very much.
    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf
    page 18 says how this should be done, please help.

    • Device slave address is 0x80.
      On section 5.2 you have you to write/read from device.

      • Greg

        I have tried “isdeviceConnected” but I get 0 all the time. Also I have ried connecting 24c02, also without success. How this should be configured. Do I need to define “#define TM_I2Cx_MODE I2C_Mode_I2C” in defines.h? I’m trying to use PB8 and PB9 because those are my free pins on STM32F417VGT6 micro controller. I need to run I2C ASAP, kindly please help me with that.
        Everything is connected correctly, also pullup resistors are connected to VCC from SCL and SDA. Something from program side must be wrong. Please explain step by step how whould I use your library for example for 24c02 eeprom.

        • Greg

          some news, comm is working, just I do not know how to get data from this device 🙁

        • You have the i2c1 pp2 for your pins. Make sure, that pins are really connected right.
          Initialize i2c (no need for any define).
          For device address you must specify 8bit,left aligned address.

          For example. If devuce address is 1001001 then you have to set address 10010010 for communication. Basically this means 7-bit address shifted to the left.

          For your sensor, i2c is strange documented.

          • Greg

            I know that the I2C communication is strange for this device, I need to write data directly, without register address, that is why I have asked you for help, because you are great at making those libs for STM32F4. Can I count for your knowlegde here?

          • Hmm, yea, you will need to nake a function with lowlevel drivers. So open my write function, and copy everything from there, then delete part where value is sent. Sent only address of device and command.

            Understand?

          • Greg

            I’ll try and let you know the results.

          • Basically, make new write function and delete line from new:
            TM_I2C_WriteData(I2Cx, data);

          • Greg

            Mejerle, if you can find some time to write this I’ll be really thankful because
            I cant figure this out. This is very important for me to run this sensor. Please help.

          • http://pastebin.com/EMpwXeKY
            Btw: my name is Tilen ;))

          • Greg

            Sorry for that Tilen, I was thinking that Mejerle is your name 🙂 Anyway, for command write I wrote the same code as you did, but what about reading from the device? For repeated start I just need to write I2C_Start one more time after I’ll send a command or read byte?

          • Try it if it work. Edit function provided on link.

          • Greg

            I’m giving up on this today. I hope I will figure out communication with this sensor with your help. Otherwise I’ll be doomed. 🙁

          • Greg

            Hi Tilen, could you give me your skype id or something? If you have some free time your help will be appreciated. Today I was trying to write and read 24c02, what is strange: writing is OK, but when I’m trying to read few bytes (single read function used in the loop), then the first byte from address 0x00 is ommited. When I comment out write function then read is working OK (I mean first byte is read correctly). What could be wrong?

          • Skype available on “About” section.

  • Guest

    Hi Majerle thanks for the great libraries and tutorials. Has allowed me to progress rapidly. I am wanting to debug my code by sending a integer (uint8_t in this case). I cannot find a way to convert uint8_t to char[]. I am using GCC.

    How can I convert this to a char array to send with TM_USART_Puts()?

  • mukhe

    Hey Tilen,

    i noticed that you have locked your libraries..the issue is i am using an external pull up resistors and so i have to declare a NOPULL. I had some issue with the I2C returning read Device ID values of blank 0x00 and i read somewhere that this can be solved by using external pull up resistors.

    my questions is lol, how do i unlock your library so that i could modify it

    • That may be right.
      First, you can just add external pullups without modification lib.
      Second, right click on file, properties, uncheck locked option.

      • mukhe

        Wooot! that was quick!
        alright

        anything else why i am getting blank 0x00 values back?
        i did get the 0xA0 device ID once, but since then couldnt read it….just dont know whether it’s software or hardware

        • I got always working everything using i2c and this lib. Make sure you have wiring correct and everything.

          • mukhe

            cool man, its a pcb i designed, checked the connections, everything seems to be as i think it should be but just.cant,get.it.to.work.

            alright, thanks though. will be touch later

          • You can use my IsDeviceConnected function to test if device with specific address is connected to i2c or not. Check manual in .h file

          • mukhe

            oh that would be interesting. Ok
            will get back to you once i do that

          • mukhe

            So yeah the values 1
            the rest of the values you see is DeviceID I and DeviceID II. both return 0x00 lol

            so this means that the device is alive I presume?

          • Or it has special reading method.

          • mukhe

            you are right..
            so what the datasheet expects us to do is this.
            they have register groups A, B, C and so forth
            after we gain access to register group A, only then can we have access to device Id register
            the datasheet says this:

          • mukhe

            i did accordingly but yeah its just 0x00 values coming out…

  • Saputra Ahmad Edi

    how do i know mhy stm32f4 discovery is using pin pack 1, pack 2, or pack 3?

    • You have table with pins my lib uses. If you want some sda and scl combination, theb select proper i2c and pinspack you want.

      • Saputra Ahmad Edi

        so, i cant use pin PB6 as scl and PB9 as SCL?

        i dont understand, why so many peripheral pin conected to other peripheral pin, and one periph like I2C is conected to many pin like I2C1 is 3pair pack pin

        • If you want PB6 for SCL and PB9 for SDA, then use:
          TM_I2C_Init(I2C1, TM_I2C_PinsPack_3, 100000); /* 100kHz clock */

          • Saputra Ahmad Edi

            sorry, i mean if i use different pack like i2c2 use PF1 as scl and PH5 as SDA

          • A ok, I know what you mean.
            Well I’ve made some combinations for these pins.

            This combination you want is not supported by “easy”. You haveto make changes in code.

          • Saputra Ahmad Edi

            actually i can right? if i can change the code.
            so there is no rule which pair of pin we wanna use, right?

            and if i am not wrong, stm32f4 clock value is important for any operation peripheral, is store on system_stm32f4xx.c, which value do you use for this driver? sorry i am just little newbie with this uC

          • Thats right.

          • Saputra Ahmad Edi

            when i use ds1307 library, if i reset my STM why seconds count also reset to zero? like now second is 30 and minutes is 12, i reset my STM and i get second count starting from zero again but minutes is still 12

            thanks

          • Post this there!

      • Saputra Ahmad Edi

        where can i know this packaging from?

  • Mohammad

    Hello

    Thank you for your useful I2C library. I have used your library and tm_read/write functions to communicate with GY-88 madule including MPU6050 (accelerometer & Gyro),HMCL (magnetometer) and BMP085 (barometer). Most of executions (95 %) are successful but some times I have an issue; freezing everything. When trace the execution, the problem is with checking i2c_flags and interupts. somewhere like this

    while (!I2C_GetFlagStatus(I2Cx, I2C_FLAG_SB) && TM_I2C_Timeout) {
    if (–TM_I2C_Timeout == 0x00) {

    and tm_i2c_time out starts counting up and the execution stops at this stage. I can not understand what is the problem. I decided to consult with you . Is the problem may be due to hardware (my mean is GY-88 Module),oveflow etc … or is related with coding manner?

    any Idea?

    thanks

    • Ikrame Kourid

      I have the same problem as you disd you could resoudre the problem ?

  • Jacky

    That’s a great work, thanks for sharing.

    I will use HMC5883L compass in STM and I’m implementing arduino library into STM.

    Write(int address, int data)

    {

    Wire.beginTransmission(HMC5883L_Address);

    Wire.write(address);

    Wire.write(data);

    Wire.endTransmission();

    }

    How can I write this code which is “Wire.beginTransmission(HMC5883L_Address);”, in STM?

    Thank you

    • I see now, I have function to read data without specify register address, but not for write.
      Send me a mail, I will send you info when function will be added to my lib. You can send me e-mail to my email which you can find on my site.

    • Use TM_I2C_Write(I2Cx, DeviceAddress, address, data);
      Device address is left aligned 7 bits!

      • Jacky

        So you mean,

        TM_I2C_Write(I2Cx, HMC5883L_Address, address, data); Do we need to end the transmission like in the above? and only device adress is left aligned 7 bits, right?

        • I mean like that.
          No, this function will start transmission, write and stop it for you.

          • Jacky

            Thank you

            I have two more and -hopefully- final questions

            Read(int address, int length)
            {

            Wire.beginTransmission(HMC5883L_Address);
            Wire.write(address);
            Wire.endTransmission();

            Wire.beginTransmission(HMC5883L_Address);
            Wire.requestFrom(HMC5883L_Address, length);
            uint8_t buffer[length];
            if(Wire.available() == length)
            {
            for(uint8_t i = 0; i < length; i++)
            {
            buffer[i] = Wire.read();
            }
            }
            Wire.endTransmission();
            }

            TM_I2C_ReadMulti(I2C1, HMC5883L_Address, address, buffer, length);

            1.) Is that true?
            2.) I'm also using MPU6050. Should I use same I2C channels or different?

          • TO read multi registers, you have 2 options.
            TM_I2C_ReadMulti() or
            TM_I2C_ReadMultiNoRegister(). Open tm_stm32f4_i2c.h file and take a look for different.

            You can use my MPU6050 library.

  • Gustavo Sousa

    Thanks for your help again. Great libraries.

    Tilen, I tested your example and it works fine with frequencies till 100 kHz, but I need setup the frequency to around 330 kHz.

    I used an oscilloscope to measure that, and above 100 kHz the frequencies are always less of the setup.

    Do you know why this happen?

    I tried change the “I2C_DutyCycle” to 16_9 mode, but the frequency change, but not to the exact number that I setted.

    Thanks in advance.

  • Bublo

    Hi Tilen, I’ve got some problem with your I2C library. I was using your older version, before you made GPIO lib. Communication with MPU-6050 worked good. After that, I updated your libs with last versions, and communication is not working, I still get error Device not connected, or Device is invalid. No changes in my source code was made. What is the difference between those two libs? only GPIO pin settings?
    Thanks for answer.

    • Library works just fine.
      Tested with MPU6050 2 hours ago.

      • Bublo

        I tried to test it with your example project. I changed only I2C to I2C1, TM_I2C_PinsPack_2 in your defines.h file. All other settings without changes. And still not working with new I2C lib. If I replace it to older version, everything is ok.

        • Defenitely strange.
          Send old library to my address please.

          • Bublo

            sent to your email…

  • Meric

    Hi Tilen

    Is it possible to use this library with ATMEL 24C512 EEPROM? I think its address 0X50…

    Regards

    • If EEPROM works with I2C, then yes, it is possible.

  • Jorge Del Aguila

    Hello Tilen!

    I’m trying to use your library to control the onboard DAC of the stm32 discovery (datasheet and code attached). I think that it may be failing because the dac’s datasheet says that you must have the reset pin up for a certain amount of time. Is it possible with your library to make the scl pin stay up for a little time?

    code: http://pastebin.com/3RGCH8vG
    dac’s datasheet: http://www.cirrus.com/en/pubs/proDatasheet/CS43L22_F2.pdf

    • You probably have some missunderstanding here. Reset pin should be another pin probably, third one. Noone uses scl for reset

      • Jorge Del Aguila

        Yes. Actually it is PD4 and I am holding it up before comunication but nothing happens. To explain it better I’m attaching the I2C specifications from the datasheet and the schematic of the DAC.

        Best regards!

  • Timothee Guerin

    Hi Tilen !
    It’s an amazing work that you do !

    I have a problem on my ST32f4 Discovery with the i2c library. I have no clock from SCL and so, nothing at all. I’m trying to connect to a capacitive touch controller (FT5x16, similar to FT5336 in your library), 10k pullup resistor are set.
    I’m running it with FreeRTOS, but even your exemple doesn’t work for me. I check with my oscilloscope, but SCL and SDA are always high :/

    Thanks for your amazing work !

    • Mcu?

      • Timothee Guerin

        Sorry, I just edit my first post. It’s STM32f4 Discovery.

        • Send code to my email please. Lib works fine so must be a problem on your side.

          • Timothee Guerin

            Email sent 🙂

          • Nothing received so far.

          • Timothee Guerin

            Check your spam, I don’t have any errors received.

          • Like I said, nothing received so far.

  • Mustafa

    Hello Tilen,

    I have a question. In your TM_I2C_Write, read vs function’s parameter have uint8_t type, so, how can I use this function for 24LC512 because It has 65535 byte adress area.

    Thanks.

    • Hi mustafa,

      I don’t have this implemented in this library. It is done in my HAL I2C library.
      Maybe you can take a look there.
      But basically, you have to send 2 bytes for address instead of one, so you can add this to function or make a new one 🙂

      • Mustafa

        Hi Tilen,

        Yes you are right, I am trying to write this library but as a task which is switch case style. I do not want to wait any where for write or reading operation for critical system. There is different one step for 2 byte adress. We have to swap adress byte before the to send, because eeprom accept high byte firstly secondly accept low byte.

        Thank you bro.

        • For example, if you wanna send 5 bytes with my lib, then you wait:
          1 byte for address device,
          1 byte for register address,
          5 bytes for data.
          Together is 7 bytes waiting.

          You have 2 bytes for register, so 8 bytes together. Not so much difference.

          And you can send 2 bytes for register using masks on bytes.

  • Michele

    Hello Tilen,
    thank a lot for these awsome libraries. I’ve a question maybe stupid for you. I’ve a stm32f429 discovery, and I want to use the touchscreen with a simple interface using your library. I load all the files .c indicated but when I build the main 2 errors occurs:

    .project.axf: Error: L6218E: Undefined symbol I2C_CheckEvent (referred from tm_stm32f4_i2c.o).

    .project.axf: Error: L6218E: Undefined symbol I2C_Init (referred from tm_stm32f4_i2c.o)

    I don’t understand what I can do to resolve this problem, can you halp me?

    Thanks, Michele.

    • Check dependencies section.

      • Michele

        What do you mean? I’m approcching these arguments for the first time, I need more specific advices if you can.

      • Michele

        I solved my problems. Thanks, maybe I insert you in the regards in my master thesis.
        😉

  • reshma

    hi,
    can any one please tel me how to configure i2c lines im not able receive data from SDA line

  • divum

    Hi Tilen ,

    Can you have the source code for mcp23017 I/O Expander with stm32 MCU?

  • divum

    Sorry for typo mistake….
    Do you have the source code for mcp23017 I/O Expander with stm32 MCU? if please can you share with me ..

    • I don’t have. But looks pretty easy to implement.

  • Paul Radu

    Hi
    I’m interested in configuring my ov7725 camera,which has a SCCB interface,very similar with i2C.
    Is your code compatible with the SCCB interface ?
    I tried it,my device is recognized by being connected,but everytime I try to read single or multi,the only data I get is “1”.
    Thank you.

  • Engineer

    Is it possible to switch in the pin packs? Like take MOSI from pinpack 1 und MISO from pinpack 2? Because there is no comment in the pin defintions that there are specific packs?
    Thank you

  • Pingback: STM32F411 Interfacing with I2C EEPROM | Site Title()

  • sepide

    Is your code compatible with the SCCB interface?
    thank you

  • TheDenix8

    I highly recommend changing uint8_t i in functions
    TM_I2C_ReadMulti
    TM_I2C_ReadMultiNoRegister
    TM_I2C_WriteMulti
    TM_I2C_WriteMultiNoRegister
    to uint16_t because count is also uint16_t.
    If count is higher than 127 there will be endless loop in
    for (i = 0; i < count; i++)
    When i reaches 128 it overflows back to zero over and over again.

    • I see the problem and I totally agree with you.count is up to 255 instead of 127 but I will still fix it.