16.Linux-LCD驱动(详解)
在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步:
1) 分配一个fb_info结构体: framebuffer_alloc();
2) 设置fb_info
3) 设置硬件相关的操作
4) 使能LCD,并注册fb_info: register_framebuffer()
本节需要用到的函数:
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 用来为用户空间页来分配内存; 它可能睡眠.
分配一段DMA缓存区,分配出来的内存会禁止cache缓存(因为DMA传输不需要CPU)
它和 dma_alloc_coherent ()函数相似,不过 dma_alloc_coherent ()函数是分配出来的内存会禁止cache缓存以及禁止写入缓冲区
dma_free_writecombine(dev,size,cpu_addr,handle); //释放缓存
//cpu_addr:虚拟地址,
//handle:物理地址
释放DMA缓冲区, dev和size参数和上面的一样
struct fb_info *framebuffer_alloc(size_t size, struct device *dev); //申请一个fb_info结构体,
//size:额外的内存,
//*dev:指针, 这里填0,表示这个申请的结构体里没有内容
int register_framebuffer(struct fb_info *fb_info); //向内核中注册fb_info结构体,若内存不够,注册失败会返回负数 int unregister_framebuffer(struct fb_info *fb_info) ; //注销内核中fb_info结构体
本节需要用到的结构体:
fb_info结构体如下:
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色颜色来,节省内存,不需要的话就指向一个不用的数组即可
... ...
};
其中操作函数fb_info-> fbops 结构体写法如下:
static struct fb_ops s3c_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = my_lcdfb_setcolreg,//设置调色板fb_info-> pseudo_palette,自己构造该函数 .fb_fillrect = cfb_fillrect, //填充矩形,用/drivers/video/ cfbfillrect.c里的函数即可 .fb_copyarea = cfb_copyarea, //复制数据, 用/drivers/video/cfbcopyarea.c里的函数即可 .fb_imageblit = cfb_imageblit, //绘画图形, 用/drivers/video/imageblit.c里的函数即可
};
固定的参数fb_info-> fix 结构体如下:
struct fb_fix_screeninfo {
char id[]; //id名字
unsigned long smem_start; //framebuffer物理起始地址
__u32 smem_len; //framebuffer长度,字节为单位
__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 */ __u32 line_length; /*一行的字节数 ,例:(RGB565)240*320,那么这里就等于240*16/8 */
/*以下成员都可以不需要*/
unsigned long mmio_start; /*内存映射IO的起始地址,用于应用层直接访问寄存器,可以不需要*/
__u32 mmio_len; /* 内存映射IO的长度,可以不需要*/
__u32 accel;
__u16 reserved[]; };
可变的参数fb_info-> var 结构体如下:
structfb_var_screeninfo{
__u32xres; /*可见屏幕一行有多少个像素点*/
__u32 yres; /*可见屏幕一列有多少个像素点*/
__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即可*/
__u32nonstd; /* != 0表示非标准像素格式*/
__u32 activate; /*设为0即可*/
__u32height; /*外设高度(单位mm),一般不需要填*/
__u32width; /*外设宽度(单位mm),一般不需要填*/
__u32 accel_flags; /*过时的参数,不需要填*/ /* 除了pixclock本身外,其他的都以像素时钟为 单位*/
__u32pixclock; /*像素时钟(皮秒)*/
__u32 left_margin; /*行切换,从同步到绘图之间的延迟*/
__u32right_margin; /*行切换,从绘图到同步之间的延迟*/
__u32upper_margin; /*帧切换,从同步到绘图之间的延迟*/
__u32lower_margin; /*帧切换,从绘图到同步之间的延迟*/
__u32hsync_len; /*水平同步的长度*/
__u32 vsync_len; /*垂直同步的长度*/
__u32 sync;
__u32 vmode;
__u32 rotate;
__u32reserved[]; /*保留*/ }
1.写驱动程序:
(驱动设置:参考自带的LCD平台驱动drivers/video/s3c2410fb.c )
(LCD控制寄存器设置:参考之前的LCD裸机驱动:http://www.cnblogs.com/lifexy/p/7144890.html)
1.1 步骤如下:
在驱动init入口函数中:
1)分配一个fb_info结构体
2)设置fb_info
2.1)设置固定的参数fb_info-> fix
2.2) 设置可变的参数fb_info-> var
2.3) 设置操作函数fb_info-> fbops
2.4) 设置fb_info 其它的成员
3)设置硬件相关的操作
3.1)配置LCD引脚
3.2)根据LCD手册设置LCD控制器
3.3)分配显存(framebuffer),把地址告诉LCD控制器和fb_info
4)开启LCD,并注册fb_info: register_framebuffer()
4.1) 直接在init函数中开启LCD(后面讲到电源管理,再来优化)
控制LCDCON5允许PWREN信号,
然后控制LCDCON1输出PWREN信号,
输出GPB0高电平来开背光,
4.2) 注册fb_info
在驱动exit出口函数中:
1)卸载内核中的fb_info
2) 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址
3)释放DMA缓存地址dma_free_writecombine()
4)释放注册的fb_info
1.2 具体代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h> /*LCD : 480*272 */
#define LCD_xres 480 //LCD 行分辨率
#define LCD_yres 272 //LCD列分辨率 /* GPIO prot */
static unsigned long *GPBcon;
static unsigned long *GPCcon;
static unsigned long *GPDcon;
static unsigned long *GPGcon; //GPG4:控制LCD信号
static unsigned long *GPBdat; //GPB0: 控制背光
/* LCD control */
struct lcd_reg{
unsigned long lcdcon1;
unsigned long lcdcon2;
unsigned long lcdcon3;
unsigned long lcdcon4;
unsigned long lcdcon5;
unsigned long lcdsaddr1;
unsigned long lcdsaddr2;
unsigned long lcdsaddr3 ;
unsigned long redlut;
unsigned long greenlut;
unsigned long bluelut;
unsigned long reserved[];
unsigned long dithmode;
unsigned long tpal ;
unsigned long lcdintpnd;
unsigned long lcdsrcpnd;
unsigned long lcdintmsk;
unsigned long tconsel;
}; static struct lcd_reg *lcd_reg; static struct fb_info *my_lcd; //定义一个全局变量
static u32 pseudo_palette[]; //调色板数组,被fb_info->pseudo_palette调用 static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
/*内核中的单色都是16位,默认从左到右排列,比如G颜色[0x1f],那么chan就等于0XF800*/
chan &= 0xffff;
chan >>= - bf->length; //右移,将数据靠到位0上
return chan << bf->offset; //左移一定偏移值,放入16色数据中对应的位置
} static int my_lcdfb_setcolreg(unsigned int regno, unsigned int red,unsigned int green, unsigned int blue,unsigned int transp, struct fb_info *info) //设置调色板函数,供内核调用
{
unsigned int val;
if (regno >=) //调色板数组不能大于15
return ; /* 用red,green,blue三个颜色值构造出16色数据val */
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue); ((u32 *)(info->pseudo_palette))[regno] = val; //放到调色板数组中
return ;
} static struct fb_ops my_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = my_lcdfb_setcolreg,//调用my_lcdfb_setcolreg()函数,来设置调色板fb_info-> pseudo_palette
.fb_fillrect = cfb_fillrect, //填充矩形
.fb_copyarea = cfb_copyarea, //复制数据
.fb_imageblit = cfb_imageblit, //绘画图形,
}; static int lcd_init(void)
{
/*1.申请一个fb_info结构体*/
my_lcd= framebuffer_alloc(,); /*2.设置fb_info*/ /* 2.1设置固定的参数fb_info-> fix */
/*my_lcd->fix.smem_start 物理地址后面注册MDA缓存区设置*/
strcpy(my_lcd->fix.id, "mylcd"); //名字
my_lcd->fix.smem_len =LCD_xres*LCD_yres*; //地址长
my_lcd->fix.type =FB_TYPE_PACKED_PIXELS;
my_lcd->fix.visual =FB_VISUAL_TRUECOLOR; //真彩色
my_lcd->fix.line_length =LCD_xres*; //LCD 一行的字节 /* 2.2 设置可变的参数fb_info-> var */
my_lcd->var.xres =LCD_xres; //可见屏X 分辨率
my_lcd->var.yres =LCD_yres; //可见屏y 分辨率
my_lcd->var.xres_virtual =LCD_xres; //虚拟屏x分辨率
my_lcd->var.yres_virtual =LCD_yres; //虚拟屏y分辨率
my_lcd->var.xoffset = ; //虚拟到可见屏幕之间的行偏移
my_lcd->var.yoffset =; //虚拟到可见屏幕之间的行偏移 my_lcd->var.bits_per_pixel=; //像素为16BPP
my_lcd->var.grayscale = ; //灰色比例 my_lcd->var.red.offset = ;
my_lcd->var.red.length = ;
my_lcd->var.green.offset = ;
my_lcd->var.green.length = ;
my_lcd->var.blue.offset = ;
my_lcd->var.blue.length = ; /* 2.3 设置操作函数fb_info-> fbops */
my_lcd->fbops = &my_lcdfb_ops; /* 2.4 设置fb_info 其它的成员 */
/*my_lcd->screen_base 虚拟地址在后面注册MDA缓存区设置*/
my_lcd->pseudo_palette =pseudo_palette; //保存调色板数组
my_lcd->screen_size =LCD_xres * LCD_yres *; //虚拟地址长 /*3 设置硬件相关的操作*/
/*3.1 配置LCD引脚*/
GPBcon = ioremap(0x56000010, );
GPBdat = GPBcon+;
GPCcon = ioremap(0x56000020, );
GPDcon = ioremap(0x56000030, );
GPGcon = ioremap(0x56000060, ); *GPBcon &=~(0x03<<(*));
*GPBcon |= (0x01<<(*)); //PGB0背光
*GPBdat &=~(0X1<<); //关背光
*GPCcon =0xaaaaaaaa;
*GPDcon =0xaaaaaaaa;
*GPGcon |=(0x03<<(*)); //GPG4:LCD信号 /*3.2 根据LCD手册设置LCD控制器,参考之前的裸机驱动*/
lcd_reg=ioremap(0X4D000000, sizeof( lcd_reg) );
/*HCLK:100Mhz */
lcd_reg->lcdcon1 = (<<) | (0X3<<) | (0x0C<<) ;
lcd_reg->lcdcon2 = (()<<) | (<<) | (()<<) |(()<<);
lcd_reg->lcdcon3 = (()<<) | (<<) | (());
lcd_reg->lcdcon4 = ();
lcd_reg->lcdcon5 = (<<) | (<<) | (<<) |(<<); lcd_reg->lcdcon1 &=~(<<); // 关闭PWREN信号输出
lcd_reg->lcdcon5 &=~(<<); //禁止PWREN信号 /* 3.3 分配显存(framebuffer),把地址告诉LCD控制器和fb_info*/
my_lcd->screen_base=dma_alloc_writecombine(,my_lcd->fix.smem_len, &my_lcd->fix.smem_start, GFP_KERNEL); /*lcd控制器的地址必须是物理地址*/
lcd_reg->lcdsaddr1 =(my_lcd->fix.smem_start>>)&0X3FFFFFFF; //保存缓冲起始地址A[30:1]
lcd_reg->lcdsaddr2 =((my_lcd->fix.smem_start+my_lcd->screen_size)>>)&0X1FFFFF; //保存存缓冲结束地址A[21:1]
lcd_reg->lcdsaddr3 =LCD_xres& 0x3ff; //OFFSIZE[21:11]:保存LCD上一行结尾和下一行开头的地址之间的差
//PAGEWIDTH [10:0]:保存LCD一行占的宽度(半字数为单位) /*4开启LCD,并注册fb_info: register_framebuffer()*/
/*4.1 直接在init函数中开启LCD(后面讲到电源管理,再来优化)*/
lcd_reg->lcdcon1 |=<<; //输出PWREN信号
lcd_reg->lcdcon5 |=<<; //允许PWREN信号
*GPBdat |=(0X1<<); //开背光 /*4.2 注册fb_info*/
register_framebuffer(my_lcd);
return ;
}
static int lcd_exit(void)
{
/* 1卸载内核中的fb_info*/
unregister_framebuffer(my_lcd);
/*2 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址*/
lcd_reg->lcdcon1 &=~(<<); // 关闭PWREN信号输出
lcd_reg->lcdcon5 &=~(<<); //禁止PWREN信号
*GPBdat &=~(0X1<<); //关背光
iounmap(GPBcon);
iounmap(GPCcon);
iounmap(GPDcon);
iounmap(GPGcon); /*3.释放DMA缓存地址dma_free_writecombine()*/
dma_free_writecombine(,my_lcd->screen_size,my_lcd->screen_base,my_lcd->fix.smem_start); /*4.释放注册的fb_info*/
framebuffer_release(my_lcd); return ;
} module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
2.重新编译内核,去掉默认的LCD
make menuconfig ,进入menu菜单重新设置内核参数:
进入Device Drivers-> Graphics support:
<M> S3C2410 LCD framebuffer support //将自带的LCD驱动设为模块, 不编进内核中
然后make uImage 编译内核
make modules 编译模块
为什么要编译模块?
因为LCD驱动相关的文件也没有编进内核,而fb_ops里的成员fb_fillrect(), fb_copyarea(), fb_imageblit()用的都是drivers/video下面的3个文件,所以需要这3个的.ko模块,如下图所示:
3.挂载驱动
将编译好的LCD驱动模块 和drivers/video里的3个.ko模块 放入nfs文件系统目录中
然后烧写内核, 先装载3个/drivers/video下编译好的模块,再来装载LCD驱动模块
挂载LCD驱动后, 如下图,可以通过 ls -l /dev/fb* 命令查看已挂载的LCD设备节点:
4.测试运行
测试有两种:
(echo和cat命令详解入口地址: http://www.cnblogs.com/lifexy/p/7601122.html)
echo hello> /dev/tty1 // LCD上便显示hello字段
cat Makefile>/dev/tty1 // LCD上便显示Makeflie文件的内容
4.1使用上节的键盘驱动在LCD终端打印命令行
vi /etc/inittab //修改inittab, inittab:配置文件,用于启动init进程时,读取inittab 添加->tty1::askfirst:-/bin/sh //将sh进程(命令行)输出到tty1里,也就是使LCD输出信息
然后重启,insmod装载3个/drivers/video下编译好的模块,再来insmod装载LCD驱动模块,tty1设备便有了,就能看到提示信息:
如下图,我们insmod上一节的键盘驱动后,按下enter键,便能在LCD终端上操作linux了
(上一节的键盘驱动详解入口地址: http://www.cnblogs.com/lifexy/p/7553861.html)
从上图可以看到按下enter键,它就启动了一个进程号772的-sh进程,如下图发现这个-sh的描述符都指向了tty1:
下章学习:
18.Llinux-触摸驱动(详解)
16.Linux-LCD驱动(详解)的更多相关文章
- linux usb 驱动详解
linux usb 驱动详解 USB 设备驱动代码通过urb和所有的 USB 设备通讯.urb用 struct urb 结构描述(include/linux/usb.h ). urb 以一种异步的方式 ...
- 13.Linux键盘驱动 (详解)
版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一节分析输入子系统内的intput_handler软件处理部分后,接下来我们开始写input_dev驱动 本节目标: 实现键盘驱动,让开发板的 ...
- LCD驱动详解
参考文档:<液晶屏.pdf><S3C2440用户手册><JZ2440-V3原理图> frame buffer: 显存,用于存放LCD显示数据:frame buf ...
- usb驱动---linux ACM驱动详解ACA【转】
转自:http://blog.chinaunix.net/uid-9185047-id-3404684.html DTE提供或接收数据,连接到网络中的用户端机器,主要是计算机和终端设备.与此相对地,在 ...
- Linux设备驱动详解 宋宝华 硬件基础
处理器 存储器 接口与总线 I2C时序 SPI总线时序 以太网
- 16.Linux-LCD驱动(详解)【转】
转自:https://www.cnblogs.com/lifexy/p/7604011.html 在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构 ...
- Linux /dev目录详解和Linux系统各个目录的作用
Linux /dev目录详解(转http://blog.csdn.net/maopig/article/details/7195048) 在linux下,/dev目录是很重要的,各种设备都在下面.下面 ...
- Linux串口编程详解(转)
串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...
- 25.Linux-Nor Flash驱动(详解)
1.nor硬件介绍: 从原理图中我们能看到NOR FLASH有地址线,有数据线,它和我们的SDRAM接口相似,能直接读取数据,但是不能像SDRAM直接写入数据,需要有命令才行 1.1其中我们2440的 ...
随机推荐
- 【bzoj1150】[CTSC2007]数据备份Backup 模拟费用流+链表+堆
题目描述 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏 ...
- Nginx websocket反向代理
L:106 现在主流的反向代理,通过长链接可以从服务器推送数据到页面 升级成websocket反向代理必须根据上面的配置做配置 缺点无法多路复用,也就是没办法并行 我们测试下Websocket反向代理 ...
- BZOJ2242[SDOI2011]计算器——exgcd+BSGS
题目描述 你被要求设计一个计算器完成以下三项任务: 1.给定y,z,p,计算Y^Z Mod P 的值: 2.给定y,z,p,计算满足xy≡ Z ( mod P )的最小非负整数: 3.给定y,z,p, ...
- BZOJ3724PA2014Final Krolestwo——欧拉回路+构造
题目描述 你有一个无向连通图,边的总数为偶数.设图中有k个奇点(度数为奇数的点),你需要把它们配成k/2个点对(显然k被2整除).对于每个点对(u,v),你需要用一条长度为偶数(假设每条边长度为1)的 ...
- 对比cp和scp命令 将数据从一台linux服务器复制到另一台linux服务器
cp命令用来将一个或多个源文件或者目录复制到指定的目的文件或目录.它可以将单个源文件复制成一个指定文件名的具体的文件或一个已经存在的目录下.cp命令还支持同时复制多个文件,当一次复制多个文件时,目标文 ...
- 图论杂项细节梳理&模板(虚树,圆方树,仙人掌,欧拉路径,还有。。。)
orzYCB 虚树 %自为风月马前卒巨佬% 用于优化一类树形DP问题. 当状态转移只和树中的某些关键点有关的时候,我们把这些点和它们两两之间的LCA弄出来,以点的祖孙关系连成一棵新的树,这就是虚树. ...
- flowable6.4.1+springboot使用dmn
resources/dmn/strings_1.dmn <?xml version="1.0" encoding="UTF-8"?> <def ...
- Java和操作系统交互细节
结合 CPU 理解一行 Java 代码是怎么执行的 根据冯·诺依曼思想,计算机采用二进制作为数制基础,必须包含:运算器.控制器.存储设备,以及输入输出设备,如下图所示. enter image des ...
- 两数之和 II - 输入有序数组
题目描述 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数. 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2. 说明: 返 ...
- JavaServer Faces (JSF) with Spring
JavaServer Faces (JSF) with Spring Last modified: April 30, 2018 by baeldung Spring+ Spring MVC JSF ...