【Linux】Orangepi GPIO驱动开发实战:从入门到精通

张开发
2026/4/10 17:32:44 15 分钟阅读

分享文章

【Linux】Orangepi GPIO驱动开发实战:从入门到精通
1. 初识Orangepi GPIO开发第一次拿到Orangepi开发板时我和大多数新手一样兴奋又迷茫。这块巴掌大的小板子居然能跑完整的Linux系统还能通过GPIO控制各种外设这可比玩单片机刺激多了不过当我真正开始GPIO开发时才发现事情没那么简单。GPIOGeneral Purpose Input/Output是通用输入输出接口的简称就像开发板伸出的无数个小触手我们可以通过程序控制它们输出高电平或低电平也可以读取外部设备传来的信号。Orangepi的GPIO引脚通常以字母加数字的方式命名比如PA15、PC7等这种命名方式背后其实有一套计算规则。说到开发环境我强烈推荐VSCode配合Remote SSH插件。这个组合让我能在Windows电脑上舒服地编写代码而实际编译运行都在Orangepi上完成。记得第一次用这个组合时那种本地编辑-远程运行的丝滑体验让我感动得差点哭出来。2. wiringPi库快速上手2.1 安装与配置wiringPi是Orangepi官方推荐的GPIO控制库用起来相当顺手。安装过程比我想象的简单git clone https://github.com/orangepi-xunlong/wiringOP.git cd wiringOP ./build运行build脚本时会让你选择开发板型号我的Orangepi PC Plus选3就行。安装完成后一定要试试gpio readall命令这个命令会显示所有GPIO引脚的状态信息相当于你的GPIO地图。这里有个小坑要注意wiringPi使用的引脚编号和物理引脚编号不是一回事比如物理引脚第12号对应的是wiringPi编号0。我第一次就搞混了结果LED死活不亮折腾了半天才发现问题。2.2 基础GPIO控制用命令行控制GPIO特别适合快速测试gpio mode 0 out # 设置wPi编号0的引脚为输出模式 gpio write 0 1 # 输出高电平 gpio blink 0 500 # 让LED以500ms间隔闪烁不过真正开发时还是得用C语言。下面这个经典的点灯程序我建议每个新手都亲手敲一遍#include stdio.h #include wiringPi.h #define LED 0 // 使用wPi编号0的引脚 int main(void) { wiringPiSetup(); pinMode(LED, OUTPUT); while(1) { digitalWrite(LED, HIGH); delay(500); digitalWrite(LED, LOW); delay(500); } return 0; }编译这个程序时我踩了个大坑直接gcc led.c -o led会报错正确的编译命令应该是gcc led.c -L/usr/local/lib -lwiringPi -lpthread -lwiringPiDev -lm -lcrypt -lrt -o led这一长串参数看着吓人其实就是在告诉编译器请链接wiringPi库及其依赖的其他库。3. 深入sysfs GPIO驱动3.1 板载LED控制Orangepi开发板上通常自带两个LED电源灯和状态灯。有趣的是这些LED也可以当普通GPIO使用。通过sysfs接口就是/sys目录下的那些文件我们可以直接控制它们# 将状态灯从系统控制中释放 echo none /sys/class/leds/orangepi:red:status/trigger # 现在可以像普通LED一样控制了 echo 1 /sys/class/leds/orangepi:red:status/brightness # 点亮 echo 0 /sys/class/leds/orangepi:red:status/brightness # 熄灭 # 用完后记得恢复原状 echo cpu0 /sys/class/leds/orangepi:red:status/trigger3.2 通用GPIO控制对于普通的GPIO引脚控制流程稍微复杂些但原理相通。整个过程就像是在和内核对话告诉内核我要用GPIO12export设置方向这个引脚要当输出用direction控制电平现在输出高电平value用完归还GPIO12还给你unexport对应的命令行操作echo 12 /sys/class/gpio/export # 申请使用GPIO12 echo out /sys/class/gpio/gpio12/direction # 设为输出 echo 1 /sys/class/gpio/gpio12/value # 输出高电平 echo 0 /sys/class/gpio/gpio12/value # 输出低电平 echo 12 /sys/class/gpio/unexport # 释放GPIO12用C语言实现的话代码会稍微复杂些但逻辑更清晰#include fcntl.h #include stdio.h #include unistd.h #define GPIO 12 int main() { int fd; char buf[64]; // 申请GPIO fd open(/sys/class/gpio/export, O_WRONLY); write(fd, 12, 2); close(fd); // 设置方向 sprintf(buf, /sys/class/gpio/gpio%d/direction, GPIO); fd open(buf, O_WRONLY); write(fd, out, 3); close(fd); // 控制电平 sprintf(buf, /sys/class/gpio/gpio%d/value, GPIO); fd open(buf, O_WRONLY); write(fd, 1, 1); sleep(1); write(fd, 0, 1); close(fd); // 释放GPIO fd open(/sys/class/gpio/unexport, O_WRONLY); write(fd, 12, 2); close(fd); return 0; }4. 编写底层GPIO驱动当你需要更高性能或更精细的控制时就需要自己写内核驱动了。虽然这个过程比较复杂但理解其原理对深入Linux开发很有帮助。4.1 驱动开发基础Linux内核已经提供了完善的GPIO子系统我们要做的主要是申请GPIO资源设置输入/输出方向实现读写操作注册设备文件一个最简单的GPIO驱动框架长这样#include linux/module.h #include linux/gpio.h #define GPIO_NUM 12 static int __init gpio_driver_init(void) { if (!gpio_is_valid(GPIO_NUM)) { printk(Invalid GPIO\n); return -ENODEV; } if (gpio_request(GPIO_NUM, my_gpio) 0) { printk(GPIO request failed\n); return -EBUSY; } gpio_direction_output(GPIO_NUM, 0); gpio_set_value(GPIO_NUM, 1); return 0; } static void __exit gpio_driver_exit(void) { gpio_set_value(GPIO_NUM, 0); gpio_free(GPIO_NUM); } module_init(gpio_driver_init); module_exit(gpio_driver_exit); MODULE_LICENSE(GPL);4.2 进阶技巧在实际项目中你可能还需要实现文件操作接口file_operations添加设备树支持处理中断实现IOCTL控制比如添加文件操作后用户空间程序就能通过设备文件来控制GPIO了static struct file_operations gpio_fops { .owner THIS_MODULE, .open gpio_open, .release gpio_release, .read gpio_read, .write gpio_write, .unlocked_ioctl gpio_ioctl };驱动开发最麻烦的部分其实是编译和加载。你需要准备内核头文件编写Makefile然后用insmod加载模块。第一次成功加载自己写的驱动时那种成就感绝对值得你折腾这么久5. 实战经验与避坑指南经过几个项目的磨练我总结了一些宝贵经验引脚复用问题Orangepi的很多引脚都有多种功能比如UART、SPI等使用前务必检查引脚复用配置否则怎么调代码都没用。权限问题无论是wiringPi还是sysfs操作GPIO通常需要root权限。在生产环境中可以考虑通过udev规则给普通用户授权。性能考量sysfs方式的延迟在毫秒级如果要做精确时序控制比如WS2812彩灯最好用内核驱动或硬件PWM。电气特性别忘了GPIO有电压和电流限制驱动大功率设备时一定要加合适的驱动电路我的第一个Orangepi就是这么烧掉的...中断处理当GPIO用于输入时轮询效率太低。内核驱动可以注册中断处理函数事件触发时立即响应。最后给个实用建议在/home目录下建个gpio文件夹把常用的测试脚本、示例代码都放里面。这样下次要用时就不用从头开始折腾了。

更多文章