网站建设的开票编码,设计网站设计,wordpress文章页不显示图片,中小企业网站建设 网络营销在对分层思想、时间片轮转和状态机思想进行[简单应用]
## 二、主函数 主函数如下#xff1a; 整个主函数的中心任务为功能选择切换任务#xff0c;负责切换显示内容#xff0c;控制ui变化等#xff0c;其余任务函数除提醒任务外都是通过全局变量的形式给功能选择切换任务提…在对分层思想、时间片轮转和状态机思想进行[简单应用]
## 二、主函数 主函数如下 整个主函数的中心任务为功能选择切换任务负责切换显示内容控制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_00) { LCD_RS0; LCD_RW0; LCD_DataPortCommand; LCD_EN1; LCD_DELAY TimeRef NUM_1MS; LCD_STATE_0 1; } if(LCD_STATE_0 1) { if(TimeRef LCD_DELAY) { LCD_EN0; 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_00) { LCD_RS1; LCD_RW0; LCD_DataPortData; LCD_EN1; LCD_DELAY TimeRef NUM_1MS; LCD_STATE_0 1; } if(LCD_STATE_0 1) { if(TimeRef LCD_DELAY) { LCD_EN0; 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_STATE2; } } //转换状态 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_Line1?FSD_Line1: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_Line2?FSD_Line2: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_Colu1:FSD_Colu--; }break; } }break; case 4:{ switch(ALM_STA) { case 0:{ ALM_STA 1; }break; case 1:{ FSD_Colu 5?FSD_Colu5: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_Line1?FSD_Line1: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_Line1?FSD_Line1: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_Line2?FSD_Line2: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_Line2?FSD_Line2: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_Colu1:FSD_Colu--; }break; case 2:{//倒计时中状态 FSD_Colu 5?FSD_Colu5: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_Colu6; SHOW_CTRl_N_STATE 7; }else { FSD_Colu; } }break; case 2:{//倒计时中状态 if(FSD_Colu 6) { FSD_Colu6; 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_i0)//限位 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_NUMSTWSetNum-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; //若后台有倒计时会对其有一定影响 TH10x3c;//50ms中断一次 TL10xAF; }break; case 3:{//翻看记录 STW_SPT_NUM_i; if(STW_SPT_NUM_iSTW_SPT_NUM)//限位 STW_SPT_NUM_iSTW_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_Colu0) { FSD_Colu--; } };break; case 4:{//右 if(FSD_Colu3) { 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_STATE9;break; case 2:Tim_12_24_Set 1;SHOW_CTRl_N_STATE9;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)*10ALM_TIME[0][1]-48) DS1302_Time[4]((ALM_TIME[0][2]-48)*10ALM_TIME[0][3]-48)) { led0; return; } } if(ALM_TIME[1][4]%2) { if(DS1302_Time[3]((ALM_TIME[1][0]-48)*10ALM_TIME[1][1]-48) DS1302_Time[4]((ALM_TIME[1][2]-48)*10ALM_TIME[1][3]-48)) { led0; return; } } led 1; } ## 七、倒计时任务 倒计时任务与闹钟任务类似也是比较后灯亮具体代码如下 c /**函数定义 * brief 倒计时任务 * param 无 * retval 无 */ idata u8 TMR_P_TIME0; idata u8 TMR_FLAG 0; void TMR_Task() { if(TMR_FLAG 1) { if(DS1302_Time[5]TMR_P_TIME) { led0; return; } } led 1; TMR_FLAG0; } ## 八、功能选择任务 功能选择任务为整个项目的核心。它控制着整个项目的状态机变换方向同时针对外部按键做出的状态赋值。完成相应的显示内容切换和状态转换。具体代码如下 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_Line1;//功能设置显示行 FSD_Colu1;//功能设置显示列 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%100; LCD_SHOW_2[6] TMR_SHOW_NUM[FSD_Line-1]%60/100; LCD_SHOW_2[7] TMR_SHOW_NUM[FSD_Line-1]%100; //判断倒计时是否开启 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_S0x01||TMR_S_S0x02)//第一次开启 { TH10x3c;//50ms 中断一次 TL10xAF; TR11; } }else { LCD_SHOW_2[9] O; LCD_SHOW_2[10] F; LCD_SHOW_2[11] F; if(TMR_S_S0)//最后一次关闭 { TR10; } } //倒计时是否重置显示内容 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/60000; LCD_SHOW_2[2] STW_SHOW_NUM/600%100; LCD_SHOW_2[4] STW_SHOW_NUM/100%100; LCD_SHOW_2[5] STW_SHOW_NUM/10%100; LCD_SHOW_2[7] STW_SHOW_NUM%100; 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; TR11; } 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_00) { LCD_RS0; LCD_RW0; LCD_DataPortCommand; LCD_EN1; LCD_DELAY TimeRef NUM_1MS; LCD_STATE_0 1; } if(LCD_STATE_0 1) { if(TimeRef LCD_DELAY) { LCD_EN0; 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_00) { LCD_RS1; LCD_RW0; LCD_DataPortData; LCD_EN1; LCD_DELAY TimeRef NUM_1MS; LCD_STATE_0 1; } if(LCD_STATE_0 1) { if(TimeRef LCD_DELAY) { LCD_EN0; 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_STATE2; } } //转换状态 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_Line1?FSD_Line1: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_Line2?FSD_Line2: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_Colu1:FSD_Colu--; }break; } }break; case 4:{ switch(ALM_STA) { case 0:{ ALM_STA 1; }break; case 1:{ FSD_Colu 5?FSD_Colu5: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_Line1?FSD_Line1: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_Line1?FSD_Line1: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_Line2?FSD_Line2: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_Line2?FSD_Line2: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_Colu1:FSD_Colu--; }break; case 2:{//倒计时中状态 FSD_Colu 5?FSD_Colu5: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_Colu6; SHOW_CTRl_N_STATE 7; }else { FSD_Colu; } }break; case 2:{//倒计时中状态 if(FSD_Colu 6) { FSD_Colu6; 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_i0)//限位 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_NUMSTWSetNum-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; //若后台有倒计时会对其有一定影响 TH10x3c;//50ms中断一次 TL10xAF; }break; case 3:{//翻看记录 STW_SPT_NUM_i; if(STW_SPT_NUM_iSTW_SPT_NUM)//限位 STW_SPT_NUM_iSTW_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_Colu0) { FSD_Colu--; } };break; case 4:{//右 if(FSD_Colu3) { 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_STATE9;break; case 2:Tim_12_24_Set 1;SHOW_CTRl_N_STATE9;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)*10ALM_TIME[0][1]-48) DS1302_Time[4]((ALM_TIME[0][2]-48)*10ALM_TIME[0][3]-48)) { led0; return; } } if(ALM_TIME[1][4]%2) { if(DS1302_Time[3]((ALM_TIME[1][0]-48)*10ALM_TIME[1][1]-48) DS1302_Time[4]((ALM_TIME[1][2]-48)*10ALM_TIME[1][3]-48)) { led0; return; } } led 1; } ## 七、倒计时任务 倒计时任务与闹钟任务类似也是比较后灯亮具体代码如下 c /**函数定义 * brief 倒计时任务 * param 无 * retval 无 */ idata u8 TMR_P_TIME0; idata u8 TMR_FLAG 0; void TMR_Task() { if(TMR_FLAG 1) { if(DS1302_Time[5]TMR_P_TIME) { led0; return; } } led 1; TMR_FLAG0; } ## 八、功能选择任务 功能选择任务为整个项目的核心。它控制着整个项目的状态机变换方向同时针对外部按键做出的状态赋值。完成相应的显示内容切换和状态转换。具体代码如下 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_Line1;//功能设置显示行 FSD_Colu1;//功能设置显示列 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%100; LCD_SHOW_2[6] TMR_SHOW_NUM[FSD_Line-1]%60/100; LCD_SHOW_2[7] TMR_SHOW_NUM[FSD_Line-1]%100; //判断倒计时是否开启 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_S0x01||TMR_S_S0x02)//第一次开启 { TH10x3c;//50ms 中断一次 TL10xAF; TR11; } }else { LCD_SHOW_2[9] O; LCD_SHOW_2[10] F; LCD_SHOW_2[11] F; if(TMR_S_S0)//最后一次关闭 { TR10; } } //倒计时是否重置显示内容 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/60000; LCD_SHOW_2[2] STW_SHOW_NUM/600%100; LCD_SHOW_2[4] STW_SHOW_NUM/100%100; LCD_SHOW_2[5] STW_SHOW_NUM/10%100; LCD_SHOW_2[7] STW_SHOW_NUM%100; 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; TR11; } 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