4. LCD1602字符显示驱动
任务目标
在嵌入式Linux环境下,通过GPIO模拟时序驱动LCD1602字符液晶模块,实现两行字符的初始化与显示。本案例聚焦于用户空间GPIO操作,不涉及内核驱动模块,适用于快速验证与原型开发。
环境与依赖
- 硬件:LCD1602模块(HD44780兼容)、开发板(如树莓派、全志H3等)、杜邦线
- Linux内核需启用
/sys/class/gpio用户空间GPIO接口(CONFIG_GPIO_SYSFS) - 交叉编译工具链或开发板原生gcc
接线定义
| LCD1602引脚 | GPIO编号(示例) | 说明 |
|---|---|---|
| RS | 17 | 寄存器选择(0=指令,1=数据) |
| RW | 18 | 读写选择(接地=写) |
| E | 27 | 使能脉冲 |
| D4-D7 | 22,23,24,25 | 4位数据总线 |
RW引脚直接接地,仅执行写操作。实际GPIO编号需根据开发板调整。
关键完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define GPIO_BASE "/sys/class/gpio"
#define RS 17
#define E 27
#define D4 22
#define D5 23
#define D6 24
#define D7 25
static void gpio_export(int pin) {
int fd = open(GPIO_BASE "/export", O_WRONLY);
char buf[4];
sprintf(buf, "%d", pin);
write(fd, buf, strlen(buf));
close(fd);
usleep(100000);
}
static void gpio_direction(int pin, const char *dir) {
char path[64];
sprintf(path, GPIO_BASE "/gpio%d/direction", pin);
int fd = open(path, O_WRONLY);
write(fd, dir, strlen(dir));
close(fd);
}
static void gpio_write(int pin, int value) {
char path[64];
sprintf(path, GPIO_BASE "/gpio%d/value", pin);
int fd = open(path, O_WRONLY);
if (value) write(fd, "1", 1);
else write(fd, "0", 1);
close(fd);
}
static void lcd_pulse(void) {
gpio_write(E, 1);
usleep(500);
gpio_write(E, 0);
usleep(500);
}
static void lcd_nibble(unsigned char nibble) {
gpio_write(D4, (nibble >> 0) & 1);
gpio_write(D5, (nibble >> 1) & 1);
gpio_write(D6, (nibble >> 2) & 1);
gpio_write(D7, (nibble >> 3) & 1);
lcd_pulse();
}
static void lcd_byte(unsigned char data, int rs) {
gpio_write(RS, rs);
lcd_nibble(data >> 4);
lcd_nibble(data & 0x0F);
}
static void lcd_command(unsigned char cmd) {
lcd_byte(cmd, 0);
usleep(2000);
}
static void lcd_data(char ch) {
lcd_byte((unsigned char)ch, 1);
usleep(2000);
}
static void lcd_init(void) {
usleep(50000);
// 切换到4位模式
gpio_write(RS, 0);
lcd_nibble(0x03);
usleep(5000);
lcd_nibble(0x03);
usleep(5000);
lcd_nibble(0x03);
usleep(5000);
lcd_nibble(0x02);
usleep(5000);
// 功能设置:4位总线,2行,5x8点阵
lcd_command(0x28);
// 显示开,光标关,闪烁关
lcd_command(0x0C);
// 清屏
lcd_command(0x01);
usleep(2000);
// 光标右移,不移动显示
lcd_command(0x06);
}
static void lcd_print(const char *str) {
while (*str) {
lcd_data(*str++);
}
}
int main(void) {
int pins[] = {RS, E, D4, D5, D6, D7};
for (int i = 0; i < 6; i++) {
gpio_export(pins[i]);
gpio_direction(pins[i], "out");
}
lcd_init();
lcd_command(0x80); // 第一行地址
lcd_print("Hello Linux!");
lcd_command(0xC0); // 第二行地址
lcd_print("LCD1602 OK");
return 0;
}
编译与运行
# 交叉编译(以arm-linux-gnueabihf为例)
arm-linux-gnueabihf-gcc -o lcd1602 lcd1602.c
# 或开发板原生编译
gcc -o lcd1602 lcd1602.c
# 运行(需root权限访问GPIO)
sudo ./lcd1602
验证方法
程序运行后,观察LCD1602屏幕:
- 第一行显示
Hello Linux! - 第二行显示
LCD1602 OK - 若显示乱码或空白,检查接线是否正确、GPIO编号是否与硬件匹配、LCD对比度电位器是否调节合适
通过条件:屏幕两行字符清晰、无乱码,与预期输出完全一致。
常见问题
- 屏幕无反应:检查GPIO是否已导出成功(
ls /sys/class/gpio),确认E引脚脉冲时序是否正常 - 显示乱码:多为4位模式初始化时序错误,或D4-D7接线顺序与代码不一致
- 字符闪烁:电源纹波过大,建议在VDD与GND间加100nF去耦电容