如何在树莓派Pico的ARM汇编中读取GPIO输入引脚状态?
RP2040汇编读取GPIO输入引脚的解决方案
原代码存在的问题
- 输入使能寄存器写入错误:
str r1, [r0, #0x004]用了r1的值(5),但这里需要传入对应GPIO1的位掩码,否则无法正确使能该引脚输入。 - 缺少读取输入状态的核心逻辑:代码仅完成了引脚配置,没有实际读取按键电平的步骤。
- 未定义硬件地址符号:
ctrl2和sio_base没有明确的地址定义,汇编器无法解析这些符号。 - printf参数不完整:格式化字符串包含
%d,但未传入对应的数据参数,会导致打印异常。
修正后的完整代码
.equ GPIO_BASE, 0x40014000 .equ GPIO_CTRL_OFFSET, 0x08 ; GPIO1控制寄存器偏移(GPIO0是0x00,每个寄存器占4字节) .equ SIO_BASE, 0xD0000000 .equ SIO_GPIO_IN_EN, 0x004 ; SIO输入使能寄存器地址偏移 .equ SIO_GPIO_IN, 0x000 ; SIO输入状态寄存器地址偏移 .global _start _start: @ 配置GPIO1为SIO功能(用于通用输入输出) ldr r0, =GPIO_BASE add r0, r0, #GPIO_CTRL_OFFSET ; 计算GPIO1控制寄存器地址:0x40014008 mov r1, #5 ; 功能5对应SIO(参考RP2040数据手册2.19.2) str r1, [r0] @ 使能GPIO1的输入功能 mov r2, #1 lsl r2, r2, #1 ; 生成GPIO1的位掩码:1 << 1 = 0x02 ldr r0, =SIO_BASE str r2, [r0, #SIO_GPIO_IN_EN] ; 写入输入使能寄存器,开启GPIO1输入 led_loop: @ 读取GPIO1的当前输入状态 ldr r0, =SIO_BASE ldr r3, [r0, #SIO_GPIO_IN] ; 读取所有SIO引脚的输入状态 and r3, r3, r2 ; 只保留GPIO1对应的位 lsr r3, r3, #1 ; 将位值移到最低位,得到0(按下)或1(未按下) @ 打印按键状态 ldr r0, =helloworld mov r1, r3 ; 传递状态值作为printf的第二个参数 BL printf @ 延时控制,避免打印过快 ldr r4, =0x100000 delay_loop: subs r4, r4, #1 bne delay_loop b led_loop ; 循环读取状态 .data .align 4 helloworld: .asciz "按键状态:%d\n" ; 0表示按键按下,1表示未按下
关键逻辑说明
- 引脚功能配置:GPIO控制寄存器写入数值5,将GPIO1切换到SIO功能,这是RP2040通用GPIO输入输出的基础模式。
- 输入使能:SIO_GPIO_IN_EN寄存器的每一位对应一个GPIO引脚,置1则允许该引脚作为输入。这里通过
1 << 1生成GPIO1的位掩码,写入寄存器完成使能。 - 读取输入电平:SIO_GPIO_IN寄存器实时存储所有SIO引脚的输入电平,每一位对应一个引脚的状态。通过
and操作提取GPIO1的位,再右移得到0或1(按键未按是高电平3.3V,对应1;按下是低电平0V,对应0)。 - printf参数传递:遵循ARM调用约定,printf的第一个参数(r0)是格式化字符串地址,第二个参数(r1)是要打印的状态值。
- 延时处理:添加简单的减法循环延时,避免串口打印过于频繁,方便观察按键状态变化。
内容的提问来源于stack exchange,提问作者Dervieux Benoît
相关产品推荐
相关产品推荐

