Home | Projects | Notes > MCU Peripheral Drivers > I2C Application 2: Master Rx (Blocking) (i2c_02_master_rx_blocking.c
)
i2c_02_master_rx_blocking.c
)
I2C master (STM32 Discovery board) and I2C slave (Arduino board) communication.
When the button on the STM32 board (master) is pressed, the master shall read and display data from the Arduino board (slave). First, the master has to get the length of the data from the slave for it to properly read all data from the slave.
Use I2C SCL = 100 kHz (i.e., standard mode)
Use internal pull-up resistors for SDA and SCL line
[!] Note: 3.3 kΩ or 4.7 kΩ external pull-up resistors will be necessary in case your MCU pins do not support internal pull-up resistors.
Arduino board
STM32 board
Logic level converter
Breadboard and jumper wires
To work around the voltage level difference, a logic level shifter will be necessary.
Master sends command code 0x51 to read the 1 byte of length information from the slave.
Master sends command code 0x52 to read the complete data from the slave. (Reading byte-by-byte based on the length information fetched in Step 1)
(Master displays the received data using semihosting.)
printf()
to Print Messages in STM32CubeIDE Console
For this application, I2C communication lines SCL, SDA will be used. Find out the GPIO pins over which I2C can communicate! Look up the "Alternate function mapping" table in the datasheet.
I2C1_SCL
I2C1_SDA
Although, in the documentation, it is said that PB9 can be used as I2C1_SDA, some interference has been detected while testing due to the hardware circuitry called "SWIM" so ended up using PB7 instead.
Be careful not to directly supply 5 volts to the STM32 board pins when the board is not powered up as they may be damaged. When the logic level shifter is used, you don't need to worry about this issue.
To analyze the communication with the logic analyzer, connect the channels as follows:
CH0 - SCL
CH1 - SDA
GND - Common GND of the bread board
Sketch name: 002I2CSlaveTxString.ino
You don't need to write an application for Arduino board. It is already provided as a sketch.
As soon as you download this sketch to the Arduino board, it will operate as a slave.
i2c_02_master_rx_blocking.c
Path: Project/Src/
xxxxxxxxxx
1961/*******************************************************************************
2 * File : i2c_02_master_rx_blocking.c
3 * Brief : Program to test I2C master's Rx (blocking) functionality
4 * Author : Kyungjae Lee
5 * Date : Jun 13, 2023
6 ******************************************************************************/
7
8/**
9 * Pin selection for I2C communication
10 *
11 * I2C1_SCL - PB6 (AF4)
12 * I2C1_SDA - PB7 (AF4)
13 */
14
15/* strlen() */
16/* printf() */
17
18
19
20/* Check Arduino IDE serial monitor */
21/* STM32 Discovery board is master */
22
23/* Global variables */
24I2C_Handle_TypeDef I2C1Handle;
25
26/**
27 * delay()
28 * Brief : Spinlock delays the program execution
29 * Param : None
30 * Retval : None
31 * Note : N/A
32 */
33void delay(void)
34{
35 /* Appoximately ~200ms delay when the system clock freq is 16 MHz */
36 for (uint32_t i = 0; i < 500000 / 2; i++);
37} /* End of delay */
38
39/**
40 * I2C1_PinsInit()
41 * Brief : Initializes and configures GPIO pins to be used as I2C1 pins
42 * Param : None
43 * Retval : None
44 * Note : N/A
45 */
46void I2C1_PinsInit(void)
47{
48 GPIO_Handle_TypeDef I2CPins;
49
50 I2CPins.pGPIOx = GPIOB;
51 I2CPins.GPIO_PinConfig.GPIO_PinMode = GPIO_PIN_MODE_ALTFCN;
52 I2CPins.GPIO_PinConfig.GPIO_PinOutType = GPIO_PIN_OUT_TYPE_OD;
53 I2CPins.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PIN_PU;
54 I2CPins.GPIO_PinConfig.GPIO_PinAltFcnMode = 4;
55 I2CPins.GPIO_PinConfig.GPIO_PinSpeed = GPIO_PIN_OUT_SPEED_HIGH;
56
57 /* SCL */
58 I2CPins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_6;
59 GPIO_Init(&I2CPins);
60
61 /* SDA */
62 I2CPins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_7;
63 GPIO_Init(&I2CPins);
64} /* End of I2C1_PinsInit */
65
66/**
67 * I2C1_Init()
68 * Brief : Creates an SPI2Handle initializes SPI2 peripheral parameters
69 * Param : None
70 * Retval : None
71 * Note : N/A
72 */
73void I2C1_Init(void)
74{
75
76 I2C1Handle.pI2Cx = I2C1;
77 I2C1Handle.I2C_Config.I2C_ACKEnable = I2C_ACK_ENABLE;
78 I2C1Handle.I2C_Config.I2C_DeviceAddress = MY_ADDR;
79 /* Since STM32 board is master, I2C_DeviceAddress field does not have
80 * to be configured. However, you can assign some dummy value to it if
81 * you wanted to. When selecting the dummy address value, make sure to
82 * avoid using the reserved addresses defined in the I2C specification.
83 */
84 I2C1Handle.I2C_Config.I2C_FMDutyCycle = I2C_FM_DUTY_2;
85 I2C1Handle.I2C_Config.I2C_SCLSpeed = I2C_SCL_SPEED_SM;
86
87 I2C_Init(&I2C1Handle);
88} /* End of I2C1_Init */
89
90/**
91 * GPIO_ButtonInit()
92 * Brief : Initializes a GPIO pin for button
93 * Param : None
94 * Retval : None
95 * Note : N/A
96 */
97void GPIO_ButtonInit(void)
98{
99 GPIO_Handle_TypeDef GPIOBtn;
100
101 /* Zero-out all the fields in the structures (Very important! GPIOLed and GPIOBtn
102 * are local variables whose members may be filled with garbage values before
103 * initialization. These garbage values may set (corrupt) the bit fields that
104 * you did not touch assuming that they will be 0 by default. Do NOT make this
105 * mistake!
106 */
107 memset(&GPIOBtn, 0, sizeof(GPIOBtn));
108
109 /* GPIOBtn configuration */
110 GPIOBtn.pGPIOx = GPIOA;
111 GPIOBtn.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_0;
112 GPIOBtn.GPIO_PinConfig.GPIO_PinMode = GPIO_PIN_MODE_IN;
113 GPIOBtn.GPIO_PinConfig.GPIO_PinSpeed = GPIO_PIN_OUT_SPEED_HIGH; /* Doesn't matter */
114 //GPIOBtn.GPIO_PinConfig.GPIO_PinOutType = GPIO_PIN_OUT_TYPE_PP; /* N/A */
115 GPIOBtn.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PIN_NO_PUPD;
116 /* External pull-down resistor is already present (see the schematic) */
117 GPIO_Init(&GPIOBtn);
118} /* End of GPIO_ButtonInit */
119
120
121int main(int argc, char *argv[])
122{
123 printf("Application Running\n");
124
125 uint8_t cmdCode;
126 uint8_t len;
127
128 /* Create an Rx buffer
129 * (The Arduino sketch is written using the Arduino Wire library. The wire
130 * library has limitation on how many bytes can be transferred or received
131 * in single I2C transaction and the limit is 32 bytes. So, don't
132 * send/receive more than 32 bytes in a single I2C transaction. You may want
133 * to split it into multiple I2C transactions in such cases.)
134 */
135 uint8_t rxBuff[32];
136
137 /* Initialize GPIO pin for button */
138 GPIO_ButtonInit();
139
140 /* Initialize I2C pins */
141 I2C1_PinsInit();
142
143 /* Configure I2C peripheral */
144 I2C1_Init();
145
146 /* Enable I2C peripheral (PE bit gets set here) */
147 I2C_PeriControl(I2C1, ENABLE);
148
149 /* Enable ACK
150 * ACK bit is set and cleared by SW, and cleared by HW when PE=0.
151 * Since PE bit has just been set in the 'I2C_PeriControl()' function,
152 * now you can set the ACK bit. */
153 I2C_ManageACK(I2C1, ENABLE);
154
155 /* Wait for button press */
156 while (1)
157 {
158 /* Wait until button is pressed */
159 while (!GPIO_ReadFromInputPin(GPIOA, GPIO_PIN_0));
160
161 /* Introduce debouncing time for button press */
162 delay();
163
164 /* Send the command to fetch 1 byte length info */
165 cmdCode = 0x51; /* Command code for asking 1 byte length info */
166 I2C_MasterTxBlocking(&I2C1Handle, &cmdCode, 1, SLAVE_ADDR, I2C_REPEATED_START_EN);
167
168 /* Fetch and store the length info received from the slave in @len */
169 I2C_MasterRxBlocking(&I2C1Handle, &len, 1, SLAVE_ADDR, I2C_REPEATED_START_EN);
170
171 /* Send the command to receive the complete data from slave */
172 cmdCode = 0x52; /* Command code for reading complete data from slave */
173 I2C_MasterTxBlocking(&I2C1Handle, &cmdCode, 1, SLAVE_ADDR, I2C_REPEATED_START_EN);
174
175 /* Receive the complete data from slave */
176 I2C_MasterRxBlocking(&I2C1Handle, rxBuff, len, SLAVE_ADDR, I2C_REPEATED_START_DI);
177 /* Since this is the last transaction, disable the repeated start.
178 * After this transaction, you should be able to see the whole
179 * data received in the Rx buffer.
180 */
181
182 /* Print the data received to the console
183 *
184 * Note: To use printf() function to print the data to console,
185 * a couple of settings have to be done.
186 * See, https://jackklee.com/arm-cortex-m3-m4-processor-architecture/using-printf-on-arm-cortex-m3-m4-m7-based-mcus
187 *
188 * You can also use semihosting feature instead.
189 */
190 rxBuff[len + 1] = '\0';
191 /* Due to the I2C_MasterRxBlocking() mechanism, rxBuff does not contain
192 * the terminating null ('\n') character. Add it here!
193 */
194 printf("Data received: %s\n", rxBuff);
195 }
196} /* End of main */
002I2CSlaveTxString.ino
)xxxxxxxxxx
621// Wire Slave Transmitter and receiver
2//Uno, Ethernet A4 (SDA), A5 (SCL)
3
4
5// Include the required Wire library for I2C<br>#include <Wire.h>
6int LED = 13;
7uint8_t active_command = 0xff,led_status=0;
8char name_msg[32] = "Msg from slave.\n";
9
10uint16_t device_id = 0xFF45;
11
12
13
14uint8_t get_len_of_data(void)
15{
16 return (uint8_t)strlen(name_msg);
17}
18void setup() {
19 // Define the LED pin as Output
20 pinMode (LED, OUTPUT);
21
22 // Start the I2C Bus as Slave on address 9
23 Wire.begin(SLAVE_ADDR);
24
25 // Attach a function to trigger when something is received.
26 Wire.onReceive(receiveEvent);
27 Wire.onRequest(requestEvent);
28
29
30}
31
32
33//write
34void receiveEvent(int bytes) {
35 active_command = Wire.read(); // read one character from the I2C
36}
37
38//read
39void requestEvent() {
40
41 if(active_command == 0X51)
42 {
43 uint8_t len = get_len_of_data();
44 Wire.write(&len,1);
45 active_command = 0xff;
46 }
47
48
49 if(active_command == 0x52)
50 {
51 // Wire.write(strlen(name));
52 Wire.write(name_msg,get_len_of_data());
53 // Wire.write((uint8_t*)&name_msg[32],18);
54 active_command = 0xff;
55 }
56 //Wire.write("hello "); // respond with message of 6 bytes
57 // as expected by master
58}
59void loop() {
60
61
62}
The following snapshots are taken using the Logic Analyzer.