用关中断和互斥量来保护多线程共享的全局变量

发布者:勾剑寒最新更新时间:2021-11-04 来源: eefocus关键字:单片机  关中断  互斥量  多线程共享  全局变量 手机看文章 扫描二维码
随时随地手机看文章

一、使用proteus绘制简单的电路图,用于后续仿真

二、编写程序


/********************************************************************************************************************

---- @Project: Mutex

---- @File: main.c

---- @Edit: ZHQ

---- @Version: V1.0

---- @CreationTime: 20200810

---- @ModifiedTime: 20200810

---- @Description: LED闪烁,蜂鸣器报警

---- 让蜂鸣器在前面3秒发生一次短叫报警,在后面6秒发生一次长叫报警,如此反复循环。 

---- 单片机:AT89C52

********************************************************************************************************************/

#include "reg52.h"

 

/*——————宏定义——————*/

#define FOSC 11059200L

#define T1MS (65536-FOSC/12/1000)   /*1ms timer calculation method in 12Tmode*/

 

#define const_time_05s 400   /*0.5秒钟的时间需要的定时中断次数*/

#define const_time_1s 800   /*1秒钟的时间需要的定时中断次数*/

#define const_time_3s 2400   /*3秒钟的时间需要的定时中断次数*/

#define const_time_6s 4800  /*6秒钟的时间需要的定时中断次数*/

 

#define const_voice_short  100   /*蜂鸣器短叫的持续时间*/

#define const_voice_long   800  /*蜂鸣器长叫的持续时间*/

 

/*——————变量函数定义及声明——————*/

/*定义LED口*/

sbit LED = P3^5;

/*定义蜂鸣器口*/

sbit BUZZER = P2^7;

 

/*LED步骤变量*/

unsigned char ucLedStep=0; 

/*LED统计定时中断次数的延时计数器*/

unsigned int  uiTimeLedCnt=0;

 

/*报警步骤变量*/

unsigned char ucAlarmStep=0; 

/*报警统计定时中断次数的延时计数器*/

unsigned int  uiTimeAlarmCnt=0;

 

/*蜂鸣器鸣叫的持续时间计数器*/

unsigned int  uiVoiceCnt=0;

 

unsigned char ucLock = 0; /* 互斥量,俗称原子锁 */

 

/**

* @brief  定时器0初始化函数

* @param  无

* @retval 初始化T0

**/

void Init_T0(void)

{

TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/

TL0 = T1MS;                     /*initial timer0 low byte*/

TH0 = T1MS >> 8;                /*initial timer0 high byte*/

}

/**

* @brief  定时器0中断函数

* @param  无

* @retval 无

**/

void ISR_T0(void) interrupt 1

{

TF0 = 0;  /*清除中断标志*/

  TR0 = 0; /*关中断*/

  if(uiTimeLedCnt < 0xffff)  /*设定这个条件,防止uiTimeCnt超范围*/

  {

      uiTimeLedCnt ++;  /*累加定时中断的次数*/

  }

  if(ucLock == 0) /* 互斥量判断 */

  {

if(uiTimeAlarmCnt < 0xffff)  /*设定这个条件,防止uiTimeCnt超范围*/

{

uiTimeAlarmCnt ++;  /*累加定时中断的次数*/

}

if(0 != uiVoiceCnt)

{

uiVoiceCnt --;

BUZZER = 0;

}

else

{

BUZZER = 1;

}

  }

TL0 = T1MS;                     /*initial timer0 low byte*/

TH0 = T1MS >> 8;                /*initial timer0 high byte*/

  TR0 = 1; /*开中断*/

}

/**

* @brief  外围初始化函数

* @param  无

* @retval 初始化外围

**/

void Init_Peripheral(void)

{

ET0 = 1;/*允许定时中断*/

TR0 = 1;/*启动定时中断*/

EA = 1;/*开总中断*/

 

}

 

/**

* @brief  初始化函数

* @param  无

* @retval 初始化单片机

**/

void Init(void)

{

Init_T0();

LED = 0;

BUZZER = 1;

}

/**

* @brief  LED闪烁函数

* @param  无

* @retval 控制LED闪烁

**/

void Led_Flicker(void)

{

switch(ucLedStep)

{

case 0:

if(uiTimeLedCnt >= const_time_05s) /*时间到,灯亮*/

{

ET0 = 0; /*禁止定时中断*/

uiTimeLedCnt = 0; 

LED = 1;

ucLedStep = 1;/*切换到下一步*/

ET0 = 1; /*允许定时中断*/

}

break;

case 1:

if(uiTimeLedCnt >= const_time_05s) /*时间到,灯灭*/

{

ET0 = 0; /*禁止定时中断*/

uiTimeLedCnt = 0; 

LED = 0;

ucLedStep = 0;/*返回到上一步*/

ET0 = 1; /*允许定时中断*/

}

break;

}

}

/**

* @brief  报警器报警函数

* @param  无

* @retval 报警器报警.

* 保护多线程共享全局变量的原理:

* 多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,

* 而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。

**/

void Alarm_Run(void)

{

switch(ucAlarmStep)

{

case 0:

if(uiTimeAlarmCnt >= const_time_3s) /*时间到*/

{

/* 

* 用关中断来保护多线程共享的全局变量:

* 因为uiTimeAlarmCnt和uiVoiceCnt都是unsigned int类型,本质上是由两个字节组成。

* 在C语言中uiTimeAlarmCnt=0和uiVoiceCnt=const_voice_short看似一条指令,

* 实际上经过编译之后它不只一条汇编指令。由于另外一个定时中断线程里也会对这个变量

* 进行判断和操作,如果不禁止定时中断或者采取其它措施,定时函数往往会在主函数还没有

* 结束操作共享变量前就去访问或处理这个共享变量,这就会引起冲突,导致系统运行异常。

*/

ET0 = 0; /*禁止定时中断*/

uiTimeAlarmCnt = 0; 

uiVoiceCnt = const_voice_short;  /*蜂鸣器短叫*/

ucAlarmStep = 1;/*切换到下一步*/

ET0 = 1; /*允许定时中断*/

}

break;

case 1:

if(uiTimeAlarmCnt >= const_time_6s) /*时间到*/

{

/* 

* 用互斥量来保护多线程共享的全局变量:

* 我觉得,在这种场合,用互斥量比前面用关中断的方法更加好。

* 因为一旦关闭了定时中断,整个中断函数就会在那一刻停止运行了,

* 而加一个互斥量,既能保护全局变量,又能让定时中断函数正常运行,

* 真是一举两得。 

*/

ucLock = 0; /*互斥量加锁。 俗称原子锁*/

uiTimeAlarmCnt = 0; 

uiVoiceCnt = const_voice_long;  /*蜂鸣器长叫*/

ucAlarmStep = 0;/*返回到上一步*/

ucLock = 0; /*互斥量解锁*/

}

break;

}

}

/**

* @brief  延时函数

* @param  无

* @retval 无

**/

void Delay_Long(unsigned int uiDelayLong)

{

   unsigned int i;

   unsigned int j;

   for(i=0;i   {

      for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/

          {

             ; /*一个分号相当于执行一条空语句*/

          }

   }

}

/*——————主函数——————*/

/**

* @brief  主函数

* @param  无

* @retval 实现LED灯闪烁

**/

void main()

{

/*单片机初始化*/

Init();

/*延时,延时时间一般是0.3秒到2秒之间,等待外围芯片和模块上电稳定*/

Delay_Long(100);

/*单片机外围初始化*/

Init_Peripheral();

while(1)

{

/*第一个任务,实现LED闪烁*/

Led_Flicker();

/*第二个任务,实现报警器定时报警*/

Alarm_Run();

}

}

/* 注释一:


* 如何知道1秒钟需要多少个定时中断?


* 这个需要编写一段小程序测试,得到测试的结果后再按比例修正。


* 步骤:


* 第一步:在程序代码上先写入1秒钟大概需要200个定时中断。


* 第二步:基于以上1秒钟的基准,编写一个60秒的简单测试程序(如果编写超过


* 60秒的时间,这个精度还会更高)。比如,编写一个用蜂鸣器的声音来识别计时的


* 起始和终止的测试程序。


* 第三步:把程序烧录进单片机后,上电开始测试,手上同步打开手机里的秒表。


*         如果单片机仅仅跑了27秒。


* 第四步:那么最终得出1秒钟需要的定时中断次数是:const_time_1s=(200*60)/27=444


*/


三、仿真实现

更改蜂鸣器短叫和长叫的时间的宏,可修改相应时间。(proteus蜂鸣器默认12V,可修改为5V,否则不报警)

关键字:单片机  关中断  互斥量  多线程共享  全局变量 引用地址:用关中断和互斥量来保护多线程共享的全局变量

上一篇:51单片机按键控制数码管
下一篇:指针的第五大好处,指针在众多数组中的中转站作用

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved