内容介绍:之前有一个项目想要使用Linux系统但是为了省电及节约成本就使用一个黑白液晶屏,现在把驱动调试设计过程记录一下.
Linux内核: linux-2.6.35.3
Board: ZLG iMX287开发板
液晶屏: ZLE12864A-FFSSWE-NAA是一个ZigBee开发板的液晶模块.
首先最主要的工作就是液晶驱动的设计,对于这种液晶,没办法使用iMX287芯片的LCD Interface,所以只能使用GPIO口来模拟时序了.如果写一个通用的字符设备驱动也可以,但是这毕竟不是很好的办法.所以还是要使用FrameBuffer的驱动.
首先找到我们的参考驱动drivers/auxdisplay
其说明文档在Documentation/auxdisplay/中
首先这个驱动是给x86电脑使用的,通过并口来连接.这里用我们的ARM操作就需要使用GPIO来操作.
修改之后的接口连接(使用串行接口模式)
CS -> P2.12
SCL -> P2.14
SI -> P2.15
RST -> P2.4
A0 -> P2.13
下面写出12864操作函数接口模块,这里实现了接口,模块很简单,没什么好说的.
点击(此处)折叠或打开
/*
* 2015-01-21 20:53:59
* LCD接口函数实现
*
*
**/
#include /* module */
#include /* file operation */
#include /* get_user() */
#include /* ioctl */
#include "mach/regs-pinctrl.h"/*GPIO REG Address*/
#include "lcd12864.h"
typedef unsigned char uchar;
typedef unsigned char uint8;
typedef unsigned int uint16;
void __iomem *pinctrl_base = NULL;
#define L_CS(a) do{ writel(1<<12, pinctrl_base + HW_PINCTRL_DOUT2_CLR-(a<<2)); }while(0)
//CS P2.12
#define L_LD(a) do{ writel(1<<13, pinctrl_base + HW_PINCTRL_DOUT2_CLR-(a<<2)); }while(0)
//A0=H data A0=L command P2.13
#define L_CK(a) do{ writel(1<<14, pinctrl_base + HW_PINCTRL_DOUT2_CLR-(a<<2)); }while(0)
//CLK P2.14
#define L_DA(a) do{ writel(1<<15, pinctrl_base + HW_PINCTRL_DOUT2_CLR-(a<<2)); }while(0)
//SI P2.15
#define L_BK(a) do{;}while(0) //backlight
#define L_RST(a) do{ writel(1<<4, pinctrl_base + HW_PINCTRL_DOUT2_CLR-(a<<2)); }while(0)
//RST P2.4
void lcd12864_writecmd(unsigned char Command)
{
unsigned char j, bit7;
L_CK(1); // = 1;
L_LD(0); // = 0;
L_CS(0); // = 0;
for (j = 0; j < 8; j++)
{
bit7 = Command & 0x80;
if (bit7 == 0)
{
L_DA(0); // = 0;
}
else
{
L_DA(1); // = 1;
}
L_CK(0); // = 0;
// delay_us(10);
L_CK(1); // = 1;
Command = Command << 1;
}
L_CS(1); // = 1;
}
void lcd12864_writedata(unsigned char DDate)
{
unsigned char j, bit7;
L_CK(1); // = 1;
L_LD(1); // = 1;
L_CS(0); // = 0;
for (j = 0; j < 8; j++)
{
bit7 = DDate & 0x80;
if (bit7 == 0)
{
L_DA(0); // = 0;
}
else
{
L_DA(1); // = 1;
}
L_CK(0); // = 0;
// delay_us(10);
L_CK(1); // = 1;
DDate = DDate << 1;
}
L_CS(1); // = 1;
}
void initLCDM(void)
{
uchar ContrastLevel;//定义对比度
ContrastLevel = 0xa0;//对比度,根据不同的 LCD 调节,否则无法显示。
lcd12864_writecmd(0xaf);//开显示
lcd12864_writecmd(0x40);//显示起始行为 0
lcd12864_writecmd(0xa0);//RAM 列地址与列驱动同顺序
lcd12864_writecmd(0xa6);//正向显示
lcd12864_writecmd(0xa4);//显示全亮功能关闭
lcd12864_writecmd(0xa2);//LCD 偏压比 1/9
lcd12864_writecmd(0xc8);//行驱动方向为反向
lcd12864_writecmd(0x2f);//启用内部 LCD 驱动电源
lcd12864_writecmd(0xf8);//升压电路设置指令代码
lcd12864_writecmd(0x00);//倍压设置为 4X
lcd12864_writecmd(ContrastLevel); //设置对比度
lcd12864_writecmd(0xaf);//开显示
}
void ResetLCD(void)
{
L_RST(1);
L_RST(0);
// delay_ms(10);
L_RST(1);
}
void ClearRAM(void)
{
uint8 i,j;
for (i = 0; i < 8; i++)
{
lcd12864_writecmd(i|0xb0);
lcd12864_writecmd(0x10);
lcd12864_writecmd(0x00);
for (j = 0; j < 132; j++)
{
lcd12864_writedata(0xAA);
}
}
}
static unsigned int is_lcd12864_inited = 0;
unsigned int lcd12864_isinited(void){
return is_lcd12864_inited;
}
EXPORT_SYMBOL_GPL(lcd12864_writedata);
EXPORT_SYMBOL_GPL(lcd12864_writecmd);
EXPORT_SYMBOL_GPL(lcd12864_isinited);
/*********************************************************************************************************
Module Functions
*********************************************************************************************************/
static int __init lcdModule_init (void)
{
int iRet=0;
printk("\nlcd12864 module init!\n");
//printk("you should see a few line on LCDi\n");
pinctrl_base = ioremap(0x80018000, 0x1B80);
writel(0xFF<<24 | 0x300, pinctrl_base+HW_PINCTRL_MUXSEL4_SET);
//set io port mode for 2.12,2.13,2.14,2.15 to GPIO
writel(0xF<<12|0x1<<4, pinctrl_base+HW_PINCTRL_DOE2_SET); //set to output mode
ResetLCD();
initLCDM();
ClearRAM(); //请液晶缓存
//lcd12864_writecmd(0xA7);
is_lcd12864_inited = 1;
return iRet;
}
static void __exit lcdModule_exit (void) /* warning:return void */
{
printk("\nlcd module exit!\n");
iounmap(pinctrl_base);
is_lcd12864_inited = 0;
}
/*********************************************************************************************************
Driver Definitions
*********************************************************************************************************/
module_init(lcdModule_init);
module_exit(lcdModule_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("schspa@gmail.com");
MODULE_DESCRIPTION("iMX287 lcd12864 driver By Schspa");
下面是重要的内容,我们的FrameBuffer接口
注意我们的在mmap函数中的vma->vm_pgoff之前老是映射的不对,加上之后就对了
点击(此处)折叠或打开
/*
* Filename: cfag12864bfb.c
* Version: 0.1.0
* Description: cfag12864b LCD framebuffer driver
* License: GPLv2
* Depends: cfag12864b
*
* Author: Copyright (C) Miguel Ojeda Sandonis
* Date: 2006-10-31
*
* Modified: schspa
* Data: 2015-01-21
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void cfag12864b_sync(void);
extern unsigned char *cfag12864b_buffer_nocache;
#define CFAG12864BFB_NAME "cfag12864bfb"
static struct fb_fix_screeninfo cfag12864bfb_fix __devinitdata = {
.id = "cfag12864b",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_MONO10,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.line_length = CFAG12864B_WIDTH / 8,
.accel = FB_ACCEL_NONE,
};
static struct fb_var_screeninfo cfag12864bfb_var __devinitdata = {
.xres = CFAG12864B_WIDTH,
.yres = CFAG12864B_HEIGHT,
.xres_virtual = CFAG12864B_WIDTH,
.yres_virtual = CFAG12864B_HEIGHT,
.bits_per_pixel = 1,
.red = { 0, 1, 0 },
.green = { 0, 1, 0 },
.blue = { 0, 1, 0 },
.left_margin = 0,
.right_margin = 0,
.upper_margin = 0,
.lower_margin = 0,
.vmode = FB_VMODE_NONINTERLACED,
};
static int cfag12864bfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
printk("cfag12864bfb_mmap\r\n");
unsigned long page;
unsigned char i;
unsigned long start = (unsigned long)vma->vm_start;
//unsigned long end = (unsigned long)vma->vm_end;
unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
//if(size > CFAG12864B_SIZE){
printk("size %d \n", size);
// return -ENOMEM;
//}
vma->vm_flags |= VM_IO | VM_SHARED;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
//得到物理地址 注意这里边的vma->vm_pgoff 负责可能出现不正常的情况.
page = virt_to_phys(cfag12864b_buffer+vma->vm_pgoff);
//将用户空间的一个vma虚拟内存区映射到以page开始的一段连续物理页面上
if(remap_pfn_range(vma,start,page>>PAGE_SHIFT,size,PAGE_SHARED)){//第三个参数是页帧号,由物理地址右移PAGE_SHIFT得到
printk("remap_pfn_range failed\r\n");
return -1;
}
return 0;
// return vm_insert_page(vma, vma->vm_start,
// virt_to_page(cfag12864b_buffer));
}
static int cfag12864bfb_sync(struct fb_info *info)
{
printk("fb_sync\r\n");
cfag12864b_sync();
return 0;//vm_insert_page(vma, vma->vm_start,
//virt_to_page(cfag12864b_buffer));
}
static int cfag12864bfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg){
int i = 0;
printk("fb ioctl %d\r\n", cmd);
cfag12864b_sync();
return 0;
}
static struct fb_ops cfag12864bfb_ops = {
.owner = THIS_MODULE,
// .fb_read = fb_sys_read,
// .fb_write = fb_sys_write,
// .fb_fillrect = sys_fillrect,
// .fb_copyarea = sys_copyarea,
// .fb_imageblit = sys_imageblit,
.fb_ioctl = cfag12864bfb_ioctl,
.fb_mmap = cfag12864bfb_mmap,
.fb_sync = cfag12864bfb_sync,
};
static int __devinit cfag12864bfb_probe(struct platform_device *device)
{
int i = 0;
int ret = -EINVAL;
struct fb_info *info = framebuffer_alloc(0, &device->dev);
if (!info)
goto none;
info->screen_base = (char __iomem *) cfag12864b_buffer;
info->screen_size = CFAG12864B_SIZE;
info->fbops = &cfag12864bfb_ops;
info->fix = cfag12864bfb_fix;
info->var = cfag12864bfb_var;
info->pseudo_palette = NULL;
info->par = NULL;
info->flags = FBINFO_FLAG_DEFAULT;
if (register_framebuffer(info) < 0)
goto fballoced;
platform_set_drvdata(device, info);
printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
info->fix.id);
for(i = 0; i<1024; i++){
info->screen_base[i] = 0x0F;
}
return 0;
fballoced:
framebuffer_release(info);
none:
return ret;
}
static int __devexit cfag12864bfb_remove(struct platform_device *device)
{
struct fb_info *info = platform_get_drvdata(device);
if (info) {
unregister_framebuffer(info);
framebuffer_release(info);
}
return 0;
}
static struct platform_driver cfag12864bfb_driver = {
.probe = cfag12864bfb_probe,
.remove = __devexit_p(cfag12864bfb_remove),
.driver = {
.name = CFAG12864BFB_NAME,
},
};
static struct platform_device *cfag12864bfb_device;
static int __init cfag12864bfb_init(void)
{
int ret = -EINVAL;
//printk("PAGE_SIZE:%d\r\n",PAGE_SIZE);
/* cfag12864b_init() must be called first */
if (!cfag12864b_isinited()) {
printk(KERN_ERR CFAG12864BFB_NAME ": ERROR: "
"cfag12864b is not initialized\n");
goto none;
}
if (cfag12864b_enable()) {
printk(KERN_ERR CFAG12864BFB_NAME ": ERROR: "
"can't enable cfag12864b refreshing (being used)\n");
return -ENODEV;
}
ret = platform_driver_register(&cfag12864bfb_driver);
if (!ret) {
cfag12864bfb_device =
platform_device_alloc(CFAG12864BFB_NAME, 0);
if (cfag12864bfb_device)
ret = platform_device_add(cfag12864bfb_device);
else
ret = -ENOMEM;
if (ret) {
platform_device_put(cfag12864bfb_device);
platform_driver_unregister(&cfag12864bfb_driver);
}
}
none:
return ret;
}
static void __exit cfag12864bfb_exit(void)
{
platform_device_unregister(cfag12864bfb_device);
platform_driver_unregister(&cfag12864bfb_driver);
cfag12864b_disable();
}
module_init(cfag12864bfb_init);
module_exit(cfag12864bfb_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Miguel Ojeda Sandonis ");
MODULE_DESCRIPTION("cfag12864b LCD framebuffer driver");