use*_*118 0 c++ arduino interrupt volatile c++-standard-library
我现在有一些代码希望在Teensy 3.6微控制器上的基于计时器的中断中运行。该代码访问类的[global]对象数组。我已经将该数组和所有成员变量标记为volatile,我认为这是正确处理中断的第一步。
我标记为volatile的成员变量之一是std :: bitset,我想称其为非易失性方法,我不能这样做
"passing 'volatile std::bitset<16u>' as 'this' argument discards qualifiers [-fpermissive]"
Run Code Online (Sandbox Code Playgroud)
我想我可以复制位集库并将所有内容切换为volatile,但是我认为这不是必需的,所以我认为有更好的解决方案,或者我在错误地考虑问题。
请让我知道应该怎么做。
这些答案似乎建议在ISR和多线程程序中访问ISR中的全局变量时使用volatile: C'Volatile'关键字?,
在中断例程中使用C ++对象(和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;
}
Run Code Online (Sandbox Code Playgroud)
当我们查看程序集时,我们在中看到,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
Run Code Online (Sandbox Code Playgroud)
优化是好的,并且在大多数情况下,您不需要使用volatile。如果您正在执行内存映射的IO,或者正在将值写为引脚,就好像它们是一个指针一样,那就是使用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];
}
}
Run Code Online (Sandbox Code Playgroud)
然后,我们可以正常地编写一个类,并且只要它是可复制的,就可以从易失性版本中创建一个副本:
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);
};
Run Code Online (Sandbox Code Playgroud)
我们还可以将此行为封装在一个类中,以便我们可以“检出”易失变量:
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();
}
};
Run Code Online (Sandbox Code Playgroud)
使用此功能,我们可以创建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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
92 次 |
| 最近记录: |