我正在尝试编写一个裸机代码来编程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
_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
答案 0 :(得分:2)
这不是一个真正的答案,它更像是一个评论,但评论框只允许有限的空间和格式。
根据此knowledge base article,PL111仅支持像素映射模式。即你必须打开和关闭每个像素。因此,如果您选择了24位真彩色,则帧缓冲区中的24位用于控制每个像素的颜色,8表示红色,8表示绿色,8表示蓝色。这些位是如何组织的是任何人的猜测。例如:
我的猜测是第一个选项是使用的选项(每个像素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
}
}