Home | Projects | Notes > MCU Peripheral Drivers > I2C Application 1: Master Tx (Blocking) (i2c_01_master_tx_blocking.c
)
i2c_01_master_tx_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 send data to the Arduino board (slave). The data received by the Arduino board shall be displayed on the serial monitor terminal of the Arduino IDE.
Use I2C SCL = 100 kHz (i.e., standard mode)
Use external pull-up resistors (3.3 kΩ) for SDA and SCL line
[!] Note: If you don't have external pull-up resistors, you can also try activating the STM32 I2C pin's internal pull-up resistors.
According to the I2C specification:
Arduino board
STM32 board
Logic level converter
Breadboard and jumper wires
2 pull-up resistors of resistance 3.3 kΩ or 4.7 kΩ (You can also use internal pull-up resistors of the pins in place of external resistors.)
Q: We calculated the Rp(max) to be 3 kΩ. If 3 is the max, then why would 3.3 or 4.7 kΩ resistors work? Wouldn't these be higher resistance than the max? Is there a tolerable range? If so, how do I find this range?
A: Rp(max) refers to the maximum recommended value for the pull-up resistor. If the calculated Rp(max) is 3kOhms, it means that the manufacturer or standard recommends using a pull-up resistor with a value equal to or lower than 3kOhms for reliable operation.
However, using a resistor with a slightly higher value, such as 3.3kOhms or 4.7kOhms, is generally acceptable in practice. The bus capacitance and other factors in the system design can influence the actual pull-up resistor value that works reliably. As long as the chosen resistor value is close to the recommended range and the overall system performance is satisfactory, using 3.3kOhms or 4.7kOhms resistors should be fine.
It's important to note that selecting a pull-up resistor with a significantly higher value than Rp(max) may lead to slower rise times and increased susceptibility to noise or signal integrity issues. On the other hand, choosing a resistor with a significantly lower value may result in excessive current flow and power dissipation. Therefore, it's generally best to stay within the recommended range while considering the specific requirements and constraints of the system.
To work around the voltage level difference, a logic level shifter will be necessary.
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: 001I2CSlaveRxString.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_01_master_tx_blocking.c
Path: Project/Src/
xxxxxxxxxx
1611/*******************************************************************************
2 * File : i2c_01_master_tx_blocking.c
3 * Brief : Program to test I2C master's (blocking) Tx functionality
4 * Author : Kyungjae Lee
5 * Date : Jun 12, 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
17
18
19/* Check Arduino IDE serial monitor */
20/* STM32 Discovery board is master */
21
22/* Global variables */
23I2C_Handle_TypeDef I2C1Handle;
24
25/**
26 * delay()
27 * Brief : Spinlock delays the program execution
28 * Param : None
29 * Retval : None
30 * Note : N/A
31 */
32void delay(void)
33{
34 /* Appoximately ~200ms delay when the system clock freq is 16 MHz */
35 for (uint32_t i = 0; i < 500000 / 2; i++);
36} /* End of delay() */
37
38/**
39 * I2C1_PinsInit()
40 * Brief : Initializes and configures GPIO pins to be used as I2C1 pins
41 * Param : None
42 * Retval : None
43 * Note : N/A
44 */
45void I2C1_PinsInit(void)
46{
47 GPIO_Handle_TypeDef I2CPins;
48
49 I2CPins.pGPIOx = GPIOB;
50 I2CPins.GPIO_PinConfig.GPIO_PinMode = GPIO_PIN_MODE_ALTFCN;
51 I2CPins.GPIO_PinConfig.GPIO_PinOutType = GPIO_PIN_OUT_TYPE_OD;
52 I2CPins.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PIN_PU;
53 I2CPins.GPIO_PinConfig.GPIO_PinAltFcnMode = 4;
54 I2CPins.GPIO_PinConfig.GPIO_PinSpeed = GPIO_PIN_OUT_SPEED_HIGH;
55
56 /* SCL */
57 I2CPins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_6;
58 GPIO_Init(&I2CPins);
59
60 /* SDA */
61 I2CPins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_7;
62 GPIO_Init(&I2CPins);
63} /* End of I2C1_PinsInit */
64
65/**
66 * I2C1_Init()
67 * Brief : Creates an SPI2Handle initializes SPI2 peripheral parameters
68 * Param : None
69 * Retval : None
70 * Note : N/A
71 */
72void I2C1_Init(void)
73{
74
75 I2C1Handle.pI2Cx = I2C1;
76 I2C1Handle.I2C_Config.I2C_ACKEnable = I2C_ACK_ENABLE;
77 I2C1Handle.I2C_Config.I2C_DeviceAddress = MY_ADDR;
78 /* Since STM32 board is master, I2C_DeviceAddress field does not have
79 * to be configured. However, you can assign some dummy value to it if
80 * you wanted to. When selecting the dummy address value, make sure to
81 * avoid using the reserved addresses defined in the I2C specification.
82 */
83 I2C1Handle.I2C_Config.I2C_FMDutyCycle = I2C_FM_DUTY_2;
84 I2C1Handle.I2C_Config.I2C_SCLSpeed = I2C_SCL_SPEED_SM;
85
86 I2C_Init(&I2C1Handle);
87} /* End of I2C1_Init */
88
89/**
90 * GPIO_ButtonInit()
91 * Brief : Initializes a GPIO pin for button
92 * Param : None
93 * Retval : None
94 * Note : N/A
95 */
96void GPIO_ButtonInit(void)
97{
98 GPIO_Handle_TypeDef GPIOBtn;
99
100 /* Zero-out all the fields in the structures (Very important! GPIOLed and GPIOBtn
101 * are local variables whose members may be filled with garbage values before
102 * initialization. These garbage values may set (corrupt) the bit fields that
103 * you did not touch assuming that they will be 0 by default. Do NOT make this
104 * mistake!
105 */
106 memset(&GPIOBtn, 0, sizeof(GPIOBtn));
107
108 /* GPIOBtn configuration */
109 GPIOBtn.pGPIOx = GPIOA;
110 GPIOBtn.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_0;
111 GPIOBtn.GPIO_PinConfig.GPIO_PinMode = GPIO_PIN_MODE_IN;
112 GPIOBtn.GPIO_PinConfig.GPIO_PinSpeed = GPIO_PIN_OUT_SPEED_HIGH; /* Doesn't matter */
113 //GPIOBtn.GPIO_PinConfig.GPIO_PinOutType = GPIO_PIN_OUT_TYPE_PP; /* N/A */
114 GPIOBtn.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PIN_NO_PUPD;
115 /* External pull-down resistor is already present (see the schematic) */
116 GPIO_Init(&GPIOBtn);
117} /* End of GPIO_ButtonInit */
118
119
120int main(int argc, char *argv[])
121{
122 /* Create data to send
123 * (The Arduino sketch is written using the Arduino Wire library. The wire
124 * library has limitation on how many bytes can be transferred or received
125 * in single I2C transaction and the limit is 32 bytes. So, don't
126 * send/receive more than 32 bytes in a single I2C transaction. You may want
127 * to split it into multiple I2C transactions in such cases.)
128 */
129 uint8_t data[] = "I2C master Tx testing\n";
130
131 /* Initialize GPIO pin for button */
132 GPIO_ButtonInit();
133
134 /* Initialize I2C pins */
135 I2C1_PinsInit();
136
137 /* Configure I2C peripheral */
138 I2C1_Init();
139
140 /* Enable I2C peripheral */
141 I2C_PeriControl(I2C1, ENABLE);
142
143 /* Wait for button press */
144 while (1)
145 {
146 /* Wait until button is pressed */
147 while (!GPIO_ReadFromInputPin(GPIOA, GPIO_PIN_0));
148
149 /* Introduce debouncing time for button press */
150 delay();
151
152 /* Send data to slave
153 * Note: 'I2C_MasterTxBlocking()' API has been modified for handling
154 * extended feature (i.e., repeated start) that are used
155 * in the next level applications. Another parameter has been
156 * added after @slaveAddr.
157 * TODO: Update the following API call to make this application work!
158 */
159 //I2C_MasterTxBlocking(&I2C1Handle, data, strlen((char *)data), SLAVE_ADDR);
160 }
161} /* End of main */
001I2CSlaveRxString.ino
)xxxxxxxxxx
431// Wire Slave Receiver
2//Uno, Ethernet A4 (SDA), A5 (SCL)
3
4
5
6
7int LED = 13;
8char rx_buffer[32] ;
9uint32_t cnt =0;
10uint8_t message[50];
11void setup() {
12
13 Serial.begin(9600);
14 // Define the LED pin as Output
15 pinMode (LED, OUTPUT);
16
17 // Start the I2C Bus as Slave on address 0X69
18 Wire.begin(MY_ADDR);
19
20 // Attach a function to trigger when something is received.
21 Wire.onReceive(receiveEvent);
22
23 sprintf(message,"Slave is ready : Address 0x%x",MY_ADDR);
24 Serial.println((char*)message );
25 Serial.println("Waiting for data from master");
26}
27
28void loop(void)
29{
30
31}
32
33void receiveEvent(int bytes)
34{
35 while( Wire.available() )
36 {
37 rx_buffer[cnt++] = Wire.read();
38 }
39 rx_buffer[cnt] = '\0';
40 cnt=0;
41 Serial.print("Received:");
42 Serial.println((char*)rx_buffer);
43}