isakura 发表于 2023-11-14 16:52:45

请教一个基础的程序问题,一个变量在两个中断中赋值失效


、现在定义了一个变量time,在定时器中的操作
if(time)
{
time--;
}

在另一个中断中,time=20;

会出现time=20的时候会偶尔失效,本来是该重新开始定时的,但是没有。

想知道这种一般怎么搞?每次进中断的时候关闭其他中断?

isakura 发表于 2023-11-14 16:53:45

变量定义 加了 volitate,但是还是会出现问题

t3486784401 发表于 2023-11-14 17:11:29

critical section 临界区问题。 volatile 只限定编译器不缓存,仍不能解决临界区问题。

解决办法:

a. 互斥访问:例如中断禁止嵌套、中断外访问前关中断

b. 确保原子操作:例如使用与 CPU 位宽一致的变量,并确认一个指令完成读 - 修改- 写 操作。

albert_w 发表于 2023-11-14 17:26:28

禁止中断嵌套?
确保一个跑完另一个才能跑

tang_qianfeng 发表于 2023-11-14 17:47:27

32位应该没问题b

isakura 发表于 2023-11-14 18:03:36

tang_qianfeng 发表于 2023-11-14 17:47
32位应该没问题b
(引用自5楼)

用的是雅特力的F413,现在测出来的

isakura 发表于 2023-11-14 18:07:55

tang_qianfeng 发表于 2023-11-14 17:47
32位应该没问题b
(引用自5楼)

我定义的是unsigned char 类型

PPS 发表于 2023-11-14 18:12:41

出这种问题是正常的,这个内核是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:13:19

本帖最后由 tang_qianfeng 于 2023-11-14 18:16 编辑

isakura 发表于 2023-11-14 18:07
我定义的是unsigned char 类型
(引用自7楼)
楼上说的对的,你写20的时候,有可能写完就被覆盖了,把中断优先级搞成一样还出错吗?

isakura 发表于 2023-11-14 18:14:48

tang_qianfeng 发表于 2023-11-14 18:13
char应该不会有问题的
(引用自9楼)

现在就出现了,想的头疼....

chendaon 发表于 2023-11-14 18:36:15

看汇编,单条指令完成内存赋值一般没有大问题。

了无 发表于 2023-11-14 18:48:15

tang_qianfeng 发表于 2023-11-14 18:13
楼上说的对的,你写20的时候,有可能写完就被覆盖了,把中断优先级搞成一样还出错吗?
...
(引用自9楼)

8楼说的对,不想关中断的话,优先级设置成一样应该也可以解决问题。你这个属于多线程操作了。

PPS 发表于 2023-11-14 18:49:58

isakura 发表于 2023-11-14 16:53
变量定义 加了 volitate,但是还是会出现问题
(引用自2楼)

你都没弄清楚volatile的作用。

isakura 发表于 2023-11-14 19:30:47

tang_qianfeng 发表于 2023-11-14 18:13
楼上说的对的,你写20的时候,有可能写完就被覆盖了,把中断优先级搞成一样还出错吗?
...
(引用自9楼)

这个搞定,专牛角尖了,没想到设置优先级....

一直都在想怎么避开两个中断干涉

isakura 发表于 2023-11-14 19:31:39

结贴,多谢各位大佬提供帮助........解决的办法就是朴实无华....

iamseer 发表于 2023-11-15 02:02:40

isakura 发表于 2023-11-14 19:31
结贴,多谢各位大佬提供帮助........解决的办法就是朴实无华....
(引用自15楼)

这不叫解决问题,不搞清楚原子操作的原理下次继续吃亏。

jenkins 发表于 2023-11-15 05:24:31

使用uint32_t

bigharpoon 发表于 2023-11-15 11:49:14

当多个中断或者多个任务读写全局变量时,这个全局变量就成共享资源了。
为避免竞争和数据破坏,满足共享资源互斥条件的方法通常有:
(1)关中断;
(2)禁止任务调度;
(3)使用信号量

isakura 发表于 2023-11-15 15:44:47

bigharpoon 发表于 2023-11-15 11:49
当多个中断或者多个任务读写全局变量时,这个全局变量就成共享资源了。
为避免竞争和数据破坏,满足共享资 ...
(引用自18楼)

我想着关中断,但是也怕频繁中断打开关闭出问题...

没跑系统,不存在调度情况;

增加信号量这个到时候不知道怎么弄

isakura 发表于 2023-11-15 15:45:45

iamseer 发表于 2023-11-15 02:02
这不叫解决问题,不搞清楚原子操作的原理下次继续吃亏。
(引用自16楼)

原子操作理解,但是这种情况下不会处理

isakura 发表于 2023-11-15 15:46:21

jenkins 发表于 2023-11-15 05:24
使用uint32_t
(引用自17楼)

意思是将数据定义为32位的就可以?

shiva_shiva 发表于 2023-11-15 16:36:19

经典问题

wshtyr 发表于 2023-11-15 16:50:42

isakura 发表于 2023-11-15 15:44
我想着关中断,但是也怕频繁中断打开关闭出问题...

没跑系统,不存在调度情况;
(引用自19楼)

不用把单片机当人,只要关中断后干完活迅速恢复中断,不会有问题的。特别是这种只要几条指令的原子操作,关中断是最常规的做法。

随便一个操作系统,每秒至少上百次甚至上千次开关中断,都能正常跑

tang_qianfeng 发表于 2023-11-15 17:11:44

isakura 发表于 2023-11-15 15:44
我想着关中断,但是也怕频繁中断打开关闭出问题...

没跑系统,不存在调度情况;

(引用自19楼)

开关中断又啥问题啊?和你变量++原理差不多,又不是开关继电器

t3486784401 发表于 2023-11-15 17:15:17

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 才能放行。

jenkins 发表于 2023-11-15 17:17:31

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位数据在这种情况下可能不是原子操作。

GZZXB 发表于 2023-11-15 17:53:13

   有时候不太好关中断,也不好设置一样的优先级。如果这个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;
}

陆小凤之北京 发表于 2023-11-15 18:29:27

逻辑上的竞争冒险,关中断是最常用的做法,操作数据尤其是写数据的时候避免被打断。

iamseer 发表于 2023-11-15 22:47:49

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]
查看完整版本: 请教一个基础的程序问题,一个变量在两个中断中赋值失效