0

我正在尝试编写裸机代码来编程 PL111 LCD 控制器。我正在使用为 Realview ARM Cortex-A8 设置的 QEMU 模拟器。我之前设法使用 QEMU 的“-serial stdio”选项在 linux 终端窗口上打印字符。

我已经浏览了 PL111 文档,但我无法弄清楚一件事。我设置了 PL111 控制器的 LCDUPBASE 来包含内存中帧缓冲区的地址。但是我可以简单地将 ascii 值写入帧缓冲区,LCD 控制器会选择我的帧缓冲区并在屏幕上显示相应的字符,还是我需要对 ascii 值执行某种转换(根据我已经存在的标准)我不知道)在将它们写入帧缓冲区之前?

如果前者是真的,这种转换是由控制器本身根据控制器硬件中的一些转换表处理的吗?诸如背景颜色之类的东西呢?PL111 文件没有说明这一点。这个障碍也让我想到了 GPU 的作用,如果我也有一个 GPU,它在这个方案中的位置是什么,它的作用到底是什么?

是否有任何好的资源、文档或书籍可以帮助更好地理解这些概念。如果我的问题听起来很愚蠢,请原谅我。我没有太多嵌入式/外围编程经验,我基本上是在尝试学习/熟悉 ARMv7 架构,我认为如果我可以在 QEMU 控制台上打印我的汇编编程打印而不是linux控制台。(使用“-serial stdio”选项)

如果这里的人可以帮助我,我将非常感激。谢谢

/* boot.s */

.section .data

.section .bss

.section .text

.globl _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          */

/ * ** 中断向量表结束* * **** /

_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 初始化代码 */

    .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]

用于填充实际上使整个屏幕变白的帧缓冲区的代码片段。当仅使用 i=800 和 j=600x4 不起作用时,我随机取了一个大值 10000

  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;

    }
  }
 }

我在初始化代码后从程序集中调用了这个函数。帧缓冲区起始地址为 0x00000000

4

1 回答 1

2

这不是一个真正的答案,它更像是一个评论,但评论框只允许有限的空间和格式。

根据这篇知识库文章,PL111 仅支持像素映射模式。即你必须打开和关闭每个像素。因此,如果您选择了 24 位真彩色,则帧缓冲区中的 24 位用于控制每个像素的颜色,8 位用于红色,8 位用于绿色,8 位用于蓝色。这些位的组织方式是任何人的猜测。例如:

  • 它可能使用三个字节的连续序列,
  • 或者它可能使用四个字节的连续序列,但不使用最高有效字节
  • 或者它可能将位图组织成平面,例如,所有红色值,然后是所有绿色值,然后是所有蓝色值。
  • 作为一个极端的选择,您最多可以有 24 个平面,因此所有像素的第一位首先出现,然后是所有像素的第二位,依此类推。

我的猜测是第一个选项是使用的(每像素 24 个连续位)。您可以通过将 0xFF 放在帧缓冲区的前六个字节中来测试这一点。这应该将顶行上的左侧两个像素变为白色。如果它将第一个白色和第二个像素变为青色,则您知道每个像素有 32 位忽略最高有效字节(并且字节是小端序)。如果前六个像素变为红色或其他颜色,则您将帧缓冲区组织成平面。

为了使实际字符出现,您需要为每个可能的字符绘制位图,并且需要将它们传送到帧缓冲区中。您可以在软件中执行此操作,但应该有某种可用的硬件支持。

编辑

好的,我们来设置如何将一个字符放入帧缓冲区。我将使用 C,因为我的 ARM 汇编技能并不出色。此外,这完全未经测试甚至编译

假设 8 x 8 字符打包 8 个像素到一个字节和一个 24 位彩色帧缓冲区​​。

 #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
    }
}
于 2012-04-12T10:17:33.807 回答