开发板上的按键不能只是摆设
各位看官,今天咱们玩点阴间的。当你们还在用按键触发中断、搞事件标志组、玩消息队列的时候,我选择了一条邪路——把4个卑微的物理按键变成24小时不间断被监视的"数字劳工",并且把它们的"心理活动"实时投影在OLED上,上演一出裸机轮询的极权主义美学。
硬件?简单到 insulting:MM32F5330(这芯片跑分能吊打当年登月计算机),4个弹片按键(寿命10万次,今天让你1秒尝尽10万次的心跳),一块0.96寸OLED(128×64像素,每个像素都在尖叫"我看见了!")
轮询即正义,延时即和平
在main.c这个微型社会架构里,我植入了赤裸裸的暴力循环。上电那一刻,MCU就成了无情的监工:
int main(void)
{
PLATFORM_Init(); // 平台初始化,准备搭建数字监狱
OLED_Init(); // 显示器亮起,老大哥在看着你
OLED_ShowString(1,1,"MM32F5333"); // 宣告主权:这片屏幕姓MM
while(1) // 永恒的循环,比《盗梦空间》还狠
{
PLATFORM_LED_Toggle(LED1); // 心跳灯,证明MCU还没过劳死
PLATFORM_DelayMS(200); // 每200ms喘息一下,资本家的仁慈
// 四个按键的状态数组,它们的人生被量化成0和1
static uint8_t KeyState[4] = {0, 0, 0, 0};
static uint8_t KeyCount[4] = {0, 0, 0, 0}; // 计数器,记录按键被"审讯"的次数
GPIO_Configure(); // 配置GPIO,给按键戴上电子镣铐
printf("\r\nPress K1-K4..."); // 虚伪的提示,其实不按也得按
while (1) // 嵌套循环,恶魔的低语
{
// 下面四行,是核心暴政:每10ms审讯一次所有按键
KEY_FSM_Handler(&KeyState[0], &KeyCount[0],
GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_4), Bit_SET, "K1");
// ↑ 对PC4号按键实施"SET电平有效"政策,不按的时候是高电平,按下去反而是低电平?
// 官方例程就是这么叛逆,反人类设计但We'll Make It Work
KEY_FSM_Handler(&KeyState[1], &KeyCount[1],
GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5), Bit_RESET, "K2");
// ↑ PC5这位更惨,Bit_RESET才有效,天生贱命必须常按才能被看见
KEY_FSM_Handler(&KeyState[2], &KeyCount[2],
GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1), Bit_RESET, "K3");
// ↑ PB1:别问,问就是RESET党
KEY_FSM_Handler(&KeyState[3], &KeyCount[3],
GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2), Bit_RESET, "K4");
// ↑ PB2:四个按键三种待遇,MCU界的种姓制度
// LED同步指示:按键状态直接映射到LED,物理层面的公开处刑
PLATFORM_LED_Enable(LED1, (FunctionalState)GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_4));
PLATFORM_LED_Enable(LED2, (FunctionalState)GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5));
PLATFORM_LED_Enable(LED3, (FunctionalState)GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1));
PLATFORM_LED_Enable(LED4, (FunctionalState)GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2));
PLATFORM_DelayMS(10); // 10ms轮询一次,按键的瞳孔地震
// OLED实时直播:把按键的屈辱状态打在公屏上
OLED_ShowNum(3, 1, KeyState[0], 1); // 第3行第1列:K1的精神状态
OLED_ShowNum(3, 3, KeyState[1], 1); // 第3行第3列:K2的灵魂编号
OLED_ShowNum(3, 5, KeyState[2], 1); // 第3行第5列:K3的忏悔值
OLED_ShowNum(3, 7, KeyState[3], 1); // 第3行第7列:K4的忠诚度
// 这画面,四个数字在OLED上跳动,像四个囚徒在牢房里数数
}
}
}
按键的心理医生,也是精神折磨大师
那KEY_FSM_Handler到底是何方神圣?它是官方例程塞进来的"按键心理医生",用状态机给按键做精神分析:
void KEY_FSM_Handler(uint8_t *state, uint8_t *count, uint8_t raw, uint8_t valid_logic, char* name)
{
switch(*state)
{
case 0: // 按键 IDLE 状态,岁月静好
if(raw == valid_logic) // 突然检测到有效电平!
{
(*count)++; // 可能误触,先计数观察
if(*count > DEBOUNCE_TIME) // 计数达标,确认不是抖动
{
*state = 1; // 状态转移:按键确认被按下
printf("\r\n%s is pressed!", name); // 昭告天下
*count = 0; // 计数器清零,准备下次折磨
}
}
else
{
*count = 0; // 误触?计数器归零,重新做人
}
break;
case 1: // 按键已按下状态
if(raw != valid_logic) // 电平恢复正常
{
(*count)++;
if(*count > DEBOUNCE_TIME)
{
*state = 0; // 返回IDLE,等待下次压迫
printf("\r\n%s is released!", name);
*count = 0;
}
}
else
{
*count = 0;
}
break;
}
}
这代码,每个按键配一个state和count,用轮询代替中断,用延时代替事件,把异步世界硬生生干成同步奴隶制。按键按下那一刻,CPU不是在服务中断,而是在10ms轮询里"恰好"抓住了你,然后像班主任点名一样把你记录在案。CPU的996福报
- 轮询频率:100Hz(10ms一次),四个按键无一幸免
- CPU占用:主循环里全是GPIO_Read、LED控制、OLED刷新,实际算力浪费在99%的空转等待
- 实时性:按键响应延迟最坏情况20ms(刚好错过上轮轮询),但谁在乎呢?又不是导弹发射
- 功耗:MM32F5330在48MHz下疯狂空转,功耗堪比小型暖手宝
- 代码美学:官方例程的Bit_SET和Bit_RESET混用,是对称美学的终极亵渎,但"能跑就行"
轮询党的末日狂欢
你可能要问:为什么不用中断?为什么不上RTOS?为什么要这样虐待自己?
答案很简单:因为可以。
在这个RTOS教程满天飞、中断驱动被奉为圣经的时代,裸机轮询就像朋克摇滚——粗糙、原始、直接,带着一种"老子就爱这么干"的叛逆。GPIO_ReadInputDataBit这个函数被调用了8次(4次状态机+4次LED映射),每一次都是对"效率"二字的公开处刑,但当你看到OLED上那四个数字忠实地跟随按键跳动时,你会明白:
有些代码,跑的不是逻辑,是态度。
四个按键,四个LED,一块OLED,一个48MHz的主循环,构建了一个微型极权社会。按键没有自由,CPU没有喘息,LED没有隐私,OLED没有灵魂。但它们都在工作,在裸机的铁拳下,以一种原始而有效的方式共存。
别在生产环境这么干
如果你敢把这段代码用在量产产品上:
- 硬件工程师会把你绑在示波器上电疗
- 软件架构师会用状态图把你凌迟
- 产品经理会让你去产线按10万次按键测试寿命
- 测试工程师会把你和按键关在一起,不按够100万次不许出来
- 但,在这块开发板上,在这间实验室里,在这段被10ms延时撕裂的时间里,你就是那个掌控一切的暴君。轮询是你的鞭子,OLED是你的审判庭,而MM32F5330,是那个默默执行你所有暴政的沉默机器。
- 按下K1,看着LED1亮起,OLED上的数字从0变1,那一刻,你触摸到了裸机编程最原始的快感——我是CPU,我即轮回。
-------------------------------------------------------------我是分割线-----------------------------------------------------------------
冷知识:把PLATFORM_DelayMS(10)改成1ms,按键会被轮询到怀疑鼠生。建议试试,烧了别找我。

开源社区 


