Skip to content

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():布置场地 ​

c
void HalKeyInit(void) {
    // 配置按键引脚为上拉输入(P1_2/P1_3/P0_1 等)
    // 记录按键初始状态
    // 把回调函数指针先清空(还没注册)
}

由 HalDriverInit() 在 HAL_KEY=TRUE 时自动调用。不用手动调。

三、HalKeyConfig():定规则、报名字 ​

c
void HalKeyConfig(bool interruptEnable, halKeyCBack_t cback);
参数含义
interruptEnableHAL_KEY_INTERRUPT_ENABLE(用中断)/ HAL_KEY_INTERRUPT_DISABLE(用轮询)
cback你的回调函数名

它做三件事:

  1. 记住工作模式(中断 or 轮询)——存到全局变量 Hal_KeyIntEnable;
  2. 记住回调函数是谁;
  3. 如果选中断模式,立即打开按键中断。

⚠️ 回调函数必须先注册后生效——这就是 5.1 里为什么要先调 HalKeyConfig(...) 再等按键。

四、两种工作模式:轮询 vs 中断 ​

模式一:轮询(SampleSwitch 默认,HAL_KEY_INTERRUPT_DISABLE) ​

HAL 任务每 100ms 定时去查一次按键引脚(4.2 讲过):

c
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():怎么"查岗" ​

c
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);   // 调用你注册的回调!
    }
}

三个关键设计:

  1. 按下为低电平:!HAL_PUSH_BUTTON_1 为真 = S3 被按下(对照原理图:S3→P1_3,上拉,按下拉低);
  2. 状态比较去抖:keys != halKeySavedKeys——只有状态变了才上报,防止重复触发;
  3. 回调分发:有变化就调 pHalKeyProcessFunction(就是你注册的 zclSampleSw_HandleKeys)。

生活类比:门卫巡逻一圈,发现"门铃状态和上一圈不一样"才汇报——不重复汇报、只报变化,这就是防抖。

六、回调函数收到什么? ​

c
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 源码与教学实践重写;原站链接

aioteer · 物联网专业课程群

💬AI 伴学配置