代码详细注释:ARM-Linux字符设备驱动开发案例:LCD汉字输出改进建议开发板断电重启还能显示汉字,显示汉字位置自定义
代码
/* LCD显示程序 - 在嵌入式设备上显示中文 */
#include <stdio.h> // 标准输入输出头文件
#include "font.h" // 字体库头文件(自定义)
#include <sys/types.h> // 系统数据类型定义
#include <sys/stat.h> // 文件状态信息
#include <fcntl.h> // 文件控制选项
#include <sys/mman.h> // 内存映射支持
#include <unistd.h> // UNIX标准函数int fd; // 全局文件描述符(LCD设备)
int (*lcd)[800] = NULL; // LCD显存映射指针(800x480分辨率)/* 初始化LCD显示 */
int init_lcd()
{// 1.打开帧缓冲设备fd = open("/dev/fb0", O_RDWR); // 以读写模式打开帧缓冲设备if (fd == -1) // 检查是否打开成功{perror("打开LCD设备失败\n"); // 打印错误信息return -1; // 返回错误状态}else{printf("打开LCD设备成功\n"); // 成功提示}// 2.映射显存到用户空间lcd = mmap(NULL, // 由系统选择映射地址800 * 480 * 4, // 映射大小(800x480 32bpp)PROT_READ | PROT_WRITE, // 可读可写MAP_SHARED, // 共享映射fd, // 文件描述符0); // 偏移量if (lcd == MAP_FAILED) // 检查映射是否成功{perror("映射LCD设备失败\n"); // 打印错误信息return -1; // 返回错误状态}else{printf("映射LCD设备成功\n"); // 成功提示}return 0; // 初始化成功
}/* 释放LCD资源 */
void free_lcd()
{// 3.解除内存映射munmap(lcd, 800 * 480 * 4); // 释放映射的显存close(fd); // 关闭设备文件
}int main()
{// 初始化LCD显示init_lcd(); // 调用初始化函数// 1.加载TrueType字体文件font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf"); // 加载字体if(!f) {printf("字体加载失败\n"); // 错误提示free_lcd(); // 释放资源return -1; // 退出程序}// 2.设置字体大小(128像素)fontSetSize(f, 128); // 设置字体大小// 3.配置直接显存画布bitmap bm; // 定义位图结构体bm.height = 480; // 画布高度(像素)bm.width = 800; // 画布宽度(像素)bm.byteperpixel = 4; // 每像素4字节(32位色)bm.map = (u8 *)lcd; // 直接映射到显存// 4.在屏幕上显示绿色文字fontPrint(f, // 字体对象&bm, // 目标画布0, // X起始位置0, // Y起始位置"DIY机器人工房", // 显示文本getColor(0, 255, 0, 0), // 颜色值(ABGR格式: 绿)0); // 背景模式(0=透明)free_lcd(); // 释放LCD资源return 0; // 程序正常退出
}
关键点说明:
颜色格式:
getColor(0, 255, 0, 0)
使用ABGR格式:A(alpha)=0:完全透明(但实际效果可能因实现而异)
B(蓝)=0
G(绿)=255
R(红)=0
实际显示绿色文字
显示流程:
初始化LCD帧缓冲 → 加载字体 → 设置画布 → 渲染文字 → 释放资源
特殊说明:
该程序直接操作显存,需要root权限运行
字体路径
/usr/share/fonts/DroidSansFallback.ttf
是开发板特定路径颜色值可能需要根据实际硬件调整(BGR/RGB顺序问题)
您的代码在开发板重启后文字消失是正常现象,因为FrameBuffer的显存是临时性的内存映射。要实现"永久显示"效果,需要从系统层面进行改造。以下是完整的解决方案:
一、根本原因分析
显存特性:
/dev/fb0
映射的是显示控制器的临时缓存所有绘制操作仅保存在易失性内存中
断电后显存内容必然丢失
程序行为:
c
mmap()映射 → 绘制文字 → 程序退出 → 内存释放
二、永久显示解决方案
方案1:开机自启动(最简单)
步骤:
将编译好的程序放到
/usr/bin/
:bash
cp your_program /usr/bin/lcd_display chmod +x /usr/bin/lcd_display
添加到启动脚本(以GEC6818为例):
bash
# 编辑/etc/init.d/rcS echo "/usr/bin/lcd_display &" >> /etc/init.d/rcS
特点:
每次启动自动恢复显示
无需修改现有代码
方案2:持久化+自动恢复(推荐)
代码改造:
c
// 在main()函数中添加持久化逻辑 int main() {// 原有初始化...while(1) {fontPrint(f, &bm, 0, 0, "DIY机器人工房", getColor(0,255,0,0), 0);sync(); // 确保数据写入磁盘sleep(10); // 每10秒重绘一次(防闪退)}// 注意:此版本需要Ctrl+C终止 }
配套措施:
bash
# 创建看门狗脚本/etc/watchdog.sh #!/bin/sh while true; doif ! pgrep lcd_display; then/usr/bin/lcd_display &fisleep 5 done# 添加到启动 chmod +x /etc/watchdog.sh echo "/etc/watchdog.sh &" >> /etc/init.d/rcS
方案3:硬件级解决方案(最可靠)
外接EEPROM存储:
c
// 保存显示状态到I2C EEPROM void save_display_state() {int eeprom_fd = open("/dev/i2c-0", O_RDWR);write(eeprom_fd, display_data, sizeof(display_data));close(eeprom_fd); }
使用FRAM存储器:
替换显存映射为持久存储区域
需硬件改造
方案4:X Window方案(适合高级应用)
bash
# 在~/.xinitrc中添加 /usr/bin/your_program & exec startxfce4
三、各方案对比
方案 | 可靠性 | 复杂度 | 是否需要硬件改造 | 适用场景 |
---|---|---|---|---|
开机自启动 | ★★☆ | 低 | 否 | 简单应用 |
看门狗+定时刷新 | ★★★ | 中 | 否 | 工业控制 |
EEPROM存储 | ★★★★ | 高 | 是 | 关键任务 |
X Window集成 | ★★☆ | 中 | 否 | 桌面环境 |
四、最佳实践建议(GEC6818开发板)
基础版:
bash
# 在/etc/init.d/rcS末尾添加: /usr/bin/lcd_display > /dev/null 2>&1 &
增强版:
c
// 修改main函数: int main() {init_lcd();font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");fontSetSize(f, 128);while(1) {bitmap bm = {480, 800, 4, (u8*)lcd};fontPrint(f, &bm, 0, 0, "DIY机器人工房", getColor(0,255,0,0), 0);fsync(fd); // 强制写入sleep(10);}return 0; // 实际不会执行到此处 }
部署命令:
bash
arm-linux-gcc -o lcd_display display.c -lfreetype cp lcd_display /usr/bin/ chmod +x /usr/bin/lcd_display echo "/usr/bin/lcd_display &" >> /etc/init.d/rcS sync
五、注意事项
显存映射的生命周期与进程绑定
开发板的文件系统可能是只读的,需要先remount:
bash
mount -o remount,rw /
长时间运行需注意内存泄漏问题
建议添加日志监控:
bash
echo "$(date) LCD程序启动" >> /var/log/lcd.log
通过以上方法,您的文字显示将在系统重启后自动恢复,实现"永久显示"效果。
在您的代码中自定义文字起始位置(而不只是左上角),需要通过修改 fontPrint
函数的坐标参数来实现。以下是具体方法和代码示例:
1. 关键修改点
在您的代码中,这行控制文字位置:
c
fontPrint(f, &bm, 0, 0, "DIY机器人工房", getColor(0,255,0,0), 0); // ↑ ↑ // X,Y坐标(左上角为0,0)
2. 自定义坐标的方法
修改 fontPrint
的 第3和第4个参数:
第3参数:X轴起始位置(水平方向,单位:像素)
第4参数:Y轴起始位置(垂直方向,单位:像素)
示例:将文字显示在屏幕中央
c
int text_x = (800 - text_width) / 2; // 水平居中 int text_y = (480 - text_height) / 2; // 垂直居中 fontPrint(f, &bm, text_x, text_y, "居中文字", getColor(0,255,0,0), 0);
3. 完整代码示例
c
// 在画布上显示字体(自定义位置) int text_x = 100; // 距离左侧100像素 int text_y = 200; // 距离顶部200像素 fontPrint(f, &bm, text_x, text_y, "DIY机器人工房", getColor(0,255,0,0), 0);
4. 动态计算文字宽度(精确居中)
如果需要根据文字内容自动计算居中位置:
c
// 先计算文字像素宽度(需要字体库支持) int text_width = 128 * strlen("DIY机器人工房"); // 假设每个字符宽=字体大小 int text_height = 128; // 字体高度// 计算居中坐标 int center_x = (800 - text_width) / 2; int center_y = (480 - text_height) / 2;fontPrint(f, &bm, center_x, center_y, "DIY机器人工房", getColor(0,255,0,0), 0);
5. 注意事项
坐标范围:
X轴有效范围:
0
到800 - 文字宽度
Y轴有效范围:
0
到480 - 文字高度
超出范围可能导致显示不全
字体尺寸影响:
c
fontSetSize(f, 128); // 字体越大,需要的空间越大
多行文字:
如需换行显示,需要手动计算Y轴偏移:c
fontPrint(f, &bm, 50, 100, "第一行", color, 0); fontPrint(f, &bm, 50, 250, "第二行", color, 0); // Y坐标增加
开发板差异:
某些嵌入式设备的坐标系可能从底部开始(如Y轴反向),此时需要调整:c
int y_pos = 480 - text_y - text_height; // 从底部计算
6. 调试技巧
添加坐标打印,方便调整位置:
c
printf("文字位置:X=%d, Y=%d\n", text_x, text_y);
通过修改这些参数,您可以自由控制文字在屏幕上的显示位置。