第五章——总结和拓展
1. 总结
总得来说,LCD1602 的通信方式其实和前面所讲的其他方式没有很大的差别,同样需要完成数据命令的读写,需要对寄存器进行配置等等。只是将串行通信变成了并行罢了。
回头来看,可以发现,我们在整个操作中我们唯一的读操作就是读取 Flag。其实,一般显示的设备都是不需要读操作的,像 OLED,TFT 等等。那我们选择增加一个引脚的意义又在哪里呢。其实这么做不仅可以简化代码,而且能够大大提高效率。事实上,Arduino 的官方 LCD1602 库LiquidCrystal.h
就是使用解析时间代替读取 Flag,那是为了兼容 4 总线的接口模式。
2. 拓展
前面我们是介绍 LCD 的 8 总线模式,那么 LCD 的 4 总线模式如何使用呢。
其实使用 4 总线模式的时候,如果通过解析时间代替 Flag,不仅可以节省 4 个 DB,还可以省下 RW。也就是说只对 LCD1602 进行写操作,不进行读操作。
只是呢,进入 4 总线模式就需要对 LCD1602 进行配置,同时发送数据也要分两次发送,相对来说就要复杂一些。
下面是我的一个示例代码,大家可以参考一下:
#define LCD_CLEAR 0x01
#define LCD_HOME 0x02
#define LCD_ENTRY_MODE 0x06
#define LCD_SURSOR_SHIFT 0x18
#define LCD_FUNCTION_SET 0x28
#define LCD_SET_CGRAM_ADDR 0x40
#define LCD_SET_DDRAM_ADDR 0x80
uint8_t LCD_DISPLAY_CONTROL = 0x0C;
const uint8_t EN = 8, RS = 9;
uint8_t data_pins[] = {7, 6, 5, 4};
const char* message = "Hello world!";
uint8_t bell[8] = {0x4, 0xe, 0xe, 0xe, 0x1f, 0x0, 0x4};
void send_pins(uint8_t);
void write_cmd(uint8_t);
void write_data(uint8_t);
void lcd_init();
void lcd_clear();
void lcd_home();
void display_off();
void display_on();
void cursor_off();
void cursor_on();
void blink_off();
void blink_on();
void scroll_left();
void scroll_right();
void set_cursor();
void print(const char);
void print(const char*);
void creat_char(uint8_t, uint8_t*);
void setup() {
lcd_init();
creat_char(0, bell);
set_cursor(0, 0);
print(message);
print('*');
set_cursor(7, 1);
print("Have a nice day! By Mr.Addict!");
set_cursor(37, 1);
write_data(0);
}
void loop() {
scroll_left();
delay(1000);
}
// 发送数据到引脚
void send_pins(uint8_t dat) {
for (int i = 0; i < 4; i++) {
digitalWrite(data_pins[i], (dat >> i) & 0x01);
}
digitalWrite(EN, HIGH);
digitalWrite(EN, LOW);
delayMicroseconds(50);
}
// 写命令
void write_cmd(uint8_t cmd) {
// 命令RS=0,RW=0
digitalWrite(RS, LOW);
// 高四位
send_pins(cmd >> 4);
// 低四位
send_pins(cmd);
}
// 写数据到DDRAM
void write_data(uint8_t dat) {
// 数据RS=1,RW=0
digitalWrite(RS, HIGH);
// 高四位
send_pins(dat >> 4);
// 低四位
send_pins(dat);
}
// LCD初始化
void lcd_init() {
for (uint8_t i = 0; i < 4; i++) {
pinMode(data_pins[i], OUTPUT);
}
pinMode(EN, OUTPUT);
pinMode(RS, OUTPUT);
delay(15);
for (uint8_t i = 0; i < 3; i++) {
send_pins(0x03);
delayMicroseconds(4200);
}
send_pins(0x02); // 设置4总线模式
write_cmd(LCD_FUNCTION_SET); // 4总线,2行显示区域,5x8字体
write_cmd(LCD_DISPLAY_CONTROL); // 显示开,光标关,闪烁关
lcd_clear(); // 清屏
write_cmd(LCD_ENTRY_MODE); // 地址自动+1,文字不动
}
// 清空屏幕
void lcd_clear() {
write_cmd(LCD_CLEAR);
delayMicroseconds(1600); // 该指令需要花较长时间
}
// 光标以及光标指针回到初始状态
void lcd_home() {
write_cmd(LCD_HOME);
delayMicroseconds(1600); // 该指令需要花较长时间
}
// 关闭屏幕
void display_off() {
LCD_DISPLAY_CONTROL &= 0x04;
write_cmd(LCD_DISPLAY_CONTROL);
}
// 打开屏幕
void display_on() {
LCD_DISPLAY_CONTROL |= 0x04;
write_cmd(LCD_DISPLAY_CONTROL);
}
// 关闭光标
void cursor_off() {
LCD_DISPLAY_CONTROL &= ~0x02;
write_cmd(LCD_DISPLAY_CONTROL);
}
// 打开光标
void cursor_on() {
LCD_DISPLAY_CONTROL |= 0x02;
write_cmd(LCD_DISPLAY_CONTROL);
}
// 关闭光标闪烁
void blink_off() {
LCD_DISPLAY_CONTROL &= ~0x01;
write_cmd(LCD_DISPLAY_CONTROL);
}
// 打开光标闪烁
void blink_on() {
LCD_DISPLAY_CONTROL |= 0x01;
write_cmd(LCD_DISPLAY_CONTROL);
}
// 向左滚动一列
void scroll_left() {
write_cmd(LCD_SURSOR_SHIFT);
}
// 向右滚动一列
void scroll_right() {
write_cmd(LCD_SURSOR_SHIFT | 0x04);
}
// 设置光标位置
void set_cursor(uint8_t x, uint8_t y) {
uint8_t addr;
if (y == 0)
addr = 0x00 + x; // 第一行字符地址从 0x00开始
else
addr = 0x40 + x; // 第二行字符地址从 0x40开始
write_cmd(addr | LCD_SET_DDRAM_ADDR); // 设置DDRAM地址
}
// 打印字符
void print(const char cha) {
write_data(cha);
}
// 打印字符串
void print(const char* str) {
while (*str) {
write_data(*str++); // 先取指针的值,再将指针地址自增
}
}
// 自定义一个新的CGRAM字符
void creat_char(uint8_t num, uint8_t* bit_map) {
// 设置CGRAM地址
write_cmd(LCD_SET_CGRAM_ADDR + 8 * num);
for (uint8_t i = 0; i < 8; i++) {
write_data(bit_map[i]);
}
}