这个帖子讨论的是 STC8H8K64U 上移植江协科技按键状态机程序失败的问题。我先把问题本质梳理清楚。
问题分析
这个帖子楼主遇到的现象是:同一个江协科技状态机按键程序,在 STC32G12K128 上运行正常,移植到 STC8H8K64U 上后,无论长按、短按、双击都会触发按键动作(判断命中)。
结合帖子中各位的回复,可能的原因有以下几个方面:
1. 变量未显式初始化(重点关注)
论坛用户 jrc 的回复很关键:"Key_Tick() 里的那些变量尽量要显示初始化,如果 IDATA 和 XDATA 没有统一清 0,上电后有可能是随机值的"。
- Keil C51 中,对于 DATA/IDATA 段,C 启动代码
STARTUP.A51 默认是会把变量清 0 的;但对于 XDATA 段,是否清 0 取决于工程设置。
- 江协的代码中若用了
static uint16_t Time[x] 这类静态数组未赋初值,而该数组恰被分配到 XDATA 且启动代码未清零,则上电后初值是随机数。
- 这会导致状态机的索引/计时判断
if(S[i] == x) 出现非预期的"命中",表现为"随便怎么按都执行按键动作"。
建议:将这些静态数组显式初始化,例如 static unsigned int Time[4] = {0};。
注意:楼主 dong 反驳说"如果不命中应该按键不起作用,而不是长短按都执行"——这个反驳也有道理,所以变量初始化可能不是唯一原因,需要结合下面几点一起排查。
2. 端口模式设置
- STC8H 系列与 STC32G 系列在 I/O 口上电默认模式上是一致的(准双向口),但代码中若显式配置了 P3 模式,需要确认配置正确。
- 楼主已确认 P3.3 初始化为准双向口并置 1,万用表实测为高电平,所以硬件端口状态本身没问题。
3. 时钟频率不同导致的时间基准差异
这是我认为最可能的根因之一:
- 江协的状态机按键依赖
Key_Tick 的调用周期来做消抖和长短按判定。
- 楼主在 STC32G 上运行良好,是因为 STC32G 与 STC8H 的实际运行主频/定时器时基可能不同。
- 移植后如果 定时器 1ms 中断的实际周期发生了变化(例如系统时钟从 24MHz 变成 11.0592MHz,或 ISP 下载时选择的时钟频率不同),那么
Key_Tick 的实际调用间隔就不再是设计值,消抖阈值全部失效,从而造成"短按当长按""单击被判双击"等乱象。
建议:用示波器或翻转 I/O 实测定时器中断的真实周期,确认 Key_Tick 是否严格每 1ms 被调用一次;同时确认 STC8H 的 系统时钟频率(在 ISP 下载软件中查看"时钟频率"设置,默认常用 24MHz 或 11.0592MHz)。
4. 状态机对"释放沿"的依赖
江协按键程序采用状态机 + 计次方式,依赖对按键按下沿和释放沿的准确采样。如果采样周期远大于抖动时间或远小于抖动时间,都会误判。楼主提到"改为 20ms 读一次就有单击遗漏",正说明采样周期必须匹配消抖时间窗(通常 10~20ms 消抖,但状态机内部计数器也要相应匹配)。
结论与建议
综合这个帖子,我给您的排查建议顺序是:
| 排查项 |
具体做法 |
| ① 变量初始化 |
把 Key_Tick 内所有 static 数组/变量显式赋初值 = {0} 或 = 0 |
| ② 实际时钟频率 |
用 ISP 软件确认 STC8H 下载时选择的系统时钟;确认定时器计算是否按该频率 |
| ③ 中断真实周期 |
用 I/O 翻转 + 示波器实测 Key_Tick 是否严格 1ms 调用一次 |
| ④ 采样周期匹配 |
消抖采样周期需与状态机内部计数器阈值匹配,不能随意改 20ms |
| ⑤ 参考最小实现 |
论坛用户 wul 已给出一个最简长/短按参考程序,可先用它验证硬件与时基 |
关于您提到的"把布丁橘长按键程序去掉 10ms 延时、改为定时器每 10ms 读取":思路是对的,把阻塞式延时改成时隙轮询是正确方向,但要注意——Key_Tick 的内部计数阈值是按原来调用周期设计的,改成 10ms 调用一次,阈值也应同步折算,否则长短按判定会整体偏移。
如果您能提供:① Keil 工程里 XDATA 是否清 0 的设置、② STC8H 下载时的实际系统时钟频率、③ Key_Tick 定时器的配置代码,我可以帮您进一步精准定位。是否需要我基于手册给您一份不依赖复杂状态机、稳定可靠的长短按按键例程(放在 1ms 定时器中断里扫描,主循环只读键值)?
🔍 搜索 "STC 单片机 论坛 帖子"
⏱️ 查找用时 2.1s | 总回答用时 8.6s