5.2 HAL 按键框架详解(选修)
本节导读
5.1 我们用三步就把按键用起来了,但"为什么这么用"还没讲透。这一节(选修)钻进 hal_key.c 内部,看按键框架的四个关键函数和两种工作模式。
搞懂这一节,你就从"会调 API"升级为"懂原理"。
原站本节内容已失效,本站按 Z-Stack 3.0 的 hal_key.c 源码与教学实践重写。
一、按键框架全家福
按键相关代码在 target\CC2530EB\hal_key.c 中,核心是四个函数:
| 函数 | 角色 | 大白话 |
|---|---|---|
HalKeyInit() | 初始化 | 把按键引脚配置好 |
HalKeyConfig() | 配置/注册 | 告诉 HAL:中断还是轮询 + 回调是谁 |
HalKeyPoll() | 扫描按键 | 挨个查按键引脚电平 |
HalKeyRead() | 读取状态 | 把"谁按着"打包成一个值返回 |
外加一个回调函数(你写的):按键有变化时被调用。
二、HalKeyInit():布置场地
void HalKeyInit(void) {
// 配置按键引脚为上拉输入(P1_2/P1_3/P0_1 等)
// 记录按键初始状态
// 把回调函数指针先清空(还没注册)
}由 HalDriverInit() 在 HAL_KEY=TRUE 时自动调用。不用手动调。
三、HalKeyConfig():定规则、报名字
void HalKeyConfig(bool interruptEnable, halKeyCBack_t cback);| 参数 | 含义 |
|---|---|
interruptEnable | HAL_KEY_INTERRUPT_ENABLE(用中断)/ HAL_KEY_INTERRUPT_DISABLE(用轮询) |
cback | 你的回调函数名 |
它做三件事:
- 记住工作模式(中断 or 轮询)——存到全局变量
Hal_KeyIntEnable; - 记住回调函数是谁;
- 如果选中断模式,立即打开按键中断。
⚠️ 回调函数必须先注册后生效——这就是 5.1 里为什么要先调
HalKeyConfig(...)再等按键。
四、两种工作模式:轮询 vs 中断
模式一:轮询(SampleSwitch 默认,HAL_KEY_INTERRUPT_DISABLE)
HAL 任务每 100ms 定时去查一次按键引脚(4.2 讲过):
if (events & HAL_KEY_EVENT) {
HalKeyPoll(); // 去查岗
if (!Hal_KeyIntEnable) {
osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, 100); // 100ms 后再查
}
}- 优点:实现简单、稳定可靠;
- 缺点:100ms 内按键"偷偷"按下又松开可能漏检(实际手按都超过 100ms,基本无感)。
模式二:中断(HAL_KEY_INTERRUPT_ENABLE)
按键引脚电平一变化,立刻触发中断,HAL 在中断里标记事件、由任务处理:
- 优点:响应快(微秒级)、省电(不用定时醒);
- 缺点:实现复杂,中断处理要小心(不能在里面干重活)。
生活类比:轮询 = 保安每 10 分钟巡逻一圈,简单但可能错过瞬间情况;中断 = 门口装感应器,有人来立刻报警,灵敏但系统要更精细。课堂实验用轮询完全够用。
五、HalKeyPoll():怎么"查岗"
void HalKeyPoll(void) {
bool valid = FALSE;
uint8 keys = 0;
// ① 读取按键引脚电平(按下为低电平 → 对应位置 1)
if (!HAL_PUSH_BUTTON_1_PORT_0_0) keys |= HAL_KEY_SW_1; // 查 S3
if (!HAL_PUSH_BUTTON_2_PORT_0_0) keys |= HAL_KEY_SW_2; // 查 S5
// ……五向键(经门电路/运放)……
// ② 与上次状态比较:变了才算"有效按键"
if (keys != halKeySavedKeys) {
valid = TRUE; // 状态有变化!
halKeySavedKeys = keys; // 更新"上次状态"
}
// ③ 有变化且有回调 → 通知应用层
if (valid && (pHalKeyProcessFunction != NULL)) {
pHalKeyProcessFunction(0, keys); // 调用你注册的回调!
}
}三个关键设计:
- 按下为低电平:
!HAL_PUSH_BUTTON_1为真 = S3 被按下(对照原理图:S3→P1_3,上拉,按下拉低); - 状态比较去抖:
keys != halKeySavedKeys——只有状态变了才上报,防止重复触发; - 回调分发:有变化就调
pHalKeyProcessFunction(就是你注册的zclSampleSw_HandleKeys)。
生活类比:门卫巡逻一圈,发现"门铃状态和上一圈不一样"才汇报——不重复汇报、只报变化,这就是防抖。
六、回调函数收到什么?
static void zclSampleSw_HandleKeys(byte shift, byte keys) {
if (keys & HAL_KEY_SW_1) { // keys 的 bit0 = S3 状态
HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE);
}
}keys是按位打包的按键状态:bit0=S3、bit1=S5……按下的键对应位为 1;shift是修饰键(一般用不到)。
判断技巧:if (keys & HAL_KEY_SW_1) = "keys 里有 S3 这位吗?有就是 S3 被按了"。
七、小结
| 问题 | 答案 |
|---|---|
| 四个核心函数? | Init(布置)/ Config(定规则+注册回调)/ Poll(查岗)/ Read(读状态) |
| 两种模式? | 轮询(100ms 定时查,简单)/ 中断(电平变化立刻响应,灵敏) |
| 怎么防抖? | keys != halKeySavedKeys:状态变了才上报 |
| 回调收到什么? | keys 按位打包,keys & HAL_KEY_SW_x 判断哪个键 |
| 回调什么时候被调? | HAL 查岗发现按键状态变化时 |
一句话总结:按键框架 = "布置(Init)→ 定规则(Config)→ 查岗(Poll)→ 上报(回调)",轮询靠 100ms 定时器、中断靠引脚电平变化,"只报变化"的设计天然防抖。学完这一节,按键对你来说已经"透明"了。
内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第三部分·5.2 节(原站已失效),本站按 Z-Stack 3.0 的 hal_key.c 源码与教学实践重写;原站链接