一、实验目的
- 掌握嵌入式 Linux 系统下 GPIO(通用输入输出)的控制方法。
- 理解通过
/dev/mem+mmap直写物理寄存器的工作原理,绕过sysfs限制。 - 学习按键消抖、下拉沿检测等输入信号处理方法。
- 实现外部按键与主板 LED 的联动控制:面包板按键按下 → 主板 LED 明暗翻转。
- 培养嵌入式系统软硬件协同调试能力。
二、实验设备
| 设备 | 型号/说明 |
|---|---|
| 嵌入式开发主板 | 迅为 iTOP-4412(精英版),Exynos 4412 ARM Cortex-A9 四核 |
| 外部按键模块 | 面包板 + 微动开关 ×2 + 杜邦线 |
| SD 卡 | 8GB TF 卡,存放编译好的可执行文件 |
| 调试工具 | Xshell 8(串口终端,波特率 115200) |
| 开发主机 | Windows 11,安装 arm-none-linux-gnueabihf 交叉编译工具链 |
三、实验原理
3.1 GPIO 控制方式对比
本实验板内核未编译 CONFIG_GPIO_SYSFS(/sys/class/gpio/ 目录不存在),无法使用传统的 sysfs 应用层控制方式。因此采用 /dev/mem + mmap 寄存器直写方案:
1 | 用户态程序 |
3.2 寄存器地址映射
Exynos4412 的 GPIO 寄存器物理基址为 0x11000000(4KB 页面内),本实验涉及的寄存器:
| 寄存器 | 物理地址 | 功能 |
|---|---|---|
| GPX1CON | 0x1100_0C20 |
GPX1 配置寄存器(输入/输出模式选择) |
| GPX1DAT | 0x1100_0C24 |
GPX1 数据寄存器(读取引脚电平) |
| GPX1PUD | 0x1100_0C28 |
GPX1 上下拉电阻配置寄存器 |
| GPL2CON | 0x1100_0260 |
GPL2 配置寄存器 |
| GPL2DAT | 0x1100_0264 |
GPL2 数据寄存器(设置 LED2 输出电平) |
| GPK1CON | 0x1100_0060 |
GPK1 配置寄存器 |
| GPK1DAT | 0x1100_0064 |
GPK1 数据寄存器(设置 LED3 输出电平) |
3.3 引脚分配
| 功能 | 丝印名称 | GPIO 编号 | 对应寄存器位 |
|---|---|---|---|
| 外部按键1(面包板) | GPX1_4 | gpio-148 | GPX1DAT[4] |
| 外部按键2(面包板) | GPX1_3 | gpio-147 | GPX1DAT[3] |
| 主板 LED2(底板左侧) | GPL2_0 | gpio-126 | GPL2DAT[0] |
| 主板 LED3(底板右侧) | GPK1_1 | gpio-90 | GPK1DAT[1] |
3.4 按键检测与消抖
1 | GPX1DAT 读取 ──→ 高电平(1) = 未按下 |
3.5 面包板硬件接线
1 | iTOP-4412 底板 20pin 扩展排针 面包板 |
四、实验步骤
4.1 硬件接线
- 将两个微动开关插入面包板。
- 用杜邦线将面包板按键1的引脚A连接到开发板 20pin 扩展口的 GPX1_4 排针。
- 用杜邦线将面包板按键2的引脚A连接到开发板 20pin 扩展口的 GPX1_3 排针。
- 将两个按键的引脚C共地,接到扩展口的 GND 排针。
4.2 软件开发
核心程序 led_button.c 工作流程:
open("/dev/mem")— 打开物理内存设备文件mmap()— 将 GPIO 物理地址0x11000000映射到用户态虚拟地址空间- 配置 GPX1 — 设置按键引脚为输入模式 + 开启内部上拉电阻
- 配置 GPL2/GPK1 — 设置 LED 引脚为输出模式
- 主循环轮询 — 检测按键按下 → 消抖确认 → 翻转 LED → 打印日志
SIGINT信号处理 — Ctrl+C 时自动熄灭 LED、解除内存映射
4.3 交叉编译
在 Windows 开发机上:
1 | arm-none-linux-gnueabihf-gcc -static -o led_button.exe led_button.c |
使用 -static 静态链接确保在精简 Linux 上也能运行。
4.4 部署运行
1 | # 开发板串口终端 |
五、核心代码及注释
1 | /* |
六、实验结果
| 测试项 | 预期结果 | 实际结果 |
|---|---|---|
| 上电后 LED 初始状态 | LED2、LED3 均熄灭 | ✅ 正确 |
| 按下按键1 | LED2 翻转(灭→亮) | ✅ 正确 |
| 再次按下按键1 | LED2 翻转(亮→灭) | ✅ 正确 |
| 按下按键2 | LED3 翻转(灭→亮) | ✅ 正确 |
| 再次按下按键2 | LED3 翻转(亮→灭) | ✅ 正确 |
| Ctrl+C 退出 | 两个 LED 自动熄灭,程序退出 | ✅ 正确 |
| 按键消抖 | 按一次只触发一次翻转,不连发 | ✅ 正确 |
七、踩坑记录与经验总结
7.1 sysfs 不可用
内核未编译 CONFIG_GPIO_SYSFS,尝试 echo 148 > /sys/class/gpio/export 失败,报 nonexistent directory。解决:改用 /dev/mem + mmap 直写物理寄存器。
7.2 SD 卡反复掉线
SD 卡插入后频繁出现 Unable to re-detect card (-123),但文件系统仍能短暂访问。解决:在卡稳定时快速执行 cp 操作,或将常用文件放到 /tmp。
7.3 CP 目标路径错误
cp /mnt/usb/led_button.exe /root/ 报 Is a directory。教训:BusyBox 的 cp 必须写完整目标文件名 cp src/xxx /dst/xxx。
7.4 寄存器地址计算
Exynos4412 数据手册中 GPIO 寄存器地址需要精确计算。CON 寄存器每个引脚占 4 bits,DAT 寄存器每个引脚占 1 bit,PUD 寄存器每个引脚占 2 bits。配置时需要先读取当前值,用掩码清除目标位后再写入新值,避免误改其他引脚配置。
7.5 按键消抖
机械按键按下时会有 5~20ms 的抖动期。本实验采用 15ms 延时 + 二次确认 的软件消抖策略,有效防止了误触发。同时增加了”等待释放”逻辑,避免按一次触发多次翻转。
八、实验拓展
基于本实验的寄存器直写方法,可以拓展实现以下功能:
- PWM 控制 LED 亮度:通过定时器 / 软件 PWM 实现 LED 呼吸灯效果
- LCD 屏显联动:按键状态和 LED 状态实时显示在 LCD 屏幕上
- 中断驱动替代轮询:使用
request_irq注册中断处理函数,降低 CPU 占用 - 内核驱动模块:将逻辑封装为标准的字符设备驱动(
/dev/led_btn),供多个应用调用
实验日期:2026年6月17日
开发板:iTOP-4412(Exynos 4412)
工具链:arm-none-linux-gnueabihf-gcc 14.2