欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-04 15:53:21
一、使用proteus绘制简单的电路图,用于后续仿真二、编写程序/********************************************************************************************************************----@Project:Mutex----@File:main.c----@Edit:ZHQ----@Version:V1.0----@CreationTime:202...

一、使用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<uiDelayLong;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,否则不报警)

本文地址:https://blog.csdn.net/DJDN426611/article/details/107924362

上一篇: 模拟生成手机号

下一篇: 文件上传