根据分析的框架,自己写一个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屏会花屏;

    显示结果

最新文章

  1. 补发:用Meal Prep+模块化饮食来减肥之实操
  2. PetaPoco4.0的事务为什么不会回滚
  3. NOI 题库 7218
  4. 关于前端CSS预处理器Sass的小知识!
  5. 学习ASP.NET MVC(五)——我的第一个ASP.NET MVC CURD页面
  6. ubuntu14.04纯命令行下连接有线网和无线网
  7. JS Replace 全部替换字符 用法
  8. vim跳到文件头和文末结尾
  9. android学习笔记一——简介
  10. 修改tomcat浏览器地址栏图标
  11. Uploadify 3.2 上传图片
  12. nginx url 重写 [转]
  13. DedeCms 建站随笔(一)
  14. Win10报错0x800f0906
  15. javascript中forEach()和jquery中each()的区别
  16. codehouse
  17. SpringBoot中使用SpringDataJPA
  18. STM32 RTC时钟的配置
  19. LINUX第四周学习
  20. Java--解压缩zip包

热门文章

  1. 获取Echarts的DataZoom的起始值
  2. AF引起的camera偶现卡顿问题
  3. winform异步系统升级—BackgroundWorker
  4. php使用PDO连接mysql数据库
  5. 【Java】异常处理_学习笔记
  6. 小程序 web 端实时运行工具
  7. MySQL wamp密码修改
  8. eclipse不显示Android SDK Manager标签
  9. mybatis if test 不为空字符串或null
  10. Python:循环语句