树莓派3B在RaspbianOS下用汇编点亮GPIO12 LED遇段错误求助
问题:Raspberry Pi汇编代码点亮GPIO12 LED出现段错误
我正在完成课程作业,需要用C和汇编实现MasterMind游戏,先尝试用汇编点亮连接到GPIO12的LED,但运行时出现段错误。以下是我的代码和编译步骤:
汇编代码(testled.s)
.section .init .globl _start _start: ldr r0,=0x3F200000 // Base adress from GPIO // Set the GPIO pin 12 as output mov r1,#1 lsl r1,#6 str r1,[r0,#0x4] // 0x3F200004 is the GPSEL1 Register, where you can choose the function for the pin // Prepare the number used for turning the LED off and on mov r1,#1 lsl r1,#12 // Main loop, loops all the letters (bl means branch with saving the current physical adress in the link register (lr) so you can jump back to it) loop$: bl morseS bl morseO bl morseS b loop$ morseS: mov r6,lr // Move the lr adress to r6 so you dont override it (would be better if implemented with stack) bl dotBlink bl dotBlink bl dotBlink bl delayMedium mov lr,r6 // Move the adress back into the link register bx lr morseO: mov r6,lr bl dashBlink bl dashBlink bl dashBlink bl delayMedium mov lr,r6 bx lr dotBlink: mov r7,lr str r1,[r0,#0x1C] // This turns the led on (According to the BCM2835 documentation the Register found at 0x3F20001C is GPSET0, so you can turn on the GPIO Pin 12 there bl delayShort str r1,[r0,#0x28] // This is GPCLR0, so used for turning the pin off again bl delayShort mov lr,r7 bx lr dashBlink: mov r7,lr str r1,[r0,#0x1C] bl delayLong str r1,[r0,#0x28] bl delayShort mov lr,r7 bx lr delayShort: ldr r4,=0x3f003004 // Address for the timer of the Raspberry Pi ldr r4,[r4] // Load the value from the timer ldr r5,=0x30D40 // 200'000 micro seconds in hexadecimal delayLoop1$: ldr r3,=0x3F003004 ldr r3,[r3] // Load new timer value sub r3,r3,r4 // Save time passed since the beginning of subroutine in r3 cmp r3,r5 // Compare blt delayLoop1$ // If difference is smaller than 200ms go to the delay loop again bx lr // If time has passed return from function delayMedium: ldr r4,=0x3f003004 ldr r4,[r4] ldr r5,=0x61A80 delayLoop3$: ldr r3,=0x3F003004 ldr r3,[r3] sub r3,r3,r4 cmp r3,r5 blt delayLoop3$ bx lr delayLong: ldr r4,=0x3f003004 ldr r4,[r4] ldr r5,=0x927C0 delayLoop2$: ldr r3,=0x3F003004 ldr r3,[r3] sub r3,r3,r4 cmp r3,r5 blt delayLoop2$ bx lr
编译运行命令
- 汇编:
as -o testled.o testled.s - 链接:
ld -o testled testled.o - 运行:
sudo ./testled
运行后出现段错误,求解决方法。
解决方案
段错误的核心原因是用户空间直接访问物理地址:Raspberry Pi的Linux系统启用了内存地址虚拟化,用户程序无法直接读写硬件物理地址,必须通过内存映射或裸机运行方式绕过限制。以下是两种可行修复方案:
方案1:用户空间映射物理内存(C+汇编结合)
用C代码完成物理内存到用户空间的映射,再调用汇编函数操作GPIO,避免直接访问物理地址触发内存保护:
- 编写C启动文件(main.c):
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #define GPIO_BASE 0x3F200000 #define TIMER_BASE 0x3F003004 #define BLOCK_SIZE (4*1024) volatile unsigned int *gpio; volatile unsigned int *timer; void led_blink(); int main() { int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mem_fd < 0) { perror("Failed to open /dev/mem"); exit(1); } // 映射GPIO寄存器到用户空间 void *gpio_map = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE); if (gpio_map == MAP_FAILED) { perror("Failed to mmap GPIO"); close(mem_fd); exit(1); } gpio = (volatile unsigned int *)gpio_map; // 映射定时器寄存器到用户空间 void *timer_map = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, TIMER_BASE & ~(BLOCK_SIZE-1)); if (timer_map == MAP_FAILED) { perror("Failed to mmap Timer"); munmap(gpio_map, BLOCK_SIZE); close(mem_fd); exit(1); } timer = (volatile unsigned int *)((char*)timer_map + (TIMER_BASE & (BLOCK_SIZE-1))); led_blink(); // 释放映射资源 munmap(gpio_map, BLOCK_SIZE); munmap(timer_map, BLOCK_SIZE); close(mem_fd); return 0; }
- 修改汇编代码(testled.s),去掉
_start入口,改为led_blink函数,通过全局变量访问寄存器:
.globl led_blink led_blink: // 将GPIO基地址存入r0 ldr r0, =gpio ldr r0, [r0] // 设置GPIO12为输出模式 mov r1, #1 lsl r1, #6 str r1, [r0, #0x4] mov r1, #1 lsl r1, #12 loop$: bl morseS bl morseO bl morseS b loop$ morseS: mov r6, lr bl dotBlink bl dotBlink bl dotBlink bl delayMedium mov lr, r6 bx lr morseO: mov r6, lr bl dashBlink bl dashBlink bl dashBlink bl delayMedium mov lr, r6 bx lr dotBlink: mov r7, lr str r1, [r0, #0x1C] bl delayShort str r1, [r0, #0x28] bl delayShort mov lr, r7 bx lr dashBlink: mov r7, lr str r1, [r0, #0x1C] bl delayLong str r1, [r0, #0x28] bl delayShort mov lr, r7 bx lr delayShort: ldr r4, =timer ldr r4, [r4] ldr r5, =0x30D40 // 200ms delayLoop1$: ldr r3, =timer ldr r3, [r3] sub r3, r3, r4 cmp r3, r5 blt delayLoop1$ bx lr delayMedium: ldr r4, =timer ldr r4, [r4] ldr r5, =0x61A80 // 400ms delayLoop3$: ldr r3, =timer ldr r3, [r3] sub r3, r3, r4 cmp r3, r5 blt delayLoop3$ bx lr delayLong: ldr r4, =timer ldr r4, [r4] ldr r5, =0x927C0 // 600ms delayLoop2$: ldr r3, =timer ldr r3, [r3] sub r3, r3, r4 cmp r3, r5 blt delayLoop2$ bx lr
- 编译链接命令:
gcc -c main.c -o main.o as -o testled.o testled.s gcc -o testled main.o testled.o sudo ./testled
方案2:裸机运行汇编代码
如果想纯汇编运行,需要编译成裸机镜像,通过SD卡引导Raspberry Pi脱离Linux系统运行:
- 编写链接脚本(linker.ld):
SECTIONS { .init 0x8000 : { *(.init) } .text : { *(.text) } }
- 编译链接成裸机镜像:
as -o testled.o testled.s ld -T linker.ld -o testled.elf testled.o objcopy testled.elf -O binary testled.bin
- 将
testled.bin重命名为kernel.img,替换SD卡中的对应文件,重启Raspberry Pi即可运行。
内容的提问来源于stack exchange,提问作者Aditya Shibu
相关产品推荐
相关产品推荐

