目录
- 原子操作基础概念
- STM32硬件原子操作支持
- C语言层面的实现方法
- 编译器内置函数应用
- 实际应用场景与案例
- 性能对比与优化建议
原子操作基础概念
什么是原子操作?
原子操作(Atomic Operation)是指在多线程环境下不可分割、不可中断的操作。从外部观察者的角度来看,原子操作要么完全执行成功,要么完全未执行,不存在中间状态。
原子操作的核心特性
- 原子性:操作不可分割,执行过程中不会被其他线程打断
- 可见性:一个线程对共享变量的修改对其他线程立即可见
- 有序性:操作按照程序指定的顺序执行
为什么需要原子操作?
在嵌入式系统中,原子操作主要用于解决以下问题:
- 中断与主程序竞争:中断服务程序与主循环同时访问共享数据
- 多任务系统竞争:RTOS中多个任务访问共享资源
- DMA与CPU竞争:DMA传输与CPU处理共享缓冲区
STM32硬件原子操作支持
ARM Cortex-M架构特性
STM32系列MCU基于ARM Cortex-M内核,其硬件层面提供了强大的原子操作支持:
1. LDREX/STREX指令对
- LDREX:加载独占(Load Exclusive)
- STREX:存储独占(Store Exclusive)
- 这对指令实现了**比较并交换(CAS)**的基本功能
2. 支持的原子指令
- 位带操作(Bit-Banding):对单个位的原子操作
- 独占访问:通过LDREX/STREX实现复杂操作的原子性
- 中断屏蔽:通过PRIMASK和FAULTMASK寄存器
STM32外设寄存器的原子性
STM32的外设寄存器设计考虑了原子操作需求:
// 例如GPIO的BSRR寄存器
GPIOA->BSRR = GPIO_PIN_5; // 原子置位
GPIOA->BSRR = GPIO_PIN_5 << 16; // 原子复位
C语言层面的实现方法
方法一:禁用中断
最简单直接的原子操作实现方式:
#include "stm32f4xx.h"
uint32_t shared_counter = 0;
void atomic_increment(void) {
uint32_t primask = __get_PRIMASK(); // 保存当前中断状态
__disable_irq(); // 关闭所有中断
shared_counter++; // 原子操作
if (!primask) { // 恢复之前的中断状态
__enable_irq();
}
}
优点 :实现简单,保证绝对原子性
缺点:影响系统实时性,不适合长时间操作
方法二:位带操作
利用STM32的位带特性实现单比特原子操作:
// 定义位带别名地址
#define BITBAND_PERIPH(address, bit) \
((address & 0xF0000000) + 0x02000000 + ((address & 0x00FFFFFF) << 5) + (bit << 2))
#define BITBAND_PERIPH_REG(address, bit) (*(volatile uint32_t*)BITBAND_PERIPH(address, bit))
// 使用示例
#define GPIOA_ODR_BIT5_BITBAND BITBAND_PERIPH_REG(&GPIOA->ODR, 5)
void atomic_gpio_toggle(void) {
GPIOA_ODR_BIT5_BITBAND = 1; // 原子置位
GPIOA_ODR_BIT5_BITBAND = 0; // 原子复位
}
方法三:LDREX/STREX实现CAS
uint32_t atomic_cas(volatile uint32_t *addr, uint32_t old_val, uint32_t new_val) {
uint32_t old;
uint32_t status;
do {
old = __LDREXW(addr); // 读取当前值
if (old != old_val) {
__CLREX(); // 清除独占状态
return old; // 值已改变,返回
}
status = __STREXW(new_val, addr); // 尝试写入新值
} while (status); // 如果写入失败,重试
return old;
}
编译器内置函数应用
GCC/ARM Compiler内置原子函数
现代编译器提供了标准的原子操作函数:
#include <stdatomic.h>
// 使用C11原子类型
atomic_uint shared_counter = ATOMIC_VAR_INIT(0);
void atomic_increment_c11(void) {
atomic_fetch_add(&shared_counter, 1); // 原子加法
}
// 或使用GCC内置函数
void atomic_increment_builtin(void) {
__atomic_fetch_add(&shared_counter, 1, __ATOMIC_SEQ_CST);
}
CMSIS-DSP原子函数
ARM提供了CMSIS-DSP库中的原子操作函数:
#include "arm_math.h"
uint32_t cmsis_atomic_counter = 0;
void cmsis_atomic_increment(void) {
// CMSIS-DSP提供的原子操作
uint32_t old_val = __LDREXW(&cmsis_atomic_counter);
uint32_t new_val = old_val + 1;
while (__STREXW(new_val, &cmsis_atomic_counter)) {
old_val = __LDREXW(&cmsis_atomic_counter);
new_val = old_val + 1;
}
}
实际应用场景与案例
场景一:中断与主程序共享数据
volatile uint32_t sensor_data_ready = 0;
volatile uint32_t sensor_value = 0;
// 中断服务程序
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
uint32_t value = read_sensor();
// 原子操作更新共享数据
__disable_irq();
sensor_value = value;
sensor_data_ready = 1;
__enable_irq();
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
// 主循环处理
void main_loop(void) {
uint32_t local_value;
uint32_t data_ready;
// 原子读取共享数据
__disable_irq();
local_value = sensor_value;
data_ready = sensor_data_ready;
__enable_irq();
if (data_ready) {
process_sensor_data(local_value);
// 原子清除标志
__disable_irq();
sensor_data_ready = 0;
__enable_irq();
}
}
场景二:RTOS任务间通信
#include "FreeRTOS.h"
#include "task.h"
typedef struct {
uint32_t head;
uint32_t tail;
uint32_t buffer[100];
} ring_buffer_t;
ring_buffer_t g_ring_buffer;
// 任务1:生产者
void producer_task(void *pvParameters) {
while (1) {
uint32_t next_head = (g_ring_buffer.head + 1) % 100;
// 检查缓冲区是否满
if (next_head != g_ring_buffer.tail) {
// 原子写入数据
__disable_irq();
g_ring_buffer.buffer[g_ring_buffer.head] = get_data();
g_ring_buffer.head = next_head;
__enable_irq();
}
vTaskDelay(1);
}
}
// 任务2:消费者
void consumer_task(void *pvParameters) {
while (1) {
// 检查缓冲区是否空
if (g_ring_buffer.tail != g_ring_buffer.head) {
uint32_t data;
// 原子读取数据
__disable_irq();
data = g_ring_buffer.buffer[g_ring_buffer.tail];
g_ring_buffer.tail = (g_ring_buffer.tail + 1) % 100;
__enable_irq();
process_data(data);
}
vTaskDelay(1);
}
}
场景三:状态机切换
typedef enum {
STATE_IDLE = 0,
STATE_RUNNING,
STATE_PAUSED,
STATE_ERROR
} system_state_t;
volatile system_state_t g_system_state = STATE_IDLE;
system_state_t get_current_state(void) {
return __LDREXW((volatile uint32_t*)&g_system_state);
}
bool set_system_state(system_state_t new_state) {
system_state_t old_state;
uint32_t status;
do {
old_state = get_current_state();
status = __STREXW((uint32_t)new_state, (volatile uint32_t*)&g_system_state);
} while (status);
return (old_state == new_state) ? false : true;
}
性能对比与优化建议
不同方法性能对比
| 方法 | 执行时间 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 禁用中断 | 高 | 简单操作 | 简单可靠 | 影响实时性 |
| 位带操作 | 低 | 单比特操作 | 高效 | 仅限单比特 |
| LDREX/STREX | 中 | 复杂操作 | 灵活 | 可能重试 |
| 内置函数 | 中 | 通用 | 标准化 | 依赖编译器 |
优化建议
- 选择合适的方法:根据操作复杂度和性能要求选择
- 减少临界区:原子操作代码越短越好
- 避免嵌套:不要在原子操作中调用其他原子操作
- 考虑重试:LDREX/STREX可能需要重试机制
- 测试验证:在实际硬件上测试并发场景
调试技巧
// 添加调试信息
#define ATOMIC_DEBUG 1
#if ATOMIC_DEBUG
#define ATOMIC_ENTER() do { \
debug_printf("Atomic op start: %s:%d\r\n", __FILE__, __LINE__); \
} while(0)
#define ATOMIC_EXIT() do { \
debug_printf("Atomic op end: %s:%d\r\n", __FILE__, __LINE__); \
} while(0)
#else
#define ATOMIC_ENTER()
#define ATOMIC_EXIT()
#endif
总结
原子操作是STM32嵌入式开发中保证数据一致性的关键技术。通过合理利用硬件特性、编译器支持和软件设计,可以有效解决多任务环境下的数据竞争问题。
关键要点:
- 理解不同原子操作方法的适用场景
- 根据性能要求选择合适的实现方式
- 尽量减少原子操作的执行时间
- 在实际硬件上充分测试并发场景
通过本文的详细介绍和示例代码,希望读者能够掌握STM32平台原子操作的实现方法,并在实际项目中灵活应用。