树莓派4B 64位裸机编程GPIO SET0寄存器操作异常问题
树莓派4B 64位裸机编程GPIO操作故障排查
问题概述
- 64位模式下操作GPIO SET0寄存器时程序直接停止运行,32位模式使用相同基地址和偏移的代码可正常工作
- QEMU+GDB调试发现,执行到读写GPIO SET0的代码行时操作无法完成,程序计数器(PC)跳转到随机地址
- 使用
aarch64-none-elf工具链编译kernel8.img,替换SD卡启动分区的镜像文件后保留系统其他文件,编译方式存疑 arm-none-eabi工具链编译的32位汇编代码可运行但不稳定,相同代码时好时坏
问题代码
执行到最后一行时程序停止:
.section ".text.boot" .global _start .equ GPIO_BASE, 0xFE200000 .equ GPIO_SET_21_OUT, 1 << 3 .equ GPIO_SEL2, 0x08 .equ GPIO_SET0, 0x1C .equ GPIO_21, 1 << 21 _start: ldr x0, =GPIO_BASE ldr x1, =GPIO_SET_21_OUT str x1, [x0, #GPIO_SEL2] ldr x1, =GPIO_21 ldr x1, [x0, #GPIO_SET0] // 此行触发问题 loop: nop b loop
核心问题与解决方法
1. 错误读取只写寄存器
GPIO SET0是只写寄存器,仅用于设置指定GPIO引脚为高电平,读取该寄存器会触发总线异常,直接导致程序崩溃。代码中最后一行的ldr读取操作完全错误,应改为写入操作。
2. 使用错误宽度的指令访问寄存器
树莓派GPIO寄存器均为32位宽度,AArch64模式下必须使用32位W寄存器的LD/STR指令,而非64位X寄存器:
- 替换
str x1, [x0, #GPIO_SEL2]为str w1, [x0, #GPIO_SEL2],避免写入64位数据破坏相邻寄存器 - 替换读取SET0的
ldr x1, [x0, #GPIO_SET0]为写入操作str w1, [x0, #GPIO_SET0]
3. 基地址与启动配置确认
- GPIO基地址
0xFE200000是树莓派4B的正确值,无需修改 - 确保树莓派以64位模式启动:在SD卡
config.txt中添加arm_64bit=1 - 裸机编程建议简化启动分区文件,仅保留
bootcode.bin、start4.elf、config.txt和kernel8.img,避免系统残留文件干扰
4. 32位代码不稳定问题
大概率也是存在寄存器访问错误(如读取只写寄存器、使用错误指令宽度),建议参照64位代码的修正逻辑排查。
修正后的代码示例
.section ".text.boot" .global _start .equ GPIO_BASE, 0xFE200000 .equ GPIO_SET_21_OUT, 1 << 3 // GPIO21对应SEL2的第3位,设置为输出模式 .equ GPIO_SEL2, 0x08 .equ GPIO_SET0, 0x1C .equ GPIO_21, 1 << 21 _start: ldr x0, =GPIO_BASE ldr w1, =GPIO_SET_21_OUT // 用32位寄存器存储配置值 str w1, [x0, #GPIO_SEL2] // 32位写入SEL2寄存器,配置GPIO21为输出 ldr w1, =GPIO_21 // 用32位寄存器存储引脚掩码 str w1, [x0, #GPIO_SET0] // 写入SET0寄存器,将GPIO21置为高电平 loop: nop b loop
内容的提问来源于stack exchange,提问作者Luca Seelbach
相关产品推荐
相关产品推荐

