单片机,主循环和中断资源访问冲突的案例

单片机主循环与中断资源访问冲突案例分析

在嵌入式系统中,主循环(Main Loop)和中断服务程序(ISR)共享资源时,如果处理不当,会引发竞态条件(Race Condition)或数据不一致问题。下面通过几个典型案例进行说明:

案例1:共享变量访问冲突

场景描述

  • 主循环:周期性读取传感器数据并计算平均值
  • 定时器中断:每10ms更新一次传感器原始数据

冲突代码示例

c 复制代码
uint16_t sensorData;  // 共享资源

// 主循环
void main(void) {
    uint32_t sum = 0;
    uint8_t count = 0;
    
    while(1) {
        sum += sensorData;  // ① 读取共享变量
        count++;
        
        if(count >= 100) {
            printf("Average: %lu\n", sum / count);
            sum = 0;
            count = 0;
        }
    }
}

// 定时器中断服务程序
void TIMER_ISR(void) {
    sensorData = ReadSensor();  // ② 更新共享变量
}

冲突原因

  • 当主循环执行①行读取sensorData时,可能被②行的中断打断
  • 若中断更新了sensorData,主循环可能读取到部分更新的数据
  • 导致计算的平均值不准确

解决方案

c 复制代码
uint16_t sensorData;
bool dataUpdated = false;

// 主循环
void main(void) {
    uint32_t sum = 0;
    uint8_t count = 0;
    
    while(1) {
        uint16_t localData;
        
        __disable_irq();           // 关中断
        if(dataUpdated) {
            localData = sensorData;
            dataUpdated = false;
        }
        __enable_irq();            // 开中断
        
        if(!dataUpdated) {
            sum += localData;
            count++;
            // ...
        }
    }
}

// 定时器中断服务程序
void TIMER_ISR(void) {
    sensorData = ReadSensor();
    dataUpdated = true;
}

案例2:缓冲区访问冲突

场景描述

  • 主循环:处理串口接收缓冲区数据
  • 串口中断:将接收到的字节存入缓冲区

冲突代码示例

c 复制代码
#define BUFFER_SIZE 16
uint8_t rxBuffer[BUFFER_SIZE];
uint8_t bufferHead = 0;
uint8_t bufferTail = 0;

// 主循环
void main(void) {
    while(1) {
        if(bufferHead != bufferTail) {  // ① 检查缓冲区是否有数据
            uint8_t data = rxBuffer[bufferTail];  // ② 读取数据
            bufferTail = (bufferTail + 1) % BUFFER_SIZE;  // ③ 更新尾指针
            
            ProcessData(data);
        }
    }
}

// 串口接收中断
void UART_RX_ISR(void) {
    uint8_t data = UART_Read();
    
    rxBuffer[bufferHead] = data;  // ④ 写入数据
    bufferHead = (bufferHead + 1) % BUFFER_SIZE;  // ⑤ 更新头指针
}

冲突原因

  • 当主循环执行①-③行操作时,可能被④-⑤行的中断打断
  • 若中断更新了bufferHead,主循环可能误判缓冲区状态
  • 导致数据丢失或缓冲区溢出

解决方案

c 复制代码
#define BUFFER_SIZE 16
uint8_t rxBuffer[BUFFER_SIZE];
uint8_t bufferHead = 0;
uint8_t bufferTail = 0;

// 主循环
void main(void) {
    while(1) {
        uint8_t localHead;
        
        __disable_irq();
        localHead = bufferHead;
        __enable_irq();
        
        if(localHead != bufferTail) {
            uint8_t data = rxBuffer[bufferTail];
            
            __disable_irq();
            bufferTail = (bufferTail + 1) % BUFFER_SIZE;
            __enable_irq();
            
            ProcessData(data);
        }
    }
}

// 串口接收中断
void UART_RX_ISR(void) {
    uint8_t data = UART_Read();
    uint8_t nextHead = (bufferHead + 1) % BUFFER_SIZE;
    
    if(nextHead != bufferTail) {  // 检查缓冲区是否已满
        rxBuffer[bufferHead] = data;
        bufferHead = nextHead;
    } else {
        HandleBufferOverflow();
    }
}

案例3:外设操作冲突

场景描述

  • 主循环:配置SPI接口并发送数据到Flash
  • 定时器中断:周期性采集ADC数据并通过SPI发送到外部设备

冲突代码示例

c 复制代码
// 主循环
void main(void) {
    while(1) {
        SPI_Configure(SPI_MODE_FLASH);  // ① 配置SPI为Flash模式
        SPI_Write(flashData, FLASH_SIZE);  // ② 发送数据到Flash
        // ...
    }
}

// 定时器中断
void TIMER_ISR(void) {
    uint16_t adcData = ADC_Read();
    
    SPI_Configure(SPI_MODE_SENSOR);  // ③ 配置SPI为传感器模式
    SPI_Write(&adcData, 2);  // ④ 发送ADC数据
}

冲突原因

  • 主循环执行①-②行时,可能被③-④行的中断打断
  • 中断修改了SPI配置,导致主循环发送的数据格式错误
  • 造成Flash写入失败或数据传输错误

解决方案

c 复制代码
bool spiBusy = false;

// 主循环
void main(void) {
    while(1) {
        if(!spiBusy) {
            spiBusy = true;
            SPI_Configure(SPI_MODE_FLASH);
            SPI_Write(flashData, FLASH_SIZE);
            spiBusy = false;
        }
    }
}

// 定时器中断
void TIMER_ISR(void) {
    if(!spiBusy) {
        uint16_t adcData = ADC_Read();
        
        spiBusy = true;
        SPI_Configure(SPI_MODE_SENSOR);
        SPI_Write(&adcData, 2);
        spiBusy = false;
    }
}

冲突预防原则

  1. 最小化临界区:只在访问共享资源的短时间内关中断
  2. 使用原子操作:对标志位等简单变量使用原子操作
  3. 资源状态管理:使用标志位标记资源是否正在使用
  4. 中断优先级控制:关键任务使用更高优先级中断
  5. 数据复制:中断中只进行数据采集,处理放到主循环

通过合理的资源管理和同步机制,可以有效避免主循环与中断之间的冲突。

相关推荐
崇山峻岭之间1 小时前
HC32F460PETB的PB2的Vout123
单片机
恶魔泡泡糖1 小时前
stm32F103C8T6标准库串口接收之中断方式2
stm32·单片机·嵌入式硬件
充哥单片机设计2 小时前
【STM32开源项目】智能小区充电桩
stm32·单片机·嵌入式硬件·毕业设计·小区充电桩·智能小区充电桩
嵌入式-老费2 小时前
esp32开发与应用(如何解决SoC冷启动的问题)
单片机·嵌入式硬件
充哥单片机设计3 小时前
【STM32开源项目】智能语音台灯
stm32·单片机·嵌入式硬件·毕业设计·单片机设计·智能语音台灯
EatFan4 小时前
全栈自造 Status Deck(四):硬件终于到了,从点亮屏幕到跑通第一个 Demo
单片机·物联网·esp32
sunoo-2295 小时前
【嵌入式裸机开发Day04】i.MX6ULL 蜂鸣器驱动详解:从引脚地址查找到代码实现全流程
arm开发·单片机·嵌入式硬件·学习·串口·裸机
Brilliantwxx5 小时前
【STM32】 SPI阻塞式通信与HAL源码 (源码分析)
stm32·单片机·嵌入式硬件
Doraemomo6 小时前
IMX6ULL裸机开发——PWM
arm开发·嵌入式硬件·imx6ull
老当益壮梁奶奶7 小时前
ARM汇编学习笔记(五):从按键轮询到 GIC 中断控制器(ARM Cortex-A7 )
arm开发·笔记·单片机·嵌入式硬件