Post

3 followers Follow
0
Avatar

TouchGFX port for STM32F4xx (using 8080 I/F II Mode communication)

We intended to use the STMF429I-DISCOVERY Board as an electronics base for a proto (TouchGFX is already ported to this board).
We don’t want to use the (poor) TFT display of the board. We intend to use another TFT Display with the same controller (ILI 9341).

There’s one problem / challenge with the TFT we want to use. It cannot be connected via RGB Interface.
The TFT module can only be configured to communicate in 8080 I/F II Mode (CPU 16-bit I/F II or CPU 8-bit I/F II or CPU 18-bit I/F II or CPU 9-bit I/F II).

  • Is there a hardware layer port available which runs an STM32F429 (or similar) using the 8080 interface ?
  • If not: Can I port the hardware layer by myself (with a relatively low effort)?

Thanks for any response.

Stefan Lindegger

Official comment

Avatar

Hi Stefan,
We do not have an 8080 implementation I'm afraid. I think you'll be able to get something running relatively quickly though. TouchGFX calls a hook function whenever something in the frame buffer changes, and you'll need to implement this so it transfers the updated region to the display using 8080. This is explained in more detail in this knowledge base article: https://touchgfx.zendesk.com/hc/en-us/articles/203642951-MCUs-without-TFT-controller

You can also check out this forum post: https://touchgfx.zendesk.com/hc/communities/public/questions/201481402-Porting-TouchGFX-to-8080-based-LCDs

Maybe John has some additional tips he can share.

Søren Snehøj Nielsen

Please sign in to leave a comment.

9 comments

0
Avatar

Hi Søren
Thank's for your answer. I think with the descriptions in your linked articles, I should be able to port the HAL to 8080 interface.
Thanks a lot.
BR
Stefan

Stefan Lindegger 0 votes
0
Avatar

Stefan

Surely I could share the code for 8080 with FMC interface in this forum but it is sad to say, I am running into difficulty too!

I am using a bridge chip with 8080 interface which is similar to ILI9341 I believe, as long as they are both 8080. The pinout is summarized below:
//Pinout summary for FMC interfacing SSD2805 in 8080 16-bit IF
/** FMC GPIO Configuration

PF0 ------> FMC_A0 (DC)
PE7 ------> FMC_D4
PE8 ------> FMC_D5
PE9 ------> FMC_D6
PE10 ------> FMC_D7
PE11 ------> FMC_D8
PE12 ------> FMC_D9
PE13 ------> FMC_D10
PE14 ------> FMC_D11
PE15 ------> FMC_D12
PD8 ------> FMC_D13
PD9 ------> FMC_D14
PD10 ------> FMC_D15
PD14 ------> FMC_D0
PD15 ------> FMC_D1
PD0 ------> FMC_D2
PD1 ------> FMC_D3
PD4 ------> FMC_NOE (RD#)
PD5 ------> FMC_NWE (WR#)
PG9 ------> FMC_NE2 (CS#)
*/

The code works alone, in the sense tht this LCD interface works when no SDRAM got initialized.

Data read/write uses memory mapped IO as follows:
#define LCD_BANK_ADDR ((uint32_t)0x64000000) //bank2 address for FSMC SRAM
#define LCD_DC_CMD ((uint32_t)0x00) //DC line (FMC_A0(PF0)) for 8080-mode LCD
#define LCD_DC_DATA ((uint32_t)0x02)

#define writeData(ui16Data) (_IO uint16t) (LCD_BANK_ADDR + LCD_DC_DATA) = ui16Data
#define writeCmd(ui16Cmd) (_IO uint16t) (LCD_BANK_ADDR + LCD_DC_CMD) = ui16Cmd

The problem is that, this 8080-based using FMC interface to STM32F429. Whenever SDRAM got initialized, LCD is not working and vice versa.

How to make FMC for SDRAM and SRAM co-exist? This is the question.

Anyone has experience on this? Look forward to any suggestion.

John

JohnLeung 0 votes
0
Avatar

One more comment on this. From various application notes I do notice this statement regarding FSMC: "FSMC performs only one access at a time to an external device".

So, to make LCD in 8080 interface (SRAM interface) co-exists with SDRAM onboard of stm32f429-disco, does it mean we need to build a cache in internal memory of the MCU? Any DMA to copy directly data from SDRAM to SRAM of 8080-LCD?

JohnLeung 0 votes
0
Avatar

Hi John,

I have never tried using both SRAM and SDRAM simultaneously but I would be surprised if that did not work. I am basing this on that NOR+SDRAM certainly works, and NOR is the same principle as SRAM (static memory). Along the same lines I would guess that the "only one access at a time" statement simply means that a second access is delayed until first access completes. We certainly do not need a cache with simultaneous NOR+SDRAM.

There is an errata for MCU revisions Y and 1 of the STM32F429 which does not allow simulatenous static and dynamic memories. This was fixed in rev. 3. This could explain it, try checking your MCU rev.

Apart from that try posting on the ST forums. And keep us updated on this issue :)

Søren Snehøj Nielsen 0 votes
0
Avatar

Soren

It seems the problem last time was due to some stupid mistake with RCC setup. Making a good progress as I can initialize both SDRAM and LCD as SRAM now. A further question regarding framebuffer data read/write.

Looking at the file stm32f429i_discovery_sdram.c and find this function:

SDRAM_ReadBuffer(uint32_t * pBuffer, uint32_t uwReadAddress, uint32_t uwBufferSize).

 

Is it the function used by TouchGFX or there is another one packed inside touchgfx_core.lib?

As far as I know TouchGFX uses up to16-bit color, that is half of the pBuffer data pointer width. Should I pack two pixels into one  32-bit data or simply ignore the higher 16-bit ?

John

JohnLeung 0 votes
0
Avatar

Soren

Standalone driver with FMC initialized for SRAM(LCD) and SDRAM seems OK now. With SDRAM (framebuffer) filled with a pure color of red, green, and blue, the LCD refreshed with main program as below.

There remains to get the driver down to flushFrameBuffer(const Rect &rect) and activate TE pin for Vsync.

/**

* main.c

*/

 

#include "stm32f4xx.h"
#include "SSD2805_S6D04D2.h"
#include "stm32f429i_discovery_sdram.h"
#include "tick.h"

//void MX_GPIO_Init(void);
void SDRAM_LoadPage(uint16_t color)
{
 uint32_t val;
 
   /* Disable write protection */
  FMC_SDRAMWriteProtectionConfig(FMC_Bank2_SDRAM, DISABLE);
 
  /* Wait until the SDRAM controller is ready */
  while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)
  {
  }

 for(val = 0; val<115200UL; val+=2) //115200 = 240*240*2 bytes
 {
  *(uint32_t *) (SDRAM_BANK_ADDR + val) = color;
 }
}

int main(void)
{
 TICK_InitTick();
 SDRAM_Init();

 displayInit();
 displaySetArea(0, 0, 240, 240);

 for(;;)
 {
  SDRAM_LoadPage(0xF800); //Red
  displayXferFB(0, 0, 240, 240, SDRAM_BANK_ADDR);
  TICK_Delay(1000);
  SDRAM_LoadPage(0x07E0); //Green
  displayXferFB(0, 0, 240, 240, SDRAM_BANK_ADDR);
  TICK_Delay(1000);
  SDRAM_LoadPage(0x001F); //Blue
  displayXferFB(0, 0, 240, 240, SDRAM_BANK_ADDR);
  TICK_Delay(1000);
 }
}

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

/**

* SSD2805_S6D04D2.c

*/

#include "SSD2805_S6D04D2.h"
#include "tick.h"

#define Swap(x) (((uint16_t)x<<8) | ((uint16_t)x>>8))
#define DelayMs(ms) TICK_Delay(ms)


static __inline void writeReg(uint8_t ui8Reg, uint16_t ui16Data)
{
 writeCmd(ui8Reg);
 //insert wait here for slow clock & 320MHz, this is set with Timing.AddressSetupTime = 1 with FMC init
 //because address hold time = 1 HCLK. Especially important when MCU running at 168MHz (1 HCLK = 5.9ns!)
 writeData(ui16Data);
}

static __inline void writeTDCSize(uint32_t ui32TDCSize)
{
 writeReg(MCS_PSCR1, (uint16_t)ui32TDCSize);
 writeReg(MCS_PSCR2, (uint16_t)(ui32TDCSize>>16));
}

static void gpioFMCConfig(void);
/*
void gpioCSConfig(void)
{
 GPIO_InitTypeDef GPIO_InitStruct;
 
 GPIO_InitStruct.GPIO_Pin = LCD_CS_PIN;
 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //output push-pull mode
 GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
 
 GPIO_Init(LCD_CS_BASE, &GPIO_InitStruct);
 
 GPIO_WriteBit(LCD_CS_BASE, LCD_CS_PIN, Bit_SET);
}
*/
void gpioResetConfig(void)
{
 GPIO_InitTypeDef GPIO_InitStruct;
 
 GPIO_InitStruct.GPIO_Pin = LCD_RST_PIN;
 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //output push-pull mode
 GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_LCD_RST, ENABLE);
 
 GPIO_Init(LCD_RST_BASE, &GPIO_InitStruct);
 
 GPIO_WriteBit(LCD_RST_BASE, LCD_RST_PIN, Bit_SET); 
}

void gpioBacklightConfig(void)
{
 GPIO_InitTypeDef GPIO_InitStruct;
 
 GPIO_InitStruct.GPIO_Pin = LCD_BACKLIGHT_PIN;
 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //output push-pull mode
 GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_LCD_BACKLIGHT, ENABLE);
 
 GPIO_Init(LCD_BACKLIGHT_BASE, &GPIO_InitStruct);
 
 GPIO_WriteBit(LCD_BACKLIGHT_BASE, LCD_BACKLIGHT_PIN, Bit_RESET);
}

 /**
 * gpioFMCConfig() sets alternate function for GPIO pins for FMC
 * Pinout summary for FSMC interfacing SSD2805 in 8080 16-bit IF
  * FMC GPIO Configuration 
  PG9   ------> FMC_NE2 (CS#)
  PF0   ------> FMC_A0 (DC)
  PE7   ------> FMC_D4
  PE8   ------> FMC_D5
  PE9   ------> FMC_D6
  PE10   ------> FMC_D7
  PE11   ------> FMC_D8
  PE12   ------> FMC_D9
  PE13   ------> FMC_D10
  PE14   ------> FMC_D11
  PE15   ------> FMC_D12
  PD0   ------> FMC_D2
  PD1   ------> FMC_D3
  PD4   ------> FMC_NOE (RD#)
  PD5   ------> FMC_NWE (WR#)
  PD8   ------> FMC_D13
  PD9   ------> FMC_D14
  PD10   ------> FMC_D15
  PD14   ------> FMC_D0
  PD15   ------> FMC_D1
 */
static void gpioFMCConfig(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 
 //GPIO clock enable
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD |
 RCC_AHB1Periph_GPIOE |
 RCC_AHB1Periph_GPIOF |
 RCC_AHB1Periph_GPIOG, ENABLE);
 
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
 
 //PG9 configured as FMC function CS# (FMC_NE2)
 GPIO_PinAFConfig(GPIOG, GPIO_PinSource9, GPIO_AF_FMC);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
 GPIO_Init(GPIOG, &GPIO_InitStructure);
 
 //PF0 configured as FMC function DC (FMC_A0)
 GPIO_PinAFConfig(GPIOF, GPIO_PinSource0, GPIO_AF_FMC);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
 GPIO_Init(GPIOF, &GPIO_InitStructure); 
 
 //PE[7:15] configured as FMC data bus
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FMC);
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 |
                GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 |
                GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
                
 GPIO_Init(GPIOE, &GPIO_InitStructure);

 //PD0,1,4,5,8,9,10,14,15 configured as FMC data bus & WR#, RD#
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FMC);
 GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FMC);
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 |
                GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_9 |
                GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15;
                
 GPIO_Init(GPIOD, &GPIO_InitStructure);
}

void displayInit(void)
{
 FMC_NORSRAMInitTypeDef hsram1;

 /* Tick Init for 1msec clock tick for DelayMs() */
 //TICK_InitTick();
 
 /*-- FMC Configuration ------------------------------------------------------*/
 gpioFMCConfig();

  /* Enable the FMC/FSMC interface clock */
  //RCC->AHB3ENR         |= 0x00000001;
 RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FMC, ENABLE);
 
/*
#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
  // Configure and enable Bank1_SRAM2
  FMC_Bank1->BTCR[2]  = 0x00001011;
  FMC_Bank1->BTCR[3]  = 0x00000201;
  FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif // STM32F427_437xx || STM32F429_439xx
*/
 gpioResetConfig();   //configure reset line as gpio default high
 gpioBacklightConfig(); //configure backlight control as gpio default backlight off
 
 //Low level reset
 resetHigh();
 DelayMs(300);
 resetLow();
 DelayMs(10);
 resetHigh();
 DelayMs(10);
 
 /**
 * Set PLL. Before PLL of SSD2805 is started, the min WRX period
 * is 6T with T=1/20MHz with an external TX_CLK = 20MHz OSC onboard.
 * The min peiod for WRX = 300ns.
 * RCC set for HSI RC @16MHz to output such long WRX strobe pulse
 * with original OSC and CLK configuration saved
 */
 RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
 while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY)!=SET)
  ;

 hsram1.FMC_Bank        = FMC_Bank1_NORSRAM2;
 hsram1.FMC_DataAddressMux   = FMC_DataAddressMux_Disable;
 hsram1.FMC_MemoryType     = FMC_MemoryType_SRAM ;
 hsram1.FMC_MemoryDataWidth   = FMC_NORSRAM_MemoryDataWidth_16b;
 hsram1.FMC_BurstAccessMode   = FMC_BurstAccessMode_Disable;
 hsram1.FMC_WaitSignalPolarity = FMC_WaitSignalPolarity_Low ;
 hsram1.FMC_WrapMode      = FMC_WrapMode_Disable;
 hsram1.FMC_WaitSignalActive  = FMC_WaitSignalActive_BeforeWaitState;
 hsram1.FMC_WriteOperation   = FMC_WriteOperation_Enable;
 hsram1.FMC_WaitSignal     = FMC_WaitSignal_Disable;
 hsram1.FMC_ExtendedMode    = FMC_ExtendedMode_Disable;
 hsram1.FMC_AsynchronousWait  = FMC_AsynchronousWait_Disable;
 hsram1.FMC_WriteBurst     = FMC_WriteBurst_Disable ;
 hsram1.FMC_ContinousClock   = FMC_CClock_SyncOnly;

 /**
 *Timing measured in HCLK with MCU's SYSCLK=16MHz.
 *Before PLL of SSD2805 has been started, the min WRX period
 *is 6T with T=1/20MHz. Therefore the min peiod for WRX = 300ns
 *with an external TX_CLK = 20MHz OSC onboard 
 */
 hsram1.FMC_WriteTimingStruct->FMC_AddressSetupTime   = 5;
 hsram1.FMC_WriteTimingStruct->FMC_AddressHoldTime    = 0;
 hsram1.FMC_WriteTimingStruct->FMC_DataSetupTime     = 5; //5*1/16MHz @ 312.5ns > 300ns
 hsram1.FMC_WriteTimingStruct->FMC_BusTurnAroundDuration = 0;
 hsram1.FMC_WriteTimingStruct->FMC_AccessMode      = FMC_AccessMode_A;
 
 hsram1.FMC_WriteTimingStruct->FMC_AddressSetupTime    = 5;
 
 FMC_NORSRAMInit(&hsram1);
 FMC_NORSRAMCmd(LCD_FMC_BANK, ENABLE);
 
 /**
  *PLL  = clock*MUL/(PDIV*DIV)
  *   = clock*(BAh[7:0]+1)/((BAh[15:12]+1)*(BAh[11:8]+1))
  *   = 20*(0x0f+1)/1*1 = 20*16 = 320MHz
  *Remark: 350MHz >= fvco >= 225MHz for SSD2805 because the max. speed per lane is 350Mbps
  *Set PLL by writing a value 0x000f to MCS register at 0xBA
 */ 
 writeReg(MCS_PLCR, 0x000F);
 writeReg(MCS_PCR,  0x0001); //enable PLL
 DelayMs(2);         //wait 2ms for PLL start. 
 
 //Now switch it back to PLL clock for 180MHz
 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)!=SET)
  ;

 /**
 *
 * Now it is safe to set FMC running at higher speed.
 * Set clock control register for SYS_CLK & LP clock speed
 * SYS_CLK = TX_CLK/(BBh[7:6]+1), TX_CLK = external oscillator clock speed
 * In this case, SYS_CLK = 20MHz/(1+1)=10MHz. Measure SYS_CLK pin to verify it.
 * LP clock = PLL/(8*(BBh[5:0]+1)) = 320/(8*(4+1)) = 8MHz, conform to LG panel's spec, default LP = 8Mbps
 * S6D04D2 is the controller of LG 1.54" panel.
 */
 
 /**
 * Very important!!!
 * Compiler optimization level should be set for Level 1,2 with Optimize for Time enabled
 * Somehow, Optimization Level 3 doesn't work. Optimization for Time also required enable.
 *
 * This is important for the func writeReg(uint8_t ui8Reg, uint16_t ui16Data)
 * to insert a min. 9.4ns for PWcsh time in WR# even when SSD2805 running
 * at 320MHz, especially when Compiler optimization applied.
 * PWcsl at 9.4ns is the min. for SSD2805 at 320MHz PLL clock (3*T with T=3.125ns),
 * PWcsh also min. at 9.4ns.
 * For now, AddressSetupTime set 5 with PWcsh set 36ns, PWcsl set 12ns for a stable result.
 * Refresh time for a complete frame with 16-bit addressing for 240x240 pixels is calculated as :
 * Time = 240*240*(12+36)ns = 2.76ms. This update time is even less than the BSYNC (TE) blanking time
 * which is set to 5.6ms for frame update.
 */
 
 hsram1.FMC_WriteTimingStruct->FMC_AddressSetupTime  = 5;//5*HCLK = 29.7ns 
 hsram1.FMC_WriteTimingStruct->FMC_DataSetupTime   = 2;//2*HCLK = 12ns
 FMC_NORSRAMInit(&hsram1);
 FMC_NORSRAMCmd(LCD_FMC_BANK, ENABLE);
 
 writeReg(MCS_CCR, 0x0044);     //Set LP Clock here with divider = 5, i.e. LP clock = PLL/(8*(4+1)) = 8MHz
                   //SYSD_CLK divider set to 01 for SYS_CLK=TX_CLK/2=10MHz
                   //output sys_clk for debug. Now check sys_clk pin for 10MHz signal
 writeReg(MCS_TR,  0x0100);     //Enable SYS_CLK output with END and CO set 0 for MIPI lane data
                   //endianness and color order set here
 DelayMs(100);            //don't know why, this long delay is required. This value found

 /**
 * Set MIPI packet format.
 * EOT packet enable, write operation, it is a DCS packet
 * HS clock is disabled, video mode disabled, in HS mode to send data
 */
 writeReg(MCS_CFGR, 0x0243);   
 writeReg(MCS_PSCR3,0x0400);   //MCS_PSCR3:Packet size threshold, min=2, max=0x400
 
 //Set Virtual Channel (VC) to use
 writeReg(MCS_VCR, 0x0000);

 //Now write DCS command to LG panel for system power-on upon reset
 writeTDCSize(0);
 writeCmd(DCS_SLPOUT);        //DCS sleep-out command      
 
 DelayMs(100);            //wait for LG panel after sleep out 
 
 //Now configuration parameters sent to LG
  writeTDCSize(1);           //define TDC size to be 1
  writeReg(DCS_COLMOD, 0x0005);         //DCS command to set pixel format for 16-bit RGB
 
 //Turn on BSYNC pin (like TE), set to 5.6ms high pulse for frame update interval
 writeTDCSize(2);
 writeReg(0xF1, 0x5A5A);        //unlock MCS
 writeTDCSize(12);
 writeCmd(0xF2);
 writeData(0xF000);
 writeData(0x5603);
 writeData(0x0008);
 writeData(0x0001);
 writeData(0x0000);
 writeData(0x0356);
 writeTDCSize(2);
 writeReg(0xF1, 0xA5A5);        //lock MCS
 
 //Turn on Display
 writeTDCSize(0);  
 writeCmd(DCS_DISPON);         //display ON DCS command to LG panel
 DelayMs(1);              //from a blind trial!!!
 
 backlightOn();
}

void displaySetArea(uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
 uint16_t ec, ep; //ec=end column, ep=end page
 uint32_t tdcSize; //specific for SSD2805
 
 if (!width || !height) return;
 
 tdcSize = 4;
 writeTDCSize(tdcSize);
 writeCmd(DCS_CASET); //column address set
 writeData(Swap(x));  //set SC; swapping hibyte:lowbyte required

 ec = x + width -1;
 writeData(Swap(ec)); //set EC
 
 writeCmd(DCS_PASET); //page address set
 writeData(Swap(y));  //set SP
 
 ep = y + height - 1;
 writeData(Swap(ep)); //set EP
}

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--)
 {
  writeData(*(__IO uint16_t *)(frameBufferStart + write_pointer));
  write_pointer+=2;
 }
}

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

/**

* SSD2805_S6D04D2.h

*/

/**
* Prerequisite:
* 1. include search path ..\..\..\touchgfx\framework\include\platform\hal\ST\mcu\stm32f4x9\vendor
* for non-Cube driver provided by TouchGFX
* 2. include stm32f4xx_fmc.c, stm32f4xx_gpio.c, & stm32f4xx_rcc.c src code in project
* 3. tick timer for 1ms interval for DelayMs(ms)
*/

#ifndef SSD2805_S6D04D2_H_
#define SSD2805_S6D04D2_H_

#ifdef __cplusplus
extern "C"{
#endif

#include "stm32f4xx.h"
//#include "stm32f4xx_hal.h"
/*
 * ***************************************************************************************
 * Display Command Set (DCS) is a set of common commands for controlling
 * the display device in compliance with the DSI standard. Each command
 * is an eight-bit code with 00h to AFh assigned to the User Command Set
 * and all other codes assigned to the Manufacturer Command Set.
 * DCS defined here with prefix DCS_ and MCS with MCS_
 * ***************************************************************************************
 */
#define DCS_NOP                 0x00  //no operation. This is an empty command
#define DCS_SWRESET             0x01    //soft reset
#define DCS_RDNUMED       0x05  //read number of errors on DSI
#define DCS_RDDPM               0x0A    //Read display power mode
#define DCS_RDDMADCTL           0x0B    //Read display MADCTL for column address order, RGB/BGR order etc
#define DCS_RDDCOLMOD           0x0C    //Read display display pixel format
#define DCS_RDDIM               0x0D    //Read display image mode
#define DCS_RDDSM               0x0E    //Read display signal mode
#define DCS_RDDSDR              0x0F    //Read display self-diagnostic result
#define DCS_SLPIN               0x10  //sleep in command to enter the min. power mode
#define DCS_SLPOUT              0x11  //sleep out command to turn off sleep mode
#define DCS_NORON               0x13    //Normal display mode on
#define DCS_INVOFF              0x20  //Display inversion off
#define DCS_INVON               0x21  //Display inversion on
#define DCS_DISPOFF             0x28  //Display off with a blank page inserted. No change of frame memory content
#define DCS_DISPON              0x29  //Display on to recover from Display Off Mode

#define DCS_CASET           0x2A  //Column Address Set to define area of frame memory where MPU can access
                  //Parameters are inclusive, that means from SC[15:0] = 0 to EC[15:0] = 239
                  //for the panel resolution of 240 pixels in width
#define DCS_PASET           0x2B  //Page Address Set, similar to DCS_CASET, this command for SP[15:0] and EP[15:0]

#define DCS_RAMWR           0x2C    //command to transfer data from MPU to frame memory
                  //It is important to note that when this command is accepted, the column &
                  //page registers are reset to the Start Column/Start Page defined in DCS_CASET &
                  //DCS_PASET

#define DCS_RAMRD           0x2E  //This command is used to transfer data from display's frame memory back to MPU.
                  //When this command is accepted, the column & page registers are reset to the Start
                  //Column.Start Page defined in DCS_CASET & DCS_PASET

#define DCS_TEOFF           0x34  //Tearing effect line OFF

#define DCS_TEON            0x35  //Tearing effect line ON

#define DCS_MADCTL          0x36    //memory access control for frame buffer scanning direction etc
#define DCS_IDMOFF        0x38  //This command is used to recover from idle mode on
#define DCS_IDMON          0x39  //This command is used to enter into idle mode to reduce to 8 color depth
#define DCS_COLMOD          0x3A    //interface pixel format set
#define DCS_RAMWRC          0x3C    //This command continues writing to memory after DCS_RAMWR. When this command
                  //is accepted, no column/page registers are reset. This command is good for
                  //writing data to frame memory in block for speed optimization
#define DCS_RAMRDC       0x3E  //Counterpart for DCS_RAMWRC to "block read" from frame memory to MPU
#define DCS_RDDDBS     0xA1  //Returns supplier ID
#define DCS_RDDDBC     0xA8  //continue reading supplier's ID after DCS_RDDDBS has been interrupted

//SSD2805 specific registers
#define MCS_CFGR    0xB7 //configuration register
#define MCS_VCR     0xB8 //VC control register to set virtual channel
#define MCS_PCR     0xB9 //PLL control register
#define MCS_PLCR    0xBA //PLL config register
#define MCS_CCR     0xBB //Clock control register

#define MCS_PSCR1    0xBC //Packet size control register 1, specifies the total number of data bytes (in byte)
               //to be transmitted by SSD2805 in the next operation.
#define MCS_PSCR2    0xBD //Packet size control register 2
#define MCS_PSCR3    0xBE //Packet size threshold, min=2, max=0x400

#define MCS_TR     0xD6 //Test register

//physical dimension of the display
#define DISP_HOR_RESOLUTION 240
#define DISP_VER_RESOLUTION 240

//#define LCD_CS_BASE  GPIOG
//#define LCD_CS_PIN  GPIO_Pin_9

#define LCD_RST_BASE GPIOD
#define LCD_RST_PIN  GPIO_Pin_2
#define RCC_AHB1Periph_LCD_RST RCC_AHB1Periph_GPIOD

#define LCD_TE_BASE         GPIOA
#define LCD_TE_PIN         GPIO_Pin_9
#define LCD_TE_EXTI         EXTI_Line9            //this is to match GPIOA9, level shifted by a mosfet from 1.8V -> 3.3V
#define LCD_TE_IRQn         EXTI9_5_IRQn           //this is to match GPIOA9 interrupt handler number to line 5 to 9
#define EXTI_IRQHandler       EXTI9_5_IRQHandler //this is to match EXTI_IRQ handler in BoardConfiguration.cpp
#define LCD_TE_RCC_AHB1Periph_GPIO  RCC_AHB1Periph_GPIOA
#define LCD_TE_PortSourceGPIO    EXTI_PortSourceGPIOA
#define LCD_TE_PinSource      EXTI_PinSource9

#define LCD_BACKLIGHT_BASE GPIOD
#define LCD_BACKLIGHT_PIN  GPIO_Pin_7
#define RCC_AHB1Periph_LCD_BACKLIGHT RCC_AHB1Periph_GPIOD

#define LCD_BANK_ADDR   ((uint32_t)0x64000000) //bank2 address for FSMC SRAM
#define LCD_FMC_BANK   FMC_Bank1_NORSRAM2   //hardware specific with NE2 wired to CS# of SSD2805
#define LCD_DC_CMD      ((uint32_t)0x00)    //DC line (FMC_A0(PF0)) for 8080-mode LCD
#define LCD_DC_DATA    ((uint32_t)0x02) 


 /** GPIO pins
 PD2  ------> RESET
 PD7  ------> Backlight enable
 */

//Pinout summary for FSMC interfacing SSD2805 in 8080 16-bit IF
  /** FMC GPIO Configuration 
  PF0   ------> FMC_A0 (DC)
  PE7   ------> FMC_D4
  PE8   ------> FMC_D5
  PE9   ------> FMC_D6
  PE10   ------> FMC_D7
  PE11   ------> FMC_D8
  PE12   ------> FMC_D9
  PE13   ------> FMC_D10
  PE14   ------> FMC_D11
  PE15   ------> FMC_D12
  PD8   ------> FMC_D13
  PD9   ------> FMC_D14
  PD10   ------> FMC_D15
  PD14   ------> FMC_D0
  PD15   ------> FMC_D1
  PD0   ------> FMC_D2
  PD1   ------> FMC_D3
  PD4   ------> FMC_NOE (RD#)
  PD5   ------> FMC_NWE (WR#)
  PG9   ------> FMC_NE2 (CS#)
  */

/* Read/Write Buffers */
//extern uint16_t FrameBuffer[DISP_VER_RESOLUTION][DISP_HOR_RESOLUTION];

/**
* @func  extern void DisplayCSConfig(void)
* @brief This function is required to over-ride FSMC NEx Alternate function
     for CS# strobe. FSMC NEx strobes in every data/command write cycle.
     On the contrary, SSD2805 requires single CS# strobe for multiple
     command data.
* @param None
* @return None
*/
//extern void gpioCSConfig(void);
extern void gpioResetConfig(void);
extern void gpioBacklightConfig(void);
extern void displayInit(void);
extern void displaySetArea(uint16_t x, uint16_t y, uint16_t width, uint16_t height);
extern void displayXferFB(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t frameBufferStart);

/**
* @func  flushFrameBuffer(const Rect &rect)
* @brief In order to transfer the contents of the frame buffer to the display,
*     override the function HAL::flushFrameBuffer(const Rect&).
*     This function is called at the end of each frame,
*     with the parameter Rect describing the area of the frame
*     buffer that was changed.
*     This area must then be transferred to the display using the
*     appropriate communication protocol.
* @param const Rect& rect pass by reference for a rectangle
* @return None
*/
//extern void flushFrameBuffer(const Rect& rect);


//#define csLow()         GPIO_WriteBit(LCD_CS_BASE, LCD_CS_PIN, Bit_RESET)
//#define csHigh()        GPIO_WriteBit(LCD_CS_BASE, LCD_CS_PIN, Bit_SET)

#define resetHigh()       GPIO_WriteBit(LCD_RST_BASE, LCD_RST_PIN, Bit_SET)
#define resetLow()       GPIO_WriteBit(LCD_RST_BASE, LCD_RST_PIN, Bit_RESET)

#define backlightOff()     GPIO_WriteBit(LCD_BACKLIGHT_BASE, LCD_BACKLIGHT_PIN, Bit_RESET)
#define backlightOn()      GPIO_WriteBit(LCD_BACKLIGHT_BASE, LCD_BACKLIGHT_PIN, Bit_SET)

#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

#ifdef __cplusplus
}
#endif

#endif

 

 

 

 

JohnLeung 0 votes
0
Avatar

John,

Thanks for sharing this!

As for your questions, the SDRAM_ReadBuffer() function is not used by TouchGFX. We have our own function (HAL::blockCopy) which handles unaligned transfers in a more optimal way.

The frame buffer format is packed, so an aligned 32-bit word will contain data for two pixels.

Søren Snehøj Nielsen 0 votes