## 一、概述
在對分層思想、時間片輪轉和狀態機思想進行[簡單應用](http://www.zg4o1577.cn/bbs/dpj-214415-1.html)后,我想對其進行更進一步的應用——使用這三種思想對我本人在2020年的一個[51電子鐘項目](http://www.zg4o1577.cn/bbs/dpj-215488-1.html)進行重構使其運行效率更高更好的利用單片機的各種資源。
仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
51hei.gif (86.89 KB, 下載次數: 50)
下載附件
2022-9-26 01:38 上傳
## 二、主函數
主函數如下:
整個主函數的中心任務為功能選擇切換任務,負責切換顯示內容,控制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;
}
}
```
該線程所需的驅動并未實現完全的狀態機化由于單總線所需的延時較為精準在嘗試了進行狀態機化后發現會出現讀取亂碼的情況,且部分函數所需的延時時間太短甚至需要關閉定時器中斷以對該延時進行保護,可對標操作系統的臨界區保護。
## 五、按鍵獲取任務
按鍵獲取任務基本架構、分層與[按鍵控制數碼管項目](http://www.zg4o1577.cn/bbs/dpj-214415-1.html)類似,但由于本項目的功能較多且針對于項目的顯示內容使用了現態的方式進行控制,即按鍵任務無法得知完成顯示任務前的狀態(功能),故引入過去態和未來態,前者供需要在現態中進行狀態轉換的任務標記其源頭,后者用于在使用完相應的資源后跳轉到相應的任務上。故按鍵對于當前功能界面的判斷使用了過去態的方式,同時配合行號和列號完成相應功能。具體代碼如下:
```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選擇,使用按鍵選擇和設置相應的功能。本項目的代碼對于狀態機思想進行了比較深入的運用希望各位能夠在其中有所收獲。同時也歡迎大家留言討論共同進步。
Keil代碼與Proteus8.8仿真下載:
仿真程序.7z
(124.09 KB, 下載次數: 101)
2022-9-26 01:39 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|