cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H753 SPI Slave Tx DMA Problem (Circular Mode)

geunyoung
Associate

When using SPI6 in Slave mode with TX DMA in Circular mode, the SPI FIFO is not updated with data from the DMA buffer for the first SPI FIFO size (8 bytes).

geunyoung_0-1762475298528.png

 

 


Both the SPI Master and SPI Slave were debugged:

The SPI Master transmits one frame.

On the SPI Slave board, after checking the reception using LL_BDMA_IsActiveFlag_TC1(BDMA), the MISO data is updated.

The SPI Master then checks the received data on the SPI Slave side using a breakpoint.

 static void MX_SPI6_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI6;
PeriphClkInitStruct.Spi6ClockSelection = RCC_SPI6CLKSOURCE_D3PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}

LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_SPI6);

LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOB);

GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = LL_GPIO_PIN_3|LL_GPIO_PIN_4|LL_GPIO_PIN_5;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

/* SPI6 DMA Init */

/* SPI6_TX Init */
LL_BDMA_SetPeriphRequest(BDMA, LL_BDMA_CHANNEL_0, LL_DMAMUX2_REQ_SPI6_TX);

LL_BDMA_SetDataTransferDirection(BDMA, LL_BDMA_CHANNEL_0, LL_BDMA_DIRECTION_MEMORY_TO_PERIPH);
LL_BDMA_SetChannelPriorityLevel(BDMA, LL_BDMA_CHANNEL_0, LL_BDMA_PRIORITY_VERYHIGH);
LL_BDMA_SetMode(BDMA, LL_BDMA_CHANNEL_0, LL_BDMA_MODE_CIRCULAR);
LL_BDMA_SetPeriphIncMode(BDMA, LL_BDMA_CHANNEL_0, LL_BDMA_PERIPH_NOINCREMENT);
LL_BDMA_SetMemoryIncMode(BDMA, LL_BDMA_CHANNEL_0, LL_BDMA_MEMORY_INCREMENT);
LL_BDMA_SetPeriphSize(BDMA, LL_BDMA_CHANNEL_0, LL_BDMA_PDATAALIGN_BYTE);
LL_BDMA_SetMemorySize(BDMA, LL_BDMA_CHANNEL_0, LL_BDMA_MDATAALIGN_BYTE);

/* SPI6_RX Init */
LL_BDMA_SetPeriphRequest(BDMA, LL_BDMA_CHANNEL_1, LL_DMAMUX2_REQ_SPI6_RX);
LL_BDMA_SetDataTransferDirection(BDMA, LL_BDMA_CHANNEL_1, LL_BDMA_DIRECTION_PERIPH_TO_MEMORY);
LL_BDMA_SetChannelPriorityLevel(BDMA, LL_BDMA_CHANNEL_1, LL_BDMA_PRIORITY_VERYHIGH);
LL_BDMA_SetMode(BDMA, LL_BDMA_CHANNEL_1, LL_BDMA_MODE_CIRCULAR);
LL_BDMA_SetPeriphIncMode(BDMA, LL_BDMA_CHANNEL_1, LL_BDMA_PERIPH_NOINCREMENT);
LL_BDMA_SetMemoryIncMode(BDMA, LL_BDMA_CHANNEL_1, LL_BDMA_MEMORY_INCREMENT);
LL_BDMA_SetPeriphSize(BDMA, LL_BDMA_CHANNEL_1, LL_BDMA_PDATAALIGN_BYTE);
LL_BDMA_SetMemorySize(BDMA, LL_BDMA_CHANNEL_1, LL_BDMA_MDATAALIGN_BYTE);

/* SPI6 interrupt Init */
NVIC_SetPriority(SPI6_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(SPI6_IRQn);

/* SPI6 parameter configuration*/
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_HARD_INPUT;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 0x0;
LL_SPI_Init(SPI6, &SPI_InitStruct);
LL_SPI_SetStandard(SPI6, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_SetFIFOThreshold(SPI6, LL_SPI_FIFO_TH_01DATA);
LL_SPI_DisableNSSPulseMgt(SPI6);
}

static inline void spi_init(uint8_t *rx_data, uint8_t *tx_data, uint8_t size) {
LL_BDMA_InitTypeDef bdma_init_struct = {0};

bdma_init_struct.PeriphOrM2MSrcAddress = (uint32_t)&SPI6->RXDR;
bdma_init_struct.MemoryOrM2MDstAddress = (uint32_t)rx_data;
bdma_init_struct.Direction = LL_BDMA_DIRECTION_PERIPH_TO_MEMORY;
bdma_init_struct.Mode = LL_BDMA_MODE_CIRCULAR;
bdma_init_struct.MemoryOrM2MDstIncMode = LL_BDMA_MEMORY_INCREMENT;
bdma_init_struct.NbData = size;
bdma_init_struct.PeriphRequest = LL_DMAMUX2_REQ_SPI6_RX;
LL_BDMA_Init(BDMA, LL_BDMA_CHANNEL_1, &bdma_init_struct);

LL_BDMA_StructInit(&bdma_init_struct);
bdma_init_struct.PeriphOrM2MSrcAddress = (uint32_t)&SPI6->TXDR;
bdma_init_struct.MemoryOrM2MDstAddress = (uint32_t)tx_data;
bdma_init_struct.Direction = LL_BDMA_DIRECTION_MEMORY_TO_PERIPH;
bdma_init_struct.Mode = LL_BDMA_MODE_CIRCULAR;
bdma_init_struct.MemoryOrM2MDstIncMode = LL_BDMA_MEMORY_INCREMENT;
bdma_init_struct.NbData = size;
bdma_init_struct.PeriphRequest = LL_DMAMUX2_REQ_SPI6_TX;
LL_BDMA_Init(BDMA, LL_BDMA_CHANNEL_0, &bdma_init_struct);

LL_BDMA_EnableChannel(BDMA, LL_BDMA_CHANNEL_1);
LL_BDMA_EnableChannel(BDMA, LL_BDMA_CHANNEL_0);
LL_SPI_EnableDMAReq_RX(SPI6);
LL_SPI_EnableDMAReq_TX(SPI6);

LL_SPI_Enable(SPI6);

LL_BDMA_EnableChannel(BDMA, LL_BDMA_CHANNEL_1);
LL_BDMA_EnableChannel(BDMA, LL_BDMA_CHANNEL_0);
}

 

__attribute__((section(".non_cache"))) uint8_t recvData[17];

__attribute__((section(".non_cache"))) uint8_t sendData[17];

int main(void)
{
MX_SPI6_Init();
spi_init(recvData, sendData, 17);

while(1) {
if (LL_BDMA_IsActiveFlag_TC1(BDMA)) {

LL_BDMA_ClearFlag_TC1(BDMA);
for (i = 0; i < 17; i++) {
sendData[i] = test_idx;
test_idx++;
}
}
}
}

 

3 REPLIES 3
TDK
Super User

When TC happens, the next byte is already sent out or received. Process the HT flag and update/process the first half of the buffer, then process the TC flag and update/process the second half of the buffer.

Some other inconsistencies here. Your table has 18 columns but your code only sends 17 bytes.

Unclear if DMA interrupts are enabled or not.

BDMA TC flag checked but not cleared, so it'll always trigger.

If you feel a post has answered your question, please click "Accept as Solution".

Thank you for your response.
While summarizing the code, the LL_BDMA_ClearFlag_TC1(BDMA); part was missing, so I have corrected it.
I also updated the figure, as it was incorrectly provided earlier.

  • Receiving 17 bytes on the slave works correctly. (The data is received properly.)

  • The problem lies in data transmission from the slave to the master.
    (In the master, it should receive the data updated by the slave in the next frame, but it seems to be received with an 8-byte offset.)

TDK
Super User

I don't know how much I can help because the full picture isn't shown.

> The problem lies in data transmission from the slave to the master.

Why do you think this? Show me what you're looking at and how you're looking at it. Show a screenshot. Breakpoints aren't a great way of examining the state of memory. Points can be taken from different times.

And again, it's folly not to process each half-buffer as they are received. Otherwise, you need to process the entire buffer before another single byte is received. Maybe you can win that race condition, maybe not. Depends on SPI speed and other stuff happening on the CPU.

Why is LL_BDMA_EnableChannel being called twice on each channel? Is this the actual code?

If you feel a post has answered your question, please click "Accept as Solution".