Home | Projects | Notes > MCU Peripheral Drivers > SPI Application 2: Master Tx (Blocking) (spi_02_master_tx_blocking.c
)
spi_02_master_tx_blocking.c
)
SPI master(STM) and SPI slave(Arduino) communication
When the button on the master is pressed, master shall send a string of data to the slave in connection. The data received by the slave shall be displayed on the slave's serial port.
Requirements
Use SPI full duplex mode
ST board will be in SPI master mode, and Arduino board will be in SPI slave mode.
Use DFF = 0
Use hardware slave management (SSM = 0)
SCLK speed = 500 KHz, fclk = 16MHz
In this application, master is not going to receive anything from the slave. So, configuring the MISO pin is not necessary.
The slave does not know how many bytes of data master is going to send. So, the master must first send the number of bytes the slave should expect to receive.
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.
For this application, MOSI, SCK and NSS pins will be used. Find out the GPIO pins over which SPI2 can communicate! Look up the "Alternate function mapping" table in the datasheet.
SPI2_MOSI
SPI2_SCK
SPI2_MISO
SPI2_NSS
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 - SCLK
CH1 - MOSI
CH2 - MISO
CH3 - NSS
GND - Common GND of the bread board
Sketch name: 001SPISlaveRxString.ino
As soon as you download this sketch to the Arduino board, it will operate as a slave.
spi_02_master_tx_blocking.c
Path: Project/Src/
xxxxxxxxxx
2001/*******************************************************************************
2 * File : spi_02_master_tx_blocking.c
3 * Brief : Program to test SPI master's Tx (blocking) functionality
4 * (with slave)
5 * Author : Kyungjae Lee
6 * Date : Jun 02, 2023
7 ******************************************************************************/
8
9/**
10 * Pin selection for SPI communication
11 *
12 * SPI2_SCK - PB13 (AF5)
13 * SPI2_MOSI - PB15 (AF5)
14 * SPI2_MISO - PB14 (AF5)
15 * SPI2_NSS - PB12 (AF5)
16 */
17
18/* strlen() */
19
20
21/**
22 * delay()
23 * Brief : Spinlock delays the program execution
24 * Param : None
25 * Retval : None
26 * Note : N/A
27 */
28void delay(void)
29{
30 /* Appoximately ~200ms delay when the system clock freq is 16 MHz */
31 for (uint32_t i = 0; i < 500000 / 2; i++);
32} /* End of Delay */
33
34/**
35 * SPI2_PinsInit()
36 * Brief : Initializes and configures GPIO pins to be used as SPI2 pins
37 * Param : None
38 * Retval : None
39 * Note : N/A
40 */
41void SPI2_PinsInit(void)
42{
43 GPIO_Handle_TypeDef SPI2Pins;
44
45 /* Zero-out all the fields in the structures (Very important! SPI2Pins
46 * is a local variables whose members may be filled with garbage values before
47 * initialization. These garbage values may set (corrupt) the bit fields that
48 * you did not touch assuming that they will be 0 by default. Do NOT make this
49 * mistake!
50 */
51 memset(&SPI2Pins, 0, sizeof(SPI2Pins));
52
53 SPI2Pins.pGPIOx = GPIOB;
54 SPI2Pins.GPIO_PinConfig.GPIO_PinMode = GPIO_PIN_MODE_ALTFCN;
55 SPI2Pins.GPIO_PinConfig.GPIO_PinAltFcnMode = 5;
56 SPI2Pins.GPIO_PinConfig.GPIO_PinOutType = GPIO_PIN_OUT_TYPE_PP;
57 /* I2C - Open-drain only!, SPI - Push-pull okay! */
58 SPI2Pins.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PIN_NO_PUPD; /* Optional */
59 SPI2Pins.GPIO_PinConfig.GPIO_PinSpeed = GPIO_PIN_OUT_SPEED_HIGH; /* Medium or slow ok as well */
60
61 /* SCLK */
62 SPI2Pins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_13;
63 GPIO_Init(&SPI2Pins);
64
65 /* MOSI */
66 SPI2Pins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_15;
67 GPIO_Init(&SPI2Pins);
68
69 /* MISO (Not required for this application, save it for other use) */
70 //SPI2Pins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_14;
71 //GPIO_Init(&SPI2Pins);
72
73 /* NSS */
74 SPI2Pins.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_12;
75 GPIO_Init(&SPI2Pins);
76} /* End of SPI2_PinsInit */
77
78/**
79 * SPI2_Init()
80 * Brief : Creates an SPI2Handle and initializes SPI2 peripheral parameters
81 * Param : None
82 * Retval : None
83 * Note : N/A
84 */
85void SPI2_Init(void)
86{
87 SPI_Handle_TypeDef SPI2Handle;
88
89 /* Zero-out all the fields in the structures (Very important! SPI2Handle
90 * is a local variables whose members may be filled with garbage values before
91 * initialization. These garbage values may set (corrupt) the bit fields that
92 * you did not touch assuming that they will be 0 by default. Do NOT make this
93 * mistake!
94 */
95 memset(&SPI2Handle, 0, sizeof(SPI2Handle));
96
97 SPI2Handle.pSPIx = SPI2;
98 SPI2Handle.SPI_Config.SPI_BusConfig = SPI_BUS_CONFIG_FULL_DUPLEX;
99 SPI2Handle.SPI_Config.SPI_DeviceMode = SPI_DEVICE_MODE_MASTER;
100 SPI2Handle.SPI_Config.SPI_SCLKSpeed = SPI_SCLK_SPEED_PRESCALAR_32; /* Generates 500KHz SCLK */
101 /* Min prescalar -> maximum clk speed */
102 SPI2Handle.SPI_Config.SPI_DFF = SPI_DFF_8BITS;
103 SPI2Handle.SPI_Config.SPI_CPOL = SPI_CPOL_LOW;
104 SPI2Handle.SPI_Config.SPI_CPHA = SPI_CPHA_LOW;
105 SPI2Handle.SPI_Config.SPI_SSM = SPI_SSM_DI; /* HW slave mgmt enabled (SSM = 0) for NSS pin */
106
107 SPI_Init(&SPI2Handle);
108} /* End of SPI2_Init */
109
110/**
111 * GPIO_ButtonInit()
112 * Brief : Initializes a GPIO pin for button
113 * Param : None
114 * Retval : None
115 * Note : N/A
116 */
117void GPIO_ButtonInit(void)
118{
119 GPIO_Handle_TypeDef GPIOBtn;
120
121 /* Zero-out all the fields in the structures (Very important! GPIOBtn
122 * is a local variables whose members may be filled with garbage values before
123 * initialization. These garbage values may set (corrupt) the bit fields that
124 * you did not touch assuming that they will be 0 by default. Do NOT make this
125 * mistake!
126 */
127 memset(&GPIOBtn, 0, sizeof(GPIOBtn));
128
129 /* GPIOBtn configuration */
130 GPIOBtn.pGPIOx = GPIOA;
131 GPIOBtn.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_0;
132 GPIOBtn.GPIO_PinConfig.GPIO_PinMode = GPIO_PIN_MODE_IN;
133 GPIOBtn.GPIO_PinConfig.GPIO_PinSpeed = GPIO_PIN_OUT_SPEED_HIGH; /* Doesn't matter */
134 //GPIOBtn.GPIO_PinConfig.GPIO_PinOutType = GPIO_PIN_OUT_TYPE_PP; /* N/A */
135 GPIOBtn.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_PIN_NO_PUPD;
136 /* External pull-down resistor is already present (see the schematic) */
137 GPIO_Init(&GPIOBtn);
138} /* End of GPIO_ButtonInit */
139
140
141int main(int argc, char *argv[])
142{
143 char msg[] = "Hello world";
144
145 /* Initialize and configure GPIO pin for user button */
146 GPIO_ButtonInit();
147
148 /* Initialize and configure GPIO pins to be used as SPI2 pins */
149 SPI2_PinsInit();
150
151 /* Initialize SPI2 peripheral parameters */
152 SPI2_Init();
153 /* At this point, all the required parameters are loaded into SPIx control registers.
154 * But, this does not mean that SPI2 peripheral is enabled.
155 *
156 * SPI configuration must be completed before it is enabled. When SPI is enabled, it
157 * will be busy communicating with other device(s) and will not allow modifying its
158 * control registers.
159 */
160
161 /* Enable NSS output (Set SPI_CR2 bit[2] SSOE - Slave Select Output Enable) */
162 SPI_SSOEConfig(SPI2, ENABLE);
163 /* Setting SSOE bit to 1 enables the NSS output.
164 * The NSS pin is automatically managed by the hardware.
165 * i.e., When SPE = 1, NSS will be pulled to low, and when SPE = 0, NSS will be
166 * pulled to high.
167 */
168
169 while (1)
170 {
171 /* Wait until button is pressed */
172 while (!GPIO_ReadFromInputPin(GPIOA, GPIO_PIN_0));
173
174 /* Introduce debouncing time for button press */
175 delay();
176
177 /* Enable SPI2 peripheral (Set SPI_CR1 bit[6] SPE - Peripheral enabled) */
178 SPI_PeriControl(SPI2, ENABLE);
179
180 /* Arduino sketch expects 1 byte of length information followed by data */
181 /* Send length information to the slave first */
182 uint8_t msgLen = strlen(msg);
183 SPI_TxBlocking(SPI2, &msgLen, 1);
184 /* Send data */
185 SPI_TxBlocking(SPI2, (uint8_t *)msg, strlen(msg));
186
187 /* Wait until SPI no longer busy */
188 while (SPI2->SR & (0x1 << SPI_SR_BSY));
189 /* SPI_SR bit[7] - BSY (Busy flag)
190 * 0: SPI (or I2S) not busy
191 * 1: SPI (or I2S) is busy in communication or Tx buffer is not empty
192 * This flag is set and cleared by hardware.
193 */
194
195 /* Disable SPI2 peripheral (Terminate communication) */
196 SPI_PeriControl(SPI2, DISABLE);
197 }
198
199 return 0;
200} /* End of main */
The master's clock frequency has been adjusted from 2 MHz (prescalar = 8) to 500 KHz (prescalar = 32) to be compatible with the baudrate (1200) of the slave.
001SPISlaveRxString.ino
)xxxxxxxxxx
881/* SPI Slave Demo
2
3 *
4 * SPI pin numbers:
5 * SCK 13 // Serial Clock.
6 * MISO 12 // Master In Slave Out.
7 * MOSI 11 // Master Out Slave In.
8 * SS 10 // Slave Select . Arduino SPI pins respond only if SS pulled low by the master
9 *
10
11 */
12
13
14
15
16
17
18
19char dataBuff[500];
20
21//Initialize SPI slave.
22void SPI_SlaveInit(void)
23{
24 // Initialize SPI pins.
25 pinMode(SCK, INPUT);
26 pinMode(MOSI, INPUT);
27 pinMode(MISO, OUTPUT);
28 pinMode(SS, INPUT);
29 //make SPI as slave
30
31 // Enable SPI as slave.
32 SPCR = (1 << SPE);
33}
34
35//This function returns SPDR Contents
36uint8_t SPI_SlaveReceive(void)
37{
38 /* Wait for reception complete */
39 while(!(SPSR & (1<<SPIF)));
40 /* Return Data Register */
41 return SPDR;
42}
43
44
45//sends one byte of data
46void SPI_SlaveTransmit(char data)
47{
48 /* Start transmission */
49 SPDR = data;
50 /* Wait for transmission complete */
51 while(!(SPSR & (1<<SPIF)));
52}
53
54
55// The setup() function runs right after reset.
56void setup()
57{
58 // Initialize serial communication
59 Serial.begin(1200);
60 // Initialize SPI Slave.
61 SPI_SlaveInit();
62 Serial.println("Slave Initialized");
63}
64
65// The loop function runs continuously after setup().
66void loop()
67{
68 uint32_t i;
69 uint16_t dataLen = 0;
70 Serial.println("Slave waiting for ss to go low");
71 while(digitalRead(SS));
72
73 i = 0;
74 dataLen = SPI_SlaveReceive();
75 for(i = 0 ; i < dataLen ; i++ )
76 {
77 dataBuff[i] = SPI_SlaveReceive();
78 }
79
80
81 // Serial.println(String(i,HEX));
82 dataBuff[i] = '\0';
83
84 Serial.println("Rcvd:");
85 Serial.println(dataBuff);
86 Serial.print("Length:");
87 Serial.println(dataLen);
88}
Original baudrate (9600) has been changed to 1200 since the communication didn't work with the original baudrate. (In the STM32 application, the master's clock frequency has been adjusted from 2 MHz to 500 KHz accordingly.)
The following snapshots are taken using the Logic Analyzer.
Notice that as soon as
SPE
is set to 0,NSS
is pulled to GND (LOW), and as soon asSPE
is set to 1,NSS
is pulled to HIGH automatically by the hardware.
Although data was successfully sent out by the STM32 Discovery board (as shown in the snapshot above), the Arduino Uno R3 board didn't seem to receive the data.
SPI pins of the STM32 board was set to 2MHz, and Arduino board serial monitor baudrate was set to 9600.
Test and debug this application again!