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.cPath: Project/Src/
xxxxxxxxxx2001/*******************************************************************************2 * File : spi_02_master_tx_blocking.c3 * Brief : Program to test SPI master's Tx (blocking) functionality4 * (with slave)5 * Author : Kyungjae Lee6 * Date : Jun 02, 20237 ******************************************************************************/8
9/**10 * Pin selection for SPI communication11 *12 * SPI2_SCK - PB13 (AF5)13 * SPI2_MOSI - PB15 (AF5)14 * SPI2_MISO - PB14 (AF5)15 * SPI2_NSS - PB12 (AF5)16 */17
18/* strlen() */1920
21/**22 * delay()23 * Brief : Spinlock delays the program execution24 * Param : None25 * Retval : None26 * Note : N/A27 */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 pins37 * Param : None38 * Retval : None39 * Note : N/A40 */41void SPI2_PinsInit(void)42{43 GPIO_Handle_TypeDef SPI2Pins;44
45 /* Zero-out all the fields in the structures (Very important! SPI2Pins46 * is a local variables whose members may be filled with garbage values before47 * initialization. These garbage values may set (corrupt) the bit fields that48 * you did not touch assuming that they will be 0 by default. Do NOT make this49 * 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 parameters81 * Param : None82 * Retval : None83 * Note : N/A84 */85void SPI2_Init(void)86{87 SPI_Handle_TypeDef SPI2Handle;88
89 /* Zero-out all the fields in the structures (Very important! SPI2Handle90 * is a local variables whose members may be filled with garbage values before91 * initialization. These garbage values may set (corrupt) the bit fields that92 * you did not touch assuming that they will be 0 by default. Do NOT make this93 * 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 button113 * Param : None114 * Retval : None115 * Note : N/A116 */117void GPIO_ButtonInit(void)118{119 GPIO_Handle_TypeDef GPIOBtn;120
121 /* Zero-out all the fields in the structures (Very important! GPIOBtn122 * is a local variables whose members may be filled with garbage values before123 * initialization. These garbage values may set (corrupt) the bit fields that124 * you did not touch assuming that they will be 0 by default. Do NOT make this125 * 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, it157 * will be busy communicating with other device(s) and will not allow modifying its158 * 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 be166 * 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 busy191 * 1: SPI (or I2S) is busy in communication or Tx buffer is not empty192 * 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)xxxxxxxxxx881/* SPI Slave Demo2
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 master9 *10 11 */12131415161718
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 slave30 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
SPEis set to 0,NSSis pulled to GND (LOW), and as soon asSPEis set to 1,NSSis 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!
