Tina SDK Linux Kernel 基本使用(实战篇:为开发板添加用户按键驱动支持)
本文是全志Tina-SDK Linux内核开发实战系列的第二篇,以 100ASK_T113s3-Pro开发板上的用户按键(USER KEY) 为例,手把手带你完成从硬件原理图分析、Pinctrl/GPIO子系统理解、设备树修改、内核模块配置到驱动加载验证的完整流程。学完本节,你将掌握为任何嵌入式Linux系统添加独立按键或GPIO输入设备的方法,并深入理解Pinctrl框架在设备树中的配置方式。
本文延续上一篇的超详细风格,所有内容全面展开,不偷工减料。
🎯 本任务的学习目标和重点掌握程度
| 分级 | 图标 | 含义 | 工作要求 |
|---|---|---|---|
| 必须掌握 | ⭐⭐⭐ | 独立作业,考核核心 | 能独立完成从原理图到设备树再到验证的全过程。 |
| 重点掌握 | ⭐⭐ | 了解原理,能按文档完成 | 理解Pinctrl基本概念,能看懂设备树中的引脚配置。 |
| 了解即可 | ⭐ | 知道概念,能复述 | 知道Pinctrl框架的分层,面试能说清楚。 |
优先掌握:必须掌握⭐⭐⭐的内容是你的核心竞争力;重点掌握⭐⭐能帮你快速定位问题;了解即可⭐部分可以后续再深入学习。
文章目录
-
- [Tina SDK Linux Kernel 基本使用(实战篇:为开发板添加用户按键驱动支持)](#Tina SDK Linux Kernel 基本使用(实战篇:为开发板添加用户按键驱动支持))
- [🎯 本任务的学习目标和重点掌握程度](#🎯 本任务的学习目标和重点掌握程度)
- 一、项目背景:我们要做什么?
- [二、理论基础:Pinctrl 子系统与 GPIO(⭐⭐ 重点掌握)](#二、理论基础:Pinctrl 子系统与 GPIO(⭐⭐ 重点掌握))
-
- [2.1 Pinctrl 系统架构](#2.1 Pinctrl 系统架构)
- [2.2 全志平台 Pinctrl 的重要节点](#2.2 全志平台 Pinctrl 的重要节点)
- [2.3 设备树中描述一个 GPIO 按键的格式](#2.3 设备树中描述一个 GPIO 按键的格式)
- [三、查看硬件原理图与数据手册(⭐⭐⭐ 必须掌握)](#三、查看硬件原理图与数据手册(⭐⭐⭐ 必须掌握))
-
- [3.1 从原理图找到按键引脚](#3.1 从原理图找到按键引脚)
- [3.2 确认引脚未被其他功能占用](#3.2 确认引脚未被其他功能占用)
- [四、修改设备树(⭐⭐⭐ 必须掌握)](#四、修改设备树(⭐⭐⭐ 必须掌握))
-
- [4.1 定位板级设备树文件](#4.1 定位板级设备树文件)
- [4.2 添加 gpio-keys 节点](#4.2 添加 gpio-keys 节点)
- [4.3 检查引脚冲突](#4.3 检查引脚冲突)
- [五、配置内核模块(⭐⭐⭐ 必须掌握)](#五、配置内核模块(⭐⭐⭐ 必须掌握))
-
- [5.1 进入 Tina 配置主界面](#5.1 进入 Tina 配置主界面)
- [5.2 找到 gpio-keys 模块](#5.2 找到 gpio-keys 模块)
- [5.3 保存配置](#5.3 保存配置)
- [六、编译、打包与烧录(⭐⭐ 重点掌握)](#六、编译、打包与烧录(⭐⭐ 重点掌握))
-
- [6.1 编译系统](#6.1 编译系统)
- [6.2 打包镜像](#6.2 打包镜像)
- [6.3 烧录镜像](#6.3 烧录镜像)
- [七、验证按键功能(⭐⭐⭐ 必须掌握)](#七、验证按键功能(⭐⭐⭐ 必须掌握))
-
- [7.1 检查模块是否已自动加载](#7.1 检查模块是否已自动加载)
- [7.2 手动加载模块](#7.2 手动加载模块)
- [7.3 查看输入设备列表](#7.3 查看输入设备列表)
- [7.4 测试按键事件](#7.4 测试按键事件)
- [7.5 设置开机自动加载](#7.5 设置开机自动加载)
- [八、进阶:理解 GPIO 配置的格式](#八、进阶:理解 GPIO 配置的格式)
- 九、常见问题排查
- 十、面试官提问环节
-
- [第1问:设备树中 `gpio-keys` 节点的 `compatible` 属性为什么是 `"gpio-keys"`?它如何匹配驱动?](#第1问:设备树中
gpio-keys节点的compatible属性为什么是"gpio-keys"?它如何匹配驱动?) - [第2问:Pinctrl 子系统中的 `default` 和 `sleep` 状态是什么意思?什么时候会切换?](#第2问:Pinctrl 子系统中的
default和sleep状态是什么意思?什么时候会切换?) - [第3问:为什么在 TinaSDK 中不能直接用 `make kernel_menuconfig` 来永久保存内核配置?应该怎么做?](#第3问:为什么在 TinaSDK 中不能直接用
make kernel_menuconfig来永久保存内核配置?应该怎么做?) - [第4问:在验证按键时,`cat /dev/input/event3` 出现乱码,这是否代表失败?怎样正确观察按键事件?](#第4问:在验证按键时,
cat /dev/input/event3出现乱码,这是否代表失败?怎样正确观察按键事件?)
- [第1问:设备树中 `gpio-keys` 节点的 `compatible` 属性为什么是 `"gpio-keys"`?它如何匹配驱动?](#第1问:设备树中
- 十一、总结
一、项目背景:我们要做什么?

在嵌入式Linux产品中,经常需要用到独立按键(如用户按键(USER KEY) 、复位键、音量键等)。Linux内核提供了 gpio-keys 驱动,可以将连接到GPIO的按键抽象为输入设备,向上层上报按键事件(如 KEY_VOLUMEDOWN、KEY_POWER等)。
本次任务:为 100ASK_T113s3-Pro开发板 上的 USER KEY 添加驱动支持,使得按下按键时系统能够检测到,并上报对应的键值(例如 KEY_VOLUMEDOWN 或自定义键值),后续应用可以通过 /dev/input/eventX 读取按键事件。
硬件连接分析 :通过查阅开发板原理图(100ASK_T113-Pro_Base-SCH_V1.2.pdf)和实物图,USER KEY 连接到 T113 芯片的 PB4 引脚,且按键按下时引脚为低电平(GPIO_ACTIVE_LOW)。我们需要在设备树中配置该引脚为中断模式,并关联对应的键值。
二、理论基础:Pinctrl 子系统与 GPIO(⭐⭐ 重点掌握)
在动手修改设备树之前,你需要先了解一些背景知识。Linux内核使用 Pinctrl(Pin Controller)子系统 统一管理所有引脚的复用和电气特性。
2.1 Pinctrl 系统架构
Pinctrl 框架分为四层(参考图示):

text
Device Tree 描述引脚的配置信息(设备树节点)
↓
Pinctrl Interface 上层驱动调用的接口(如 pinctrl_select_state)
↓
Pinctrl Framework Linux 内核核心框架(处理状态、复用和配置)
↓
Pinctrl Driver 芯片厂商实现的底层驱动(全志为 SUNXI Pinctrl)
Pinctrl 主要处理三类功能:
- Pin State(引脚状态) :支持
default(正常运行)、sleep(休眠)、idle(空闲)等不同状态,每种状态可以配置不同的引脚功能。 - Pin Mux(引脚复用) :一个引脚可以承担多种功能(如 GPIO、UART、I2C、SPI等),通过设置 Mux 值来切换。例如 PE0 可以作为
NCSI_PCLK、RGMII_RXD1、I2S1_MCLK、PWM0、SDC1_CLK、UART3_TX、TWI3_SCK等。 - Pin Config(引脚配置):配置驱动能力、上拉/下拉、输入/输出等电气特性。
2.2 全志平台 Pinctrl 的重要节点
在 T113 的设备树中,有两个 Pinctrl 节点:
| 节点名 | 寄存器地址 | 管理的引脚 | 注意 |
|---|---|---|---|
pio |
0x02000000 |
PL0 之前的引脚(包括 PA~PK) | 大部分外设引脚 |
r_pio |
0x07022000 |
PL0 及以后的引脚 | 部分低功耗、RTC相关引脚 |
我们使用的 PB4 属于 pio 管理。
2.3 设备树中描述一个 GPIO 按键的格式
gpio-keys 驱动要求我们在设备树中创建一个节点,格式如下:
c
gpio-keys {
compatible = "gpio-keys";
status = "okay";
user-key {
gpios = <&pio PB 4 GPIO_ACTIVE_LOW>;
linux,code = <114>; // KEY_VOLUMEDOWN 的键值
label = "user key";
debounce-interval = <10>; // 消抖时间(毫秒)
wakeup-source; // 可作为唤醒源
};
};
各属性含义:
compatible:匹配内核中的gpio-keys驱动。gpios:指定 GPIO 引脚、极性。linux,code:上报给 input 子系统的键值(<114>对应KEY_VOLUMEDOWN,也可以使用其他值如KEY_POWER、KEY_ENTER等)。label:此按键的描述名称。debounce-interval:简单消抖延迟(毫秒)。wakeup-source:系统休眠时该按键可以唤醒系统(可选).
三、查看硬件原理图与数据手册(⭐⭐⭐ 必须掌握)
3.1 从原理图找到按键引脚

查阅 100ASK_T113-Pro_Base-SCH_V1.2.pdf 中的按键部分(实物图标注 USER KEY 旁边有 SW2)。根据电路图,USER KEY 连接到 T113 的 PB4 引脚,按下时接地(低电平)。因此我们需要在设备树中配置 GPIO_ACTIVE_LOW。
3.2 确认引脚未被其他功能占用
查阅 T113-s3_datasheet_v1.6.pdf 第40页的 GPIO Multiplex Function 表,PB4 的默认功能是 LCDD-D8,但也可复用为其他功能。在我们的开发板上,PB4 未用于其他外设,因此可以直接作为 GPIO 使用。
排查冲突 :在添加前,建议用
grep -r "PB4" device/检查是否已有其他设备使用了该引脚。如果有冲突,需要解除冲突或更换引脚。
四、修改设备树(⭐⭐⭐ 必须掌握)
4.1 定位板级设备树文件
回顾之前的内容,板级设备树位于:
bash
# 切换到板级配置目录
ccconfigs
cd linux-5.4
vim board.dts
快捷命令说明 :执行 ccconfigs 可直接进入 device/config/chips/t113/configs/100ask/linux/ 目录,再执行 vim board.dts 编辑设备树。
4.2 添加 gpio-keys 节点
在 board.dts 文件的合适位置(通常在根节点下,远离其他大括号),添加如下内容:

c
&pio {
// ... 原有配置保持不变 ...
};
/* 新增按键设备节点 */
gpio-keys {
compatible = "gpio-keys";
status = "okay";
user-key {
gpios = <&pio PB 4 GPIO_ACTIVE_LOW>; // PB4, 低电平有效
linux,code = <114>; // KEY_VOLUMEDOWN
label = "user key";
debounce-interval = <10>;
wakeup-source;
};
/* 如果你还有别的按键,可以继续添加,例如:
power-key {
gpios = <&pio PE 1 GPIO_ACTIVE_LOW>;
linux,code = <116>; // KEY_POWER
label = "power key";
debounce-interval = <10>;
};
*/
};
4.3 检查引脚冲突
检查是否有其他设备也使用了 PB4。如果在 board.dts 或 sun8iw20p1.dtsi 中搜索到 PB4,且状态为 okay,则需要禁用或修改。通常情况下开发板默认未使用,可以直接添加。
验证方法 :在 board.dts 中搜索 PB4,若无其他节点使用,则安全。
五、配置内核模块(⭐⭐⭐ 必须掌握)
我们已经知道,在 TinaSDK 中,内核模块是通过 make menuconfig 而不是 make kernel_menuconfig 来永久选中的。
5.1 进入 Tina 配置主界面
bash
# 回到 SDK 根目录
cd ~/tina-d1-h
source build/envsetup.sh
lunch 4 # 选择 t113_100ask-tina
make menuconfig
5.2 找到 gpio-keys 模块
在菜单中依次进入:
text
Kernel modules --->
Input modules --->
[*] kmod-input-gpio-keys ---> GPIO key support

使用 Y 键选中该项(变为 <*>)。注意我们不需要 kmod-input-gpio-keys-polled(轮询方式),除非你的按键不适合中断方式。
5.3 保存配置
按 Esc 两次退出到主界面,选择 <Save>,确认保存为 .config,然后退出。
配置保存后,Tina 会在后续 make 时自动将 CONFIG_KEYBOARD_GPIO 等选项写入内核的 .config,并编译 gpio-keys.ko 模块。
六、编译、打包与烧录(⭐⭐ 重点掌握)
6.1 编译系统
bash
# 在 SDK 根目录执行
make -j$(nproc)
编译过程中,你会看到系统编译 gpio_keys.ko 模块的日志(类似下图):
text
CC [M] drivers/input/keyboard/gpio_keys.o
Building modules, stage 2.
CC [M] drivers/input/keyboard/gpio_keys.mod.o
LD [M] drivers/input/keyboard/gpio_keys.ko

6.2 打包镜像
bash
pack
打包成功后,最终镜像路径为:
text
/home/ubuntu/tina-d1-h/out/t113-100ask/tina_t113-100ask_uart3.img
使用 ls -lh 查看大小(约43MB)。
6.3 烧录镜像
将镜像复制到 Windows(通过共享文件夹或拖拽),使用 PhoenixSuit 线刷到开发板的 SPI NAND 中。
七、验证按键功能(⭐⭐⭐ 必须掌握)
烧录完成后,启动开发板,通过串口登录。
7.1 检查模块是否已自动加载
首先查看 lsmod,看 gpio_keys 是否已在模块列表中:
bash
lsmod | grep gpio_keys

如果未出现,需要手动安装或设置开机自动加载(后面会讲)。按照预期,Tina 默认不会自动加载 gpio_keys.ko,需要手动处理。
7.2 手动加载模块
进入模块目录并手动安装:
bash
cd /lib/modules/5.4.61/
insmod gpio_keys.ko
加载后,内核日志会显示:
text
input: gpio-keys as /devices/platform/gpio-keys/input/input3
此时新的输入设备节点 /dev/input/event3 已创建。

7.3 查看输入设备列表
bash
cat /proc/bus/input/devices
在输出中找到 gpio-keys 段,确认其对应的 H 行(Handlers)为 event2。
7.4 测试按键事件
bash
cat /dev/input/event2
然后按下开发板上的 USER KEY 按键,终端上应出现二进制乱码输出(表明有数据上报)。你也可以使用 hexdump 查看具体数据:
bash
hexdump /dev/input/event3
按下按键会输出类似:
text
0000000 0a9a 0000 3b2c 0008 0001 0072 0001 0000
...

其中 0001 0072 中的 0x72 就是十进制 114,即 KEY_VOLUMEDOWN。说明按键驱动工作正常。
7.5 设置开机自动加载
为了避免每次都要 insmod,可以修改系统启动脚本 /etc/init.d/rc.modules:
bash
vi /etc/init.d/rc.modules
在文件中仿照其他 .ko 的加载方式,添加一行:
bash
/sbin/insmod /lib/modules/5.4.61/gpio_keys.ko

保存后重启,系统即会自动加载 gpio_keys 模块。
八、进阶:理解 GPIO 配置的格式
在设备树中,gpios = <&pio PB 4 GPIO_ACTIVE_LOW>; 是一个简化的写法。更完整的 GPIO 属性在旧版设备树中可能使用 6 个数字:
text
gpios = <&pio bank pin mux pull drive data>;
对应关系:
| 字段 | 含义 | 示例值 |
|---|---|---|
&pio |
指向哪个 Pinctrl 控制器 | &pio 或 &r_pio |
| bank | GPIO 组 | PB 对应数值 1? 但设备树中通常直接写 PB,由宏转换 |
| pin | 组内序号 | 4 |
| mux | 复用模式,输入为 0, 输出为 1 | 0(输入) |
| pull | 上下拉,0=禁用,1=上拉,2=下拉 | 0 |
| drive | 驱动能力 | 通常 0xffffffff 表示默认 |
| data | 输出电平(仅输出时有效) | 0xffffffff 忽略 |
但在新的设备树中,gpios = <&pio PB 4 GPIO_ACTIVE_LOW>; 使用了 GPIO 子系统中定义的标志宏,更简洁。
九、常见问题排查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
lsmod 看不到 gpio_keys |
模块未编译或未安装 | 检查 make menuconfig 中是否选中,重新 make |
insmod 失败,提示 Unknown symbol |
依赖模块未加载 | 检查 input-core 是否已编译进内核 |
按键无反应,cat /dev/input/eventX 无输出 |
设备树配置错误或引脚冲突 | 检查 gpios 的引脚号和极性,确认无其他设备占用 PB4 |
| 按键上报乱码,但无正确键值 | 测试工具问题 | 用 hexdump 查看具体上报数据,确认 linux,code 值正确 |
十、面试官提问环节
第1问:设备树中 gpio-keys 节点的 compatible 属性为什么是 "gpio-keys"?它如何匹配驱动?
参考答案 :
compatible 属性是设备树与内核驱动匹配的关键字段。内核中的 gpio-keys 驱动通过 of_match_table 声明自己支持 "gpio-keys"。当内核解析设备树时,发现节点 compatible 为 "gpio-keys",就会调用该驱动的 probe 函数来初始化设备。
第2问:Pinctrl 子系统中的 default 和 sleep 状态是什么意思?什么时候会切换?
参考答案:
default:系统正常运行时的引脚状态(如 UART 的 TX/RX 引脚工作在 UART 模式)。sleep:系统进入休眠状态时的引脚状态,通常会将引脚设置为gpio_in或高阻态以节省功耗。
当系统通过pm_runtime或suspend/resume切换电源状态时,Pinctrl 框架会自动调用pinctrl_select_state来切换引脚配置。
第3问:为什么在 TinaSDK 中不能直接用 make kernel_menuconfig 来永久保存内核配置?应该怎么做?
参考答案 :
因为 TinaSDK 的内核配置是由多个片段动态合并而成的(generic/config-5.4 + board/linux/config-5.4)。直接修改 .config 会在下次 make 时被覆盖。正确做法是通过 make menuconfig(Tina主配置)中的 Kernel modules 选项来选择需要的内核模块,这些选项会被转化为内核配置片段,永久保存在 modules.mk 或 config-5.4 中。如果需要修改更深层次的内核选项,应修改板级 config-5.4 文件。
第4问:在验证按键时,cat /dev/input/event3 出现乱码,这是否代表失败?怎样正确观察按键事件?
参考答案 :
cat 将二进制数据直接打印到终端会造成乱码,这并不代表失败,反而说明有数据上报。要正确查看按键事件,可以使用 hexdump /dev/input/event3 查看十六进制数据,或者安装 evtest 工具:evtest /dev/input/event3,它会解析并显示每个事件的具体类型、键值和状态。
十一、总结
本文带你完整走了一遍为 T113 开发板添加用户按键驱动支持的流程,包括:
- 硬件分析:从原理图找到 USER KEY 的引脚 PB4 和极性。
- 设备树修改 :添加
gpio-keys节点,配置 GPIO 和键值。 - 内核模块配置 :在 Tina 的
make menuconfig中选中kmod-input-gpio-keys。 - 编译与烧录 :执行
make和pack,生成镜像并烧录。 - 验证与动手 :手动加载模块,用
hexdump或evtest测试,最后设为开机自动加载。
同时,你还深入了解了 Pinctrl 子系统的概念、设备树中 GPIO 配置的格式,以及 TinaSDK 中内核模块的配置机制。
| 学习阶段 | 任务 |
|---|---|
| ⭐ 入门 | 按本文步骤完成按键驱动添加,成功看到按键事件。 |
| ⭐⭐ 进阶 | 修改 linux,code 为其他值(如 KEY_POWER),并观察上层应用反应。 |
| ⭐⭐⭐ 高级 | 查阅 Pinctrl 驱动源码,理解引脚状态切换的实现细节。 |
整个系列完结撒花! 你从一个完全不懂嵌入式 Linux 的小白,到现在已经能够独立为开发板添加 LCD 触摸屏和按键驱动,理解和修改设备树,配置内核模块,烧录验证。你已经具备了嵌入式 Linux 驱动开发的初步能力。后续可以深入学习 I2C/SPI 子系统、设备树语法、内核调试等高级主题。
祝你职场顺利,考核满分!🎉
