前段時間看到了分層思想、時間片輪轉和狀態機的相關文章正好近期手上有一些小項目,于是就進行了一些嘗試。相關的概念就不在細說了,很多嵌入式的公眾號文章都有介紹過,下面只敘述下我開發的經歷幫助后來者少走彎路也是我個人的一個記錄。
我在看到講述相關思想的文章后在腦子里想著架構出一個相對比較完善的系統。
分層思想不用多說根據相應的軟硬件結構進行分層即可,其中的好處會在代碼復用時體現出來。
時間片輪轉思想在我個人實踐的過程中走了很多彎路,本人使用的硬件為清翔電子的最小系統板,在最開始進行開發時使用定時器的方式對固定延時變量(REF20MS等)進行相應的環形累加(if(REF20MS==0)REF20MS=NUM20MS)(NUM20MS為宏定義的定時器計數記錄到對應時間所需要的值)。然后當相應的變量計數完成后對相應的標志位進行計數。
在我最初的構想中,希望能夠完成一個多個需要相同的延時時間的任務對同一個標記位的檢測即可完成延時的效果,但是在實際實現過程中遇到了很多問題。
1、如何判定任務1完成了20ms延時(僅作為舉例),判定相應的延時時間的話需要對REF20MS的值進行記錄當該變量再一次到達記錄值時則說明完成20ms延時,這有一個問題就是定時器中斷的時間一定時比單片機主函數中運行的時間要慢的于是當該變量進行記錄時定時器未能及時對延時記錄變量進行增減從而導致沒有延時的現象,當然這種問題可以通過再增加一個記錄次數的標記位來解決但是還有更好的解決方式我下面會講。
2、如何多個任務同時標記并使用同一個延時變量,這個問題使用變量記錄加標記位的方式可以去解決但是結構復雜較為繁瑣。
在遇到上述問題后我改變了想法,使用一個u32位(u64更好但是51不支持u64)的變量(u32 TimeRef)作為統一的一個時間片(定時器125us中斷一次,足夠使用一周左右),對每個任務定義一個_DELAY變量(如按鍵:u32 KEY_DELAY) 在每次需要延時時KEY_DELAY=TimeRef+NUM_20MS;(NUM20MS為宏定義的定時器計數記錄到對應時間所需要的值),在使用狀態機的思想進行狀態轉移即可。能夠較為簡單的時間一些任務輪轉的功能,整體代碼理解起來也比較簡單。整體單片機代碼如下:
- #include<reg52.h>
- #include<intrins.h>
- //嘗試分層思想與時間片輪轉
- #define u8 unsigned char
- #define u16 unsigned int
- #define u32 unsigned long int
- //按鍵引腳映射
- sbit KEY1=P3^5;
- sbit KEY2=P3^4;
- sbit KEY3=P3^3;
- sbit KEY4=P3^2;
- sbit LED1=P1^0;
- u8 KEY_DAT=0;//按鍵寄存器按鍵為1存儲當前被按下的按鍵鍵值,0為無按鍵
- u8 SEG7[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
- //時間片為125us
- #define NUM_1MS 8
- #define NUM_2MS 16
- #define NUM_5MS 40
- #define NUM_20MS 160
- #define NUM_100MS 800
- #define NUM_200MS 1600
- #define NUM_500MS 4000
- #define NUM_1000MS 8000
- //時間片輪轉變量
- u32 TimeRef=0;
- //任務延時變量
- //按鍵任務
- u32 KEY_DELAY =0;
- u8 KEY_STATE =0;
- u8 KEY_DATx =0;
- //顯示任務
- u32 SEG_DELAY =0;
- u8 SEG_STATE =0;//0位標記是否第一次進入
- u8 SEG_NUM[4] ={0,0,0,0};
- u8 SEG_I =0;
- /////////////////////////函數聲明/////////////////////////////////
- //讀按鍵鍵值
- u8 ReadKeyDat(void);
- //讀按鍵寄存器
- u8 ReadKeyReg(void);
- //中斷初始化函數
- void IsrInit(void);
- //數碼管顯示控制函數
- void SegDisplay(u16 num);
- void main(void)
- {
- u16 num=0;
- u8 key=0;
- IsrInit();
- while(1)
- {
- SegDisplay(num);
- key = ReadKeyDat();
- if(key==1)
- num++;
- if(key==2)
- num--;
- // if(key==3)
- // LED1=0;
- // if(key==4)
- // LED1=1 ;
- }
-
- }
- //數碼管顯示控制函數
- void SegDisplay(u16 num)
- {
- u8 i=0;
- if(SEG_STATE==0)//分割字符狀態
- {
- SEG_I=0;
- for(i=0;i<4;i++)
- {
- SEG_NUM[SEG_I++]=num%10;
- num=num/10;
- }
- SEG_STATE=1;
-
- }
-
- if(SEG_STATE==1)//顯示狀態
- {
- P0=SEG7[SEG_NUM[4-SEG_I]];//[];
- P2=~(0x01<<(SEG_I-1));
- SEG_I--;
- SEG_DELAY=TimeRef+NUM_5MS;
- SEG_STATE=2;
- }
- if(SEG_STATE==2)//延時等待狀態
- {
- if(TimeRef >= SEG_DELAY)
- {
- //判斷是否為最后一次延時
- if(SEG_I==0)
- SEG_STATE=0;
- else
- SEG_STATE=1;
- }
- }
-
- }
- //讀按鍵鍵值
- u8 ReadKeyDat(void)
- {
- ReadKeyReg();
- if(KEY_STATE == 0)//第一次讀取狀態
- {
- if(KEY_DAT!=0)//讀取到按鍵
- {
- KEY_DATx=KEY_DAT;
- KEY_DELAY=TimeRef+NUM_100MS;
- KEY_STATE=1;
- }
- }
-
- if(KEY_STATE == 1)//延時狀態
- {
- if(TimeRef >= KEY_DELAY)
- {
- KEY_STATE=2;
- }
- }
- if(KEY_STATE == 2)//第二次讀取狀態
- {
- if(KEY_DATx == KEY_DAT)//讀取到按鍵
- {
- KEY_STATE=0;
- return KEY_DATx;
- }
- KEY_STATE=0;
- }
- return 0;
- }
- //讀按鍵寄存器
- u8 ReadKeyReg(void)
- {
- if(KEY1==0)
- {
- KEY_DAT=1;
- return 1;
- }
- if(KEY2==0)
- {
- KEY_DAT=2;
- return 1;
- }
- if(KEY3==0)
- {
- KEY_DAT=3;
- return 1;
- }
- if(KEY4==0)
- {
- KEY_DAT=4;
- return 1;
- }
- KEY_DAT=0;
- return 0;
- }
- //中斷初始化函數
- void IsrInit(void)
- {
- EA=1;
-
- //使能定時器中斷并開啟定時器
- // ET0=1;
- // TR0=1;
- //ET1=1;
- //使能兩個外部中斷并將其設定為跳沿觸發
- // EX0=1;
- // IT0=1;
- // EX1=1;
- // IT1=1;
- //定時器0,方式2,計數器
- TMOD|=0x02;
- TH0=0x8d;//125us 中斷一次
- TL0=0x8d;
- ET0=1;
- TR0=1;
- // //使用定時器1,方式2,計數器
- // TMOD|=0x20;
- // TH1=0x8d;//125us 中斷一次
- // TL1=0x8d;
- }
- //定時器0,提供基礎時鐘125us中斷1次
- //////////////////////////統一時間完成標志位可能沒有意義//////////////////////////////////////
- int Tim0Isr() interrupt 1 using 1
- {
- TimeRef++;
- // P0=SEG7[TimeRef/100%10];//[];
- // P2=0xfe;
- }
復制代碼 Keil代碼下載:
1、按鍵檢測控制數碼管顯示.7z
(16.25 KB, 下載次數: 119)
2021-11-17 18:07 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|