基于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
关键修正说明
- ReadKP添加RET退出:每列扫描并调用COLx后立即添加
RET,确保找到按键后停止扫描,避免后续列覆盖TEMP值。 - findKey重写位检测逻辑:改用
SBRS指令直接检测最低位,逻辑更直观,修正原代码中BRCS的反转错误。 - 修正COL4注释:将注释中的“第三列”改为“第四列”,消除混淆。
- 删除冗余代码:移除ReadKey中无效的
ldi ts,0x01和clr temp。
内容的提问来源于stack exchange,提问作者William Michailidis
相关产品推荐
相关产品推荐

