LCD底层驱动分析
根据分析的框架,自己写一个LCD驱动程序
1分析LCD硬件原理图
Von和Voff接的是一个电源电路,通过LCD_POWER接的是GPG4来控制LCD电源,高电平表示开启LCD电源
VM接的是CPU的VM:VDEN /GPC4为数据使能信号,
VLINE接的CPU:HSYNC/GPC2,HSYNC信号有效时,表示一行数据的开始;
VFRAME接的CPU:VSYNC/GPC3,VSYNC信号有效时,表示一帧数据的开始
VCLK接的CPU:VCLK/GPC1 表示像素时钟信号,每个VCLK信号表示正在传输一个像素的数据;
LED-和LED+接到背光电路上,背光电路是由GPB0控制;高电平表示开启背光
VD[3~7],VD[10~15],VD[19~23]代表的是数据接口,CPU通过LCD专用的DMA来给传输像素的数据,
2 配置LCD用到GPIO引脚
根据原理图分析用的CPU 引脚,需要进行一一的配置
2.1先配置数据引脚VD;
*gpccon = 0xaaaaaaaa;
*gpdcon = 0xaaaaaaaa;
2.2配置控制引脚
/*3.注册配置GPIO,用于LCD*/
/*GPC4为配置为VDEN
*GPC2配置为HSYNC
*GPC3配置为VSYNC
*GPC1配置为VCLK
*/
*gpccon =(2<<2)|(2<<4) |(2<<6) |(2<<8);
3 配置S3C2440对应的寄存器的值
3.1 详细分析LCD时序
打开S3C2440中的LCD数据手册
S3C2440数据手册中的LCD控制时序图 这是LCD数据手册中的时序图
那么S3C2440手册中的时序图的配置需要根据LCD数据手册的时序时间来进行配置,只有这样才能对这个LCD正确操作;
根据时序图就可以分析出LCD是怎么传输数据的
3.1.1一帧数据的传输
一帧数据的传输的时钟基准是VCLK,它代表每个VCLK信号都代表一个正在传输的像素数据;对比两个数据手册可以得出这个VCLK时钟信号时间为5~12MHz
(1)INT_FrSyn是帧同步信号,如果有图像显示,就会发出一个中断信号,产生一个同步信号
(2)VSYNC是一帧的同步信号,一帧数据开始传输;通过对比可知,VSYNC脉冲宽度为Tvp,而且和S3C2440中的信号互为反向;
(3)VSPW表示VSYNC脉冲的宽度为(VDPW+1)个HSYNC信号周期=Tvp:表示VSPW+1行数据无效
(4)VBPD表示表示VSYNC信号后,还需要经过(VBPD+1)个HSYNC信号周期=Tvb;需要经过VBPD+1行无效数据后,才会出现有效数据
(5)VDEN表示有效数据开始传输;开始连续传输LINEVAL+1行数数据;
传输完后,VDEN变为低电平,还需要经过VFPD+1=Tvf个无效行数据,一帧数据才能结束;然后会进行下一帧数据传输;
3.1.2一帧数据中一行数据是怎么传输的呢
根据时序图可知;一帧数据的传输,是通过一行行数据才完成的;
(1)当HSYNC发出一个高脉冲时;一行数据开始传输;
(2)HSPW+1代表HSYNC脉冲宽度;需要经过HSPW+1个VCLK个周期=Thp;也就是需要经过HSPW+1个无效像素;
(3)HBPD+1代表还需要经过HBPD+1个VCLK时钟=Thb;也就是需要经过HBPD+1个无效像素,才可以出现有效像素。
(4)从HSYNC产生开始;在出现有效像素之前,一共需要经过HSPW+HBPD+2个无效像素,才能出现有效像素;
(5)VDEN代表数据使能信号;然后会连续传输HOZVAL+1个像素;当VDEN无效时,还需要经过HFDP+1个像素才能表示一行像素传输完成;
(6)HFPD+1代表一行有效数据传输完后,还需要经过HFPD+1个VCLK才能表示一行像素传输完毕;
3.2 详细解析LCD控制的配置方法
/*4.设置硬件相关的设置*/
/*设置LCDCON1
*设置CLKVAL[17:8]像素时钟,这个值要参考LCD手册VCLK的范围5~12MHz
*设置扫描模式TFT[6:5]=11
*设置像素模式为16BPP
*禁止LCD ENVID[0]控制信号
*/
lcd_reg->LCDCON1=(4<<8) |(0x03<<5)|(0x0c<<1);
/*设置垂直方向
*设置VBPD参考LCD手册时序图
* 设置VBPD+1=Tvb[31:24] =2; VBPD=1
*设置VSPW+1=Tvp[13:6]=10; VSPW=9;
*设置VFPD+1=Tvf[5:0]=2 VFPD=1;
*LINEVAL垂直长度为272-1=271[23:14]
*/
lcd_reg->LCDCON2=(1<<24) |(271<<14)|(1<<6) |(9<<0);
/*设置水平方向
*
*HBPD[25:9] >>2clk HBPD+1>2;HBPD=1;
*HOZVAL[18:9] 水平方向的480-1=479
*HFPD [7:0] HFPD+1 =Thf >=2;HFPD=1;
*/
lcd_reg->LCDCON3=(1<<19) |(479<<8) |(1<<0);
/*HSPW[7:0]Thp=41;HSPW+1=41;*/
lcd_reg->LCDCON4=(40<<0);
/*
*FRM565[11]=1
*INVVCLK[10]=1 来读取数据的触发方=1
*INVVLINE [9]判断HSYNC极性为反转1
*INVVFRAME[8]控制VSYNC极性反转=1
*INVVDEN[6]判断VD极性,不反转=0
*POWER[3] =0
*HWSWP=1
*BSWP=0
*/
lcd_reg->LCDCON5=(1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<1);
/*把缓冲区的地址写到寄存器中*/
lcd_reg->LCDSADDR1= (s3c_lcd->fix.smem_start >> 1) &~(3<<30);
lcd_reg->LCDSADDR2=((s3c_lcd->fix.smem_start+ s3c_lcd->screen_size)>>1) &0x1FFFFF;
lcd_reg->LCDSADDR3=(480*16/16);
4 编写LCD驱动框架
根据之前对LCD硬件的分析,现在开始编写代码
源码如下:
#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> static struct fb_info *s3c_lcd;
static u32 pseudo_palette[];
/*定义LCD配置寄存器*/
static struct s3c_reg {
u32 LCDCON1 ;
u32 LCDCON2 ;
u32 LCDCON3 ;
u32 LCDCON4 ;
u32 LCDCON5 ;
u32 LCDSADDR1;
u32 LCDSADDR2;
u32 LCDSADDR3;
u32 REDLUT;
u32 GREENLUT;
u32 BLUELUT;
u32 resver[];
u32 DITHMODE;
u32 TPAL;
u32 LCDINTPND;
u32 LCDSRCPND;
u32 LCDINTMSK;
u32 TCONSEL;
}; static volatile unsigned int *gpccon;
static volatile unsigned int *gpcdat;
static volatile unsigned int *gpdcon;
static volatile unsigned int *gpbcon;
static volatile unsigned int *gpgcon;
static volatile unsigned int *gpbdat;
static volatile struct s3c_reg *lcd_reg; static inline unsigned int chan_to_field(unsigned int chan,
struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= - bf->length;
return chan << bf->offset;
}
static int lcdfb_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{ unsigned int val;
if(regno >)
return ;
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
pseudo_palette[regno] = val;
return ;
} static struct fb_ops s3c_lcd_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static int __init lcd_init(void)
{
/*1.分配一个fb_info结构体*/
s3c_lcd = framebuffer_alloc(,NULL);
gpgcon=(volatile unsigned int *)ioremap(0x56000060,);
gpccon = (volatile unsigned int *)ioremap(0x56000020,);
gpcdat=gpccon+;
gpdcon =(volatile unsigned int *)ioremap(0x56000030,);
gpbcon=(volatile unsigned int *)ioremap(0x56000010,);
gpbdat=gpbcon+; /*2.设置*/
lcd_reg =ioremap(0x4d000000,sizeof(struct s3c_reg));
/*2.2 设置可变参数 */
s3c_lcd->var.xres = ;//X轴的实际像素
s3c_lcd->var.yres =;//y轴实际像素
s3c_lcd->var.xres_virtual =;//虚拟像素设置和实际像素一样
s3c_lcd->var.yres_virtual =;
s3c_lcd->var.xoffset =;//实际像素和虚拟像素偏移值为0
s3c_lcd->var.yoffset =;
s3c_lcd->var.bits_per_pixel =;//每个像素点有16个位组成
s3c_lcd->var.red.offset =;//red在16位域中偏移值为11
s3c_lcd->var.red.length =;
s3c_lcd->var.red.msb_right =;
s3c_lcd->var.green.offset =;//red在16位域中偏移值为11
s3c_lcd->var.green.length =;
s3c_lcd->var.green.msb_right=;
s3c_lcd->var.blue.offset =;//red在16位域中偏移值为11
s3c_lcd->var.blue.length =;
s3c_lcd->var.blue.msb_right =;
s3c_lcd->var.activate =FB_ACTIVATE_NOW;
/*2.3 设置固定参数*/
strcpy(s3c_lcd->fix.id, "mylcd");
s3c_lcd->fix.smem_len =**/;//缓冲区大小
s3c_lcd->fix.type =FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual =FB_VISUAL_TRUECOLOR;
s3c_lcd->fix.line_length =*/;
/*2.4 设置其他设置*/
s3c_lcd->pseudo_palette = pseudo_palette;
s3c_lcd->fbops =&s3c_lcd_ops;
s3c_lcd->screen_size = **/;//屏幕像素的个数
s3c_lcd->screen_base =dma_alloc_writecombine(NULL, s3c_lcd->screen_size , &s3c_lcd->fix.smem_start,
GFP_KERNEL);
/*
*配置GPC引脚为数据引脚
*VD3~VD7
*gpccon = (0x10<<22)|(0x10<<24)|(0x10<<26)|(0x10<<28)|(0x10<<30);
/*VD10~VD15 VD19~VD23
gpdcon =(0x10<<4) |(0x10<<6) |(0x10<<8) |(0x10<<10) |(0x10<<12)|(0x10<<14) |\
(0x10<<22) |(0x10<<24) |(0x10<<26) |(0x10<<28) |(0x10<<23);
*/
*gpccon = 0xaaaaaaaa; /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
*gpdcon = 0xaaaaaaaa; /* GPIO管脚用于VD[23:8] */ /*GPG4为LCD_POWER*/
*gpgcon |=(<<);
/*GPB0为输出,控制LCD背光*/
*gpbcon &=~(<<);
*gpbcon |=;
*gpbdat &=~(<<);
/*
*配置GPD
*/
/*3.注册配置GPIO,用于LCD*/
/*GPC4为配置为VDEN
*GPC2配置为HSYNC
*GPC3配置为VSYNC
*GPC1配置为VCLK
*/
*gpccon =(<<)|(<<) |(<<) |(<<);
/*4.设置硬件相关的设置*/
/*设置LCDCON1
*设置CLKVAL[17:8]像素时钟,这个值要参考LCD手册CLOCK CYCLE
*设置MMODE为[7]=0
*设置扫描模式TFT[6:5]=11
*设置像素模式为16BPP
*禁止LCD控制信号
*/ lcd_reg->LCDCON1=(<<) |(0x03<<)|(0x0c<<); /*设置垂直方向
*设置VBPD参考LCD手册时序图
* 设置VBPD+1=Tvb[31:24] =2; VBPD=1
*设置VSPW+1=Tvp[13:6]=10; VSPW=9;
*设置VFPD+1=Tvf[5:0]=2 VFPD=1;
*LINEVAL垂直长度为272-1=271[23:14]
*/
lcd_reg->LCDCON2=(<<) |(<<)|(<<) |(<<);
/*设置水平方向
*
*HBPD[25:9] >>2clk HBPD+1>2;HBPD=1;
*HOZVAL[18:9] 水平方向的480-1=479
*HFPD [7:0] HFPD+1 =Thf >=2;HFPD=1;
*
*/
lcd_reg->LCDCON3=(<<) |(<<) |(<<);
/*
* HSPW[7:0]Thp=41;HSPW+1=41;
*
*/
lcd_reg->LCDCON4=(<<);
/*
*CON5
*FRM565[11]=1
*INVVCLK[10]=1 来读取数据的触发方=1
*为巍⑽仙厥剑?
*INVVLINE [9]判断HSYNC极性为反转1
*INVVFRAME[8]控制VSYNC极性反转=1
*INVVDEN[6]判断VD极性,不反转=0
*POWER[3] =0
*HWSWP=1
*BSWP=0
*/
lcd_reg->LCDCON5=(<<) | (<<) | (<<) | (<<) | (<<);
/*把缓冲区的地址写到寄存器中*/
lcd_reg->LCDSADDR1=(s3c_lcd->fix.smem_start >> ) &~(<<);
lcd_reg->LCDSADDR2=((s3c_lcd->fix.smem_start+ s3c_lcd->screen_size)>>) &0x1FFFFF;
lcd_reg->LCDSADDR3=(*/);
/*开启LCD控制信号*/
lcd_reg->LCDCON1 |=(<<);
*gpbdat |= ; /* 输出高电平, 使能背光 */
/*POWER输出*/
lcd_reg->LCDCON5 |=(<<);
/*根据LCD手册设置LCD控制器,比如VCLK频率*/
/*分配frambuffer,并把地址告诉LCD控制器,*/
register_framebuffer(s3c_lcd);
return ;
}
static void lcd_exit(void)
{
unregister_framebuffer(s3c_lcd);
framebuffer_release(s3c_lcd);
iounmap(gpgcon);
iounmap(gpccon);
iounmap(gpbcon);
iounmap(gpdcon);
dma_free_writecombine(NULL, s3c_lcd->screen_size , &s3c_lcd->fix.smem_start,
GFP_KERNEL);
lcd_reg->LCDCON1&= ~(<<); /* 关闭LCD本身 */ *gpbdat &= ~; /* 关闭背光 */
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
重新编译内核,要把原来的内核配置中的驱动自带的LCD驱动变成模块;然后make uImage 重新生成一个内核,然后再make module 生成一个模块;因为要用到
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,这三个函数,所以要把关于这三个函数的.c文件编译成模块,
然后在编译新编的LCD驱动,
insmod cfbfillrect.ko
insmod cfbcopyarea.ko
insmod cfbimageblit.ko
insmod LCD.ko
这样就把新编译的LCD驱动加载到内核中;
echo hello > /dev/tty1命令,则hello就会显示在LCD屏上
echo LCD.ko > /dev/fb0 则LCD屏会花屏;
显示结果
LCD底层驱动分析的更多相关文章
- LCD驱动分析【转】
转自:http://blog.csdn.net/hanmengaidudu/article/details/21559153 1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其 ...
- Linux的LCD驱动分析及移植
测试平台 宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 LCD驱动分析 LCD屏的驱动总体上分成两块,一块是GUI ...
- Linux驱动之内核自带的S3C2440的LCD驱动分析
先来看一下应用程序是怎么操作屏幕的:Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这个设备来供用户 ...
- [STM8L]基于STM8L152的TAB段式LCD液晶驱动的分析 - 单片机干货 - 中国电子技术论坛 - 最好最受欢迎电子论坛!
[STM8L]基于STM8L152的TAB段式LCD液晶驱动的分析 - 单片机干货 - 中国电子技术论坛 - 最好最受欢迎电子论坛!.md 主控芯片为STM8L152C4T6自带LCD控制器,低功耗系 ...
- S3C6410 LCD驱动分析(转)
一. 理论分析1. 几个概念:FIMC : Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD: Fully Interactive Mob ...
- mini2440触摸屏驱动分析
mini2440驱动分析系列之 ---------------------------------------Mini2440触摸屏程序分析 By JeefJiang July,8th,2009 这是 ...
- 10. LCD驱动程序 ——框架分析
引言: 由LCD的硬件原理及操作(可参看韦哥博客:第017课 LCD原理详解及裸机程序分析) 我们知道只要LCD控制器的相关寄存器正确配置好,就可以在LCD面板上显示framebuffer中的内容. ...
- linux串口驱动分析——发送数据
一.应用程序中write函数到底层驱动历程 和前文提到的一样,首先先注册串口,使用uart_register_driver函数,依次分别为tty_register_driver,cdev_init函数 ...
- linux串口驱动分析——打开设备
串口驱动是由tty_driver架构实现的.一个应用程序中的函数要操作硬件,首先会经过tty,级级调用之后才会到达驱动之中.本文先介绍应用程序中打开设备的open函数的整个历程. 首先在串口初始化中会 ...
随机推荐
- Maven 手动添加 JAR 包到本地仓库
Maven 确确实实是个好东西,用来管理项目显得很方便,但是如果是通过 Maven 来远程下载 JAR 包的话,我宿舍的带宽是4兆的,4个人共用,有时候用 Maven 来远程下载 JAR 包会显得很慢 ...
- 使用vs中的发布功能发布asp.net core项目时遇到ERROR_CERTIFICATE_VALIDATION_FAILED错误
今天将VS2015编制的一个asp.net core项目发布到服务器进行测试,使用的是vs中主菜单"生成"中的"发布"功能. 遇到了一个错误,在网上反复检索尝试 ...
- Android Studio安装和配置(个人研究,有错请指导)
安装Android Studio的原因:公司有app开发者,然而公司没有测试,只好互相测试,本人并没有接触过app开发,纯小白: 自己试着安装了一下Android Studio来这里记录并分享遇到的问 ...
- javascript原型prototype浅识
C++,java是基于类的语言,主要通过类来实现继承. javascript是基于原型的语言,通过原型来实现继承. 什么是原型?每种物质,都可以追根溯源,原型就是对象的根源.继承就是追根溯源. jav ...
- Linux下配置IP及安装vmware tool
=======================CentOS 7以下======================= 配置IP: 1.获得mac地址 2.编辑ifcfg-eth0文件:vi /etc/sy ...
- 解决driver.findElement(By)运行到此处报null指针问题
1.由于自动化页面上的元素定位太多,主要是通过By来定位,而By提供了id,xpath,name差不多就可以定位到元素 可以使用一个配置文件存储页面上的定位By值,然后从配置文件获取by值,行程by方 ...
- java selenium后报错Element not found in the cache元素定位要重新赋值之前的定义
习惯上把定位的元素在操作之前就定位好, 例如: WebElement element1=driver.findElement(...); ----------declaration1 Web ...
- 我需要在Web上完成一个图片上传的功能后续(+1)
微信入口施工完成.关键字识别中增加了本次活动的"关键字",在系统中增加了链接.不过,由于地址包含私密关键参数,这里隐藏,敬请原谅. 下一步,微信链接的地址页面是要对微信用户的信息进 ...
- MVC5+EF6 入门完整教程九
前一阵子临时有事,这篇文章发布间隔比较长,我们先回顾下之前的内容,每篇文章用一句话总结重点. 文章一 MVC核心概念简介,一个基本MVC项目结构 文章二 通过开发一个最基本的登录界面,介绍了如何从Co ...
- 用c++写一个广告系统
用到的基础类库 1.sstream <sstream> 库定义了三种类:istringstream.ostringstream和stringstream,分别用来进行流的输入.输出和输入输 ...