开场白:
在前面一些章节中,我说到为了避免中止函数把某些同享数据损坏,在主函数中更改某个数据变量时,应该先封闭中止,修正完后再翻开中止;我也说到了网友“红金龙吸味”关于原子锁的主张。通过这段时刻的考虑和总结,我发现不管是关中止开中止,仍是原子锁,其实本质上都是程序在多进程中临界点的数据处理,原子锁有个专用名词叫互斥量,而我引认为豪的状态机程序结构,主函数的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
{
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个字节)的规模就会犯错。而这种大数据算法的程序是什么样的?欲知概况,请听下回分解—-大数据的加法运算。