linux驱动开发 ST7789 Framebuffer驱动移植(I.MX6ULL平台)

前言

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);//申请连续内存(dma方式)
    if(v_addr == NULL)
    {
        printk("dma allpc error!\n");
        return -1;
    }
    fb_info = framebuffer_alloc(sizeof(lcd_data_t), NULL);//额外分配lcd_data_t类型空间,位于framebuffer后面(私有数据)
    if(fb_info == NULL)
    {
        printk("fb_info allow error!\n");
        return -1;
    }
    data = fb_info->par; //data指针指向额外分配的空间
    data->spi = spi;
    fb_info->pseudo_palette = pseudo_palette;
    fb_info->var.activate   = FB_ACTIVATE_NOW;
    fb_info->var.xres = LCD_W;//240
    fb_info->var.yres = LCD_H; //240
    fb_info->var.xres_virtual = LCD_W;
    fb_info->var.yres_virtual = LCD_H;
    fb_info->var.bits_per_pixel = 32; //32bit (采用rgb888 24位图,驱动中需要自行转化)
   
    //rgb888
    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;    
}
内核线程
  • 内核自带的驱动通过LCD控制器进行显存的刷新,但是SPI并不支持,这里通过增加内核线程进行刷新
    在这里插入图片描述

  • 通过内核线程将framebuffer的显存发送给ST7789控制器进行显示

//内核线程
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的显存进行转换

  • RGB888
0 0 0 0 0 0 0 0R7 R6 R5 R4 R3 R2 R1 R0G7 G6 G5 G4 G3 G2 G1 G0B7 B6 B5 B4 B3 B2 B1 B0
  • RGB565
R7 R6 R5 R4 R3G7 G6 G5 G4 G3 G2B7 B6 B5 B4 B3
  • 内核线程刷屏中将位图转化,提取单色高位,丢失部分精度
//grb888 to rgb565
for (x = 0; x < fb_info->var.xres; x++)
{
    k = p[y*fb_info->var.xres+x];//取出一个像素点的32位数据
    // rgb888 --> rgb565        
    pdata = (unsigned char *)&k;
    c = pdata[0] >> 3; //蓝色
    c |= (pdata[1]>>2)<<5; //绿色
    c |= (pdata[2]>>3)<<11; //红色
    //发出像素数据的rgb565
    *((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; //230400
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); //mmap申请一块虚拟内存
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

在这里插入图片描述

后续章节贴完整源码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值