cancel
Showing results for 
Search instead for 
Did you mean: 

allocateBlock never ruturn

ALomb.1
Associate III

My HW:

  • NUCLEO-F446RE
  • Custom DISPLAY Board (TFT ILI9314, TPC TSC2007, SPI FLASH MX25R6435F)

My SW:

  • X-CUBE-TOUCHGFX 4.17.0
  • STM32CubeIDE Version: 1.6.1
  • STM32Cube_FW_F4_V1.26.1
  • FreeRTOS 10.3.1 with CMSIS_V2

Randomly and after some time (from 20 minutes to several hours) the TouchGFX_Task task hangs in the AllocateBlock function (FrameBufferAllocator.hpp).

The state of all blocks is other than EMPTY.

virtual uint16_t allocateBlock(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t** block)
    {
        drawingBlock++;
        if (drawingBlock == blocks)
        {
            drawingBlock = 0;
        }
        while (state[drawingBlock] != EMPTY)
        {
            FrameBufferAllocatorWaitOnTransfer();
        }
        assert(state[drawingBlock] == EMPTY);

What could be the problem ?

attached image of the debug session (drawingBlock = 2. state[2] = SENDING).

The other tasks work correctly

/Antonello

6 REPLIES 6

Hi

The DRAWN, DRAWN, SENDING states indicates that one block is being sent, the two next blocks are already drawn (ready to be sent next).

Therefore the application stops, waiting for the sending to finish.

If you are using DMA based sending, you should check if the interrupt was raised.

(a watchdog functionality (last resort), would be to fake the interrupt and fake that the block was sent).

Regards

ALomb.1
Associate III

Yes, I use DMA based sending.

The implementation is based on demo for STM32G071 Board, with the difference that I use FreeRTOS.

You mean that I lost some DMA transfers Interrupt ?

Regards

yes. If you never got the interrupt (or the software did something wrong of course), the last block is still believed to be sending. In which case the next cannot be transmitted. Maybe you can add some logging to check if all send blocks also get an transfer complete interrupt.

Regards

ALomb.1
Associate III

Hi @Flemming Gram CHRISTENSEN​ ,

I can confirm that the problem is in the Display_Bitmap function (MB1642BDisplayDriver.c). In particular, HAL_SPI_Transmit (Display_Set_Area and DCS_WRITE_MEMORY_START) and HAL_SPI_Transmit_DMA fail.

I hadn't handled the error and the IsTransmittingBlock_ variable remained set to 1 due to HAL_SPI_TxCpltCallback callback never called!

Now I have to understand why. On that spi (SPI1) only the display is connected!

Without the FreeRTOS I have been testing it for days and have never had this problem.

Do you think I should use a FreeRTOS Semaphore instead of the IsTransmittingBlock_ variable ?

File MB1642BDisplayDriver.c

#include "main.h"
#include "DCS.h"
#include "MB1642BDisplayDriver.h"
#include <assert.h>
 
extern SPI_HandleTypeDef hspi1;
extern DMA_HandleTypeDef hdma_spi1_tx;
extern TIM_HandleTypeDef htim6;
 
volatile uint16_t TE = 0;
static HAL_StatusTypeDef halSpiErrorCode = HAL_OK;
 
//Signal TE interrupt to TouchGFX
void touchgfxSignalVSync(void);
void touchgfxDisplayDriverTxCpltCallback(SPI_HandleTypeDef *hspi);
 
static void Display_DCS_Send(uint8_t command)
{
	/* Reset the nCS pin */
	HAL_GPIO_WritePin(DISPLAY_CSX_GPIO_Port, DISPLAY_CSX_Pin, GPIO_PIN_RESET);
	/* Set the DCX pin */
	HAL_GPIO_WritePin(DISPLAY_DCX_GPIO_Port, DISPLAY_DCX_Pin, GPIO_PIN_RESET);
	/* Send the command and wait until the bus is not busy before changing configuration */
	halSpiErrorCode = HAL_SPI_Transmit(&hspi1, &command, 1, HAL_MAX_DELAY);
	if(halSpiErrorCode != HAL_OK)
	{
		printf("ERROR: Display_DCS_Send [%d]\r\n", halSpiErrorCode);
	}
 
	/* Reset the DCX pin */
	HAL_GPIO_WritePin(DISPLAY_DCX_GPIO_Port, DISPLAY_DCX_Pin, GPIO_PIN_SET);
	/* Set the nCS */
	HAL_GPIO_WritePin(DISPLAY_CSX_GPIO_Port, DISPLAY_CSX_Pin, GPIO_PIN_SET);
}
 
static void Display_DCS_Send_With_Data(uint8_t command, uint8_t* data, uint8_t size)
{
	/* Reset the nCS pin */
	HAL_GPIO_WritePin(DISPLAY_CSX_GPIO_Port, DISPLAY_CSX_Pin, GPIO_PIN_RESET);
	/* Set the DCX pin */
	HAL_GPIO_WritePin(DISPLAY_DCX_GPIO_Port, DISPLAY_DCX_Pin, GPIO_PIN_RESET);
	/* Send the command and wait until the bus is not busy before changing configuration */
	halSpiErrorCode = HAL_SPI_Transmit(&hspi1, &command, 1, HAL_MAX_DELAY);
	if( halSpiErrorCode != HAL_OK)
	{
		printf("ERROR: Display_DCS_Send_With_Data command [%d]\r\n", halSpiErrorCode);
	}
	/* Reset the DCX pin */
	HAL_GPIO_WritePin(DISPLAY_DCX_GPIO_Port, DISPLAY_DCX_Pin, GPIO_PIN_SET);
	/* Send the data and wait until the bus is not busy before changing configuration */
	halSpiErrorCode = HAL_SPI_Transmit(&hspi1, data, size, HAL_MAX_DELAY);
	if( halSpiErrorCode != HAL_OK)
	{
		printf("ERROR: Display_DCS_Send_With_Data command [%d]\r\n", halSpiErrorCode);
	}
	/* Set the nCS */
	HAL_GPIO_WritePin(DISPLAY_CSX_GPIO_Port, DISPLAY_CSX_Pin, GPIO_PIN_SET);
}
 
void MB1642BDisplayDriver_DisplayOn(void)
{
  // Display ON
  Display_DCS_Send(DCS_SET_DISPLAY_ON);
  HAL_Delay(100);
}
 
void Display_OFF(void)
{
  // Display OFF
  Display_DCS_Send(DCS_SET_DISPLAY_OFF);
  HAL_Delay(100);
}
 
static uint16_t old_x0=0xFFFF, old_x1=0xFFFF, old_y0=0xFFFF, old_y1=0xFFFF;
 
void Display_Set_Area(uint16_t x0, uint16_t y0,
                      uint16_t x1, uint16_t y1)
{
  uint8_t arguments[4];
 
  // Set columns, if changed
  if (x0 != old_x0 || x1 != old_x1)
  {
    arguments[0] = x0 >> 8;
    arguments[1] = x0 & 0xFF;
    arguments[2] = x1 >> 8;
    arguments[3] = x1 & 0xFF;
    Display_DCS_Send_With_Data(0x2A, arguments, 4);
 
    old_x0 = x0;
    old_x1 = x1;
  }
 
  // Set rows, if changed
  if (y0 != old_y0 || y1 != old_y1)
  {
    arguments[0] = y0 >> 8;
    arguments[1] = y0 & 0xFF;
    arguments[2] = y1 >> 8;
    arguments[3] = y1 & 0xFF;
    Display_DCS_Send_With_Data(0x2B, arguments, 4);
 
    old_y0 = y0;
    old_y1 = y1;
  }
}
 
volatile uint8_t IsTransmittingBlock_;
void Display_Bitmap(const uint16_t *bitmap, uint16_t posx, uint16_t posy, uint16_t sizex, uint16_t sizey)
{
	IsTransmittingBlock_ = 1;
	__HAL_SPI_ENABLE(&hspi1); // Enables SPI peripheral
	uint8_t command = DCS_WRITE_MEMORY_START;
 
	// Define the display area
	Display_Set_Area(posx, posy, posx+sizex-1, posy+sizey-1);
 
	/* Reset the nCS pin */
	HAL_GPIO_WritePin(DISPLAY_CSX_GPIO_Port, DISPLAY_CSX_Pin, GPIO_PIN_RESET);
	/* Set the DCX pin */
	HAL_GPIO_WritePin(DISPLAY_DCX_GPIO_Port, DISPLAY_DCX_Pin, GPIO_PIN_RESET);
	/* Send the command and wait until the bus is not busy before changing configuration */
	halSpiErrorCode = HAL_SPI_Transmit(&hspi1, &command, 1, HAL_MAX_DELAY);
	if(halSpiErrorCode != HAL_OK)
	{
		printf("ERROR: Display_Bitmap command [%d]\r\n", halSpiErrorCode);
	}
	/* Reset the DCX pin */
	HAL_GPIO_WritePin(DISPLAY_DCX_GPIO_Port, DISPLAY_DCX_Pin, GPIO_PIN_SET);
 
	__HAL_SPI_DISABLE(&hspi1);
 
	/* Set the SPI in 16-bit mode to match endianess */
	hspi1.Instance->CR1 |= SPI_CR1_DFF;
 
	__HAL_SPI_ENABLE(&hspi1);
 
	halSpiErrorCode = HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)bitmap, sizex*sizey);
	if(halSpiErrorCode != HAL_OK)
	{
		printf("ERROR: Display_Bitmap DMA [%d]\r\n", halSpiErrorCode);
		touchgfxDisplayDriverTxCpltCallback(&hspi1);
	}
}
 
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
  UNUSED(GPIO_Pin);
 
  TE++;
  (&htim6)->Instance->CR1 &= ~(TIM_CR1_CEN);
  (&htim6)->Instance->CNT = 0;
 
  touchgfxSignalVSync();
}
 
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
  UNUSED(GPIO_Pin);
  (&htim6)->Instance->CR1 = (TIM_CR1_CEN);
}
 
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_Pin) == GPIO_PIN_SET)
	{
		HAL_GPIO_EXTI_Rising_Callback(GPIO_Pin);
	}
	else
	{
		HAL_GPIO_EXTI_Falling_Callback(GPIO_Pin);
	}
}
 
void MB1642BDisplayDriver_DisplayInit(void)
{
  uint8_t arguments[4];
  __HAL_SPI_ENABLE(&hspi1);
  // Sleep out
  Display_DCS_Send(DCS_EXIT_SLEEP_MODE);
  HAL_Delay(100);
 
  // Display Normal mode
  Display_DCS_Send(DCS_ENTER_NORMAL_MODE);
  HAL_Delay(100);
 
  // MADCTL: Exchange RGB / BGR + Mirror X
  arguments[0] = 0x88; // 0x48
  Display_DCS_Send_With_Data(DCS_SET_ADDRESS_MODE, arguments, 1);
  HAL_Delay(100);
 
  // Pixel Format
  arguments[0] = 0x05; // RGB565
  Display_DCS_Send_With_Data(DCS_SET_PIXEL_FORMAT, arguments, 1);
  HAL_Delay(100);
 
  // Tearing effect line on
  arguments[0] = 0; //0x00;
  Display_DCS_Send_With_Data(DCS_SET_TEAR_ON, arguments, 1);
  HAL_Delay(100);
 
  // Tearing effect scan line
  arguments[0] = 0;
  arguments[1] = 0;
  Display_DCS_Send_With_Data(DCS_SET_TEAR_SCANLINE, arguments, 2);
  HAL_Delay(100);
 
}
 
void MB1642BDisplayDriver_DisplayReset(void)
{
  HAL_GPIO_WritePin(DISPLAY_RESET_GPIO_Port, DISPLAY_RESET_Pin, GPIO_PIN_RESET);
  HAL_Delay(100);
  HAL_GPIO_WritePin(DISPLAY_RESET_GPIO_Port, DISPLAY_RESET_Pin, GPIO_PIN_SET);
  HAL_Delay(100);
}
 
void MB1642BDisplayDriver_Init(void)
{
  /* DMA interrupt init for SPI1_TX */
  /* DMA2_Stream3_IRQn interrupt configuration */
  //HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);
  //HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
}
 
int touchgfxDisplayDriverTransmitActive(void)
{
  return IsTransmittingBlock_;
}
 
void touchgfxDisplayDriverTransmitBlock(const uint8_t* pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
  Display_Bitmap((uint16_t*)pixels, x, y, w, h);
}
 
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(hqspi->Instance == SPI1)
	{
		IsTransmittingBlock_ = 0;
 
		/* Set the nCS */
		HAL_GPIO_WritePin(DISPLAY_CSX_GPIO_Port, DISPLAY_CSX_Pin, GPIO_PIN_SET);
 
		__HAL_SPI_DISABLE(&hspi1);
 
		/* Go back to 8-bit mode */
		hspi1.Instance->CR1 &= ~SPI_CR1_DFF;
 
		__HAL_SPI_ENABLE(&hspi1);
 
		// Signal Transfer Complete to TouchGFX
		DisplayDriver_TransferCompleteCallback();
	}
}
 
int touchgfxDisplayDriverShouldTransferBlock(uint16_t bottom)
{
  //return (bottom < getCurrentLine());
  return (bottom < (TE > 0 ? 0xFFFF : ((__IO uint16_t)htim6.Instance->CNT)));
}

I think you should look into the the two HAL_SPI functions (you have the source code). Maybe write some global variables or set breakpoints in the "failure cases".

Would be interesting to know.

You could setup an error callback and set the istransmitting to 0 (or run the transmit complete code).

I do not think you should change to a semaphore. the main issue is that the UI must be able to see if SPI is running (if not then start, if it is, then do nothing).

Regards

Vins
Senior

@Community member​ Have you solved this issue?