我有一些代码,现在希望在 Teensy 3.6 微控制器上的基于定时器的中断中运行。该代码访问类的[全局] 对象数组。我已将该数组和所有成员变量标记为 volatile,我相信这是正确处理中断的第一步。
我标记为 volatile 的成员变量之一是 std::bitset ,我想称它为非 volatile 方法,我不能这样做
"passing 'volatile std::bitset<16u>' as 'this' argument discards qualifiers [-fpermissive]"
我想我可以复制 bitset 库并将所有内容切换为 volatile,但我认为这不是必需的,所以我认为有更好的解决方案,或者我正在错误地思考问题。
请让我知道应该怎么做。
这些答案似乎建议在访问 ISR 中的全局变量时使用 volatile:ISR 和多线程程序中的 C 'Volatile' 关键字?,
在中断例程中使用 C++ 对象(和 volatile)的正确方法是什么?,
这是对许多推荐使用的外部来源的补充。也许我的原始信息不清楚,或者我的情况与这些不同。
您不应该将所有内容都设置为 volatile。Volatile 有一个特定的目的,那就是防止编译器优化对内存的读取和写入。让我们看一个非常简单的例子。
int regular_sum(int* ptr) {
int a = *ptr;
int b = *ptr;
return a + b;
}
int volatile_sum(int volatile* ptr) {
int a = *ptr;
int b = *ptr;
return a + b;
}
当我们查看程序集时,我们看到在 中regular_sum
,编译器意识到您正在取消引用同一个指针两次,并将其优化为仅取消引用一次。但是在 中volatile_sum
,编译器插入了两个解引用:
regular_sum(int*):
mov eax, DWORD PTR [rdi]
add eax, eax
ret
volatile_sum(int volatile*):
mov eax, DWORD PTR [rdi]
mov edx, DWORD PTR [rdi]
add eax, edx
ret
优化是好的,大多数时候,你不需要使用 volatile。如果您正在执行内存映射 IO,或者您将值写入引脚,就好像它们是一个指针一样,这就是您使用 volatile的地方。重申 Nathan Oliver 所说的话,
您只需要在硬件可以更改变量值的变量上使用 volatile ,因为编译器无法知道这一点。这就是 volatile 的作用,让编译器知道这是一个特殊的变量,可以以它不知道的方式进行更改。如果硬件不能改变你的值,那么你就不需要 volatile。
但是如果你在一个对象上进行计算,不要使用 volatile。对普通对象进行计算,然后将结果复制到 volatile 指针。
易失性和中断服务例程。
volatile
用于可能被中断服务程序修改的全局变量是合适的。话虽如此,volatile
不能与对象一起使用,std::bitset
因为std::bitset
不支持易失性操作,并且std::bitset
不能简单地复制。
在这方面,您有两个选择:
std::vector<volatile bool>
如果您有一个可以简单复制的类,那么您可以执行以下操作。首先,我们必须定义函数来允许我们在 volatile 类型之间进行复制:
template<class T>
T volatile_copy(T const volatile& source) {
static_assert(std::is_trivially_copyable_v<T>, "Input must be trivially copyable");
T dest;
auto* dest_ptr = dynamic_cast<char*>(&dest);
auto* source_ptr = dynamic_cast<char const volatile*>(&source);
for(int i = 0; i < sizeof(T); i++) {
dest_ptr[i] = source_ptr[i];
}
return dest;
}
template<class T>
void volatile_assign(T volatile& dest, T const& source) {
static_assert(std::is_trivially_copyable_v<T>, "Input must be trivially copyable");
auto* source_ptr = dynamic_cast<char*>(&source);
auto* dest_ptr = dynamic_cast<char volatile*>(&dest);
for(int i = 0; i < sizeof(T); i++) {
dest_ptr[i] = source_ptr[i];
}
}
然后,我们可以正常编写一个类,只要它是可复制的,我们就可以从易变版本创建一个副本:
struct MyBitset {
uint64_t bits;
// Logic
void flip() {
bits = ~bits;
}
void addOne() {
bits++;
}
};
volatile MyBitset flags;
void interrupt_handler() {
auto local = volatile_copy(flags);
// Do stuff to local
volatile_assign(flags, local);
};
我们还可以将此行为封装在一个类中,以便我们可以“检查”可变变量:
template<class T>
struct Checkout {
T local;
T volatile& source;
Checkout(T volatile& source)
: local(volatile_copy(source))
, source(source) {}
void save() {
volatile_assign(source, local);
}
~Checkout() {
save();
}
};
使用它允许我们创建volatile
变量的本地副本,对它们进行修改,结果将自动保存:
volatile MyBitset flags;
void interrupt_handler() {
auto f = Checkout(::flags);
f.local.flip(); //We can call whatever member functions we want on the local
// When the function exits, changes made to the local are automatically assigned to the volatile global
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句