Building Board Support Package (BSP) for STM32F411-Nucleo64 Part11: SPI TX Mode

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

Your email address will not be published. Required fields are marked *