实用的MCU非阻塞式框架---外设部分

常用的单片机裸机非阻塞式框架

仅作参考

ADC采样滤波

0.约1024次AD后采样获取一次温度值
1.支持双电阻高低温区采样
2.一阶滤波、限幅滤波、温差比较 ---- 冒泡排序后取平均值
3.二分法获取查表获取温度
4.添加补偿温度

/****************************************************************************************************
Function Name       :void NTC_GetTpTask(void)
Description         :
Author              :MECHT
Version             :V1.0
****************************************************************************************************/
#define ADC_GROUP       8
#define NTC_GROUP_NUM   16
void NTC_GetTpTask(void)
{
    static uint  xdata Su16TempBuf1[ADC_GROUP]  = {0};
    static uchar xdata Su8BufIndex1 = 0;				//单个通道ADC值数组下标
    static uint  xdata Su16TempBuf2[ADC_GROUP] = {0};
    static uchar xdata Su8BufIndex2 = 0;			    //单通道平均值数组下标
    static signed int xdata Su16TempBuf3[NTC_GROUP_NUM] = {0};
    static uchar xdata Su8BufIndex3 = 0;
    static signed int xdata OldNtcValue = 0;
    
    
    static uint16_t Tempd = 0;
    static uint16_t OldAdcValue = 0;
    
	uint16_t AdcTempValue   = 0;        //临时值
	uint16_t AdcTempAverage = 0;		//临时平均值
    
    // 此部分根据单片机不同更换,目标都是获取AD值
    ADCCON &= ~(0X20);      //清零
    ADCCON |= 0X40;   //Start ADC conversion
    while(!(ADCCON & 0X20));
    AdcTempValue = (ADCVH<<4)+(ADCVL>>4);
    //======================================================
    //======================================================
    
    //1.一阶滤波,需根据实际情况微调
    if(Su8BufIndex1 == 0)
        OldAdcValue = AdcTempValue;
    Su16TempBuf1[Su8BufIndex1] = 7*OldAdcValue/10 + 3*AdcTempValue/10;
    
    
    if(++Su8BufIndex1 >= ADC_GROUP)
    {
        Su8BufIndex1 = 0;
        
        AdcTempAverage = AdcAverageHandle(Su16TempBuf1,ADC_GROUP,2);  //得到ADC平均值 
        
        Su16TempBuf2[Su8BufIndex2] = AdcTempAverage;
       
        if(++Su8BufIndex2 >= ADC_GROUP)
        {
            Su8BufIndex2 = 0;
            
            NtcInfo.ADValue = AdcAverageHandle(Su16TempBuf2,ADC_GROUP,2); 
            //限幅滤波
            NtcInfo.ADValue  = ADC_Fliter(NtcInfo.ADValue );
            //==========================================================================================
			
			#if 1  //单通道  二分法查询温度
            Tempd = Find_temperaturePos(NtcInfo.ADValue,Cu16TempTable1,TEMP_Table_SIZE1);
            
            Su16TempBuf3[Su8BufIndex3] = -400+Tempd*5;
            if(++Su8BufIndex3 >= NTC_GROUP_NUM)
            {
                Su8BufIndex3 = 0;
                
                NtcInfo.NtcTp = MyAverageHandle(Su16TempBuf3,NTC_GROUP_NUM,6);
                
                //若跟上一次相差0.5℃则不更新
                if((OldNtcValue - NtcInfo.NtcTp) == 5 || (NtcInfo.NtcTp - OldNtcValue)==5)
                {
                    NtcInfo.NtcTp = OldNtcValue;
                }
                else
                {
                    OldNtcValue = NtcInfo.NtcTp;
                }
                
         
                //根据不同模式提供不同的补偿温度
                switch(WorkInfo.Function)
                {
                    case FUN_O:
                       NtcInfo.NtcTp += WorkInfo.OffsetTp1*10;
                    case FUN_W:
                    case FUN_C:
                    case FUN_F:
                        NtcInfo.NtcTp += WorkInfo.OffsetTp2*10;   //补偿温度2
                        break;
                    case FUN_H:
                    case FUN_A:
                        NtcInfo.NtcTp += WorkInfo.OffsetTp3*10;  //补偿温度3
                        break;
                }
                
                if(NtcInfo.NtcTp >= 850)
                    WorkInfo.NtcTemperature[1] = 850;
                else if(NtcInfo.NtcTp <= -300)
                    WorkInfo.NtcTemperature[1] = -300;
                else
                    WorkInfo.NtcTemperature[1] = NtcInfo.NtcTp;
                
                WorkInfo.NtcTemperature[0] = TemperUnitChange(0,WorkInfo.NtcTemperature[1]);
            }
            
			#else  //双通道切换     
            //选择分压电阻
            switch(NtcInfo.DoubleRes)
            {
                case 0:
                    NtcInfo.NtcTp = Find_temperaturePos(NtcInfo.ADValue,Cu16TempTable1,TEMP_Table_SIZE1);
                    if(NtcInfo.NtcTp >= 180)
                    {
                        NtcInfo.DoubleRes = 1;
                        //Pin_key_ntc = 1;        //单电阻
                    }
                    break;
                case 1:
                    NtcInfo.NtcTp = Find_temperaturePos(NtcInfo.ADValue,Cu16TempTable2,TEMP_Table_SIZE2);
                    NtcInfo.NtcTp += 170;	      //根据情况调整
                    if(NtcInfo.NtcTp <= 170)
                    {
                        NtcInfo.DoubleRes = 0;
                       // Pin_key_ntc = 0;        //开双电阻
                    }
                    break;
            }
            //切换PIN脚
            if(NtcInfo.DoubleRes)
            {
                Pin_key_ntc = 0;
            }
            else
            {
                Pin_key_ntc = 1;        //单电阻
            }
			#endif
			
            NtcInfo.GetOnceTpFlag = 1;
        }
	}
    
}

串口无阻塞发送–循环队列(应用层不仅仅适用于串口)

1.需要结合循环队列,非阻塞发送
2.单片机资源紧张,串口发送时无需判断是否发送成功,1ms或者2ms发送一字节数据即可

typedef struct Queue
{
	unsigned char MaxSize;				//最大个数
	unsigned char NowCount;				//参考RTOS 增加当前个数,节省一行数据
	unsigned char head;        			//队头
	unsigned char fail;					//队尾
//	unsigned char (*DataBuf)[]; 		//队列缓冲区,节省空间后面的直接用数组
}QueueObject_t;    //简易队列

unsigned char QueueEmpty(QueueObject_t *Queue)//队空
{
	//return (Queue->fail==Queue->head)?1:0;	//减少运算
	return (Queue->NowCount==0)?1:0;
}
unsigned char QueueFull(QueueObject_t *Queue) //队满
{
	//return (((Queue->fail+1)%Queue->MaxSize)== Queue->head)?1:0;
	return (Queue->NowCount >= Queue->MaxSize)?1:0;
}
void QueueAddTail(QueueObject_t *Queue,unsigned char info[],unsigned char length)  //入队 尾插
{
	if((QueueFull(Queue))) 	 //队列满退出
	{
		return;
	}
	
	//MyMemoryCpy(Queue->DataBuf[Queue->fail],info,length);
    MyMemoryCpy(SendQueueBuf[Queue->fail],info,length);			//由队列转换为数组
    
	Queue->fail = (Queue->fail+1)%Queue->MaxSize; //队尾+1,指向+1,此时指向的位置是空的,无数据
	Queue->NowCount++;                            //总量+1
}

void QueueDelete(QueueObject_t *Queue)//出队,头节点+1
{
	//if((QueueEmpty(Queue))) 	 //队列满退出,根据需求选择覆盖or退出
//	{
//		return;
//	}
	Queue->head = (Queue->head+1)%Queue->MaxSize;
	Queue->NowCount--;
}

void MyLog_Task(void)
{
	static unsigned char Su8UartSendTimes = 0;   		//发送次数,1次
	static unsigned char Su8SendIntervalCnt = 0; 		//每次发送间隔时间
	static unsigned char Su8SendSuccessIntervalCnt = 0; //一类数据发送完后的发送间隔时间
    
	if(GsUartInfo.Txing)       			//发送数据ing
    {
		//SFR  串口
		//UART_SFR_REG = GsUartInfo.QueueInfo_Tx->DataBuf[GsUartInfo.QueueInfo_Tx->head][GsUartInfo.TxBufIndex++]; //为节省空间直接将队列指针改为数组
        UART_SFR_REG = UartSendQueueBuf[GsUartSendQueueInfo.head][GsUartInfo.TxBufIndex++]; //队列改数组
        
        if(GsUartInfo.TxBufIndex >= UartSendQueueBuf[GsUartSendQueueInfo.head][1])
        {
			GsUartInfo.TxBufIndex = 0;
			GsUartInfo.Txing = 0;
			
        }
		return;
    }
	
    if(Su8SendSuccessIntervalCnt != 0)       		//间隔发送,若时间少可去除,比如100ms
	{
		Su8SendSuccessIntervalCnt--;
		return;
	}
	
	if(QueueEmpty(&GsUartSendQueueInfo))          //队列无数据时不发送
    {
		return;
    }

	
	//===========================================
	switch(GsUartInfo.TxStep)   					//发送步骤,间隔时间由轮询位置和case 2 共同决定
	{
		case 0:
			GsUartInfo.TxStep++;
			Su8UartSendTimes = 0;
			Su8SendIntervalCnt = UART_SAME_DATA_INTERVAL_TIME;   //第一次不间隔
			break;
		case 1:   								 	   //发送队列中的数据
			if(Su8UartSendTimes < GsUartInfo.TxTimes)  //固定发送次数模式时
			{
				if(Su8SendIntervalCnt++ >= UART_SAME_DATA_INTERVAL_TIME)//在1ms扫描时,间隔30ms发送以一次
				{
					Su8SendIntervalCnt = 0;
					
                    GsUartInfo.Txing = 1;			//启动发送
                    GsUartInfo.TxBufIndex = 0;		//重新发送
                    Su8UartSendTimes++;
				}
			}
			else								//实际发送多一次间隔时间1ms
			{
				GsUartInfo.TxStep++; //等待ACK,有ACK的情况则继续往下执行,否则会重发队头数据,不出队  //实际上到case 2 需要一段时间
				//加一个比较数据  ---  可用来应对MODBUS协议/或者直接使用队列头数据比较
			}
			break;
			
		case 2:						 		    //发送完成后或者  帧错误(未判断)出队
			Su8UartSendTimes   = 0;
			GsUartInfo.TxStep  = 0;
			QueueDelete(&GsUartSendQueueInfo);   //出队
			Su8SendSuccessIntervalCnt = UART_SEND_FINISH_INTERVAL_TIME;    //间隔
			break;
		default:
			break;
	}
}

软件定时器

1.设置1ms定时器中断,设置变量在定时器中断函数中++/–
2.跑裸机都可参考此框架的非阻塞式延时。
3.注意MCU的工作频率和机器周期。

unsigned int Cnt1 = 0;
unsigned int Cnt2 = 0;
void main()
{
	while(1)
	{
		if(Cnt1)      	//1ms执行一次
		{
			Cnt1 = 0;
			task_1ms();  //在此任务函数中,可以继续计时
		}
		
		if(Cnt2 >= 10)  //确定时间,10ms执行一次
		{
			Cnt2 = 0;
			task_10ms();
		}
	}
}

void timer0() interrupt 1
{	
	TL0	= 0x1C;	//与计算值会有一定偏差,需做补偿
	TH0	= 0xFC;	//定时器重赋值
	Cnt1 = 1;   //1ms定时器
	Cnt2++;     //不定	
}

按键扫描

1.机械按键按下会产生抖动,通过非阻塞式的延时消抖,轻触按键也可以通过计数消抖。

static void KeyScan(void)
{
//    if(0)               //开门、故障、某特定情况不需要按键时
//        return;
    
    //1.获取按键按位值
	GsKeyInfo.CurrentValue = Drv_Key_New_Scan();
    
	//2.按下判断和组合判断
	if(0 != GsKeyInfo.CurrentValue)//按下
	{
		if(GsKeyInfo.PreValue != GsKeyInfo.CurrentValue)
		{
            if(GsKeyInfo.Pressing == 0)
            {
                GsKeyInfo.PreValue = GsKeyInfo.CurrentValue;
                GsKeyInfo.PressCnt = 0;
            }
            else  
            {
                /*
                    此部分不固定,根据需求决定做法
                
                    若按下的情况下切换按键,根据实际情况添加功能
                    1.双键轻触  --- 在脱手处判断时间
                    2.组合键    --- 单变双、三变双,这两个按键需要脱手触发或者按下触发但不是短触
                    3.无功能  ---- 单边多/多变单都不响应
                    4.
                */
//                if(GsKeyInfo.PreValue == KeyCombine)             //上一次是组合按键值
//                {
//                    if(GsKeyInfo.CurrentValue == One_of_Combine) //释放了一个属于组合按键值之一的键
//                    {
//                        
//                    }
//                    else
//                    {
//                       GsKeyInfo.PreValue = GsKeyInfo.CurrentValue;
//                         GsKeyInfo.PressCnt = 0;
//                    }
//                }
//                else
                    
                    GsKeyInfo.PreValue = GsKeyInfo.CurrentValue;
            }
		}
        
        GsKeyInfo.Pressing = 1;
        
		GsKeyInfo.PressCnt++;
		if(GsKeyInfo.PressCnt >= KEY_MAX_TIME)
		{
			GsKeyInfo.PressCnt = KEY_MAX_TIME;
		}
		
        // ==  1.按下过程的功能
        WorkInfo.BackLightCnt = 50;   //按下背光就打开倒计时
		//===  2.长按、连续
		if(WorkInfo.WorkMode == MODE_WORK)
		{
            #if 1
			switch(GsKeyInfo.PreValue)
			{
                case KEY_Mode:
                    if(GsKeyInfo.PressCnt == 1000)	//长按10S功能
					{
						GsKeyInfo.Type     = KEY_TYPE_LONG;
						GsKeyInfo.PressOk  = 1;
						GsKeyInfo.ActionValue = GsKeyInfo.PreValue;
					}
                    break;
          
			}
            #endif
		}
	}
	else//松手,进行按键选择
	{
        GsKeyInfo.Pressing = 0;
		if(GsKeyInfo.PressCnt >= KEY_SHORT_PRESS_MIN && GsKeyInfo.PressCnt <= KEY_SHORT_PRESS_MAX)
		{
			switch(GsKeyInfo.PreValue)
			{
				case C_KEY_BitMap_KEY1:
				case C_KEY_BitMap_KEY2:
				case C_KEY_BitMap_KEY3:
				case C_KEY_BitMap_KEY4:
					GsKeyInfo.Type = KEY_TYPE_SHORT; 	//短按
					GsKeyInfo.PressOk = 1;
					GsKeyInfo.ActionValue = GsKeyInfo.PreValue;
					break;
				default:
					break;
			}
		}
		GsKeyInfo.PressCnt = 0;   //松手清零
	}
}

static void Key_Task(void)
{
	if(GsKeyInfo.PressOk)
	{
		GsKeyInfo.PressOk = 0;
		
        KeyFunction(GsKeyInfo.ActionValue,GsKeyInfo.Type);  //根据长、短类型响应功能
		
		#if 0           //按键调试串口打印
		KeyValuebuf[0] = GsKeyInfo.ActionValue >> 16;
		KeyValuebuf[1] = GsKeyInfo.ActionValue >> 8;
		UartSendDataHandle(KeyValuebuf,2,UART_KEYVALUE,UartSendBuf);
		GsComInfo.TxSendFun(UartSendBuf);
		#endif
		
		GsKeyInfo.Type = KEY_TYPE_NONE;						     //默认类型

    }
}

static void KeyFunction(uint16_t ActionValue,uint8_t Type)
{
	
	switch(ActionValue)
	{
		case 0:
			break;
        
		case KEY_FanSpeed:
			if(Type == KEY_TYPE_SHORT)
			{
				Key1_Short_Fun();
			}
			else if(Type == KEY_TYPE_LONG)
			{
				Key1_Long_Fun();
			}
			break;
        case KEY_Mode:
            if(Type == KEY_TYPE_SHORT)
            {
                Key2_Short_Fun();
            }
            else if(Type == KEY_TYPE_LONG)
            {
               
            	Key2_Long_Fun();
            }
            break;
        }
    }

数码管显示(改为扫描COM可实现调节亮度)

1.假设是共阳双八数码管。
2.假设用按键设置时,数码管闪烁显示。
3.假设是单片机的P2口连接数码管引脚。

void DisplayScan(void)   //1ms定时器中扫描
{
	static uchar Su8ScanStep = 0;//动态扫描步骤
	 
	if(GbWorkStatus)
	{
		if(GbLedEnableFlag )
		{
			//消隐
			P2 = (unsigned char)0xFF;	//共阳,给高电平不亮
			LED_COM1_CLOSE;				//第一个8片选关闭
			LED_COM2_CLOSE;				//第二个8片选关闭

			switch(Su8ScanStep)			//扫描步骤
			{
				case 0:     //first
					P2 = Gu8LedDigTableBuf[Gu8LedFirstShowData]; //在外部确定Gu8LedFirstShowData的值是多少,将0-9和部分字母的编码列举在Gu8LedDigTableBuf[]数组中
					LED_COM1_OPEN;	//选中第一个数码管
					LED_COM2_CLOSE;
					Su8ScanStep++;	//下一次显示第二个数码管
				break;
				
				case 1:     //second 
					P2 = Gu8LedDigTableBuf[Gu8LedSecondShowData];//第二位数据
					P27 = !GbLedThirdShowEnable;				 //是否带小数点
					LED_COM1_CLOSE;
					LED_COM2_OPEN;
				
					//=============================以下为闪烁部分程序,不需要闪烁时,删除下面case 1程序,添加Su8ScanStep = 0;即可
					if(!GbLedFlickerFlag)	//闪烁标志位,没有要求闪烁的话跳回到第一个数码管的显示
					{
						Gu16LedOffFlickerCnt = 0;
						Su8ScanStep = 0;
					}
					else if(++Gu16LedOffFlickerCnt >= LED_ON_FLICKER_TIME)//要求闪烁时,闪烁时期亮的时间
					{
						Gu16LedOffFlickerCnt = 0;
						Su8ScanStep++;
					}
					else
						Su8ScanStep = 0;	
					break;
				case 2://
					LED_COM1_CLOSE;
					LED_COM2_CLOSE;
					if(++Gu16LedOnFlickerCnt >= LED_OFF_FLICKER_TIME)	//闪烁时期灭的时间
					{
						Gu16LedOnFlickerCnt = 0;
						Su8ScanStep = 0;
					}
					break;
			}
		}
		else
		{
			LED_COM1_CLOSE;
			LED_COM2_CLOSE;
		}
	}
}

无源蜂鸣器

1.通电后需要一定震荡频率蜂鸣器才会响。

void BuzTask(void)
{
	if(xBuzInfo.Times)  //次数
	{
		if(xBuzInfo.Cmd)
		{
			if(xBuzInfo.SoftCnt-- == 0)
			{
				if(xBuzInfo.IntervalTime)
				{
					xBuzInfo.SoftCnt = xBuzInfo.IntervalTime; //间隔
				}
				xBuzInfo.Cmd = 0;
				xBuzInfo.Times--;						  	  //次数
			}
		}
		else
		{
			if(--xBuzInfo.SoftCnt == 0)
			{
				xBuzInfo.SoftCnt = xBuzInfo.OpenTime;
				xBuzInfo.Cmd = 1;
			}
		}
	}
	else
	{
	
	}
    
    xBuzInfo.BuzDrv();
}

static void BuzDrv(void)
{
	static uint8_t xdata Su8oldcmd = 0xFF;
	
	if(Su8oldcmd != xBuzInfo.Cmd)
	{
		Su8oldcmd = xBuzInfo.Cmd;
		
		if(xBuzInfo.Cmd)
		{
			BuzPwm_Enable;
		}
		else
		{
			BuzPwm_Disable;
		}
	}
}

##IO口模拟PWM

void IoToPwmScan(void)	//放在1ms定时器中
{
	if(Enable)	
	{
		if(++Cnt <= 100)	//100ms低电平
			PIN = 0;
		else if(Cnt <= 200) //100ms高电平
			PIN = 1;
		else
			Cnt = 0;
	}
	else
		Cnt = 0;
}

工业开关

1.分层扫描,高低都延时,随意更改开关个数

static void Switch_Scan(void)
{
    static uint8_t xdata Su8SwitchScanStep = 0;
    switch(Su8SwitchScanStep)
    {
        case 0:
            Su8SwitchScanStep = 1;
            break;
        case 1:
            F_SwitchPinValue1 = PIN_DIAL_SWITCH1;
            F_SwitchPinValue2 = PIN_DIAL_SWITCH2;
            F_SwitchPinValue3 = PIN_DIAL_SWITCH3;
            F_SwitchPinValue4 = PIN_DIAL_SWITCH4;
            F_SwitchPinValue5 = PIN_DIAL_SWITCH5;
            F_SwitchPinValue6 = PIN_DIAL_SWITCH6;
            F_SwitchPinValue7 = PIN_DIAL_SWITCH7;
            F_SwitchPinValue8 = PIN_DIAL_SWITCH8;
            Su8SwitchScanStep = 0;                      //修改此处
            break;
//        case 2:
//            P3CON |= 0x80;   
//            SWITCH_COM2 = 0;    //
//            P3CON &= ~0x40;
//            
//            Su8SwitchScanStep = 3;
//            break;
//        case 3:
//            F_SwitchPinValue5 = PIN_DIAL_SWITCH4;
//            F_SwitchPinValue6 = PIN_DIAL_SWITCH3;
//            F_SwitchPinValue7 = PIN_DIAL_SWITCH2;
//            F_SwitchPinValue8 = PIN_DIAL_SWITCH1;
//            Su8SwitchScanStep = 4;
//            break;
//        case 4:
//            Su8SwitchScanStep = 0;
//            break;
        default:
            Su8SwitchScanStep = 0;
            break;
    }
}

static uint8_t GetSwitchValue(unsigned char Pin)
{
	uint8_t PinValue = 0;
	
	switch(Pin)
	{
		case 0:
			PinValue = F_SwitchPinValue1;
			break;
		case 1:
			PinValue = F_SwitchPinValue2;
			break;
		case 2:
			PinValue = F_SwitchPinValue3;
			break;
		case 3:
			PinValue = F_SwitchPinValue4;
			break;
		case 4:
			PinValue = F_SwitchPinValue5;
			break;
		case 5:
			PinValue = F_SwitchPinValue6;
			break;
		case 6:
			PinValue = F_SwitchPinValue7;
			break;
		case 7:
			PinValue = F_SwitchPinValue8;
			break;
	}
	return PinValue;
}


//最后通过200ms滤波获取值
void Drv_DialSwitch(void)
{
	uint8_t i  = 0;			        //max == 255
	uint8_t PinValue = 0;
	static uint8_t xdata Su8LowFilterCnt[Dial_Switch_NUM]  = {0};
    static uint8_t xdata Su8HighFilterCnt[Dial_Switch_NUM] = {0};
    
    Switch_Scan();                      //底层扫描
    
	for(i = 0;i < Dial_Switch_NUM;i++)  //应用层获取
	{
		PinValue = GetSwitchValue(i);   //中间件
		
		if(!PinValue)                   //低电平是ON
		{
			Su8LowFilterCnt[i] = 0;
			
			if(Su8HighFilterCnt[i] < DialSWICHT_FILTER_TIME)
			{
				Su8HighFilterCnt[i]++;
				if(Su8HighFilterCnt[i] >= DialSWICHT_FILTER_TIME)
				{
					Gu8SwitchStateFlag_Ass |= 0x01 << i;
				}
			}
		}
		else
		{
			Su8HighFilterCnt[i] = 0;
			
			if(Su8LowFilterCnt[i] < DialSWICHT_FILTER_TIME)
			{
				Su8LowFilterCnt[i]++;
				if(Su8LowFilterCnt[i] >= DialSWICHT_FILTER_TIME)
				{
					Gu8SwitchStateFlag_Ass &= ~(0x01 << i);
				}
			}
		}
	}
}

最后:
为避免全局变量满天飞,用结构体管理全局变量,若MCU内存不够,最后可解耦;

typedef struct
{
	/*
		MenuParameterSet: 为当前设置对象,在进行 +- 时无需判断模式
		MenuNub: 菜单编号,为功能之下的子菜单,初始化为1
	*/
    unsigned char Versions;     	// 版本号
    unsigned char BoardVersions;    // 软件版本
    unsigned char ID;              
    unsigned char Unit:1;           // 温度单位      0:F  1:C
    unsigned char TpType:1;        // 当前温度类型
    
    unsigned char ReinstatedFactoryFlag:1;  //恢复出场设置标志
    
    unsigned char BuzEn:1;          //蜂鸣器使能控制
    unsigned char OffsetTp1;        //补偿温度1         
    unsigned char OffsetTp2;        //补偿温度2 
    
    unsigned char WifiFlag:1;       // WIFI连接情况
    unsigned char WorkSwitch1:1;   
    unsigned char WorkSwitch2:1;    
    unsigned char WorkSwitch3:1;    
    unsigned char WorkSwitch4:1;  
   
    unsigned char CommSuccess:1;    // 通讯完成标志
    
    unsigned int SetTemperature[2]; // 设置温度 0:F  1:C
    signed int NtcTemperature[2];   // 环境温度 0:F  1:C
    
    
	unsigned char WorkMode;      	// 工作状态:	自检、自检模式、关机、待机、设置、工作
	unsigned char OldWorkMode;		// 上一次工作模式
	
    unsigned char Function;         // 功能
    unsigned char OldFunction;
    FunctionObject_t *FunObject;    // 当前功能对应参数

    unsigned char BackLightCnt;	    // 背光倒计时  默认5S

    unsigned char InitStep;         // 初始化步骤
    unsigned int InitCnt;           // 初始化计时器
    
    unsigned char NTC_State;        // NTC状态
   
    
    //=======================  故障
    unsigned char ErrorFlag:1;		// 有故障标志
    unsigned char ErrorShowCnt;     // 故障显示计时器
	unsigned int ErrorNub;			// 故障编号      //或起来显示的故障点
    unsigned int ErrorNubBackup;    // 故障编号备份 

    unsigned char BatVoltage;       //电池电压 
    //=========================================================
    unsigned int SpecialFunCnt;             //特殊功能计时器
    
    unsigned char LowVolFlag:1;     // 低压信号
    unsigned int LowHighVolStayCnt; // 低压保持时间 
    
	unsigned char CheckStep;		// 测试步骤
    unsigned char CheckCnt;            // 测试时间
    
    //======================= 预留 ==================
    unsigned int WorkTimeS;
    unsigned int WorkTimeUpCnt;
    unsigned int WorkTestCnt;
}WorkManageObject_t;
extern WorkManageObject_t xdata WorkInfo;