2024-09-25 07:49 PM - edited 2024-09-25 07:51 PM
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);
...
2024-09-25 08:00 PM
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.
2024-09-25 08:05 PM
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.