前言
ST7789的驱动移植成功后,还需要添加Framebuffer的支持,进行硬件接口的抽象化,通过对Framebuffer的读写来直接对显存进行操作
Framebuffer
- Framebuffer驱动是将图像设备的显示进行抽象化,可以理解为一块内存空间(帧缓冲),该空间为显示内存的映射,会被刷新到LCD上;
- Framebuffer是一个字符驱动,设备节点 /dev/fbn 有了Framebuffer驱动接口的支持,应用GUI可以友好完成移植
框图
- 内核自带的驱动中,fb的字符驱动源码路径
drivers/video/fbdev/core/fbmem.c - LCD驱动中的framebuffer的配置,源码路径
/drivers/video/fbdev/mxsfb.c
注册framebuffer
- 在probe函数中进行framebuffer设备的注册,定义配置fb_info
int st7789_fb(struct spi_device *spi)
{
unsigned char *v_addr = NULL;
unsigned int p_addr = 0;
lcd_data_t *data;
v_addr = dma_alloc_coherent(NULL, X*Y*4, &p_addr, GFP_KERNEL);
if(v_addr == NULL)
{
printk("dma allpc error!\n");
return -1;
}
fb_info = framebuffer_alloc(sizeof(lcd_data_t), NULL);
if(fb_info == NULL)
{
printk("fb_info allow error!\n");
return -1;
}
data = fb_info->par;
data->spi = spi;
fb_info->pseudo_palette = pseudo_palette;
fb_info->var.activate = FB_ACTIVATE_NOW;
fb_info->var.xres = LCD_W;
fb_info->var.yres = LCD_H;
fb_info->var.xres_virtual = LCD_W;
fb_info->var.yres_virtual = LCD_H;
fb_info->var.bits_per_pixel = 32;
fb_info->var.red.offset = 16;
fb_info->var.red.length = 8;
fb_info->var.green.offset = 8;
fb_info->var.green.length = 8;
fb_info->var.blue.offset = 0;
fb_info->var.blue.length = 8;
strcpy(fb_info->fix.id, "st7789_fb");
fb_info->fix.smem_start = p_addr;
fb_info->fix.smem_len = LCD_W*LCD_H*4;
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
fb_info->fix.line_length = LCD_W*4;
fb_info->fbops = &fops;
fb_info->screen_base = v_addr;
fb_info->screen_size = LCD_W*LCD_H*4;
printk("register framebuff!\n");
register_framebuffer(fb_info);
data->thread = kthread_run(thread_func, fb_info, spi->modalias);
return 0;
}
内核线程
int thread_func(void *data)
{
lcd_data_t *ldata = fb_info->par;
while (1)
{
if (kthread_should_stop())
break;
show_fb(fb_info, ldata->spi);
}
return 0;
}
rgb888与rgb565转换
QT采用的是RGB888 24位图32bit数据,在LCD上显示,需要将framebuffer的显存进行转换
0 0 0 0 0 0 0 0 | R7 R6 R5 R4 R3 R2 R1 R0 | G7 G6 G5 G4 G3 G2 G1 G0 | B7 B6 B5 B4 B3 B2 B1 B0 |
---|
R7 R6 R5 R4 R3 | G7 G6 G5 G4 G3 G2 | B7 B6 B5 B4 B3 |
---|
- 内核线程刷屏中将位图转化,提取单色高位,丢失部分精度
for (x = 0; x < fb_info->var.xres; x++)
{
k = p[y*fb_info->var.xres+x];
pdata = (unsigned char *)&k;
c = pdata[0] >> 3;
c |= (pdata[1]>>2)<<5;
c |= (pdata[2]>>3)<<11;
*((unsigned short *)memory+y*fb_info->var.yres+x) = ((c&0xff)<<8)|((c&0xff00)>>8);
}
应用程序
流程
app用户代码
fd_fb = open(filename, O_RDWR);
if (fd_fb < 0)
{
printf("can't open file %s\r\n", filename);
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fb_base == NULL)
{
printf("can't mmap\n");
return -1;
}
memset(fb_base, 0xFF, screen_size);
int *color = (int *)fb_base;
for(i = 80; i < 160; i++)
{
for(j = 80; j < 160; j++)
{
*(color+i*var.xres+j) = 0x000000;
}
}
for(i = 100; i < 140; i++)
{
for(j = 100; j < 140; j++)
{
*(color+i*var.xres+j) = 0x00FF00;
}
}
munmap(fb_base , screen_size);
close(fd_fb);
实验现象
驱动安装后,启动应用程序
./lcd_app /dev/fb0
后续章节贴完整源码
|