搜索
bottom↓
回复: 28

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

[复制链接]

出0入14汤圆

发表于 2023-11-14 16:52:45 | 显示全部楼层 |阅读模式

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

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

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

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

出0入14汤圆

 楼主| 发表于 2023-11-14 16:53:45 | 显示全部楼层
变量定义 加了 volitate,但是还是会出现问题

出200入2554汤圆

发表于 2023-11-14 17:11:29 | 显示全部楼层
critical section 临界区问题。 volatile 只限定编译器不缓存,仍不能解决临界区问题。

解决办法:

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

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

出0入42汤圆

发表于 2023-11-14 17:26:28 | 显示全部楼层
禁止中断嵌套?
确保一个跑完另一个才能跑

出0入18汤圆

发表于 2023-11-14 17:47:27 来自手机 | 显示全部楼层
32位应该没问题b

出0入14汤圆

 楼主| 发表于 2023-11-14 18:03:36 | 显示全部楼层

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

出0入14汤圆

 楼主| 发表于 2023-11-14 18:07:55 | 显示全部楼层

我定义的是unsigned char 类型

出30入42汤圆

发表于 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;

出0入18汤圆

发表于 2023-11-14 18:13:19 来自手机 | 显示全部楼层
本帖最后由 tang_qianfeng 于 2023-11-14 18:16 编辑
isakura 发表于 2023-11-14 18:07
我定义的是unsigned char 类型
(引用自7楼)

楼上说的对的,你写20的时候,有可能写完就被覆盖了,把中断优先级搞成一样还出错吗?

出0入14汤圆

 楼主| 发表于 2023-11-14 18:14:48 | 显示全部楼层

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

出0入4汤圆

发表于 2023-11-14 18:36:15 来自手机 | 显示全部楼层
看汇编,单条指令完成内存赋值一般没有大问题。

出0入8汤圆

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

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

出30入42汤圆

发表于 2023-11-14 18:49:58 | 显示全部楼层
isakura 发表于 2023-11-14 16:53
变量定义 加了 volitate,但是还是会出现问题
(引用自2楼)

你都没弄清楚volatile的作用。

出0入14汤圆

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

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

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

出0入14汤圆

 楼主| 发表于 2023-11-14 19:31:39 | 显示全部楼层
结贴,多谢各位大佬提供帮助........解决的办法就是朴实无华....

出0入309汤圆

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

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

出0入0汤圆

发表于 2023-11-15 05:24:31 来自手机 | 显示全部楼层
使用uint32_t

出0入0汤圆

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

出0入14汤圆

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

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

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

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

出0入14汤圆

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

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

出0入14汤圆

 楼主| 发表于 2023-11-15 15:46:21 | 显示全部楼层

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

出0入0汤圆

发表于 2023-11-15 16:36:19 | 显示全部楼层
经典问题

出0入42汤圆

发表于 2023-11-15 16:50:42 | 显示全部楼层
isakura 发表于 2023-11-15 15:44
我想着关中断,但是也怕频繁中断打开关闭出问题...

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

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

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

出0入18汤圆

发表于 2023-11-15 17:11:44 来自手机 | 显示全部楼层
isakura 发表于 2023-11-15 15:44
我想着关中断,但是也怕频繁中断打开关闭出问题...

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

(引用自19楼)

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

出200入2554汤圆

发表于 2023-11-15 17:15:17 | 显示全部楼层
isakura 发表于 2023-11-15 15:46
意思是将数据定义为32位的就可以?
(引用自21楼)

这个得你去汇编层确认,但目测不太行。

如果变量是寄存器级别,也许可以;但你全局变量一般会被分配到内存区,然后就没法原子操作了:

C语言: time++;

time 是寄存器变量的情形: inc  reg;

time 是一般内存变量的情形:
load  reg, ramxxx;
inc  reg;
store  ramxxx, reg;

-----------------------------------------

CPU 一般在汇编一层是原子操作,即一条汇编指令不能被另一条打断。

但你看存储区有个 load - inc -store 这样的三条汇编,就很容易被中断打断。
这样一来高优先级的 store,容易被后续低优先级的 store 再次覆盖掉,导致出错。

-----------------------------------------

在 win32 环境,有专门的 EnterCriticalSection( )  和 LeaveCriticalSection( ) 接口,就是处理多线程临界区。

每个 Enter 都会在必要时等待,确保其他位置都正确调用了 Leave 才能放行。

出0入0汤圆

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

出0入36汤圆

发表于 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;
}

出0入0汤圆

发表于 2023-11-15 18:29:27 | 显示全部楼层
逻辑上的竞争冒险,关中断是最常用的做法,操作数据尤其是写数据的时候避免被打断。

出0入309汤圆

发表于 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位寄存器通常是一个单一的硬件操作,不会影响其他寄存器。”你的变量是一边只读一边只写当然没问题,你要读写怎么办?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-29 08:14

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表