LCD驱动具体编写:
1) 分配一个fb_info结构体
2) 设置fb_info (fix , var , fbops )
3) 设置硬件相关的操作
4) 使能LCD,并注册fb_info: register_framebuffer()
重要函数:
①:fbmem.c
struct fb_info *framebuffer_alloc(size_t size, struct device *dev);
//功能: 向内核申请一段大小为sizeof(struct fb_info) + size的空间,其中size的大小代表设备的私有数据空间,并用fb_info的par域指向该私有空间。
//参数: 参数一:空间大小 参数二: dev->dev(父类)
//申请一个fb_info结构体
void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); //分配DMA缓存区给显存
//返回值为:申请到的DMA缓冲区的虚拟地址,若为NULL,表示分配失败,则需要使用dma_free_writecombine()释放内存,避免内存泄漏
//参数如下: //*dev:指针,这里填0,表示这个申请的缓冲区里没有内容 //size:分配的地址大小(字节单位) //*handle:申请到的物理起始地址 //gfp:分配出来的内存参数,标志定义在<linux/gfp.h>,常用标志如下: //GFP_ATOMIC 用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠. //GFP_KERNEL 内核内存的正常分配. 可能睡眠. //GFP_USER 用来为用户空间页来分配内存; 它可能睡眠.
int register_framebuffer(struct fb_info *fb_info); //向内核中注册fb_info结构体,若内存不够,注册失败会返回负数 int unregister_framebuffer(struct fb_info *fb_info) ; //注销内核中fb_info结构体
error:
②: xxfb.c
struct fb_info *framebuffer_alloc(size_t size, struct device *dev); //申请一个fb_info结构体,
int register_framebuffer(struct fb_info *fb_info);//向内核中注册fb_info结构体,若内存不够,注册失败会返回负数
// 分配显存,并告诉fb_info
fb_info->screen_base = dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
error:
int unregister_framebuffer(struct fb_info *fb_info);
struct fb_info * framebuffer_release(size_t size, struct device *dev);
重要结构体 :
struct fb_info { // 记录缓冲区设备的全部信息
... ...
struct fb_var_screeninfo var; //可变的参数
struct fb_fix_screeninfo fix; //固定的参数
... ...
struct fb_ops *fbops; //操作函数
... ...
char __iomem *screen_base; //显存虚拟起始地址
unsigned long screen_size; //显存虚拟地址长度
void *pseudo_palette;
//假的16色调色板,里面存放了16色的数据,可以通过8bpp数据来找到调色板里面的16色颜色索引值,模拟出16色颜色来,节省内存,不需要的话就指向一个不用的数组即可
struct device *device;/* This is the parent */ 父设备(父类)
... ...
};
struct fb_fix_screeninfo { //记录控制器不可修改的参数(缓冲区的物理地址和长度)
char id[16]; //id名字,字符串形式的标识符
unsigned long smem_start; //控制器物理起始地址
__u32 smem_len; //控制器长度,字节为单位
__u32 type; //lcd类型,默认值0即可
__u32 type_aux; //附加类型,为0,分界
__u32 visual; //画面设置,常用参数如下
// FB_VISUAL_MONO01 0 单色,0:白色,1:黑色
// FB_VISUAL_MONO10 1 单色,1:白色,0:黑色
// FB_VISUAL_TRUECOLOR 2 真彩(TFT:真彩)
// FB_VISUAL_PSEUDOCOLOR 3 伪彩
// FB_VISUAL_DIRECTCOLOR 4 直彩
__u16 xpanstep; /*如果没有硬件panning就赋值为0 */
__u16 ypanstep; /*如果没有硬件panning就赋值为0 */
__u16 ywrapstep; /*如果没有硬件ywrap就赋值为0 */
char __iomem *screen_base; /* "显存“的基地址 */
unsigned long screen_size; /* ”显存“的大小 */
void *pseudo_palette; /* 16位假的调色板 */
__u32 line_length; /*一行的字节数 ,例:(RGB565)240*320,那么这里就等于240*16/8 */
/*以下成员都可以不需要*/
unsigned long mmio_start; /*内存映射IO的起始地址,用于应用层直接访问寄存器,可以不需要*/
__u32 mmio_len; /* 内存映射IO的长度,可以不需要*/
__u32 accel;
__u16 reserved[3]; /* 保留 */
};
struct fb_var_screeninfo { //记录控制器可以修改的参数 (分辨率,像素位数)
__u32 xres; /*可见屏幕一行有多少个像素点(就是分辨率X)*/ __u32 yres; /*可见屏幕一列有多少个像素点(就是分辨率Y)*/ __u32 xres_virtual; /*虚拟屏幕一行有多少个像素点*/ __u32 yres_virtual; /*虚拟屏幕一列有多少个像素点*/ __u32 xoffset; /*虚拟到可见屏幕之间的行偏移,若可见和虚拟的分辨率一样,就直接设为0*/ __u32 yoffset; /*虚拟到可见屏幕之间的列偏移*/ __u32 bits_per_pixel; /*每个像素的位数即BPP,比如:RGB565则填入16*/ __u32 grayscale; /*非0时,指的是灰度,真彩直接填0即可*/ struct fb_bitfield red;//fb缓存的R位域, fb_bitfield结构体成员如下: __u32 offset; //区域偏移值,比如RGB565中的R,就在第11位 __u32 length; //区域长度,比如RGB565的R,共有5位 __u32 msb_right; //msb_right ==0,表示数据左边最大, msb_right!=0,表示数据右边最大 struct fb_bitfield green; /*fb缓存的G位域*/ struct fb_bitfield blue; /*fb缓存的B位域*/ /*以下参数都可以不填,默认为0*/ struct fb_bitfield transp; /*透明度,不需要填0即可*/ __u32 nonstd; /* != 0表示非标准像素格式*/ __u32 activate; /*设为0即可*/ __u32 height; /*外设高度(单位mm),一般不需要填*/ __u32 width; /*外设宽度(单位mm),一般不需要填*/ __u32 accel_flags; /*过时的参数,不需要填*/ /* 除了pixclock本身外,其他的都以像素时钟为 单位*/ __u32 pixclock; /*像素时钟(皮秒)*/ __u32 left_margin; /*行切换,从同步到绘图之间的延迟*/ __u32 right_margin; /*行切换,从绘图到同步之间的延迟*/ __u32 upper_margin; /*帧切换,从同步到绘图之间的延迟*/ __u32l ower_margin; /*帧切换,从绘图到同步之间的延迟*/ __u32 hsync_len; /*水平同步的长度*/ __u32 vsync_len; /*垂直同步的长度*/ __u32 sync; __u32 vmode; __u32 rotate; __u32 reserved[5]; /*保留*/ }
static struct fb_ops = { // 底层硬件操作函数的集合
.owner = THIS_MODULE //被使用时阻止模块被卸载
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); //检查可变参数并进行设置
int (*fb_set_par)(struct fb_info *info); //根据设置的值进行更新,使之有效
int (*fb_blank)(int blank, struct fb_info *info); //显示空白
.fb_setcolreg //设置调色板fb_info-> pseudo_palette,自己构造该函数, //设置颜色寄存器
.fb_fillrect //填充矩形,用/drivers/video/ cfbfillrect.c里的函数即可, //矩形填充
.fb_copyarea //复制数据, 用/drivers/video/cfbcopyarea.c里的函数即可 //复制数据
.fb_imageblit //绘画图形, 用/drivers/video/imageblit.c里的函数即可 //图形填充
};
时序参数:
① 控制器输出时序
帧参数:
VSYNC:垂直同步时钟
VSPW :垂直同步脉宽 VBPD:垂直后肩 LINEVAL:垂直有效 VFPD:垂直前肩
INT_FRSyn :帧同步中断
VDEN : 视频数据使能
行参数:
HSYNC:水平同步时钟 VCLK :像素时钟
HSPW :水平同步脉宽 HBPD:水平后肩 HOZVAL:水平有效 HFPD:水平前肩
VD : 视频数据
VDEN : 视频数据使能信号
LDEN : 行结束信号
② 液晶屏输出时序
<程序控制>
1.打开设备
int fd = open("/dev/fd0",O_RDWR);
2.获取到lcd屏的信息xres,yres,bpp
应用: ioctl(fd,FBIOGET_VSCREENINFO,&var);
---------------------------------------------------------------------------------
驱动: static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
3.映射显存到应用空间
char *addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, int fd, 0);
framebuffer_ptr =(char *)mmap( NULL,//如果此值为NULL,则表示用内核来自动给你分配一块虚拟空间
screensize, //空间大小
PROT_READ|PROT_WRITE,//权限
MAP_SHARED, //是否可以共享
framebuffer_fd, //文件描述符
0); //从哪个地方开始
4. 得到图片的数据,将数据写入到映射的虚拟地址指向的空间
运行程序的格式:./lcd_test dev/fbx pic.bmp
/*1.打开一副图片*/
pic_fd =open(argv[2],O_RDWR);
printf("pic_fd=%d
",pic_fd);
/*2.获取图片大小*/
len =lseek(pic_fd, 0, SEEK_END);
printf("len =%ld
",len);
/*3.读取图片数据*/
read(pic_fd,buffer,len);
/*4.初始化lcd*/
fd=init_lcd(argv[1]);
//5.buffer存放了bmp图片数据,framebuffer_ptr映射后返回的显存的地址
draw_bmp(buffer,(unsigned short *) framebuffer_ptr);
代码示例:https://www.cnblogs.com/panda-w/p/10992943.html
<笔记>
1.
帧率:每秒生成图片个数(60fps就是每秒钟显卡生成60张画面图片)
刷新率:显示信号输出刷新的速度。60赫兹(hertz)就是每秒钟显卡向显示器输出60次信号。
假设帧数是刷新率的1/2,那么意思就是显卡每两次向显示器输出的画面是用一幅画面。相反,如果帧数是刷新率的2倍,那么画面每改变两次,其中只有1次是被显卡发送并在显示器上显示的。 所以高于刷新率的帧数都是无效帧数,对画面效果没有任何提升,反而可能导致画面异常。
帧率 =dotclock/((xres+left_margin+right_margin+hsync)*(yres+upper_margin+low_margin+vsync))
而android系统最高帧率为60fps,所以最好保证lcd的帧率也应大于等于60fps。xres和yres已经由硬件固定,因此根据公式调整其他参数,可以调整lcd帧率,使其尽量接近60fps。
https://blog.csdn.net/zqh2007/article/details/46504835
先说明下像素时钟pixclock的概念
pixclock=1/dotclock 其中dotclock是视频硬件在显示器上绘制像素的速率
dotclock=(x向分辨率+左空边+右空边+HSYNC长度)* (y向分辨率+上空边+下空边+YSYNC长度)*整屏的刷新率
其中x向分辨率、左空边、右空边、HSYNC长度、y向分辨率、上空边、下空边和YSYNC长度可以在X35LCD说明文档中查到。
整屏的刷新率计算方法如下:
假如我们通过查X35LCD说明文档,知道fclk=6.34MHZ,那么画一个像素需要的时间就是1/6.34us,如果屏的大小是240*320,那么现实一行需要的时间就是240/6.34us,每条扫描线是240,但是水平回扫和水平同步也需要时间,如果水平回扫和水平同步需要29个像素时钟,因此,画一条扫描线完整的时间就是(240+29) /6.34us。完整的屏有320根线,但是垂直回扫和垂直同步也需要时间,如果垂直回扫和垂直同步需要13个像素时钟,那么画一个完整的屏需要(240+29)*(320+13)/6.34us,所以整屏的刷新率就是6.34/((240+29)*(320+13))MHZ