Author Topic: Adding Colour Displays to Embedded Projects  (Read 1642 times)

0 Members and 1 Guest are viewing this topic.

Jason

  • Administrator
  • *
  • Posts: 728
  • I love guitars
  • Respect: +62
Adding Colour Displays to Embedded Projects
« on: January 14, 2011, 09:17:54 AM »
+1
Adding A Colour Display to an Embedded Project
Traditionally, most projects of an embedded nature have either used alphanumeric LCD displays, or more recently, black and white graphics LCD modules. They are simple to operate and relatively cheap. Thanks to the mobile market, colour LCD displays have become available, and at low cost. I want to expand my GNU graphics library for embedded systems, so I acquired a suitable LCD module (with touchscreen) for the job.

Choosing a Display
Most colour displays have been the cellphone size i.e. 80 x 60 pixels and somewhat bigger. A decent size, for use in the industrial automation and kinds of fields I work in would have to be at least 320 x 240, so I acquired one, via a local distributor, manufactured by WINSTAR, which is a TFT with a built in graphics controller. Most TFT panels are just a video interface i.e. HSYNC, VSYNC and a pixel bus. This module, includes a graphics controller, the SSD1963 (which I have reasons to suspect, is what was running in the Nokia N70). The module I chose was the WF35DTIBCDE, a 3.5 inch TFT panel, with controller and touchscreen.

The first problem
Most of these displays have what is known as a FFC connector. FFC stands for (Flex Foil Connector) which is a flexible plastic strip with conductive traces, which plugs into thin slotted connectors. To counteract this problem and to provide a decent platform to hold the thing together, I usually make breakout boards. So, just before Christmas, I quickly had one made, just as the PCB manufacturer was closing for the holidays. I assembled it during my holidays, and it looks like this:


The above board provides a connection point to normal wires, which I can then wire into a development board, or into a breadboard as in this case I will be doing. The interface to the controller is in the form of an 8-bit databus and control lines, and power. The display operates at 3.3V and includes a backlight. The interface is designed to be attached to the Intel Architecture i.e. 80x86, 8051 and virtually all Intel processors and microcontrollers, and mapped into the memory space.

Bus Interface
Most embedded microcontrollers today do not have exposed busses, and consequently all memory is inside the chip. This is done for reasons of economy and to make it easy to comply with CE mark requirements. They do however have copious numbers of I/O ports. These I/O ports, are mostly 8 bits wide and each bit can be configured to be either an input, or an output. Some port pins can be configured to connect to internal peripherals such as the UARTs, timers, SPI/I2C and other interfaces, not to mention A/D and D/A converters. Now, the lack of exposed bus, presents a problem at first glance, however, not strictly so. The solution to drive this kind of device from I/O ports is to use the tried-and-tested method of bit-banging. So for this project we will bit-bang the x86 bus.

The LCD therefore has the following signals:

RESET        = An active low signal, when pulled to logic 0, the LCD controller logic is reset
RD              = READ. When pulled low, the LCD controller will output data onto the bus, which can then be read by the microprocessor
WR             = WRITE. When pulled low, the LCD controller will accept data from the microprocessor and store it internally.
CS              = CHIP SELECT. When asserted low, the LCD controller will respond to the microprocessor. This is used traditionally with
                     address decoding logic to map the display into the microprocessor's memory or I/O map.
RS              = REGISTER SELECT. This pin if tied low (0) allows the microprocessor to access the control registers and command register. If tied
                     high(1) it allows the microprocessor to access the graphics memory buffer
D0 ~ D7     = Databus. This is the bi-directional data bus used to read/write data from the graphics controller.

Now, to get this to work, I have to connect it to my choice of microcontroller. I have a Freescale MC9S08DZ60 available, and it has some I/O ports. So what we do is connect it up as follows:

D0 ~ D7 -> PTA0 ~ PTA7. All data pins to the entire set of PORT A pins. This means a byte can be written in one go to the LCD. The control signal pins can be connected as desired. I randomly wired them up as follows:

CS -> PTE2 (PORT E bit 2)
WR -> PTE3
RD -> PTE4
RS -> PTE5
RESET ->PTD0 (PORT D bit 0)

Now, once all that was done, it looked like this:


Now, let's take a closer look:


The power supply is 3.3V. Since it was Dec holidays and the stores are closed, I hacked this item out of a MultiChoice DSD990 STB.
(I installed it into a packaging case of a hard drive to protect myself or my son from being electrocuted)
The In-Circuit Debugger connects via USB to my PC and integrates into Freescale CodeWarrior (formerly Metrowerks CodeWarrior).

Getting Started
Now, the time is to get this baby going. Let's start writing some code to perform some basic initialization.
The first step is to create an empty project in Codewarrior, using the wizard. Once that's done lets start doing some work.

First we define our hardware in main.c
Quote
/* Hardware Definitions */
#define LED PTDD_PTDD1
#define CS PTED_PTED2
#define WR PTED_PTED3
#define RD PTED_PTED4
#define RS PTED_PTED5
#define RESET PTDD_PTDD0


To then correctly emulate the 80x86 databus, we need to set up the ports correctly when the micro starts, let's just do that in main()
Quote
void main(void) {

          InitCOP();                    /* Disable watchdog for now */
          InitPLL();                    /* Set up PLL and clock CPU @ 40MHz */

          /* Initialise I/O ports */         
          InitPTA(0xFF, 0x00);
          InitPTE(0x3C, 0x3C);
          InitPTD(0x03, 0x01);


          InitRTC(0x13);                /* Initialise the RTC timer for 10mS */
          TIMER_COUNTER = 0;
          PRESCALER = 0;
          EnableInterrupts;                /* Enable Interrupts (macro) */


And now, the magic part: let's create the workhorse function calls to do the bit-banged bus interface:
Quote
void Write_Command (unsigned char command){
   RD = 1;                       /* Ensure read signal is high */
   RS = 0;                       /* RS = 0, access command register */
   WR= 0;                        /* Pull WR signal low */
   CS = 0;                       /* Pull CS signal low, LCD controller is now in write cycle */
   PTAD = command;               /* Present data to LCD controller */
   CS = 1;                       /* Pull CS high to end "bus cycle" */
   WR= 1;                        /* Pull WR to finish the end of the "bus cycle" */
}
void Write_Data (unsigned char data1){
   RD = 1;                       
   RS = 1;
   WR = 0;
   CS = 0;
   PTAD = data1;
   CS = 1;
   WR= 1;
}


Now that we have those two routines in place, we can start with initialization code to get the LCD set up correctly. That will be covered in Part 2  :D
« Last Edit: January 14, 2011, 09:50:17 AM by Jason »
Be conservative in what you send, be liberal in what you accept from others- Jon B Postel
 

Jason

  • Administrator
  • *
  • Posts: 728
  • I love guitars
  • Respect: +62
Part Two
« Reply #1 on: January 14, 2011, 11:04:21 PM »
0
Part Two - Getting some stuff to work
Okay, we now have those basic routines as shown, to talk to the display. I optimized them further, removing some stuff that was not needed i.e. code to constantly assert RD as high, and the result was as follows:



As can be seen, that is definitely not right.

So what can be wrong?
Well, I have precious little detail about the underlying TFT panel, in order to program the graphics controller to provide the right sort of signalling. So off I send e-mail to Winstar. While I waited for the LCD manufacturer to respond to my e-mail, here is the code listing we have thus far.

main.c
Quote
/*-------------------------------------------------------------------------------------------------------
        Winstar colour LCD Driver Development
        Author: Jason Mitchell
        Date: 4/01/2011
       
        Revision History
        ----------------
        04/01-2011 - 1st attempt to get LCD going... getting partial frame and black line down the middle
--------------------------------------------------------------------------------------------------------- */
       
/* Include Files */
#include <hidef.h>          /* for EnableInterrupts macro */
#include "derivative.h"       /* include peripheral declarations */
#include "dz.c"         /* Common routines for DZ series micros */

/* Global Variables */
unsigned int TIMER_COUNTER;
unsigned int PRESCALER;

unsigned char n; 
unsigned char toggle;

/* Hardware Definitions */
#define LED PTDD_PTDD1
#define CS PTED_PTED2
#define WR PTED_PTED3
#define RD PTED_PTED4
#define RS PTED_PTED5
#define RESET PTDD_PTDD0

void Timer(unsigned int delay){
   TIMER_COUNTER = delay;
   while(TIMER_COUNTER != 0){
      ;
   }
}         

void PulseTimer(void){
   unsigned char tdelay;
   for(tdelay = 0; tdelay < 120; tdelay++){
      ;
   }
}
void CmdTimer(void){
   unsigned int cmd_delay;
   for(cmd_delay = 0; cmd_delay < 240; cmd_delay++){
      ;
   }
}

void WriteLCDCmd(unsigned char data){
   RS = 0;      /* Select command register */
   WR= 0;       /* Assert WR = 0 */
   CS = 0;       /* Assert CS, LCD controller is now reading data */
   PTAD = data;    /* Put byte to be written on D0 ~ D7 */
   CS = 1;       /* Latch it into LCD controller */
   WR= 1;       /* Finish "bus cycle" */
}

void WriteLCDData(unsigned char data){
   RS = 1;      /* Select data register */
   WR = 0;      /* Assert WR = 0 */
   CS = 0;      /* Assert CS, LCD controller is now reading data */
   PTAD = data;        /* Put byte to be written on port to D0 ~ D7 */
   CS = 1;      /* Latch it into LCD controller */
   WR= 1;       /* Finish "BUS CYCLE" */
}

void InitLCD(void){
    RESET = 0;
    Timer(100);
    RESET = 1;
    Timer(100);
    WriteLCDCmd(0x11);  /* Exit sleep mode */
    WriteLCDCmd(0x13);  /* Fullscreen LCD operation mode */
    WriteLCDCmd(0xB0);
    WriteLCDData(0x24); //1
    WriteLCDData(0x00); //2
    WriteLCDData(0x01); //3
    WriteLCDData(0x41); //4
    WriteLCDData(0x00); //5
    WriteLCDData(0xF1); //6
    WriteLCDData(0x00); //7
   
    WriteLCDCmd(0xB6);
    WriteLCDData(0x01); //1
    WriteLCDData(0xEF); //2
    WriteLCDData(0x00); //3
    WriteLCDData(0x07); //4
    WriteLCDData(0x01); //5
    WriteLCDData(0x00); //6
    WriteLCDData(0x00); //7
   
    /* Configure PLL */
    WriteLCDCmd(0xE2);
    WriteLCDData(0x08); //1
    WriteLCDData(0x01); //2
    WriteLCDData(0x54); //3

    WriteLCDCmd(0xE0);
    WriteLCDData(0x01); //1 
    Timer(10);

    WriteLCDCmd(0xE0);
    WriteLCDData(0x03); //1
    WriteLCDCmd(0x01);
   
   
   
    WriteLCDCmd(0x29);  /* LCD on */
   
 
 
}



/*                            Main Function
-------------------------------------------------------------------------------- */

void main(void) {

          InitCOP();                    /* Disable watchdog for now */
          InitPLL();                    /* Set up PLL and clock CPU @ 40MHz */

          /* Initialise I/O ports */         
          InitPTA(0xFF, 0x00);
          InitPTE(0x3C, 0x3C);
          InitPTD(0x03, 0x01);

          InitRTC(0x13);                /* Initialise the RTC timer for 10mS */
          TIMER_COUNTER = 0;
          PRESCALER = 0;
          EnableInterrupts;                /* Enable Interrupts (macro) */

          /* LCD Driver development code
          ------------------------------- */
          Timer(100);
   InitLCD();


          for( ;; ) {
                ;
          }
}


/* Real Time Interrupt Timer */

 interrupt 25 void RTCInt(void) {         
          RTCSC |= (RTCSC_RTIF_MASK);   /* Clear interrupt flag */
         
          /* System timer */
          if (TIMER_COUNTER > 0){
             TIMER_COUNTER--;
          }
          /* Check prescaler for slow tick-rate */
          PRESCALER++;                      /* Increment value */
          if (PRESCALER == 100){
                              LED = !LED;  /* toggle LED pin */
                              PRESCALER = 0; /* Reset the prescaler */
          }
 }
Be conservative in what you send, be liberal in what you accept from others- Jon B Postel
 

Jason

  • Administrator
  • *
  • Posts: 728
  • I love guitars
  • Respect: +62
Part 3
« Reply #2 on: January 14, 2011, 11:48:43 PM »
0
Part 3 - Getting the parameters correct
The LCD manufacturer sent me an application note with the correct parameters to get this display up and running. I got it on 5/01/2010
The question that now arises is, what can we expect to see on the LCD if all is well?
Well to answer that question, you need to know that computer memory, when power is applied will have all kinds of random data
stored at each address. With monochrome LCD's you will generally see what I call the starry sky which is a space kinda screen, with random dots all over the space. So, for a colour LCD I expect to see random coloured dots filling the screen.

I rewrote the LCDInit() function in main.c...
Quote
/*-------------------------------------------------------------------------------------------------------
        Winstar colour LCD Driver Development
        Author: Jason Mitchell
        Date: 4/01/2011
       
        Revision History
        ----------------
        04/01/2011 - 1st attempt to get LCD going... getting partial frame and black line down the middle
        13/01/2011 - 2nd attempt, using info from LCD manufacturer, successful LCD initialization
--------------------------------------------------------------------------------------------------------- */
       
/* Include Files */
#include <hidef.h>          /* for EnableInterrupts macro */
#include "derivative.h"       /* include peripheral declarations */
#include "dz.c"         /* Common routines for DZ series micros */

/* Global Variables */
unsigned int TIMER_COUNTER;
unsigned int PRESCALER;

unsigned char n; 
unsigned char toggle;

/* Hardware Definitions */
#define LED PTDD_PTDD1
#define CS PTED_PTED2
#define WR PTED_PTED3
#define RD PTED_PTED4
#define RS PTED_PTED5
#define RESET PTDD_PTDD0

void Timer(unsigned int delay){
   TIMER_COUNTER = delay;
   while(TIMER_COUNTER != 0){
      ;
   }
}         

/* LCD Control routines */

/* Name: WriteLCDCmd()
   Function: Writes byte to SSD1963 command register
------------------------------------------------------- */   
void WriteLCDCmd(unsigned char data){
   RS = 0;      /* Select command register */
   WR= 0;       /* Assert WR = 0 */
   CS = 0;       /* Assert CS, LCD controller is now reading data */
   PTAD = data;    /* Put byte to be written on D0 ~ D7 */
   CS = 1;       /* Latch it into LCD controller */
   WR= 1;       /* Finish "bus cycle" */
}

/* Name: WriteLCDData()
   Function: Writes byte to SSD1963 data register
------------------------------------------------------- */   
void WriteLCDData(unsigned char data){
   RS = 1;      /* Select data register */
   WR = 0;      /* Assert WR = 0 */
   CS = 0;      /* Assert CS, LCD controller is now reading data */
   PTAD = data;        /* Put byte to be written on port to D0 ~ D7 */
   CS = 1;      /* Latch it into LCD controller */
   WR= 1;       /* Finish "BUS CYCLE" */
}

/* Name: CommandWrite
   Function: Single-stage command and argument write, required for some commands
----------------------------------------------------------------------------------- */   
void CommandWrite(unsigned char command,unsigned char data){
   WriteLCDCmd(command);
   WriteLCDData(data);
}

/* Name: InitLCD()
   Function: Initialise the LCD controller and make it operational
--------------------------------------------------------------------*/
void InitLCD(void){
   RESET = 0;      /* Pull reset line low */
   Timer(1);         /* 10mS delay */
   RESET = 1;      /* Pull reset line high */
   Timer(1);         /* 10mS delay */
   WriteLCDCmd(0x01);       /* Perform software reset sequence */
   WriteLCDCmd(0x01);
   WriteLCDCmd(0x01);

   CommandWrite(0xe0,0x01);    /* Start up the PLL to generate graphics clock */
   CommandWrite(0xe0,0x03);    /* Lock the PLL on frequency */
   WriteLCDCmd(0xb0);       /* Set mode: 18 bit TFT */
   WriteLCDData(0x0c);    /* SET TFT MODE & hsync+Vsync+DEN MODE */
   WriteLCDData(0x80);    /* SET TFT MODE & hsync+Vsync+DEN MODE */
   WriteLCDData(0x01);    /* SET horizontal size=320-1 High Byte */
   WriteLCDData(0x3f);    /* SET horizontal size=320-1 Low Byte  */
   WriteLCDData(0x00);    /* SET vertical size=240-1 High Byte   */
   WriteLCDData(0xef);    /* SET vertical size=240-1 Low Byte    */
   WriteLCDData(0x00);    /* SET even/odd line RGB seq = RGB     */
   CommandWrite(0xf0,0x00);    /* SET pixel data interface format = 8 bit */
   CommandWrite(0x3a,0x60);    /* SET R G B format = 6 6 6       */         
   WriteLCDCmd(0xe2);       /* SET PLL freq = 113.33MHz graphics core clock */
   WriteLCDData(0x22);
   WriteLCDData(0x03);
   WriteLCDData(0x04);
   WriteLCDCmd(0xe6);       /* SET PCLK freq = 19MHz pixel clock   */
   WriteLCDData(0x00);    
   WriteLCDData(0xea);    
   WriteLCDData(0xec);    
   WriteLCDCmd(0xb4);       /* SET Horizontal back porch */
   WriteLCDData(0x01);    /* SET HSYNC Total = 440 pixels */
   WriteLCDData(0xb8);
   WriteLCDData(0x00);    /* SET horizontal front porch = 68 */
   WriteLCDData(0x44);
   WriteLCDData(0x0f);    /* SET Vertical back porch 16 = 15 + 1 */
   WriteLCDData(0x00);    /* SET Hsync pulse start position */
   WriteLCDData(0x00);
   WriteLCDData(0x00);    /* SET Hsync pulse subpixel start position */
   WriteLCDCmd(0xb6);       /* SET Vertical back porch */
   WriteLCDData(0x01);    /* SET Vsync total 265 = 264 + 1 */
   WriteLCDData(0x08);
   WriteLCDData(0x00);    /* SET Vertical front porch = 18 */
   WriteLCDData(0x12);
   WriteLCDData(0x07);    /* SET VSYNC PULSE WIDTH pulse 8 = 7 + 1 */
   WriteLCDData(0x00);    /* SET VSYNC pulse start position */
   WriteLCDData(0x00);
   WriteLCDCmd(0x2a);       /* SET column address */
   WriteLCDData(0x00);    /* SET start column address = 0 */
   WriteLCDData(0x00);
   WriteLCDData(0x01);    /* SET end column address = 320 */
   WriteLCDData(0x3f);
   WriteLCDCmd(0x2b);       /* SET page address */
   WriteLCDData(0x00);    /* SET start page address = 0 */
   WriteLCDData(0x00);
   WriteLCDData(0x00);    /* SET end page address = 240 */
   WriteLCDData(0xef);
   WriteLCDCmd(0x29);       /* Enable display */
   WriteLCDCmd(0x2c);   
   
}



/*                            Main Function
-------------------------------------------------------------------------------- */

void main(void) {

          InitCOP();                    /* Disable watchdog for now */
          InitPLL();                    /* Set up PLL and clock CPU @ 40MHz */

          /* Initialise I/O ports */         
          InitPTA(0xFF, 0x00);
          InitPTE(0x3C, 0x3C);
          InitPTD(0x03, 0x01);

          InitRTC(0x13);                /* Initialise the RTC timer for 10mS */
          TIMER_COUNTER = 0;
          PRESCALER = 0;
          EnableInterrupts;                /* Enable Interrupts (macro) */

          /* LCD Driver development code
          ------------------------------- */
          Timer(100);          /* Wait 1 second */
   InitLCD();         /* Initialize the LCD */


          for( ;; ) {
                ;
          }
}


/* Real Time Interrupt Timer */

 interrupt 25 void RTCInt(void) {         
          RTCSC |= (RTCSC_RTIF_MASK);   /* Clear interrupt flag */
         
          /* System timer */
          if (TIMER_COUNTER > 0){
             TIMER_COUNTER--;
          }
          /* Check prescaler for slow tick-rate */
          PRESCALER++;                      /* Increment value */
          if (PRESCALER == 100){
                              LED = !LED;  /* toggle LED pin */
                              PRESCALER = 0; /* Reset the prescaler */
          }
 }


So I hit F7 to compile it and then I ran the debugger....

Fuck yeah!!!
Be conservative in what you send, be liberal in what you accept from others- Jon B Postel
 

Jason

  • Administrator
  • *
  • Posts: 728
  • I love guitars
  • Respect: +62
Part 4 - Colour and gamut testing
« Reply #3 on: January 15, 2011, 12:13:07 AM »
0
Part 4 - Colour and Gamut testing
Now that something was working, time to add a routine to fill the screen with one colour so that we can do some tests and checks
to see how well this thing can represent colours.

So I wrote the following routine FillScreen(), which basically writes every pixel to the same specified RGB values....
Quote
/* Name: WritePixelData
   Function: Writes 3 pixel bytes (24-bit color) to the graphics memory
------------------------------------------------------------------------- */
void WritePixelData(unsigned char red, unsigned char green, unsigned char blue){
   WriteLCDData(red);
   WriteLCDData(green);
   WriteLCDData(blue);   
   
}
/* Name: WindowSet
   Function: Set current display window co-ordinates
------------------------------------------------------- */   
void WindowSet(unsigned int s_x,unsigned int e_x,unsigned int s_y,unsigned int e_y){
   WriteLCDCmd(0x2a);       /* SET page address */
   WriteLCDData((s_x)>>8 );    /* SET start page address = 0     */
   WriteLCDData(s_x);
   WriteLCDData((e_x)>>8 );    /* SET end page address = 320     */
   WriteLCDData(e_x);
   WriteLCDCmd(0x2b);       /* SET column address     */
   WriteLCDData((s_y)>>8 );    /* SET start column address = 0 */
   WriteLCDData(s_y);
   WriteLCDData((e_y)>>8 );    /* SET end column address = 240 */
   WriteLCDData(e_y);
}
/* Name: FillScreen
   Function: Fills screen with solid colour specified
---------------------------------------------------------- */   
void FillScreen(unsigned char r, unsigned char g, unsigned char b){
   unsigned int x,y;
   WindowSet(0x0000,0x013f,0x0000,0x00ef);
   WriteLCDCmd(0x2c);
   for (x=0;x<240;x++){
      for (y= 0;y<320;y++){
      WritePixelData(r, g, b);
      }
   }
}


So, I run a test with pure white (RGB = 255, 255, 255) and the result is predictably, a white screen. Boring!
Let's open up Photoshop... and mix up a colour....


And then, we call the function with the values Photoshop gave us...
Quote
FillScreen(105,90,68);

And this is the result, compare the photo with Photoshop :D

NOTE: Unfortunately my camera can't get a really good representation of this colour...

Next time: Part 5 - Putting a 24 bit image on the screen....

Be conservative in what you send, be liberal in what you accept from others- Jon B Postel
 

Jason

  • Administrator
  • *
  • Posts: 728
  • I love guitars
  • Respect: +62
Part 5 - Putting an Image on the screen
« Reply #4 on: January 15, 2011, 08:49:18 PM »
0
Part 5 - Putting an Image on the screen
Okay, now some time for real action. However we have some problems.

1. The microcontroller I am using only has 60kbytes of FLASH.
2. I forgot my license file at the office so my C compiler is locked down to 32kbytes of code size
3. I have no tool to convert BMP/JPEG to hex, never mind a const char array.


Okay, we know that, 320 x 240 = 76800 bytes x 3 bytes for each pixel = 230400 bytes or 230.4kByte, which is not going to fit in FLASH.
Taking into account our little C compiler limitation, we can store an image of 320 x 32 and have some space left over for the driver code.

So, the first step is, open up Photoshop and create the following kick-ass banner:


Save that as a .bmp file.
Then, we download and run a program called bmp2h_conv. We open the bitmap in that program.

Now we have to be careful here, to get the correct RGB sequence I had to play around with this program. So the settings shown above are right, and we set it to output a binary file .bin

But binary still doesn't help us... we need to get these values into a C array const char Name [] = {
So I open up the binary file in my good old friend, WinHEX, and I can see that the binary values are there, and its saved in 32 bit
(long) values... shit!

Then, I remembered one can copy and paste to the clipboard in WinHEX. So I did the following:
1. Ctrl+A to select all the bytes
2. Alt+E -> Edit -> Copy Block -> C Source

Then open up Notepad, and am greeted with the following beautiful C array...
Quote
unsigned char data[40960] = {
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
   0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xF9, 0xF9, 0xF9, 0x00, 0x99, 0x96, 0x96, 0x00,
   0x8F, 0x8E, 0x8C, 0x00, 0xF7, 0xF7, 0xF7, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xF2, 0xF1, 0xF0, 0x00,
       etc.....

Now, how the hell do I get rid of all the extra bytes i.e. the upper MSB of the 32 bit word (long). Then I remembered, one
can import comma delimited text data into Microsoft Excel, and I did just that.... I imported the text file, directly into Excel.

Next thing, select the columns with the unwanted data, and save as space delimited text (MS Office 2000 Premium has that feature)

Open up the resultant text file, add the const char to it at the top, and copy it to the clipboard. Then in our project we create a new file called sourcegraphics.c and paste the array in there.. it looks like this:

Quote
/* ---------------------------------------------------------------------------------------------
   Filename: sourcegraphics.c
   Function: Bitmap image for LCD
------------------------------------------------------------------------------------------------- */   


const char Img01[] = {
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xF9, 0xF9, 0x99, 0x96, 0x96,
et, cetera.....


Now, to write this lot to the LCD, we need to create the following routines to read the array:
Quote
/* Name: LoadGraphic
   Function: Fetches bitmap from array and loads it into the LCD
----------------------------------------------------------------- */
void LoadGraphic(const char *string){
          unsigned int graphptr = 0;               /* define a pointer to consecutive bytes */
   unsigned int x,y;
   unsigned char r, g, b;
   WindowSet(0x0000,0x013f,0x0000,0x00ef);
   WriteLCDCmd(0x2c);   
   for (x=0;x<240;x++){
      for (y= 0;y<320;y++){
         if(graphptr < 30720){         
            r = string[graphptr];
            g = string[graphptr + 1];
            b = string[graphptr + 2];
            WritePixelData(r, g, b);
            graphptr = graphptr + 3;
         } else {
            WritePixelData(255, 255, 255);
         }
      }
   }      
   
}   


Now we compile the code, and run it, and I am happy to present the following screen shot:


Now, for the list of problems with this setup (and you thought it was perfect...)
1. The microcontroller used is 8 bit, and even though it runs at 20MHz (flat out) it takes strain. The screen takes 300mS to fill, you can literally see it fill from top to bottom like an al-cheapo electronic picture frame.
2. The lack of memory, is an issue, and to effectively solve it we need mass storage. This microcontroller is too slow to adequately update the display or even manage icons and stuff.

To solve these problems, I am going to wire up this LCD to a 32 bit microcontroller. That will be covered in a separate article.
This concludes this full length article about interfacing a colour LCD to an 8-bit microcontroller. Virtually all of this is applicable to any microcontroller platform and my code is portable.

I hope you enjoyed this, and remember, I can be contacted via PM for advice, source code, tools, etc...


Be conservative in what you send, be liberal in what you accept from others- Jon B Postel
 

Epsilon

  • Administrator
  • *
  • Posts: 1984
  • Relativistic Momentum
  • Respect: +36
Re: Adding Colour Displays to Embedded Projects
« Reply #5 on: January 26, 2011, 07:31:56 PM »
0
Awesome series Jason!
Gave me some insight as to how embedded software works and from a hardware perspective it was very interesting as well.
Keep up the good work!
Information wants to be free

Jason

  • Administrator
  • *
  • Posts: 728
  • I love guitars
  • Respect: +62
Re: Adding Colour Displays to Embedded Projects
« Reply #6 on: March 30, 2011, 12:56:53 PM »
0
A long overdue update:
Some progress....
Be conservative in what you send, be liberal in what you accept from others- Jon B Postel