[STM32]: Making a custom i2c slave device using STM32F103
I2C is a serial protocol and its greatest advantage over other protocol is that it requires constant two wires irrespective of number of devices connected.
In this article, we shall discuss on how to design your own custom i2c slave device on stm32f103.
Why STM32F103?
32 bit, fast mcu speed 72 MHz.
Cheap price < 2$ and easily available.
I2C speed 100Khz and 400Khz supported.
Interrupt & DMA support on i2c.
appoint cpu intensive work to i2c slave e.g. motor control, adc sampling etcs.
Requirements
stm32f103 blue pill board or similar
i2c master (Arduino)
Custom i2c slave device
We are going to design a simple math calculator which requires two numbers (1 byte size) as input and it provides addition, subtraction and multiply of those two numbers as a reply to master query.
In slave mode, its not recommended to use polling method since ADDR event might be missed by slave in case of slave is doing some other tasks.
This would make i2c master to get stuck waiting for ADDR flag to be set. These are the reasons why its recommended to use ADDR and STOPF
using events and manage data transfers using interrupts or DMA.
We would use i2c event interrupt to handle this.
Design
Slave registers
We need to design our device registers or commands which is queried by master to control our custom slave device.
SET_NUMBERS_REG 0x01
master writes three bytes after writing slave address on i2c line. [START][Addr << 1 + 0] [SET_NUMBERS_REG][n1][n2][STOP]
GET_ADD_REG 0x02
master sets the register first and then read two bytes. [START][Addr << 1 + 0][GET_ADD_REG][STOP] [START][Addr << 1 + 1][ReadByte1][ReadByte2][STOP]
GET_SUB_REG 0x03
master sets the register first and then read two bytes. [START][Addr << 1 + 0][GET_SUB_REG][STOP] [START][Addr << 1 + 1][ReadByte1][ReadByte2][STOP]
GET_MUL_REG 0x04
master sets the register first and then read two bytes. [START][Addr << 1 + 0][GET_MUL_REG][STOP] [START][Addr << 1 + 1][ReadByte1][ReadByte2][STOP]
Code
I shall be using libopencm3 library but this code can be easily ported to other libraries.
SDA and SCL of I2C1 are PB7 and PB6 respectively. i2c1_ev_isr is the interrupt function for i2c events. To enable this ISR function, we need to call i2c_enable_interrupt()
and enable both interrupt events namely I2C_CR2_ITEVTEN and I2C_CR2_ITBUFEN along with nvic interrupt NVIC_I2C1_EV_IRQ.
Lets write our i2c setup function.
Lets write our logic for handling data into ISR function.
our main function is given below.
I have put the code at github. Refer README.md for how to compile code and upload to stm32f103.
In Action
The below codes are typically the way i2c master would query to this custom i2c slave device.
This can be easily implemented on Arduino using Wire library.