第05章 项目实现
约 3052 字大约 10 分钟
2026-03-23
第5章. 项目实现 基于CAF构建键盘应用
现在我们重新创建一个新的空白项目,项目名称为keyboard,并创建一个空白的BSP包,开发板名称为atguigu_mini_keyboard,vendor填写atguigu。按照上一章的示例,启用基本CAF框架,并创建main module。
1. 整体项目架构
1.1. 按键与HID数据流
1.2. 上位机协议流程
1.3. 显示与灯效流程
2. 矩阵按键
Nordic在NCS中附带了很多现成的module,也就是说如果我们构建一个键盘应用,并不用一切都从0开始。例如矩阵按键和GPIO按键扫描程序button_module。Button module基于GPIO + 中断扫描矩阵按键,所以我们需要打开GPIO硬件。
2.1 需求描述
实现一个 6 行 4 列矩阵键盘,但实际只使用其中 18 个有效按键。当用户按下有效按键时,系统可以识别对应的按键位置,并通过 RTT 输出对应的 key_id。
2.2 主控GPIO
行:
R0 -> P0.15
R1 -> P0.07
R2 -> P0.12
R3 -> P0.04
R4 -> P1.09
R5 -> P0.08
列:
C0 -> P0.05
C1 -> P0.06
C2 -> P0.26
C3 -> P0.30row-gpios = <&gpio0 15 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
<&gpio0 7 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
<&gpio0 12 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
<&gpio0 4 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
<&gpio1 9 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
<&gpio0 8 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
col-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>,
<&gpio0 6 GPIO_ACTIVE_HIGH>,
<&gpio0 26 GPIO_ACTIVE_HIGH>,
<&gpio0 30 GPIO_ACTIVE_HIGH>;2.3 涉及CAF模块
app_event_manager
CAF button_event
CAF keep_alive_event
CAF power_event2.4 参考提示词
请帮我实现一个基于 Zephyr / NCS 的 6x4 矩阵按键模块。
要求:
- 使用 Zephyr 官方 gpio-kbd-matrix 驱动
- 使用 Zephyr 官方 input-keymap 做坐标到按键码映射
- 实际有效按键为 18 个
- 行引脚为 P0.15, P0.07, P0.12, P0.04, P1.09, P0.08
- 列引脚为 P0.05, P0.06, P0.26, P0.30
- row-gpios 使用 GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH
- col-gpios 使用 GPIO_ACTIVE_HIGH
- 配置 actual-key-mask 为 <0x3e 0x3e 0x3e 0x0b>
- 按键事件最终转换为 CAF button_event3. 按键唤醒
3.1 需求描述
实现一个 按键低功耗唤醒功能。系统空闲一段时间后进入低功耗状态,当用户按下任意有效矩阵按键时,系统可以从低功耗状态恢复,并通过 RTT 输出进入低功耗和退出低功耗的状态日志。
3.2 涉及CFA模块
CAF Power Manager
CAF power_event
CAF keep_alive_event
app_event_manager3.3 参考提示词
请帮我实现一个基于 CAF Power Manager 的按键唤醒模块。
要求:
- 使用 app_event_manager 作为事件总线
- 启用 CAF Power Manager
- 系统空闲后自动进入低功耗 suspended 状态,不进入 System OFF
- 按键按下时,如果当前处于低功耗状态,则发送 wake_up_event
- 如果当前未处于低功耗状态,则发送 keep_alive_event
- 写一个 power_state_logger.c,监听 power_down_event 和 wake_up_event
- 进入低功耗时打印 enter low power
- 退出低功耗时打印 exit low power4. 编码器模块
4.1 需求描述
实现一个 EC11 旋钮编码器旋转识别功能。当用户顺时针或逆时针旋转旋钮时,系统可以识别旋转方向、旋转步数和累计角度,并通过 RTT 输出旋钮方向、当前步数和累计角度。
4.2. 主控GPIO
EC11A -> P0.10
EC11B -> P1.06encoder: encoder {
compatible = "gpio-qdec";
status = "okay";
/* EC11A -> P0.10, EC11B -> P1.06. Swap order if direction is reversed. */
gpios = <&gpio0 10 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>,
<&gpio1 6 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>;
steps-per-period = <2>;
zephyr,axis = <INPUT_REL_WHEEL>;
sample-time-us = <2000>;
idle-poll-time-us = <2000>;
idle-timeout-ms = <200>;
};4.3 涉及CAF模块
Zephyr gpio-qdec
Zephyr input subsystem
CAF keep_alive_event
CAF power_event
app_event_manager4.4 参考提示词
请帮我实现一个基于 Zephyr 官方 gpio-qdec 的 EC11 旋钮编码器模块。
要求:
- A/B 两相接在 P0.10 和 P1.06
- 两个输入都配置 GPIO_PULL_UP | GPIO_ACTIVE_HIGH
- 在 DTS 中启用 gpio-qdec
- steps-per-period 配置为 2
- zephyr,axis 设置为 INPUT_REL_WHEEL
- sample-time-us 为 2000
- idle-poll-time-us 为 2000
- idle-timeout-ms 为 200
- 应用层监听 INPUT_EV_REL 和 INPUT_REL_WHEEL
- value > 0 视为 clockwise,value < 0 视为 counterclockwise
- 每次旋转打印方向、步数、累计角度
- 旋钮旋转时也要复用低功耗逻辑,低功耗时发送 wake_up_event,正常时发送 keep_alive_event
- 因为 P0.10 是 NFC 复用脚,DTS 里必须加 &uicr { nfct-pins-as-gpios; };5. 电源管理模块
5.1 需求描述
电源管理模块应该包含充放电状态汇报,IP5306保活,电池电压测量以及SOC水平汇报。这里面我们需要驱动IP5306,但是Zephyr中没有现成的驱动,所以我们需要套用Zephyr的开发标准,开发一套IP5306的驱动
- 通过 I2C 读充电状态
- 定期(每8s)给 IP5306 的 wakeup 脚打 keepalive 脉冲
- 读取
5.2 IP5306驱动开发
Zephyr驱动开发文档:
https://docs.zephyrproject.org/latest/kernel/drivers/index.html
https://docs.zephyrproject.org/latest/develop/modules.html
我们使用外部zephyr module的形式开发驱动,所以要遵循zephyr module约定的结构,大致如下:

5.3 主控GPIO引脚
- I2C SDA:P1.00
- I2C SCL:P0.24
- IP5306 wakeup/keepalive 引脚:P0.22,active low
- BAT_ADC: P0.31 / AIN7
- BAT_ADC_EN P0.095.4 参考提示词
请在这个 Zephyr/NCS 项目中实现 IP5306 PMIC 驱动和电池状态模块。
硬件信息:
- MCU:nRF52840
- PMIC:IP5306,挂在 I2C0,地址 0x75
- I2C SDA:P1.00
- I2C SCL:P0.24
- IP5306 wakeup/keepalive 引脚:P0.22,active low
- 电池电压通过 /vbatt voltage-divider 采样
功能要求:
1. 新增 out-of-tree IP5306 驱动。
2. 驱动通过 I2C 读取 IP5306 状态:
- 读寄存器 0x70 bit3 判断 charging
- 读寄存器 0x71 bit3 判断 full
3. 驱动提供接口:
- ip5306_init()
- ip5306_get_status()
4. 驱动支持 keepalive 脉冲,防止 IP5306 轻载自动关机:
- wakeup GPIO 每 8000 ms 产生一次有效脉冲
- 脉冲宽度 500 ms
- wakeup GPIO 是 active low,所以实际有效电平为低
5. 优先使用硬件 keepalive:
- RTC2 计时
- GPIOTE 控制 GPIO toggle
- GPPI/PPI 连接 RTC compare 事件到 GPIOTE task
- 尽量不依赖线程定时器,保证低功耗状态下仍能产生脉冲
6. 在 devicetree 增加 binding 和 board DTS 节点。
7. 在 battery_module 中初始化 IP5306,并周期性读取 charging/full 状态。
8. 保持现有项目风格,不引入不必要的重构。
3300 mV -> 0%
4200 mV -> 100%
中间按 10% 档位四舍五入
验收标准:
- 项目能编译通过。
- devicetree 中有 compatible = "injoinic,ip5306" 的节点。
- IP5306 I2C 地址为 0x75。
- P0.22 每 8 秒出现一个约 500 ms 的低电平脉冲。
- battery_module 能读取并发布 charging/full 状态。
- 轻载待机时 IP5306 不会自动关断系统供电。6. 模式切换模块
目标
通过 ADC 识别 USB、BLE、2.4G 三种模式,并发布 mode_switch_event。
需要提供的资料
- 模式开关电路
- 每个档位对应的理论电压
- ADC 引脚
- 电阻分压值
- 是否需要滤波、防抖、迟滞
当前工程资料
- ADC:AIN5,对应 P0.29
- USB 阈值:小于 825 mV
- 2.4G 阈值:825 mV 到 2475 mV
- BLE 阈值:大于等于 2475 mV
涉及模块
mode_switch_module.cmode_policy_module.cdisplay_module.c
验收标准
- 三个档位识别准确
- 切换不会反复抖动
- 屏幕模式显示正确
- USB/BLE 栈按模式启停
提示词模板
这是我的三档模式开关电路和 ADC 采样电压,请帮我设计 mode_switch_module.c 的阈值和防抖策略。
USB 档电压范围:__
2.4G 档电压范围:__
BLE 档电压范围:__
ADC 引脚:__
请输出推荐阈值、是否需要迟滞,以及如何发布 mode_switch_event。7. USB_HID模块
目标
先跑通 USB 有线键盘。USB 是最容易调试的 HID 链路,建议优先完成。
需要提供的资料
- VID/PID
- 厂商名、产品名
- HID report 类型:Boot、NKRO、Consumer
- 是否需要 CDC
- 是否需要多接口
当前工程资料
- VID:
0x1915 - PID:
0x52F0 - Manufacturer:
Atguigu - Product:
WH Mini Keyboard
涉及模块
usb_device_module.cusb_hid_keyboard_module.cusb_hid_consumer_module.chid_flowctrl_module.ckeyboard_core_module.c
验收标准
- 插 USB 后主机枚举出键盘
- 数字小键盘按键能输入
- NumLock LED 状态能回传到固件
- 旋钮能控制音量
- Boot Protocol 和 Report Protocol 切换正常
提示词模板
请帮我实现或检查 USB HID 键盘链路。
要求:
1. USB 枚举为复合设备,包含 keyboard HID、consumer HID、CDC ACM;
2. 键盘支持 Boot Protocol 和 NKRO Report Protocol;
3. Consumer report 支持音量加减、静音;
4. 主机 LED output report 要转换成 hid_led_event;
5. 请检查 descriptor、report size 和 keyboard_core_module.c 是否一致。8 BLE HID 键盘
目标
跑通 BLE HID,支持配对、加密、通知、键盘输入和 consumer control。
需要提供的资料
- BLE 设备名
- BLE Appearance
- 配对数量
- 是否必须加密
- 是否支持 Swift Pair
- 是否区分普通 BLE 和 2.4G dongle identity
验收标准
- 手机或电脑可以发现设备
- 可以配对并加密连接
- BLE HID 可以输入键盘按键
- 旋钮可以走 BLE consumer report
- BLE 电量服务可见
- 断开后能重新连接
提示词模板
请帮我检查 BLE HID 键盘实现。
要求:
1. 支持 keyboard report、consumer report 和 boot keyboard;
2. 只有连接加密完成且通知打开后才允许发送 HID report;
3. LED output report 要发布 hid_led_event;
4. 和 USB HID 使用同一套 keyboard_core 和 hid_flowctrl;
5. 检查 prj.conf 中 BLE/HIDS/BAS/Settings 配置是否完整。9. HID 流控与传输策略
目标
让键盘核心不关心当前走 USB 还是 BLE,由策略模块和流控模块统一决定。
需要提供的资料
- 三种模式下 HID 走向
- 是否允许 USB 和 BLE 同时在线
- 键盘报告是否覆盖旧值
- 音量等 consumer pulse 是否必须 FIFO
当前策略
- USB 模式:HID 走 USB
- BLE 模式:HID 走 BLE,普通 BLE profile
- 2.4G 模式:HID 走 BLE,dongle profile
涉及模块
mode_policy_module.ctransport_policy_eventhid_flowctrl_module.ckeyboard_core_module.c
验收标准
- 模式切换后不会向旧通道继续发按键
- 切换模式时会释放旧通道按键
- Consumer pulse 不丢失或乱序
- 键盘 report 不因拥塞积压旧状态
提示词模板
请帮我设计 HID 发送流控。
要求:
1. 键盘按键报告使用 latest 策略;
2. 音量加减等 consumer pulse 使用 FIFO 策略;
3. 支持 USB keys、USB consumer、BLE shared 三个发送通道;
4. 发送前必须等待对应 HID 通道 ready;
5. 发送完成通过 hid_report_sent_event 释放 in-flight 状态。10. LED 灯效模块需求
10.1 功能目标
控制 WS2812 灯带,支持按键触发的 key fade 效果、主题色更新和灯带开关。
10.2 硬件
- LED strip:WS2812 over SPI。
- SPI1 SCK:P1.04。
- SPI1 MOSI/Data:P0.20。
- chain length:17。
- supply GPIO:P0.13 active high。
10.3 输入事件
button_event:按键触发灯效。theme_rgb_update_event:更新主题色。led_strip_en_event:启停灯带。- 生命周期和电源事件。
10.4 行为
- 按键按下时,在对应 LED 上触发渐隐效果。
- 主题色变化后,后续灯效使用新颜色。
- power down 时关闭或暂停灯带。
- wake up 后恢复。
10.5 验收
- 按键时对应灯位有渐隐效果。
- 上位机或设置页改主题后灯效颜色变化。
- 灯带关闭后不再发光。
11. UI 显示
11.1 功能目标
基于 LVGL + ST7789V 显示主界面和设置页面。
11.2 硬件
所用 MCU 引脚(nRF52840)
| 信号 | 外设 | GPIO 引脚 | 电平有效 | 说明 |
|---|---|---|---|---|
| SCK | SPI3 | P1.13 | — | SPI 时钟 |
| MOSI | SPI3 | P0.28 | — | SPI 数据输出(只写,无 MISO) |
| CS | SPI3 | P0.02 | ACTIVE_LOW | 片选 |
| DC | GPIO | P0.03 | ACTIVE_HIGH | 数据/命令选择 |
| RESET | GPIO | P1.10 | ACTIVE_LOW | 硬件复位 |
| 背光 | PWM0 CH0 | P1.11 | 反转极性 | 100Hz PWM,通过 led_on/led_off API 控制 |
屏幕特性:
| 参数 | 值 |
|---|---|
| 驱动芯片 | Sitronix ST7789V |
| 接口类型 | SPI 4 线(MIPI DBI Type C option 3,只写模式) |
| 最大 SPI 频率 | 32 MHz |
| 色深 | 16 bit/pixel (RGB565),65K 色 |
| 物理分辨率 | 320 × 240 |
| 有效区域 | 320 × 172(y 偏移 34,即顶部跳过 34 行) |
| x 偏移 | 0 |
| LVGL 像素格式 | 16 bit,启用字节交换 (LV_COLOR_16_SWAP) |
| 就绪等待时间 | 120 ms |
11.3 输入事件
显示模块订阅:
bat_state_eventble_bond_multi_eventble_peer_eventble_peer_search_eventdatetime_eventhid_led_eventmode_switch_eventsettings_mode_eventsettings_view_eventtheme_rgb_update_event- 生命周期和电源事件
11.4 主界面显示
主界面应显示:
- 当前连接模式:USB/BLE/2.4G。
- 电池百分比。
- 充电/满电图标。
- BLE 搜索/连接状态。
- NumLock/CapsLock 等 HID LED 状态。
- 日期和时间。
11.5 验收
- 开机后主界面出现。
- 模式拨档变化时 UI 模式变化。
- 电池状态变化时 UI 电量/充电图标变化。
- BLE 连接状态变化时 UI 更新。
