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反相图