树莓派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
相关产品推荐
相关产品推荐

