
In this eleventh part of Board Support Package (BSP), we shall develop SPI driver to send multiple bytes with timeout.
In this guide, we shall cover the following:
- Developing the header file.
- Developing the source file.
- Code test.
- Results.
1. Developing The Header File:
Before heading into developing the header file, you can refer to this guide about SPI and how to configure it.
Create new header file with name of spi_bsp.h.
Within the header file, include the following:
#include "stdint.h" #include "stm32f4xx.h"
Since there are multiple SPI in STM32F411, we shall declare the following enum for clock enable functionality:
typedef enum
{
spi1=0,
spi2=1,
spi3=2,
spi4=3,
spi5=4
}SPI_EnableTypedef;Also, we shall declare enum to handle the status of the transmission as following:
typedef enum
{
SPI_Succuss=0,
SPI_Timeout=1,
}SPI_StatusTypedef;Also, declare enum to handle the configuration as following:
typedef enum
{
eight_bit=0,
sixteen_bit=1,
software_slave=1,
hardware_slave=0,
LSB_First=1,
MSB_First=0,
FCLK_2=0,
FCLK_4=1,
FCLK_8=2,
FCLK_16=3,
FCLK_32=4,
FCLK_64=5,
FCLK_128=6,
FCLK_256=7,
Master_mode=1,
Slave_mode=0,
CPOL0=0,
CPOL1=1,
CPHA0=0,
CPHA1=1,
}SPI_ConfigurationTypedef;The configurations as following:
- Data format: 8-bit or 16-bit.
- Software or hardware slave management.
- MSB or LSB first.
- Baudrate
- Master or slave mode.
- CPOL and CPHA mode.
Also, we shall create a structure to handle the configuration as following:
typedef struct
{
uint8_t mode;
uint8_t dataFromat;
uint8_t slaveManagement;
uint8_t LSBFirst;
uint8_t baud;
uint8_t CPOL;
uint8_t CPHA;
}SPI_ConfigTypedef;Declare the following functions:
void SPI_Enable_Clock(SPI_EnableTypedef spi_number); void BSP_SPI_Config(SPI_TypeDef *spi, SPI_ConfigTypedef *con); SPI_StatusTypedef BSP_SPI_Transmit(SPI_TypeDef *spi ,uint8_t *data,uint32_t size, uint32_t timeout);
Hence, the header file as following:
#ifndef SPI_BSP_H_
#define SPI_BSP_H_
#include "stdint.h"
#include "stm32f4xx.h"
typedef enum
{
spi1=0,
spi2=1,
spi3=2,
spi4=3,
spi5=4
}SPI_EnableTypedef;
typedef enum
{
SPI_Succuss=0,
SPI_Timeout=1,
}SPI_StatusTypedef;
typedef enum
{
eight_bit=0,
sixteen_bit=1,
software_slave=1,
hardware_slave=0,
LSB_First=1,
MSB_First=0,
FCLK_2=0,
FCLK_4=1,
FCLK_8=2,
FCLK_16=3,
FCLK_32=4,
FCLK_64=5,
FCLK_128=6,
FCLK_256=7,
Master_mode=1,
Slave_mode=0,
CPOL0=0,
CPOL1=1,
CPHA0=0,
CPHA1=1,
}SPI_ConfigurationTypedef;
typedef struct
{
uint8_t mode;
uint8_t dataFromat;
uint8_t slaveManagement;
uint8_t LSBFirst;
uint8_t baud;
uint8_t CPOL;
uint8_t CPHA;
}SPI_ConfigTypedef;
void SPI_Enable_Clock(SPI_EnableTypedef spi_number);
void BSP_SPI_Config(SPI_TypeDef *spi, SPI_ConfigTypedef *con);
SPI_StatusTypedef BSP_SPI_Transmit(SPI_TypeDef *spi ,uint8_t *data,uint32_t size, uint32_t timeout);
#endif /* SPI_BSP_H_ */Thats all for the header file.
2. Developing The Source File:
Create new source file with name of spi_bsp.c.
Within the source file, include the following header files:
#include "spi_bsp.h" #include "bsp.h"
For SPI clock enable function:
void SPI_Enable_Clock(SPI_EnableTypedef spi_number)
{
switch (spi_number)
{
case spi1: RCC->APB2ENR|=RCC_APB2ENR_SPI1EN;
break;
case spi2: RCC->APB1ENR|=RCC_APB1ENR_SPI2EN;
break;
case spi3: RCC->APB1ENR|=RCC_APB1ENR_SPI3EN;
break;
case spi4: RCC->APB2ENR|=RCC_APB2ENR_SPI4EN;
break;
case spi5: RCC->APB2ENR|=RCC_APB2ENR_SPI5EN;
break;
default :
break;
}
}The function takes SPI_EnableTypedef as argument to enable which SPI.
For SPI configuration function:
void BSP_SPI_Config(SPI_TypeDef *spi, SPI_ConfigTypedef *con)
{
switch (con->mode)
{
case Master_mode: spi->CR1|=(1<<SPI_CR1_MSTR_Pos);
break;
case Slave_mode: spi->CR1&=~(1<<SPI_CR1_MSTR_Pos); break;
default : break;
}
switch (con->dataFromat)
{
case MSB_First: spi->CR1&=~(con->dataFromat<<SPI_CR1_LSBFIRST_Pos);
break;
case LSB_First: spi->CR1|=(con->dataFromat<<SPI_CR1_LSBFIRST_Pos); break;
default: break;
}
if(con->baud ==FCLK_2)
{
spi->CR1&=~(SPI_CR1_BR_Msk<<SPI_CR1_BR_Pos);
}
else
{
spi->CR1|=(con->baud<<SPI_CR1_BR_Pos);
}
if (con->CPOL==CPOL0)
{
spi->CR1&=~(1<<SPI_CR1_CPOL_Pos);
}
else
{
spi->CR1|=(con->CPOL<<SPI_CR1_CPOL_Pos);
}
if (con->CPHA==CPHA0)
{
spi->CR1&=~(1<<SPI_CR1_CPHA_Pos);
}
else
{
spi->CR1|=(con->CPHA<<SPI_CR1_CPHA_Pos);
}
switch (con->slaveManagement)
{
case software_slave: spi->CR1|=(1<<SPI_CR1_SSM_Pos)|(1<<SPI_CR1_SSI_Pos); break;
case hardware_slave: spi->CR1&=~((1<<SPI_CR1_SSM_Pos)|(1<<SPI_CR1_SSI_Pos)); break;
default: break;
}
spi->CR1|=SPI_CR1_SPE;
}
For SPI transmit function:
SPI_StatusTypedef BSP_SPI_Transmit(SPI_TypeDef *spi ,uint8_t *data,uint32_t size, uint32_t timeout)
{
uint32_t i=0;
uint32_t start=BSP_Get_Ticks();
while(i<size)
{
/*Wait until TXE is set*/
while(!(spi->SR & (SPI_SR_TXE)))
{
if((BSP_Get_Ticks()-start)>timeout){return SPI_Timeout;}
}
/*Write the data to the data register*/
spi->DR = data[i];
i++;
}
/*Wait until TXE is set*/
while(!(spi->SR & (SPI_SR_TXE))){if((BSP_Get_Ticks()-start)>timeout){return SPI_Timeout;}}
/*Wait for BUSY flag to reset*/
while((spi->SR & (SPI_SR_BSY))){if((BSP_Get_Ticks()-start)>timeout){return SPI_Timeout;}}
/*Clear OVR flag*/
(void)spi->DR;
(void)spi->SR;
return SPI_Succuss;
}
Hence, the entire source code as following:
#include "spi_bsp.h"
#include "bsp.h"
void SPI_Enable_Clock(SPI_EnableTypedef spi_number)
{
switch (spi_number)
{
case spi1: RCC->APB2ENR|=RCC_APB2ENR_SPI1EN;
break;
case spi2: RCC->APB1ENR|=RCC_APB1ENR_SPI2EN;
break;
case spi3: RCC->APB1ENR|=RCC_APB1ENR_SPI3EN;
break;
case spi4: RCC->APB2ENR|=RCC_APB2ENR_SPI4EN;
break;
case spi5: RCC->APB2ENR|=RCC_APB2ENR_SPI5EN;
break;
default :
break;
}
}
void BSP_SPI_Config(SPI_TypeDef *spi, SPI_ConfigTypedef *con)
{
switch (con->mode)
{
case Master_mode: spi->CR1|=(1<<SPI_CR1_MSTR_Pos);
break;
case Slave_mode: spi->CR1&=~(1<<SPI_CR1_MSTR_Pos); break;
default : break;
}
switch (con->dataFromat)
{
case MSB_First: spi->CR1&=~(con->dataFromat<<SPI_CR1_LSBFIRST_Pos);
break;
case LSB_First: spi->CR1|=(con->dataFromat<<SPI_CR1_LSBFIRST_Pos); break;
default: break;
}
if(con->baud ==FCLK_2)
{
spi->CR1&=~(SPI_CR1_BR_Msk<<SPI_CR1_BR_Pos);
}
else
{
spi->CR1|=(con->baud<<SPI_CR1_BR_Pos);
}
if (con->CPOL==CPOL0)
{
spi->CR1&=~(1<<SPI_CR1_CPOL_Pos);
}
else
{
spi->CR1|=(con->CPOL<<SPI_CR1_CPOL_Pos);
}
if (con->CPHA==CPHA0)
{
spi->CR1&=~(1<<SPI_CR1_CPHA_Pos);
}
else
{
spi->CR1|=(con->CPHA<<SPI_CR1_CPHA_Pos);
}
switch (con->slaveManagement)
{
case software_slave: spi->CR1|=(1<<SPI_CR1_SSM_Pos)|(1<<SPI_CR1_SSI_Pos); break;
case hardware_slave: spi->CR1&=~((1<<SPI_CR1_SSM_Pos)|(1<<SPI_CR1_SSI_Pos)); break;
default: break;
}
spi->CR1|=SPI_CR1_SPE;
}
SPI_StatusTypedef BSP_SPI_Transmit(SPI_TypeDef *spi ,uint8_t *data,uint32_t size, uint32_t timeout)
{
uint32_t i=0;
uint32_t start=BSP_Get_Ticks();
while(i<size)
{
/*Wait until TXE is set*/
while(!(spi->SR & (SPI_SR_TXE)))
{
if((BSP_Get_Ticks()-start)>timeout){return SPI_Timeout;}
}
/*Write the data to the data register*/
spi->DR = data[i];
i++;
}
/*Wait until TXE is set*/
while(!(spi->SR & (SPI_SR_TXE))){if((BSP_Get_Ticks()-start)>timeout){return SPI_Timeout;}}
/*Wait for BUSY flag to reset*/
while((spi->SR & (SPI_SR_BSY))){if((BSP_Get_Ticks()-start)>timeout){return SPI_Timeout;}}
/*Clear OVR flag*/
(void)spi->DR;
(void)spi->SR;
return SPI_Succuss;
}
3. Code Test:
In main.c:
#include "bsp.h"
#include "uart_bsp.h"
#include "exti_bsp.h"
#include "bsp_debug.h"
#include "spi_bsp.h"
void clock_config(void);
uint8_t SPI_TX_Data[5]={0x10,0x12,0x14,0x16,0x18};
GPIO_Output_Typedef CS_Pin;
int main()
{
#if FPU_EN
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));
#endif
clock_config();
BSP_Ticks_Init(100000000);
GPIO_Configure_Typedef PA5_SPI,PA6_SPI,PA7_SPI;
GPIOA_CLOCK_ENABLE();
PA5_SPI.PinNumber=5;
PA5_SPI.Mode=Alternate_function;
PA5_SPI.AlternateType=AF5;
PA5_SPI.OutPutspeed=High_Speed;
PA6_SPI.PinNumber=6;
PA6_SPI.Mode=Alternate_function;
PA6_SPI.AlternateType=AF5;
PA6_SPI.OutPutspeed=High_Speed;
PA7_SPI.PinNumber=7;
PA7_SPI.Mode=Alternate_function;
PA7_SPI.AlternateType=AF5;
PA6_SPI.OutPutspeed=High_Speed;
GPIO_Initialization(GPIOA,&PA5_SPI);
GPIO_Initialization(GPIOA,&PA6_SPI);
GPIO_Initialization(GPIOA,&PA7_SPI);
GPIO_Configure_Typedef CS_Pin_Config;
CS_Pin_Config.PinNumber=pin0;
CS_Pin_Config.Mode=OUTPUT;
CS_Pin_Config.OutPutspeed=High_Speed;
GPIO_Initialization(GPIOA,&CS_Pin_Config);
GPIO_WritePin(GPIOA,&CS_Pin,Set);
SPI_Enable_Clock(spi1);
SPI_ConfigTypedef cSPI1;
cSPI1.mode=Master_mode;
cSPI1.slaveManagement=software_slave;
cSPI1.baud=FCLK_64;
cSPI1.CPHA=CPHA0;
cSPI1.CPOL=CPOL0;
cSPI1.LSBFirst=MSB_First;
BSP_SPI_Config(SPI1,&cSPI1);
while(1)
{
GPIO_WritePin(GPIOA,&CS_Pin,Reset);
BSP_SPI_Transmit(SPI1,SPI_TX_Data,5,100);
GPIO_WritePin(GPIOA,&CS_Pin,Set);
BSP_Delay(10);
}
}
void clock_config(void)
{
Clock_Config_Typedef clockConfig;
clockConfig.PLL_M= 4;
clockConfig.PLL_N= 200;
clockConfig.PLL_P= 4;
clockConfig.AHB1Prescaler=AHB1_Prescaler1;
clockConfig.APB1Prescaler=APB1_Prescaler2;
clockConfig.APB2Prescaler=APB2_Prescaler1;
clockConfig.clockSourc=External_Oscillator;
clockConfig.flash_latency= Three_wait_state;
Clock_Configuration(&clockConfig);
}
4. Results:
By using logic analyzer, connect pins PA0(CS), PA5 (SCK) and PA7 (MOSI) to logic analyzer and you should get the following results:

Happy coding 🙂
Add Comment