Post

3 followers Follow
0
Avatar

Porting TouchGFX to 8080-based LCDs

Using STM32-disco to port TouchGFX4.3.0 to 8080-based LCDs (also called mcu-type LCD). Platform is STM32-disco. Low level driver for this mcu-type LCD created and it is working without TouchGFX. The LCD is mapped to FMC address at 0x64000000 as external SRAM (basically a 8080-based LCD is like a piece of external SRAM). Now the hard part is to port this low level LCD driver to TouchGFX.

I started with touchgfx_demo2014_small. Under BoardConfiguration.cpp inside touchgfx namespace there are touchgfx_init() and hw_init(). Low level LCD initialization functions for stm32-LCD removed and replaced by mcu-type LCD initialization functions. The problem is, TouchGFX frame buffer is declared as frameBuf0=(uint32_t)(0xd0000000) and how is it going to let the system "know" we are mapping SRAM at 0x64000000 to the framebuffer at 0xd0000000?

This may be a stm32 question rather than a TouchGFX one.

Any suggestion is welcome.

Thanks in advance.

JohnLeung

Official comment

Avatar

Hi John,
Is your display in fact acting like a fully addressable SRAM, or have you only connected a single address pin (to command/data pin of display) such that you are e.g. writing data at 0x64000000 and commands at 0x64000002? I am assuming the latter, since this is the most common. In that case, TouchGFX still needs to render to a different frame buffer somewhere in memory, and you need to implement a certain hook function to transfer the updated region of the frame buffer to the display. This process is described in this article: https://touchgfx.zendesk.com/hc/en-us/articles/203642951-MCUs-without-TFT-controller

Søren Pingel Dalsgaard
Comment actions Permalink

Please sign in to leave a comment.

37 comments

0
Avatar

Soren

Thanks a lot. The article is very helpful. Indeed, I am following the latter implementation with FMC_A0 for RS, low for command and high for data. As a result, writeData and writeCmd are defined as :

#define LCD_BANK_ADDR ((uint32_t)0x64000000)

#define LCD_DC_CMD ((uint32_t)0x00)

#define LCD_DC_DATA ((uint32_t)0x02)

...
#define writeData(ui16Data) (__IO uint16_t) (LCD_BANK_ADDR + LCD_DC_DATA) = ui16Data
#define writeCmd(ui16Cmd) (__IO uint16_t) (LCD_BANK_ADDR + LCD_DC_CMD) = ui16Cmd

In BoardConfiguration.cpp, hal.setFrameBufferStartAddress((uint16_t*)frameBuf0, 16, false), and hal.lockDMAToFrontPorch(false) called following your article.

All STM32-LCD init functions from hw_init() removed and replaced by a single init function for my 8080-LCD.

A class declared:

class MyHAL : public STM32F4HAL
{
  public :
    void flushFrameBuffer(const Rect& rect)
    {
        // Remember to call base implementation
        STM32F4HAL::flushFrameBuffer(rect);

        displaySetArea(rect.x, rect.y, rect.width, rect.height);
        displayXferFB(rect.x, rect.y, rect.width, rect.height, frameBuf0);
    }
};

Have not implemented Vsync yet but it can be done by TE signal from the LCD module.

Will keep the result posted here for whats happening...

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

Still struggling to write a 8080-driver with a major problem.

I am using project from ../app/demo/touchgfx_demo2014_small/target/ST/STM32F429I-DISCO. Modification has been made to replace internal LCD controller of STM32F429 with a new 8080-driver. It is due to the demo size, it takes more than 1 minute for online debug every time a change is made to the source code. Project size is mainly loaded with individual graphical effects under Gui such as AnimatedGraphicsPresenter, BumpMapImage, etc. There are altogether 24 demo sections under Gui folder.

The question is that, is it possible to trim down this demo to just one or two to speed up the debug process? How to do it?

Best Regards

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Hi John,
Indeed the demo application is quite large, especially because that board has no external flash so all image data is placed internally, so it takes a long time to flash new code.

As a starting point I would suggest basing your initial project on the EmptyApplication which does not contain any graphics data at all. You could start by adding just a few Box widgets to the view to get something drawn. Check out the getting started section of our help center for pointers on doing a simple application.
Alternatively, to stick with the demo application you might just delete some of the bitmaps from the assets folder of that project, and comment out the code that will no longer be able to compile. I think especially the "live data display" has some large bitmaps that take up a lot of space.

Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

One more question. I find touchgfx_demo2014_small is the only project on stm32f429-disco without Cube support. The hal driver is a legacy one instead of Cube. Unfortunately it was based on Cube I have made the low level driver.

Is there any further example for stm32f429-disco that is based on Cube by using stm32f4xx_hal_xxx drivers instead?

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Hi John.
There is no CUBE based port for the stm32f429-disco at the moment.
You can do one your self by modifying the existing STM324x9I-CUBE port for STM324x9I-EVAL boards.

This article provides a few pointers to get you started.

Martin Stig Stissing 0 votes
Comment actions Permalink
0
Avatar

Soren

Stand-alone program to copy SDRAM framebuffer to 8080-LCD (SRAM) is working. Vsync is simulated with TE output from the LCD with a low->high pulse.

The next challenge is to plug in the LCD driver into TouchGFX sample program. Although LCD now got initialized, there is no periodic framebuffer transfer from SDRAM to SRAM under TouchGFX. Attached picture shows the screen output with only random pixel after power on reset.

A .c file namely stm32f4xx_it.c contains the EXTI IRQ as follows:

void LCD_EXTI_IRQHandler(void)
{
   /* Make sure that interrupt flag is set */
    if (EXTI_GetITStatus(LCD_TE_EXTI_Line) != RESET) { 
   
    //OSWrappers::signalVSync();
        /* Clear interrupt flag */
        EXTI_ClearITPendingBit(LCD_TE_EXTI_Line);
    }
}

 

The problem is, how to override this function to OSWappers::signalVSync().

Any suggestion is appreciated.

 

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

Have implemented a new LCD_EXT_IRQHandler() handler function for signalVSync() in STM32F4HAL.cpp and it has been proved to be working, at least I could toggle the green LED of a heart rate sensor wired for Pin_G2. Toggle rate is 30Hz, half the frame of the LCD (60Hz). See picture below.

Code as follows:

#ifdef USE_SSD2805_S6D04D2
extern "C"
 __irq void LCD_EXTI_IRQHandler(void)
{
   /* Make sure that interrupt flag is set */
    if (EXTI_GetITStatus(LCD_TE_EXTI_Line) != RESET) { 
   
    OSWrappers::signalVSync();
    touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ); //debug
        /* Clear interrupt flag */
        EXTI_ClearITPendingBit(LCD_TE_EXTI_Line);
    }
}
#endif

 

But the LCD is still not updated, that means the framework is not bumping the graphic content from SDRAM to SRAM.

I have implemented flushFrameBuffer() and flushFramBufferflushFrameBuffer(const Rect& rect) in STM32F4HAL.cpp:

#ifdef USE_SSD2805_S6D04D2
 class MyHAL : public STM32F4HAL
 {
   public :
  void flushFrameBuffer(void); 
  void flushFrameBuffer(const Rect& rect);
 };
 
 void MyHAL::flushFrameBuffer(void)
 {
   STM32F4HAL::flushFrameBuffer();
   displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION);
   displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
 }
 
 void MyHAL::flushFrameBuffer(const Rect& rect)
 {
   // Remember to call base implementation
   STM32F4HAL::flushFrameBuffer(rect);
   
   displaySetArea(rect.x, rect.y, rect.width, rect.height);
   displayXferFB(rect.x, rect.y, rect.width, rect.height, 0xD0000000);
 }
#endif

 

What did I miss?

 

John

 

 

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

OK, by a "blunt force" I could display the small demo now! Here is the changes:

#ifdef USE_SSD2805_S6D04D2
extern "C"
 __irq void LCD_EXTI_IRQHandler(void)
{
   /* Make sure that interrupt flag is set */
    if (EXTI_GetITStatus(LCD_TE_EXTI_Line) != RESET) { 
    OSWrappers::signalVSync();
    displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION); //force update the whole screen here by blunt force
    displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
    //touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ); //debug
        /* Clear interrupt flag */
        EXTI_ClearITPendingBit(LCD_TE_EXTI_Line);
    }
}
#endif

 

My  problem is a limited C++ knowledge. How to override flushFrameBuffer() in a better way?

 

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Hi John,

Looks like you are making good progress.

As you mention yourself, the goal is to make the flushFrameBuffer() function do the transfer, instead of the interrupt routine. This is because there might be tearing if the display transfer takes place before TouchGFX is completely done with drawing a frame. Also, if the displayXferFB function takes a long time to execute, it should not be in interrupt context. I don't know if it performs a manual copy or just initiates a DMA transfer.

I assume the problem is you never get your MyHAL::flushFrameBuffer() called? The MyHAL class you created look completely right to me. I wonder if you remembered to also inform TouchGFX that it should use MyHAL instead of the default STM32F4HAL? This is done in the BoardConfiguration, in the call to touchgfx_generic_init. This function has a template argument which is the HAL type, so it must be changed to "MyHAL" in order for your changes to have effect.

If you already did that but still are not getting flushFrameBuffer() called, let me know. In that case also please make sure you are using at least TouchGFX version 4.4.0, as there was a bugfix potentially related to this.

 

 

 

Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

Indeed, MyHAL::flushFrameBuffer() never get called at first but after having changed the code to MyHAL &hal= (MyHAL &)touchgfx_generic_init<STM32F4HAL>(noDMA, display, tc, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0) still didn't work.

Also upgraded to release 4.4.1 but the problem is not solved.

I am beginning to consider it is the problem of C++ support in Keil C. I am using Keil uVision5 to compile the project. I also tried the override keyword but like this. But I needed to add --cpp11 to support override keyword in Keil. After adding this, there are a bunch of errors from RTOS.

class MyHAL : public STM32F4HAL
 {
   public :
  virtual void flushFrameBuffer(void) override
  {
   STM32F4HAL::flushFrameBuffer();
   displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION);
   displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
  }
  virtual void flushFrameBuffer(const Rect& rect) override
  {
   // Remember to call base implementation
   STM32F4HAL::flushFrameBuffer(rect);
   displaySetArea(rect.x, rect.y, rect.width, rect.height);
   displayXferFB(rect.x, rect.y, rect.width, rect.height, 0xD0000000);
  }
 };

/*****************************************************************/

//BoardConfiguration.cpp

This is the whole namespace :

namespace touchgfx
{
 NoDMA noDMA; //new - TechToys
  STM32F4DMA dma;
  ResistiveTouchController tc;
  CortexMMCUInstrumentation mcuInstr;
  LCD16bpp display;

#ifdef USE_SSD2805_S6D04D2
 
 class MyHAL : public STM32F4HAL
 {
   public :
  virtual void flushFrameBuffer(void)
  {
   STM32F4HAL::flushFrameBuffer();
   displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION);
   displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
  }
  virtual void flushFrameBuffer(const Rect& rect)
  {
   // Remember to call base implementation
   STM32F4HAL::flushFrameBuffer(rect);
   displaySetArea(rect.x, rect.y, rect.width, rect.height);
   displayXferFB(rect.x, rect.y, rect.width, rect.height, 0xD0000000);
  }
 };
 
#endif
 
  void touchgfx_init()
  {
#ifdef USE_SSD2805_S6D04D2
  
  MyHAL &hal= (MyHAL &)touchgfx_generic_init<STM32F4HAL>(noDMA, display, tc, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0);
  hal.setFrameBufferStartAddress((uint16_t*)frameBuf0, 16, false, true);
  // This platform can handle simultaneous DMA and TFT accesses to SDRAM, so disable lock to increase performance.
  hal.lockDMAToFrontPorch(false);
#else
  HAL& hal = touchgfx_generic_init<STM32F4HAL>(dma, display, tc, 240, 320, 0, 0);
  hal.setFrameBufferStartAddress((uint16_t*)frameBuf0);
#endif
    
    hal.setTouchSampleRate(2);  
    mcuInstr.init();

    //Set MCU instrumentation and Load calculation
    hal.setMCUInstrumentation(&mcuInstr);
    hal.enableMCULoadCalculation(true);
  }

  void hw_init()
  {
#ifdef USE_SSD2805_S6D04D2
   /* Configure the FMC Parallel interface : SDRAM is used as Frame Buffer for TouchGFX */
  SDRAM_Init();
  displayInit();  //8080 LCD initialization 
#else
    /* Configure LCD */
    LCD_Config();

    /* Enable Layer 1 */
    LTDC_LayerCmd(LTDC_Layer1, ENABLE);

    /* Reload LTDC configuration  */
    LTDC_ReloadConfig(LTDC_IMReload);

    /* Enable The LCD */
    LTDC_Cmd(ENABLE);

    /* Set LCD foreground layer */
    LCD_SetLayer(LCD_FOREGROUND_LAYER);
#endif
    GPIO::init();

  }

 

/*************************************************************/

//STM32F4HAL.cpp as follows:

#include <touchgfx/hal/OSWrappers.hpp>
#include <platform/hal/ST/mcu/stm32f4x9/STM32F4HAL.hpp>
#include <platform/hal/ST/mcu/stm32f4x9/STM32F4DMA.hpp>
#include <touchgfx/lcd/LCD.hpp>
#include <touchgfx/hal/GPIO.hpp>

#include "stm32f4xx.h"

extern "C"{
 #include "SSD2805_S6D04D2.h"
 #include "stm32f4xx_exti.h"
}

uint16_t* STM32F4HAL::getTFTFrameBuffer() const
{
 #ifdef USE_SSD2805_S6D04D2
  return (uint16_t*)(0xD0000000); 
 #else
  return (uint16_t*)LTDC_Layer1->CFBAR;
 #endif
}


void STM32F4HAL::setTFTFrameBuffer(uint16_t* adr)
{
  LTDC_Layer1->CFBAR = (uint32_t)adr;

  /* Reload immediate */
  LTDC->SRCR = (uint32_t)LTDC_IMReload;
}

void STM32F4HAL::configureInterrupts()
{
 #ifdef USE_SSD2805_S6D04D2
 NVIC_SetPriority(LCD_TE_IRQn, 9);
 #else
  NVIC_SetPriority(DMA2D_IRQn, 9);
  NVIC_SetPriority(LTDC_IRQn, 9); 
 #endif
}

static uint16_t lcd_int_active_line;
static uint16_t lcd_int_porch_line;

/* Enable LCD line interrupt, when entering video (active) area */
void STM32F4HAL::enableLCDControllerInterrupt()
{
  lcd_int_active_line = (LTDC->BPCR & 0x7FF) - 1;
  lcd_int_porch_line = (LTDC->AWCR & 0x7FF) - 1;
  LTDC->LIPCR =  lcd_int_active_line;
  LTDC->IER = 1;
}

void STM32F4HAL::disableInterrupts()
{
 #ifdef USE_SSD2805_S6D04D2
   NVIC_DisableIRQ(LCD_TE_IRQn);
 #else
     NVIC_DisableIRQ(LTDC_IRQn);
   NVIC_DisableIRQ(DMA2D_IRQn);
 #endif
}

void STM32F4HAL::enableInterrupts()
{
 #ifdef USE_SSD2805_S6D04D2
  NVIC_EnableIRQ(LCD_TE_IRQn);
 #else
    NVIC_EnableIRQ(LTDC_IRQn);
    NVIC_EnableIRQ(DMA2D_IRQn); 
 #endif
}

volatile int hasVSync = 0;


extern "C"
__irq void LTDC_IRQHandler( void )
{
  if (LTDC->ISR & 1)
  {
    LTDC->ICR = 1;
    if (LTDC->LIPCR == lcd_int_active_line)
    {
      //entering active area
      LTDC->LIPCR = lcd_int_porch_line;
      touchgfx::OSWrappers::signalVSync();
      touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ);

    }
    else
    {
      //exiting active area
       LTDC->LIPCR = lcd_int_active_line;
       touchgfx::HAL::getInstance()->frontPorchEntered();
    }
  }
}

extern "C"
__irq void DMA2D_IRQHandler( void )
{
  if (DMA2D->ISR & 2)
  {
    DMA2D->IFCR = 2;
    touchgfx::HAL::getInstance()->signalDMAInterrupt();
  }
}

#ifdef USE_SSD2805_S6D04D2
extern "C"
 __irq void LCD_EXTI_IRQHandler(void)
{
   /* Make sure that interrupt flag is set */
    if (EXTI_GetITStatus(LCD_TE_EXTI_Line) != RESET) { 
    OSWrappers::signalVSync();
    //displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION); //force update the whole screen here by blunt force
    //displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
    //touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ); //debug
        /* Clear interrupt flag */
        EXTI_ClearITPendingBit(LCD_TE_EXTI_Line);
    }
}
#endif

/*************************************************************/

Nevertheless, by forcing displaySetArea() and displayXferFB() to run under EXTI_IRQHandler() does make the demo at least working. So I am fairly confident that once the framework is calling MyHAL::flushFrameBuffer the porting could be finished.

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

John,

You need to modify the touchgfx_generic_init() call to the following:

 MyHAL &hal= touchgfx_generic_init<MyHAL>(noDMA, display, tc, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0);

The template argument in <> is what determines the type of your HAL class. So you must here specify your MyHAL subclass in order for your function override to take effect.

It should not be necessary to do anything else than the above - do not enable C++11.

Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

Actually I tried that as well but it is still not updating.

MyHAL &hal= touchgfx_generic_init<MyHAL>(noDMA, display, tc, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0) still not able to update MyHAL ::flushframebuffer(). Something is not understood very well.

Remarks:

In declaration HAL& hal = touchgfx_generic_init<STM32F4HAL>(noDMA, display, tc, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0), no casting (HAL&) is required in front of touchgfx_generic_init.

However in MyHAL &hal= (MyHAL&) touchgfx_generic_init<MyHAL>(noDMA, display, tc, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0) . I needed to include a casting for MyHAL reference (MyHAL&) in front.

John

John

 

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

A second thought is to implement flushFrameBuffer() in STM32F4HAL.cpp.

/************************************************************/

STM32F4HAL.cpp

#include "SSD2805_S6D04D2.h"

.....

void STM32F4HAL::flushFrameBuffer(void)

    displaySetArea(0, 0, 240, 240);
    displayXferFB(0, 0, 240, 240, 0xD0000000);
}
   
void STM32F4HAL::flushFrameBuffer(const Rect& rect)
{
    displaySetArea(rect.x, rect.y, rect.width, rect.height);
    displayXferFB(rect.x, rect.y, rect.width, rect.height, 0xD0000000);
}

....

/*******************************************************************/

STM32F4HAL.hpp:

class STM32F4HAL:public HAL

{

 public:

  STM32F4HAL(touchgfx::DMA_Interface& dma, touchgfx::LCD& display, touchgfx::TouchController& tc, uint16_t width, uint16_t height) : touchgfx::HAL(dma, display, tc, width, height)
  {
  }

  virtual void flushFrameBuffer();
  virtual void flushFrameBuffer(const Rect& rect);

....

}

 

Will try it tomorrow to see what happen.

 

John

}

 

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Hi John,

Yes as an alternative you can also just modify the STM32F4HAL class instead of creating a MyHAL.

Regarding the problem when using MyHAL, I do not understand why this does not work. There are two potential problems here:

  1. Something wrong with the touchgfx_generic_init or MyHAL definition
  2. Initialization works, but TouchGFX core does not actually call the function.

To determine which, you can try temporarily call the flushFrameBuffer function explicitly:

void touchgfx_init() 
{
  MyHAL &hal= touchgfx_generic_init<MyHAL>(noDMA, display, tc,   DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0);
  hal.flushFrameBuffer(); //Does this invoke your MyHAL version of the   function?
}
Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

I tried temporarily calling the flushFrameBuffer function explicitly this morning.

In this way, I use the same MyHAL class and run hal.setFrameBuffer() right after touchgfx_generic_init initialization. Code snippet here:

  MyHAL& hal= (MyHAL &) touchgfx_generic_init<MyHAL>(noDMA, display, tc, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0, 0);
  hal.setFrameBufferStartAddress((uint16_t*)frameBuf0, 16, false, true);
  hal.flushFrameBuffer();

A breakpoint at hal.flushFrameBuffer() did refresh the LCD with content shown here. At least I could see the Living Room temperature bar on top.

At this point, I believe initialization of MyHAL is good, right?

However, this became a still frame after removing the breakpoint and get it run normally. That means the framework doesn't pump graphical content from OSWrappers::signalVSync() at 60Hz. I am running LCD_EXTI_IRQHandler() according to this snippet:

#ifdef USE_SSD2805_S6D04D2
extern "C"
 __irq void LCD_EXTI_IRQHandler(void)
{
   /* Make sure that interrupt flag is set */
    if (EXTI_GetITStatus(LCD_TE_EXTI_Line) != RESET) { 
    touchgfx::OSWrappers::signalVSync();
    //displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION); //force update the whole screen here by blunt force
    //displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
    //touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ); //debug
        /* Clear interrupt flag */
        EXTI_ClearITPendingBit(LCD_TE_EXTI_Line);
    }
}
#endif

Modifying STM32F4HAL.cpp for flushFrameBuffer(void) inside could not solve the problem either.

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

One more details. I set TE pin interrupt priority as 9, for an EXTI interrupt.

Does it matter?

void STM32F4HAL::configureInterrupts()
{
 #ifdef USE_SSD2805_S6D04D2
 NVIC_SetPriority(LCD_TE_IRQn, 9);
 #else
  NVIC_SetPriority(DMA2D_IRQn, 9);
  NVIC_SetPriority(LTDC_IRQn, 9); 
 #endif
}

 

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

John,

I just had a look at your files - it looks like you are using TouchGFX 4.3.0.

We did a bugfix for 4.4.0 which I believe may be the cause of flushFrameBuffer not being called. If you are indeed on 4.3.0, please update to the latest eval version and see if that fixes the problem. Because the code looks completely right to me.

Giving the EXTI interrupt a low priority is correct.

Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

I tried version 4.4.1 as well but the problem is still unsolved. It is frustrating. Let's keep this problem in mind for the time being. I will come back to this later. I will keep flushframebuffer() in EXTI irq for now.

What I am more concerned is on Touch Screen porting. This iPod Nano6 LCD has a CTP built-in. By reverse engineering I could use it with a CTP controller SSD2541 which is an I2C based driver. This is fairly to ST1232.

I read from the manual that sampleTouch(int32 &x, int32_t &y) is scanned in every VSync, in my case it is the same EXTI irq.

If touchGFX doesn't run flushframebuffer(), it won't run sampleTouch() either, right?

In this case, could I explicitly run sampleTouch() in EXTI irq like flushframebuffer()?

At the end, what do I miss if I run both flushframebuffer() and sampleTouch() just in EXTI irq?

 

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

One more thought. I am using just an evaluation version. Is it the reason why porting is not possible?

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

John,

The only difference between eval and licensed version is that there is no watermark in licensed core library.

If flushFrameBuffer is not called then I suspect sampleTouch is not called either. This underlying problem needs to be solved I'm afraid. Could you give me a few pointers regarding:

  • Have you verified that your EXTI int actually runs every 60Hz (e.g. toggle a pin and use a scope)
  • If you hook into NoTouchController::sampleTouch, check if that function is actually called every frame? 
  • It is not clear to me if your application actually works with the manual displayXfer call in EXTI int. If you do stuff every tick in your View's handleTickEvent function for instance, does that code actually run? Or does the application only display a still image (meaning that maybe TouchGFX only renders 1 frame ever)
  • If you switch to double buffering (use the default hal.setFrameBufferStartAddress function in touchgfx_init), does that alter the behavior?
  • Please verify that you have a call to hal.lockDMAToFrontPorch(false) in your touchgfx_init function.
Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

When all functions

touchgfx::OSWrappers::signalVSync(),

displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION), &

displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000)

got executed in EXTI irq, GUI is working. A movie to download from dropbox : https://www.dropbox.com/s/cpt1oz6p0uxd2na/2015-12-09%2012-00-16.061.wmv?dl=0

This is only true when double buffering has been disabled with

hal.setFrameBufferStartAddress((uint16_t*)frameBuf0, 16, false, false) &

hal.lockDMAToFrontPorch(false) to disable DMA lock to front porch.

Pin GPIOG_Pin_2 (VSYNC_FREQ) hooked to Ch1, TE hooked to Ch2 on a DSO. Traces are recorded here with a TE period at 60Hz, in phase with GPIO_Pin_2 toggle rate. The complete code in EXTI irq as below:

#ifdef USE_SSD2805_S6D04D2
extern "C"
 __irq void LCD_EXTI_IRQHandler(void)
{
   /* Make sure that interrupt flag is set */
    if (EXTI_GetITStatus(LCD_TE_EXTI_Line) != RESET) { 
    touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ); //debug 
    touchgfx::OSWrappers::signalVSync();
    displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION); //force update the whole screen here by blunt force
    displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
        /* Clear interrupt flag */
    EXTI_ClearITPendingBit(LCD_TE_EXTI_Line);
    }
}
#endif

When double buffering is enabled with hal.setFrameBufferStartAddress((uint16_t*)frameBuf0, 16, true, false), only the first frame got copied to LCD. No tick update from frame buffer anymore, hence a frozen frame got.

An interesting observation is that, when touchgfx::OSWrappers::signalVSync() has been removed leaving only displayXferFB() and displaySetArea(), only the first frame could be updated with a frozen frame forever. That means  touchgfx::OSWrappers::signalVSync() was doing the job to update graphic content from the framebuffer.

 

Another observation is a change in TE period and pulse width when touchgfx::OSWrappers::signalVSync() got executed in EXTI irq. TE timing should be a property of the LCD. It must not be altered with signalVSync(). In fact, TE should be a 50Hz signal instead of 60Hz. The frame rate was set to 50Hz in LCD initialization code. The reason why a shift in TE output is not clear yet.

John

 

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

I am trying to trace how flushframebuffer() is invoked from EXTI irq. With program stepping, EXI _irq is brached to OSWrappers::signalVSync() in OSWrappers.cpp, but I can't see any flushframebuffer() is invoked even when the onboard RGB LCD on STM32-disco is used.

Is it invoked from touchgfx_core.lib or what so I can't step thru' it?

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

The function OSWrappers::signalVSync is what triggers the GUI task to draw a single frame. So this function must be called periodically (in your EXTI int), otherwise nothing will happen.

The actual call to flushFrameBuffer happens inside the touchgfx core, as part of the frame rendering process, so you cannot single-step through this.

I am a bit concerned about calling displayXFer/displaySetArea from the EXTI int, since you should only place code that is extremely fast to execute inside an interrupt. I am guessing that those two functions take quite some time to execute, and may potentially disrupt your timing.

I am not sure I am able to remote-debug my way through this problem you are having. But just to clarify, the only thing you should need is the following (pseudo):

EXTI_int()
{
//Clear interrupt
OSWrappers::signalVSync();
}
class MyHAL : public STM32F4HAL
{
public:
void flushFrameBuffer(const Rect& r)
{
displaySetArea(r);
displayXferFB(r);
}
};
touchgfx_init()
{
MyHAL& hal = touchgfx_generic_init<MyHAL>( ..);
hal.flushFrameBuffer(rect); // test call to make sure overriding works
hal.setFrameBufferStartAddress(SDRAM_ADR, 16, false);
hal.lockDMAToFrontPorch(false);
}

You should remove the LCD interrupt handler (or at least make sure the LTDC controller is not running so you do not also risk to get calls to signalVSync etc. from there). The DMA interrupt handler should be kept in place if you use the STM32F4DMA class. If you use NoDMA then the dma interrupt handler is not necessary. The LCD and DMA handlers are in STM32F4HAL.cpp file.

 

Please double and triple check that the above is exactly what you are doing :)

Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

Let's take a step backward to tackle this problem.

I can see virtual void flushFrameBuffer(const Rect& rect) declared in HAL.hpp as the base class. Default "touchgfx_demo2014_small" uses LTDC as the TFT controller therefore it is no more than a port to touchgfx by itself. That means flushFrameBuffer() for LTDC should not be hidden in touchgfx core.

If this assumption is correct, I should be able to find LTDC's own flushFrameBuffer() somewhere in the source code, like STM32F4HAL::setTFTFrameBuffer(uint16_t* adr), STM32F4HAL::enableLCDControllerInterrupt(), etc. that override base class in HAL.hpp.

Why can't I find any flushFrameBuffer() in STM32F4HAL.cpp?

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

John,

When using the LTDC controller, there is no need to do anything special in flushFrameBuffer, since the LTDC will automatically transfer the data to the display. Therefore there is no implementation of flushFrameBuffer in the STM32F4HAL class, and the only place it needs to be implemented is if you're running on a target where you manually copy FB data to display (like in your case with 8080).

Did you verify regarding the code I wrote earlier? Does it make a difference whether or not you call the base HAL::flushFrameBuffer from your MyHAL::flushFrameBuffer()?

 

Søren Pingel Dalsgaard 0 votes
Comment actions Permalink
0
Avatar

Soren

Do you mean trying this?

  void flushFrameBuffer(void)
  {
   HAL::flushFrameBuffer(); //instead of STM32F4HAL::flushFrameBuffer()
   displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION);
   displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000); 
  }

 

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

Not sure if I am asking for too much. Actually I could send the hardware to you without the STM32-disco. That hardware stacks on STM32-disco so that you may run what I am doing...

Ignore this message if this is not good.

John

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

Found an important clue today. GPIO pin toggle function added to the new class MyHAL function flushFrameBuffer(const Rect& rect) as follows:

  void MyHAL::flushFrameBuffer(const Rect& rect)
  {
   // Remember to call base implementation
    HAL::flushFrameBuffer(rect);
   touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ); //debug 

//it should not be a whole screen update for Rect& rect but it is just

//for debug purpose as these are known to work in EXTI() _irq
   displaySetArea(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION);
   displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, 0xD0000000);
  }

Function flushFrameBuffer(const Rect& rect) got called periodically with a proof reflected by VSYNC_FREQ pin toggle from time to time. Because it is the first page from demo2014_small demo, only intermittent VSYNC_FREQ toggle could be observed as I believe, MyHAL::flushFrameBuffer(const Rect& rect) called only when there is a change in graphic content.

Although touchgfx::GPIO::toggle(touchgfx::GPIO::VSYNC_FREQ) got called, for some unknown reason, displaySetArea() and displayXferFB() don't work as expected in this function. It could be these functions called but not running or they simply never got called. As a result, only a frozen frame could be obtained at the moment when this class is instantiated.

Constructor of MyHAL modified to call MyHAL::flushFrameBuffer() when it is instantiated.

 class MyHAL : public STM32F4HAL
 {
   public :
  MyHAL(touchgfx::DMA_Interface& dma, touchgfx::LCD& display, touchgfx::TouchController& tc, uint16_t width, uint16_t height) : STM32F4HAL(dma, display, tc, width, height)
    {
   MyHAL::flushFrameBuffer(); //display the first frame when instantiated
    }
  
  virtual void flushFrameBuffer();
  virtual void flushFrameBuffer(const Rect& rect);
  
 };

 

My conclusion is:

displaySetArea() and displayXferFB() works in EXTI __irq, but not in function MyHAL::flushFrameBuffer(const Rect& rect) for some unknown reason. That is why a frozen frame only could be seen.

 

John

 

JohnLeung 0 votes
Comment actions Permalink
0
Avatar

Soren

OK, now I can be sure that displayXferFB() got called. I have changed this function to include a pin toggle on GPIOG_2:

void displayXferFB(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t frameBufferStart)
{
 __IO uint32_t write_pointer = (uint32_t)(DISP_HOR_RESOLUTION*y + x);
 
 uint32_t _area  = (uint32_t)width*height;
 uint32_t tdcSize = (uint32_t)2*width; //SSD2805 specific, set payload in bytes

 writeTDCSize(tdcSize);        //SSD2805 specific
 
 //set DCS write command (write command DCS_RAMWR)
 writeCmd(DCS_RAMWR);         //8080-IF with DCS_RAMWR to start writing to SRAM of SSD2805
 
 while(_area--)
 {
  GPIO_ToggleBits(GPIOG, GPIO_Pin_2);
  writeData(*(__IO uint16_t *)(frameBufferStart + write_pointer));
  write_pointer+=2;
 }
 
}

A burst of pin toggle happens intermittently. That means, this function called but content of SDRAM couldn't be copied to SRAM if it runs from flushFrameBuffer. On the other hand, if this function called from EXTI __irq directly, SDRAM data copying to SRAM is good.

 

How to solve it?

John

JohnLeung 0 votes
Comment actions Permalink