Library 05- SPI for STM32F4
Let’s say something about SPI. SPI (or Serial Peripheral Interface) is a protocol named by Motorola. With him you can control sensors, SD card and much more.
SPI protocol works in a ways where there is one master and multiple slaves, In other words, master is our STM32F429 Discovery board and let’s say, SD card is slave.
SPI uses 3 main wires. Because more slaves can be connected to one master on same SPI peripheral, there is 4th pin called SS. SPI pin names are:
- SCLK: Serial clock, clock for synchronization slave with master
- MOSI: Master Out Slave In. data sent from master to slave
- MISO: Master In Slave Out, data send from slave to master
- SS: Slave Select, with this pin low, you enable slave. This is useful, when you have more devices on same SPI. If you have more devices as slaves, you have to manually set pins for every slave separately. When you want to send data to some slave, you put SS pin for that slave low, send data and put it back high. If SS pin is HIGH, slave does not detect your incoming data.
Our STM32F429 Discovery supports 6 SPIs. Except SPI6, all SPIs have at least 2 configurable pins, which you want to use. I searched for pins used for peripheral and show to you in bottom table.
Pins pack 1 | Pins pack 2 | Pins pack 3 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
SPIx | MOSI | MISO | SCK | MOSI | MISO | SCK | MOSI | MISO | SCK | APB |
SPI1 | PA7 | PA6 | PA5 | PB5 | PB4 | PB3 | 2 | |||
SPI2 | PC3 | PC2 | PB10 | PB15 | PB14 | PB13 | PI3 | PI2 | PI0 | 1 |
SPI3 | PB5 | PB4 | PB3 | PC12 | PC11 | PC10 | 1 | |||
SPI4 | PE6 | PE5 | PE2 | PE14 | PE13 | PE12 | 2 | |||
SPI5 | PF9 | PF8 | PF7 | PF11 | PH7 | PH6 | 2 | |||
SPI6 | PG14 | PG12 | PG13 | 2 |
On the 8th column is APB. APB means Advanced Peripheral Bus. Inside our mcu are APB1 and APB2. This two are used for peripheral clock configuration. There is also AHB (Advanced High-performance Bus) for GPIO pins.
The difference between APB1 and APB2 is:
- APB1 has clock 4x slower than processor speed, in our case we have 180MHz core clock, APB1 has 45MHz
- APB1 communication bus has 45MHz, so SPI works also on 45MHz
- And because minimal prescaler for SPI is 2, max frequency is 45/2 = 22.5 MHz
- SPI Clock = APBx / prescaler = 45MHz / 2 = 22.5MHz
- By default, prescaler 32 is used:
- SPI Clock = APBx / prescaler = 45MHz / 32 = 1.40625MHz
- APB2 has clock 2x slower than processor speed, in our case we have 180MHz core clock, APB2 has 90MHz
- APB2 communication bus has 90MHz
- And because minimal prescaler for SPI is 2, max frequency is 45MHz
- SPI Clock = APBx / prescaler = 90MHz / 2 = 45MHz
- By default, prescaler 32 is used:
- SPI Clock = APBx / prescaler = 90MHz / 32 = 2.8125MHz
This means that SPI1, SPI4, SPI5 and SPI6 have maximum frequency of 45MHz, SPI2 and SPI3 have 22.5MHz. In both cases frequency can be slower, set with internal prescalers.
Library
Features
- All possible SPI’s available on STM32F4
- 8- or 16- bit SPI mode
- Default SPI configuration
- Master mode
- 8 data bits
- prescaler set to divide APBx clock with 32
- Clock is low on idle and data sampled at rising edge (SPI Mode 0)
- first bit is MSB bit
- 2 wires full-duplex
- SS pin is software configurable
Dependencies
- CMSIS
- STM32F4xx
- STM32F4xx RCC
- STM32F4xx GPIO
- STM32F4xx SPI
- TM
- TM GPIO
- defines.h
- attributes.h
- TM GPIO
You can configure your settings in defines.h file:
1 2 3 4 5 6 7 8 9 10 11 |
//Options can be overwriten in defines.h file #define TM_SPIx_PRESCALER SPI_BaudRatePrescaler_32 //Specify datasize #define TM_SPIx_DATASIZE SPI_DataSize_8b //Specify which bit is first #define TM_SPIx_FIRSTBIT SPI_FirstBit_MSB //Mode, master or slave #define TM_SPIx_MASTERSLAVE SPI_Mode_Master //Specify mode of operation, clock polarity and clock phase //Modes 0 to 3 are possible #define TM_SPIx_MODE TM_SPI_Mode_0 |
If you want to initialize SPI1 and set pins PA7, PA6 and PA5 for MOSI, MISO and SCK then you would call something like that:
1 2 |
//Initialize SPI1 with pins pack 1 TM_SPI_Init(SPI1, TM_SPI_PinsPack_1); |
or with pins pack 2
1 2 |
//Initialize SPI1 with pins pack 2 TM_SPI1_Init(TM_SPI_PinsPack_2); |
This principle works for all 6 SPIs.
Then you want to send some data over SPI. You can do this with
1 2 3 4 |
//Send data at SPIx where x is from 1 to 6. //8bit data to be sent over SPIx //Data received from slave is returned uint8_t TM_SPI_Send(SPI_TypeDef* SPIx, uint8_t data); |
This function sends 8bit data over SPIx. When SPI finished transmission, 8bit received data is returned.
Functions and enumerations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
/** * Every SPIx has 3 pins for MISO, MOSI and SCK */ typedef enum { TM_SPI_PinsPack_1, TM_SPI_PinsPack_2, TM_SPI_PinsPack_3, TM_SPI_PinsPack_Custom } TM_SPI_PinsPack_t; /** * Initialize SPIx * * Parameters: * - SPI_TypeDef* SPIx: * SPI 1 - 6 * - TM_SPI_PinsPack_t pinspack: select pins pack to use * - TM_SPI_PinsPack_1 * - TM_SPI_PinsPack_2 * - TM_SPI_PinsPack_3 */ extern void TM_SPI_Init(SPI_TypeDef* SPIx, TM_SPI_PinsPack_t pinspack); /** * Initialize SPIx with SPI mode * * Parameters: * - SPI_TypeDef* SPIx: * SPI 1 - 6 * - TM_SPI_PinsPack_t pinspack: select pins pack to use * - TM_SPI_PinsPack_1 * - TM_SPI_PinsPack_2 * - TM_SPI_PinsPack_3 * - TM_SPI_Mode_t SPI_Mode: * SPI mode to be initialized * * No return */ extern void TM_SPI_InitWithMode(SPI_TypeDef* SPIx, TM_SPI_PinsPack_t pinspack, TM_SPI_Mode_t SPI_Mode); /** * Initialize SPIx with most used SPI features * * Parameters: * - SPI_TypeDef* SPIx: * SPI, x valid 1 - 6 * - TM_SPI_PinsPack_t pinspack: select pins pack to use * - TM_SPI_PinsPack_1 * - TM_SPI_PinsPack_2 * - TM_SPI_PinsPack_3 * - TM_SPI_Mode_t SPI_Mode: * SPI mode to be initialized, CPOL and CPHA settings, Mode 0 to 3 * - uint16_t SPI_BaudRatePrescaler: * SPI baudrate prescaler values. SPI_BaudRatePrescaler_x is valid, valid x is: 2, 4, 8, 16, 32, 64, 128, 256 * - uint16_t SPI_Mode: * Master or slave mode: SPI_Mode_Master or SPI_Mode_Slave valid. * - uint16_t SPI_FirstBit: * Select first bit in your transmission, SPI_FirstBit_MSB or SPI_FirstBit_LSB are valid * * No return */ extern void TM_SPI_InitFull(SPI_TypeDef* SPIx, TM_SPI_PinsPack_t pinspack, uint16_t SPI_BaudRatePrescaler, TM_SPI_Mode_t SPI_Mode_t, uint16_t SPI_Mode, uint16_t SPI_FirstBit); /** * This function can be used to calculate smaller prescaler for your clock frequency * SPI has 8 prescalers available, 2,4,6,...,128,256 * * This function will return you a bits you must set in your CR1 register. * * Imagine, you can use 20MHz max clock in your system, your system is running on 168MHz, and you use SPI on APB2 bus. * On 168 and 180MHz devices, APB2 works on Fclk/2, so 84 and 90MHz. * So, if you calculate this, prescaler will need to be 84MHz / 20MHz = 4.xx, but if you use 4 prescaler, then you will be over 20MHz. * You need 8 prescaler then. This function will calculate this. * * Parameters: * - SPI_TypeDef* SPIx: * SPI, x valid 1 - 6 * - uint32_t MAX_SPI_Frequency: * Maximal frequency you can use on your device on SPI port * * Returns bits you must set in SPI CR1 register. */ extern uint16_t TM_SPI_GetPrescalerFromMaxFrequency(SPI_TypeDef* SPIx, uint32_t MAX_SPI_Frequency); /** * Send and receive data over SPI * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint8_t data: data to be sent * * Returns: Data returned from slave */ extern uint8_t TM_SPI_Send(SPI_TypeDef* SPIx, uint8_t data); /** * Send and receive multiple data bytes over SPI * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint8_t dataOut: pointer to data to be sent out * - uint8_t dataIn: pointer to received data * - uint16_t count: number of bytes to send * * No return */ extern void TM_SPI_SendMulti(SPI_TypeDef* SPIx, uint8_t* dataOut, uint8_t* dataIn, uint16_t count); /** * Write multiple data via SPI * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint8_t dataOut: pointer to data to be sent out * - uint16_t count: number of bytes to send * * No return */ extern void TM_SPI_WriteMulti(SPI_TypeDef* SPIx, uint8_t* dataOut, uint16_t count); /** * Send and receive multiple data bytes over SPI * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint8_t dataIn: pointer to received data * - uint8_t dummy: dummy byte to be sent to SPI * - uint16_t count: number of bytes to receive * * No return */ extern void TM_SPI_ReadMulti(SPI_TypeDef* SPIx, uint8_t *dataIn, uint8_t dummy, uint16_t count); /** * Send and receive data over SPI in 16-bit SPI mode * Selected SPI must be set in 16-bit mode * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint16_t data: data to be sent * * Returns: Data returned from slave */ extern uint16_t TM_SPI_Send16(SPI_TypeDef* SPIx, uint16_t data); /** * Send and receive multiple data bytes over SPI in 16-bit SPI mode * Selected SPI must be set in 16-bit mode * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint16_t dataOut: pointer to data to be sent out * - uint16_t dataIn: pointer to received data * - uint16_t count: number of bytes to send * * No return */ extern void TM_SPI_SendMulti16(SPI_TypeDef* SPIx, uint16_t* dataOut, uint16_t* dataIn, uint16_t count); /** * Write multiple data via SPI in 16-bit SPI mode * Selected SPI must be set in 16-bit mode * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint16_t dataOut: pointer to data to be sent out * - uint16_t count: number of bytes to send * * No return */ extern void TM_SPI_WriteMulti16(SPI_TypeDef* SPIx, uint16_t* dataOut, uint16_t count); /** * Send and receive multiple data bytes over SPI in 16-bit SPI mode * Selected SPI must be set in 16-bit mode * * Parameters: * - SPI_TypeDef* SPIx: Select SPI which will operate with data * - uint16_t dataIn: pointer to received data * - uint16_t dummy: dummy 16bit to be sent to SPI * - uint16_t count: number of bytes to receive * * No return */ extern void TM_SPI_ReadMulti16(SPI_TypeDef* SPIx, uint16_t* dataIn, uint16_t dummy, uint16_t count); /** * Callback for custom pins initialization. * * When you call TM_SPI_Init() function, and if you pass TM_SPI_PinsPack_Custom to function, * then this function will be called where you can initialize custom pins for SPI peripheral. * * Parameters: * - SPI_TypeDef* SPIx: * SPI for which initialization will be set * * With __weak parameter to prevent link errors if not defined by user * * No return */ extern __weak void TM_SPI_InitCustomPinsCallback(SPI_TypeDef* SPIx); |
Main example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
/** * Keil project for SPI * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 * @packs STM32F4xx Keil packs version 2.2.0 or greater required * @stdperiph STM32F4xx Standard peripheral drivers version 1.4.0 or greater required */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_spi.h" int main(void) { /* Initialize system */ SystemInit(); /* Initialize SPI */ /* SCK = PA5, MOSI = PA7, MISO = PA6 */ TM_SPI_Init(SPI1, TM_SPI_PinsPack_1); /* Send 0x15 over SPI1 */ TM_SPI_Send(SPI1, 0x15); while(1) { } } |
Project is available on Github, download library below.
SPI library for all SPI peripherals
Recent comments