1 Star 0 Fork 4

armku / FN1895E-MCU101

forked from 建伟F4nniu / FN1895E-MCU101 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
MCU060.md 6.95 KB
一键复制 编辑 原始数据 按行查看 历史
建伟F4nniu 提交于 2016-12-22 11:54 . * 首行加粗.

#第六十节:用关中断和互斥量来保护多线程共享的全局变量。

开场白: 在前面一些章节中,我提到为了防止中断函数把某些共享数据破坏,在主函数中更改某个数据变量时,应该先关闭中断,修改完后再打开中断;我也提到了网友“红金龙吸味”关于原子锁的建议。经过这段时间的思考和总结,我发现不管是关中断开中断,还是原子锁,其实本质上都是程序在多进程中临界点的数据处理,原子锁有个专用名词叫互斥量,而我引以为豪的状态机程序框架,主函数的switch语句,外加一个定时中断,本质上就是2个独立进程在不断切换并行运行。 为什么要保护多线程共享的全局变量?因为,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。 这一节要教大家一个知识点:如何用关中断和互斥量来保护多线程共享的全局变量。

具体内容,请看源代码讲解。

(1)硬件平台: 基于朱兆祺51单片机学习板。

(2)实现功能: 在第5节的基础上略作修改,让蜂鸣器在前面3秒发生一次短叫报警,在后面6秒发生一次长叫报警,如此反复循环。

(3)源代码讲解如下:

#include "REG52.H"


#define const_time_3s 1332   //3秒钟的时间需要的定时中断次数
#define const_time_6s 2664   //6秒钟的时间需要的定时中断次数

#define const_voice_short  40   //蜂鸣器短叫的持续时间
#define const_voice_long   200  //蜂鸣器长叫的持续时间

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void led_flicker();
void alarm_run();   
void T0_time();  //定时中断函数

sbit beep_dr=P2^7; //蜂鸣器的驱动IO口

unsigned char ucAlarmStep=0; //报警的步骤变量
unsigned int  uiTimeAlarmCnt=0; //报警统计定时中断次数的延时计数器

unsigned int  uiVoiceCnt=0;  //蜂鸣器鸣叫的持续时间计数器

unsigned char ucLock=0;     //互斥量,俗称原子锁
void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
      alarm_run();   //报警器定时报警
   }

}


/* 注释一:
* 保护多线程共享全局变量的原理:
* 多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,
* 而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。
* 鸿哥的基本程序框架都是两线程为主,一个是main函数线程,一个是定时函数线程。
*/

void alarm_run() //报警器的应用程序
{
  
  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;  //蜂鸣器短叫
                          ET0=1; //开启允许定时中断
              ucAlarmStep=1; //切换到下一个步骤
           }
           break;
     case 1:
           if(uiTimeAlarmCnt>=const_time_6s) //时间到
           {
/* 注释三:
* 用互斥量来保护多线程共享的全局变量:
* 我觉得,在这种场合,用互斥量比前面用关中断的方法更加好。
* 因为一旦关闭了定时中断,整个中断函数就会在那一刻停止运行了,
* 而加一个互斥量,既能保护全局变量,又能让定时中断函数正常运行,
* 真是一举两得。
*/
                      ucLock=1;  //互斥量加锁。 俗称原子锁
              uiTimeAlarmCnt=0; //时间计数器清零
              uiVoiceCnt=const_voice_long;  //蜂鸣器长叫
                      ucLock=0; //互斥量解锁。  俗称原子锁

              ucAlarmStep=0; //返回到上一个步骤
           }
           break;
  }

}

void T0_time() interrupt 1
{
  TF0=0;  //清除中断标志
  TR0=0; //关中断
  
  if(ucLock==0) //互斥量判断
  {
     if(uiTimeAlarmCnt<0xffff)  //设定这个条件,防止uiTimeAlarmCnt超范围。
     {
         uiTimeAlarmCnt++;  //报警的时间计数器,累加定时中断的次数,
     }

     if(uiVoiceCnt!=0)
     {
         uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
         beep_dr=0;  //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
     }
     else
     {
         ; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
         beep_dr=1;  //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
     }
  }

  TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //开中断
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i<uiDelayLong;i++)
   {
      for(j=0;j<500;j++)  //内嵌循环的空指令数量
          {
             ; //一个分号相当于执行一条空语句
          }
   }
}


void initial_myself()  //第一区 初始化单片机
{
  beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。

  TMOD=0x01;  //设置定时器0为工作方式1


  TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;

}
void initial_peripheral() //第二区 初始化外围
{
  EA=1;     //开总中断
  ET0=1;    //允许定时中断
  TR0=1;    //启动定时中断

}

总结陈词: 从下一节开始我准备用几章节的内容来讲常用的数学算法程序。这些程序经常要用在计算器,工控,以及高精度的仪器仪表等领域。C语言的语法中不是已经提供了+,-,*,/这些运算符号吗?为什么还要专门写算法程序?因为那些运算符只能进行简单的运算,一旦数据超过了unsigned long(4个字节)的范围就会出错。而解决这种问题的大数据算法程序是什么样的?欲知详情,请听下回分解----大数据的加法运算。

(未完待续,下节更精彩,不要走开哦)

C
1
https://gitee.com/armku/FN1895E-MCU101.git
git@gitee.com:armku/FN1895E-MCU101.git
armku
FN1895E-MCU101
FN1895E-MCU101
master

搜索帮助