基于忙标志位的PIC18F458 LCD显示汇编代码故障排查
PIC18F458 LCD汇编代码仅显示最后一个字符问题排查
问题描述
首次进行LCD开发,为PIC18F458编写了采用**忙标志位(而非延时)**的汇编代码,目标是在LCD上显示ASCII字符'N'和'O',但遇到以下异常:
- 在MPLAB X IDE调试时表现符合预期
- 在Proteus仿真中仅显示字符'O'
- 若将末尾的无限循环改为重复加载并显示字符的循环,两个字符均可正常显示
- 当使用延时替代忙标志位时,汇编代码可正常运行;采用忙标志位的C版本代码也能正常工作
汇编代码
REGA EQU 0x07 ; 为延时子程序预留寄存器 REGB EQU 0x08 REGC EQU 0x09 LCD_DATA EQU PORTD ; LCD数据引脚RD0-RD7 LCD_CTRL EQU PORTC ; LCD控制引脚 RS EQU RC0 ; LCD的RS引脚 RW EQU RC1 ; LCD的R/W引脚 EN EQU RC2 ; LCD的Enable引脚 ORG 0000H ; 从ROM地址0开始烧录 CLRF TRISD ; 设置PORT D为输出 CLRF TRISC ; 设置PORTC为输出 BCF LCD_CTRL,EN ; Enable引脚初始为低 CALL LDELAY ; 初始化等待 MOVLW 0x38 ; 初始化LCD为2行、5x7字符模式 CALL COMMAND ; 发送命令 CALL LDELAY ; 初始化保持延时 MOVLW 0x0E ; 开启LCD显示、开启光标 CALL COMMAND ; 发送命令 CALL READY ; 检查LCD是否就绪 MOVLW 0x01 ; 清屏命令 CALL COMMAND ; 发送命令 CALL READY ; 检查LCD是否就绪 MOVLW 0x06 ; 设置光标右移 CALL COMMAND ; 发送命令 CALL READY ; 检查LCD是否就绪 MOVLW 0x86 ; 设置光标位置:第1行第6位 CALL COMMAND ; 发送命令 CALL READY ; 检查LCD是否就绪 MOVLW A'N' ; 准备显示字符'N' CALL DATA_DISPLAY CALL READY ; 检查LCD是否就绪 MOVLW A'O' ; 准备显示字符'O' CALL DATA_DISPLAY CALL READY ; 检查LCD是否就绪 HERE BRA HERE ;------------------------------------------------------ COMMAND MOVWF LCD_DATA ; 将命令码写入LCD数据端口 BCF LCD_CTRL,RS ; RS=0,发送命令 BCF LCD_CTRL,RW ; R/W=0,写操作 BSF LCD_CTRL,EN ; E=1,产生高脉冲 CALL SDELAY ; 延长Enable脉冲宽度 BCF LCD_CTRL,EN ; E=0,产生下降沿锁存数据 RETURN ;----------------------------------------------------- DATA_DISPLAY MOVWF LCD_DATA ; 将WREG中的数据写入LCD数据端口 BSF LCD_CTRL,RS ; RS=1,发送数据 BCF LCD_CTRL,RW ; R/W=0,写操作 BSF LCD_CTRL,EN ; E=1,产生高脉冲 CALL SDELAY ; 延长Enable脉冲宽度 BCF LCD_CTRL,EN ; E=0,产生下降沿锁存数据 RETURN ;----------------------------------------------------- ; 检查忙标志 READY SETF TRISD ; 设置PORTD为输入,读取LCD数据 BCF LCD_CTRL,RS ; RS=0,访问命令寄存器 BSF LCD_CTRL,RW ; R/W=1,读操作 ; 读取命令寄存器并检查忙标志 BACK BSF LCD_CTRL,EN ; E=1,触发LCD输出数据 CALL SDELAY ; 等待数据稳定 BCF LCD_CTRL,EN ; E=0,结束读操作 BTFSC LCD_DATA,7 ; 忙标志为0则跳过循环 BRA BACK CLRF TRISD ; 恢复PORTD为输出 RETURN ; 1/4秒延时 LDELAY MOVLW D'200' ; WREG加载数值200 MOVWF REGA ; 存入延时寄存器A L1 MOVLW D'250' ; WREG加载数值250 MOVWF REGB ; 存入延时寄存器B L2 NOP ; 空操作延时 NOP DECF REGB, F ; 寄存器B自减 BNZ L2 ; 未减到0则循环 DECF REGA, F ; 寄存器A自减 BNZ L1 ; 未减到0则循环 RETURN ; 短延时 SDELAY MOVLW D'2' ; WREG加载数值2 MOVWF REGC ; 存入延时寄存器C S1 NOP DECF REGC, F ; 寄存器C自减 BNZ S1 ; 未减到0则循环 RETURN END
C版本参考代码
#define ldata PORTD // PORTD作为LCD数据引脚 #define rs PORTCbits.RC0 // RS引脚对应PORTC.0 #define rw PORTCbits.RC1 // R/W引脚对应PORTC.1 #define en PORTCbits.RC2 // Enable引脚对应PORTC.2 #define busy PORTDbits.RD7 // 忙标志对应PORTD.7 void lcdcmd(unsigned char value); void lcddata(unsigned char value); void lcdready(void); void MSDelay(unsigned int itime); void main() { TRISD = 0; // 设置PORTD为输出 TRISB = 0; // 设置PORTB为输出 en = 0; // Enable引脚初始为低 MSDelay(250); // 初始化长延时 lcdcmd(0x38); // 初始化LCD为2行、5x7字符模式 MSDelay(250); // 初始化保持延时 lcdcmd(0x0E); // 开启LCD显示、开启光标 lcdready(); // 检查LCD忙标志 lcdcmd(0x01); // 清屏命令 lcdready(); // 检查LCD忙标志 lcdcmd(0x06); // 设置光标右移 lcdready(); // 检查LCD忙标志 lcdcmd(0x86); // 设置光标位置:第1行第6位 lcdready(); // 检查LCD忙标志 lcddata('N'); // 显示字符'N' lcdready(); // 检查LCD忙标志 lcddata('O'); // 显示字符'O' } void lcdcmd(unsigned char value) { ldata = value; // 将命令值写入数据端口 rs = 0; rw = 0; en = 1; // 产生Enable脉冲 MSDelay(1); en = 0; } void lcddata(unsigned char value) { ldata = value; // 将数据值写入数据端口 rs = 1; rw = 0; en = 1; // 产生Enable脉冲 MSDelay(1); en = 0; } void lcdready() { TRISD = 0xFF; // 设置PORTD为输入 rs = 0; rw = 1; do // 循环等待忙标志清零 { en = 1; // 产生Enable脉冲 MSDelay(1); }while(busy==1); en = 0; TRISD = 0; } void MSDelay(unsigned int itime) { unsigned int i, j; for(i=0;i<itime;i++) for(j=0;j<135;j++); }
问题原因及修复
核心问题:忙标志读取时序错误
汇编版READY子程序中,在Enable引脚置低后才读取忙标志,此时LCD的数据输出已经失效,导致程序误以为LCD已经就绪,后续的字符写入操作会覆盖之前的字符。而C版本是在Enable引脚为高电平时直接采样忙标志,时序符合LCD的读操作要求。
修复方案
- 修正忙标志读取时序
修改READY子程序,在Enable引脚为高电平期间读取忙标志:READY SETF TRISD ; 设置PORTD为输入 BCF LCD_CTRL,RS ; RS=0,访问命令寄存器 BSF LCD_CTRL,RW ; RW=1,读操作 BACK BSF LCD_CTRL,EN ; E=1,触发LCD输出数据 CALL SDELAY ; 等待数据稳定 BTFSC LCD_DATA,7 ; 高电平期间读取忙标志 BRA BACK ; 忙则继续等待 BCF LCD_CTRL,EN ; E=0,结束读操作 CLRF TRISD ; 恢复PORTD为输出 RETURN - 延长短延时时长
将SDELAY的计数值从D'2'改为D'10',确保LCD数据有足够时间稳定:SDELAY MOVLW D'10' MOVWF REGC S1 NOP DECF REGC, F BNZ S1 RETURN - 优化初始化流程
将初始化阶段的LDELAY替换为READY,确保每一步操作都等待LCD就绪,避免初始化不充分导致的异常。
内容的提问来源于stack exchange,提问作者aLoHa
相关产品推荐
相关产品推荐

