基于状态机与时间片轮的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;
                               

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

石更单片机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值