Question

I am trying to write a bare metal code to program PL111 LCD controller. I am using QEMU emulator set up for Realview ARM Cortex-A8. I earlier managed to print characters on the linux terminal window using QEMU's "-serial stdio" option.

I have gone through PL111 document and I can't figure out one thing. I set up PL111 controller's LCDUPBASE tovcontain the address of the frame buffer in memory. But can I simply write ascii values to the frame buffer and the LCD controller would pick my frame buffer and display the corresponding characters on the screen or do I need to perform some kind of conversion on ascii values( as per an already existing standard which I am not aware of) before writing them to the frame buffer?

In case of the former being true, is this conversion handled by the controller itself as per some conversion table in controller hardware? What about stuff such as background colour? The PL111 document doesn't say anything about this. This hurdle also made me think about the role of a GPU, if I were having a GPU as well, where would it fit it in this scheme and what exactly would be its role?

Are there any good resources, documents or books that can help to understand these concepts better. Pardon me if my questions sound stupid. I don't have much experience of embedded/peripheral programming, I am basically trying to learn/get acquainted with ARMv7 architecure and i thought it would be nice and interesting if I could get to print my assembly programming prints on the QEMU console rather than linux console. (using "-serial stdio" option)

I'd really appreciate it if people here can help me with this. Thanks

/* boot.s */

.section .data

.section .bss

.section .text

.globl _start

_start:

/*** Interrupt Vector Table Start**/

b _RESET_HANDLER                /* Reset Handler        */
b _UNDEF_HANDLER        /* Undef Instruction Handler    */
b _SWI_HANDLER          /* Software Interrupt Handler   */
b _PREFETCHABORT_HANDLER    /* Prefect Abort Handler    */
b _DATAABORT_HANDLER        /* Data Abort Handler       */
b _IRQ_HANDLER          /* IRQ Handler          */
b _FIQ_HANDLER          /* FIQ Handler          */

/*** Interrupt Vector Table End******/

_FIQ_HANDLER:

b .     /* Not implemented yet, so go in infinite loop */

_IRQ_HANDLER:

b .    /* _isr_irq   /* jump to interrupt service routine */

_DATAABORT_HANDLER:

b .     /* Not implemented yet, so go in infinite loop */

_PREFETCHABORT_HANDLER:

b .     /* Not implemented yet, so go in infinite loop */

_SWI_HANDLER:

b .     /* Not implemented yet, so go in infinite loop */

_UNDEF_HANDLER:

b .     /* Not implemented yet, so go in infinite loop */

_RESET_HANDLER:

b _initialize_cpu

cpuinitialize.s =>

.section .data

.section .bss

.section .text

.globl _initialize_cpu

_initialize_cpu:

/* LCD initialization code */

    .include "ColourLCDPL111.s"
    .set SYS_OSC4, 0x1000001C  /* Mapped register for OSCCLK4*/ 
    .set SYS_LOCK, 0x10000020  /* reference clock CLCDCLK for PL111*/

     movw r0, #:lower16:SYS_LOCK          /* Unlocking the register*/
     movt r0, #:upper16:SYS_LOCK
     movw r1, #0xA05F    
             str  r1, [r0]

         movw r2, #:lower16:SYS_OSC4   /* Setting the CLCDCLK frequency 36MHz*/
         movt r2, #:upper16:SYS_OSC4
         movw r1, #0x2CAC    
                 str  r1, [r2]

     str  r1, [r0]                /* Locking the register again*/


         movw r0, #:lower16:LCDTiming0_ADDR
         movt r0, #:upper16:LCDTiming0_ADDR
         movw r1, #:lower16:0x1313A4C4      /* PPL = 49 ; HSW = 3 TODO:change*/
         movt r1, #:upper16:0x1313A4C4      /* HBP = 5  ; HFP = 5 */
             str  r1, [r0]

             movw r0, #:lower16:LCDTiming1_ADDR
             movt r0, #:upper16:LCDTiming1_ADDR
             movw r1, #:lower16:0x0505F657        /* LPP = 600 ; VSW = 2 TODO:change*/
             movt r1, #:upper16:0x0505F657        /* VBP = 2   ; VFP = 2 */
             str  r1, [r0]


     movw r0, #:lower16:LCDTiming2_ADDR
     movt r0, #:upper16:LCDTiming2_ADDR
     movw r1, #:lower16:0x071F1800          /* CPL[25:16] = 799     ; BCD[26] =  1 (PCD Bypassed)   */
     movt r1, #:upper16:0x071F1800          /* PCD        = ignored */                  
             str  r1, [r0]


     movw r0, #:lower16:LCDUPBASE_ADDR   /* Setting up frame buffer address to 0x00000000*/
     movt r0, #:upper16:LCDUPBASE_ADDR
     mov  r1, #0x0  
     str  r1, [r0]

     movw r0, #:lower16:LCDControl_ADDR     
     movt r0, #:upper16:LCDControl_ADDR
     movw  r1, #0x082B          /* Setting up TFT 24Bit Mode */  
     str  r1, [r0]

     movw r0, #:lower16:LCDIMSC_ADDR    /* LCD interrupts: Disabled for now */
     movt r0, #:upper16:LCDIMSC_ADDR
     mov  r1, #0x00000000            
     str  r1, [r0]

     mov  r0, #40            /* lets try to print 'A' at frame buffer + 40 */
     mov  r1, #65   
     str  r1, [r0]

Code snippet for filling the frame buffer that actually turned the whole screen white. I took a randomly large value 10000 when simply using i=800 and j=600x4 didn't work

  void PopulateFrameBuffer(void)
  {
  unsigned int i,j;
  unsigned char *ptr = (unsigned char *)0x0;
  for(i=0; i<800;i++)
    {
    for (j=0;j<(600*10000);j++)
    {
      *ptr++=0xFF;

    }
  }
 }

I called this function from assembly after initialization code. Frame buffer start address is 0x00000000

Was it helpful?

Solution

This isn't really an answer, it's more of a comment, but the comment box only allows limited space and formatting.

According to this knowledge base article, the PL111 supports only pixel mapped modes. i.e. you have to turn on and off each pixel. So, if you have chosen 24 bit true colour, 24 bits from the frame buffer are used to control the colour of each pixel, 8 for red, 8 for green and 8 for blue. how those bits are organised is anybody's guess. For instance:

  • it might use a contiguous sequence of three bytes,
  • or it might use a contiguous sequence of four bytes but not using the most significant byte
  • or it might organise the bitmap into planes, for instance, all the red values, followed by all the green values, followed by all the blue values.
  • as an extreme option, you could have up to 24 planes, so the first bit of all pixels come first followed by the second bit of all pixels and so on.

My guess is that the first option is the one used (24 contiguous bits per pixel). You can test this by putting 0xFF in the first six bytes of the frame buffer. That should turn the left hand two pixels on the top row white. If it turns the first white and the second pixel cyan, you know that you have 32 bits per pixel ignoring the most significant byte (and the bytes are little endian). If the first six pixels turn red or another colour, you have a frame buffer organised into planes.

In order to make actual characters come out, you'll need a bit map for each possible character and you'll need to blit them into the frame buffer. You can do this in software, but there should be some sort of hardware support available.

Edit

OK, let's set how to put a character into the frame buffer. I'll use C because my ARM assembly skills aren't fantastic. Also, this is totally untested or even compiled

Assume 8 x 8 characters packed 8 pixels to the byte and a 24 bit colour frame buffer.

 #define PIXEL_WIDTH 3   // width in bytes of a pixel
 #define CHAR_WIDTH  8
 #define CHAR_HEIGHT 8

 #define RED_BYTE    2   // Index of the RGB components in the pixel in the framebuffer (little endian)
 #define GREEN_BYTE  1
 #define BLUE_BYTE   0

 struct FrameBufferDescriptor
 {
     uint8_t* start;           // Address of first byte in the buffer
     size_t rasterLineWidth;   // width in bytes of each line of pixels in the display.
 };

/*
 *  Draw a character at the (x, y) coordinates (0, 0) is top left.
 *  frameBuffer: describes position and width of the frame buffer
 *  x, y: coordinates of top left corner of the character
 *  characterMap: monochrome bitmap of 1 x 8 x 8 character.  Pixel packed 8 per byte.  
 *                If a bit is set, it means foreground colour, if cleared, it means 
 *                background colour
 *  foreground, background: RGB colours for foreground and background.
 */
void drawCharacter(struct FramBufferDescriptor* frameBuffer, size_t x, size_t y, uint8_t* characterMap, uint32_t foreground, uint32_t background)
{
    // first where to start drawing in the frame buffer
    uint8_t* destination =  frameBuffer->start + (y * rasterLineWidth) + (x * PIXEL_WIDTH);
    // char is 8 x 8
    for (int row = 0 ; row < CHAR_HEIGHT ; ++row)  // iterate for each row
    {
        for (pixel = 0 ; pixel < CHAR_WIDTH ; ++pixel)   // iterate for each picxel on the row
        {
            // determine the coloutr of this pixel
            uint32_t colour = background;
            if ((characterMap[row] & (1 << (CHAR_WIDTH - pixel - 1))) != 0) // Most sig bit will be on the left
            {
                colour = foreground;
            }
            // Put the RGB values in the framebuffer
            destination[pixel * PIXEL_WIDTH + RED_BYTE] = colour >> (RED_BYTE * 8);
            destination[pixel * PIXEL_WIDTH + GREEN_BYTE] = colour >> (GREEN_BYTE * 8);
            destination[pixel * PIXEL_WIDTH + BLUE_BYTE] = colour >> (BLUE_BYTE * 8);
        }
        destination += frameBuffer->rasterLineWidth;  // Go to next line of the frame buffer
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top