用FPGA驱动LCD1602显示自定义字符:从原理到Verilog代码实战(附完整工程)
FPGA驱动LCD1602显示自定义字符:从CGRAM原理到Verilog实战
在嵌入式显示领域,LCD1602因其经典可靠而广受欢迎。但大多数开发者仅停留在调用内置字符的层面,当需要显示温度符号、单位标志或简单图标时往往束手无策。本文将揭示如何通过FPGA操控LCD1602的CGRAM(Character Generator RAM)实现完全自定义字符显示,并以"°C"温度符号为例展示完整开发流程。
1. CGRAM工作机制深度解析
LCD1602内置的CGROM存储了160个预定义字符(包括字母、数字和日文假名),而8个可编程的CGRAM位置才是实现个性化的关键。每个CGRAM位置对应一个5x8点阵字符,其工作原理如下:
- 地址映射:CGRAM地址范围0x00-0x3F,每8字节定义一个字符(实际使用前5位)
- 数据格式:每个字节代表一行点阵,LSB对应最右侧像素。例如定义"°"符号:
8'b00110, // 第1行 8'b01001, // 第2行 8'b01001, // 第3行 8'b00110, // 第4行 8'b00000, // 第5行 8'b00000, // 第6行 8'b00000, // 第7行 8'b00000 // 第8行 - 调用机制:写入DDRAM时使用0x00-0x07地址即可调用对应CGRAM字符
注意:CGRAM内容会在断电后丢失,每次上电需重新写入。部分型号LCD在初始化时会清空CGRAM。
2. 硬件接口与时序优化
在原有驱动框架基础上扩展CGRAM功能,需特别注意时序控制:
| 信号线 | 方向 | CGRAM操作时的状态 |
|---|---|---|
| RS | FPGA→LCD | 0-写指令;1-写数据 |
| RW | FPGA→LCD | 保持低电平(写模式) |
| E | FPGA→LCD | 高脉冲宽度>450ns |
| DB0-DB7 | FPGA→LCD | 指令/数据总线 |
关键操作序列:
- 设置CGRAM地址指令(0x40|addr)
- 连续写入8字节点阵数据
- 返回DDRAM模式(0x80|addr)
// CGRAM写入状态机片段示例 parameter SET_CGRAM_ADDR = 4'd10; parameter WRITE_CGRAM = 4'd11; case(state) SET_CGRAM_ADDR: begin lcd_rs <= 0; lcd_data <= 8'h40; // 设置CGRAM地址基址 state <= WRITE_CGRAM; end WRITE_CGRAM: begin lcd_rs <= 1; lcd_data <= custom_char[char_line]; if(char_line == 3'd7) begin state <= NEXT_STATE; end end endcase3. 完整温度符号实现方案
3.1 自定义字符设计
实现"°C"显示需要两个自定义字符:
- 度符号(°)
- CGRAM地址:0x00
- 点阵数据:
[0x07, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00] # 5x8点阵
- 大写字母C
- CGRAM地址:0x08
- 点阵数据需与内置字体风格保持一致
3.2 Verilog代码集成
在原有状态机中增加CGRAM初始化阶段:
// 状态定义扩展 parameter INIT_CGRAM = 4'd5; parameter DISP_TEMP = 4'd12; // 点阵数据存储 reg [7:0] degree_char [0:7]; reg [7:0] celsius_char [0:7]; initial begin // 初始化度符号 degree_char[0] = 8'b00110; degree_char[1] = 8'b01001; // ...完整数据省略 end // 状态机新增分支 INIT_CGRAM: begin if(sub_state == 0) begin lcd_data <= 8'h40; // 设置CGRAM地址 sub_state <= 1; end else begin lcd_data <= degree_char[char_ptr]; if(char_ptr == 7) begin state <= DISP_TEMP; end end end3.3 显示控制技巧
- 混合显示:DDRAM中交替使用CGROM和CGRAM字符
- 地址计算:显示位置需考虑行偏移(第一行0x80+col,第二行0xC0+col)
- 优化建议:
- 将常用符号固化在CGRAM的固定位置
- 使用宏定义提高代码可读性:
`define DEGREE_SYM 8'h00 // CGRAM位置0 `define CELSIUS_C 8'h01 // CGRAM位置1
4. 调试与性能优化
4.1 常见问题排查
字符显示错乱:
- 检查CGRAM地址是否越界
- 验证点阵数据位序(LSB/MSB)
显示位置偏移:
- 确认DDRAM地址计算正确
- 检查状态机是否意外修改了地址指针
时序问题:
- 用逻辑分析仪捕获E信号脉宽
- 确保各状态间有足够延时
4.2 性能提升方案
- 双缓冲技术:在FPGA中开辟CGRAM缓存区,实现动态更新
- 预编译工具:使用Python生成点阵数据Verilog初始化代码:
def generate_verilog(data, symbol_name): print(f"// {symbol_name} definition") for i, line in enumerate(data): print(f"8'b{line:08b}", end=', ' if i<7 else '') - 资源优化:共用相同字符的点阵数据,减少CGRAM占用
在完成上述实现后,当系统需要显示"25°C"时,DDRAM中只需写入:
0x32 0x35 0x00 0x01 // "2" "5" CGRAM0 CGRAM1