1. 项目概述:闲置串口引脚变身实用小夜灯
作为一名长期折腾Android设备的硬件爱好者,我经常发现开发板上那些未被充分利用的串口引脚。以高通DragonBoard为例,UART2_TX/RX这类引脚在大多数用户手中完全处于闲置状态。这让我萌生了一个想法:能否将这些"摸鱼"的引脚改造成实用的GPIO接口,比如驱动一个小夜灯?
这个改造的核心价值在于:
- 资源再利用:让闲置硬件发挥实际功能
- 学习价值:通过实践理解Linux设备树和引脚复用的核心机制
- 实用意义:获得一个可编程控制的LED照明源
整个过程涉及三个关键技术点:
- 引脚功能重映射(从UART到GPIO)
- 设备树(Device Tree)配置
- 内核驱动交互
特别说明:本文示例基于高通平台(msm8998),但原理适用于所有使用设备树的ARM平台。不同芯片厂商的主要区别在于寄存器命名和引脚编号规则。
2. 硬件准备与原理分析
2.1 所需材料清单
进行这个改造需要准备以下硬件:
- 支持解锁bootloader的Android设备(建议使用开发板如DragonBoard)
- USB转TTL串口模块(用于调试)
- 5mm LED灯(建议红色,压降约1.8V)
- 220Ω限流电阻
- 杜邦线和焊接工具
2.2 电路连接原理
典型的共阳极LED连接方式:
code复制3.3V电源 → 220Ω电阻 → LED阳极 → LED阴极 → GPIO引脚
当GPIO输出低电平时形成回路,LED点亮。电阻值计算:
code复制R = (Vcc - Vled) / Iled
以3.3V电源、1.8V LED、5mA电流为例:
(3.3 - 1.8)/0.005 = 300Ω → 选用220Ω(更安全)
2.3 引脚复用原理
现代SoC的引脚通常具有多种功能模式。以高通为例,每个引脚都有:
- 一个功能选择寄存器(控制是GPIO/UART/I2C等)
- 一个方向寄存器(输入/输出)
- 一个数据寄存器(高低电平)
设备树中的pinctrl配置就是告诉内核如何设置这些寄存器。关键参数包括:
function:选择引脚功能bias:上下拉电阻配置drive-strength:驱动能力
3. 详细实现步骤
3.1 确定目标引脚
首先需要确认UART2_TX对应的GPIO编号。有三种查找方式:
-
官方文档:
查询芯片参考手册,如高通TRM文档的"Pin Configuration"章节 -
内核调试接口:
bash复制adb shell "cat /sys/kernel/debug/pinctrl/*/pins | grep uart"
- 原理图追踪:
在开发板原理图中查找UART2_TX网络连接的引脚编号
3.2 修改设备树
设备树修改主要涉及三个部分:
3.2.1 引脚控制配置
在&tlmm节点中添加:
dts复制my_led_pin: my_led_pin {
pins = "gpio45";
function = "gpio";
drive-strength = <8>; // 8mA驱动能力
bias-disable; // 无上下拉
output-high; // 默认输出高电平
};
3.2.2 LED设备注册
在根节点中添加:
dts复制leds {
compatible = "gpio-leds";
status = "okay";
status_red: red_led {
label = "my:red:status";
gpios = <&tlmm 45 GPIO_ACTIVE_LOW>;
default-state = "off";
pinctrl-names = "default";
pinctrl-0 = <&my_led_pin>;
};
};
3.2.3 禁用原功能
找到对应的UART节点并禁用:
dts复制&uart2 {
status = "disabled";
};
3.3 编译与刷写
3.3.1 单独编译设备树
bash复制make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- msm8998-miatoll.dtb
3.3.2 替换boot.img中的dtb
使用Magiskboot工具:
bash复制magiskboot unpack boot.img
cp arch/arm64/boot/dts/qcom/msm8998-miatoll.dtb boot.img-dtb
magiskboot repack boot.img new_boot.img
3.3.3 刷入设备
bash复制fastboot flash boot new_boot.img
fastboot reboot
4. 使用与调试
4.1 基本控制
验证LED设备是否成功注册:
bash复制ls /sys/class/leds/my:red:status/
控制LED亮度:
bash复制# 点亮
echo 255 > /sys/class/leds/my:red:status/brightness
# 熄灭
echo 0 > /sys/class/leds/my:red:status/brightness
4.2 高级功能实现
4.2.1 呼吸灯效果
使用PWM调光:
bash复制# 设置呼吸效果参数
echo 1000 > /sys/class/leds/my:red:status/delay_on
echo 1000 > /sys/class/leds/my:red:status/delay_off
echo 1 > /sys/class/leds/my:red:status/soft_blink
4.2.2 自动化控制
通过Tasker实现充电时自动点亮:
- 创建Profile:State → Power → AC
- 创建Task:Code → Run Shell
bash复制echo 255 > /sys/class/leds/my:red:status/brightness
5. 常见问题排查
5.1 LED不亮
检查步骤:
- 确认GPIO是否正确配置为输出:
bash复制cat /sys/kernel/debug/gpio | grep gpio-45 - 测量引脚电压:
- 输出低电平时应≈0V
- 输出高电平时应≈3.3V
5.2 系统启动失败
可能原因:
- 引脚冲突(未正确禁用原功能)
- 设备树语法错误
解决方案:
- 通过串口查看内核启动日志
- 检查dmesg输出:
bash复制dmesg | grep -E 'gpio|led|uart'
5.3 不同平台适配
联发科(MTK)平台差异:
- 控制器节点名为
&pio - 引脚编号方式不同:
dts复制&pio { my_led_pin: my_led_pin { pins = "PIN_GPIO123"; function = "gpio"; }; };
6. 安全注意事项
-
电气安全:
- 确保LED电流不超过GPIO驱动能力(通常8-16mA)
- 避免引脚短路到电源或地
-
系统安全:
- 修改前备份原始boot.img
- 在开发板上先测试,再应用于主力设备
-
权限管理:
- /sys/class/leds/接口通常需要root权限
- 考虑编写一个普通用户可访问的守护进程
7. 扩展应用思路
这个基础改造可以衍生出多种实用场景:
-
硬件状态指示器:
- 网络活动指示灯
- 电池充电状态指示
-
物理按钮扩展:
- 将GPIO配置为输入模式
- 通过中断实现物理按钮功能
-
传感器集成:
- 连接红外、超声波等简单传感器
- 构建物联网边缘节点
我在实际项目中曾用类似方法,将手机的红外发射管改造成了环境光传感器中断触发源。关键在于理解设备树的灵活性和内核驱动的可扩展性。
