【问题标题】:arm, Programming PL111 controller and printing characters on the LCDarm,编程 PL111 控制器并在 LCD 上打印字符
【发布时间】:2012-04-09 07:24:35
【问题描述】:

我正在尝试编写一个裸机代码来对 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]

用于填充实际上将整个屏幕变白的帧缓冲区的代码 sn-p。当简单地使用 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

【问题讨论】:

  • 您是否尝试将 ASCII 值写入帧缓冲区?会发生什么?
  • @JeremyP 我还没试过,我现在只写了一些初始化代码。我将为 TFT 模式使用 24 位 RGB 格式。我在想,如果我的帧缓冲区包含 24 位 RGB 值,那么它们如何不被解释为颜色值,而是 ascii 字符,如果它被解释为 ascii 字符,什么会决定背景颜色。我翻阅了其他一些 LCD 控制器规格,但没有一个人谈到这一点。另外我还读到,在 VGA 编程中,在像素数据中,您有用于编码背景、前景色和 ascii 字符的位域
  • 好吧,我当然会尝试使用控制器来看看会发生什么。因为它是 QEMU,所以你不能造成任何伤害。
  • @JeremyP 我尝试按照建议将 ascii 值写入帧缓冲区
  • @JeremyP 我尝试按照您的建议将 ascii 值写入帧缓冲区,但没有在控制台上打印。我已将代码放入帖子的编辑中。 QEMU 控制台窗口确实从其默认大小扩大了,我在左上角看到了一点点颜色污迹,但仅此而已。我已经对此进行了相当多的搜索,但还没有找到任何实质性的东西。帧缓冲区中的 24 位值只是一个像素的 RGB 值。几个这样的像素将形成一个字符。必须有某种标准方法/算法以这种方式对像素进行编码

标签: controller arm framebuffer lcd


【解决方案1】:

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

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

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

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

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

编辑

好的,让我们设置如何将字符放入帧缓冲区。我将使用 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
    }
}

【讨论】:

  • 无论您在答案的第一部分中所说的内容都在 PL111 文档中给出,关于如何为给定的 bpp 打包位的内容。这是我正在寻找的答案的最后一段。 “每个可能的字符的位图”实际上在某种程度上回答了我。我对此进行了互联网搜索,在我的搜索中,我发现人们在谈论 8x8 ascii 位图,字体类似于此链接forum.osdev.org/viewtopic.php?f=2&t=22033。虽然我已经理解了这个想法,但我仍然不清楚如何实际实施它
  • 在这个链接forum.osdev.org/viewtopic.php?f=2&t=22033中,这个家伙给出了一个128x8的二维数组的头文件,他称之为8x8位图字体。首先,它是 8x8 的吗?其次,位图和字体可以互换使用来表达相同的含义吗?以及如何使用此位图填充我的帧缓冲区,我是否使用 ascii 值作为上述 2D 位图数组的行索引,然后使用这 8 个行值并将它们排列在我的帧缓冲区中的 4x2 像素数据的矩形块中或类似的东西? (它应该是他所说的 8x8,但只有 8 个 vals 是不可能的 8x8
  • 我已经为此苦苦挣扎了好几天了,我的实现有点卡住了,我似乎无法让自己跳过这部分并实现其他事情。我只设法找到关于这整件事的零散信息。我正在寻找的是一个简单但具体的教程/文档,它从头开始解释事物,我将从那里开始构建它并使其适应我的要求。我真的要感谢你花时间帮助我。
  • @san216: 整个字体在一个二维数组中。他有 128 个 8 x 8 字符。不要忘记表示 1 8 像素行只需要 1 个字节。
  • 是的,我后来知道他实际上是在使用字节的每一位来表示一个像素。因此,每个像素只能是 1 或 0。我刚刚编写了一个代码来将此 1bbp 位图映射到每个像素的 24bbp rgb 位图,其中 1 将由 24Bit (0,0,255) 表示,即红色和零将是 (0, 0,0)。我还没有测试它。
猜你喜欢
  • 2020-09-20
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多