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

树莓派4 GPIO编程出现Segmentation Fault问题求助

树莓派4 GPIO配置段错误问题排查与修复

问题原因

  • 直接访问物理地址无权限:用户空间程序不能直接操作物理内存地址(0xfe200000),必须通过mmap将GPIO物理内存区域映射到进程的虚拟地址空间后才能合法访问,直接解引用物理地址指针会触发段错误。
  • 函数语法错误:Init_GPIO函数的大括号不匹配,后续的上下拉配置代码跑到了函数体外,属于语法错误,会导致编译或运行异常。
  • 位运算逻辑错误:代码中(valueinput * mask)是错误写法,valueinput本身已经是移位后的位设置值,不需要乘以mask,直接使用valueinput即可。

修复方案

1. 实现物理内存映射

通过mmap从/dev/mem设备中映射GPIO寄存器对应的物理内存区域,步骤如下:

  • 打开/dev/mem设备文件(需要root权限)
  • 映射足够覆盖GPIO寄存器的内存区域(4KB足够)
  • 基于映射后的虚拟地址初始化各个寄存器指针
  • 操作完成后释放映射并关闭文件

2. 修正函数语法错误

将上下拉配置代码移到Init_GPIO函数内部,修正大括号匹配问题。

3. 修正位运算逻辑

把(valueinput * mask)改为valueinput,避免无效的位运算。

完整修复代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>

#define GPIO_BASEADDRESS 0xfe200000
#define GPIO_MAP_SIZE    4096  // 映射4KB足够覆盖GPIO寄存器区域

volatile uint32_t *gpio = NULL;
volatile uint32_t *GPFSEL1 = NULL;
volatile uint32_t *GPFSEL2 = NULL;
volatile uint32_t *GPSET0 = NULL;
volatile uint32_t *GPCLR0 = NULL;
volatile uint32_t *GPLEV0 = NULL;
volatile uint32_t *GPIO_PUP_PDN_CNTRL_REG1 = NULL;

// 初始化GPIO内存映射
int gpio_map() {
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        perror("Failed to open /dev/mem");
        return -1;
    }

    gpio = (volatile uint32_t*)mmap(NULL, GPIO_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_BASEADDRESS);
    close(fd);

    if (gpio == MAP_FAILED) {
        perror("mmap failed");
        return -1;
    }

    // 基于映射后的虚拟地址初始化寄存器指针
    GPFSEL1 = gpio + 0x04/4;
    GPFSEL2 = gpio + 0x08/4;
    GPSET0 = gpio + 0x1c/4;
    GPCLR0 = gpio + 0x28/4;
    GPLEV0 = gpio + 0x34/4;
    GPIO_PUP_PDN_CNTRL_REG1 = gpio + 0xe8/4;

    return 0;
}

// 释放GPIO内存映射
void gpio_unmap() {
    if (gpio != NULL && gpio != MAP_FAILED) {
        munmap((void*)gpio, GPIO_MAP_SIZE);
        gpio = NULL;
    }
}

static void Init_GPIO(int pin_number, char type, char high_low, char pullup_pulldown) {
    int valueinput = 0b000 << ((pin_number - 10) * 3);
    int valueoutput = 0b001 << ((pin_number - 10) * 3);
    int mask = 0b111 << ((pin_number - 10) * 3);

    if (pin_number <= 19) {
        if (type == 'i') {
            *GPFSEL1 = (*GPFSEL1 & ~mask) | valueinput;
        } else if (type == 'o') {
            *GPFSEL1 = (*GPFSEL1 & ~mask) | valueoutput;
        } else {
            printf("输入类型未指定,请使用'o'或'i'\n");
        }
    } else if (pin_number > 19 && pin_number <= 29) {
        mask = 0b111 << ((pin_number - 20) * 3);
        valueinput = 0b000 << ((pin_number - 20) * 3);
        valueoutput = 0b001 << ((pin_number - 20) * 3);
        if (type == 'i') {
            *GPFSEL2 = (*GPFSEL2 & ~mask) | valueinput;
        } else if (type == 'o') {
            *GPFSEL2 = (*GPFSEL2 & ~mask) | valueoutput;
        } else {
            printf("输入类型未指定,请使用'o'或'i'\n");
        }
    }

    if (high_low == 'h') {
        *GPSET0 = 1 << pin_number;
    } else if (high_low == 'l') {
        *GPCLR0 = 1 << pin_number;
    } else {
        printf("高低电平未指定,请使用'h'或'l'\n");
    }

    // 上下拉配置修正:适配引脚范围
    int pupd_mask, pupd_val;
    if (pin_number >= 16 && pin_number <= 31) {
        pupd_mask = 0b11 << ((pin_number - 16) * 2);
        if (pullup_pulldown == 'p') {
            pupd_val = 0b01 << ((pin_number - 16) * 2);
            *GPIO_PUP_PDN_CNTRL_REG1 = (*GPIO_PUP_PDN_CNTRL_REG1 & ~pupd_mask) | pupd_val;
        } else if (pullup_pulldown == 'n') {
            pupd_val = 0b10 << ((pin_number - 16) * 2);
            *GPIO_PUP_PDN_CNTRL_REG1 = (*GPIO_PUP_PDN_CNTRL_REG1 & ~pupd_mask) | pupd_val;
        } else {
            printf("上下拉未指定,请使用'p'或'n'\n");
        }
    } else {
        printf("当前引脚范围不支持此上下拉配置逻辑\n");
    }
}

int main() {
    // 先完成内存映射
    if (gpio_map() != 0) {
        exit(EXIT_FAILURE);
    }

    printf("GPIO映射虚拟地址: %p\n", gpio);
    Init_GPIO(18, 'i', 'l', 'p');

    // 手动配置GPIO18为输入的验证代码
    volatile uint32_t valueinput = 0b000 << ((18 - 10) * 3);
    volatile uint32_t mask = 0b111 << ((18 - 10) * 3);
    *GPFSEL1 = (*GPFSEL1 & ~mask) | valueinput;

    // 释放映射
    gpio_unmap();
    return 0;
}

编译与运行说明

  • 编译命令:gcc gpio_config.c -o gpio_config
  • 必须以root权限运行:sudo ./gpio_config(访问/dev/mem需要root权限)

内容的提问来源于stack exchange,提问作者Gambling Math

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 04:15:42