打印

关于前后台程序操作同一个变量的问题

[复制链接]
2450|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiaoing|  楼主 | 2008-10-7 19:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
今天写了个单片机测频的程序,突然想到关于前后台程序操作同一个变量的问题,实例代码如下。其中MsuFqy是有可能前后台程序同时操作的变量,这里的计数器0中断对变量MsuFqy进行写操作,函数MsuReadFqy对MsuFqy进行读操作,那么有没有必要在函数MsuReadFqy中先关总中断,然后才读取MsuReadFqy的值,重新打开总中断?还是直接读就可以了?大家都知道如果函数MsuReadFqy是对MsuFqy变量进行写操作,因为变量MsuFqy是int型的有两个字节组成,不能一条指令完成读写操作,也就是说如果函数MsuReadFqy正在对变量MsuFqy进行写操作,还没完成写操作,此时进入计时器0中断,同样对变量MsuFqy进行写操作,那么就会破坏变量变量MsuFqy,导致函数MsuReadFqy写变量MsuFqy错误,所以对于写操作,必须通过开关中断来避免这种情况。那么对于这里的读操作,是否也有这个必要?说了这么多,不知道大家有没有明白我的意思?呵呵

#include <reg52.h>


static unsigned int MsuFqy;    /* 测量频率 */


void MsuTimer0Init (void)
{
  TMOD &= 0xF0;               /* 清零与定时器0相关的位,与定时器1相关的位不变 */
  TMOD |= 0x01;               /* 定时器0工作模式1 */

  TH0 = 0x9E;                 /* 定时25ms对应的定时器0重装值 */
  TL0 = 0x58;

  TF0 = 0;                    /* 清零定时器0中断标志位 */
  ET0 = 1;                    /* 允许定时器0中断 */
  TR0 = 1;                    /* 启动定时器0 */
}

void MsuTimer0Isr (void) interrupt 1
{
  EA = 0;                     /* 关总中断 */

  TR0 = 0;                    /* 停止定时器0 */

#if 1
    TR1 = 0;                  /* 停止计数器1 */

    MsuFqy = (unsigned int)TH1 << 8;
    MsuFqy |= (unsigned int)TL1;

    TR1 = 1;                  /* 启动计数器1 */
#endif

  TH0 = 0x9E;                 /* 定时25ms对应的定时器0重装值 */
  TL0 = 0x58;

  TR0 = 1;                    /* 重新启动定时器0 */

  EA = 1;                     /* 关总中断 */
}

unsigned int MsuReadFqy (void)
{
  unsigned int k;

  EA = 0;                     /* 这里有没有必要开关总中断? */
  k = MsuFqy;
  EA = 1;                     /* 这里有没有必要开关总中断? */

  return (k);
}

void main (void)
{
  unsigned int fqy;

  MsuTimer0Init();
  EA = 1;

  while (1)
  {
    fqy = MsuReadFqy();
  }
}

相关帖子

沙发
zyok| | 2008-10-7 19:23 | 只看该作者

有必要~

使用特权

评论回复
板凳
xiaoing|  楼主 | 2008-10-7 19:38 | 只看该作者

回复主题:关于前后台程序操作同一个变量的问题

    以下再贴上上述代码的Keil反汇编代码,为便于大家阅读,我整理了下。大家可以发现变量MsuFqy其实是0x08(MSB)和0x09(LSB)组成的,当函数MsuReadFqy正在对变量MsuFqy进行读操作时,即语句:
          MOV      R6,0x08  ;MSB
          MOV      R7,0x09  ;LSB
若此时,只执行了“MOV R6,0x08”,“MOV R7,0x09”还没有执行,此时刚好产生计数器0中断,对变量MsuFqy进行写操作,改写单元0x08和0x09,写完毕后返回函数MsuReadFqy继续读操作即执行“MOV R7,0x09”,此时0x09单元的值已经被改写了,所以函数MsuReadFqy读取量MsuFqy的值其实是错误的,所以说我认为在读写变量时都有必要开关中断。不知各位大牛如何认为?


          ORG      0000H
          LJMP     start

          ORG      000BH
          LJMP     MsuTimer0Isr


;-----------------------------------------------------
; MsuReadFqy()
;-----------------------------------------------------
MsuReadFqy:
          CLR      EA

          MOV      R6,0x08  ;MSB
          MOV      R7,0x09  ;LSB

          SETB     EA

          RET


;-----------------------------------------------------
; MsuTimer0Isr()
;-----------------------------------------------------
MsuTimer0Isr:

          PUSH     ACC        ; 保护现场
          PUSH     PSW


          MOV      PSW,#0x00

          ;-------------------------------------------
          PUSH     0x04       ; 保护寄存器
          PUSH     0x05
          PUSH     0x06
          PUSH     0x07
 
          CLR      EA
          CLR      TR0

          ;-------------------------------------------
          CLR      TR1

          MOV      R7,TH1
          MOV      A,R7
          MOV      R6,A
          MOV      R5,TL1
          MOV      R4,#0x00

          CLR      A
          ADD      A,R5
          MOV      0x09,A

          MOV      A,R4
          ADDC     A,R6
          MOV      0x08,A

          SETB     TR1
          ;-------------------------------------------

          MOV      TH0,#0x9E
          MOV      TL0,#0x58

          SETB     TR0
          SETB     EA

          ;-------------------------------------------
          POP      0x07       ; 恢复寄存器
          POP      0x06
          POP      0x05
          POP      0x04


          POP      PSW        ; 恢复现场
          POP      ACC

          RETI

;-----------------------------------------------------
; MsuTimer0Init()
;-----------------------------------------------------
MsuTimer0Init:
          ANL      TMOD,#0xF0
          ORL      TMOD,#0x01
          MOV      TH0,#0x9E
          MOV      TL0,#0x58
          CLR      TF0
          SETB     ET0

          RET      



;-----------------------------------------------------
; main()
;-----------------------------------------------------
main:
          LCALL    MsuTimer0Init
          SETB     EA

loop1:
          LCALL    MsuReadFqy
          MOV      0x0A,R6      ; R6 is MSB
          MOV      0x0B,R7      ; R7 is LSB
          SJMP     loop1



;-----------------------------------------------------
; start()
;-----------------------------------------------------
start:
          MOV      R0,#0x7F
          CLR      A

loop2:    MOV      @R0,A
          DJNZ     R0,loop2
          MOV      SP,#0x0B
          LJMP     MAIN

使用特权

评论回复
地板
5880527| | 2008-10-7 19:52 | 只看该作者

没必要,以前讨论过

在函数读字节之前先把某一位单元清0,在中断里需要修改此字节的地方把那位单元置1,函数读完后字节后再判断一下此标志是否给置1了,如还是0则读到的数据可以用,是1就再读一次就可以了

使用特权

评论回复
5
xiaoing|  楼主 | 2008-10-7 20:36 | 只看该作者

回复主题:关于前后台程序操作同一个变量的问题

楼上的也是一个解决方法,但前提条件是两次中断有足够的时间间隔!想想还是开关中断简单。

使用特权

评论回复
6
johnwjl| | 2008-10-7 20:52 | 只看该作者

关中断,和操作系统中的临界段一样。

使用特权

评论回复
7
5880527| | 2008-10-7 21:18 | 只看该作者

为什么要关

把那两字节读过来放到临时单元里,就两条语句而已,如果真那么短的时间又来中断,你咋就不怕关了中断后的那两条读字节语句丢失了一次中断呢,你主循环根本就谈不上及时性啥的,如果那位单元给置1了那本次调用函数直接跳过,不进行任何相关操作便是

使用特权

评论回复
8
xiaoing|  楼主 | 2008-10-8 13:33 | 只看该作者

回复主题:关于前后台程序操作同一个变量的问题

恩,楼上有道理。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

22

主题

158

帖子

0

粉丝