I am having a strange issue where SPI3 on a black pill (STM32F411CE) sends an extra 0xBB byte during transmission of data using DMA (stream0 for RX and stream5 for TX). This does not happen for SPI1 or SPI2 which use similar code. No matter what data I send using the SPI3_transmitReceive_DMA function it always sends 0xBB first. Example:
GPIOA->BSRR = GPIO_BSRR_BR0;
uint8_t u8_dummyData[3] = {0x01, 0x02, 0x03};
SPI3_transmitReceive_DMA(u8_dummyData, u8_rcvBuffer, 3);
GPIOA->BSRR = GPIO_BSRR_BS0;
PA0 is the CS pin and active state is low.
Result from a logic analyser is attached. I used a proper oscilloscope too and see the same result. Do you see anything obvious I have done wrong? Thank you.

uint8_t u8_getTxFinished(uint8_t u8_SPINum)
{
if (u8_SPINum == 1)
{
return s_SPI_txFinishedStates.u8_SPI1_tx_finished;
}
else if (u8_SPINum == 2)
{
return s_SPI_txFinishedStates.u8_SPI2_tx_finished;
}
else if (u8_SPINum == 3)
{
return s_SPI_txFinishedStates.u8_SPI3_tx_finished;
}
else if (u8_SPINum == 4)
{
return s_SPI_txFinishedStates.u8_SPI4_tx_finished;
}
else if (u8_SPINum == 5)
{
return s_SPI_txFinishedStates.u8_SPI5_tx_finished;
}
else
{
return 0;
}
}
void rstTxFinished(uint8_t u8_SPINum)
{
if (u8_SPINum == 1)
{
s_SPI_txFinishedStates.u8_SPI1_tx_finished = 0;
}
else if (u8_SPINum == 2)
{
s_SPI_txFinishedStates.u8_SPI2_tx_finished = 0;
}
else if (u8_SPINum == 3)
{
s_SPI_txFinishedStates.u8_SPI3_tx_finished = 0;
}
else if (u8_SPINum == 4)
{
s_SPI_txFinishedStates.u8_SPI4_tx_finished = 0;
}
else if (u8_SPINum == 5)
{
s_SPI_txFinishedStates.u8_SPI5_tx_finished = 0;
}
}
uint8_t u8_getRxFinished(uint8_t u8_SPINum)
{
if (u8_SPINum == 1)
{
return s_SPI_rxFinishedStates.u8_SPI1_rx_finished;
}
else if (u8_SPINum == 2)
{
return s_SPI_rxFinishedStates.u8_SPI2_rx_finished;
}
else if (u8_SPINum == 3)
{
return s_SPI_rxFinishedStates.u8_SPI3_rx_finished;
}
else if (u8_SPINum == 4)
{
return s_SPI_rxFinishedStates.u8_SPI4_rx_finished;
}
else if (u8_SPINum == 5)
{
return s_SPI_rxFinishedStates.u8_SPI5_rx_finished;
}
else
{
return 0;
}
}
void rstRxFinished(uint8_t u8_SPINum)
{
if (u8_SPINum == 1)
{
s_SPI_rxFinishedStates.u8_SPI1_rx_finished = 0;
}
else if (u8_SPINum == 2)
{
s_SPI_rxFinishedStates.u8_SPI2_rx_finished = 0;
}
else if (u8_SPINum == 3)
{
s_SPI_rxFinishedStates.u8_SPI3_rx_finished = 0;
}
else if (u8_SPINum == 4)
{
s_SPI_rxFinishedStates.u8_SPI4_rx_finished = 0;
}
else if (u8_SPINum == 5)
{
s_SPI_rxFinishedStates.u8_SPI5_rx_finished = 0;
}
}
void SPI3_DMA_init()
{
// Enable clock for GPIOB
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
// Set PB3 (SCK3), PB4 (MISO3) and PB5 (MOSI3) to alternate function mode
GPIOB->MODER |= GPIO_MODER_MODE3_1|GPIO_MODER_MODE4_1|GPIO_MODER_MODE5_1;
// Set which type of alternate function it is
GPIOB->AFR[0] |= (0x06<<12)|(0x06<<16)|(0x06<<20);
// Enable clock access to SPI3
RCC->APB1ENR |= RCC_APB1ENR_SPI3EN;
// Set software slave management
SPI3->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI;
// Set SPI in master mode
SPI3->CR1 |= SPI_CR1_MSTR;
// Enable SPI peripheral
SPI3->CR1 |= SPI_CR1_SPE;
// SPI3 RX DMA INIT
DMA1_STREAM0_init();
//SPI3 TX DMA INIT
DMA1_STREAM5_init();
}
static void clear_DMA1_Stream0_LowInterrupt_FCR_Flags(void)
{
uint32_t u32_clearFlags = 0;
u32_clearFlags |= 1 << 0;
u32_clearFlags |= 1 << 2;
u32_clearFlags |= 1 << 3;
u32_clearFlags |= 1 << 4;
u32_clearFlags |= 1 << 5;
/* Stream 0 so clear LO FCR instead of HI FCR */
DMA1->LIFCR |= u32_clearFlags;
}
static void clear_DMA1_Stream5_HiInterrupt_FCR_Flags(void)
{
uint32_t u32_clearFlags = 0;
u32_clearFlags |= 1 << 6; /* CFEIF5 */
u32_clearFlags |= 1 << 8; /* CDMEIF5 */
u32_clearFlags |= 1 << 9; /* CTEIF5 */
u32_clearFlags |= 1 << 10; /* CHTIF5 */
u32_clearFlags |= 1 << 11; /* CTCIF5 */
/* Stream 5 so clear HI FCR instead of LO FCR */
DMA1->HIFCR |= u32_clearFlags;
}
void SPI3_transmitReceive_DMA(uint8_t *dummy_data, uint8_t *data, uint8_t len)
{
clear_DMA1_Stream5_HiInterrupt_FCR_Flags();
clear_DMA1_Stream0_LowInterrupt_FCR_Flags();
/*RX*/
DMA1_Stream0->PAR = (uint32_t)&SPI3->DR;
DMA1_Stream0->M0AR = (uint32_t)data;
DMA1_Stream0->NDTR = len;
DMA1_Stream0->CR |= DMA_SxCR_EN;
/*TX*/
DMA1_Stream5->PAR = (uint32_t)&SPI3->DR;
DMA1_Stream5->M0AR = (uint32_t)dummy_data;
DMA1_Stream5->NDTR = len;
DMA1_Stream5->CR |= DMA_SxCR_EN;
/* Wait for DMA to finish receiving */
while (u8_getRxFinished(nSPI3) == 0) {;}
rstRxFinished(nSPI3);
}
void DMA1_STREAM0_init()
{
/*********** ************ *************/
/*********** ************ *************/
/*********** FOR SPI 3 RX *************/
/*********** ************ *************/
/*********** ************ *************/
uint32_t u32_DMARegConstructor;
/* Enable DMA1 on AHB1 clock */
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
/* Disable stream - Dont enable until sending data */
DMA1_Stream0->CR &= ~DMA_SxCR_EN;
/* Enable DMA clock */
SPI3->CR2 |= SPI_CR2_RXDMAEN;
u32_DMARegConstructor = 0;
/* Select Stream 0, Channel 0 for SPI3 RX*/
u32_DMARegConstructor |= 0x000 << 25;
/* Set Memory increment mode - Memory address point
* increment after each data transfer
*/
u32_DMARegConstructor |= DMA_SxCR_MINC;
/* Memory to peripheral */
u32_DMARegConstructor |= 1 << 6;
/* TCIE Interrupt enabled */
u32_DMARegConstructor |= 1 << 4;
/* Half txfr interrupt enabled */
u32_DMARegConstructor |= 1 << 3;
/* Txfr err interrupt enabled */
u32_DMARegConstructor |= 1 << 2;
/* Direct mode err interrupt enabled */
u32_DMARegConstructor |= 1 << 1;
/* Set entire register with stream 0 disabled for now */
DMA1_Stream0->CR = u32_DMARegConstructor;
/* Reset constructor to zero */
u32_DMARegConstructor = 0;
/* Disable direct mode */
u32_DMARegConstructor |= 0 << 2;
/* Set FTH to full buffer i.e. 0011*/
u32_DMARegConstructor |= 0x03; /* 0011 */
DMA1_Stream0->FCR |= u32_DMARegConstructor;
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
}
void DMA1_STREAM5_init()
{
/*********** ************ *************/
/*********** ************ *************/
/*********** FOR SPI 3 TX *************/
/*********** ************ *************/
/*********** ************ *************/
uint32_t u32_DMARegConstructor;
/* Enable DMA1 on AHB1 clock */
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
/* Disable stream - Dont enable until sending data */
DMA1_Stream5->CR &= ~DMA_SxCR_EN;
/* Enable DMA clock */
SPI3->CR2 |= SPI_CR2_TXDMAEN;
u32_DMARegConstructor = 0;
/* Select Stream 5, Channel 0 for SPI3 TX*/
u32_DMARegConstructor |= 0x000 << 25;
/* Set Memory increment mode - Memory address point
* increment after each data transfer
*/
u32_DMARegConstructor |= DMA_SxCR_MINC;
/* Memory to peripheral */
u32_DMARegConstructor |= 1 << 6;
/* TCIE Interrupt enabled */
u32_DMARegConstructor |= 1 << 4;
/* Half txfr interrupt enabled */
u32_DMARegConstructor |= 1 << 3;
/* Txfr err interrupt enabled */
u32_DMARegConstructor |= 1 << 2;
/* Direct mode err interrupt enabled */
u32_DMARegConstructor |= 1 << 1;
/* Set entire register with stream 5 disabled for now */
DMA1_Stream5->CR = u32_DMARegConstructor;
/* Reset constructor to zero */
u32_DMARegConstructor = 0;
/* Disable direct mode */
u32_DMARegConstructor |= 0 << 2;
/* Set FTH to full buffer i.e. 0011*/
u32_DMARegConstructor |= 0x03; /* 0011 */
DMA1_Stream5->FCR |= u32_DMARegConstructor;
NVIC_EnableIRQ(DMA1_Stream5_IRQn);
}
void DMA1_Stream0_IRQHandler(void)
{
if(DMA1->LISR & (DMA_LISR_TCIF0))
{
//_rx_finished = 1;
s_SPI_rxFinishedStates.u8_SPI3_rx_finished = 1;
DMA1_Stream0->CR &= ~DMA_SxCR_EN;
DMA1->LIFCR |= DMA_LIFCR_CTCIF0;
}
/* Check and clear HTIF Stream 0 flag */
if(DMA1->LISR&(DMA_LISR_HTIF0))
{
DMA1->LIFCR |= DMA_LIFCR_CHTIF0;
}
/* Check and clear TEIF Stream 0 flag */
if(DMA1->LISR&(DMA_LISR_TEIF0))
{
DMA1->LIFCR |= (DMA_LIFCR_CTEIF0);
}
/* Check and clear DMEIF Stream 0 flag */
if(DMA1->LISR&(DMA_LISR_DMEIF0))
{
DMA1->LIFCR |= (DMA_LIFCR_CDMEIF0);
}
/* Check and clear FEIF Stream 0 flag */
if(DMA1->LISR & (DMA_LISR_FEIF0))
{
DMA1->LIFCR |= (DMA_LIFCR_CFEIF0);
}
/* Clear DMA 1 stream 0 interrupt */
NVIC_ClearPendingIRQ(DMA1_Stream0_IRQn);
}
void DMA1_Stream5_IRQHandler(void)
{
if(DMA1->HISR & (DMA_HISR_TCIF5))
{
//_tx_finished = 1;
s_SPI_txFinishedStates.u8_SPI3_tx_finished = 1;
DMA1_Stream5->CR &= ~DMA_SxCR_EN;
DMA1->HIFCR |= DMA_HIFCR_CTCIF5;
}
/* Check and clear HTIF Stream 5 flag */
if(DMA1->HISR & (DMA_HISR_HTIF5))
{
DMA1->HIFCR |= DMA_HIFCR_CHTIF5;
}
/* Check and clear TEIF Stream 5 flag */
if(DMA1->HISR & (DMA_HISR_TEIF5))
{
DMA1->HIFCR |= (DMA_HIFCR_CTEIF5);
}
/* Check and clear DMEIF Stream 5 flag */
if(DMA1->HISR & (DMA_HISR_DMEIF5))
{
DMA1->HIFCR |= (DMA_HIFCR_CDMEIF5);
}
/* Check and clear FEIF Stream 5 flag */
if(DMA1->HISR & (DMA_HISR_FEIF5))
{
DMA1->HIFCR |= (DMA_HIFCR_CFEIF5);
}
/* Clear DMA 1 stream 5 interrupt */
NVIC_ClearPendingIRQ(DMA1_Stream5_IRQn);
}
Header file:
#ifndef INC_STM32F411_SPI_H_
#define INC_STM32F411_SPI_H_
#include "stm32f4xx.h"
#define nSPI1 1
#define nSPI2 2
#define nSPI3 3
#define nSPI4 4
#define nSPI5 5
typedef struct SPITxFinishedStats {
uint8_t u8_SPI1_tx_finished;
uint8_t u8_SPI2_tx_finished;
uint8_t u8_SPI3_tx_finished;
uint8_t u8_SPI4_tx_finished;
uint8_t u8_SPI5_tx_finished;
} SPITxFinishedStats;
typedef struct SPIRxFinishedStats {
uint8_t u8_SPI1_rx_finished;
uint8_t u8_SPI2_rx_finished;
uint8_t u8_SPI3_rx_finished;
uint8_t u8_SPI4_rx_finished;
uint8_t u8_SPI5_rx_finished;
} SPIRxFinishedStats;
void rstRxFinished(uint8_t u8_SPINum);
uint8_t u8_getRxFinished(uint8_t u8_SPINum);
void rstTxFinished(uint8_t u8_SPINum);
uint8_t u8_getTxFinished(uint8_t u8_SPINum);
/* SPI 3 */
void SPI3_DMA_init();
void SPI3_transmitReceive_DMA(uint8_t *dummy_data, uint8_t *data, uint8_t len);
void DMA1_STREAM5_init(); /* SPI 3 */
void DMA1_STREAM0_init();/* SPI 3 */
main.c which uses the library code posted before like this:
#include "main.h"
#include "STM32F411_SPI.h"
#include "stdint.h"
uint8_t u8_data[5] = {0xAA, 0xBB, 0xCC, 0xFF, 0xFE};
uint8_t u8_rcvBuff[3] = {0xDD, 0xBB, 0xCC};
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* ***************** INIT DMA HERE *************** */
/* ***************** *************** */
/* ***************** *************** */
SPI3_DMA_init();
GPIOA->MODER |= GPIO_MODER_MODER0_0;
GPIOA->BSRR = GPIO_BSRR_BS0;
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
/* ***************** TRANSMIT HERE *************** */
/* ***************** *************** */
/* ***************** *************** */
GPIOA->BSRR = GPIO_BSRR_BR0;
uint8_t u8_dummyData[3] = {0x01, 0x02, 0x03};
SPI3_transmitReceive_DMA(u8_dummyData, u8_rcvBuff, 3);
GPIOA->BSRR = GPIO_BSRR_BS0;
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV4;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV4;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : PA10 */
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */