☰
嵌入式 Linux 驱动开发学习笔记(三)设备树、GPIO子系统、中断与外设驱动实战
2026/10/15 6:06:33 网站建设 项目流程

一、传统驱动方式存在的问题

在前面的 LED 驱动中,通常采用直接操作寄存器的方法。

例如:

GPIO寄存器地址

MUX寄存器地址

PAD寄存器地址

DATA寄存器地址

DIR寄存器地址

驱动程序中保存:

硬件地址

GPIO编号

寄存器偏移

结构如下:

应用程序

↓

驱动程序

↓

硬编码硬件地址

↓

寄存器

↓

LED

这种方式虽然能够实现功能,但是存在明显缺点。


1. 移植困难

不同芯片:

GPIO地址不同

寄存器不同

引脚不同

导致:

修改驱动源码

重新编译

重新测试


2. 驱动复用性差

例如:

同一个 LED 驱动:

运行在:

i.MX6ULL

RK3568

STM32MP1

由于硬件资源不同,需要修改代码。


因此 Linux 引入:

设备与驱动分离思想。


二、Linux设备与驱动分离思想

Linux驱动设计核心思想:

设备描述硬件

驱动负责控制

结构:

设备树

↓

描述硬件资源

↓

驱动程序

↓

操作硬件

这样:

硬件变化:

修改设备树即可。

驱动代码:

无需大量修改。


三、Device Tree(设备树)

3.1 什么是设备树?

设备树:

英文:

Device Tree

简称:

DT

它是一种描述硬件资源的数据结构。

Linux启动时:

U-Boot

↓

加载dtb

↓

Linux Kernel解析

↓

创建设备对象


3.2 设备树文件

设备树源码:

arch/arm/boot/dts

主要文件:

文件

作用

dts

设备树源码

dtsi

公共描述文件

dtb

编译后的二进制文件

编译:

make dtbs

生成:

xxx.dtb


四、设备树基本语法

设备树采用树形结构:

node {

property;

};

例如:

led {

compatible = "gpio-led";

status = "okay";

};


五、设备树重要属性

1. compatible

作用:

设备和驱动匹配。

设备树:

compatible="pute-led";

驱动:

of_match_table

通过字符串匹配。


2. status

控制设备状态。

开启:

status="okay";

关闭:

status="disabled";


3. reg

描述寄存器资源。

格式:

地址 + 长度

例如:

reg=<0x0209c000 0x04>;

表示:

寄存器地址

+

占用空间


六、pinctrl子系统

在实际硬件中,一个GPIO不仅仅是编号。

还涉及:

引脚复用

上拉下拉

驱动能力

电气属性

这些由:

pinctrl子系统管理。


pinctrl作用

简单理解:

pinctrl

负责:

这个引脚是什么功能

例如:

复用成GPIO

配置上下拉


设备树:

pinctrl_led {

fsl,pins =

<

MX6UL_PAD_GPIO1_IO03__GPIO1_IO03

>;

};

表示:

将某个引脚配置为 GPIO。


七、GPIO子系统

pinctrl负责:

引脚配置

GPIO负责:

输入输出控制

关系:

pinctrl

↓

GPIO功能

↓

GPIO子系统

↓

控制电平


GPIO常用接口:

获取GPIO

of_get_named_gpio()


申请GPIO

gpio_request()


设置输出

gpio_direction_output()


输出电平

gpio_set_value()


读取状态

gpio_get_value()


八、设备树LED驱动

传统方式:

驱动知道GPIO地址

设备树方式:

设备树提供GPIO信息

↓

驱动解析

↓

控制GPIO


完整流程:

设备树

↓

获取GPIO编号

↓

申请GPIO

↓

设置输出

↓

控制LED


应用:

echo 1 > /dev/led

最终:

用户程序

↓

write()

↓

LED驱动

↓

GPIO

↓

LED


九、Linux中断机制

除了控制LED,嵌入式设备大量使用中断。

例如:

按键

触摸屏

传感器

通信设备


为什么需要中断?

传统查询方式:

CPU不停检查设备状态

缺点:

浪费CPU

实时性差

中断方式:

设备发生事件

↓

通知CPU

↓

执行中断处理函数


十、Linux中断处理流程

完整流程:

硬件产生中断

↓

CPU响应

↓

进入中断处理函数

↓

执行任务

↓

返回


十一、中断相关概念

中断号

每一个中断都有编号:

IRQ Number

Linux通过中断号找到对应处理函数。


中断服务函数

格式:

irqreturn_t handler(

int irq,

void *dev_id

)

作用:

处理中断事件。


十二、中断注册

Linux提供:

request_irq()

注册中断。

例如:

request_irq(

irq,

handler,

flags,

name,

dev

);

参数:

参数

作用

irq

中断号

handler

处理函数

flags

触发方式

name

名称


十三、中断释放

卸载驱动时:

需要:

free_irq()

释放资源。


十四、按键中断驱动

按键驱动是学习中断最典型案例。

工作流程:

按键按下

↓

GPIO电平变化

↓

产生中断

↓

执行中断函数

↓

记录事件

↓

用户读取


结构:

按键

|

GPIO输入

|

GPIO中断

|

Linux IRQ系统

|

驱动程序

|

用户程序


十五、设备树描述按键

设备树提供:

GPIO编号

中断信息

触发方式

例如:

key {

compatible="gpio-key";

interrupt-parent=<gpio>;

interrupts=<xxx>;

};


驱动:

读取:

GPIO资源

中断号

触发方式

然后:

request_irq()

注册。


十六、驱动开发完整流程

到这里,一个完整 Linux 外设驱动流程已经形成。


第一步:描述硬件

设备树:

描述GPIO

描述中断

描述资源


第二步:驱动匹配

通过:

compatible

找到驱动。


第三步:解析资源

驱动:

读取设备树

获取GPIO

获取IRQ


第四步:初始化设备

例如:

LED:

GPIO输出

按键:

GPIO输入

注册中断


第五步:提供用户接口

例如:

字符设备

misc设备

sysfs

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询