cancel
Showing results for 
Search instead for 
Did you mean: 

[TouchGFX] Can not write to External flash using QuasSPI while running with TouchGFX

HPham.1590
Senior

Hello, I'm using STM32F746NG Discovery Kit.

I'm trying to Write/Read data to/from External Flash using QuadSPI with BSP driver.

Below is my simple function, I don't know why It's can not write to the memory.

I really don't know exactly how to determine the block address and the Read/Write Address, can anyone help me to correct it?

Many thanks,

Hieu

  uint8_t * pData = "Hello World";
  int i;
  if (BSP_QSPI_Erase_Block(0x90000000) == QSPI_OK)
  {
	  if(BSP_QSPI_Write(pData, 0x90000000, 12) == QSPI_OK)
	  {
		i = 1;
	  }
  }
static void MX_QUADSPI_Init(void)
{
 
  /* USER CODE BEGIN QUADSPI_Init 0 */
 
  /* USER CODE END QUADSPI_Init 0 */
 
  /* USER CODE BEGIN QUADSPI_Init 1 */
 
  /* USER CODE END QUADSPI_Init 1 */
  /* QUADSPI parameter configuration*/
  hqspi.Instance = QUADSPI;
//  HAL_QSPI_DeInit(&QSPIHandle); //User Code
  hqspi.Init.ClockPrescaler = 1;
//  hqspi.Init.ClockPrescaler = 2;//User Code
  hqspi.Init.FifoThreshold = 4;
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 24;
//  QSPIHandle.Init.FlashSize = POSITION_VAL(0x1000000) - 1; //User Code
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_6_CYCLE;
//  QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE; //User Code
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
  hqspi.Init.FlashID = QSPI_FLASH_ID_1;
  hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
  if (HAL_QSPI_Init(&hqspi) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN QUADSPI_Init 2 */
  BSP_QSPI_Init();
 
  BSP_QSPI_MemoryMappedMode();
  // This line below is disabled to test Write to external flash function
//  HAL_NVIC_DisableIRQ(QUADSPI_IRQn);
 
  /* USER CODE END QUADSPI_Init 2 */
}

19 REPLIES 19
MM..1
Chief III

Go back to how store 1kB data? You plan use QSPI, but better is store in internal flash , then Clive ask have you free sector in internal flash?

How big is code?

yeb, I changed the plan to internal flash and it's working seem OK, but now i have to erase 1 sector 256KB (Sector 7), I don't know how to erase only some page in this sector. Can you give me some solution?

Many thanks

Hieu

Sorry for the lately reply, clive1.

As your suggestion, I changed to internal flash and it's seem working correctly, but i'm confusing about the sector, I follow ST's flash example and it's have to erase 1 sector to write to it, but in stm32f7, sector 7 has 256KB, but I just need to write 1 or 2KB, so have any way to erase only one or two page in this sector?

Thank you,

Hieu

Use EEPROM emulation code and use last sector with 256kB , code it incrementaly write , and when is full writed erase it.

Second way when you need more space for code is use first sector for bootloader second for EEPROM emulator and code start from third.

First sectors is smaler.

Thank you, I'll try it.

KShim.1738
Associate III

I found a solution for writing external flash while TouchGFX app is running. Speaking clearly TouchGFX app must be paused to write the external flash. And I think it should be done voluntarily by TouchGFX app itself. That will be the safest way I reckon.

Below is the example. I used a flag instead of IPC provided by RTOS. Note that how the flag is set by both sides. And I have read HAL_QSPI_Abort() is enough to go back to indirect mode from memory mapped mode somewhere else on this forum.

Pausing ToughGFX app is enough to me because it's for updating some images in the external flash. I am afraid that this might not be the one for others if they don't want to pause it.

But I believe that Pausing TouchGFX is the only solution to write the external flash. Because TouchGFX will cause hard fault if it continues to run as soon as QSPI interface goes into indirect mode.

//------------------------------- sync functions -----------------------------
volatile uint8_t guSync;
 
#define TGFX_STATE_NOT_STARTED 0x00
#define TGFX_STATE_RUNNING     0x04
#define TGFX_STOP_REQUESTED    0x01
#define TGFX_STATE_STOPPED     0x80
 
void gusync_init(void) {
    guSync = TGFX_STATE_NOT_STARTED;
}
 
void pause_touchgfx_if_requested(void) {
    while(guSync & TGFX_STOP_REQUESTED) {
        guSync |= TGFX_STATE_STOPPED;
        DBG_PRINT(DBG_INFO, "==== tgfx waits ==== \r\n");
        vTaskDelay(100);
    }
    guSync &= ~TGFX_STATE_STOPPED;
    DBG_PRINT(DBG_INFO, "==== tgfx woke up==== \r\n");
}
 
void wait_for_touchgfx_to_stop(void) {
    guSync |= TGFX_STOP_REQUESTED;
    while(!(guSync & TGFX_STATE_STOPPED))
    {
        DBG_PRINT(DBG_INFO, "==== update waiting ==== \r\n");
        vTaskDelay(1000);
    }
}
 
void let_touchgfx_continue(void) {
    guSync &= ~TGFX_STOP_REQUESTED;
    DBG_PRINT(DBG_INFO, "==== tfgfx released ==== \r\n");
}
 
 
void set_indrect_mode(void) {
    BSP_QSPI_DeInit();
    BSP_QSPI_Init();
    HAL_NVIC_DisableIRQ(QUADSPI_IRQn);
}
 
void set_memory_mapped_mode(void) {
    BSP_QSPI_DeInit();
    BSP_QSPI_Init();
    BSP_QSPI_MemoryMappedMode();
    HAL_NVIC_DisableIRQ(QUADSPI_IRQn);
}
 
 
 
//------------------------------- TouchGFX -----------------------------
void MainView::Screen_Saver()
{
  if(1){
    if(pre_value != sec_cnt){
      if(++mIdleTime >= (10) && !AD_Container.isVisible()) // 10�
      //if(++mIdleTime >= (10*60) && !AD_Container.isVisible()) // 10*60 : 10��
      {
        mIdleTime = 0;
        ChangeContainer(CONTAINER_AD);
      }
      else if(AD_Container.isVisible())
      {
        if(++mADChangeTime == AD_CHANGE_TIME){ // x� ����
  /* Kyle 20201020 */
  pause_touchgfx_if_requested();
 
          mADChangeTime = 0;
          UpdateADImage();
        }
      }
    }
 
//------------------------------- update -----------------------------
    while(1) {
            wait_for_touchgfx_to_stop();
 
            set_indrect_mode();
            BSP_QSPI_Erase_Block(TEST_READ_WRITE_ADDRESS);
            BSP_QSPI_Write(teststr, TEST_READ_WRITE_ADDRESS, strlen(teststr));
            BSP_QSPI_Read(buffer, TEST_READ_WRITE_ADDRESS, 20);
            DBG_PRINT_ARRAY_BYTE(DBG_INFO, buffer, 20);
            set_memory_mapped_mode();
 
            let_touchgfx_continue();
            vTaskDelay(5000);
}

I experienced the problem with hardfault and found your solution. Has anybody used it?

I use the following but there I get sometimes hardfaults if the osDelays are too short. The solution below works however.

Maybe my hardfaults with lower osDelay() occured only with debugger. I also don't know which osDelay I can ommit. 100 ms between suspend of touchgfx and handleFlashOperations were too short.

Is there an other way to know if the delay is already active?

Is there a delay needed after writing to the flash before reactivation the touchgfx?

			(void) osDelay(100);
			(void) osThreadSuspend( Task_TouchGFXHandle );
			(void) osDelay(100);
			(void) HAL_NVIC_DisableIRQ(LTDC_IRQn);
			(void) HAL_NVIC_DisableIRQ(DMA2D_IRQn);
			(void) osDelay(500);
 
			handleFlashOperations();
 
			(void) osDelay(500);
			HAL_NVIC_EnableIRQ(LTDC_IRQn);
			HAL_NVIC_EnableIRQ(DMA2D_IRQn);
			(void) osDelay(100);
			(void) osThreadResume( Task_TouchGFXHandle );

>>Is there an other way to know if the delay is already active?

Have some mutex or semaphore

>>Is there a delay needed after writing to the flash before reactivation the touchgfx?

The erase / write should inherently wait for the QSPI device to come ready.

If cached, watch for coherency issues, ie what's cached vs what you changed.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

You don't have to disable LTDC and DMA2D interrupts to write flash in my opinion. Just stopping the threads accessing serial flash via memory mapped mode will be enough.

read the tick counter to check if osDelay() works or not; but I don't believe osDelay() doesn't work.

osThreadSuspend() might stop the thread in the middle of read operation in memory mapped mode. I think it is better to make TouchGFX thread yield the cpu voluntarily using flag or semaphore/mutex, which seems to be safer.

It might be worth checking handleFlashOperations() too.

I have seen, there is a function in order to query if the thread is suspended, therefore I use now:

(void) osThreadSuspend( Task_TouchGFXHandle );
while ( osThreadIsSuspended(Task_TouchGFXHandle) != osOK)
{
	osDelay(1);
}

I will try not do disable the interupts.

You wrote: "osThreadSuspend() might stop the thread in the middle of read operation in memory mapped mode. I think it is better to make TouchGFX thread yield the cpu voluntarily using flag or semaphore/mutex, which seems to be safer."

In my application only logs data but TouchGFX will read if the screen is changed. But this is no problem, is it?