You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于忙标志位的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的读操作要求。

修复方案

  1. 修正忙标志读取时序
    修改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
    
  2. 延长短延时时长
    将SDELAY的计数值从D'2'改为D'10',确保LCD数据有足够时间稳定:
    SDELAY  
            MOVLW   D'10'           
            MOVWF   REGC            
    S1      NOP
            DECF    REGC, F         
            BNZ     S1              
            RETURN
    
  3. 优化初始化流程
    将初始化阶段的LDELAY替换为READY,确保每一步操作都等待LCD就绪,避免初始化不充分导致的异常。

内容的提问来源于stack exchange,提问作者aLoHa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 21:25:43