搜索
bottom↓
回复: 20

代码临界区问题

[复制链接]

出0入0汤圆

发表于 2009-4-28 10:02:55 | 显示全部楼层 |阅读模式
你好,我想问一下。在单片机编程中,我有一个位结构。一些位用于中断,一些位用于正常的大循环。这时,我在大循环中写一些数据到一些位(这些位只有在大循环中才会赋值),这时中断来了,修改了这个位结构中的另外一些位(这些位只能够用于中断中),当退出中断时,会不会改变使主循环中要赋值的位发生改变,主循环要赋值的位是不是正确,谢谢。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2009-4-28 11:16:40 | 显示全部楼层
视具体情况而定,中断中修改的位如果和主循环中要修改的位同属一个变量可能会出问题,
中断中修改的值可能会无效....
主循环中的赋值不会有问题

如果是操作端口,应该没有问题,因为端口置位是一条指令完成的

出0入0汤圆

 楼主| 发表于 2009-4-29 09:23:47 | 显示全部楼层
中断中修改的位如果和主循环中要修改的位同属一个变量可能会出问题,这个我明白。
现在是中断中修改的位和主循环中要修改的位不同属一个变量,会有问题吗?

出0入0汤圆

发表于 2009-4-29 09:29:40 | 显示全部楼层
不会。保险起见,你定义2个结构吧

出0入0汤圆

发表于 2009-4-29 09:43:31 | 显示全部楼层
不属于一个8位变量,没有越界操作,则完全不会有问题
对于8位芯片是这样

出0入0汤圆

 楼主| 发表于 2009-4-29 10:24:45 | 显示全部楼层
我用的芯片是atmega128
typedef struct
{
    unsigned char pc_control:  1;
    unsigned char outon: 1;
    unsigned char lock: 1;
    unsigned char Rev: 1 ;
    unsigned char over_voltge: 1;
    unsigned char over_power:   1;
    unsigned char TransOn:       1;
    unsigned char over_current:  1;
    unsigned char StOn:       1;
    unsigned char ReadForOutOn:       1;
    unsigned char Program:       1;
    unsigned char Cali:       1;
    unsigned char OverTemperature:       1;
    unsigned char OffSound:       1;
    unsigned char SelExtTrig:       1;
    unsigned char DataRetFromGpib:       1;
}loadstate_type ;
比如说DataRetFromGpib属于中断要赋值的位,其他的都是主循环要赋值的位。
如果这时我正在给主循环要赋值的位赋值,这时中断来了,给DataRetFromGpib位赋了值,退出中断,主循环继续给主循环要赋值的位继续赋值,赋完以后,这些值都是我想要的吗?

出0入0汤圆

发表于 2009-4-29 10:31:16 | 显示全部楼层
对于8位单片机
unsigned char pc_control:  1;
    unsigned char outon: 1;
    unsigned char lock: 1;
    unsigned char Rev: 1 ;
    unsigned char over_voltge: 1;
    unsigned char over_power:   1;
    unsigned char TransOn:       1;
    unsigned char over_current:  1;
不受任何影响
======================================
unsigned char StOn:       1;
    unsigned char ReadForOutOn:       1;
    unsigned char Program:       1;
    unsigned char Cali:       1;
    unsigned char OverTemperature:       1;
    unsigned char OffSound:       1;
    unsigned char SelExtTrig:       1;
在主循环赋值不受影响
======================================
unsigned char DataRetFromGpib:       1;
在中断赋值后,返回主循环中可能会出现赋值不成功

出0入0汤圆

发表于 2009-4-29 10:37:14 | 显示全部楼层
把中断要赋值的位单独拿出来吧(直接用一个字节代替),不要与其他位掺和在一起。

atmega48有三字节专用存储位的寄存器 GPIOR0,GPIOR1,GPIOR2。
把位放到IO寄存器,效率会高很多。
不知道atmega128有没有这三个寄存器。

程序多写临界段也没什么不好。

出0入0汤圆

 楼主| 发表于 2009-4-29 10:45:11 | 显示全部楼层
IO寄存器与IO端口寄存器有区别吗?
把位放到IO寄存器,会不会引起输出引脚的变化。

出0入0汤圆

发表于 2009-4-29 10:52:40 | 显示全部楼层

(原文件名:Image0280.JPG)

出0入0汤圆

发表于 2009-4-29 10:52:53 | 显示全部楼层
"atmega48有三字节专用存储位的寄存器 GPIOR0,GPIOR1,GPIOR2。"
LZ仔细看看,都说了是存储用的寄存器了,
这种可以直接对应sbi等位操作指令...我也不知道m128有没有这三个...
我只用过m16和m32

出0入0汤圆

 楼主| 发表于 2009-4-29 11:05:07 | 显示全部楼层
m128没有,所以不能用IO寄存器,IO寄存器与IO端口寄存器还是有区别。

typedef struct
{
        unsigned char SoudFreq: 1;
        unsigned char DisOrCleSetVal: 1;
        unsigned char ConfAndQiut: 1;
        unsigned char bIs_Meas_Volt: 1;
        unsigned char IntSetChag: 1;
        unsigned char DataMissErr: 1;
        unsigned char DataAck: 1;
        unsigned char SystRemFromGPIB: 1;
        unsigned char SystLocFromGPIB: 1;
        unsigned char SystRwlockFromGPIB: 1;
        unsigned char IFCSDCFromGPIB: 1;
        unsigned char LoadSystRwlockFromGPIB: 1;
        unsigned char FlagOfFlow: 1;
        unsigned char TmpFlagOfFlow: 1;
}Int_flag_type;

Int_flag.SoudFreq=!Int_flag.SoudFreq;
这是一段汇编结果,我正在研究。
+00001E76:   91800750    LDS     R24,0x0750       Load direct from data space
+00001E78:   7081        ANDI    R24,0x01         Logical AND with immediate
+00001E79:   821E        STD     Y+6,R1           Store indirect with displacement
+00001E7A:   2388        TST     R24              Test for Zero or Minus
+00001E7B:   F411        BRNE    PC+0x03          Branch if not equal
+00001E7C:   E081        LDI     R24,0x01         Load immediate
+00001E7D:   838E        STD     Y+6,R24          Store indirect with displacement
+00001E7E:   819E        LDD     R25,Y+6          Load indirect with displacement
+00001E7F:   7091        ANDI    R25,0x01         Logical AND with immediate
+00001E80:   91800750    LDS     R24,0x0750       Load direct from data space
+00001E82:   7F8E        ANDI    R24,0xFE         Logical AND with immediate
+00001E83:   2B89        OR      R24,R25          Logical OR
+00001E84:   93800750    STS     0x0750,R24       Store direct to data space
主要研究“在中断赋值后,返回主循环中可能会出现赋值不成功 ”这种说法。

出0入0汤圆

发表于 2009-4-29 11:37:38 | 显示全部楼层
OR      R24,R25          Logical OR
;中断如果发生在这里,则中断中修改内存0x0750后,退出中断,下面又被赋成进入中断之前R24的值,当然就是“中断赋值不成功”
STS     0x0750,R24       Store direct to data space

出0入0汤圆

发表于 2009-4-29 11:44:31 | 显示全部楼层
解决方法有两种
1.生成不止一条汇编运算前前禁止中断,运算后恢复中断
不推荐1,很麻烦

2.将中断要赋的位变量与非中断要赋值的位变量分开到不同的8位RAM地址中
合理安排一下顺序就搞定了,最好再注释一下
比如

typedef struct
{  
    unsigned char pc_control:  1;
    unsigned char outon: 1;
    unsigned char lock: 1;
    unsigned char Rev: 1 ;
    unsigned char over_voltge: 1;
    unsigned char over_power:   1;
    unsigned char TransOn:       1;
    unsigned char over_current:  1;
    //
    unsigned char StOn:       1;
    unsigned char ReadForOutOn:       1;
    unsigned char Program:       1;
    unsigned char Cali:       1;
    unsigned char OverTemperature:       1;
    unsigned char OffSound:       1;
    unsigned char SelExtTrig:       1;
    unsigned char Reserved1:       1;
    // 以下中断中修改
    unsigned char DataRetFromGpib:       1;
}loadstate_type ;

出0入0汤圆

 楼主| 发表于 2009-4-29 11:49:37 | 显示全部楼层
研究结果如下:LDS     R24,0x0750       这是调用
                     .
                     .
                  中间运算
              LDS     R24,0x0750       这是调用
                  中间运算
                     .
                     .
              STS     0x0750,R24       运算结果保存
假如我在运行完第一句“LDS     R24,0x0750”来了一个中断,改变其他一些位,然后保存退出。这时回到主循环,r24没有得到及时更新,r24与其他一些位进行比较,假如r24=0,(r1初始设为0)不跳转,r25就为1,如果r24=1,r25=0;LDS     R24,0x0750 这时调出实际值,与r25或,然后保存。这个结论是“在中断赋值后,返回主循环中不会会出现赋值不成功 ”
假如我在运行完第二句“LDS     R24,0x0750”来了一个中断,改变其他一些位,然后保存退出。这时回到主循环,r24没有得到及时更新,与r25或,然后保存。这时中断变量值更改被忽略了。这个结论是“在中断赋值后,返回主循环中,中断赋值不成功 ”
总的结论如下:
在中断赋值后,返回主循环中,中断赋值可能不成功,主程序赋值没问题。

出0入0汤圆

发表于 2009-4-29 11:51:07 | 显示全部楼层
第二种方严重依赖平台与编译器,不推荐。

第一种方法安全,通用。
至于麻烦?只不过多写两,三句代码而已,能有多麻烦?

出0入0汤圆

发表于 2009-4-29 11:55:25 | 显示全部楼层
赋值多了的地方多了就麻烦了,就算用了宏,过多的屏蔽中断也不是很爽...
最好的方法是分在两个结构体中,移植时使用对齐就不存在问题....

出0入0汤圆

发表于 2009-4-29 12:17:52 | 显示全部楼层
赋值多了的地方多了就麻烦了,就算用了宏,过多的屏蔽中断也不是很爽...
________________________________________________________________________


再多也没什么。OS临界段铺天盖地,还不照样用。

重要不是多,而是屏蔽中断时间不能太长。

出0入0汤圆

 楼主| 发表于 2009-4-29 13:04:12 | 显示全部楼层
谢谢诸位的讨论,我已经决定定义一个专门给中断的位结构。彻底解决问题。

出10入61汤圆

发表于 2016-12-22 21:16:50 | 显示全部楼层
void_c 发表于 2009-4-29 12:17
赋值多了的地方多了就麻烦了,就算用了宏,过多的屏蔽中断也不是很爽...
_______________________________ ...

请教:
我的51程序是典型的结构, 中断里接收串口/slave spi放入环形队列里, 主循环里从队列取出转发或处理.
为了保证原子操作, 访问队列的这段代码要加保护,我用的关全局中断.
现在问题来了, 主循环中要出队时,关了中断,这时SPI或串口有数据来了,但主循环并没有这么快取完数据开中断, 导致无法进中断接收数据(SPI大约20us就会收一个数据)

能优化的都优化了,但还是因为关了中断丢了数据,但访问临界区又不得不关中断, 请教有什么解决方法?

出0入0汤圆

发表于 2017-1-18 15:24:06 | 显示全部楼层
主程序中操作环形队列不能操作20us
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-5-17 01:49

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

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