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

基于ATmega32A的4x4矩阵键盘扫描LED显示代码故障求助

AVR汇编4x4矩阵键盘扫描故障排查与修复

问题根源

代码始终显示最后一个掩码值而非实际按键,由以下逻辑错误导致:

1. ReadKP无提前退出机制

ReadKP会依次扫描所有4列,即使前面列已检测到按键,程序仍会执行后续列扫描,最后一列的掩码会覆盖TEMP寄存器中的正确按键值,最终LED显示最后一列掩码(0x7F)。

2. findKey位操作逻辑反转

按键按下时对应输入引脚为低电平(上拉输入配置),读取PINC后按下的位是0。原代码用LSR右移后检测BRCS(进位为1跳转),与实际逻辑相反;同时循环计数逻辑存在偏差。

3. COL4注释错误

COL4的注释误写为“第三列”,需修正为“第四列”。

4. ReadKey冗余代码

ReadKey里的ldi ts, 0x01和clr temp为无效代码,可直接删除。


修正后的完整代码

; ****************************************************
; EEET2256 - Laboratory 3B (2023) Template
; Author: Dr. Glenn Matthews
; Last Updated:  21/08/2023 11:34:04 AM
; lab3b_template.asm (修正版)
;*****************************************************
; Define variables.
.def  temp  = R16
.def  temp_2 = R17
.def  ts = r20
.equ SP = 0xDF

; Reset Vector.
reset:
   rjmp start

;***********************************************************
; This program reads a single number from the keypad.
;***********************************************************
; Program starts here after Reset
start:
    LDI  TEMP,  SP      ; Init Stack pointer
    OUT  0x3D,  TEMP

    CALL Init           ; Initialise the system.

loop:       
    ; Get a single key from the keypad and return it in R16 (temp)
    CALL ReadKey       

    ; Once the key has been recieved, put it on the PORTB leds.             
    CALL Display
    
    RJMP     loop           ; Do all again - You don't need to reinitialize

;************************************************************************
;
Init:
    PUSH TEMP  ; Save TEMP on the stack

    ; Configure the lower 4 bits of Port C as inputs for the keypad.
    LDI TEMP, 0x0F
    OUT DDRC, TEMP

    LDI TEMP, 0xFF
    OUT PORTC, TEMP

    ; Set all pins in Port B as outputs for the LEDs.
    OUT DDRB, TEMP

    POP TEMP  ; Restore TEMP from the stack
    RET 
;************************************************************************
;
; ReadKey - Read a single digit from the keypad and store in TEMP
; Uses:  R16 (Temp)
; Returns: Temp
;
 ReadKey:
    LDI TEMP, 0xFF
    OUT PORTC, TEMP
    NOP
    IN TEMP_2, PINC
    CPI TEMP_2, 0XFF
    BREQ ReadKey
    CALL Delay
    RCALL ReadKP            ; Read one number from keypad and return in Temp (R16)
    
    RET                     ; Exit back to calling routine

ReadKP:
    LDI TEMP_2, 0xEF
    OUT PORTC, TEMP_2
    NOP
    IN TEMP, PINC
    CPSE TEMP_2,TEMP
    CALL COL1
    RET ; 找到按键后立即返回,停止后续列扫描

    LDI TEMP_2, 0xDF
    OUT PORTC, TEMP_2
    NOP
    IN TEMP, PINC
    CPSE TEMP_2,TEMP
    CALL COL2
    RET ; 找到按键后立即返回

    LDI TEMP_2, 0xBF
    OUT PORTC, TEMP_2
    NOP
    IN TEMP, PINC
    CPSE TEMP_2,TEMP
    CALL COL3
    RET ; 找到按键后立即返回

    LDI TEMP_2, 0x7F
    OUT PORTC, TEMP_2
    NOP
    IN TEMP, PINC
    CPSE TEMP_2,TEMP
    CALL COL4
    RET

;************************************************************************
;
; Takes whatever is in the Temp register and outputs it to the LEDs
Display:
        OUT PortB, temp
        RET
;*************************************
;
; Delay routine
;
; this has an inner loop and an outer loop. The delay is approximately
; equal to 256*256*number of inner loop instruction cycles.
; You can vary this by changing the initial values in the accumulator.
; If you need a much longer delay change one of the loop counters
; to a 16-bit register such as X or Y.
;
;*************************************
Delay:
    PUSH R16            ; Save R16 and 17 as we're going to use them
    PUSH R17            ; as loop counters
    LDI R16, 10
L1: 
    LDI R17, 250
L2: 
    DEC R17
    BRNE L2
    DEC R16
    BRNE L1
    POP R17
    POP R16
    RET

COL1:
    ; Check 1st column for button press, load KEYCOL1 << 1 into ZH:ZL
    LDI ZH, HIGH(KEYCOL1 << 1) ; High byte of key values
    LDI ZL, LOW(KEYCOL1 << 1)  ; Low byte of key values
    CALL findKey
    RET                         ; Return from this subroutine

COL2:
    ; Check 2nd column for button press, load KEYCOL2 << 1 into ZH:ZL
    LDI ZH, HIGH(KEYCOL2 << 1) ; High byte of key values
    LDI ZL, LOW(KEYCOL2 << 1)  ; Low byte of key values
    CALL findKey
    RET                         ; Return from this subroutine

COL3:
    ; Check 3rd column for button press, load KEYCOL3 << 1 into ZH:ZL
    LDI ZH, HIGH(KEYCOL3 << 1) ; High byte of key values
    LDI ZL, LOW(KEYCOL3 << 1)  ; Low byte of key values
    CALL findKey
    RET                         ; Return from this subroutine

COL4:
    ; Check 4th column for button press, load KEYCOL4 << 1 into ZH:ZL
    LDI ZH, HIGH(KEYCOL4 << 1) ; High byte of key values
    LDI ZL, LOW(KEYCOL4 << 1)  ; Low byte of key values
    CALL findKey
    RET                         ; Return from this subroutine

findKey:
    ; Find the correct key in the column by matching the row value
    CLR TEMP_2
findKey_loop:
    SBRS TEMP, 0 ; 检查最低位是否为1(未按下),是则跳过下一条指令
    RJMP findKey_match ; 最低位为0,说明此行为按下的行,跳转匹配
    INC TEMP_2 ; 行计数+1
    LSR TEMP ; 右移一位,检查下一行
    RJMP findKey_loop
findKey_match:
    ADD ZL, TEMP_2     ; Add TEMP_2 to ZL (address in memory)
    LPM TEMP, Z     ; Load the key value from memory to TEMP
    RET

KEYCOL1:        .DB 0x00, 0x04, 0x08, 0x0C  ; Define characters for column 1 keys
KEYCOL2:        .DB 0x01, 0x05, 0x09, 0x0D  ; Define characters for column 2 keys
KEYCOL3:        .DB 0x02, 0x06, 0x0A, 0x0E ; Define characters for column 3 keys
KEYCOL4:        .DB 0x03, 0x07, 0x0B, 0x0F ; Define characters for column 4 keys

关键修正说明

  1. ReadKP添加RET退出:每列扫描并调用COLx后立即添加RET,确保找到按键后停止扫描,避免后续列覆盖TEMP值。
  2. findKey重写位检测逻辑:改用SBRS指令直接检测最低位,逻辑更直观,修正原代码中BRCS的反转错误。
  3. 修正COL4注释:将注释中的“第三列”改为“第四列”,消除混淆。
  4. 删除冗余代码:移除ReadKey中无效的ldi ts,0x01和clr temp。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 23:52:32