基于s3c2440的12864液晶驱动

12864是128*64点阵液晶模块的点阵数简称,业界约定俗成的简称。

一、液晶显示模块概述
12864A-1 汉字图形点阵液晶显示模块,可显示汉字及图形,内置 8192 个中文汉字(16X16 点阵,16*8=128,16*4=64,一行只能写 8 个汉字,4 行;、128 个字符(8X16 点阵)及 64X256 点阵显示 RAM(GDRAM))。
主要技术参数和显示特性:
电源:VDD 3.3V~+5V(内置升压电路,无需负压);
显示内容:128 列× 64 行(128 表示点数)
显示颜色:黄绿
显示角度:6:00 钟直视
LCD 类型:STN
与 MCU 接口:8 位或 4 位并行/3 位串行
配置 LED 背光
多种软件功能:光标显示、画面移位、自定义字符、睡眠模式等

引脚定义:D0-D7,RS,RES,RD,WR,CS,BL;具体头文件定义如下:

 
  
#ifndef LCD12864_H
#define LCD12864_H

#define LCD12864_MAJOR 242
#define DEV_NAME "lcd12864"

#define LCD12864_IOCTL 'l'
#define SET_FONT_SIZE _IOW(LCD12864_IOCTL,0 ,unsigned int)
#define SET_LCD_PAGE _IOW(LCD12864_IOCTL,1 ,unsigned int)
#define SET_LCD_COLUMN _IOW(LCD12864_IOCTL,2 ,unsigned int)
#define CLR_SCREEN _IOW(LCD12864_IOCTL,3 ,unsigned int)
#define BACKLIGHT_CTRL _IOW(LCD12864_IOCTL,4 ,unsigned int)
#define CONTRAST_SETTING _IOW(LCD12864_IOCTL,5 ,unsigned int)

#define LCD_DATA_0 S3C2410_GPC8
#define LCD_DATA_1 S3C2410_GPC9
#define LCD_DATA_2 S3C2410_GPC10
#define LCD_DATA_3 S3C2410_GPC11

#define LCD_DATA_4 S3C2410_GPC12
#define LCD_DATA_5 S3C2410_GPC13
#define LCD_DATA_6 S3C2410_GPC14
#define LCD_DATA_7 S3C2410_GPC15


#define LCD_E_RD S3C2410_GPD0
#define LCD_RW_WR S3C2410_GPD1
#define LCD_RS S3C2410_GPD2
#define LCD_RES S3C2410_GPD3
#define LCD_CS1 S3C2410_GPD4
#define LCD_BL S3C2410_GPD5


#define LCD_DATA_CFG0_OUTP S3C2410_GPC8_OUTP
#define LCD_DATA_CFG1_OUTP S3C2410_GPC9_OUTP
#define LCD_DATA_CFG2_OUTP S3C2410_GPC10_OUTP
#define LCD_DATA_CFG3_OUTP S3C2410_GPC11_OUTP

#define LCD_DATA_CFG4_OUTP S3C2410_GPC12_OUTP
#define LCD_DATA_CFG5_OUTP S3C2410_GPC13_OUTP
#define LCD_DATA_CFG6_OUTP S3C2410_GPC14_OUTP
#define LCD_DATA_CFG7_OUTP S3C2410_GPC15_OUTP


#define LCD_E_RD_CFG_OUTP S3C2410_GPD0_OUTP
#define LCD_RW_WR_CFG_OUTP S3C2410_GPD1_OUTP
#define LCD_RS_CFG_OUTP S3C2410_GPD2_OUTP
#define LCD_RES_CFG_OUTP S3C2410_GPD3_OUTP
#define LCD_CS1_CFG_OUTP S3C2410_GPD4_OUTP
#define LCD_BL_CFG_OUTP S3C2410_GPD5_OUTP


#define FONT_SIZE_8 8
#define FONT_SIZE_16 16


#endif

将引脚单独用头文件重新自己定义一次,以提高代码的可移植性。

下面看看模块初始化/释放函数:

 
  
static int __init lcd12864_init( void )
{
int ret;
dev_t devno;

DBPRINTF(KERN_ALERT
" %s enter!\n " ,__func__);
devno
= MKDEV(lcd12864_major, 0 );
if ((ret = register_chrdev_region(devno, 1 ,DEV_NAME)) < 0 )
{
if ((ret = alloc_chrdev_region( & devno, 0 , 1 , DEV_NAME)) < 0 )
{
DBPRINTF(KERN_ALERT
" chrdev region fail!ret=%d;\n " ,ret);
return ret;
}
else
{
lcd12864_major
= MAJOR(devno);
}
}
cdev_init(
& lcd_cdev, & lcd12864_ops);
lcd_cdev.owner
= THIS_MODULE;
lcd_cdev.ops
= & lcd12864_ops;

ret
= cdev_add( & lcd_cdev, devno, 1 );
if (ret < 0 )
{
goto fail_reg;
}

lcd_dev_class
= class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(lcd_dev_class))
{
goto fail_cdev;
}
device_create(lcd_dev_class, NULL, devno, NULL, DEV_NAME);

lcd_bd_info
= kmalloc( sizeof ( struct lcd_board_info),GFP_KERNEL);
lcd_bd_info
-> font_size = 16 ;

#ifdef NORMAL
lcd_bd_info
-> pag = 0 ;
lcd_bd_info
-> column = 0 ;
#else
lcd_bd_info
-> pag = 6 ;
lcd_bd_info
-> column = 112 ;
#endif
lcd_bd_info
-> lcd_data_t = lcd_data_table;
lcd_bd_info
-> lcd_data_cfg_t = lcd_data_cfg_table;

spin_lock_init(
& lock );

gpio_pin_init();
// gpio_pin_init();


ret
= init_lcd12864();
if (ret < 0 )
{
goto fail_dev;
}
back_light_ctrl(
1 );
clear_lcd_screen(
0x00 );
DBPRINTF(KERN_ALERT
" %s leave,init success!\n " ,__func__);
return ret;

fail_dev:
device_destroy(lcd_dev_class, devno);
class_destroy(lcd_dev_class);

fail_cdev:
cdev_del(
& lcd_cdev);
fail_reg:
unregister_chrdev_region(devno,
1 );
return ret;
}


static void __exit lcd12864_exit( void )
{
kfree(lcd_bd_info);
device_destroy(lcd_dev_class, MKDEV(lcd12864_major,
0 ));
class_destroy(lcd_dev_class);
cdev_del(
& lcd_cdev);
unregister_chrdev_region(MKDEV(lcd12864_major,
0 ), 1 );
}

module_init(lcd12864_init);
module_exit(lcd12864_exit);

MODULE_LICENSE(
" GPL " );
MODULE_AUTHOR(
" HOY " );
MODULE_DESCRIPTION(
" gonsin 128x64 lcd driver " );

以上主要工作是:通过register_chrdev_region静态分配设备号devno,如果分配失败,则通过系统alloc_chrdev_region动态分配;

此处通过cdev创建字符设备;通过cdev_add添加入系统中。通过class_create和device_create生成并注册一个逻辑设备,通过此工作,可以在/dev/下面看到设备名;

初始化lcd_board_info结构,lcd_board_info结构定义如下:

 
  
struct lcd_board_info{

unsigned
int font_size;
unsigned
int pag;
unsigned
int column;
unsigned
long * lcd_data_t;
unsigned
long * lcd_data_cfg_t;
};

static struct lcd_board_info * lcd_bd_info;

其他变量和头文件定义如下:

 
  
#include < linux / fs.h >
#include
< linux / module.h >
#include
< linux / init.h >
#include
< mach / regs - gpio.h >
#include
< linux / ioctl.h >
#include
< linux / cdev.h >
#include
< linux / slab.h >
#include
< linux / uaccess.h >
#include
< linux / device.h >
#include
< asm / io.h >
#include
< linux / delay.h >

#define DEBUG // 调试状态
#ifdef DEBUG
#define DBPRINTF(fmt,args...) printk(KERN_ALERT "lcd:" fmt, ##args)
#else
#define DBPRINTF(fmt,args...)
#endif

#include
" lcd_12864.h "

#define NORMAL // 正常模式


static struct class * lcd_dev_class;

struct cdev lcd_cdev;

static int lcd12864_major = LCD12864_MAJOR;

static unsigned long lcd_data_table[] = {
LCD_DATA_0,
LCD_DATA_1,
LCD_DATA_2,
LCD_DATA_3,

LCD_DATA_4,
LCD_DATA_5,
LCD_DATA_6,
LCD_DATA_7,
};

/**/
static unsigned long lcd_data_cfg_table[] =
{
LCD_DATA_CFG0_OUTP,
LCD_DATA_CFG1_OUTP,
LCD_DATA_CFG2_OUTP,
LCD_DATA_CFG3_OUTP,

LCD_DATA_CFG4_OUTP,
LCD_DATA_CFG5_OUTP,
LCD_DATA_CFG6_OUTP,
LCD_DATA_CFG7_OUTP,
};

/*
static unsigned char io_read_byte()
{

}
*/
spinlock_t
lock ;

然后调用以下函数进行gpio引脚的初始化、液晶屏的初始化:

 
  
static int gpio_pin_init( void )
{

int ii;
s3c2410_gpio_cfgpin(LCD_CS1, LCD_CS1_CFG_OUTP);
s3c2410_gpio_cfgpin(LCD_RES, LCD_RES_CFG_OUTP);
s3c2410_gpio_cfgpin(LCD_RS, LCD_RS_CFG_OUTP);
s3c2410_gpio_cfgpin(LCD_RW_WR, LCD_RW_WR_CFG_OUTP);
s3c2410_gpio_cfgpin(LCD_E_RD, LCD_E_RD_CFG_OUTP);
s3c2410_gpio_cfgpin(LCD_BL, LCD_BL_CFG_OUTP);

for (ii = 0 ; ii < 8 ; ii ++ )
{
s3c2410_gpio_cfgpin(lcd_bd_info
-> lcd_data_t[ii], lcd_bd_info -> lcd_data_cfg_t[ii]);
}
return 0 ;
}


static unsigned char init_cmd[] = {
0xe2 ,
0xa0 ,
0xc0 ,
0xA2 ,
0x2B ,
0x2E ,
0x2F ,
0x27 ,
0xAF ,
0 ,
};


static int init_lcd12864( void )
{

int ii;
s3c2410_gpio_setpin(LCD_CS1,
0 );
udelay(
500 );
s3c2410_gpio_setpin(LCD_RES,
0 );
udelay(
500 );
s3c2410_gpio_setpin(LCD_RES,
1 );
udelay(
500 );

for (ii = 0 ;init_cmd[ii] != 0 ;ii ++ )
{
send_cmd(init_cmd[ii]);
udelay(
50 );
}
return 0 ;
}

以下为写液晶屏和写命令的操作:

 
  
#ifdef NORMAL
/* 往液晶中写入1个字符数据 */
static void io_write_byte(unsigned char data)
{
unsigned
char ii;
spin_lock(
& lock );

for (ii = 0 ;ii < 8 ;ii ++ )
{
if (data & 0b1)
{
s3c2410_gpio_setpin(lcd_bd_info
-> lcd_data_t[ii], 1 );
}
else
{
s3c2410_gpio_setpin(lcd_bd_info
-> lcd_data_t[ii], 0 );
}
data
= data >> 1 ;
}
// __raw_writel(((__raw_readl(S3C2410_GPCDAT)&~(0xff<<8))|(data<<8)),S3C2410_GPCDAT);

spin_unlock(
& lock );

s3c2410_gpio_setpin(LCD_RS,
1 );
s3c2410_gpio_setpin(LCD_RW_WR,
0 );
s3c2410_gpio_setpin(LCD_E_RD,
1 );
s3c2410_gpio_setpin(LCD_CS1,
1 );
udelay(
1 );
s3c2410_gpio_setpin(LCD_CS1,
0 );
udelay(
1 );

}
#else
static void io_write_byte(unsigned char data)
{
unsigned
char ii;
spin_lock(
& lock );
for (ii = 0 ;ii < 8 ;ii ++ )
{
if (data & 0x80 )
{
// DBPRINTF(KERN_ALERT "[||||||||||||||||||||]\n");
s3c2410_gpio_setpin(lcd_bd_info -> lcd_data_t[ii], 1 );
}
else
{
// DBPRINTF(KERN_ALERT "[--------------------]\n");
s3c2410_gpio_setpin(lcd_bd_info -> lcd_data_t[ii], 0 );
}
data
= data << 1 ;
}
spin_unlock(
& lock );
// DBPRINTF(KERN_ALERT "\n");

s3c2410_gpio_setpin(LCD_RS,
1 );
s3c2410_gpio_setpin(LCD_RW_WR,
0 );
s3c2410_gpio_setpin(LCD_E_RD,
1 );

s3c2410_gpio_setpin(LCD_CS1,
1 );
udelay(
1 );
s3c2410_gpio_setpin(LCD_CS1,
0 );
udelay(
1 );
}
#endif

// 发送命令
static int send_cmd(unsigned char cmd)
{
int ii;
unsigned
char tmp;
tmp
= 0x00 ;
spin_lock(
& lock );

for (ii = 0 ;ii < 8 ;ii ++ )
{
if (cmd & 0x80 )
{
s3c2410_gpio_setpin(lcd_bd_info
-> lcd_data_t[ii], 1 );
}
else
{
s3c2410_gpio_setpin(lcd_bd_info
-> lcd_data_t[ii], 0 );
}
cmd
= cmd << 1 ;
}
/* ***************** */
/*
for(ii=0;ii<8;ii++)
{
tmp = tmp<<1;
if(cmd&0b1)
{
tmp|=0b1;
}
else
{
tmp &= 0b0;
}
cmd = cmd>>1;

}
__raw_writel(((__raw_readl(S3C2410_GPCDAT)&~(0xff<<8))|(tmp<<8)),S3C2410_GPCDAT);
*/
/* ***************** */

spin_unlock(
& lock );


s3c2410_gpio_setpin(LCD_RS,
0 );
s3c2410_gpio_setpin(LCD_RW_WR,
0 );
s3c2410_gpio_setpin(LCD_E_RD,
1 );
s3c2410_gpio_setpin(LCD_CS1,
1 );
udelay(
1 );

s3c2410_gpio_setpin(LCD_CS1,
0 );
udelay(
1 );
return 0 ;
}



static int dev_write_data( const char * data)
{
unsigned
char dl,dh;
int i,j;
spin_lock(
& lock );
dh
= lcd_bd_info -> column / 16 ;
dl
= lcd_bd_info -> column - dh * 16 ;
for (i = 0 ; i < 2 ; i ++ )
{
send_cmd(
0xB7 - i - lcd_bd_info -> pag);
send_cmd(
0x10 + dh);
send_cmd(dl);

#ifdef NORMAL
for (j = 0 ;j < 16 ;j ++ )
io_write_byte(data[lcd_bd_info
-> font_size * i + j]);
#else
for (j = 0 ;j < 16 ;j ++ )
io_write_byte(data[lcd_bd_info
-> font_size * ( 1 - i) + ( 15 - j)]);
#endif
}
spin_unlock(
& lock );
return 0 ;
}

因为公司结构方面的需要,要将液晶倒转显示,所以此处做了两个不同的方案,如果定义了NORMAL,则正常显示,否则倒转显示;

其他对液晶屏控制的操作如下:

 
  
/* 清除屏幕 */
static void clear_lcd_screen(unsigned char clr_data)
{
unsigned
char page_addr = 0xb7 ;
unsigned
int page_num;
int ii;
for (page_num = 0 ; page_num < 8 ; page_num ++ )
{
send_cmd(page_addr);
send_cmd(
0x10 );
send_cmd(
0x00 );
for (ii = 0 ; ii < 128 ; ii ++ )
io_write_byte(clr_data);
page_addr
-- ;
}
}

static int lcd12864_open( struct inode * inode, struct file * filp)
{
return 0 ;
}

static int lcd12864_release( struct inode * inode, struct file * filp)
{
return 0 ;
}

static ssize_t lcd12864_write( struct file * filp, char __user * buf, size_t size, loff_t offset)
{
char * kbuf;
// int count;

kbuf
= kmalloc(size, GFP_KERNEL);
copy_from_user(kbuf, buf, size);
dev_write_data(kbuf);

kfree(kbuf);
return 0 ;
}

static int contrast_setting( int val)
{
if ((val & 0xff ) > 0x3e )
return - 1 ;
send_cmd(
0x81 );
send_cmd(val
& 0xff );
DBPRINTF(KERN_ALERT
" contrast setting,val is%d\n " ,val);
return 0 ;
}
/* 背光控制 */
static int back_light_ctrl( int bl)
{
if (bl < 0 )
{
return - 1 ;
}
else if (bl == 0 )
{
s3c2410_gpio_setpin(LCD_BL,
0 );
DBPRINTF(KERN_ALERT
" back light set value is 0\n " );
}
else
{
s3c2410_gpio_setpin(LCD_BL,
1 );
DBPRINTF(KERN_ALERT
" back light set value is 1\n " );
}
return 0 ;
}

static int lcd12864_ioctl( struct inode * inode, struct file * filp, unsigned int cmd, unsigned long args)
{
int ret;
spin_lock(
& lock );
ret
= 0 ;
DBPRINTF(KERN_ALERT
" %s " ,__func__);
switch (cmd)
{
case SET_FONT_SIZE:
{
int size = * ( int * )args;
if (size == FONT_SIZE_8 || size == FONT_SIZE_16)
{
lcd_bd_info
-> font_size = size;
}
else
{
ret
= - 1 ;
DBPRINTF(KERN_ALERT
" set font size fail!\n " );
goto ioctl_fail;
}
break ;
}
case SET_LCD_PAGE:
{
int tmp = * ( int * )args;
if (tmp > 63 )
return - 1 ;


#ifdef NORMAL
lcd_bd_info
-> pag = tmp;
#else
lcd_bd_info
-> pag = 6 - tmp;
#endif
DBPRINTF(KERN_ALERT
" set pag is %d\n " ,lcd_bd_info -> pag);
break ;
}
case SET_LCD_COLUMN:
{
int tmp = * ( int * )args;
if (tmp > 127 )
return - 1 ;
#ifdef NORMAL
lcd_bd_info
-> column = tmp;
#else
lcd_bd_info
-> column = 112 - tmp;
#endif
DBPRINTF(KERN_ALERT
" set column is %d\n " ,lcd_bd_info -> column);
break ;
}
case CLR_SCREEN:
{
unsigned
char data = * ( char * )args;
clear_lcd_screen(data);
break ;
}
case BACKLIGHT_CTRL:
{
int bl = * ( int * )args;
ret
= back_light_ctrl(bl);
break ;
}
case CONTRAST_SETTING:
{
int con = * ( int * )args;
ret
= contrast_setting(con);
break ;
}

}
spin_unlock(
& lock );
return ret;
ioctl_fail:
spin_unlock(
& lock );
return ret;
}

最后看看file_operations结构体:

 
  
static struct file_operations lcd12864_ops = {
.owner
= THIS_MODULE,
.open
= lcd12864_open,
.release
= lcd12864_release,
.write
= lcd12864_write,
// .read = lcd12864_read,
.ioctl = lcd12864_ioctl,
};

以上则是液晶屏的驱动程序,很简单吧?哈哈~~

Makefile如下所示:

 
  
obj - m: = lcd_12864.o
KDIR
=/ home / project / linux - res / linux - 2.6 . 30.4 /

all:
$(MAKE)
- C $(KDIR) M = $(PWD)

.PHONY:clean
clean:
rm
- f * .o * .ko * .mod.c * .mod.o * .tmp_versions

最后是液晶屏测试程序:

 
  
enum {
FONT_SIZE8,
FONT_SIZE16,
FONT_SIZE32,
};


/* 将字符数组里面的每一位取反 */
void oppersite( const char * cin, __out char * cout, int len)
{
int i = 0 ;

for (i = 0 ;i < len;i ++ )
{
cout[i]
=~ cin[i];
}
}

/* 由size_flag 解析出字体大小 */
int prase_size_flag( int size_flag)
{
int fsize;
switch (size_flag)
{
case FONT_SIZE8:
fsize
= 8 ;
break ;
case FONT_SIZE16:
fsize
= 16 ;
break ;
case FONT_SIZE32:
fsize
= 32 ;
break ;
default :
printf(
" size_flag is involid!\n " );
return - 1 ;
}
return fsize;
}

int prase_row( int pag)
{
return pag * 2 ;
}

int prase_column( int col)
{
return col * 16 ;
}

/* 显示一个选中的字符
ch:字符的16进制数组
pag:第几页;0-7
col:第几列;0-127
size_flag:字体枚举
*/
int show_select_ch( int fd, const char * ch, int pag, int col, int size_flag)
{
char chtmp[ 50 ];
int fsize;

if ( ! ch)
return - 1 ;

if ((fsize = prase_size_flag(size_flag)) < 0 )
return - 1 ;

int size = fsize * fsize / 8 ;
oppersite(ch, chtmp, size);
if (ioctl(fd, SET_LCD_PAGE, & pag) < 0 )
return - 1 ;
if (ioctl(fd, SET_LCD_COLUMN, & col) < 0 )
return - 1 ;
if (write(fd, chtmp, size) < 0 )
return - 1 ;
return 0 ;
}
/* 显示一个字符
ch:字符的16进制数组
pag:第几页;0-7
col:第几列;0-127
size_flag:字体枚举
*/
int show_ch( int fd, const char * ch, int pag, int col, int size_flag)
{
if ( ! ch)
return - 1 ;

int fsize;
if ((fsize = prase_size_flag(size_flag)) < 0 )
return - 1 ;

int size = fsize * fsize / 8 ;
if (ioctl(fd, SET_LCD_PAGE, & pag) < 0 )
return - 1 ;
if (ioctl(fd, SET_LCD_COLUMN, & col) < 0 )
return - 1 ;
if (write(fd, ch, size) < 0 )
return - 1 ;
return 0 ;
}

/* 显示一行字符串 */
int show_str( int fd, const char ** str, int count, int pag, int col, int size_flag)
{
int fsize;
int i;
if ((fsize = prase_size_flag(size_flag)) < 0 )
return - 1 ;

for (i = 0 ; i < count; i ++ )
{
if (pag < PAGE_COUNT && col < COLUMN_COUNT)
{
if (show_ch(fd, str[i], pag, col, size_flag) < 0 )
return - 1 ;
col
+= fsize;
}
}
return 0 ;
}

/* 显示一行选中的字符串 */
int show_select_str( int fd, const char ** str, int count, int pag, int col, int size_flag)
{
int fsize;
int i;
if ((fsize = prase_size_flag(size_flag)) < 0 )
return - 1 ;

for (i = 0 ; i < count; i ++ )
{
if (pag < PAGE_COUNT && col < COLUMN_COUNT)
{
if (show_select_ch(fd, str[i], pag, col, size_flag) < 0 )
return - 1 ;
col
+= fsize;
}
}
return 0 ;
}

/* 对比度调节0-62 */
int contrast_ctrl( int fd, int val)
{
if (ioctl(fd,CONTRAST_SETTING, & val) < 0 )
return - 1 ;
return 0 ;
}

/* 背光调节 1:亮,0:灭 */
int backlight_ctrl( int fd, int val)
{
if (ioctl(fd, BACKLIGHT_CTRL, & val) < 0 )
return - 1 ;
return 0 ;
}

/* 清屏 */
int clear_screen( int fd, int val)
{
if (ioctl(fd, CLR_SCREEN, & val) < 0 )
return - 1 ;
return 0 ;
}


/* 字体大小 */
int font_size( int fd, int val)
{
if (ioctl(fd, SET_FONT_SIZE, & val) < 0 )
return - 1 ;
return 0 ;
}
 
  
int main( void )
{
fd
= open(LCD_DEV_NAME,O_RDWR);
show_ch(fd, zi,
0 , 0 , 1 );
show_select_ch(fd, zi,
0 , 32 , 1 );
show_str(fd, tmp,
8 , 2 , 0 , 1 );
show_select_str(fd, tmp,
8 , 4 , 0 , 1 );
close(fd);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值