STM32L0/F0/F3 I2C Tutorial


Inter-Integrated Circuit is a common communications method  used for intra-board communications among ICs supporting the protocol. The maximum distance you can connect two devices when "on-board" is not an option depends on materials being used. Wire capacitance, resistor values, and frequency all come into play and will ultimately determine the maximum communicable distance. But that is not the subject of this post. We are here to learn about how to implement the I2C peripheral in the STM32LO/F0/F3 devices (they are all identical except for 1 bit described later). For more I2C specific knowledge check this site out: https://www.i2c-bus.org/
So lets take a crack at it. In this tutorial I will cover how to set up the microcontroller in master mode to be a receiver and transmitter. The microcontroller also supports being a slave, I may cover that in a later psot if anyone requests it. As the tutorial progresses I will introduce the AT24C02 EEPROM device which I will be using for demonstration purposes.


Configuration

The I2C will be configured in the follow fashion:
  • 7 bit addressing mode
  • Fast mode @ 300KHz
  • No interrupts and No DMA for this first tutorial, that will be covered later in the tutorial.

RCC CLOCK GATING

First thing as usual is to enable the clock gating for the I2C peripheral and the necessary GPIO port in the RCC controller. As well as configuring  the GPIO pins to operate in I2C modes. The STM32L0 series has a register specifically to enable GPIO called the IOPENR register and the I2C1 peripheral is in the APB1ENR register inside the RCC block. I2C1 on the L0 uses GPIO port A pins 9 /10 for Clock and Data respectively. The F0 and F3 use different pins so make sure you check that accordingly.  In the driver we will be writing in future posts we will address these pin differences. 

GPIO CONFIG

The next logical step is to configure those two pins as required to operate as I2C pins. According to a table that is not on the STM32L0 reference manual but is found in the STM32F1 RM. According to this table we have to setup the pins as alternate function open-drain. this makes perfect sense because if you have done your research, which I hope you have, the I2C protocol calls for pull up resistors on the clock and data lines. This means the I2C peripheral drives the lines low to generate a logic-0 and lets go of the line and lets the resistor pull it up to generate a logic-1  So we will configure the pins as stated in this table. For my purposes I will not add external pull ups because the module I will be programming already has those pull-up resistors. However you can always enable the internal pull-up resistors of the GPIO pins, as oppose to adding external ones. The internal pull-ups are enabled by setting the pin to output a high level on the BSRR or ODR register while in input or alternate function mode. You only want to do this for I2C, other protocols on this microcontroller do not call for pull-ups. I will still include the code for enabling the pulls up for your reference even though i do not need them.

Alternate Function Register 

Next you must setup the alternate function on the pins to be I2C. The microcontroller supports pin remapping you could also have a different alternate functions on the same pins. According to the following table found in the datasheet (not the reference manual which people mistakenly call "datasheet" ) the alternate function for I2C is Alternate Function 1. In the header file for the chip the alternate function registers are configured as an array of two structures, you will see this in the code. Furthermore the alternate function registers are divided into high and low registers like the gpio, high is used for pins 8 - 15 and low is used for pin 0 - 7. Note that this is for the SMT32L0 , check your RM and Datasheet for I2c pins and alternate functions for STM32F0 and F3.

I2C Registers

Timing Register: TIMINGR

Now we have to set the timing register, now I will admit I did not feel bold enough to attempt to conquer the timing register. So I used  nifty little excel file provided by ST to generate some approximate values for us.You can download that file from here . You can also open up cubeMX and set up the microcontroller as you wish and setup the i2c and observe in the "i2c parameter settings" tab what values CubeMX assigns the timing register. I could not get mine to produce 400KHz even when using a full cubeMX hal project it did not produce 400KHz , so give it a shot, at best I got around 324KHz which more than suits my needs.

Control Register 1: CR1


Bits 19-23 consists of 4 non I2C related settings, these bits are for the SMBUS, not covered here yet. 
Bit 19 is the general call enable register. I2C has a special "call" that when it is sent out on the data line all slaves will reply...if they want.. this bit 19, will let you decide when you are using the microcontroller as an I2C slave if you want it to reply to the call of duty ;) 
Bit 18 will allow the I2C peripheral to wake up the microcontroller form stop mode. (this is the only bit that does not exist in the STM32F0 ) 
Bit 17 enables or disables clock stretching, this should almost always be enabled. Clock stretching allows the slave or master to stretch the clock if it needs more time to complete a command. For example if I send a command to my EEPROM to send me some data, it may be a little slower to complete that command and to not corrupt our communication line its best for it to stretch the clock line
Bit 16 is slave byte control, havent used it yet.. ill back to  you on that.
Bits 14-15 are enabling DMA transfer from the peripheral to memory locations of your choice. we will do this later in the tutorial.
Bit 12 is for enabling the analog filter per the I2C standard. 
Bits 8-11 configure the digital filters, note you cannot have digital filters enabled and wake up from stop mode also enabled(bit 18) 
Bits 1 -7 are a variety of interrupt enable bits , error(7), transfer-complete(6), stop-detection(5), NACK-detection(4), address match detection(3), RX interrupt (2), TX interrupt(1)
Bit 0 enables the i2c peripheral. This should always be the last thing you enable, and you should disable it while making any changes.

Control Register 2 : CR2


Bit 26 packet error check is non I2C related. It is used for SMBUS
Bit 25 will automatically send a stop condition when the number of specified bytes are sent.
Bit 24 allows you to control what happens when the number of specified bytes are sent
Bits 16 -23 allows you to specify how many bytes will be send to the given slave address. As you can see there are only 8 bits so it only allows you to send 255 bytes at a time.
Bits 13-15 allows you to send those specific commands, NACK , STOP and START
Bit 12 concerns itself with 10 bit addressing mode which I will not use
Bit 11 sets the addressing mode to 7 or 10, I will use 7.
Bit 10 allows to to specify if the current mode will be a write transfer or read transfer, this allows the hardware to append a 0 for write mode to the LSB of the address or a 1 to the LSB for read mode. This means we do not have to do it in software.
Bits 0 - 9 are where you enter the slave address of the device you  are talking to. Your slave device should have two 8 bit address, well technically 7 bit... but what I mean is that if the the address is 1010-0000 this is the write address and the read address will be the same but add a 1 in the LSB to make it 1010-0001 , this is how the I2C protocol is defined. 

Interrupt and Status Register : ISR

This baby is very important because it will control the flow of our program. Basically anytime any of these events occur a flag is set in this register and we check it/ poll / wait for it and then clear it to move on to the next step. 
Bits 11-13 are SMBUS specific bits and thus non I2C related.
Bit 16 transfer direction used in slave mode this is set when the adress is being sent by the master and there is a 1 in the LSB
Bit 10 is the overrun error, this is why it is important to have clock strecthing enabled, overrun occurs when you the device cannot respond in time and more data is being requested or sent. For example if I tell my EEPROM to write  bytes and it takes it sometime to do that write operation but I dont give it time and I send it more data to write.... this will be an overrun error. This is avoided with clock stretching. The device will basically halt the clock until its done and then activate the clock again and that is when my new data will get clocked in....more or less.
Bit 9 is arbitration loss, since I2C allows for multimaster communication, this means there may a time when you as a master have lost control of the communication lines and must wait your damn turn.
Bit 8 is BERR.. means its cold outside. or it could mean there is a bus error...who knows.
Bit 7 transfer complete reload allows you to relaod the nbytes part of control register 1 with a new number of bytes to be sent..this is used when your streaming a lot of bytes more than 255 to automate the process.
Bit 6 is our friend and it means transfer complete and that means it has sent all the bytes you specified in the Nbytes
Bit 5 lets you a know a STOP condition has been detected
Bit 4 means a NACK was received.
Bit 3 in slave mode means the address received matches on the of the configured slave addresses in the OAR. 
Bit 2 means we have data to read in the RX register
Bit 1 means the I2C is waiting for you to write more data in the TX register
Bit 0 means similar to above, but it specifies the TX buffer is empty and thus ready for more data.

Interrupt Clear Register: ICR


There are some flags in the interrupt status register that are cleared by writing to the TX register, by reading from the RX register and some other flag are cleared by using this clear register. So putting a 1 on the STOPCF (stop clear flag) bit of this register will clear the Stop detection flag in the status register.

Ultimately we have the RX register and TX registers which should seem pretty obvious by now. We will read received data from the RX register and we will write data to be transmitted on the TX register.
The next two registers are the OAR1 and OAR2, these are used when you are configuring the microcontroller to be an I2C slave. These registers is where you would write your own slave address. It allows you to have 2 own addresses. Cool story.

The TIMEOUT register is strictly for the SMBUS and this non I2C related.
The PEC register is the packet error check register and SMBUS specific thus non I2C related.

Comments

  1. Hi Edwin, ST reference manuals would be much more valuable if they had examples like the one you present.
    Congratulations and thank you very much.

    ReplyDelete
Share your comments with me

Archive

Contact Form

Send