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

树莓派5 GPIO引脚访问失败:gpio_request()调用报错求助

树莓派GPIO内核模块gpio_request()报错排查方案

我在树莓派上开发一款用于访问GPIO引脚的内核模块,调用gpio_request()函数时持续报错,更换多个引脚编号后问题仍未解决,内核日志会打印对应的错误信息,模块代码如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johannes 4 GNU/Linux");
MODULE_DESCRIPTION("A simple gpio driver for setting a LED and reading a button");

static dev_t my_device_nr;
static struct class *my_class;
static struct cdev my_device;

#define DRIVER_NAME "my_gpio_driver"
#define DRIVER_CLASS "MyModuleClass"

static ssize_t driver_read(struct file *File, char *user_buffer, size_t count, loff_t *offs)
{
    int to_copy, not_copied, delta;
    char tmp[3] = " \n";
    to_copy = min(count, sizeof(tmp));
    printk("Value of button: %d\n", gpio_get_value(17));
    tmp[0] = gpio_get_value(17) + '0';
    not_copied = copy_to_user(user_buffer, &tmp, to_copy);
    delta = to_copy - not_copied;
    return delta;
}

static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs)
{
    int to_copy, not_copied, delta;
    char value;
    to_copy = min(count, sizeof(value));
    not_copied = copy_from_user(&value, user_buffer, to_copy);
    switch (value) {
        case '0':
            gpio_set_value(4, 0);
            break;
        case '1':
            gpio_set_value(4, 1);
            break;
        default:
            printk("Invalid Input!\n");
            break;
    }
    delta = to_copy - not_copied;

    return delta;
}

static int driver_open(struct inode *device_file, struct file *instance)
{
    printk("dev_nr - open was called!\n");
    return 0;
}

static int driver_close(struct inode *device_file, struct file *instance)
{
    printk("dev_nr - close was called!\n");
    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = driver_open,
    .release = driver_close,
    .read = driver_read,
    .write = driver_write
};

static int __init ModuleInit(void)
{
    printk("Hello, Kernel!\n");

    if (alloc_chrdev_region(&my_device_nr, 0, 1, DRIVER_NAME) < 0) {
        printk("Device Nr. could not be allocated!\n");
        return -1;
    }
    printk("read_write - Device Nr. Major: %d, Minor: %d was registered!\n", my_device_nr >> 20, my_device_nr && 0xfffff);

    if ((my_class = class_create(DRIVER_CLASS)) == NULL) {
        printk("Device class can not be created!\n");
        goto ClassError;
    }

    if (device_create(my_class, NULL, my_device_nr, NULL, DRIVER_NAME) == NULL) {
        printk("Can not create device file!\n");
        goto FileError;
    }

    cdev_init(&my_device, &fops);

    if (cdev_add(&my_device, my_device_nr, 1) == -1) {
        printk("Registering of device to kernel failed!\n");
        goto AddError;
    }

    /* GPIO 4 init */
    int ret;
    ret = gpio_request(4, "led_gpio");
    if (ret) {
        printk(KERN_ERR "Failed to request GPIO pin\n");
        return ret;
    }

    /* Set GPIO 4 direction */
    ret = gpio_direction_output(4, 0);
    if (ret) {
        printk(KERN_ERR "Failed to set GPIO pin direction\n");
        gpio_free(4);
        return ret;
    }

    /* GPIO 17 init */
    if (gpio_request(17, "rpi-gpio-17")) {
        printk("Can not allocate GPIO 17\n");
        goto Gpio4Error;
    }

    /* Set GPIO 17 direction */
    if (gpio_direction_input(17)) {
        printk("Can not set GPIO 17 to input!\n");
        goto Gpio17Error;
    }

    return 0;
Gpio17Error:
    gpio_free(17);
Gpio4Error:
    gpio_free(4);
AddError:
    device_destroy(my_class, my_device_nr);
FileError:
    class_destroy(my_class);
ClassError:
    unregister_chrdev_region(my_device_nr, 1);
    return -1;
}

static void __exit ModuleExit(void) {
    gpio_set_value(4, 0);
    gpio_free(17);
    gpio_free(4);
    cdev_del(&my_device);
    device_destroy(my_class, my_device_nr);
    class_destroy(my_class);
    unregister_chrdev_region(my_device_nr, 1);
    printk("Goodbye, Kernel\n");
}

module_init(ModuleInit);
module_exit(ModuleExit);

排查步骤

  • 确认引脚编号体系:树莓派内核使用BCM GPIO编号,而非物理引脚编号。比如物理引脚11对应BCM 17,物理引脚7对应BCM 4,确保你请求的是正确的BCM编号。
  • 检查引脚占用情况:执行cat /sys/kernel/debug/gpio查看系统中已被占用的GPIO引脚,确认目标引脚未被其他驱动、sysfs导出或进程占用。
  • 打印具体错误码:修改代码中gpio_request()的错误日志,输出具体返回值:
    ret = gpio_request(4, "led_gpio");
    if (ret) {
        printk(KERN_ERR "Failed to request GPIO 4, error code: %d\n", ret);
        goto AddError;
    }
    
    常见错误码对应问题:
    • -EBUSY:引脚已被占用
    • -EINVAL:引脚编号无效(超出树莓派GPIO范围)
    • -ENOMEM:内核内存分配失败
  • 修复资源清理漏洞:当前代码中GPIO4请求失败时直接return ret,未清理已分配的字符设备、类等资源,会导致泄漏。应改为跳转到AddError标签执行清理逻辑。
  • 验证内核GPIO驱动:执行zcat /proc/config.gz | grep CONFIG_GPIO_BCM2835,确认输出为CONFIG_GPIO_BCM2835=y或=m,确保BCM2835 GPIO驱动已启用。
  • 确保root权限:加载模块时必须使用root权限(sudo insmod xxx.ko或sudo modprobe xxx),普通用户无法操作内核GPIO资源。

内容的提问来源于stack exchange,提问作者Lochan R

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 14:40:54