请教一个基础的程序问题,一个变量在两个中断中赋值失效
、现在定义了一个变量time,在定时器中的操作
if(time)
{
time--;
}
在另一个中断中,time=20;
会出现time=20的时候会偶尔失效,本来是该重新开始定时的,但是没有。
想知道这种一般怎么搞?每次进中断的时候关闭其他中断? 变量定义 加了 volitate,但是还是会出现问题 critical section 临界区问题。 volatile 只限定编译器不缓存,仍不能解决临界区问题。
解决办法:
a. 互斥访问:例如中断禁止嵌套、中断外访问前关中断
b. 确保原子操作:例如使用与 CPU 位宽一致的变量,并确认一个指令完成读 - 修改- 写 操作。 禁止中断嵌套?
确保一个跑完另一个才能跑 32位应该没问题b tang_qianfeng 发表于 2023-11-14 17:47
32位应该没问题b
(引用自5楼)
用的是雅特力的F413,现在测出来的 tang_qianfeng 发表于 2023-11-14 17:47
32位应该没问题b
(引用自5楼)
我定义的是unsigned char 类型 出这种问题是正常的,这个内核是load/store结构的,先load,然后改变,最后store回去。你这个属于还没store回去就被中断改成20,然后退出中断回去后继续store,你写的20当然会被覆盖。
流程如下,假设当前time为30:
1. 先Load time的值30到寄存器;
2. 把30加1变成31;
3. 被中断time变成20;
4. 中断退出把31store回time,于是time变成31; 本帖最后由 tang_qianfeng 于 2023-11-14 18:16 编辑
isakura 发表于 2023-11-14 18:07
我定义的是unsigned char 类型
(引用自7楼)
楼上说的对的,你写20的时候,有可能写完就被覆盖了,把中断优先级搞成一样还出错吗?
tang_qianfeng 发表于 2023-11-14 18:13
char应该不会有问题的
(引用自9楼)
现在就出现了,想的头疼.... 看汇编,单条指令完成内存赋值一般没有大问题。 tang_qianfeng 发表于 2023-11-14 18:13
楼上说的对的,你写20的时候,有可能写完就被覆盖了,把中断优先级搞成一样还出错吗?
...
(引用自9楼)
8楼说的对,不想关中断的话,优先级设置成一样应该也可以解决问题。你这个属于多线程操作了。 isakura 发表于 2023-11-14 16:53
变量定义 加了 volitate,但是还是会出现问题
(引用自2楼)
你都没弄清楚volatile的作用。 tang_qianfeng 发表于 2023-11-14 18:13
楼上说的对的,你写20的时候,有可能写完就被覆盖了,把中断优先级搞成一样还出错吗?
...
(引用自9楼)
这个搞定,专牛角尖了,没想到设置优先级....
一直都在想怎么避开两个中断干涉 结贴,多谢各位大佬提供帮助........解决的办法就是朴实无华.... isakura 发表于 2023-11-14 19:31
结贴,多谢各位大佬提供帮助........解决的办法就是朴实无华....
(引用自15楼)
这不叫解决问题,不搞清楚原子操作的原理下次继续吃亏。 使用uint32_t 当多个中断或者多个任务读写全局变量时,这个全局变量就成共享资源了。
为避免竞争和数据破坏,满足共享资源互斥条件的方法通常有:
(1)关中断;
(2)禁止任务调度;
(3)使用信号量 bigharpoon 发表于 2023-11-15 11:49
当多个中断或者多个任务读写全局变量时,这个全局变量就成共享资源了。
为避免竞争和数据破坏,满足共享资 ...
(引用自18楼)
我想着关中断,但是也怕频繁中断打开关闭出问题...
没跑系统,不存在调度情况;
增加信号量这个到时候不知道怎么弄 iamseer 发表于 2023-11-15 02:02
这不叫解决问题,不搞清楚原子操作的原理下次继续吃亏。
(引用自16楼)
原子操作理解,但是这种情况下不会处理 jenkins 发表于 2023-11-15 05:24
使用uint32_t
(引用自17楼)
意思是将数据定义为32位的就可以? 经典问题 isakura 发表于 2023-11-15 15:44
我想着关中断,但是也怕频繁中断打开关闭出问题...
没跑系统,不存在调度情况;
(引用自19楼)
不用把单片机当人,只要关中断后干完活迅速恢复中断,不会有问题的。特别是这种只要几条指令的原子操作,关中断是最常规的做法。
随便一个操作系统,每秒至少上百次甚至上千次开关中断,都能正常跑
isakura 发表于 2023-11-15 15:44
我想着关中断,但是也怕频繁中断打开关闭出问题...
没跑系统,不存在调度情况;
(引用自19楼)
开关中断又啥问题啊?和你变量++原理差不多,又不是开关继电器 isakura 发表于 2023-11-15 15:46
意思是将数据定义为32位的就可以?
(引用自21楼)
这个得你去汇编层确认,但目测不太行。
如果变量是寄存器级别,也许可以;但你全局变量一般会被分配到内存区,然后就没法原子操作了:
C语言: time++;
time 是寄存器变量的情形: increg;
time 是一般内存变量的情形:
loadreg, ramxxx;
increg;
storeramxxx, reg;
-----------------------------------------
CPU 一般在汇编一层是原子操作,即一条汇编指令不能被另一条打断。
但你看存储区有个 load - inc -store 这样的三条汇编,就很容易被中断打断。
这样一来高优先级的 store,容易被后续低优先级的 store 再次覆盖掉,导致出错。
-----------------------------------------
在 win32 环境,有专门的 EnterCriticalSection( )和 LeaveCriticalSection( ) 接口,就是处理多线程临界区。
每个 Enter 都会在必要时等待,确保其他位置都正确调用了 Leave 才能放行。 isakura 发表于 2023-11-15 15:46
意思是将数据定义为32位的就可以?
(引用自21楼)
单片机是多少位的就定义成多少位的变量
8位、16位和32位单片机对uint8_t、uint16_t和uint32_t的读写是否原子操作取决于单片机的硬件设计。
对于大多数8位单片机,uint8_t的读写通常是原子操作。这是因为8位单片机通常使用8位寄存器来存储数据。读取或写入一个8位寄存器通常是一个单一的硬件操作,不会影响其他寄存器。
对于16位单片机,uint16_t的读写通常也是原子操作。这是因为16位单片机通常使用16位寄存器来存储数据。读取或写入一个16位寄存器通常是一个单一的硬件操作,不会影响其他寄存器。
对于32位单片机,uint32_t的读写是否原子操作取决于单片机的硬件设计。有些32位单片机使用32位寄存器来存储数据。读取或写入一个32位寄存器通常是一个单一的硬件操作,不会影响其他寄存器。但也有一些32位单片机使用两个16位寄存器来存储32位数据。读取或写入一个32位数据在这种情况下可能不是原子操作。 有时候不太好关中断,也不好设置一样的优先级。如果这个time不是需求很精准的话。
1) 加个flag
static volatile uint8_t a;
flag=1;
time=20;
flag=0;
if(0==flag)
{
if(time)
{
time--;
}
}
2) 写入后校验是否写入ok
time=20;
a=time;
if(20==a)
{
ok;
}
else
{
time=20;
} 逻辑上的竞争冒险,关中断是最常用的做法,操作数据尤其是写数据的时候避免被打断。 jenkins 发表于 2023-11-15 17:17
单片机是多少位的就定义成多少位的变量
8位、16位和32位单片机对uint8_t、uint16_t和uint32_t的读写是否 ...
(引用自26楼)
不要在这发这些有问题的回答误导新人。
举个例子:
对于大多数8位单片机,uint8_t的读写通常是原子操作。这是因为8位单片机通常使用8位寄存器来存储数据。读取或写入一个8位寄存器通常是一个单一的硬件操作,不会影响其他寄存器。
“uint8_t的读写通常是原子操作”,“通常”有个屁用,不通常的时候就让程序出错吗?
“这是因为8位单片机通常使用8位寄存器来存储数据。”你的数据不进内存?寄存器和内存交换数据怎么办?
“读取或写入一个8位寄存器通常是一个单一的硬件操作,不会影响其他寄存器。”你的变量是一边只读一边只写当然没问题,你要读写怎么办?
页:
[1]