【发布时间】:2020-11-07 06:41:06
【问题描述】:
对于 avr 8 位微控制器,必须在某些 8 位整数变量中设置或清除单个位(标志)。这个设置/清除函数可以从普通上下文( main )和中断处理程序( Isr() )中调用。因此,必须将变量设置为volatile 以防止它重新排序或将其保存在寄存器中的某个位置。 std::atomic 在这里不是一个有效的选项,因为没有底层操作系统,也没有多 cpu 内核,也没有缓存,所以不需要某种内存栅栏。甚至 std::atomic 也不是任何 avr c++ 库的一部分。
设置标志的操作类似于: some_flags|= new_set_flags。
但是c++20 我收到警告:warning: compound assignment with 'volatile'-qualified left operand is deprecated [-Wvolatile]。
使用临时变量重写函数是没有问题的,但感觉这不是在这种情况下不推荐使用 volatile 关键字的意图。
顺便说一句:由于变量存储在 RAM 中,cpu 无法在单个汇编指令中设置内存中的位。因此,整个操作必须是原子的。对于那个用例,avr-lib 有ATOMIC_BLOCK(ATOMIC_RESTORESTATE),它只是禁用中断。
#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);
}
问题:如果此标志/变量在没有 OS 或 MMU 的单核裸机控制器的不同上下文中使用,那么将标志设置为 int 变量的“正确”方法是什么。
如果你想在一个 IO 寄存器上设置一个位,就像PORTA|=0x01; 一样,编译器可以在单个汇编指令中执行该操作。
#include <avr/io.h>
int main()
{
PORTA|=0x01; // main.cpp:57:10: warning: compound assignment with 'volatile'-qualified left operand is deprecated [-Wvolatile]
}
0000006c <main>:
6c: d8 9a sbi 0x1b, 0 ; 27
6e: 90 e0 ldi r25, 0x00 ; 0
70: 80 e0 ldi r24, 0x00 ; 0
72: 08 95 ret
【问题讨论】:
-
@KamilCuk:如果你不能在该机器上为给定的数据类型实现无锁原子,你必须为你的底层操作系统设置一个锁。
-
@KamilCuk:我有一个关于在给定控制器的 int var 上设置单个位的问题。在某些机器上实现无日志原子是一个有趣的讨论,但给定的控制器无法实现它。它根本没有汇编指令来从 ram 上的某个位置读取/修改/写入。所以这个讨论对给定的主题没有帮助。
-
弃用的要点是您的单指令更新不是可移植假设。如果您不关心可移植性,您可以为此目的使用一些特定于实现的 intrinsic(或者如果您的编译器不存在这样的内在函数(还),则使用内联汇编)。
标签: c++ volatile c++20 bitflags