基于S3C2440的LCD1602驅(qū)動(dòng)的設(shè)計(jì)
開發(fā)目標(biāo)平臺(tái):linux-2.6.28+S3C2440+CRAMFS/YAFFS2
開發(fā)環(huán)境:UBUNTU-9.04+arm-linux-gcc-3.4.1
這次需要在s3c2440上開發(fā)一個(gè)簡(jiǎn)單的字符型LCD驅(qū)動(dòng),具體情況如下:
LCD1602共有16根管腳,控制信號(hào)3根RS,RW,RE,數(shù)據(jù)線8根DB0~DB7,可以支持8位數(shù)據(jù)格式和4位數(shù)據(jù)格式。
RS:寄存器選擇1:數(shù)據(jù);0:指令【由GPIO控制】
R/W:讀、寫操作選擇1:讀;0:寫【我的硬件里直接置低電平】
RE:使能信號(hào)【由GPIO控制】
數(shù)據(jù)線DB4~DB7,分別連接4根GPIO口,DB0~DB3置高。
首先內(nèi)核注冊(cè)驅(qū)動(dòng)函數(shù):
static int __init mylcd_init(void)
{
int i;
int ret = 0;
for(i = 0;i < MAX_NUM;i++){
s3c2410_gpio_cfgpin(plcd[i].pin,plcd[i].pin_out);
}
//初始化GPIO為輸出
ret =register_chrdev(LCD_MAJOR,DEVICE_NAME,&mylcd_fops);
if(ret < 0){
printk(KERN_WARNING "Cannot register driver of mylcdn");
return ret;
}
printk(DEVICE_NAME " Init Is Okn");
return 0;
}
驅(qū)動(dòng)注銷函數(shù)
static void __exit mylcd_exit(void)
{
unregister_chrdev(LCD_MAJOR,DEVICE_NAME);
printk(KERN_INFO " remove driver of myLCDn");
}
module_init(mylcd_init);
module_exit(mylcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qlx-1983@163.com");
MODULE_DESCRIPTION("mylcd driver for s3c2440");
驅(qū)動(dòng)文件操作結(jié)構(gòu)體,我對(duì)外提供IOCTL接口:
static struct file_operations mylcd_fops = {
.owner = THIS_MODULE,
.ioctl = mylcd_ioctl,
.open = mylcd_open,
.release = mylcd_release
};
驅(qū)動(dòng)的open和release函數(shù)的實(shí)現(xiàn):
static int mylcd_open(struct inode *inode,struct file *file)
{
return 0;
}
static int mylcd_release(struct inode *inode,struct file *file)
{
return 0;
}
ioctl函數(shù)的實(shí)現(xiàn):
static int mylcd_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned int arg)
{
if(_IOC_TYPE(cmd) != GPIO_IOF_MAGIC)
{
return -ENOTTY;
}
if(_IOC_NR(cmd) >= GPIO_MAXNR)
{
return -ENOTTY;
}
switch(cmd)
{
case LCD_INIT: //初始化
myLcd_port_init();
break;
case LCD_SCREEN_CLEAR: //清屏
myLcd_Screen_Clear();
break;
case LCD_CURSOR_RETURN: //歸位
myLcd_Cursor_Home();
break;
case LCD_DDRAM_AD_SET: //設(shè)置DDRAM地址
myLcd_ddram_ad_set(arg);
break;
case LCD_DATA_WRITE: //寫數(shù)據(jù)
myLcd_data_write(arg);
break;
default:
break;
}
return 0;
}
因?yàn)槲覍?duì)LCD的功能要求比較簡(jiǎn)單,所以,只是提供了上面這些功能,當(dāng)然,LCD1602還有很多接口可以對(duì)外提供,如果你感興趣,可以補(bǔ)充一下!
另外,附上針對(duì)這個(gè)LCD我寫的上層接口函數(shù):
int OpenLcd(const char *lcd_name)
{
int Lcd_fd;
if(access(lcd_name,F_OK) == 0){
printf("%s Can Be opened!n",lcd_name);
}else{
system("mknod /dev/myLcd c 204 0");
}
Lcd_fd = open(lcd_name,O_RDWR);
if(Lcd_fd < 0){
printf("Open %s Is Failed!n");
return 0;
}
printf("Open The Screen!n");
return Lcd_fd;
}
void CloseLcd(int Lcd_fd)
{
printf("Close The Screen!n");
close(Lcd_fd);
}
void InitLcd(int Lcd_fd)
{
printf("Init The Screen!n");
ioctl(Lcd_fd,LCD_INIT,0);
}
void ClearLcd(int Lcd_fd)
{
printf("Clear The Screen!n");
ioctl(Lcd_fd,LCD_SCREEN_CLEAR,0);
ioctl(Lcd_fd,LCD_CURSOR_RETURN,0);
}
void PrintfLcd(int Lcd_fd,const char *src,unsigned int pos)
{
int src_len = 0;
int i;
src_len = strlen(src);
if((pos >= 0)&&(pos <= 32)){
if(src_len <= (32-pos)){
ioctl(Lcd_fd,LCD_DDRAM_AD_SET,pos);
for(i = 0;i < src_len ;i++){
ioctl(Lcd_fd,LCD_DATA_WRITE,src[i]);
}
}else{
printf("The Lcd Is Not Enough Lengthn");
}
}else{
printf("The Pos Is Out Size Of Addressn");
}
}
以下是測(cè)試應(yīng)用程序:
int main(int argc, char *argv[])
{
int Lcd_fd;
Lcd_fd = OpenLcd(DEV_NAME);
InitLcd(Lcd_fd);
PrintfLcd(Lcd_fd,"AAAABBB",0);
PrintfLcd(Lcd_fd,"FFFFFFFFFFF",16);
sleep(10);
ClearLcd(Lcd_fd);
PrintfLcd(Lcd_fd,"AAAABBB",0);
PrintfLcd(Lcd_fd,"DDDDDDDD",16);
CloseLcd(Lcd_fd);
}