You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32F401CCU6外部中断字节拆分发送数据异常及bitCounter重置疑问

STM32外部中断引脚输出异常问题分析

问题背景

使用42MHz的STM32F401CCU6芯片,PB0引脚连接50kHz外部中断源,PB10引脚需要在每次下降沿事件时切换状态。初始代码运行时输出值持续异常,但改用二进制数组存储数据后恢复正常,修改代码后虽正常运行,但对其中一处逻辑存在疑问。


初始代码异常原因

先看初始异常代码:

uint8_t dataSND[4] = {0xA0, 0xB0, 0xC0, 0xD0};//data
uint8_t mode = 0;

void sendIO_Data(uint8_t state)
{
    if (state == 0)
        GPIOB->BSRR = GPIO_BSRR_BR_10;//set low
        //HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    else
        GPIOB->BSRR = GPIO_BSRR_BS_10;//set high
        //HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
}

void setIO_In()
{
    GPIOB->MODER &= ~(GPIO_MODER_MODE10);//I/O to input
}

void setIO_Out()
{
    GPIOB->MODER |= GPIO_MODER_MODE10_0;//I/O to output
}

void sendDATA()//send bit 0-7 of "dataSND" values 0-3
{
    sendIO_Data((dataSND[byteAddress] >> bitCntr) & 1);

    if (byteAddress == 3 && bitCntr == 7)
    {
        byteAddress = 0;
        bitCntr = 0;
        mode = 0;
        setIO_In();
        return;
    }

    bitCntr++;
    if (bitCntr == 8)
    {
        bitCntr = 0;
        byteAddress++;
    }
}


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_0)
    {
        uint8_t clock_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); //GPIO_PIN_SET - rising, GPIO_PIN_RESET - falling
        uint8_t reset_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
        if (reset_state == GPIO_PIN_SET)
        {
            setIO_Out();
            cntr = 0;
            mode = 1;
        }

        if (clock_state == GPIO_PIN_RESET && mode == 1)
        {
            sendDATA();
        }
    }
}

异常的核心原因:

  • 变量未声明且缺失volatile修饰:byteAddress、bitCntr、cntr三个关键计数变量均未声明,属于未定义行为,运行时会取随机值;同时这些变量在中断回调和sendDATA函数中交叉使用,必须加volatile修饰,防止编译器将其优化为寄存器变量,导致内存值和寄存器值不一致。
  • 计数变量重置逻辑错误:启动传输时(reset_state == GPIO_PIN_SET),代码重置的是cntr,但sendDATA实际使用的是byteAddress和bitCntr,等于没重置计数,第一次触发时计数变量为随机值,直接导致输出错位。
  • 逻辑分支的潜在问题:sendDATA中满足byteAddress ==3 && bitCntr ==7时直接return,虽逻辑上能重置变量,但如果之前计数变量异常,会导致后续传输逻辑混乱。

二进制数组版本正常的原因

改用二进制数组后代码正常,代码如下:

uint8_t dataSND[32] = {0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,1,0,1,1};

void sendDATA()
{
    sendIO_Data(dataSND[cntr]);

    if (cntr >= 32)//+1
    {
        cntr = 0;
        mode = 0;
        setIO_In();
    }
    cntr++;
}

原因在于:

  • 逻辑简化,出错点减少:直接按顺序读取数组中预存的每一位值,不需要移位、位运算和多变量计数,避免了因多变量同步错误导致的输出异常。
  • 计数逻辑直接:仅用cntr一个变量跟踪当前输出位置,重置逻辑清晰,只要cntr正确初始化并加volatile,就能保证每次传输从起始位开始。

修改后为何要显式设置bitCounter = 0

修改后的代码给所有全局变量加了volatile,并在启动传输时设置bitCounter =0,代码如下:

volatile uint8_t byteAddress = 0;
volatile uint8_t bitCounter = 0;
volatile uint8_t dataSND[4] = {0xA0, 0xB0, 0xC0, 0xD0};//data
volatile uint8_t mode = 0;

void sendIO_Data(uint8_t state)
{
    if (state == 0)
        GPIOB->BSRR = GPIO_BSRR_BR_10;//set low
    else
        GPIOB->BSRR = GPIO_BSRR_BS_10;//set high
}

void setIO_In()
{
    GPIOB->MODER &= ~(GPIO_MODER_MODE10);
}

void setIO_Out()
{
    GPIOB->MODER |= GPIO_MODER_MODE10_0;
}

void sendDATA()
{
    sendIO_Data((dataSND[byteAddress] >> bitCounter) & 1);

    bitCounter++;

    if (bitCounter > 7)
    {
        bitCounter = 0;
        byteAddress++;
    }

    if (byteAddress > 3)
    {
        byteAddress = 0;
        bitCounter = 0;
        mode = 0;
        setIO_In();
    }
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_0)
    {
        uint8_t clock_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); //GPIO_PIN_SET - rising, GPIO_PIN_RESET - falling
        uint8_t reset_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);

        if (reset_state == GPIO_PIN_SET)
        {
            setIO_Out();
            bitCounter = 0;
            mode = 1;
        }

        if (clock_state == GPIO_PIN_RESET && mode == 1)
        {
            sendDATA();
        }
    }
}

显式设置bitCounter =0的原因:

  • 规避未初始化或异常残留值:系统复位后、异常中断嵌套或其他代码意外修改都可能导致bitCounter不为0,显式重置能确保每次启动传输时从数据的第0位开始,避免输出错位。
  • 保证传输的一致性:无论上一次传输是否正常结束,只要触发启动信号,就强制将计数变量复位,确保每一轮传输的起始状态一致。
  • 配合volatile的可靠性:虽然加了volatile保证变量值实时更新,但显式初始化能消除任何潜在的不确定状态,让代码鲁棒性更强。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 04:37:58