STM32G0+PN532(I2C)多次读卡后无法通过IRQ唤醒STOP模式求助
STM32G031K8 + PN532低功耗STOP模式唤醒异常问题解决
环境与问题描述
- 主控:STM32G031K8
- 通信方式:I²C连接PN532模块
- 适配库:pn532-lib
- 需求:MCU进入STOP模式节省电量,通过PN532的IRQ引脚(EXTI0)唤醒
异常现象
- 读卡完成后IRQ电平未恢复,需添加临时虚拟读取才能进入STOP模式
- 多次刷卡后,IRQ偶尔停止触发,MCU卡死在STOP模式
- 补充:读卡后快速移卡,下次可正常唤醒;若卡片留在感应区直到MCU进入STOP,后续靠近卡片无法唤醒
用户代码
主函数:
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C2_Init(); MX_USART2_UART_Init(); HAL_Delay(1000); printf("Inicializando PN532...\r\n"); PN532_Init(&pn532); uint8_t version[4]; if (PN532_GetFirmwareVersion(&pn532, version) != PN532_STATUS_OK) { printf("No se encontró el PN532 :(\r\n"); while (1) { HAL_Delay(500); } } printf("Firmware PN5xx: %d.%d\r\n", version[1], version[2]); PN532_SamConfiguration(&pn532); printf("Esperando una tarjeta...\r\n"); HAL_NVIC_EnableIRQ(EXTI0_1_IRQn); while (1) { if (!flag_card_detected) { printf("Entrando en modo stop\r\n"); PN532_ReadPassiveTarget(&pn532, dummy, PN532_MIFARE_ISO14443A, 50); __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); HAL_SuspendTick(); __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WUF); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config(); HAL_ResumeTick(); HAL_NVIC_EnableIRQ(EXTI0_1_IRQn); printf("Despertando\r\n"); } else { uint8_t response[32]; HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6); HAL_Delay(500); int rc = PN532_ReadPassiveTarget(&pn532, response, PN532_MIFARE_ISO14443A, 1000); if (rc > 0) { uint8_t uidLen = (uint8_t)rc; printf("Tarjeta detectada, UID: "); for (uint8_t i = 0; i < uidLen; i++) { printf("%02X ", response[i]); } printf("\r\n"); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); HAL_Delay(1000); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); } else { printf("No se pudo leer la tarjeta.\r\n"); } flag_card_detected = false; } } }
EXTI0中断处理函数:
void EXTI0_1_IRQHandler(void) { uint32_t current_tick = HAL_GetTick(); if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); if ((current_tick - last_exti_tick) > DEBOUNCE_DELAY_MS) { flag_card_detected = true; last_exti_tick = current_tick; } else { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); } } }
问题根源
- PN532的IRQ为边沿触发,仅在卡片出现/消失时触发电平变化;若卡片持续停留,IRQ会保持固定电平,无法触发下一次唤醒
- 读卡完成后未主动重置PN532的检测状态,导致IRQ引脚电平未恢复到空闲状态
- 临时虚拟读取未彻底清除PN532内部的状态残留,多次操作后出现异常
修复方案
1. 读卡后主动重置PN532检测状态
在处理完卡片数据后,发送命令让PN532重新进入卡片检测模式,确保IRQ恢复空闲电平:
// 在flag_card_detected = false;之前添加 PN532_ReadPassiveTarget(&pn532, NULL, PN532_MIFARE_ISO14443A, 0);
2. 修改EXTI为双边沿触发
PN532的IRQ在卡片出现和消失时都会产生电平变化,改为双边沿触发可覆盖两种场景:
在MX_GPIO_Init()中修改配置:
GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; // 双边沿触发 GPIO_InitStruct.Pull = GPIO_PULLUP; // 根据PN532 IRQ电平选择上拉/下拉 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
3. 移除临时虚拟读取,优化STOP进入流程
删除原代码中的临时读取操作,进入STOP前仅需清除EXTI中断标志:
if (!flag_card_detected) { printf("Entrando en modo stop\r\n"); __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); HAL_SuspendTick(); __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WUF); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config(); HAL_ResumeTick(); // 初始化时已开启EXTI中断,无需重复启用 printf("Despertando\r\n"); }
4. 简化中断处理函数
移除冗余的中断标志清除操作:
void EXTI0_1_IRQHandler(void) { uint32_t current_tick = HAL_GetTick(); if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); if ((current_tick - last_exti_tick) > DEBOUNCE_DELAY_MS) { flag_card_detected = true; last_exti_tick = current_tick; } } }
5. 确认PN532供电与低功耗配置
- 若MCU进入STOP时PN532保持供电,可通过命令开启PN532的自动低功耗模式
- 若PN532被断电,需在MCU唤醒后重新初始化PN532
验证要点
- 卡片停留时,处理完成后IRQ引脚是否恢复到空闲电平
- 多次刷卡/留卡后,是否能正常唤醒MCU
- 检查STOP模式下的电流消耗是否符合预期
内容的提问来源于stack exchange,提问作者user31230865




