基于状态机与时间片轮的51单片机电子钟设计 程序Proteus仿真

在对分层思想、时间片轮转和状态机思想进行[简单应用]

## 二、主函数
主函数如下:
整个主函数的中心任务为功能选择切换任务,负责切换显示内容,控制ui变化等,其余任务函数除提醒任务外都是通过全局变量的形式给功能选择切换任务提供资源或从该任务获取内容。
```c
#include "sys.h"
#include "task.h"
//系统时钟为12MHZ
void main()
{
        u8 key = 0;
        //各类初始化任务
        TimerInit();
        DS1302_Init();
        DS18B20_ConvertT();
        while(1)
        {
                LCD_Task();//显示任务,按照给定的内容进行显示
                SHOW_CTRl_Task();//功能选择切换任务
                TEMP_Task();//温度读取任务
                ReadKey_Task();//按键读取任务
                ALM_Task();//闹钟提醒任务
                TMR_Task();//倒计时提醒任务
        }
}
```
## 三、显示任务
由于显示任务涉及到了多个层级的函数,从最底层写命令、写数据,到中间层显示和初始化等函数。再到最顶层控制多行的显示。故使用了多级状态机的形式来完成lcd任务的状态机内容。由于C语言顺序执行的特性。规定同一层级使用同一个状态机,可以有效减少状态机的数量同时也能保证系统的稳定运行。
### 顶层代码如下

```c
void LCD_Task()
{        //显示初始化状态
        if(LCD_STATE == 0)
        {
                if(LCD_Init() == 1)
                {
                        LCD_STATE =1;
                }
        }
        //显示第一行状态  可能还需细分,视需求而定
        if(LCD_STATE == 1)
        {
                if(LCD_ShowString(1,Column1,LCD_SHOW_1) == 1)
                {
                        LCD_STATE =2;
                }
        }
        if(LCD_STATE == 2)
        {
                if(LCD_ShowString(2,Column2,LCD_SHOW_2) == 1)
                {
                        LCD_STATE =3;
                }
        }
        //显示暂停状态
        if(LCD_STATE == 3)
        {
        }
}
```
### 中间层代码如下:
```c
u8 LCD_Init()
{        
        if(LCD_STATE_1 == 0)
        {
                //八位数据接口,两行显示,5*7点阵
                if(LCD_WriteCommand(0x38) == 1)
                {
                        LCD_STATE_1 = 1;
                }
        }
        
        if(LCD_STATE_1 == 1)
        {
                //显示开,光标关,闪烁关
                if(LCD_WriteCommand(0x0c) == 1)
                {
                        LCD_STATE_1 = 2;
                }
        }
        
        if(LCD_STATE_1 == 2)
        {
                //数据读写操作后,光标自动加一,画面不动
                if(LCD_WriteCommand(0x06) == 1)
                {
                        LCD_STATE_1 = 3;
                }
        }
        
        if(LCD_STATE_1 == 3)
        {
                //光标复位,清屏
                if(LCD_WriteCommand(0x01) == 1)
                {
                        LCD_STATE_1 = 0;
                        return 1;
                }
        }
        return 0;
}
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 0:显示字符未完成
        *         1:显示字符完成
  */
u8 LCD_ShowChar(u8 Line,u8 Column,char Char)
{
        if(LCD_STATE_1 == 0)
        {
                if(LCD_SetCursor(Line,Column) == 1)
                        LCD_STATE_1 = 1;
        }
        if(LCD_STATE_1 == 1)
        {
                if(LCD_WriteData(Char) == 1)
                {
                        LCD_STATE_1 = 0;
                        return 1;
                }
        }
        return 0;
}
```
### 底层代码如下:
```c
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
u8 LCD_WriteCommand(u8 Command)
{
        if(LCD_STATE_0==0)
        {
                LCD_RS=0;
                LCD_RW=0;
                LCD_DataPort=Command;
                LCD_EN=1;
                LCD_DELAY = TimeRef + NUM_1MS;
                LCD_STATE_0 = 1;
        }
        if(LCD_STATE_0 == 1)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_EN=0;
                        LCD_DELAY = TimeRef + NUM_1MS;
                        LCD_STATE_0 = 2;
                }
        }
        if(LCD_STATE_0 == 2)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_STATE_0 = 0;
                        return 1;
                }
        }
        return 0;
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 0:未写入完成
        *         1:写入完成
  */
u8 LCD_WriteData(u8 Data)
{
        if(LCD_STATE_0==0)
        {
                LCD_RS=1;
                LCD_RW=0;
                LCD_DataPort=Data;
                LCD_EN=1;
                LCD_DELAY = TimeRef + NUM_1MS;
                LCD_STATE_0 = 1;
        }
        if(LCD_STATE_0 == 1)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_EN=0;
                        LCD_DELAY = TimeRef + NUM_1MS;
                        LCD_STATE_0 = 2;
                }
        }
        if(LCD_STATE_0 == 2)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_STATE_0 = 0;
                        return 1;
                }
        }
        return 0;
}
```
## 四、温度获取任务
温度获取任务主要分为初始化获取和循环获取。初始化获取用于立刻获取温度值防止出现刚开机时不显示温度的情况。循环获取用于间隔一段时间获取一次温度,实现较为实时的温度显示。基本代码如下
```c
//函数定义:
/**
  * @brief  温度获取任务
  * @param  无
  * @retval 无
  */
void TEMP_Task()
{        //初始化状态
        if(TEMP_STATE == 0)
        {
                if(DS18B20_ConvertT() == 1)
                {
                        TEMP_DELAY = TimeRef + NUM_1000MS;
                        TEMP_STATE = 1;
                }
        }
        //延时状态
        if(TEMP_STATE == 1)
        {
                if(TimeRef >= TEMP_DELAY)
                {
                        TEMP_STATE=2;
                }
        }
        //转换状态
        if(TEMP_STATE == 2)
        {
                if(DS18B20_ConvertT() == 1)
                {
                        TEMP_STATE = 3;
                }
        }
        if(TEMP_STATE == 3)
        {
                Temp = DS18B20_ReadT();
                TEMP_DELAY = TimeRef + (u32)NUM_1000MS*1800;
                TEMP_STATE =1;
        }
}
```
该线程所需的驱动并未实现完全的状态机化由于单总线所需的延时较为精准在尝试了进行状态机化后发现会出现读取乱码的情况,且部分函数所需的延时时间太短甚至需要关闭定时器中断以对该延时进行保护,可对标操作系统的临界区保护。
## 五、按键获取任务
按键获取任务基本架构、分层与[按键控制数码管项目](51单片机按键数码管显示 时间片轮转+状态机 源程序 - 51单片机)类似,但由于本项目的功能较多且针对于项目的显示内容使用了现态的方式进行控制,即按键任务无法得知完成显示任务前的状态(功能),故引入过去态和未来态,前者供需要在现态中进行状态转换的任务标记其源头,后者用于在使用完相应的资源后跳转到相应的任务上。故按键对于当前功能界面的判断使用了过去态的方式,同时配合行号和列号完成相应功能。具体代码如下:

```c
void ReadKey_Task()
{
        if(ReadKeyEN)
        {
                ReadKeyValue = ReadKeyDat();
                //记录状态机的次态、现态、下态,在根据次态确认按下按键执行的内容
                if(SHOW_CTRl_P_STATE == 4)//功能选择页面的按键选择
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;
                                case 1:{
                                        Key_F_STATE_C = 6;
                                        FSD_Line = 1;
                                        FSD_Colu = 1;
                                }break;
                                case 2:Key_F_STATE_C = 7;break;
                                case 3:Key_F_STATE_C = 8;break;
                                case 4:Key_F_STATE_C = 9;break;
                                case 5:SHOW_CTRl_N_STATE = Key_F_STATE_C ;break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 6)//闹钟设置页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;
                                case 1:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        FSD_Line==1?FSD_Line=1:FSD_Line--;
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                                case 1:{
                                                        ALM_TIME[FSD_Line-1][FSD_Colu-1]--;
                                                        if(ALM_TIME[FSD_Line-1][FSD_Colu-1]<48)
                                                        {
                                                                ALM_TIME[FSD_Line-1][FSD_Colu-1]=48;
                                                        }
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                        }
                                }break;
                                case 2:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        FSD_Line==2?FSD_Line=2:FSD_Line++;
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                                case 1:{
                                                        ALM_TIME[FSD_Line-1][FSD_Colu-1]++;
                                                        if((FSD_Colu-1)==0)
                                                        {
                                                                if(ALM_TIME[FSD_Line-1][FSD_Colu-1]>=50)
                                                                {
                                                                        ALM_TIME[FSD_Line-1][FSD_Colu-1]=50;
                                                                        if(ALM_TIME[FSD_Line-1][1] >= 52)
                                                                                ALM_TIME[FSD_Line-1][1]=52;
                                                                }
                                                        }else if((FSD_Colu-1)==1)
                                                        {
                                                                if(ALM_TIME[FSD_Line-1][0]==50)
                                                                {
                                                                        if(ALM_TIME[FSD_Line-1][FSD_Colu-1]>52)
                                                                                ALM_TIME[FSD_Line-1][FSD_Colu-1]=52;
                                                                }
                                                        }else if((FSD_Colu-1)==2)
                                                        {
                                                                if(ALM_TIME[FSD_Line-1][2]>=53)
                                                                {
                                                                        ALM_TIME[FSD_Line-1][2]=53;
                                                                }
                                                        }
                                                        if(ALM_TIME[FSD_Line-1][FSD_Colu-1]>=57)
                                                        {
                                                                ALM_TIME[FSD_Line-1][FSD_Colu-1]=57;
                                                        }
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                        }
                                }break;
                                case 3:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                }break;
                                                case 1:{
                                                        FSD_Colu == 1?FSD_Colu=1:FSD_Colu--;
                                                }break;
                                        }
                                }break;
                                case 4:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        ALM_STA = 1;
                                                }break;
                                                case 1:{
                                                        FSD_Colu >= 5?FSD_Colu=5:FSD_Colu++;
                                                }break;
                                        }
                                }break;
                                case 5:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        SHOW_CTRl_N_STATE = 2;
                                                }break;
                                                case 1:{
                                                        ALM_STA = 0;
                                                }break;
                                        }
                                }break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 7)//倒计时页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;//无按键按下
                                
                                case 1:{//up
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                        FSD_Line==1?FSD_Line=1:FSD_Line--;
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 0:;break;
                                                                case 1:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=600)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=600;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=600;
                                                                        }
                                                                }break;
                                                                case 2:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=60)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=60;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=60;
                                                                        }
                                                                }break;
                                                                case 3:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=10)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=10;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=10;
                                                                        }
                                                                }break;
                                                                case 4:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=1)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=1;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=1;
                                                                        }
                                                                }break;
                                                                case 5:{
                                                                        TMR_S_S|=(0x01<<(FSD_Line-1));
                                                                        TMR_STA = 2;
                                                                }break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 2:{//倒计时中状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 4:{//切换第几个倒计时
                                                                        FSD_Line==1?FSD_Line=1:FSD_Line--;
                                                                        SHOW_CTRl_N_STATE = 7;
                                                                }break;
                                                                case 5:TMR_S_S|=(0x01<<(FSD_Line-1));break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                        }
                                }break;
                                
                                case 2:{//down
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                        FSD_Line==2?FSD_Line=2:FSD_Line++;
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 0:;break;
                                                                case 1:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5399)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=600;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=600;
                                                                        }
                                                                }break;
                                                                case 2:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5939)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=60 ;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=60;
                                                                        }
                                                                }break;
                                                                case 3:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5989)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=10 ;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=10;
                                                                        }
                                                                }break;
                                                                case 4:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5998)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=1 ;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=1;
                                                                        }
                                                                }break;
                                                                case 5:TMR_S_S&=(!(0x01<<(FSD_Line-1)));break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 2:{//倒计时中状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 4:{
                                                                        FSD_Line==2?FSD_Line=2:FSD_Line++;
                                                                        SHOW_CTRl_N_STATE = 7;
                                                                }break;
                                                                case 5:TMR_S_S&=(!(0x01<<(FSD_Line-1)));break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                        }
                                }break;
                                
                                case 3:{//left
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        FSD_Colu == 1?FSD_Colu=1:FSD_Colu--;
                                                }break;
                                                case 2:{//倒计时中状态
                                                        FSD_Colu == 5?FSD_Colu=5:FSD_Colu--;
                                                }break;
                                        }
                                }break;
                                
                                case 4:{//right
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                        if(TMR_S_S &(0x01<<(FSD_Line-1)))//判断该倒计时是否开启中
                                                        {
                                                                TMR_STA = 2;//从切换倒计时状态变为设置倒计时状态
                                                                FSD_Colu =5;
                                                        }else
                                                        {
                                                                TMR_STA = 1;//从切换倒计时状态变为设置倒计时状态
                                                                FSD_Colu =1;
                                                        }
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        if(FSD_Colu >= 6)
                                                        {
                                                                FSD_Colu=6;
                                                                SHOW_CTRl_N_STATE = 7;
                                                        }else
                                                        {
                                                                FSD_Colu++;
                                                        }
                                                }break;
                                                case 2:{//倒计时中状态
                                                        if(FSD_Colu >= 6)
                                                        {
                                                                FSD_Colu=6;
                                                                SHOW_CTRl_N_STATE = 7;
                                                        }else
                                                        {
                                                                FSD_Colu++;
                                                        }
                                                }break;
                                        }
                                }break;
                                
                                case 5:{//confirm
                                        switch(TMR_STA)
                                        {
                                                case 0:{ //切换倒计时
                                                        SHOW_CTRl_P_STATE = 2;//切换为时间显示态
                                                        SHOW_CTRl_N_STATE = 2;//切换为时间显示态
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        TMR_STA = 0;//切换为切换状态
                                                }break;
                                                case 2:{//倒计时中状态
                                                        if(FSD_Colu == 6)
                                                        {
                                                                TMR_SHOW_NUM[FSD_Line-1] = TMR_SHOW_NUM_RST[FSD_Line-1];
                                                        }else
                                                        {
                                                                TMR_STA = 0;//切换为切换状态
                                                        }
                                                }break;
                                        }
                                }break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 8)//秒表页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;//无按键按下
                                case 1:{//上
                                        switch(STW_STA)
                                        {
                                                case 0:{//计时中
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{
                                                                        STW_STA = 1;
                                                                        STW_SPT_NUM_i = 0;
                                                                        STW_SHOW_NUM_SPT[0] = STW_SHOW_NUM;
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                                case 2:{
                                                                }break;
                                                                case 3:{
                                                                }break;
                                                        }
                                                }break;
                                                case 1:{//计时暂停
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{//不变
                                                                }break;
                                                                case 2:{//清除
                                                                }break;
                                                                case 3:{//翻看记录
                                                                        
                                                                        if(STW_SPT_NUM_i>0)//限位
                                                                                STW_SPT_NUM_i--;
                                                                        STW_SHOW_NUM = STW_SHOW_NUM_SPT[STW_SPT_NUM_i];
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                        }
                                                }break;
                                        }
                                };break;
                                case 2:{//下
                                        switch(STW_STA)
                                        {
                                                case 0:{//计时中
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{
                                                                }break;
                                                                case 2:{//记录
                                                                        if(STW_SPT_NUM<STWSetNum-1)
                                                                        {
                                                                                STW_SPT_NUM++;
                                                                        }
                                                                        STW_SHOW_NUM_SPT[STW_SPT_NUM] = STW_SHOW_NUM;
                                                                }break;
                                                                case 3:{
                                                                }break;
                                                        }
                                                }break;
                                                case 1:{//计时暂停
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{
                                                                        STW_STA = 0;
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                                case 2:{//清除
                                                                        STW_SHOW_NUM = 0;
                                                                        STW_SPT_NUM = 0;
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                        //若后台有倒计时会对其有一定影响
                                                                        TH1=0x3c;//50ms中断一次
                                                                        TL1=0xAF;
                                                                }break;
                                                                case 3:{//翻看记录
                                                                        STW_SPT_NUM_i++;
                                                                        if(STW_SPT_NUM_i>=STW_SPT_NUM)//限位
                                                                                STW_SPT_NUM_i=STW_SPT_NUM;
                                                                        STW_SHOW_NUM = STW_SHOW_NUM_SPT[STW_SPT_NUM_i];
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                        }
                                                }break;
                                        }
                                };break;
                                case 3:{//左
                                        if(FSD_Colu>0)
                                        {
                                                FSD_Colu--;
                                        }
                                };break;
                                case 4:{//右
                                        if(FSD_Colu<3)
                                        {
                                                FSD_Colu++;
                                        }
                                };break;
                                case 5:{//确认
                                        SHOW_CTRl_P_STATE = 2;//切换为时间显示态
                                        SHOW_CTRl_N_STATE = 2;//切换为时间显示态
                                };break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 9)//24/12时页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;
                                case 1:Tim_12_24_Set = 0;SHOW_CTRl_N_STATE=9;break;
                                case 2:Tim_12_24_Set = 1;SHOW_CTRl_N_STATE=9;break;
                                case 5:SHOW_CTRl_N_STATE = 2; ;break;
                        }
                }
               
        }
}
```
## 六、闹钟任务
闹钟任务钟主要使用闹钟的有效值判断闹钟是否开启,若开启则将设置的闹钟值与当前时间进行比较,当当前时间与设置的闹钟时间一致时则led灯亮。具体代码如下:

```c
/**函数定义:
  * @brief  闹钟任务
  * @param  无
  * @retval 无
  */
void ALM_Task()
{
        if(ALM_TIME[0][4]%2)
        {
                if(DS1302_Time[3]==((ALM_TIME[0][0]-48)*10+ALM_TIME[0][1]-48) && DS1302_Time[4]==((ALM_TIME[0][2]-48)*10+ALM_TIME[0][3]-48))
                {
                        led=0;
                        return;
                }
        }
        
        if(ALM_TIME[1][4]%2)
        {
                if(DS1302_Time[3]==((ALM_TIME[1][0]-48)*10+ALM_TIME[1][1]-48) && DS1302_Time[4]==((ALM_TIME[1][2]-48)*10+ALM_TIME[1][3]-48))
                {
                        led=0;
                        return;
                }
        }
        led =1;
}
```
## 七、倒计时任务
倒计时任务与闹钟任务类似,也是比较后灯亮具体代码如下

```c
/**函数定义:
  * @brief  倒计时任务
  * @param  无
  * @retval 无
  */
idata u8 TMR_P_TIME=0;
idata u8 TMR_FLAG =0;
void TMR_Task()
{
        if(TMR_FLAG == 1)
        {
                if(DS1302_Time[5]<TMR_P_TIME)
                {
                        led=0;
                        return;
                }
        }        
        led =1;
        TMR_FLAG=0;
}
```
## 八、功能选择任务
功能选择任务为整个项目的核心。它控制着整个项目的状态机变换方向,同时针对外部按键做出的状态赋值。完成相应的显示内容切换和状态转换。具体代码如下:

```c
//根据页面设计相应的状态机,需要统一延时态返回到上一状态
void SHOW_CTRl_Task()
{
        //初始化状态
        if(SHOW_CTRl_N_STATE == 0)
        {
                if(LCD_STATE == 3)
                {
                        SHOW_CTRl_DELAY = TimeRef + NUM_1000MS;
                        SHOW_CTRl_N_STATE = 1;//切换到延时态
                        SHOW_CTRl_F_STATE = 2;//延时态结束后切换到时间显示态
                }
        }
        //延时态
        if(SHOW_CTRl_N_STATE == 1)
        {
                if(TimeRef >= SHOW_CTRl_DELAY)
                {
                        SHOW_CTRl_N_STATE = SHOW_CTRl_F_STATE;
                }
        }
        //时间显示态
        if(SHOW_CTRl_N_STATE == 2)
        {
                LCD_ShowBase();
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE =2;
        }
        //等待显示完成
        if(SHOW_CTRl_N_STATE == 3)
        {
                if(LCD_STATE == 3)
                {
                        SHOW_CTRl_N_STATE = SHOW_CTRl_F_STATE;
                }
        }
        //外部触发动作,功能选择态
        if(SHOW_CTRl_N_STATE == 4)
        {
                //出现需要按下两次功能选择按键才能正常显示的现象,疑似与lcd状态机有关,需要进行修改
                strcpy(LCD_SHOW_1,"U:RLM L:STW     ");
                strcpy(LCD_SHOW_2,"D:TMR R:24/12   ");
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
                //初始化该变量以防在多个功能使用时出现问题。
                FSD_Line=1;//功能设置显示行
                FSD_Colu=1;//功能设置显示列
                STW_SPT_NUM = 0;
                //状态设置
                SHOW_CTRl_P_STATE = 4;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
                ReadKeyEN = 1;
                Key_F_STATE_C = SHOW_CTRl_F_STATE;//初始化按键状态防止状态机被意外改变
        }
        //显示暂停态
        if(SHOW_CTRl_N_STATE == 5)
        {
                SHOW_CTRl_N_STATE = SHOW_CTRl_F_STATE;
        }
        //闹钟设置状态
        if(SHOW_CTRl_N_STATE == 6)
        {
                strcpy(LCD_SHOW_1,"     RLMSet     ");
                strcpy(LCD_SHOW_2,"A  00:00        ");
                LCD_SHOW_2[1] = FSD_Line +'0';
                LCD_SHOW_2[3] = ALM_TIME[FSD_Line-1][0];
                LCD_SHOW_2[4] = ALM_TIME[FSD_Line-1][1];
                LCD_SHOW_2[6] = ALM_TIME[FSD_Line-1][2];
                LCD_SHOW_2[7] = ALM_TIME[FSD_Line-1][3];
               
                if(ALM_TIME[FSD_Line-1][4]%2)
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'N';
                        LCD_SHOW_2[11] = ' ';
                }else
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'F';
                        LCD_SHOW_2[11] = 'F';
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
                SHOW_CTRl_P_STATE = 6;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
                //倒计时状态(开启关闭重置)
        if(SHOW_CTRl_N_STATE == 7)
        {
                strcpy(LCD_SHOW_1,"     TMRSet     ");
                strcpy(LCD_SHOW_2,"T    :       R  ");
                //只负责显示
                LCD_SHOW_2[1] = FSD_Line +'0';
                LCD_SHOW_2[3] = TMR_SHOW_NUM[FSD_Line-1]/600 +'0';
                LCD_SHOW_2[4] = TMR_SHOW_NUM[FSD_Line-1]/60%10+'0';
                LCD_SHOW_2[6] = TMR_SHOW_NUM[FSD_Line-1]%60/10+'0';
                LCD_SHOW_2[7] = TMR_SHOW_NUM[FSD_Line-1]%10+'0';
                //判断倒计时是否开启
                if(TMR_S_S &(0x01<<(FSD_Line-1)))//开启
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'N';
                        LCD_SHOW_2[11] = ' ';
                        if(TMR_S_S==0x01||TMR_S_S==0x02)//第一次开启
                        {
                                TH1=0x3c;//50ms 中断一次
                                TL1=0xAF;
                                TR1=1;
                        }
                }else
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'F';
                        LCD_SHOW_2[11] = 'F';
                        if(TMR_S_S==0)//最后一次关闭
                        {
                                TR1=0;
                        }
                }
                //倒计时是否重置显示内容
                if(FSD_Colu == 6)
                {
                        LCD_SHOW_2[15] = '?';
                }else
                {
                        LCD_SHOW_2[15] = ' ';
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
                SHOW_CTRl_P_STATE = 7;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
                //秒表设置状态
        if(SHOW_CTRl_N_STATE == 8)
        {
                strcpy(LCD_SHOW_1,"     STWSet     ");
                strcpy(LCD_SHOW_2,"   :  :  OFF RES");        
                //只负责显示
                LCD_SHOW_2[1] = STW_SHOW_NUM/6000+'0';
                LCD_SHOW_2[2] = STW_SHOW_NUM/600%10+'0';
                LCD_SHOW_2[4] = STW_SHOW_NUM/100%10+'0';
                LCD_SHOW_2[5] = STW_SHOW_NUM/10%10+'0';
                LCD_SHOW_2[7] = STW_SHOW_NUM%10+'0';
                if(STW_STA)
                {
                        LCD_SHOW_2[9] ='O';
                        LCD_SHOW_2[10]='F';
                        LCD_SHOW_2[11]='F';
                        LCD_SHOW_2[13]='R';
                        LCD_SHOW_2[14]='S';
                        LCD_SHOW_2[15]='T';
                        //在按下RST键时重新写入定时器值
                }else
                {
                        LCD_SHOW_2[9] ='O';
                        LCD_SHOW_2[10]='N';
                        LCD_SHOW_2[11]=' ';
                        
                        LCD_SHOW_2[13]='S';
                        LCD_SHOW_2[14]='P';
                        LCD_SHOW_2[15]='T';
                        TR1=1;                        
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
               
                SHOW_CTRl_P_STATE = 8;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
                //12/24设置状态
        if(SHOW_CTRl_N_STATE == 9)
        {
                strcpy(LCD_SHOW_1,"    24/12Set    ");
                strcpy(LCD_SHOW_2,"                ");
                if(Tim_12_24_Set)
                {
                        LCD_SHOW_2[4] = '1';
                        LCD_SHOW_2[5] = '2';
                        LCD_SHOW_2[6] = 'H';
                }else
                {
                        LCD_SHOW_2[4] = '2';
                        LCD_SHOW_2[5] = '4';
                        LCD_SHOW_2[6] = 'H';
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
               
                SHOW_CTRl_P_STATE = 9;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
}
```
## 九、总结
本项目使用了分层、时间片轮、状态机思想完成了51电子钟的设计。其主要功能包含基本时间显示、温度显示、倒计时、秒表、闹钟、24h/12h选择,使用按键选择和设置相应的功能。本项目的代码对于状态机思想进行了比较深入的运用希望各位能够在其中有所收获。```c
void LCD_Task()
{        //显示初始化状态
        if(LCD_STATE == 0)
        {
                if(LCD_Init() == 1)
                {
                        LCD_STATE =1;
                }
        }
        //显示第一行状态  可能还需细分,视需求而定
        if(LCD_STATE == 1)
        {
                if(LCD_ShowString(1,Column1,LCD_SHOW_1) == 1)
                {
                        LCD_STATE =2;
                }
        }
        if(LCD_STATE == 2)
        {
                if(LCD_ShowString(2,Column2,LCD_SHOW_2) == 1)
                {
                        LCD_STATE =3;
                }
        }
        //显示暂停状态
        if(LCD_STATE == 3)
        {
        }
}
```
### 中间层代码如下:
```c
u8 LCD_Init()
{        
        if(LCD_STATE_1 == 0)
        {
                //八位数据接口,两行显示,5*7点阵
                if(LCD_WriteCommand(0x38) == 1)
                {
                        LCD_STATE_1 = 1;
                }
        }
        
        if(LCD_STATE_1 == 1)
        {
                //显示开,光标关,闪烁关
                if(LCD_WriteCommand(0x0c) == 1)
                {
                        LCD_STATE_1 = 2;
                }
        }
        
        if(LCD_STATE_1 == 2)
        {
                //数据读写操作后,光标自动加一,画面不动
                if(LCD_WriteCommand(0x06) == 1)
                {
                        LCD_STATE_1 = 3;
                }
        }
        
        if(LCD_STATE_1 == 3)
        {
                //光标复位,清屏
                if(LCD_WriteCommand(0x01) == 1)
                {
                        LCD_STATE_1 = 0;
                        return 1;
                }
        }
        return 0;
}
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 0:显示字符未完成
        *         1:显示字符完成
  */
u8 LCD_ShowChar(u8 Line,u8 Column,char Char)
{
        if(LCD_STATE_1 == 0)
        {
                if(LCD_SetCursor(Line,Column) == 1)
                        LCD_STATE_1 = 1;
        }
        if(LCD_STATE_1 == 1)
        {
                if(LCD_WriteData(Char) == 1)
                {
                        LCD_STATE_1 = 0;
                        return 1;
                }
        }
        return 0;
}
```
### 底层代码如下:
```c
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
u8 LCD_WriteCommand(u8 Command)
{
        if(LCD_STATE_0==0)
        {
                LCD_RS=0;
                LCD_RW=0;
                LCD_DataPort=Command;
                LCD_EN=1;
                LCD_DELAY = TimeRef + NUM_1MS;
                LCD_STATE_0 = 1;
        }
        if(LCD_STATE_0 == 1)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_EN=0;
                        LCD_DELAY = TimeRef + NUM_1MS;
                        LCD_STATE_0 = 2;
                }
        }
        if(LCD_STATE_0 == 2)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_STATE_0 = 0;
                        return 1;
                }
        }
        return 0;
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 0:未写入完成
        *         1:写入完成
  */
u8 LCD_WriteData(u8 Data)
{
        if(LCD_STATE_0==0)
        {
                LCD_RS=1;
                LCD_RW=0;
                LCD_DataPort=Data;
                LCD_EN=1;
                LCD_DELAY = TimeRef + NUM_1MS;
                LCD_STATE_0 = 1;
        }
        if(LCD_STATE_0 == 1)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_EN=0;
                        LCD_DELAY = TimeRef + NUM_1MS;
                        LCD_STATE_0 = 2;
                }
        }
        if(LCD_STATE_0 == 2)
        {
                if(TimeRef >= LCD_DELAY)
                {
                        LCD_STATE_0 = 0;
                        return 1;
                }
        }
        return 0;
}
```
## 四、温度获取任务
温度获取任务主要分为初始化获取和循环获取。初始化获取用于立刻获取温度值防止出现刚开机时不显示温度的情况。循环获取用于间隔一段时间获取一次温度,实现较为实时的温度显示。基本代码如下
```c
//函数定义:
/**
  * @brief  温度获取任务
  * @param  无
  * @retval 无
  */
void TEMP_Task()
{        //初始化状态
        if(TEMP_STATE == 0)
        {
                if(DS18B20_ConvertT() == 1)
                {
                        TEMP_DELAY = TimeRef + NUM_1000MS;
                        TEMP_STATE = 1;
                }
        }
        //延时状态
        if(TEMP_STATE == 1)
        {
                if(TimeRef >= TEMP_DELAY)
                {
                        TEMP_STATE=2;
                }
        }
        //转换状态
        if(TEMP_STATE == 2)
        {
                if(DS18B20_ConvertT() == 1)
                {
                        TEMP_STATE = 3;
                }
        }
        if(TEMP_STATE == 3)
        {
                Temp = DS18B20_ReadT();
                TEMP_DELAY = TimeRef + (u32)NUM_1000MS*1800;
                TEMP_STATE =1;
        }
}
```
该线程所需的驱动并未实现完全的状态机化由于单总线所需的延时较为精准在尝试了进行状态机化后发现会出现读取乱码的情况,且部分函数所需的延时时间太短甚至需要关闭定时器中断以对该延时进行保护,可对标操作系统的临界区保护。
## 五、按键获取任务
按键获取任务基本架构、分层与[按键控制数码管项目](51单片机按键数码管显示 时间片轮转+状态机 源程序 - 51单片机)类似,但由于本项目的功能较多且针对于项目的显示内容使用了现态的方式进行控制,即按键任务无法得知完成显示任务前的状态(功能),故引入过去态和未来态,前者供需要在现态中进行状态转换的任务标记其源头,后者用于在使用完相应的资源后跳转到相应的任务上。故按键对于当前功能界面的判断使用了过去态的方式,同时配合行号和列号完成相应功能。具体代码如下:

```c
void ReadKey_Task()
{
        if(ReadKeyEN)
        {
                ReadKeyValue = ReadKeyDat();
                //记录状态机的次态、现态、下态,在根据次态确认按下按键执行的内容
                if(SHOW_CTRl_P_STATE == 4)//功能选择页面的按键选择
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;
                                case 1:{
                                        Key_F_STATE_C = 6;
                                        FSD_Line = 1;
                                        FSD_Colu = 1;
                                }break;
                                case 2:Key_F_STATE_C = 7;break;
                                case 3:Key_F_STATE_C = 8;break;
                                case 4:Key_F_STATE_C = 9;break;
                                case 5:SHOW_CTRl_N_STATE = Key_F_STATE_C ;break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 6)//闹钟设置页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;
                                case 1:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        FSD_Line==1?FSD_Line=1:FSD_Line--;
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                                case 1:{
                                                        ALM_TIME[FSD_Line-1][FSD_Colu-1]--;
                                                        if(ALM_TIME[FSD_Line-1][FSD_Colu-1]<48)
                                                        {
                                                                ALM_TIME[FSD_Line-1][FSD_Colu-1]=48;
                                                        }
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                        }
                                }break;
                                case 2:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        FSD_Line==2?FSD_Line=2:FSD_Line++;
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                                case 1:{
                                                        ALM_TIME[FSD_Line-1][FSD_Colu-1]++;
                                                        if((FSD_Colu-1)==0)
                                                        {
                                                                if(ALM_TIME[FSD_Line-1][FSD_Colu-1]>=50)
                                                                {
                                                                        ALM_TIME[FSD_Line-1][FSD_Colu-1]=50;
                                                                        if(ALM_TIME[FSD_Line-1][1] >= 52)
                                                                                ALM_TIME[FSD_Line-1][1]=52;
                                                                }
                                                        }else if((FSD_Colu-1)==1)
                                                        {
                                                                if(ALM_TIME[FSD_Line-1][0]==50)
                                                                {
                                                                        if(ALM_TIME[FSD_Line-1][FSD_Colu-1]>52)
                                                                                ALM_TIME[FSD_Line-1][FSD_Colu-1]=52;
                                                                }
                                                        }else if((FSD_Colu-1)==2)
                                                        {
                                                                if(ALM_TIME[FSD_Line-1][2]>=53)
                                                                {
                                                                        ALM_TIME[FSD_Line-1][2]=53;
                                                                }
                                                        }
                                                        if(ALM_TIME[FSD_Line-1][FSD_Colu-1]>=57)
                                                        {
                                                                ALM_TIME[FSD_Line-1][FSD_Colu-1]=57;
                                                        }
                                                        SHOW_CTRl_N_STATE = 6;
                                                }break;
                                        }
                                }break;
                                case 3:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                }break;
                                                case 1:{
                                                        FSD_Colu == 1?FSD_Colu=1:FSD_Colu--;
                                                }break;
                                        }
                                }break;
                                case 4:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        ALM_STA = 1;
                                                }break;
                                                case 1:{
                                                        FSD_Colu >= 5?FSD_Colu=5:FSD_Colu++;
                                                }break;
                                        }
                                }break;
                                case 5:{
                                        switch(ALM_STA)
                                        {
                                                case 0:{
                                                        SHOW_CTRl_N_STATE = 2;
                                                }break;
                                                case 1:{
                                                        ALM_STA = 0;
                                                }break;
                                        }
                                }break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 7)//倒计时页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;//无按键按下
                                
                                case 1:{//up
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                        FSD_Line==1?FSD_Line=1:FSD_Line--;
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 0:;break;
                                                                case 1:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=600)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=600;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=600;
                                                                        }
                                                                }break;
                                                                case 2:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=60)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=60;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=60;
                                                                        }
                                                                }break;
                                                                case 3:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=10)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=10;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=10;
                                                                        }
                                                                }break;
                                                                case 4:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]>=1)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]-=1;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]-=1;
                                                                        }
                                                                }break;
                                                                case 5:{
                                                                        TMR_S_S|=(0x01<<(FSD_Line-1));
                                                                        TMR_STA = 2;
                                                                }break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 2:{//倒计时中状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 4:{//切换第几个倒计时
                                                                        FSD_Line==1?FSD_Line=1:FSD_Line--;
                                                                        SHOW_CTRl_N_STATE = 7;
                                                                }break;
                                                                case 5:TMR_S_S|=(0x01<<(FSD_Line-1));break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                        }
                                }break;
                                
                                case 2:{//down
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                        FSD_Line==2?FSD_Line=2:FSD_Line++;
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 0:;break;
                                                                case 1:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5399)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=600;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=600;
                                                                        }
                                                                }break;
                                                                case 2:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5939)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=60 ;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=60;
                                                                        }
                                                                }break;
                                                                case 3:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5989)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=10 ;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=10;
                                                                        }
                                                                }break;
                                                                case 4:{
                                                                        if(TMR_SHOW_NUM[FSD_Line-1]<=5998)
                                                                        {
                                                                                TMR_SHOW_NUM[FSD_Line-1]+=1 ;
                                                                                TMR_SHOW_NUM_RST[FSD_Line-1]+=1;
                                                                        }
                                                                }break;
                                                                case 5:TMR_S_S&=(!(0x01<<(FSD_Line-1)));break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                                case 2:{//倒计时中状态
                                                        switch(FSD_Colu)
                                                        {
                                                                case 4:{
                                                                        FSD_Line==2?FSD_Line=2:FSD_Line++;
                                                                        SHOW_CTRl_N_STATE = 7;
                                                                }break;
                                                                case 5:TMR_S_S&=(!(0x01<<(FSD_Line-1)));break;
                                                                case 6:;break;
                                                        }
                                                        SHOW_CTRl_N_STATE = 7;
                                                }break;
                                        }
                                }break;
                                
                                case 3:{//left
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        FSD_Colu == 1?FSD_Colu=1:FSD_Colu--;
                                                }break;
                                                case 2:{//倒计时中状态
                                                        FSD_Colu == 5?FSD_Colu=5:FSD_Colu--;
                                                }break;
                                        }
                                }break;
                                
                                case 4:{//right
                                        switch(TMR_STA)
                                        {
                                                case 0:{//切换倒计时
                                                        if(TMR_S_S &(0x01<<(FSD_Line-1)))//判断该倒计时是否开启中
                                                        {
                                                                TMR_STA = 2;//从切换倒计时状态变为设置倒计时状态
                                                                FSD_Colu =5;
                                                        }else
                                                        {
                                                                TMR_STA = 1;//从切换倒计时状态变为设置倒计时状态
                                                                FSD_Colu =1;
                                                        }
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        if(FSD_Colu >= 6)
                                                        {
                                                                FSD_Colu=6;
                                                                SHOW_CTRl_N_STATE = 7;
                                                        }else
                                                        {
                                                                FSD_Colu++;
                                                        }
                                                }break;
                                                case 2:{//倒计时中状态
                                                        if(FSD_Colu >= 6)
                                                        {
                                                                FSD_Colu=6;
                                                                SHOW_CTRl_N_STATE = 7;
                                                        }else
                                                        {
                                                                FSD_Colu++;
                                                        }
                                                }break;
                                        }
                                }break;
                                
                                case 5:{//confirm
                                        switch(TMR_STA)
                                        {
                                                case 0:{ //切换倒计时
                                                        SHOW_CTRl_P_STATE = 2;//切换为时间显示态
                                                        SHOW_CTRl_N_STATE = 2;//切换为时间显示态
                                                }break;
                                                case 1:{//设置倒计时状态
                                                        TMR_STA = 0;//切换为切换状态
                                                }break;
                                                case 2:{//倒计时中状态
                                                        if(FSD_Colu == 6)
                                                        {
                                                                TMR_SHOW_NUM[FSD_Line-1] = TMR_SHOW_NUM_RST[FSD_Line-1];
                                                        }else
                                                        {
                                                                TMR_STA = 0;//切换为切换状态
                                                        }
                                                }break;
                                        }
                                }break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 8)//秒表页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;//无按键按下
                                case 1:{//上
                                        switch(STW_STA)
                                        {
                                                case 0:{//计时中
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{
                                                                        STW_STA = 1;
                                                                        STW_SPT_NUM_i = 0;
                                                                        STW_SHOW_NUM_SPT[0] = STW_SHOW_NUM;
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                                case 2:{
                                                                }break;
                                                                case 3:{
                                                                }break;
                                                        }
                                                }break;
                                                case 1:{//计时暂停
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{//不变
                                                                }break;
                                                                case 2:{//清除
                                                                }break;
                                                                case 3:{//翻看记录
                                                                        
                                                                        if(STW_SPT_NUM_i>0)//限位
                                                                                STW_SPT_NUM_i--;
                                                                        STW_SHOW_NUM = STW_SHOW_NUM_SPT[STW_SPT_NUM_i];
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                        }
                                                }break;
                                        }
                                };break;
                                case 2:{//下
                                        switch(STW_STA)
                                        {
                                                case 0:{//计时中
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{
                                                                }break;
                                                                case 2:{//记录
                                                                        if(STW_SPT_NUM<STWSetNum-1)
                                                                        {
                                                                                STW_SPT_NUM++;
                                                                        }
                                                                        STW_SHOW_NUM_SPT[STW_SPT_NUM] = STW_SHOW_NUM;
                                                                }break;
                                                                case 3:{
                                                                }break;
                                                        }
                                                }break;
                                                case 1:{//计时暂停
                                                        switch(FSD_Colu)
                                                        {
                                                                case 1:{
                                                                        STW_STA = 0;
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                                case 2:{//清除
                                                                        STW_SHOW_NUM = 0;
                                                                        STW_SPT_NUM = 0;
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                        //若后台有倒计时会对其有一定影响
                                                                        TH1=0x3c;//50ms中断一次
                                                                        TL1=0xAF;
                                                                }break;
                                                                case 3:{//翻看记录
                                                                        STW_SPT_NUM_i++;
                                                                        if(STW_SPT_NUM_i>=STW_SPT_NUM)//限位
                                                                                STW_SPT_NUM_i=STW_SPT_NUM;
                                                                        STW_SHOW_NUM = STW_SHOW_NUM_SPT[STW_SPT_NUM_i];
                                                                        SHOW_CTRl_N_STATE = 8;
                                                                }break;
                                                        }
                                                }break;
                                        }
                                };break;
                                case 3:{//左
                                        if(FSD_Colu>0)
                                        {
                                                FSD_Colu--;
                                        }
                                };break;
                                case 4:{//右
                                        if(FSD_Colu<3)
                                        {
                                                FSD_Colu++;
                                        }
                                };break;
                                case 5:{//确认
                                        SHOW_CTRl_P_STATE = 2;//切换为时间显示态
                                        SHOW_CTRl_N_STATE = 2;//切换为时间显示态
                                };break;
                        }
                }
                if(SHOW_CTRl_P_STATE == 9)//24/12时页面的按键
                {
                        switch(ReadKeyValue)
                        {
                                case 0:;break;
                                case 1:Tim_12_24_Set = 0;SHOW_CTRl_N_STATE=9;break;
                                case 2:Tim_12_24_Set = 1;SHOW_CTRl_N_STATE=9;break;
                                case 5:SHOW_CTRl_N_STATE = 2; ;break;
                        }
                }
               
        }
}
```
## 六、闹钟任务
闹钟任务钟主要使用闹钟的有效值判断闹钟是否开启,若开启则将设置的闹钟值与当前时间进行比较,当当前时间与设置的闹钟时间一致时则led灯亮。具体代码如下:

```c
/**函数定义:
  * @brief  闹钟任务
  * @param  无
  * @retval 无
  */
void ALM_Task()
{
        if(ALM_TIME[0][4]%2)
        {
                if(DS1302_Time[3]==((ALM_TIME[0][0]-48)*10+ALM_TIME[0][1]-48) && DS1302_Time[4]==((ALM_TIME[0][2]-48)*10+ALM_TIME[0][3]-48))
                {
                        led=0;
                        return;
                }
        }
        
        if(ALM_TIME[1][4]%2)
        {
                if(DS1302_Time[3]==((ALM_TIME[1][0]-48)*10+ALM_TIME[1][1]-48) && DS1302_Time[4]==((ALM_TIME[1][2]-48)*10+ALM_TIME[1][3]-48))
                {
                        led=0;
                        return;
                }
        }
        led =1;
}
```
## 七、倒计时任务
倒计时任务与闹钟任务类似,也是比较后灯亮具体代码如下

```c
/**函数定义:
  * @brief  倒计时任务
  * @param  无
  * @retval 无
  */
idata u8 TMR_P_TIME=0;
idata u8 TMR_FLAG =0;
void TMR_Task()
{
        if(TMR_FLAG == 1)
        {
                if(DS1302_Time[5]<TMR_P_TIME)
                {
                        led=0;
                        return;
                }
        }        
        led =1;
        TMR_FLAG=0;
}
```
## 八、功能选择任务
功能选择任务为整个项目的核心。它控制着整个项目的状态机变换方向,同时针对外部按键做出的状态赋值。完成相应的显示内容切换和状态转换。具体代码如下:

```c
//根据页面设计相应的状态机,需要统一延时态返回到上一状态
void SHOW_CTRl_Task()
{
        //初始化状态
        if(SHOW_CTRl_N_STATE == 0)
        {
                if(LCD_STATE == 3)
                {
                        SHOW_CTRl_DELAY = TimeRef + NUM_1000MS;
                        SHOW_CTRl_N_STATE = 1;//切换到延时态
                        SHOW_CTRl_F_STATE = 2;//延时态结束后切换到时间显示态
                }
        }
        //延时态
        if(SHOW_CTRl_N_STATE == 1)
        {
                if(TimeRef >= SHOW_CTRl_DELAY)
                {
                        SHOW_CTRl_N_STATE = SHOW_CTRl_F_STATE;
                }
        }
        //时间显示态
        if(SHOW_CTRl_N_STATE == 2)
        {
                LCD_ShowBase();
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE =2;
        }
        //等待显示完成
        if(SHOW_CTRl_N_STATE == 3)
        {
                if(LCD_STATE == 3)
                {
                        SHOW_CTRl_N_STATE = SHOW_CTRl_F_STATE;
                }
        }
        //外部触发动作,功能选择态
        if(SHOW_CTRl_N_STATE == 4)
        {
                //出现需要按下两次功能选择按键才能正常显示的现象,疑似与lcd状态机有关,需要进行修改
                strcpy(LCD_SHOW_1,"U:RLM L:STW     ");
                strcpy(LCD_SHOW_2,"D:TMR R:24/12   ");
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
                //初始化该变量以防在多个功能使用时出现问题。
                FSD_Line=1;//功能设置显示行
                FSD_Colu=1;//功能设置显示列
                STW_SPT_NUM = 0;
                //状态设置
                SHOW_CTRl_P_STATE = 4;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
                ReadKeyEN = 1;
                Key_F_STATE_C = SHOW_CTRl_F_STATE;//初始化按键状态防止状态机被意外改变
        }
        //显示暂停态
        if(SHOW_CTRl_N_STATE == 5)
        {
                SHOW_CTRl_N_STATE = SHOW_CTRl_F_STATE;
        }
        //闹钟设置状态
        if(SHOW_CTRl_N_STATE == 6)
        {
                strcpy(LCD_SHOW_1,"     RLMSet     ");
                strcpy(LCD_SHOW_2,"A  00:00        ");
                LCD_SHOW_2[1] = FSD_Line +'0';
                LCD_SHOW_2[3] = ALM_TIME[FSD_Line-1][0];
                LCD_SHOW_2[4] = ALM_TIME[FSD_Line-1][1];
                LCD_SHOW_2[6] = ALM_TIME[FSD_Line-1][2];
                LCD_SHOW_2[7] = ALM_TIME[FSD_Line-1][3];
               
                if(ALM_TIME[FSD_Line-1][4]%2)
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'N';
                        LCD_SHOW_2[11] = ' ';
                }else
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'F';
                        LCD_SHOW_2[11] = 'F';
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
                SHOW_CTRl_P_STATE = 6;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
                //倒计时状态(开启关闭重置)
        if(SHOW_CTRl_N_STATE == 7)
        {
                strcpy(LCD_SHOW_1,"     TMRSet     ");
                strcpy(LCD_SHOW_2,"T    :       R  ");
                //只负责显示
                LCD_SHOW_2[1] = FSD_Line +'0';
                LCD_SHOW_2[3] = TMR_SHOW_NUM[FSD_Line-1]/600 +'0';
                LCD_SHOW_2[4] = TMR_SHOW_NUM[FSD_Line-1]/60%10+'0';
                LCD_SHOW_2[6] = TMR_SHOW_NUM[FSD_Line-1]%60/10+'0';
                LCD_SHOW_2[7] = TMR_SHOW_NUM[FSD_Line-1]%10+'0';
                //判断倒计时是否开启
                if(TMR_S_S &(0x01<<(FSD_Line-1)))//开启
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'N';
                        LCD_SHOW_2[11] = ' ';
                        if(TMR_S_S==0x01||TMR_S_S==0x02)//第一次开启
                        {
                                TH1=0x3c;//50ms 中断一次
                                TL1=0xAF;
                                TR1=1;
                        }
                }else
                {
                        LCD_SHOW_2[9] = 'O';
                        LCD_SHOW_2[10] = 'F';
                        LCD_SHOW_2[11] = 'F';
                        if(TMR_S_S==0)//最后一次关闭
                        {
                                TR1=0;
                        }
                }
                //倒计时是否重置显示内容
                if(FSD_Colu == 6)
                {
                        LCD_SHOW_2[15] = '?';
                }else
                {
                        LCD_SHOW_2[15] = ' ';
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
                SHOW_CTRl_P_STATE = 7;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
                //秒表设置状态
        if(SHOW_CTRl_N_STATE == 8)
        {
                strcpy(LCD_SHOW_1,"     STWSet     ");
                strcpy(LCD_SHOW_2,"   :  :  OFF RES");        
                //只负责显示
                LCD_SHOW_2[1] = STW_SHOW_NUM/6000+'0';
                LCD_SHOW_2[2] = STW_SHOW_NUM/600%10+'0';
                LCD_SHOW_2[4] = STW_SHOW_NUM/100%10+'0';
                LCD_SHOW_2[5] = STW_SHOW_NUM/10%10+'0';
                LCD_SHOW_2[7] = STW_SHOW_NUM%10+'0';
                if(STW_STA)
                {
                        LCD_SHOW_2[9] ='O';
                        LCD_SHOW_2[10]='F';
                        LCD_SHOW_2[11]='F';
                        LCD_SHOW_2[13]='R';
                        LCD_SHOW_2[14]='S';
                        LCD_SHOW_2[15]='T';
                        //在按下RST键时重新写入定时器值
                }else
                {
                        LCD_SHOW_2[9] ='O';
                        LCD_SHOW_2[10]='N';
                        LCD_SHOW_2[11]=' ';
                        
                        LCD_SHOW_2[13]='S';
                        LCD_SHOW_2[14]='P';
                        LCD_SHOW_2[15]='T';
                        TR1=1;                        
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
               
                SHOW_CTRl_P_STATE = 8;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
                //12/24设置状态
        if(SHOW_CTRl_N_STATE == 9)
        {
                strcpy(LCD_SHOW_1,"    24/12Set    ");
                strcpy(LCD_SHOW_2,"                ");
                if(Tim_12_24_Set)
                {
                        LCD_SHOW_2[4] = '1';
                        LCD_SHOW_2[5] = '2';
                        LCD_SHOW_2[6] = 'H';
                }else
                {
                        LCD_SHOW_2[4] = '2';
                        LCD_SHOW_2[5] = '4';
                        LCD_SHOW_2[6] = 'H';
                }
                Column1 = 1;   
                LCD_STATE = 1;
                Column2 = 1;
               
                SHOW_CTRl_P_STATE = 9;
                SHOW_CTRl_N_STATE = 3;
                SHOW_CTRl_F_STATE = 5;
        }
}
```
## 九、总结
本项目使用了分层、时间片轮、状态机思想完成了51电子钟的设计。其主要功能包含基本时间显示、温度显示、倒计时、秒表、闹钟、24h/12h选择,使用按键选择和设置相应的功能。本项目的代码对于状态机思想进行了比较深入的运用希望各位能够在其中有所收获。

下载链接:https://download.csdn.net/download/tang2010up/89467902

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/857931.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB Map-Reduce 简介

MongoDB Map-Reduce 简介 MongoDB 是一个流行的 NoSQL 数据库,它使用文档存储数据,这些数据以 JSON 格式存储。MongoDB 提供了多种数据处理方法,其中 Map-Reduce 是一种用于批量处理和聚合数据的功能强大的工具。Map-Reduce 允许用户对大量数据进行自定义的聚合操作,适用于…

【大数据 复习】第7章 MapReduce(重中之重)

一、概念 1.MapReduce 设计就是“计算向数据靠拢”&#xff0c;而不是“数据向计算靠拢”&#xff0c;因为移动&#xff0c;数据需要大量的网络传输开销。 2.Hadoop MapReduce是分布式并行编程模型MapReduce的开源实现。 3.特点 &#xff08;1&#xff09;非共享式&#xff0c;…

MySQL学习笔记-进阶篇-视图和存储过程

四、视图和存储过程 视图 存储过程 基本语法 创建 CREATE PROCEDURE ([参数列表]) BEGIN --SQL END; 调用 CALL 存储过程名&#xff08;[参数列表]&#xff09; 查看 --查看指定数据库的存储过程及状态信息 SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SHCEMA…

构建高可用Java微服务架构的秘籍

构建高可用Java微服务架构的秘籍 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 随着云计算和分布式系统的快速发展&#xff0c;微服务架构已成为构建大型应用…

indexedDB---掌握浏览器内建数据库的基本用法

1.认识indexedDB IndexedDB 是一个浏览器内建的数据库&#xff0c;它可以存放对象格式的数据&#xff0c;类似本地存储localstore&#xff0c;但是相比localStore 10MB的存储量&#xff0c;indexedDB可存储的数据量远超过这个数值&#xff0c;具体是多少呢&#xff1f; 默认情…

【软件设计】详细设计说明书(word原件,项目直接套用)

软件详细设计说明书 1.系统总体设计 2.性能设计 3.系统功能模块详细设计 4.数据库设计 5.接口设计 6.系统出错处理设计 7.系统处理规定 软件全套资料&#xff1a;本文末个人名片直接获取或者进主页。

C语言笔试题:实现把一个无符号整型数字的二进制序列反序后输出

目录 题目 实例 方法一&#xff1a;直接交换 方法二&#xff1a;间接交换 拓展 题目 编写一个函数&#xff0c;将一个无符号整数的所有位逆序&#xff08;在32位机器下&#xff09; 实例 例如有一个无符号整数 unsigned int num 32; unsigned int 在32位系统中占4个字…

Selenium - 翻页(常用代码)

文章目录 本文基于 macOS 10.15.7 | selenium - Version: 4.21.0 | Python 3.11.5 import time import os import sys from selenium import webdriver from selenium.webdriver.common.by import Bylist_path list.txtdef next_page(driver):# 示例&#xff1a;获取页面列表…

python从入门到精通10:字符串遍历

在Python中&#xff0c;字符串遍历是一个常见的操作&#xff0c;它允许我们逐个访问字符串中的每个字符。下面将详细讲解Python字符串遍历的几种方式&#xff0c;并结合示例代码进行说明。 1. 使用for…in循环遍历字符串 for…in循环是Python中最常用的遍历字符串的方式。这种…

starrocks进行数据的删除

StarRocks 是一个高性能的分析型数据库&#xff0c;它支持对数据进行各种操作&#xff0c;包括删除操作。在 StarRocks 中&#xff0c;您可以使用 DELETE FROM 语句来删除表中满足特定条件的行。 根据 StarRocks 的文档&#xff0c;DELETE 语句的基本语法如下&#xff1a; sq…

洛谷 P10584 [蓝桥杯 2024 国 A] 数学题(整除分块+杜教筛)

题目 思路来源 登录 - Luogu Spilopelia 题解 参考了两篇洛谷题解&#xff0c;第一篇能得出这个式子&#xff0c;第二篇有比较严格的复杂度分析 结合去年蓝桥杯洛谷P9238&#xff0c;基本就能得出这题的正确做法 代码 #include<bits/stdc.h> #include<iostream&g…

测试辅助工具(抓包工具)的使用2 之 抓包工具的基本用法

1.过滤设置: Filters- --- 勾选use Filters- --- 下拉选择show only the following hosts ---- 输入域名或者ip地址(多个地址用;隔开) --- 点击action(Run filterset now) 2.删除数据 方式一:点击Remove all 方式二: 黑窗口输入cls,回车 删除一条数据:选中数据---右键选择Rem…

C++ | Leetcode C++题解之第179题最大数

题目&#xff1a; 题解&#xff1a; class Solution { public:string largestNumber(vector<int> &nums) {sort(nums.begin(), nums.end(), [](const int &x, const int &y) {return to_string(x) to_string(y) > to_string(y) to_string(x);});if (nu…

基于riscv架构的DAYU800开发板套件介绍

一、简介 润和-SCDAYU800 开发平台基于平头哥高性能 RISC-V 开源架构曳影 TH1520 芯片&#xff0c;集成4核高性能RISC-V处理器玄铁C910的平头哥曳影1520&#xff0c;AI算力达4TOPs支持蓝牙、音频、视频和摄像头等功能,支持多种视频输入输出接口,并提供丰富的扩展接口&#xff…

Java中的标准注解与数据校验:深入解析与实例

目录 Java中的标准注解与数据校验&#xff1a;深入解析与实例1. 基础校验注解NonNullNotBlankSize 2. 数值校验注解Min & MaxPositive & Negative 3. 枚举与模式匹配自定义注解示例&#xff08;概念性展示&#xff09; 4. 自定义校验逻辑结论 Java中的标准注解与数据校…

Apple - Cocoa Event Handling Guide

本文翻译整理自&#xff1a;Cocoa Event Handling Guide&#xff08; https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/Introduction/Introduction.html#//apple_ref/doc/uid/10000060i 文章目录 一、导言本文件的组织另见 二、事件…

北京宠物美容护理app,化身奇迹“萌”宠

随着经济与互联网的飞速发展&#xff0c;宠物在短视频领域大放异彩&#xff0c;吸引着越来越多的人开始养宠物&#xff0c;为了让宠物更健康&#xff0c;更可爱的成长&#xff0c;宠物美容护理的需求也不断增加&#xff0c;为了帮助人们更加便捷、高效的进行美容、健康护理&…

HBase 中设计 RowKey

HBase 中设计 RowKey 在 HBase 中设计 RowKey 是非常重要的&#xff0c;它直接影响着数据的存储和检索效率。下面是一些设计 RowKey 的原则&#xff1a; 1. 唯一性&#xff08;Uniqueness&#xff09;&#xff1a;RowKey 必须是唯一的&#xff0c;因为在 HBase 中&#xff0c;数…

数据分类以及常见的数据格式

数据可以按照存储格式和用途进行归类&#xff0c;常见的分类方式包括数据库格式&#xff08;如SQL、NoSQL&#xff09;和数据文件格式&#xff08;如文本、图像、音频、视频等&#xff09;。 图片和视频的终端采集设备 数码相机&#xff08;包括单反、无反光镜和紧凑型相机&a…