STM32 门禁系统:看门狗设计与管理员菜单退出刷卡失效问题的解决
问题
智能门禁系统基本功能完成后,遇到了两个关键问题:
-
看门狗无法正常工作:IWDG 初始化卡死在
PVU位等待,WWDG 超时时间太短不适用 -
管理员卡退出后刷不了卡:刷卡进入管理员菜单,按 ESC 退出后无法再次刷卡
一、看门狗方案选型与实现
方案对比
| 方案 | 结果 | 原因 |
|---|---|---|
| IWDG | ❌ 无法启动 | LSI 时钟不稳定,PVU 位长期为 1 |
| WWDG | ❌ 超时太短 | 最大约 40ms,无法满足 ESP 初始化(30s) |
| 软件看门狗 | ✅ 成功 | 使用 TIM2 中断 + 软复位,超时可调 |
软件看门狗实现
核心思想:定时器中断检查喂狗标志,超时则执行 NVIC_SystemReset()。
main.c 全局变量:
TIM_HandleTypeDef htim2;
volatile uint8_t wdg_feed_flag = 0;
定时器初始化(1秒超时):
void MX_TIM2_Init(void)
{__HAL_RCC_TIM2_CLK_ENABLE();htim2.Instance = TIM2;htim2.Init.Prescaler = 7200 - 1;htim2.Init.Period = 10000 - 1;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;HAL_TIM_Base_Init(&htim2);HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);HAL_NVIC_EnableIRQ(TIM2_IRQn);HAL_TIM_Base_Start_IT(&htim2);
}
中断回调:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM2) {if (wdg_feed_flag == 0) {NVIC_SystemReset(); // 超时复位} else {wdg_feed_flag = 0;}}
}
空闲任务喂狗:
void StartDefaultTask(void const * argument)
{for(;;) {wdg_feed_flag = 1;osDelay(200);}
}
stm32f1xx_it.c 中断入口:
void TIM2_IRQHandler(void)
{HAL_TIM_IRQHandler(&htim2);
}
关键点:启动顺序很重要——先喂一次狗再启动定时器,否则系统会在 FreeRTOS 启动前触发复位。
wdg_feed_flag = 1;
MX_TIM2_Init();
MX_FREERTOS_Init();
osKernelStart();
二、管理员卡退出后刷卡失效问题
问题现象
-
密码进入管理员界面 → 退出 → ✅ 正常刷卡
-
刷卡进入管理员界面 → 退出 → ❌ 无法刷卡
根因分析
调用链对比:
密码进入:
Regular_User() → Regular_Admin() → 退出后返回 Regular_User 循环
刷卡进入(原始代码):
Uid_Read() → Regular_Admin() → 退出后 break → 退出 Uid_Read
问题:Uid_Read 中管理员卡分支使用了 break 退出循环,导致 RC522 状态未正确恢复,且无法再次进入读卡流程。
解决方案
1. 在管理员菜单 ESC 退出时复位 RC522
// AdminOperation.c - Regular_Admin
else if (KEY_ESC == key){RC522_Reset();RC522_AntennaOn();return;
}
2. 在 Uid_Read 管理员卡分支添加 break 并让循环继续
else if(read_Admin == current || read_Admin_New1 == current){OLED_Clear();Regular_Admin();break; // 退出循环,让 StartRC522Task 继续
}
3. StartRC522Task 成功读卡后复位 RC522
if (RC522_ReadCardID(user_uid) == MI_OK) {Uid_Read();RC522_Halt();RC522_Reset(); // 保证状态干净RC522_AntennaOn();
}
4. 消除 Regular_User 的递归调用(避免栈溢出和卡顿)
// 原代码(递归调用)
}else if (key == KEY_ESC){Regular_User();
}
// 改为返回
}else if (key == KEY_ESC){return;
}
5. 在 Uid_Read 循环中添加延时(避免忙等导致卡顿)
void Uid_Read(void)
{while(1) {if(RC522_ReadCardID(user_uid) == MI_OK) {// 处理卡片...break;}osDelay(10); // 让出 CPU}
}
三、最终效果
| 问题 | 解决方案 | 效果 |
|---|---|---|
| 看门狗无法启动 | 软件看门狗(TIM2 + 软复位) | ✅ 1秒超时自动复位 |
| 管理员卡退出后刷卡失效 | 退出时复位 RC522 + Uid_Read 退出循环 | ✅ 退出后可正常刷卡 |
| 系统卡顿 | 消除递归调用 + 添加 osDelay | ✅ 响应流畅 |
四、关键经验
-
IWDG 依赖 LSI,如果 LSI 不稳定,考虑软件看门狗替代方案
-
看门狗启动顺序:必须在系统稳定后启动,否则会陷入复位循环
-
RC522 状态管理:每次退出关键操作时主动复位,保证下次读卡正常
-
避免递归调用:在嵌入式系统中递归会大量消耗栈空间,应使用循环 + return
-
任务循环必须让出 CPU:
while(1)中必须有osDelay或阻塞等待
