stm32中优先使用原子操作的具体实现方式
在STM32中,优先使用的原子操作主要包括位带操作、LDREX/STREX指令以及CMSIS提供的原子操作宏,以下是具体实现方式12:1. 位带操作(Bit-Banding)
通过硬件直接对单个比特进行原子读写,适用于GPIO或寄存器位操作1:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) + (bitnum << 2))
#define MEM_ADDR(addr)*((volatile uint32_t *)(addr))
// 原子设置GPIOB的PIN5为高电平
MEM_ADDR(BITBAND(&GPIOB->ODR, 5)) = 1;
2. LDREX/STREX指令
基于Cortex-M的独占访问指令,适用于共享变量修改23:
volatile uint32_t shared_var = 0;
void atomic_increment(void) {
uint32_t val;
do {
val = __LDREXW(&shared_var);// 独占加载
val += 1;
} while (__STREXW(val, &shared_var));// 独占存储,失败则重试
__CLREX();// 清除独占标记
}
3. CMSIS原子操作宏
CMSIS库提供的封装宏,如ATOMIC_SET_BIT和ATOMIC_MODIFY_REG24:
#include "core_cm4.h"
// 原子设置USART1的CR1寄存器第3位
ATOMIC_SET_BIT(USART1->CR1, USART_CR1_TXEIE);
// 原子修改TIM2的CCER寄存器
ATOMIC_MODIFY_REG(TIM2->CCER, TIM_CCER_CC1E, 0x1);
#include "core_cm4.h"
// 原子设置USART1的CR1寄存器位
ATOMIC_SET_BIT(USART1->CR1, USART_CR1_TXEIE);
// 原子修改变量
uint32_t val = 0;
ATOMIC_MODIFY_REG(val, 0xFF, 0x55);// 将低8位改为0x55
4. GPIO的BSRR寄存器
通过置位/复位寄存器实现GPIO的原子操作1:
// 原子设置PB5高电平、PB6低电平
GPIOB->BSRR = (1 << 5) | (1 << (6 + 16));
临界区保护
HAL库提供__disable_irq()和__enable_irq()函数,可临时禁用中断实现原子性
volatile uint32_t counter = 0;
void atomic_increment(void) {
__disable_irq();// 进入临界区
counter++; // 原子操作
__enable_irq(); // 退出临界区
}
代码实现示例
以下是一个综合示例,展示原子操作在多线程环境中的应用35:
atomic_ops.c
已完成
此代码演示了计数器原子递增和GPIO标志位原子设置,适用于中断与主程序共享数据的场景1
#include "stm32f4xx.h"
#include "core_cm4.h"
volatile uint32_t counter = 0;
// 使用LDREX/STREX原子递增
void atomic_counter_inc(void) {
uint32_t val;
do {
val = __LDREXW(&counter);
val++;
} while (__STREXW(val, &counter));
__CLREX();
}
// 使用CMSIS宏原子设置标志位
void atomic_flag_set(volatile uint32_t *reg, uint32_t bit) {
ATOMIC_SET_BIT(*reg, bit);
}
int main(void) {
atomic_counter_inc();
atomic_flag_set(&GPIOA->ODR, 5);
return 0;
}
#include "stm32f4xx.h"
#include "core_cm4.h"
volatile uint32_t global_var = 0;// 待修改的全局变量
void atomic_write(uint32_t new_value) {
uint32_t current_val;
do {
current_val = __LDREXW(&global_var);// 独占加载当前值
} while (__STREXW(new_value, &global_var));// 尝试写入新值,失败则重试
__CLREX();// 清除独占标记(防止后续LDREX受影响)
}
int main(void) {
atomic_write(0x12345678);// 原子写入新值
while(1);
}
————————————————
版权声明:本文为CSDN博主「中少奇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chencichang/article/details/149795611
页:
[1]