cancel
Showing results for 
Search instead for 
Did you mean: 

SPI DMA cannot send multiply bytes

AlanCui4080
Associate II

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);
...

 

 

 

 

2 REPLIES 2
TDK
Guru

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.

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

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.