LCD驱动分析_LCD控制器设置及代码详解
理解LCD屏幕的驱动原理与调试过程,示例的驱动IC为GC9308,展示整个屏幕的驱动过程。

理解LCD屏幕的驱动原理与调试过程,⽰例的驱动IC为GC9308,展⽰整个屏幕的驱动过程。
起因最近拿到了⼀个⽐较新的驱动 IC 的 LCD 了,此前 K210 上⾯使⽤的都是 ST7789V ILI9342C SH1106 这类驱动 IC 的屏幕模块。
这次来了⼀个 GC9308 ,我想我需要认识⼀下屏幕驱动的整体架构,也就是拿起数据⼿册当作学习教材来学了,实际上学完以后,懂了以后都不难,重点在如何总结这些屏幕的驱动逻辑,以此打下往后的屏幕驱动理解基础。
我需要读懂图像的⼆进制定义、还有传输⽅式,我找了⼀本中⽂的屏幕数据⼿册来读,了解⼀下相关的流程和细节本⽂我只会交待软件层⾯的理解,硬件⽅⾯的定义和特性我⽆法给出准确的解释,姑不会提及。
屏幕的发展历程让我们看⼀下这个⼤哥的故事,就很好的说明了这段 LCD MCU 发展的历史。
记得在很早的时候,那时候还都是FSTN的显⽰屏满天飞的时候(也是⼩弟刚刚毕业开始作⼿机的时候)。
LCD的驱动电路有很多是两⽚芯⽚的,⼀⽚LCDC,⼀⽚LCD Driver,⼀般的LCDC⾥⾯有⼀个display的buffer。
LCDDriver是电路驱动液晶显⽰部分的电路,没有什么好讲的。
更早的时候,LCD上就⼀⽚LCDDriver就⾏了,程序员需要控制两个(H,V)场扫描信号,⽽且程序员希望在某个坐标显⽰,都需要编程控制驱动电路来实现,后来发现显⽰屏越来越⼤,⽽MCU以及程序员没有这个能⼒和精⼒来对LCD进⾏这类的同步控制,于是LCDC就诞⽣出来承担起这些个功能。
后来加上了buffer,就是说程序员可以把⼤批的显⽰内容以显⽰矩阵(display matrix)的形式写到buffer⾥,让LCDC来读取buffer⾥的数据再由LCDDriver显⽰到显⽰屏上。
后来这个buffer越来越⼤,除了显⽰矩阵以外还放很多命令,所以也不能⽼把它笼统的叫buffer啊,所以就对放显⽰矩阵的存储空间有了⼀个专⽤的名字叫做GRAM。
基于STM32单片机FSMC接口驱动LCD的配置与分析

基于STM32单片机FSMC接口驱动LCD的配置与分析概述:STM32单片机是一款高性能、低功耗的32位ARM Cortex-M系列微控制器。
它具有丰富的外设接口,其中包括FSMC(Flexible Static Memory Controller)接口,用于连接外部存储器,例如LCD显示器。
本文将详细介绍如何配置和驱动LCD显示器,以及分析FSMC接口的工作原理。
一、LCD驱动接口配置1. 在STM32的标准外设库中,FSMC的配置函数位于STM32F10x_stdperiph_driver库的stm32f10x_fsmc.c和stm32f10x_fsmc.h文件中。
通过这些函数,可以配置FSMC接口的参数,以使它能够连接和驱动LCD。
2.首先,需要配置FSMC的时钟预分频值。
根据LCD的要求以及系统时钟频率,选择适当的预分频值。
这可以通过设置FSMC_BCRx寄存器中的MBKEN和PS字段来实现。
3.然后,需要配置FSMC的存储芯片选择使能信号(CSEN)和片选信号(ALE)。
这可以通过设置FSMC_BCRx寄存器中的CSEN和ALEN字段来实现。
4.接下来,需要配置FSMC的读写延迟、数据宽度、存储器类型等参数。
这可以通过设置FSMC_BTRx和FSMC_BWTRx寄存器来实现。
5.最后,需要配置FSMC的地址线、数据线和控制线的映射关系。
这可以通过设置FSMC_BCRx寄存器中的MWID、MTYP、MUXEN、MWID和NWID 字段来实现。
二、FSMC接口工作原理1.FSMC接口是一种高速并行接口,它通过多路复用来连接不同的外部存储器。
它具有独立的读/写数据线和地址线,以及控制线,用于选择读/写操作和片选信号。
2. FSMC接口支持不同类型的存储器,例如SRAM、NOR Flash、NAND Flash和LCD。
每种存储器都有不同的时序和接口要求。
3.FSMC接口的时序参数主要包括时钟预分频值、读/写延迟、数据宽度和地址线宽度等。
LCD驱动代码 2

LCD驱动主要程序如下:#ifdef CONFIG_PM#include <linux/pm.h>#endif#include "mys3c2410fbdata.h"#define CONFIG_FB_S3C2410_DEBUG/* Debugging stuff */#ifdef CONFIG_FB_S3C2410_DEBUGstatic int debug = 1;#elsestatic int debug = 0;#endif#define dprintk(msg...) { printk(KERN_ALERT "s3c2410fb: " msg); }/*FrameBuffer设备名称*/static char driver_name[] = "s3c2410_lcd";/*定义一个结构体用来维护驱动程序中各函数中用到的变量*/struct s3c2410fb_var{int lcd_irq_no; /*保存LCD中断号*/struct clk *lcd_clock; /*保存从平台时钟队列中获取的LCD时钟*/struct resource *lcd_mem; /*LCD的IO空间*/void __iomem *lcd_base; /*LCD的IO空间映射到虚拟地址*/struct device *dev;struct s3c2410fb_hw regs; /*表示5个LCD配置寄存器,s3c2410fb_hw 定义在mach-s3c2410/include/mach/fb.h中*//*定义一个数组来充当调色板。
据数据手册描述,TFT屏色位模式为8BPP 时,调色板(颜色表)的长度为256,调色板起始地址为0x4D000400*/ u32 palette_buffer[256];u32 pseudo_pal[16];unsigned int palette_ready; /*标识调色板是否准备好了*/};/*用做清空调色板(颜色表)*/#define PALETTE_BUFF_CLEAR (0x80000000)/*填充调色板*/static void s3c2410fb_write_palette(struct s3c2410fb_var *fbvar){unsigned int i;void __iomem *regs = fbvar->lcd_base;fbvar->palette_ready = 0;for (i = 0; i < 256; i++){unsigned long ent = fbvar->palette_buffer[i];if (ent == PALETTE_BUFF_CLEAR){continue;}/*#define S3C2440_ADDR(x) ((void __iomem __force *)0xF0000000 + (x)) #define S3C24XX_V A_LCD S3C2440_ADDR(0x00300000)#define S3C2440_LCDREG(x) ((x) + S3C24XX_V A_LCD)#define S3C2440_TFTPAL(x) S3C2410_LCDREG((0x400 + (x)*4))*/writel(ent, regs + S3C2440_TFTPAL(i));if (readw(regs + S3C2440_TFTPAL(i)) == ent){fbvar->palette_buffer[i] = PALETTE_BUFF_CLEAR;}else{fbvar->palette_ready = 1;}}}/*LCD中断服务程序*/static irqreturn_t lcd_fb_irq(int irq, void *dev_id){struct s3c2410fb_var *fbvar = dev_id;void __iomem *lcd_irq_base;/*LCD中断挂起寄存器基地址*/lcd_irq_base = fbvar->lcd_base + S3C2440_LCDINTBASE;/*读取LCD中断挂起寄存器的值*/lcdirq = readl(lcd_irq_base + S3C2440_LCDINTPND);/*判断是否为中断挂起状态*/if(lcdirq & S3C2440_LCDINT_FRSYNC){/*填充调色板*/if (fbvar->palette_ready){s3c2410fb_write_palette(fbvar);}/*设置帧已插入中断请求*/writel(S3C2440_LCDINT_FRSYNC, lcd_irq_base +S3C24XX_LCDINTPND);writel(S3C2440_LCDINT_FRSYNC, lcd_irq_base +S3C24XX_LCDSRCPND);}return IRQ_HANDLED;}/*该函数实现修改GPIO端口的值,注意第三个参数mask的作用是将要设置的寄存器值先清零*/static inline void modify_gpio(void __iomem *reg, unsigned long set, unsigned long mask){unsigned long tmp;tmp = readl(reg) & ~mask;writel(tmp | set, reg);}/*LCD各寄存器进行初始化*/static int s3c2410fb_init_registers(struct fb_info *fbinfo){void __iomem *tpal;void __iomem *lpcsel;/*从lcd_fb_probe探测函数设置的私有变量结构体中再获得LCD相关信息的数据*/struct s3c2410fb_var *fbvar = fbinfo->par;struct s3c2410fb_mach_info *mach_info = fbvar->dev->platform_data;/*获得临时调色板寄存器基地址,S3C2440_TPAL宏定义在mach-s3c2440/include/mach/regs-lcd.h中。
LCD驱动代码

1. 驱动代码#include<linux/module.h>#include<linux/kernel.h>#include<linux/fs.h>#include<linux/init.h>#include<linux/delay.h>#include<asm/uaccess.h>#include<asm/irq.h>#include<asm/io.h>#include<linux/device.h>#define DEV_NAME "cdev_leds"#define rGPBCON (*(volatile unsigned *)(base_addr+0))#define rGPBDAT (*(volatile unsigned *)(base_addr+1))#define rGPBUP (*(volatile unsigned *)(base_addr+2))struct class *char_dev_class;static struct class_device *p_class_devs;int major=0;struct file *filp;unsigned int *base_addr=0;#define DEV_SIZE (4)int first_chardev_open(struct inode *inode,struct file *file){printk(KERN_EMERG"open() call\r\n");return 0;}ssize_t first_chardev_read(struct file *file,char __user *buf,ssize_t count,loff_t *f_pos){int retval=0;char led_buffer[DEV_SIZE]={2,2,2,2};printk(KERN_EMERG"read() call\n");led_buffer[0]=!(rGPBDAT&(1<<5));led_buffer[1]=!(rGPBDAT&(1<<6));led_buffer[2]=!(rGPBDAT&(1<<7));led_buffer[3]=!(rGPBDAT&(1<<8));if(*f_pos>=DEV_SIZE)goto out;if(*f_pos+count>DEV_SIZE)count=DEV_SIZE-*f_pos;if(copy_to_user(buf,&led_buffer[0+*f_pos],count)){retval=-EFAULT;goto out;}*f_pos+=count;retval=count;return retval;out:return 0;}ssize_t first_chardev_write(struct file *file,const char __user *buf,size_t count,loff_t *f_pos){char led_buffer[DEV_SIZE]={3,3,3,3};int retval=0;int i=0;printk(KERN_EMERG"write() call\n");if(*f_pos>=DEV_SIZE)goto out;if(*f_pos+count>DEV_SIZE)count=DEV_SIZE-*f_pos;if(copy_from_user((void *)led_buffer[*f_pos],(const void __user *)buf,count)){retval=-EFAULT;goto out;}for(i=0;i<count;i++){if(led_buffer[*f_pos+i]==1)rGPBDAT&=~(1<<(5+*f_pos+i));else if(led_buffer[*f_pos]==0)rGPBDAT|=(1<<(5+*f_pos+i));else{printk("value err!\r\n");goto out;}}*f_pos+=count;retval=count;out:return retval;}int first_chardev_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg){printk(KERN_EMERG"ioctrl() call\n");return 0;}int first_chardev_release(struct inode *inode,struct file *file) {printk(KERN_EMERG"release call\n");return 0;}loff_t first_chardev_llseek(struct file *file,loff_t off,int whence) {loff_t newpos=0;int offset=off;printk(KERN_EMERG"llseek call\n");printk(KERN_EMERG"offset:%d;whence:%d\r\n",offset,whence);switch(whence){case SEEK_SET:newpos=offset;printk(KERN_EMERG"SEEK_SET:newpos:%d\n",newpos);break;case SEEK_CUR:newpos=filp->f_pos+offset;printk(KERN_EMERG"SEEK_CUR:newpos:%d\n",newpos);break;case SEEK_END:newpos=4+offset;printk(KERN_EMERG"SEEK_END:newpos:%d\n",newpos);break;default:return -EINVAL;}if(newpos<0)return -EINVAL;filp->f_pos=newpos;return newpos;}static struct file_operations first_chardev_fops={.owner=THIS_MODULE,.open=first_chardev_open,.release=first_chardev_release,.read=first_chardev_read,.write=first_chardev_write,.llseek=first_chardev_llseek,.ioctl=first_chardev_ioctl,};static int __init s3c24xx_leds_init(void){printk(KERN_EMERG"module init mow!\n");base_addr=(unsigned int *)ioremap(0x56000010,16);rGPBCON&=~(3<<10|3<<12|3<<14|3<<16);rGPBCON|=(3<<10|3<<12|3<14|3<<16);rGPBUP&=~(1<<5|1<<6|1<<7|1<<8);rGPBDAT&=~(1<<5|1<<6|1<<7|1<<8);rGPBDAT|=(1<<5|1<<6|1<<7|1<<8);major=register_chrdev(major,DEV_NAME,&first_chardev_fops);printk(KERN_EMERG"major:%d\n",major);char_dev_class=class_create(THIS_MODULE,"myleds");if(IS_ERR(char_dev_class)){printk("Err,failed in creating class.\n");return 0;}p_class_devs=device_create(char_dev_class,NULL,MKDEV(major,0), NULL,DEV_NAME);printk(DEV_NAME"initialized\n");return 0;}static void __exit s3c24xx_leds_exit(void){unregister_chrdev(major,DEV_NAME);//device_unregister(p_class_devs);device_destroy(char_dev_class,MKDEV(major,0));class_destroy(char_dev_class);iounmap(base_addr);}module_init(s3c24xx_leds_init);module_exit(s3c24xx_leds_exit);MODULE_AUTHOR("XYD");MODULE_VERSION("1.0");MODULE_DESCRIPTION("S3C2440 LED Driver");MODULE_LICENSE("GPL");2.Makefile代码obj-m:=005th_led_cdev.oKDIR:=/lib/modules/$(shell uname -r)/buildall:make -C $(KDIR) M=$(PWD) modules#cp 005th_led_cdev.ko 005th_led_cdev_test/opt/s3c2440/root_nfs/home/#rm -rf *.o *.mod.o *.symvers .*.o *.cmd modules.order *.bak.tmp_versionsclean:make -C $(KDIR) M=$(PWD) cleanrm -rf *.ko *.o *.mod.o *.mod.c *.symvers .*.o *.cmd modules.order *.bak,tmp_versions *~ *.markers3.测试代码#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<sys/ioctl.h>#define DEV_NAME "/dev/cdev_leds"int main(int argc,char *argv[]){int fd=0;int ret=0;int i=0;int pos=0;unsigned char recv_buf[10]={2,2,2,2};unsigned char send_buf[10]={0,1,0,1};fd=open(DEV_NAME,O_RDWR);if(fd<0){printf(DEV_NAME"can not open devce!\n");prrror("open:");exit(0);}for(i=0;i<4;i++){ret=write(fd,&send_buf[i],1);printf("ret: %d\n",ret);sleep(1);}pos=lseek(fd,1,SEEK_SET);printf("ret:%d\n",ret);sleep(1);ret=read(fd,&recv_buf[pos],4-pos);printf("%d;%d;%d;%d\n",recv_buf[0],recv_buf[1],recv_buf[2],recv_buf[3]);if(recv_buf[0]==0)printf("led0 off\n");else if(recv_buf[0]==1)printf("led0 on\n");elseprintf("led0 status unnowke\n");if(recv_buf[1]==0)printf("led1 off\n");else if(recv_buf[1]==1)printf("led1 on\n");elseprintf("led1 status unnowke\n");if(recv_buf[2]==0)printf("led2 off\n");else if(recv_buf[2]==1)printf("led2 on\n");elseprintf("led2 status unnowke\n");if(recv_buf[3]==0)printf("led3 off\n");else if(recv_buf[3]==1)printf("led3 on\n");elseprintf("led3 status unnowke\n");sleep(1);ret=lseek(fd,SEEK_CUR);printf("ret:%d\n",ret);sleep(1);ret=lseek(fd,SEEK_END);printf("ret:%d\n",ret);sleep(1);return 0;}。
LCD液晶显示驱动程序设计指引

美的集团制冷事业本部企业标准QJ/MK03.056-2004 LCD液晶显示驱动程序设计指引1适用范围《LCD液晶显示驱动程序设计指引》主要对采用液晶驱动芯片HD1621(或此系列芯片)进行LCD 液晶的驱动方法进行了分析,说明了驱动芯片的功能、软件编制方法和注意事项,并提供了程序范例,为以后的程序设计者提供类似的开发参考。
2引用资料范例程序采用日本NEC公司的RA78K0S系列汇编语言编写,具体技术资料参照78K0S系列八位单片机UPD78F9177芯片的相关资料。
液晶驱动芯片参考资料:具体见HT1621DATASHEET。
3定义汇编语言:是用于编写微处理器软件的最基本编程语言。
汇编程序包:是一组程序的总称,用于把汇编语言的源程序文件转换成机器代码的程序,通常包括汇编程序﹑连接程序﹑目标码转换程序和其它库管理程序﹑表转换程序等。
LCD:液晶显示器简称。
4HT162X驱动芯片资料介绍4.1概述HT162X系列芯片是由HOTEK公司开发生产的多功能LCD 驱动器芯片,HT162X 的软件配置特性使其适合于各种LCD 的应用包括LCD 模块和显示子系统,主控器与HT162X通信只需要3 到4 条线。
由于采用了电容型偏置电压充电泵使得HT1620 的操作电流非常的小。
HT162X 系列包括多款产品适合不同的应用,目前广泛应用于各种液晶驱动控制上。
4.2芯片特性➢操作电压2.4V~3.3V➢LCD 电压3.6V~4.9V可调➢可选择1/2 或1/3 偏置1/2, 1/3 或1/4 占空比➢内部时基频率源➢片内电容型偏置充电泵➢读/写地址自动增加➢3线(或4线)串行接口➢软件配置特性➢两个可选的蜂鸣器频率2KHz 或4KHz4.3HT162X系列芯片选型表片内振荡器-√√-√√√晶体振荡器√√-√√√√5HT1621芯片说明HT1621为32*4位LCD驱动器,共有四种子型号,分别是HT1621-48SSO、HT1621B-48SSOP/DIP、HT1621D-28SKDIP,我们现在使用的为HT1621B-48SSOP,以下就以此芯片为例进行说明。
RK3399MipiLCDDriver代码分析·Rockchip

RK3399MipiLCDDriver代码分析·Rockchip RK3399 Mipi LCD Driver 代码分析KernelVersion: 4.4.70+Documentation/devicetree/bindings/video/rockchip_fb.txt+概览总的来说,RK LCD 的 driver 有如下四个部分:+1. FB 框架相关的部分2. LCDC 控制器相关的部分3. LCD 屏幕配置相关的部分4. Mipi 驱动代码➜ rockchip git:(master) ✗ tree ./driver/video/.├── backlight 背光相关├── fbdev FB 框架│└── core FB 核⼼代码│├── fbmem.c│└── fbsysfs.c└── rockchip├── rk_fb.c 平台 FB 驱动├── rkfb_sysfs.c├── lcdc│├── rk322x_lcdc.c│└── rk322x_lcdc.h├── screen│├── lcd_general.c│├── lcd_mipi.c│└── rk_screen.c 屏幕配置⽂件共⽤代码└── transmitter Mipi 驱动代码├── rk32_mipi_dsi.c├── rk32_mipi_dsi.h├── mipi_dsi.c└── mipi_dsi.hRK FBDEV 框架相关代码drivers/video/fbdev/core/fbmem.cdrivers/video/rockchip/rk_fb.cdrivers/video/rockchip/rkfb_sysfs.cinclude/linux/rk_fb.hfbmem.c是 upstream 的代码。
它的作⽤在于:向上提供了和⽤户空间交接的接⼝(open/read/write/ioctl); 向下联系平台相关的 fb 驱动 rk_fb.c。
lcd设备驱动之全解析
linux中LCD设备驱动(1)——framebuffer(帧缓冲)1、framebuffer 帧缓冲帧缓冲(framebuffer)是Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差别,允许上层应用步伐在图形模式下直接对显示缓冲区进行读写操纵。
用户不必体贴物理显示缓冲区的具体位置及存放方法,这些都由帧缓冲设备驱动自己来完成。
framebuffer机制模仿显卡的功效,将显卡硬件结构抽象为一系列的数据结构,可以通过framebuffer的读写直接对显存进行操纵。
用户可以将framebuffer看成是显存的一个映像,将其映射到进程空间后,就可以直接进行读写操纵,写操纵会直接反应在屏幕上。
framebuffer是个字符设备,主设备号为29,对应于/dev/fb%d 设备文件。
通常,使用如下方法(前面的数字表现次设备号)0 = /dev/fb0 第一个fb 设备1 = /dev/fb1 第二个fb 设备fb 也是一种普通的内存设备,可以读写其内容。
例如,屏幕抓屏:cp /dev/fb0 myfilefb 虽然可以像内存设备(/dev/mem)一样,对其read,write,seek 以及mmap。
但区别在于fb 使用的不是整个内存区,而是显存部门。
2、fb与应用步伐的交互对付用户步伐而言,它和其他的设备并没有什么区别,用户可以把fb看成是一块内存,既可以向内存中写数据,也可以读数据。
fb的显示缓冲区位于内核空间,应用步伐可以把此空间映射到自己的用户空间,在进行操纵。
在应用步伐中,操纵/dev/fbn的一般步调如下:(1)打开/dev/fbn设备文件。
(2)用ioctl()操纵取恰当前显示屏幕的参数,如屏幕辨别率、每个像素点的比特数。
凭据屏幕参数可盘算屏幕缓冲区的巨细。
(3)用mmap()函数,将屏幕缓冲区映射到用户空间。
(4)映射后就可以直接读/写屏幕缓冲区,进行画图和图片显示了。
LCD驱动程序开发指南
LCD驱动程序开发指南LCD(液晶显示器)是一种常见的显示设备,广泛应用于电子产品中。
开发LCD驱动程序是实现LCD显示的重要环节。
本文将对LCD驱动程序的开发进行指南。
首先,开发LCD驱动程序前需要了解LCD的基本原理和特性。
LCD主要由像素阵列、控制芯片和背光源组成。
像素阵列是由许多小点组成的,每个小点称为像素。
控制芯片负责控制像素点的显示和刷新。
背光源提供背光亮度和显示效果。
在开发LCD驱动程序时,需要先选择适合的开发板和LCD控制芯片。
开发板上的LCD接口和控制芯片的接口需要匹配。
然后需要了解LCD控制芯片的规格和技术手册,包括寄存器的功能和操作方式等。
接下来,需初始化LCD控制芯片。
初始化的目的是设置LCD控制芯片的工作模式和参数。
通常包括设置像素点的个数和位深度、设置显示模式和刷新率等。
初始化还需要设置LCD的工作电压和背光源的亮度。
初始化的具体步骤可以参考LCD控制芯片的技术手册。
然后,需编写LCD控制代码。
这部分工作需要根据LCD控制芯片的通讯协议和接口规范进行。
常见的通讯协议有RGB接口、I2C总线和SPI总线等。
根据协议进行通讯后,可以发送命令和数据到LCD控制芯片,以实现显示和刷新。
具体的代码编写要根据具体的开发板和控制芯片进行,可以参考控制芯片的技术手册和示例代码。
另外,需编写LCD驱动程序的测试代码。
测试代码可以验证LCD驱动程序的功能和正确性。
可以通过显示图像、文本和图形等方式进行测试。
测试代码应涵盖各种显示模式和刷新率,以保证LCD驱动程序的稳定性和可靠性。
最后,需进行调试和优化。
调试主要是针对LCD显示不正常或显示效果不理想的问题。
可以通过查看LCD控制芯片的寄存器状态和输出信号等,确定问题的原因和解决方案。
优化可以提高LCD驱动程序的性能和效率,例如采用硬件加速或优化算法等。
综上所述,开发LCD驱动程序需要了解LCD的基本原理和特性,选择适合的开发板和LCD控制芯片,进行初始化和编写控制代码,编写测试代码,进行调试和优化。
Linux驱动之LCD驱动编写
Linux驱动之LCD驱动编写在这篇博客中已经分析了编写LCD驱动的步骤,接下来就按照这个步骤来字尝试字节编写LCD驱动。
⽤的LCD屏幕为tft 屏,每个像素点为16bit。
对应与红绿蓝分别为565。
1、分配⼀个fb_info结构2、设置fb_info结构3、硬件相关的操作,配置LCD时钟、配置IO端⼝、配置LCD寄存器。
4、最终注册fbinfo结构到registered_fb数组要理解LCD的⼯作原理,需要了解LCD的时钟,在TFT的LCD中有如下的时钟。
这个⼏个时钟数据在配置LCD寄存器时都说需要设置的。
1、VCLK:两个像素之间的时钟,即两个像素隔多长时间才能显⽰下⼀个像素2、HSYNC:⽔平同步时钟,即第⼀⾏像素点显⽰完成之后隔多长时间才能开始下⼀⾏的显⽰3、VSYNC:垂直⽅向的同步时钟,也叫帧同步信号,即⼀帧数据显⽰完成之后(⼀帧数据表⽰⼀个屏幕显⽰完成,即⼀个显存的数据全部取完),过多长下⼀帧数据才开始显⽰本节需要⽤到的函数: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[16]; //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[3];};可变的参数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[5]; /*保留*/}1.写驱动程序:(驱动设置:参考⾃带的LCD平台驱动drivers/video/s3c2410fb.c )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_info4)开启LCD,并注册fb_info: register_framebuffer() 4.1) 直接在init函数中开启LCD(后⾯讲到电源管理,再来优化) 控制LCDCON5允许PWREN信号, 然后控制LCDCON1输出PWREN信号, 输出GPB0⾼电平来开背光, 4.2) 注册fb_info在驱动exit出⼝函数中:1)卸载内核中的fb_info2) 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址3)释放DMA缓存地址dma_free_writecombine()4)释放注册的fb_info1.2 具体代码如下:#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <asm/io.h> //含有iomap函数iounmap函数#include <asm/uaccess.h>//含有copy_from_user函数#include <linux/device.h>//含有类相关的处理函数#include <linux/fb.h> //含有fb_info结构体定义//#include <asm/dma-mapping.h> //含有dma_free_writecombine宏定义#include <linux/dma-mapping.h> //含有dma_free_writecombine宏定义#include <linux/platform_device.h>//含有平台设备总线模型相关变量#include <linux/mm.h>#include <linux/slab.h>//#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控制寄存器放在⼀个结构体⾥⾯*/struct lcd_regs {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[9];unsigned long dithmode;unsigned long tpal;unsigned long lcdintpnd;unsigned long lcdsrcpnd;unsigned long lcdintmsk;unsigned long lpcsel;};static struct fb_info *s3c_mylcdfb_info;//fb_info结构体static volatile unsigned long *gpbcon;//GPB0⽤于lcd背光的控制static volatile unsigned long *gpbdat;//GPB0⽤于lcd背光的控制static volatile unsigned long *gpccon;static volatile unsigned long *gpdcon;static volatile unsigned long *gpgcon;//GPG4⽤于lcd电源static volatile struct lcd_regs* lcd_regs;//lcd寄存器static u32 pseudo_palette[16]; //调⾊板内存/* from pxafb.c */static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf){chan &= 0xffff; //取出16bit的数据chan >>= 16 - bf->length; //return chan << bf->offset;}static int s3c_mylcdfb_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 > 16)return1;/* ⽤red,green,blue三原⾊构造出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;pseudo_palette[regno] = val;return0;}static struct fb_ops s3c_mylcdfb_ops = { //操作函数结构体.owner = THIS_MODULE,.fb_setcolreg = s3c_mylcdfb_setcolreg,//待会设置,这个是调⾊板,如果使⽤⼩于16bit的像素需要⽤到 .fb_fillrect = cfb_fillrect,.fb_copyarea = cfb_copyarea,.fb_imageblit = cfb_imageblit,};static int lcd_drv_init(void){/*1、分配⼀个fb_info*/s3c_mylcdfb_info = framebuffer_alloc(0,NULL);//size为额外分配的⼤⼩,这⾥不需要,所以设为0if(s3c_mylcdfb_info==NULL){printk("unframebuffer_alloc\n");return1;}/*2、设置*//*2.1 设置固定的参数*/strcpy(s3c_mylcdfb_info->fix.id, "mylcd");//名字//s3c_mylcdfb_info->fix.smem_start = ;//显存的物理起始地址,后⾯设置s3c_mylcdfb_info->fix.smem_len = 480*272*16/8;//单位为字节,每个像素点占⽤16bit :565,显存的⼤⼩ s3c_mylcdfb_info->fix.type = FB_TYPE_PACKED_PIXELS;//LCD类型,填充像素的类型 tft//s3c_mylcdfb_info->fix.type_aux= //附加的LCD类型,不需要设置s3c_mylcdfb_info->fix.visual = FB_VISUAL_TRUECOLOR;//视觉类型,选择真彩⾊s3c_mylcdfb_info->fix.line_length = 480*16/8; //⼀⾏的长度,单位为字节// s3c_mylcdfb_info->fix.mmio_start = //控制lcd的寄存器的物理地址// s3c_mylcdfb_info->fix.mmio_len = //控制lcd的寄存器的⼤⼩/*2.2 设置可变的参数*/s3c_mylcdfb_info->var.xres = 480;//x⽅向的分辨率s3c_mylcdfb_info->var.yres = 272;//y⽅向的分辨率s3c_mylcdfb_info->var.xres_virtual = 480;//x⽅向的虚拟分辨率s3c_mylcdfb_info->var.yres_virtual = 272;//y⽅向的虚拟分辨率s3c_mylcdfb_info->var.bits_per_pixel = 16;//每个像素的⼤⼩,单位为bits3c_mylcdfb_info->var.grayscale = 0;//灰度值s3c_mylcdfb_info->var.red.length = 5;//红⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.green.length = 6;//绿⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.blue.length = 5;//蓝⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.red.offset= 11;//红⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.green.offset= 6;//绿⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.blue.offset=0;//蓝⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.red.msb_right= 0;//低位在前还是⾼位在前,⼀般⾼位在前,也就是⼩端模式s3c_mylcdfb_info->var.green.msb_right= 0;s3c_mylcdfb_info->var.blue.msb_right=0;s3c_mylcdfb_info->var.activate = FB_ACTIVATE_NOW;//使⽤默认参数,显存⽴刻⽣效/*2.3 设置操作函数*/s3c_mylcdfb_info->fbops = &s3c_mylcdfb_ops;/*2.4 其它的⼀些设置 */s3c_mylcdfb_info->pseudo_palette = pseudo_palette;//调⾊板的地址//s3c_mylcdfb_info->screen_base = ;//显存的虚拟基地址s3c_mylcdfb_info->screen_size = 480*272*16/8;//单位为字节,每个像素点占⽤16bit :565,显存的⼤⼩/*3、硬件相关的操作 *//*3.1、配置GPIO⽤于LCD*/gpbcon = ioremap(0x56000010, 8);//将实际的寄存器地址转换为虚拟地址gpccon = ioremap(0x56000020 , 4);gpdcon = ioremap(0x56000030 , 4);gpgcon = ioremap(0x56000060 , 4);gpbdat = gpbcon + 1;*gpccon = 0xaaaaaaaa; /* GPIO管脚⽤于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */*gpdcon = 0xaaaaaaaa; /* GPIO管脚⽤于VD[23:8] */*gpbcon &= ~(3); /* GPB0设置为输出引脚 */*gpbcon |= 1;*gpbdat &= ~1; /* 输出低电平关闭LCD背光 */*gpgcon |= (3<<8); /* GPG4⽤作LCD_PWREN 电源*//*3.2、根据LCD⼿册设置LCD控制器,⽐如VCLK的频率等 */lcd_regs = ioremap(0X4D000000 , sizeof(struct lcd_regs));/** bit[17:8] : VCLK = HCLK / [(CLKVAL+1) x 2]* 10M = 100M/[(CLKVAL+1) x 2]* CLKVAL = 4** bit[6:5] :PNRMODE = 11显⽰模式,选择TFT模式** bit[4:1] :BPPMODE = 1100;像素=16bit 565** bit[0] :ENVID = 0;先关闭LCD控制器*/lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1);///** [31:24] : VBPD = 帧同步信号发出后,过多长时间开始显⽰数据,单位为⾏,理解为1⾏的时间* 看LCD⼿册tvb = VBPD + 1 = 2;所以VBPD = 1** [23:14]:LINEVAL + 1= 272;,所以LINEVAL = 271;垂直⽅向尺⼨,多少⾏** [13:6]:VFPD = ⼀帧的数据传输完成之后,过多长时间开始下⼀帧数据的帧同步信号,单位为⾏,理解为1⾏的时间 * 看LCD⼿册tvf = VFPD + 1 = 2;所以VFPD = 1** [5:0]:VSPW = 帧同步信号的脉冲宽度,单位为⾏* 看LCD⼿册tvp = VSPW + 1 =10;所以VSPW = 9*/lcd_regs->lcdcon2 = (1<<24) | (271<<14) | (1<<6) | (9<<0);/** [25:19]:HBPD = ⾏同步信号发出后,经过多少个VCLK,才发送像素的数据,单位为VCLK* 看LCD⼿册thb = HBPD + 1 = 2;所以HBPD=1** [18:8]:HOZVAL + 1 = 480,所以 HOZVAL = 479;⽔平⽅向尺⼨,多少列**[7:0]:HFPD = ⼀⾏的像素数据传输完成之后,经过多长时间,才能发送下⼀个⾏同步信号,单位为VCLK*看LCD⼿册thf = HFPD + 1 = 2;所以HFPD = 1;*/lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1<<0);/** [7:0]:HSPW = ⾏同步信号的脉冲宽度,单位为VCLK* 看LCD⼿册thp = HSPW + 1 = 41;所以HSPW = 40**/lcd_regs->lcdcon4 = (40<<0);/** [11] :FRM565 = 1;16位模式的格式 R:G:B = 5:6:5* [10] :INVVCLK = 0;VCLK在哪个边沿取数据 = 0表⽰下降沿取数据* [9] :INVVLINE = 1;⾏同步信号是否需要反转= 1需要反转* [8] :INVVFRAME = 1;帧同步信号是否需要反转= 1需要反转* [7] :INVVD = 0; 数据是否需要反转* [6] :INVVDEN = 0; 数据使能信号是否需要反转* [5] :INVPWREN = 0;电源使能信号是否需要反转* [4] :INVLEND = 0;⾏结束信号是否需要反转* [3] :PWREN = 0;电源使能信号,先不使能* [2] :ENLEND = 1;//⾏结束信号先使能* [1:0] :BSWP 、HWSWP = 0 1;字节内部不需要交换,字节间需要交换*/lcd_regs->lcdcon5= (1<<11) | (3<<8) | (1<<2) | (1<<0);/*3.3、显存和调⾊板设置 *//**利⽤dma_alloc_writecombine分配⼀块连续的显存*/s3c_mylcdfb_info->screen_base = dma_alloc_writecombine(NULL,s3c_mylcdfb_info->screen_size,(&(s3c_mylcdfb_info->fix.smem_start)),GFP_KERNEL);//返回虚拟地址if(s3c_mylcdfb_info->screen_base==NULL) //如果显存分配失败,直接返回{printk("undma_alloc_writecombine\n");return1;}/**将显存的地址告诉LCD控制器(物理地址)*/lcd_regs->lcdsaddr1 = (s3c_mylcdfb_info->fix.smem_start >> 1) & (~(3<<30));//起始地址lcd_regs->lcdsaddr2 = ((s3c_mylcdfb_info->fix.smem_start + s3c_mylcdfb_info->screen_size) >> 1) & 0x1fffff;//结束地址lcd_regs->lcdsaddr3 = (480*16/16); /* ⼀⾏的长度(单位: 2字节) *///s3c_lcd->fix.smem_start = xxx; /* 显存的物理地址 *//* 启动LCD */lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器 */lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本⾝电源 */*gpbdat |= 1; /* 输出⾼电平, 使能背光 *//*4、注册LCD*/register_framebuffer(s3c_mylcdfb_info);printk("register_framebuffer\n");return0;}static void lcd_drv_exit(void){unregister_framebuffer(s3c_mylcdfb_info);lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD本⾝ */*gpbdat &= ~1; /* 关闭背光 */dma_free_writecombine(NULL, s3c_mylcdfb_info->fix.smem_len, s3c_mylcdfb_info->screen_base, s3c_mylcdfb_info->fix.smem_start);iounmap(lcd_regs);iounmap(gpbcon);iounmap(gpccon);iounmap(gpdcon);iounmap(gpgcon);framebuffer_release(s3c_mylcdfb_info);}module_init(lcd_drv_init);module_exit(lcd_drv_exit);MODULE_LICENSE("GPL");2.重新编译内核,去掉默认的LCDmake 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 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了从上图可以看到按下enter键,它就启动了⼀个进程号772的-sh进程,如下图发现这个-sh的描述符都指向了tty1:以上内容转载⾃。
LCD控制器 (1)
A[31]A[30]…A[1]A[0]B[31]B[30]…B[1]B[0]C[31]C[30]…C[1]C[0]…
4位或8位单扫描数据显示
0000 0004
0008
┇ …
1000
1004
┇ …
A[31: 0] B[31: 0] C[31: 0]
┇┇ L[3…1: 0] M[31: 0]
┇ …
A[31]A[30]…A[1]A[0]B[31]B[30]…B[1]B[0]C[31]C[30]…C[1]C[0]… L[31]L[30]…L[1]L[0]M[31]M[30]…M[1]M[0]
数据传送速率=水平尺寸×垂直尺寸×帧速率×模式值
• 帧速率的计算公式为
VCLK=MCLK/(VCLKVAL×2) 帧速率 ={[(1/VCLK)×(HOZVAL+1)+(1/MCLK)×(WLH+WDLY+LINEBLANK)]× (LINEVAL+1)}
• VCLK还可以使用下式计算:
VCLK=(HOZVAL+1)/{[1/(帧速率×(LlNEVAL+1))]- [(WLH+WDLY+LINEBLANK) /MCLK]}
液晶类型和扫描模式 单色液晶 4级灰度屏 16级灰度屏 彩色液晶
4位单扫描 1/4 1/4 1/4 3/4
液晶扫描模式值
8位单扫描或4位双扫描 1/8 1/8 1/8 3/8
(3)设置数据帧显示控制 • LCDBASEU:设置显示扫描方式中的开始地址(单扫描方
式)或高位缓存地址(双扫描方式)。 • LCDBASEL:设置双扫描方式的低位缓存开始地址。可以使
1 个像素
4位单扫描
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
LCD驱动分析_LCD控制器设置及代码详解
1. LCD工作的硬件需求:
要使一块LCD正常的显示文字或图像,不仅需要LCD驱动器,而且还需要相应的LCD 控制器。
在通常情况下,生产厂商把LCD驱动器会以COF/COG的形式与LCD玻璃基板制作在一起,而LCD控制器则是由外部的电路来实现,现在很多的MCU内部都集成了LCD控制器,如S3C2410/2440等。
通过LCD控制器就可以产生LCD驱动器所需要的控制信号来控制STN/TFT屏了。
2. S3C2440内部LCD控制器结构图:
我们根据数据手册来描述一下这个集成在S3C2440内部的LCD控制器:
a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组成;
b:REGBANK由17个可编程的寄存器组和一块256*16的调色板内存组成,它们用来配置LCD控制器的;
c:LCDCDMA是一个专用的DMA,它能自动地把在侦内存中的视频数据传送到LCD驱动器,通过使用这个DMA通道,视频数据在不需要CPU的干预的情况下显示在LCD屏上;
d:VIDPRCS接收来自LCDCDMA的数据,将数据转换为合适的数据格式,比如说4/8位单扫,4位双扫显示模式,然后通过数据端口VD[23:0]传送视频数据到LCD驱动器;e:TIMEGEN由可编程的逻辑组成,他生成LCD驱动器需要的控制信号,比如VSYNC、HSYNC、VCLK和LEND等等,而这些控制信号又与REGBANK寄存器组中的LCDCON1/2/3/4/5的配置密切相关,通过不同的配置,TIMEGEN就能产生这些信号的不同形态,从而支持不同的LCD驱动器(即不同的STN/TFT屏)。
3. 常见TFT屏工作时序分析:
LCD提供的外部接口信号:。