C++ 在裸机控制器上不同上下文的int变量中设置标志
对于avr 8位微控制器,必须在某个8位整数变量中设置或清除单个位(标志)。可以从正常上下文(main)和中断处理程序(Isr())调用此set/clear函数。因此,必须使变量C++ 在裸机控制器上不同上下文的int变量中设置标志,c++,volatile,c++20,bitflags,C++,Volatile,C++20,Bitflags,对于avr 8位微控制器,必须在某个8位整数变量中设置或清除单个位(标志)。可以从正常上下文(main)和中断处理程序(Isr())调用此set/clear函数。因此,必须使变量易失性,以防止其重新排序或将其保留在寄存器中的某个位置std::atomic在这里不是一个有效的选项,因为没有底层操作系统、多cpu内核或缓存,所以不需要某种内存围栏。即使 STD::原子< /代码>不是任何AVR C++库的一部分。< /P> 设置标志的操作类似于:some_flags |=new_set_flags
易失性
,以防止其重新排序或将其保留在寄存器中的某个位置std::atomic
在这里不是一个有效的选项,因为没有底层操作系统、多cpu内核或缓存,所以不需要某种内存围栏。即使<代码> STD::原子< /代码>不是任何AVR C++库的一部分。< /P>
设置标志的操作类似于:some_flags |=new_set_flags
但是使用c++20
我得到了警告:警告:使用'volatile'的复合赋值-不推荐使用限定的左操作数[-Wvolatile]
通过使用临时变量重写函数是没有问题的,但它认为在这种情况下,不推荐使用volatile关键字并不是有意的
顺便说一句:由于变量存储在RAM中,cpu无法在单个汇编指令中设置内存中的一位。因此,整个操作必须是原子的。对于这种用例,avr库有原子块(原子块)
,它只是禁用中断
#include <util/atomic.h>
volatile uint8_t some_flags;
void SetFlag( uint8_t new_set_flags )
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
uint8_t tmp = some_flags;
tmp |= new_set_flags;
some_flags = tmp;
... vs ...
some_flags|= new_set_flags; // main.cpp:65:19: warning: compound assignment with 'volatile'-qualified left operand is deprecated [-Wvolatile]
}
}
void SomeIsr()
{
SetFlag( 0x02 );
}
int main()
{
SetFlag( 0x01);
}
0000006c:
6c:d8 9a sbi 0x1b,0;27
6e:90 e0本地设计院r25,0x00;0
70:80 e0本地设计院r24,0x00;0
72:08 95 ret
基本原理是,即使在易失性变量上,复合赋值或前后递增或递减也不是原子的,而程序员可以将其视为单个操作。此外,该标准规定,E1 op=E2与E1=E1 op E2相同,只是E1仅评估一次
这意味着不谨慎的程序员可以使用
volatile uint8_t some_flags;
...
some_flags|= new_set_flags;
期望它是原子的,即使在硬件中断的情况下,也不需要原子
在机器级别,它看起来像3个操作:
load value from memory
update accumulator register
store value to memory
这意味着在没有更多预防措施的情况下,如果两个执行线程(此处为正常处理线程和ISR线程)交错,则会出现争用情况:
normal loads
! ISR takes the processor
ISR loads updates and stores
! return from ISR
normal updates and stores erasing the change from ISR
当程序使用temp变量时,很明显可能会出现争用情况
< >对你来说不好的是,C++的委托人已经否决了使用它的意图,以便以后完全删除它。
因此,您可以:
- 在您的代码规范中添加允许对易失性变量进行复合赋值所依赖的内容,并希望编译器能够为其提供选项(即使不是很好,也感觉合理)
- 添加与C++17兼容但不支持C++20及以上版本的代码规范
- 只需将其写成
some_flags=some_flags | new_set_flags代码>
我喜欢最后一种方法,因为对于一个易失性字节,编译器没有产生一个效率较低的代码,它从早期C版本到最后一个C++ 1/P>是一致的。
参考资料:
- :当前版本
- :原始版本,包含广泛的背景信息,主要是拟议变更的原因?部分
volatile
并映射到给定地址)而言,不使用volatile听起来是一个非常糟糕的主意,并且会阻止大量代码。E1=E1 op E2确实没有改善:-)好的,我不在标准委员会,所以我必须处理结果。我将使用这种“新”的设置位的方法,即使这是一种痛苦。@Klaus:volatile并没有被弃用!不推荐使用的是volatile上的复合赋值…非常清楚。但这将导致不推荐使用标准IO处理:-)PORTA |=0x01;现在不推荐使用打开io端口的位。对我来说,这听起来简直是错的!如果您希望保留旧的C语法,则可以始终使用C模块。无论如何,我不支持委员会的决定,也不会试图责怪它:这只是一个事实,我们将不得不接受。。。顺便说一句,PORTA |=0x01
在可以存在多个执行线程时始终包含可能的争用条件。
load value from memory
update accumulator register
store value to memory
normal loads
! ISR takes the processor
ISR loads updates and stores
! return from ISR
normal updates and stores erasing the change from ISR