You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

STM32G0+PN532(I2C)多次读卡后无法通过IRQ唤醒STOP模式求助

STM32G031K8 + PN532低功耗STOP模式唤醒异常问题解决

环境与问题描述

  • 主控:STM32G031K8
  • 通信方式:I²C连接PN532模块
  • 适配库:pn532-lib
  • 需求:MCU进入STOP模式节省电量,通过PN532的IRQ引脚(EXTI0)唤醒

异常现象

  1. 读卡完成后IRQ电平未恢复,需添加临时虚拟读取才能进入STOP模式
  2. 多次刷卡后,IRQ偶尔停止触发,MCU卡死在STOP模式
  3. 补充:读卡后快速移卡,下次可正常唤醒;若卡片留在感应区直到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);
        }
    }
}

问题根源

  1. PN532的IRQ为边沿触发,仅在卡片出现/消失时触发电平变化;若卡片持续停留,IRQ会保持固定电平,无法触发下一次唤醒
  2. 读卡完成后未主动重置PN532的检测状态,导致IRQ引脚电平未恢复到空闲状态
  3. 临时虚拟读取未彻底清除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

验证要点

  1. 卡片停留时,处理完成后IRQ引脚是否恢复到空闲电平
  2. 多次刷卡/留卡后,是否能正常唤醒MCU
  3. 检查STOP模式下的电流消耗是否符合预期

内容的提问来源于stack exchange,提问作者user31230865

火山引擎 最新活动