SPI using Registers in STM32
This is another tutorial in the register based series for STM32, and today we will cover the SPI. I will cover both sending and receiving data in the master mode. Also I am going to use the ADXL345 for the demonstration
SPI communication uses 4 Pins i.e
- MOSI Master Out Slave In is used to send the data to the Slave
- MISO Master in Slave out is used to receive the data from the slave
- SCK Serial Clock is used to keep the master and slave in sync
- SS Slave select is used to select and unselect the slave
- In Full-duplex mode, all four pins are used
- There are separate pins for send (MOSI) and for receive (MISO)
- When we write data into the Data Register of the master, this data goes into the Tx Shift Register of the master
- Which then gets shifted to the Rx Shift Register of the Slave
- And finally goes into the data register of the slave, from where it is read by the slave device.
Slave select (NSS) pin management
In Master mode, Slave Select management can be done using two different ways
- Software NSS management : This means that we want to manage the slave using the software. So we don’t have to rely on the physical NSS pin, and instead we can use any pin from the mcu as the Slave Select pin.
- Hardware NSS management : Here we have to use the actual NSS pin (fixed pin) to control the slave device. The NSS signal is driven low as soon as the SPI is enabled in master mode, and is kept low until the SPI is disabled.
We will be using the software NSS management in this tutorial.
The configuration for the SPI is relatively simpler than what we have seen in other peripherals. Here we only have to modify 1 register. Let’s see the steps to configure the SPI
Like I mentioned, it’s very simple to configure the SPI. See the configuration code below
- First of all enable the SPI 1 clock in the RCC_APB2ENR Register
- Now will modify the CPOL and CPHA bits according to the slave requirement ( watch video if you don’t know what this means)
- Enable the master mode
- Next is the prescalar. The slave device can support upto 5 MBits/s, so I am keeping the presclalar of 16
This will divide the APB2 clock (80 MHz) by 16, and bring it down to 5MHz
- The data format is selected as MSB first
- Then we will configure the software slave management. Like I said in the beginning, we will use the software to control the slave, so we need to set these SSM and SSI bits
- Next we will configure the full duplex mode by resetting the RXONLY (10th) bit
- Next is the data length bit, and we will keep the 8 bit data.
- Then reset the entire CR2 register, since we will not be setting up any DMA or Interrupt in this tutorial
- Before Transmitting the data, we will wait for the TXE (Transmit Register Empty) bit in the Status Register to set. This indicates that the Transmit Register is empty and we can load the data
- After transmitting the data, we will wait for the BSY (Busy) bit in the Status Register to reset. This will indicate that the SPI is not busy in communication anymore and we can proceed with other things
- Here we are waiting for the TXE bit to set before sending the data
- To send the data, we have to copy it in the DR (Data Register)
- After all the data has been transmitted, we will wait for the busy flag to reset
- Before exiting the Transmit function, we will make a dummy read to the data register and the status register.
- This is to clear the overrun flag, which gets set when we transfer the data to the device.
The receiving process is as follows:-
- Before receiving the data, we must send some dummy byte to the device. Since the slave device is in the transmission mode, this dummy byte does not change the registers or the data of the registers
- On receiving the dummy byte, the device transmits one byte of data.
- This will set the RXNE (Receive Buffer Not Empty) bit. This will indicate that there is some data in the Data Register, which is ready to be read.
- And later we can copy the data from the Data Register into our buffer. This clears the RXNE bit
- As you can see above, we wait for the busy flag to reset.
- Then we send some dummy byte to the device. I am transmitting 0
- next we wait for the RXNE bit to set
- And finally copy the data from the DR into our buffer
F4 GPIO CONFIG
I am using the following Pins for the SPI 1:
- PA5 -> CLK
- PA6 -> MISO
- PA7 -> MOSI
- PA9 -> Slave Select
The first three pins need to be set in the alternate function mode, and the PA9 will be set as the general output pin.
The Alternate function mode depends on the microcontroller. For example, let’s see the Alternate function description for F446RE
You can see in the picture above that the SPI 1 is the AF5. Also since I am using Pins PA5, 6 and 7, I am going to use the AFRL Register. In case you are using the Pins 8 to 15, you must use the AFRH register
Let’s see the configuration now
- First we will enable the GPIOA clock
- Then select the Alternate Function mode for PA5, PA6 and PA7 and the output mode for PA9
- Next we will set the speed for all four pins. The speed is set to HIGH Speed
- And finally configure the Alternate Function in the AFR (AFRL)
F103 GPIO CONFIG
The Configuration in F103 is slightly different. F103 do not have the Alternate Function Registers and therefore we have to use the Control Register to configure the SPI pins
Below is the Picture from the F103 Reference manual, suggesting the configuration for the SPI
Since we are using SPI in Master Full Duplex mode, we will configure the pins accordingly. I have already covered how to configure the GPIO in F103, you can check the article STM32F103 Clock Setup using Registers
- Here we will enable the GPIOA clock
- Then reset the entire Control Register
- Now set the AF output Push Pull mode for PA5 -> Clock
- Do the Same for the PA7 -> MOSI
- We need to set the Alternate Function input mode for the PA6 -> MISO
- And set the general Output mode for the PA4 -> SS
Here are some other function that we will be using in this tutorial.
Here I am keeping the focus on the SPI, so we will see only the SPI related part. I am not going to talk about how the ADXL actually works. You can check out the other article I wrote about it ADXL345 Accelerometer and STM32
- To ENABLE the SPI, we will set the SPE bit (bit 6 in CR1)
- To DISABLE SPI, we will simply clear that bit
- The slave can be selected by Pulling the SS pin LOW
- And to release the Slave, we have to Pull the SS pin HIGH
In order to communicate with the slave device, we have to follow the procedure as mentioned below
To send the data
- Select the Slave device
- Send the register address
- Send the data
- Release the slave device
To read the data
- Select the slave device
- Send the Register address, from where you want to read
- Read the data
- Unselect the slave device
- Here we will configure the system, SPI, GPIO etc.
- Then Initialize the ADXL
- Read 6 Bytes of data from the ADXL
- And later convert this data into the acceleration values
You can check out the picture from the keil debugger is shown below
The picture shows the values of the acceleration in the x, y and z axis