Skip to main content
Visitor II
September 26, 2024
Question

SPI DMA cannot send multiply bytes

  • September 26, 2024
  • 1 reply
  • 709 views

Hi,

 

I'm using STM32G431 to drive my SPI screen. Due to the high cpu load, i decided to change the IT sending mode to DMA. After putting a little changes on code, it doesn't work any more, and i have confirmed it will work again once get it back to IT mode.

I placed breakpoints in HAL_SPI_ErrorCallback and HAL_SPI_Transmit_DMA, besides my oscilloscope probes on MOSI and SCK. It seems that it would success if i send only one byte per request, and fail if i send 0x80 bytes.

I checked SPI_SR, bit BSY and TXE was set when HAL_SPI_ErrorCallback was called. And one more confused thing is it failed at the third tires, even through the first and second tries was not send.

 

 

// void SPI2_IRQHandler(void)
// {
// HAL_SPI_IRQHandler(&screen_spi_handle);
// }
void DMA1_Channel1_IRQHandler(void)
{
 HAL_DMA_IRQHandler(&screen_dma_handle);
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef* hspi)
{
 (void)hspi;
 vTaskNotifyGiveFromISR(screen_task_handle, NULL);
}
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef* hspi)
{
 while (1)
 {
 }
}

 

screen.c:

 

void screen_init()
{
 gpio_screen_mosi.Pin = GPIO_PIN_15;
 gpio_screen_mosi.Mode = GPIO_MODE_AF_PP;
 gpio_screen_mosi.Pull = GPIO_NOPULL;
 gpio_screen_mosi.Speed = GPIO_SPEED_FREQ_MEDIUM;
 gpio_screen_mosi.Alternate = GPIO_AF5_SPI2;
 HAL_GPIO_Init(GPIOB, &gpio_screen_mosi);

 gpio_screen_sck.Pin = GPIO_PIN_13;
 gpio_screen_sck.Mode = GPIO_MODE_AF_PP;
 gpio_screen_sck.Pull = GPIO_NOPULL;
 gpio_screen_sck.Speed = GPIO_SPEED_FREQ_MEDIUM;
 gpio_screen_sck.Alternate = GPIO_AF5_SPI2;
 HAL_GPIO_Init(GPIOB, &gpio_screen_sck);

 gpio_screen_cs.Pin = GPIO_PIN_12;
 gpio_screen_cs.Mode = GPIO_MODE_OUTPUT_PP;
 gpio_screen_cs.Pull = GPIO_PULLUP;
 gpio_screen_cs.Speed = GPIO_SPEED_FREQ_MEDIUM;
 HAL_GPIO_Init(GPIOB, &gpio_screen_cs);

 gpio_screen_dc.Pin = GPIO_PIN_11;
 gpio_screen_dc.Mode = GPIO_MODE_OUTPUT_PP;
 gpio_screen_dc.Pull = GPIO_NOPULL;
 gpio_screen_dc.Speed = GPIO_SPEED_FREQ_MEDIUM;
 HAL_GPIO_Init(GPIOB, &gpio_screen_dc);

 gpio_screen_reset.Pin = GPIO_PIN_10;
 gpio_screen_reset.Mode = GPIO_MODE_OUTPUT_PP;
 gpio_screen_reset.Pull = GPIO_PULLDOWN;
 gpio_screen_reset.Speed = GPIO_SPEED_FREQ_MEDIUM;
 HAL_GPIO_Init(GPIOB, &gpio_screen_reset);

 __HAL_RCC_SPI2_CLK_ENABLE();

 screen_spi_handle.Instance = SPI2;
 screen_spi_handle.Init.Mode = SPI_MODE_MASTER;
 screen_spi_handle.Init.Direction = SPI_DIRECTION_2LINES;
 screen_spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;
 screen_spi_handle.Init.CLKPolarity = SPI_POLARITY_HIGH;
 screen_spi_handle.Init.CLKPhase = SPI_PHASE_2EDGE;
 screen_spi_handle.Init.NSS = SPI_NSS_SOFT;
 screen_spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 2.25Mhz
 screen_spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
 screen_spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
 screen_spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
 screen_spi_handle.Init.CRCPolynomial = 7;
 screen_spi_handle.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
 screen_spi_handle.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

 HAL_StatusTypeDef result = HAL_SPI_Init(&screen_spi_handle);
 if (result != HAL_OK)
 hal_perror("screen", "HAL_SPI_Init", result);

 // HAL_NVIC_SetPriority(SPI2_IRQn, 2, 0);
 // HAL_NVIC_EnableIRQ(SPI2_IRQn);

 __HAL_RCC_DMAMUX1_CLK_ENABLE();
 __HAL_RCC_DMA1_CLK_ENABLE();

 screen_dma_handle.Instance = DMA1_Channel1;
 screen_dma_handle.Init.Request = DMA_REQUEST_SPI2_TX;
 screen_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
 screen_dma_handle.Init.MemInc = DMA_MINC_ENABLE;
 screen_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
 screen_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
 screen_dma_handle.Init.Mode = DMA_NORMAL;
 screen_dma_handle.Init.Priority = DMA_PRIORITY_LOW;

 result = HAL_DMA_Init(&screen_dma_handle);
 if (result != HAL_OK)
 hal_perror("screen", "HAL_DMA_Init", result);
 __HAL_LINKDMA(&screen_spi_handle, hdmatx, screen_dma_handle);

 HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 2, 1);
 HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

 printf("screen: ready\n");
 u8g2_Setup_sh1106_128x64_noname_f(&u8g2_handle, U8G2_R0, u8x8_byte_method, u8x8_gpio_and_delay_method);
 uint8_t* buf = (uint8_t*)pvPortMalloc(u8g2_GetBufferSize(&u8g2_handle)); // dynamically allocate a buffer of the
 // required size
 if (buf == NULL)
 hal_perror("screen", "pvPortMalloc", buf);
 u8g2_SetBufferPtr(&u8g2_handle, buf);
 
 u8g2_InitDisplay(&u8g2_handle);
 printf("screen: setup for sh1106_128x64_noname_f\n");
 u8g2_SetPowerSave(&u8g2_handle, 0);
 u8g2_ClearBuffer(&u8g2_handle);
 u8g2_SetFont(&u8g2_handle, u8g2_font_profont12_mf);
 u8g2_DrawStr(&u8g2_handle, 0, 12, "Initializing...");
 u8g2_SendBuffer(&u8g2_handle);
 printf("screen: \"Initializing...\" has been printed on the screen\n");
}

uint8_t u8x8_byte_method(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr)
{
 HAL_StatusTypeDef result;
 switch (msg)
 {
 case U8X8_MSG_BYTE_SEND:
 result = HAL_SPI_Transmit_DMA(&screen_spi_handle, (uint8_t*)arg_ptr, arg_int);
 if (result != HAL_OK)
 hal_perror("screen", "HAL_SPI_Transmit_DMA", result);
 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
...

 

 

 

 

    This topic has been closed for replies.

    1 reply

    Super User
    September 26, 2024

    If HAL_SPI_ErrorCallback is called, the reason for the error will be found in the handle, in hspi->ErrorCode. It could be that the buffer is not in a location that the DMA can access.

    Since DMA takes time to complete, you should also verify that the SPI is ready before you issue another DMA command.

    Visitor II
    September 26, 2024

    Hi,

    The hspi->ErrorCode =  0x20, which told /*!< Error on RXNE/TXE/BSY/FTLVL/FRLVL Flag */. Then, SPI_SR = 0x82, so BSY and TXE was set.

    I'm using FreeRTOS so, the thread will block untill TxCpltCallback send signal to it.