Chinaunix首页 | 论坛 | 博客
  • 博客访问: 53169
  • 博文数量: 11
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 27
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-28 12:29
文章分类

全部博文(11)

文章存档

2016年(2)

2015年(5)

2014年(3)

2013年(1)

我的朋友

分类: LINUX

2016-10-12 22:45:53

原文地址:mini2440(linux)驱动1602 作者:mclovein

1602.c----------------------------------------------
/*
* 由于没有能找到mini2440的连续的8个GPIO口,不得不用gpf和gpg拼凑一个8bit数据总线
* 这也使得程序罗嗦了。对1602的三条控制总线的控制,使用的是2440的3.2v的gpio电压,虽然
* 1602要求最小的控制电压是0.7x5v=3.5v,但是3.2v的电压也可以正常的控制,不放心就只好
* 在加一篇74lvx3245了(在淘宝上4元一片)
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include // ndelay udelay mdelay(unsigned long)
#include "1602.h"
#define DEVICE_NAME "LCD1602"
#define LCD1602_MAJOR 237
static void *gpfcon;
static void *gpgcon;
#define GPFCON           (0x56000050)
#define GPGCON           (0x56000060)
#define LCD1602_MIN_IO  0x08003000+100 //保证写出去的地址是nGCS1范围之内的
void delay_us(unsigned long us) // 延时微秒

 udelay(us);
void delay_ms(unsigned long ms) // 延时毫秒
{    
    mdelay(ms);
}
// 产生一个使能脉冲 
void lcd_e_toggle(void) 

 lcd1602_en_out(HIGH);
 delay_us(20); 
 lcd1602_en_out(LOW); 
}
// 循环检测LCD忙标志(BF),直到其值为0,方可执行下一指令  
void lcd_wait_until_finish(void) 

 lcd1602_rw_out(HIGH); // RW置1,状态为读          
 lcd1602_rs_out(LOW);      // RS置0,读状态时RS需置低电平     
 lcd1602_en_out(HIGH); // E 置1,读取信息 
    
 while(lcd1602_bf_in());        // 循环直至BF=0 
 lcd1602_en_out(LOW);    // E重置为0 
// 向LCD写命令字 
void lcd_command(unsigned char cmd) 

 lcd1602_rw_out(LOW);              // RW置0,状态为写 
 lcd1602_rs_out(LOW);               // RS置0,写入命令字 
// lcd_e_toggle();          // 产生使能脉冲,在下降沿开始执行,将命令字读入 
 lcd1602_en_out(HIGH);
 lcd1602_bus_write(cmd);     // 将命令字cmd送入LCD的数据端口
 delay_us(20);   
 lcd1602_en_out(LOW); 
 delay_us(20); 
// lcd_wait_until_finish(); // 等待执行完毕 
// 设置显示位置(即写入显示地址),x,y均从0开始 
 
void lcd_goto_xy(unsigned char x, unsigned char y) 

 unsigned char p;       // p为字符显示位置,即DDRAM中的地址 
    if (y==0) 
    { 
  p = 0x00 + x;      // (0,0)显示位置为0x00 
    } 
 else 
 { 
  p = 0x40 + x;      // (0,1)显示位置为0x40 
    } 
 lcd_command(p + 0x80); // 写入显示地址时DB7须为高电平,加0x80 
// 写字符(传入的参数实际为所需显示字符的地址,即液晶字符产生器中字符的地址)   
void lcd_putc(unsigned char c) 

 lcd1602_rw_out(LOW);            // RW置0,状态为写 
 lcd1602_rs_out(HIGH);              // RS置1,写入数据 
 lcd1602_en_out(HIGH);
 lcd1602_bus_write(c);     // 将命令字cmd送入LCD的数据端口   
 delay_us(20); 
 lcd1602_en_out(LOW);
// lcd_wait_until_finish();  // 等待完成
 delay_us(20); 
// 指定位置写字符 
void lcd_xy_putc(unsigned char x, unsigned char y, unsigned char c) 

 lcd_goto_xy(x,y);
 delay_us(100); //要延迟下
 lcd_putc(c); 

 
// 写字符串  
void lcd_puts(unsigned char *s) 

 while(*s) 
 { 
  lcd_putc(*s);
  delay_us(100); //要延迟下
  s++; 
    } 

  
// 指定位置写字符串    
void lcd_xy_puts(unsigned char x, unsigned char y, unsigned char *s) 

 lcd_goto_xy(x, y);
 delay_us(100); //要延迟下
 lcd_puts(s); 
void lcd1602_clear(void)
{
 lcd_command(0x01);
 delay_ms(10);
}
// LCD初始化  
void lcd1602_init(void) 
 delay_ms(15);
 lcd_command(0X38);
 delay_ms(5);
 lcd_command(0X38);
 delay_ms(5);
 lcd_command(0X38);
 delay_ms(5);
 lcd_command(0X38);  
 delay_ms(5);
 lcd_command(0X08);
 delay_ms(5);
 lcd_command(0X01);
 delay_ms(10);
 lcd_command(0X06);
 delay_ms(5); 
 lcd_command(0X0C);
 delay_ms(5);
 lcd_command(0X02);
 delay_ms(5);
 lcd_command(0XC0);
 delay_ms(5);
}
/*
* hl 为0:输出低电平,非0:输出高电平
*/
void lcd1602_en_out(int hl) //使能,往gpf
{
 unsigned long data;
 volatile unsigned long * gpgdata;
 gpgdata = (volatile unsigned long*)gpgcon + 1;
 if(hl != 0)data = 1;else data = 0;
 writel((readl(gpgdata) & ~(1 << 3)) | (data << 3),gpgdata);
}
void lcd1602_rw_out(int hl) //读写1602控制,H:读1602,L:写入1602
{
 unsigned long data;
 volatile unsigned long * gpgdata;
 gpgdata = (volatile unsigned long*)gpgcon + 1;
 if(hl != 0)data = 1;else data = 0;
 writel((readl(gpgdata) & ~(1 << 10)) | (data << 10),gpgdata);
// lvx3245_tr_out(!hl); //这里要取反,省了一个反向器
}
void lcd1602_rs_out(int hl) //输入指令数据选择
{
 unsigned long data;
 volatile unsigned long * gpgdata;
 gpgdata = (volatile unsigned long*)gpgcon + 1;
 if(hl != 0)data = 1;else data = 0;
 writel((readl(gpgdata) & ~(1 << 9)) | (data << 9),gpgdata); 
}
//H:A->B,L:B->A
void lvx3245_tr_out(int hl)
{
 unsigned long data;
 volatile unsigned long * gpgdata;
 gpgdata = (volatile unsigned long*)gpgcon + 1;
 if(hl != 0)
  data = 1;
 else
  data = 0;
 writel((readl(gpgdata) & ~(1 << 1)) | (data << 1),gpgdata);
}
//向1602的8根数据总线写入数据
void lcd1602_bus_write(unsigned char c)
{
 volatile unsigned long * gpfdata,*gpgdata;
 gpfdata = (volatile unsigned long*)gpfcon + 1;
 gpgdata = (volatile unsigned long*)gpgcon + 1; 
 writel( (readl(gpfcon) & ~(0x3fff)) | (0x1555), gpfcon); //gpf0~gpf6配置成输出GPIO,这里输出低7位。
 writel((readl(gpfdata) & ~0x7f) | ((c<<1)>>1),gpfdata); //写出低7位
 writel( (readl(gpgcon) & ~0x3) | (0x1), gpgcon); //gpg配置成输出GPIO,这里输出第8位。
 writel((readl(gpgdata) & ~1) | (c>>7),gpgdata);
}
//从1602的8根数据总线读入数据
unsigned char lcd1602_bus_read(void)
{
 unsigned char data;
 volatile unsigned long * gpfdata,*gpgdata;
 gpfdata = (volatile unsigned long*)gpfcon + 1;
 gpgdata = (volatile unsigned long*)gpgcon + 1; 
 writel( (readl(gpfcon) & ~(0x3fff)) | (0x0), gpfcon); //gpf0~gpf6配置成输入GPIO,读取低7位。
 data = (readb(gpfdata)<<1)>>1; //读取低7位,去掉第 8 bit
 writel( (readl(gpgcon) & ~0x3) | (0x0), gpgcon); //gpg配置成输入GPIO,这里读入第8位。
 data |= ((readl(gpgdata) & 0x1)<<7);
 return data;
}
//BF=1表示液晶显示器忙,返回值就是bf的状态
int lcd1602_bf_in(void)
{
 return (lcd1602_bus_read() & 0x80);
}
int lcd1602_open(struct inode *inode, struct file *filp)
{
 return 0;
}
int lcd1602_release(struct inode *inode, struct file *filp)
{
 return 0;
}
static ssize_t lcd1602_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
 return 0;
}
static ssize_t lcd1602_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
 return 0;
}
#define PUT_CHAR 0
#define CLEAR 1
static int my_LCD1602_ioctl(
 struct inode *inode,
 struct file *file,
 unsigned int cmd,
 unsigned long arg)
{
 switch (cmd){
  case PUT_CHAR:
   lcd_putc((unsigned char)arg);
  case CLEAR:
   lcd1602_clear();
  default:
   return -EINVAL;
 }
}
static struct file_operations my_LCD1602_fops = {
 .owner = THIS_MODULE,
 .ioctl = my_LCD1602_ioctl,
 .read = lcd1602_read,
 .write = lcd1602_write,
 .open = lcd1602_open,
 .release = lcd1602_release,
};
static int my_LCD1602_init(void)
{
 int ret;
 gpfcon=ioremap_nocache(GPFCON,0x0000008);//也映射下数据寄存器
 gpgcon=ioremap_nocache(GPGCON,0x0000008);
 writel( (readl(gpgcon) & ~(0x3 << 18)) | (0x1 << 18), gpgcon); //GPG9作为LCD1602_RS
 writel( (readl(gpgcon) & ~(0x3 << 20)) | (0x1 << 20), gpgcon); //GPG10作为LCD1602_RW
 writel( (readl(gpgcon) & ~(0x3 << 6)) | (0x1 << 6), gpgcon); //GPG3作为LCD1602_EN,以上都是输出功能。
//因为有上拉电阻,默认应该是高,A->B
 writel( (readl(gpgcon) & ~(0x3 << 2)) | (0x1 << 2), gpgcon); //GPG1作为74lvx3245的T/R选择,H:A->B,L:B->A
 lvx3245_tr_out(HIGH); //确保是H:A->B
 ret = register_chrdev(LCD1602_MAJOR, DEVICE_NAME, &my_LCD1602_fops);
 if (ret < 0) {
  printk(DEVICE_NAME ":can't register major number\n");
  return ret;
 }
 devfs_mk_cdev(MKDEV(LCD1602_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);
 lcd1602_init();
 lcd_goto_xy(0,0);          // 字符位置:(0,0)
 udelay(100);
 lcd_puts("hello,world!");    // 显示字符
 lcd_goto_xy(0,1);          // 字符位置:(0,0)
 udelay(100);
 lcd_puts("Suliven 2010.1.4");    // 显示字符
 printk(DEVICE_NAME " initialized\n");
 return 0;
}
static void __exit my_LCD1602_exit(void)
{
 devfs_remove(DEVICE_NAME);
 unregister_chrdev(LCD1602_MAJOR, DEVICE_NAME);
 printk(DEVICE_NAME " uninstall\n");
}
module_init(my_LCD1602_init);
module_exit(my_LCD1602_exit);
MODULE_AUTHOR("lzd");
MODULE_DESCRIPTION("s3c2440 LCD1602 driver");
MODULE_LICENSE("GPL");

1602.h----------------------------------------------

#ifndef LCD_H 
#define LCD_H 
 
#define LCD_DATA_PORT P0  // 液晶BD0~BD7与P0口相连 

#define LCD_RS P2_0       // 液晶 RS  引脚与P2.0相接 
#define LCD_RW P2_1       // 液晶 R/W 引脚与P2.1相接 
#define LCD_EN  P2_2       // 液晶 E   引脚与P2.2相接 
#define LCD_BF P0_7       // 液晶 DB7 引脚与P0.7相接 Busy Free 

#define HIGH 1
#define LOW 0
  
// 函数原型 
 
// 向LCD写命令字 
extern void lcd_command(unsigned char cmd); 
 
// 设置显示位置(即写入显示地址),行列均从0开始 
extern void lcd_goto_xy(unsigned char x, unsigned char y); 
 
// 写字符(传入的参数实际为所需显示字符的地址,即液晶字符产生器中字符的地址) 
extern void lcd_putc(unsigned char c); 

// 指定位置写字符 
extern void lcd_xy_putc(unsigned char x, unsigned char y, unsigned char c); 
 
// 写字符串 
extern void lcd_puts(unsigned char *s); 
 
// 指定位置写字符串 
extern void lcd_xy_puts(unsigned char x, unsigned char y, unsigned char *s); 
 
// LCD初始化 
extern void lcd1602_init(void);

//对1602三根控制线的控制
extern void lcd1602_en_out(int hl); //使能
extern void lcd1602_rw_out(int hl); //读写1602控制
extern void lcd1602_rs_out(int hl); //输入指令数据选择

extern int lcd1602_bf_in(void); //读入操作状态,bf为busy free
extern void lvx3245_tr_out(int hl);
extern void lcd1602_bus_write(unsigned char c);
extern unsigned char lcd1602_bus_read(void);
#endif //LCD_H 


接线图
74LVX3245WM的pcb反相图
阅读(1581) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~