久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 6074|回復: 42
打印 上一主題 下一主題
收起左側

關于延時機制的狀態機討論

  [復制鏈接]
跳轉到指定樓層
樓主
ID:326261 發表于 2023-5-22 09:24 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
這個帖子對于我這個剛入門的軟件開發遇到的瓶頸,希望各位大牛幫忙,謝謝。
也望此貼下大家踴躍討論,給出不同意見,為各位新人解答疑惑。

--------------------------------------分割線---------------------------------------------------------------------------------
目前遇到的問題:
-1、在做LCD1602顯示的時候,因為LCD1602需要做忙檢測延時,硬件又沒有預留讀IO的口,導致需要做延時才能應付LCD1602忙檢測,延時1ms。

-2、系統時間要求較高,不能用原地延時,則需要做狀態機。

-3、但是這個延時的狀態機需要怎么做才能每次按鍵進來,顯示字符,發送一個數據/指令,下次還能再進來,重新回到同一個地方?

我假認的處理方式(未編程確認)是:
①、每次進來發送一次數據/指令設置為一個狀態,這樣下次進這個狀態檢測既可,但是這樣的狀態數太多了,幾乎重新寫了整個程序。

②、字符都放置到數組里面,每次進來讀取數組,每次發送一組指令/數據進入下一個狀態,結構體附帶數據個數,相比①,這樣就縮小了每次發一組顯示數據的狀態數,這樣寫要考慮數據放的順序,和狀態數也是不少的。

③、我假想,設置一個臨時狀態,進來后將當前狀態給到臨時狀態,當前狀態+1,判斷是否相等,狀態機到了之后,將當前狀態給到臨時狀態,這樣,相等就運行,不相等就進入延時,好像幾乎第①相等,具體思路還沒捋順

......
4、而且我這個是多級菜單的顯示,每個顯示都要做狀態機也太多了

----------------到此,希望各位給點思路,延時狀態機該如何寫,代碼重構要怎么去實施,在此先謝謝大家-------------------------------------
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:94031 發表于 2023-5-22 11:18 | 只看該作者
延時用中斷,要延時時打開延時中斷并設標志,繼續干別的事,延時到改延時標記,這樣延時不影響干別的事。
回復

使用道具 舉報

板凳
ID:1065084 發表于 2023-5-22 11:29 | 只看該作者
狀態機嘛,就是狀態嘛,為什么不能有子狀態呢?
你這個提問的最大問題是沒有寫清楚系統功能和需求,根據系統功能和需求來切分狀態。
我假設 你是 1602顯示,按鍵控制,18B20測溫,外部通信這幾個功能。

顯示改變的觸發條件是  溫度變化,按鍵動作,通信動作

18b20的觸發條件就是比如定時器1s測溫一次 ,測溫后讀出數據

按鍵的觸發條件就是 20ms讀一次按鍵狀態

外部通信就是比如串口接受數據,接受一定量數據觸發數據處理

上面這些就是狀態機中的狀態,所謂的狀態就是一個用于判斷的數字 ,譬如按鍵  就是為不同按鍵狀態設置不同鍵值,然后根據鍵值和當前菜單狀態判斷下一步操作。
所以狀態機不是一個變量用起來沒完,而是多個變量代表不同意義 ,比如鍵值為0說明按鍵未動,那顯示函數中根據鍵值判斷當前菜單不操作。比如18b20溫度測量后比較上一次值確定“溫度變化”狀態是否刷新,刷新后顯示函數中的溫度才會變化。
如果系統對低功耗沒有要求,那系統大部分時間都是在判斷這個狀態進不去,那個狀態也進不去的“空轉”,大體分開來講狀態機的變化條件就是 “時間”和“事件”兩種,你所謂的“延時”代碼在系統內可以分為兩種 :
1.不精確的延時,采用“時間片”的方法,譬如你最小延時為1ms,可以定時器1ms觸發一次,比如按鍵就20次觸發一次按鍵掃描。譬如你顯示函數中寫完數據要等待1ms反應,因為你長于1ms也無所謂,所以你直接將寫完數據設置為一個狀態1,下一次可以寫數據設置為一個狀態2,狀態1的觸發條件是寫數據動作,狀態2的觸發條件是狀態1下定時器觸發了一次。
2.精確的延時,一般是先處理完等待的任務后,關閉中斷,使用cpu精確延時,注意這時要考慮阻塞情況,會比較麻煩。

狀態機編程是將你所有的程序打亂根據時間和事件進行重排。系統內正常就沒有延時函數了,所有需要延時的地方都要重新判斷和排列,當然你也可以編寫一個“假延時”函數用于暫時釋放系統資源,但是這個有軟件重入的問題存在,這時上操作系統相對會更好一些。當然對于你這特別簡單的項目,狀態機還是明顯優于操作系統的,操作系統有cpu 內存和固件開銷,絕大部分項目不需要的。
回復

使用道具 舉報

地板
ID:161164 發表于 2023-5-22 11:50 | 只看該作者
1. 除了清屏,其它寫命令/數據延時只需40us
2. 建立一個顯存數組(或兩個,一行一個), 有數據更新,只更新顯存,然后更新標志位置位(Flag_
3. 在主循環中根據更新標志位把顯存數組寫進LCD,每次循環只寫一個字
  1. u8 DispBuff[32];
  2. bit Flag_Row1_Update = 0;
  3. bit Flag_Row2_Update = 0;
  4. void LCD_Update()
  5. {
  6.         static u8 i = 0;
  7.         if(Flag_Row1_Update)
  8.         {
  9.                 if(i==0)LCD_WriteCMD(0x80);
  10.                 LCD_WriteData(DispBuff[i]);
  11.                 if(++i>=16)
  12.                 {
  13.                         i=0;
  14.                         Flag_Row1_Update = 0;
  15.                 }
  16.         }else if(Flag_Row2_Update)
  17.         {
  18.                 if(i==0)LCD_WriteCMD(0xC0);
  19.                 LCD_WriteData(DispBuff[i+16]);
  20.                 if(++i>=16)
  21.                 {
  22.                         i=0;
  23.                         Flag_Row2_Update = 0;
  24.                 }
  25.         }
  26. }
復制代碼
回復

使用道具 舉報

5#
ID:326261 發表于 2023-5-22 13:19 | 只看該作者
xuyaqi 發表于 2023-5-22 11:18
延時用中斷,要延時時打開延時中斷并設標志,繼續干別的事,延時到改延時標記,這樣延時不影響干別的事。

是的,分出一個定時器來作延時中斷,一般延時使用,用一個計數寄存器器來判斷延時時間。
回復

使用道具 舉報

6#
ID:526543 發表于 2023-5-22 13:21 | 只看該作者
針對你提到的問題,我可以給你一些建議和思路來處理延時狀態機的編寫和代碼重構。  首先,關于問題1中LCD1602的忙檢測延時,如果你的硬件沒有預留讀取IO的口,可以考慮使用軟件延時。你可以通過循環執行一段時間來模擬延時,例如使用一個延時函數,該函數通過循環指定次數來達到延時的效果。你可以根據你的具體需求來調整循環的次數,以達到所需的延時時間。  關于問題2中系統時間要求較高的情況,可以考慮使用狀態機來處理。狀態機是一種用于管理系統狀態的方法,它可以幫助你在不使用原地延時的情況下實現所需的功能。你可以定義不同的狀態,并在每個狀態中執行相應的操作。在狀態機中,你可以使用條件語句(如switch-case語句)來根據當前狀態執行相應的操作或者切換到下一個狀態。  對于問題3,你可以考慮使用一個臨時狀態來幫助處理延時狀態機。當進入一個狀態時,將當前狀態保存到臨時狀態中,并將當前狀態遞增。然后,判斷當前狀態是否等于臨時狀態,如果相等,則執行相應的操作,否則進行延時。這樣可以確保每次進入狀態機時都會檢測到是否需要延時。  對于多級菜單的顯示,可以考慮使用層級結構來管理狀態機。你可以定義不同的狀態和菜單層級,并在每個狀態中執行相應的操作或者切換到下一個狀態。這樣可以避免在每個顯示中都編寫獨立的狀態機,而是通過管理菜單層級來實現整體的狀態控制。  在進行代碼重構時,你可以考慮將字符放置到數組中,使用循環來處理數組中的數據。這樣可以簡化代碼,并減少狀態的數量。你可以定義一個索引變量來追蹤當前處理的數組元素,每次處理完一個元素后,遞增索引,然后根據索引獲取下一個要處理的元素。  最后,為了更好地進行代碼重構,建議使用函數和模塊化編程的方式。將不同的功能模塊分解成獨立的函數,并通過函數的調用來實現狀態的切換和處理。這樣可以提高代碼的可讀性和可維護性,同時也便于進行功能擴展和修改。  希望以上的建議對你有所幫助,
回復

使用道具 舉報

7#
ID:326261 發表于 2023-5-22 13:24 | 只看該作者
yuxuesuixing 發表于 2023-5-22 11:29
狀態機嘛,就是狀態嘛,為什么不能有子狀態呢?
你這個提問的最大問題是沒有寫清楚系統功能和需求,根據系 ...

嗯對,狀態機即為判斷當前狀態來進入對應功能,代碼需要重構的。
現在有疑問的地方就是,每發一個字節就需要延時,LCD顯示一次需要連續發34個字節,所以狀態數比較多。
并且此程序是作多級菜單的,就是發不同顯示,那狀態機的狀態數更多了,狀態機的代碼都要超過源程序代碼量了,感覺有點矛盾。
回復

使用道具 舉報

8#
ID:326261 發表于 2023-5-22 14:05 | 只看該作者
lkc8210 發表于 2023-5-22 11:50
1. 除了清屏,其它寫命令/數據延時只需40us
2. 建立一個顯存數組(或兩個,一行一個), 有數據更新,只更新 ...

請問指令和數據一個字節所需要的時間是怎么得到40us的嗎?是否有文檔確認?
圖中是時序圖,你是從時序圖推出指令/數據時間的嗎?

你的方案是比較接近實際的,加上延時判斷最好了。
我目前做的也是和你這個方式類似,所有數據放進一個數據包里,每次更新數據,則更新數據包狀態機,如:LCD1602兩行數據,加指令,需要38個狀態機狀態,每次更新數據,就寫一遍狀態機數據。


這個應該是比較好的一個方法了

屏幕截圖 2023-05-22 135815.png (121.77 KB, 下載次數: 97)

屏幕截圖 2023-05-22 135815.png
回復

使用道具 舉報

9#
ID:161164 發表于 2023-5-22 14:12 | 只看該作者
工學院陳偉霆 發表于 2023-5-22 14:05
請問指令和數據一個字節所需要的時間是怎么得到40us的嗎?是否有文檔確認?
圖中是時序圖,你是從時序圖 ...


回復

使用道具 舉報

10#
ID:326261 發表于 2023-5-22 14:32 | 只看該作者

對的,感謝,是我看漏了,抱歉
回復

使用道具 舉報

11#
ID:326261 發表于 2023-5-22 14:34 | 只看該作者
123456ZXC1 發表于 2023-5-22 13:21
針對你提到的問題,我可以給你一些建議和思路來處理延時狀態機的編寫和代碼重構。  首先,關于問題1中LCD16 ...

受益匪淺,感謝!
回復

使用道具 舉報

12#
ID:94031 發表于 2023-5-22 14:41 | 只看該作者
按你的描述,就是想解決LCD1602顯示與及時響應按鍵的沖突,不需要搞得那么復雜,處理LCD1602顯示時間很短,完全可以處理完LCD1602顯示后判斷按鍵處理按鍵,這有什么問題嗎?
回復

使用道具 舉報

13#
ID:326261 發表于 2023-5-22 14:51 | 只看該作者
xuyaqi 發表于 2023-5-22 14:41
按你的描述,就是想解決LCD1602顯示與及時響應按鍵的沖突,不需要搞得那么復雜,處理LCD1602顯示時間很短, ...

我的問題是
因為LCD忙檢測需要延時,但不能影響程序響應,即不能原地延時。

程序中遇到的問題是代碼重構,上面的解決方法應該是需要狀態機了,狀態機的編寫遇到了困惑,我的顯示太多了,每發一個字節進入一次狀態機狀態,那么一路下來狀態機太龐大了。
回復

使用道具 舉報

14#
ID:94031 發表于 2023-5-22 15:05 | 只看該作者
原地延時影響什么功能了。
回復

使用道具 舉報

15#
ID:326261 發表于 2023-5-22 15:10 | 只看該作者
xuyaqi 發表于 2023-5-22 15:05
原地延時影響什么功能了。

影響了另一部分程序的數據收發和IO判斷響應,總的說,ms級的原地延時還是盡量避免,并且LCD1602,一次發16個字節,就有16ms以上的固定時間了。
回復

使用道具 舉報

16#
ID:94031 發表于 2023-5-22 15:31 | 只看該作者
工學院陳偉霆 發表于 2023-5-22 15:10
影響了另一部分程序的數據收發和IO判斷響應,總的說,ms級的原地延時還是盡量避免,并且LCD1602,一次發1 ...

那你數據收發和IO判斷期間不刷新LCD1602可以嗎?
回復

使用道具 舉報

17#
ID:326261 發表于 2023-5-22 15:39 | 只看該作者
xuyaqi 發表于 2023-5-22 15:31
那你數據收發和IO判斷期間不刷新LCD1602可以嗎?

我的按鍵控制LCD1602刷新,在LCD刷新時不能影響IO口的接收判斷。

按照您的說法,設兩個狀態位,那么代碼需要考慮的地方應該會很多。

關于延時機制,我還是不建議經常使用原地循環做延時,除非時間非常短。
回復

使用道具 舉報

18#
ID:384109 發表于 2023-5-22 15:48 | 只看該作者
感覺還是你的整體結構和構思的問題,雖然一直在強度LCD寫數據延時等待不好,而硬件電路又沒有設計查忙功能,那么即使硬件電路設計了查忙功能,那么查忙的最大延時一樣還是1.6毫秒,那這種情況你又如何處理,又如何設置所謂的狀態機
回復

使用道具 舉報

19#
ID:94031 發表于 2023-5-22 16:02 | 只看該作者
工學院陳偉霆 發表于 2023-5-22 15:39
我的按鍵控制LCD1602刷新,在LCD刷新時不能影響IO口的接收判斷。

按照您的說法,設兩個狀態位,那么代 ...

16ms間隔不會影響IO口的接收判斷,完全可以刷新完LCD再判斷IO口,當然實時性要求高的系統要用狀態機,但我覺得你這個應用不需要。你也可以每20ms來個定時中斷,判斷一下有無IO變化,有,處理IO,沒有,刷新LCD。
回復

使用道具 舉報

20#
ID:353115 發表于 2023-5-22 16:14 | 只看該作者
按鍵要改變當前頁面,只需要提交數據到數組,頁面的刷新定時執行即可,這樣不會造成按鍵快速按下導致頁面刷新堵塞。
回復

使用道具 舉報

21#
ID:996773 發表于 2023-5-22 16:18 | 只看該作者


樓主的編程思路可能有問題,狀態機我也不懂,我也寫過類似的程序,我的思路是這樣,1602不要查忙,

不要查忙,不要查忙,重點說三遍。一開機1602初始化,把固定不變的顯示字符都顯示,然后就是主程

序不停的查按鍵,一旦按鍵觸發,再去執行模數轉換和處理,在去把1602變化的顯示單元顯示,這種程

序夠簡單,定時器和中斷都不需要夠運行了,不要以為用了定時器和中斷就是高手,真正高手是用更少的資

源干定時器中斷同樣能做到的事
回復

使用道具 舉報

22#
ID:326261 發表于 2023-5-22 16:52 | 只看該作者
hi等你 發表于 2023-5-22 16:18
樓主的編程思路可能有問題,狀態機我也不懂,我也寫過類似的程序,我的思路是這樣,1602不要查忙,

...

可能主MCU不同,你的程序不需要查忙,具體不細究

我的主MCU是stm32f4,主頻180M,IO時鐘45M/90M,外加spi控制595轉并驅動DB0-DB7去顯示。

出現的問題是,SPI發送速度足夠快,第一個字節發送完,顯示緩存還沒穩定,已經開始發第二個字節了,導致顯示出現亂碼。

為什么查忙?LCD數據手冊你是否看過?時序邏輯圖、指令時間,你是否掌握?
作為一個開發者,不僅僅是開發,還要保障系統的極限和穩定,不浪費資源的同時,達到手冊的最佳要求,且不會受干擾影響程序運行。

不是你說的用了定時器和中斷就是高手,我也在找解決辦法,是程序實時性比較高,不能兼容原地等待,如果你有更好的方法,能夠保證不用原地等待,也能保證LCD顯示正確,我洗耳恭聽
回復

使用道具 舉報

23#
ID:326261 發表于 2023-5-22 16:57 | 只看該作者
qsssuv 發表于 2023-5-22 16:14
按鍵要改變當前頁面,只需要提交數據到數組,頁面的刷新定時執行即可,這樣不會造成按鍵快速按下導致頁面刷 ...

方法是可行的,定時刷新我覺得沒有必要,既然LCD有顯存,發送一次即可,多次發送會占用系統資源。
現在的問題不是按鍵按的快,是顯示的數據發送太快,需要在每個發送字節加上一個延時,來替代忙檢測。

可以在按鍵按下的時候刷新數據,但是發送數據延時也要加上的,所以狀態機也是要的。
回復

使用道具 舉報

24#
ID:275826 發表于 2023-5-22 18:05 | 只看該作者
學習了一下,編程最簡單能實現樓主要求的方法就是用非阻塞延時函數, 也就是通過循環執行一段時間來模擬延時,該函數通過循環指定次數來達到延時的效果。你可以根據你的具體需求來調整循環的次數,以達到所需的延時時間。
回復

使用道具 舉報

25#
ID:213173 發表于 2023-5-23 10:20 | 只看該作者
LCD1602、DS18B20、按鍵消抖、長短按識別等都是對時序有一定要求的外部硬件。用常見的阻滯型延時函數寫代碼,單獨使用一般都沒有什么問題,多個外部硬件組合在一起就會起沖突。樓主這個困局其實不難處理,也不一定非得用狀態機、時間片。就用一個定時器,主函數按合適的最小周期輪詢所有子函數模塊,反正單片機也累不死。以下示例改編于一款溫度上下限報警程序+仿真,其中的數碼管是為了代替可能增加其它硬件刻意塞進去攪局、P0分時復用增加難度的。此代碼有可能對樓主有所幫助。其中變量、注釋未必恰當,亦或錯漏,僅供參考,請高手繞道勿噴。

  1. #include <AT89X52.h>
  2. #define uint unsigned int
  3. #define uchar unsigned char           //宏定義
  4. #define key_S 10                                //定義短按(約20ms)
  5. #define key_L key_S*50                        //定義長按(約500ms)
  6. #define key_I key_S*38                        //定義長按連+/-間隔(約120ms)

  7. sbit LCD_RS =P2^4;                                //液晶屏RS口  寫指令低,寫數據高
  8. sbit LCD_EN =P2^5;                                //液晶屏EN口  高脈沖使能寫指令/數據,低跳變執行
  9. sbit limit_H=P2^6;                        //定義上限燈光報警
  10. sbit limit_L=P2^7;                        //定義下限燈光報警
  11. sbit SET  =  P3^3;                            //定義調整鍵
  12. sbit DEC  =  P3^4;                            //定義減少鍵
  13. sbit ADD  =  P3^5;                            //定義增加鍵
  14. sbit BEEP =  P3^6;                            //定義蜂鳴器
  15. sbit DQ   =  P3^7;               //定義DS18B20總線I/O        
  16.                              
  17. uchar code LEDData[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x89,0x83,0x86,0xA1,0x86,0x8E};
  18. uchar data dis_buf[4];//數碼管顯示緩存
  19. uchar data dis_buf1[]={'T','e','m','p',':','0','0','0','.','0',0xdf,'C'};
  20. int Ceiling=38;                  //上限報警溫度,默認值為38
  21. int Bottom=5;                   //下限報警溫度,默認值為5
  22. int temp,tempM;                //溫度變量
  23. uchar tempH,tempL;        //高4位以上整數,低4位小數
  24. bit negative;                        //負號標志
  25. uchar KeySec=0;                //鍵值

  26. /*****延時子程序*****/
  27. void Delay(uint num)//T=num*8+6us
  28. {
  29.         while(--num);
  30. }

  31. /*******液晶寫指令程序********/
  32. void write_com(uchar com)
  33. {
  34.         LCD_RS=0;        //允許寫指令
  35.         P0=com;                //傳遞指令
  36.         Delay(120);        //延時966us
  37.         LCD_EN=1;        //使能寫入
  38.         Delay(120);        //延時966us
  39.         LCD_EN=0;        //低跳變執行
  40.         
  41. }
  42. /*******液晶寫數據程序********/
  43. void write_date(uchar date)
  44. {
  45.         LCD_RS=1;        //允許寫數據
  46.         P0 =date;        //傳遞數據
  47.         Delay(120);        //延時966us
  48.         LCD_EN=1;        //使能寫入
  49.         Delay(120);        //延時966us
  50.         LCD_EN=0;        //低跳變執行        
  51. }
  52. /*******液晶初始化程序********/
  53. void LCDinit()                //設置顯示模式、
  54. {
  55.         LCD_EN=0;        //初始設置LCD_EN低電平
  56.         write_com(0x38);//設置16*2顯示,5*7點陣,8位數據接口
  57.         write_com(0x01);//顯示清零,數據指針清零
  58.         write_com(0x0c);//設置開顯示,不顯光標
  59.         write_com(0x06);//設置寫一個字符后地址指針加1
  60. }
  61. /*****初始化DS18B20*****/
  62. void Init_DS18B20(void)
  63. {
  64.   uchar x=0;
  65.   DQ = 1;         //DQ復位
  66.   Delay(2);    //稍做延時22us
  67.   DQ = 0;         //單片機將DQ拉低
  68.   Delay(60);   //精確延時大于480us//486us
  69.   DQ = 1;         //拉高總線
  70.   Delay(12);        //102us
  71.   x = DQ;           //稍做延時后,如果x=0則初始化成功,x=1則初始化失敗
  72.   Delay(6);                //54us
  73. }
  74. /*****讀一個字節*****/
  75. uchar ReadOneChar(void)
  76. {
  77.         uchar i=0;
  78.         uchar dat = 0;
  79.         for(i=8;i>0;i--)
  80.         {
  81.                 DQ = 0;     // 給脈沖信號
  82.                 dat>>=1;
  83.                 DQ = 1;     // 給脈沖信號
  84.                 if(DQ)
  85.                 dat|=0x80;
  86.                 Delay(6);        //54us
  87.         }
  88.         return(dat);
  89. }
  90. /*****寫一個字節*****/
  91. void WriteOneChar(uchar dat)
  92. {
  93.         uchar i=0;
  94.         for (i=8; i>0; i--)
  95.         {
  96.                 DQ = 0;
  97.                 DQ = dat&0x01;
  98.                 Delay(6);        //54us
  99.                 DQ = 1;
  100.                 dat>>=1;
  101.         }
  102.         Delay(6);                //54us
  103. }

  104. /*****溫度值轉換*****/
  105. void ReadTemperature()
  106. {
  107.         static uchar i=0;
  108.         uchar a=0,b=0;
  109.         switch(i)
  110.         {
  111.                 case 0:
  112.                         Init_DS18B20();
  113.                         WriteOneChar(0xCC);//跳過讀序號列號的操作
  114.                         WriteOneChar(0x44);//啟動溫度轉換
  115.                 break;
  116.                 case 3:
  117.                         Init_DS18B20();
  118.                         WriteOneChar(0xCC);//跳過讀序號列號的操作
  119.                         WriteOneChar(0xBE);//讀取溫度寄存器
  120.                 break;
  121.                 case 5:
  122.                         a=ReadOneChar();    //讀低8位
  123.                         b=ReadOneChar();    //讀高8位
  124.                         temp=b<<8|a; //高8位+低8位轉移到t
  125.                 break;
  126.                 case 6:
  127.                         tempM=temp;
  128.                         if(temp&0x8000)
  129.                         {
  130.                                 negative=1;                        //負號標志
  131.                                 temp=~temp+1;                //取反加1
  132.                         }
  133.                         else negative=0;                //正數                                
  134.                         tempH=temp>>4;                   //分解出整數
  135.                         tempL=temp&0x0F;           //分解出小數
  136.                         tempL=tempL*0.625;        //保留1位小數
  137.         }
  138.         i=++i%7;
  139. }

  140. void disassemble()
  141. {
  142.         if(KeySec==0)//顯示初始狀態'----'
  143.         {
  144.                 dis_buf[0]=0xbf;
  145.                 dis_buf[1]=0xbf;
  146.                 dis_buf[2]=0xbf;
  147.                 dis_buf[3]=0xbf;
  148.                
  149.                 dis_buf1[5]='-';
  150.                 dis_buf1[6]='-';
  151.                 dis_buf1[7]='-';
  152.                 dis_buf1[9]='-';
  153.         }
  154.         else if(KeySec==1)//顯示溫度
  155.         {
  156.                 if(negative==1)
  157.                 {
  158.                         if(tempH>=10)
  159.                         {
  160.                                 dis_buf[0]=0xbf;//顯示負號
  161.                                 dis_buf[1]=LEDData[tempH/10%10];
  162.                                 dis_buf1[5]='-';
  163.                                 dis_buf1[6]=tempH/10%10+'0';
  164.                         }
  165.                         else if(tempH<10)//小于100
  166.                         {
  167.                                 dis_buf[0]=0xff;//不顯示               
  168.                                 dis_buf[1]=0xbf;//顯示負號
  169.                                 dis_buf1[5]=' ';
  170.                                 dis_buf1[6]='-';
  171.                         }
  172.                 }
  173.                 else
  174.                 {
  175.                         if(tempH>=100)
  176.                         {
  177.                                 dis_buf[0]=LEDData[tempH/100];
  178.                                 dis_buf1[5]=tempH/100+'0';
  179.                         }
  180.                         else
  181.                         {
  182.                                 dis_buf[0]=0xff;//不顯示
  183.                                 dis_buf1[5]=' ';
  184.                         }
  185.                         if(tempH>=10)
  186.                         {
  187.                                 dis_buf[1]=LEDData[tempH/10%10];
  188.                                 dis_buf1[6]=tempH/10%10+'0';
  189.                         }
  190.                         else
  191.                         {
  192.                                 dis_buf[1]=0xff;//不顯示
  193.                                 dis_buf1[6]=' ';
  194.                         }
  195.                 }
  196.                 dis_buf[2]=LEDData[tempH%10]&0x7f;
  197.                 dis_buf[3]=LEDData[tempL];
  198.                 dis_buf1[7]=tempH%10+'0';
  199.                 dis_buf1[9]=tempL+'0';
  200.         }
  201.         else if(KeySec==2)//顯示調整上限
  202.         {
  203.                 dis_buf[0]=0x89;//顯示'H'
  204.                 dis_buf[1]=LEDData[Ceiling/10%10];
  205.                 dis_buf[2]=LEDData[Ceiling%10]&0x7f;
  206.                 dis_buf[3]=LEDData[0];

  207.                 dis_buf1[5]='H';//顯示'H'
  208.                 dis_buf1[6]=Ceiling/10%10+'0';
  209.                 dis_buf1[7]=Ceiling%10+'0';
  210.                 dis_buf1[9]='0';
  211.         }
  212.         else if(KeySec==3)//顯示調整下限
  213.         {
  214.                 dis_buf[0]=0xc7;//顯示'L'
  215.                 dis_buf[1]=LEDData[Bottom/10%10];
  216.                 dis_buf[2]=LEDData[Bottom%10]&0x7f;
  217.                 dis_buf[3]=LEDData[0];
  218.                         
  219.                 dis_buf1[5]='L';//顯示'L'
  220.                 dis_buf1[6]=Bottom/10%10+'0';
  221.                 dis_buf1[7]=Bottom%10+'0';
  222.                 dis_buf1[9]='0';
  223.         }
  224. }
  225. /*****顯示子程序*****/
  226. void display()
  227. {
  228.         static uchar i,j;
  229.         static bit flag=0;
  230. //數碼管與LCD1602交替刷新
  231.         if(!flag)
  232.         {
  233.                 P2&=0xf0;//清P2低4位 數碼管消隱
  234.                 switch(j)
  235.                 {
  236.                         case 0:LCD_RS=0;P0=0x85;        break;//坐標指令
  237.                         case 1:LCD_RS=1;P0=dis_buf1[5]; break;//更新字符
  238.                         case 2:P0=dis_buf1[6];          break;
  239.                         case 3:P0=dis_buf1[7];          break;
  240.                         case 4:P0=dis_buf1[8];          break;
  241.                         case 5:P0=dis_buf1[9];          break;        
  242.                 }
  243.                 LCD_EN=1;
  244.                 j=++j%6;
  245.         }        
  246.         else
  247.         {
  248.                 LCD_EN=0;        
  249.                 P0=dis_buf[i];//送段碼
  250.                 P2|=0x01<<i;  //送位碼
  251.                 i=++i%4;          //位掃描計數
  252.         }
  253.         flag=~flag;
  254. }

  255. //按鍵掃描
  256. void keyscan()
  257. {
  258.         static uint time=0;
  259.         if(!SET||!DEC||!ADD)
  260.         {
  261.                 time++;
  262.                 if(time>key_L)//長按計數
  263.                         time=key_I;//連+/-間隔
  264.                 if(time==key_S)//短按消抖
  265.                 {
  266.                         if(!SET)
  267.                         {
  268.                                 KeySec++;
  269.                                 if(KeySec>3)
  270.                                 {
  271.                                         KeySec=1;
  272.                                 }
  273.                         }
  274.                 }
  275.                 if(time==key_S||time==key_L)//短按消抖或長按連+/-
  276.                 {//設置范圍0~99℃,上限不小于下限+1℃ 下限不大于上限-1℃,
  277.                         if(!DEC && KeySec>1)
  278.                         {
  279.                                 switch(KeySec)
  280.                                 {
  281.                                         case 2: if(Ceiling>1&&Ceiling>(Bottom+1))Ceiling--; break;
  282.                                         case 3: if(Bottom>0)Bottom--; break;
  283.                                 }
  284.                         }
  285.                         if(!ADD && KeySec>1)
  286.                         {
  287.                                 switch(KeySec)
  288.                                 {
  289.                                         case 2: if(Ceiling<99)Ceiling++; break;
  290.                                         case 3: if(Bottom<(Ceiling-1))Bottom++; break;
  291.                                 }
  292.                         }
  293.                 }
  294.         }
  295.         else time=0;
  296. }
  297. /*****報警子程序*****/
  298. void Alarm()
  299. {
  300.         static unsigned int count=0;

  301.         if(tempM>(Ceiling<<4))//超上限
  302.                 limit_H=0;                //LED亮
  303.         else limit_H=1;                //LED滅
  304.         if(tempM<(Bottom<<4))        //超下限
  305.                 limit_L=0;                //LED亮
  306.         else limit_L=1;                //LED滅

  307.         if(limit_H==0||limit_L==0)
  308.         {
  309.                 count++;
  310.                 if(count>50)
  311.                         BEEP=1;
  312.                 if(count>=100)
  313.                 {
  314.                         count=0;
  315.                         BEEP=0;
  316.                 }
  317.         }
  318.         else
  319.         {
  320.                 count=0;
  321.                 BEEP=0;
  322.         }
  323. }
  324. void Timer0Init()                //2毫秒@12.000MHz
  325. {
  326.         TMOD |= 0x01;                //設置定時器模式
  327.         TL0 = 0x30;                //設置定時初始值
  328.         TH0 = 0xF8;                //設置定時初始值
  329.         TF0 = 0;                //清除TF0標志
  330.         TR0 = 1;                //定時器0開始計時
  331. }

  332. /*****主函數*****/
  333. void main(void)
  334. {
  335.         uint z;
  336.         BEEP=0;//蜂鳴器初始化
  337.         P2&=0xf0;//清P2低4位 數碼管消隱
  338.         LCDinit();//初始化LCD
  339.         write_com(0x80);//固定字符
  340.         for(z=0;z<12;z++)
  341.                 write_date(dis_buf1[z]);
  342.         for(z=0;z<7;z++)
  343.                 ReadTemperature();//初始讀DS18B20
  344.         disassemble();//
  345.         for(z=380;z>0;z--)//不顯示DS18B20初始85℃
  346.         {
  347.                 display();    //顯示'----'
  348.                 Delay(250);         //2ms
  349.         }
  350.         KeySec=1;//鍵值初始化
  351.         Timer0Init();//定時器初始化
  352.         while(1)
  353.         {
  354.                 if(TF0)
  355.                 {
  356.                         TF0 = 0;
  357.                         TL0 = 0x18;                //設置定時初始值
  358.                         TH0 = 0xFC;                //設置定時初始值
  359.                         display();//顯示掃描
  360.                         ReadTemperature();//溫度檢測
  361.                         keyscan();//按鍵掃描
  362.                         disassemble();//數據分解
  363.                         Alarm();//報警檢測
  364.                 }
  365.         }  
  366. }        

復制代碼



溫度上下限報警 仿真.rar

292.91 KB, 下載次數: 7, 下載積分: 黑幣 -5

回復

使用道具 舉報

26#
ID:326261 發表于 2023-5-24 10:37 | 只看該作者
tyrl800 發表于 2023-5-22 18:05
學習了一下,編程最簡單能實現樓主要求的方法就是用非阻塞延時函數, 也就是通過循環執行一段時間來模擬延 ...

非阻塞式延時不是循環執行,是以狀態機來判斷延時。你的理解反了

把當前狀態列入保護現場,開啟延時,程序繼續運行,不影響整個程序,下次進來時,還要回到計時的節點來判斷是否定時到達,定時未到達,則繼續,定時到達則進入下一個狀態或者釋放狀態機。
回復

使用道具 舉報

27#
ID:326261 發表于 2023-5-24 10:39 | 只看該作者
wulin 發表于 2023-5-23 10:20
LCD1602、DS18B20、按鍵消抖、長短按識別等都是對時序有一定要求的外部硬件。用常見的阻滯型延時函數寫代碼 ...

你的程序我看了,如果你認真審題,當你delay開始while--的時候就已經對不上我的問題了

我的初衷就是程序中不能出現while等待,程序實時性很高的,會影響其他資源運行。
回復

使用道具 舉報

28#
ID:644357 發表于 2023-5-24 10:46 | 只看該作者
使用操作系統,把LCD任務設置高優先級,遇到忙延時就掛起或者阻塞,不影響其他線程,理查德、
回復

使用道具 舉報

29#
ID:644357 發表于 2023-5-24 10:48 | 只看該作者
使用操作系統,LCD設置高優先級,忙延時進入阻塞,顯示任務完成后掛起等待標志位恢復線程,這樣能把每一個忙延時榨干凈,還能不影響其他線程運行,也不影響中斷觸發
回復

使用道具 舉報

30#
ID:213173 發表于 2023-5-24 15:59 | 只看該作者
本帖最后由 wulin 于 2023-5-24 16:10 編輯
工學院陳偉霆 發表于 2023-5-24 10:39
你的程序我看了,如果你認真審題,當你delay開始while--的時候就已經對不上我的問題了

我的初衷就是程 ...

...........
回復

使用道具 舉報

31#
ID:384109 發表于 2023-5-24 16:27 | 只看該作者
按樓主的需求,的確如29樓說的用操作系統最好,而且樓主的還是32的F4芯片,跑操作系統就更方便了
回復

使用道具 舉報

32#
ID:744809 發表于 2023-5-24 22:55 | 只看該作者
  1. /* main.c */
  2. #include <REGX52.H>
  3. #include <stdio.h>
  4. #include "lcd1602.h"

  5. #define MAIN_FOSC_DELAY 12000000UL

  6. #define T1MS_0 (65536-MAIN_FOSC_DELAY/12/1000)   //1ms timer calculation method in 12T mode
  7. #define T1MS_1 (65536-MAIN_FOSC_DELAY/12/1000)   //1ms timer calculation method in 12T mode
  8. #define TIMER_MODE0     0x00
  9. #define TIMER_MODE1     0x01
  10. #define TIMER_MODE2     0x02
  11. #define TIMER_MODE3     0x03

  12. static bit _1_ms_flag = 0;    //1ms標志位
  13. static bit _100ms_flag = 0;   //100ms標志位
  14. static bit _500ms_flag = 0;  //500ms標志位

  15. static void Timer0Init( void );

  16. //主函數
  17. int main( void )
  18. {
  19.     idata float disTemp[4] = {0};
  20.     idata unsigned char display_buf[16];//顯示數組
  21.     idata unsigned int display_len;//顯示數據長度

  22.     Timer0Init();
  23.     MngLCD1602_Init();//LCD1602初始化
  24.     EA = 1;
  25.     while( 1 )
  26.     {
  27.         if(1 == _100ms_flag)
  28.         {
  29.             _100ms_flag = 0;
  30.             
  31.             disTemp[0] += 0.1f;
  32.             disTemp[1] += 0.2f;
  33.             disTemp[2] += 0.3f;
  34.             disTemp[3] += 0.4f;
  35.             
  36.             display_len = sprintf( display_buf, "%.2f  ", disTemp[0] );
  37.             displayString( 0, 0, display_buf, display_len );
  38.             
  39.             display_len = sprintf( display_buf, "%.2f  ", disTemp[1] );
  40.             displayString( 10, 0, display_buf, display_len );
  41.             
  42.             display_len = sprintf( display_buf, "%.2f  ", disTemp[2] );
  43.             displayString( 0, 1, display_buf, display_len );
  44.             
  45.             display_len = sprintf( display_buf, "%.2f  ", disTemp[3] );
  46.             displayString( 10, 1, display_buf, display_len );
  47.         }
  48.         else
  49.         {
  50.             /* empty */
  51.         }
  52.         
  53.         if(1 == _1_ms_flag)
  54.         {
  55.             _1_ms_flag = 0u;
  56.             MngLCD1602_Handle();
  57.         }
  58.         else
  59.         {
  60.             /* empty */
  61.         }
  62.     }
  63. }

  64. //定時器0中斷
  65. static void Timer0Init( void ) //定時器初始化為1ms一次
  66. {
  67.     TMOD |= 0X01; //選擇為定時器0模式,工作方式1,僅用TR0打開啟動。

  68.     TL0 = T1MS_0;//給定時器賦初值,定時1ms ,計算方式(65536 -(12/12*1000))%256
  69.     TH0 = T1MS_0 >> 8;//給定時器賦初值,定時1ms ,計算方式(65536 -(12/12*1000))/256
  70.     ET0 = 1; //打開定時器0中斷允許
  71.     TR0 = 1; //打開定時器
  72. }

  73. void time0_isr() interrupt 1 using 0
  74. {
  75.     static unsigned int count = 0;
  76.     static unsigned int count2 = 0;
  77.     TL0 = T1MS_0;//手動重裝載
  78.     TH0 = T1MS_0 >> 8;//手動重裝載
  79.    
  80.     _1_ms_flag = 1;//1ms標志位置1

  81.     if( ++count >= 100 ) //0.1秒到了
  82.     {
  83.         count = 0;
  84.         _100ms_flag = 1;//1秒標志位置1
  85.     }

  86.     if( ++count2 >= 500 )
  87.     {
  88.         count2 = 0;

  89.         _500ms_flag = 1;
  90.     }
  91. }

  92. /* LCD1602.h */
  93. #ifndef __LCD1602_H_
  94. #define __LCD1602_H_

  95. /**********************************
  96. 包含頭文件
  97. **********************************/
  98. #include <REGX52.H>

  99. typedef unsigned long uint32_t;
  100. typedef unsigned int  uint16_t;
  101. typedef unsigned char uint8_t;

  102. /**********************************
  103. PIN口定義
  104. **********************************/
  105. #define LCD1602_DATAPINS P0
  106. sbit LCD1602_RS = P2 ^ 6;
  107. sbit LCD1602_RW = P2 ^ 5;
  108. sbit LCD1602_E = P2 ^ 7;

  109. /**********************************
  110. 函數聲明
  111. **********************************/
  112. extern void MngLCD1602_Init( void );
  113. extern void MngLCD1602_Handle( void );
  114. extern void displayString( uint8_t x, uint8_t y, uint8_t* dat, uint8_t len);
  115. #endif

  116. /* LCD1602.c */
  117. #include "lcd1602.h"
  118. #include <string.h>

  119. #define LCD1602_ROW_NUM         2u
  120. #define LCD1602_COLUMN_NUM      16u

  121. #define LCD1602_READ_DATA()     (LCD1602_DATAPINS)
  122. #define LCD1602_WRITE_DATA(dat) (LCD1602_DATAPINS = (uint8_t)dat)
  123. #define LCD1602_SET_RS_H()      (LCD1602_RS = 1)
  124. #define LCD1602_SET_RS_L()      (LCD1602_RS = 0)

  125. #define LCD1602_SET_RW_H()      (LCD1602_RW = 1)
  126. #define LCD1602_SET_RW_L()      (LCD1602_RW = 0)

  127. #define LCD1602_SET_E_H()       (LCD1602_E = 1)
  128. #define LCD1602_SET_E_L()       (LCD1602_E = 0)
  129. typedef enum   
  130. {
  131.     false = 0u,
  132.     true,
  133. }Bool;

  134. typedef enum   
  135. {
  136.     Ce_Sending = 0u,
  137.     Ce_SendOK,
  138. }TeLCD1602_SendState;

  139. typedef enum   
  140. {
  141.     Ce_SendAddr_00 = 0u,
  142.     Ce_SendData_00,
  143.     Ce_SendAddr_10,
  144.     Ce_SendData_10,
  145. }TeLCD1602_SendStep;

  146. typedef enum   TeLCD1602_e_writeStateTypeTag
  147. {
  148.     Ce_Stay_0 = 0u,
  149.     Ce_Stay_1 = 1u,
  150.     Ce_Stay_2 = 2u,
  151. } TeLCD1602_e_writeStateType;

  152. typedef struct TsLCD1602_h_RowParaTypeTag
  153. {
  154.     const uint8_t*              e_p_DisplayStartAddr;
  155.     const uint8_t*              e_p_DisplayEndAddr;
  156. } TsLCD1602_h_RowParaType;

  157. typedef struct TsLCD1602_h_displayParaTypeTag
  158. {
  159.     TeLCD1602_SendStep          e_e_SendStep;
  160.     TeLCD1602_e_writeStateType  e_e_WriteState;
  161.     TsLCD1602_h_RowParaType     e_h_RowPara[LCD1602_ROW_NUM];
  162.     uint8_t*                    e_p_DisplayNextAddr;
  163. } TsLCD1602_h_displayParaType;


  164. static volatile uint8_t SeLCD1602_u_displayBuffer[LCD1602_ROW_NUM][LCD1602_COLUMN_NUM];

  165. static volatile TsLCD1602_h_displayParaType SsLCD1602_h_displayPara;

  166. static Bool MngLCD1602_IsBusy( void );
  167. static void MngLCD1602_Display();
  168. static TeLCD1602_SendState LcdWriteData( const uint8_t dat );
  169. static TeLCD1602_SendState LcdWriteCom( const uint8_t com ) ;

  170. static Bool MngLCD1602_IsBusy( void )
  171. {
  172.     LCD1602_WRITE_DATA(0xff);

  173.     LCD1602_SET_RS_L();
  174.     LCD1602_SET_RW_H();

  175.     if( 0 != (LCD1602_READ_DATA() & 0x80) )
  176.     {
  177.         LCD1602_SET_E_L();
  178.         LCD1602_SET_E_H();
  179.         return true;
  180.     }
  181.     else
  182.     {
  183.         LCD1602_SET_E_L();
  184.         return false;
  185.     }
  186. }

  187. static TeLCD1602_SendState LcdWriteCom( const uint8_t com )
  188. {
  189.     switch( SsLCD1602_h_displayPara.e_e_WriteState )
  190.     {
  191.         case Ce_Stay_0:
  192.             LCD1602_SET_RS_L();
  193.             LCD1602_SET_RW_L();
  194.             LCD1602_WRITE_DATA( com );
  195.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_1;
  196.             return Ce_Sending;
  197.             break;

  198.         case Ce_Stay_1:
  199.             LCD1602_SET_E_H();
  200.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_2;
  201.             return Ce_Sending;
  202.             break;

  203.         case Ce_Stay_2:
  204.             LCD1602_SET_E_L();
  205.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_0;
  206.             return Ce_SendOK;
  207.             break;
  208.         default:
  209.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_0;
  210.             return Ce_SendOK;
  211.             break;
  212.     }
  213. }

  214. static TeLCD1602_SendState LcdWriteData( const uint8_t dat )
  215. {
  216.     switch( SsLCD1602_h_displayPara.e_e_WriteState )
  217.     {
  218.         case Ce_Stay_0:
  219.             LCD1602_SET_RS_H();
  220.             LCD1602_SET_RW_L();
  221.             LCD1602_WRITE_DATA( dat );
  222.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_1;
  223.             return Ce_Sending;
  224.             break;

  225.         case Ce_Stay_1:
  226.             LCD1602_SET_E_H();
  227.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_2;
  228.             return Ce_Sending;
  229.             break;

  230.         case Ce_Stay_2:
  231.             LCD1602_SET_E_L();
  232.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_0;
  233.             return Ce_SendOK;
  234.             break;
  235.         default:
  236.             SsLCD1602_h_displayPara.e_e_WriteState = Ce_Stay_0;
  237.             return Ce_SendOK;
  238.             break;
  239.     }
  240. }

  241. static void MngLCD1602_Display( void )
  242. {
  243.     switch(SsLCD1602_h_displayPara.e_e_SendStep)
  244.     {
  245.         case Ce_SendAddr_00:
  246.             if(Ce_Stay_0 == SsLCD1602_h_displayPara.e_e_WriteState)
  247.             {
  248.                 if(false == MngLCD1602_IsBusy())
  249.                 {
  250.                     LcdWriteCom(0x80);
  251.                 }
  252.                 else
  253.                 {
  254.                     /* empty */
  255.                 }
  256.             }
  257.             else if(Ce_SendOK == LcdWriteCom(0x80)) /* 第0,0位置開始顯示 */
  258.             {
  259.                 SsLCD1602_h_displayPara.e_e_SendStep          = Ce_SendData_00;
  260.                 SsLCD1602_h_displayPara.e_e_WriteState        = Ce_Stay_0;
  261.                 SsLCD1602_h_displayPara.e_p_DisplayNextAddr   = SsLCD1602_h_displayPara.e_h_RowPara[0].e_p_DisplayStartAddr;
  262.             }
  263.             else
  264.             {
  265.                 /* empty */
  266.             }
  267.         break;
  268.         
  269.         case Ce_SendData_00:
  270.             if(Ce_Stay_0 == SsLCD1602_h_displayPara.e_e_WriteState)
  271.             {
  272.                 if(false == MngLCD1602_IsBusy())
  273.                 {
  274.                     LcdWriteData(*SsLCD1602_h_displayPara.e_p_DisplayNextAddr);
  275.                 }
  276.                 else
  277.                 {
  278.                     /* empty */
  279.                 }
  280.             }
  281.             else if(Ce_SendOK == LcdWriteData(*SsLCD1602_h_displayPara.e_p_DisplayNextAddr))
  282.             {
  283.                 SsLCD1602_h_displayPara.e_p_DisplayNextAddr++;
  284.                 if(SsLCD1602_h_displayPara.e_p_DisplayNextAddr > SsLCD1602_h_displayPara.e_h_RowPara[0].e_p_DisplayEndAddr)
  285.                 {
  286.                     SsLCD1602_h_displayPara.e_e_SendStep          = Ce_SendAddr_10;
  287.                     SsLCD1602_h_displayPara.e_e_WriteState        = Ce_Stay_0;
  288.                     SsLCD1602_h_displayPara.e_p_DisplayNextAddr   = SsLCD1602_h_displayPara.e_h_RowPara[1].e_p_DisplayStartAddr;
  289.                 }
  290.                 else
  291.                 {
  292.                     /* empty */
  293.                 }
  294.             }
  295.             else
  296.             {
  297.                 /* empty */
  298.             }
  299.         break;
  300.         
  301.         case Ce_SendAddr_10:
  302.             if(Ce_Stay_0 == SsLCD1602_h_displayPara.e_e_WriteState)
  303.             {
  304.                 if(false == MngLCD1602_IsBusy())
  305.                 {
  306.                     LcdWriteCom(0x80 + 0x40);
  307.                 }
  308.                 else
  309.                 {
  310.                     /* empty */
  311.                 }
  312.             }
  313.             else if(Ce_SendOK == LcdWriteCom(0x80 + 0x40)) /* 第0,1位置開始顯示 */
  314.             {
  315.                 SsLCD1602_h_displayPara.e_e_SendStep          = Ce_SendData_10;
  316.                 SsLCD1602_h_displayPara.e_e_WriteState        = Ce_Stay_0;
  317.                 SsLCD1602_h_displayPara.e_p_DisplayNextAddr   = SsLCD1602_h_displayPara.e_h_RowPara[1].e_p_DisplayStartAddr;
  318.             }
  319.             else
  320.             {
  321.                 /* empty */
  322.             }
  323.         break;
  324.         
  325.         case Ce_SendData_10:
  326.             if(Ce_Stay_0 == SsLCD1602_h_displayPara.e_e_WriteState)
  327.             {
  328.                 if(false == MngLCD1602_IsBusy())
  329.                 {
  330.                     LcdWriteData(*SsLCD1602_h_displayPara.e_p_DisplayNextAddr);
  331.                 }
  332.                 else
  333.                 {
  334.                     /* empty */
  335.                 }
  336.             }
  337.             else if(Ce_SendOK == LcdWriteData(*SsLCD1602_h_displayPara.e_p_DisplayNextAddr))
  338.             {
  339.                 SsLCD1602_h_displayPara.e_p_DisplayNextAddr++;
  340.                 if(SsLCD1602_h_displayPara.e_p_DisplayNextAddr > SsLCD1602_h_displayPara.e_h_RowPara[1].e_p_DisplayEndAddr)
  341.                 {
  342.                     SsLCD1602_h_displayPara.e_e_SendStep          = Ce_SendAddr_00;
  343.                     SsLCD1602_h_displayPara.e_e_WriteState        = Ce_Stay_0;
  344.                     SsLCD1602_h_displayPara.e_p_DisplayNextAddr   = SsLCD1602_h_displayPara.e_h_RowPara[0].e_p_DisplayStartAddr;
  345.                 }
  346.                 else
  347.                 {
  348.                     /* empty */
  349.                 }
  350.             }
  351.             else
  352.             {
  353.                 /* empty */
  354.             }
  355.         break;
  356.         
  357.         default :
  358.             SsLCD1602_h_displayPara.e_e_SendStep          = Ce_SendAddr_00;
  359.             SsLCD1602_h_displayPara.e_p_DisplayNextAddr   = SsLCD1602_h_displayPara.e_h_RowPara[0].e_p_DisplayStartAddr;
  360.         break;
  361.     }
  362. }

  363. void MngLCD1602_Init( void )
  364. {
  365.     uint8_t index = 0u;
  366.     uint8_t LeLCD1602InitCfg[4] = {0x38, 0x0c, 0x06, 0x01};
  367.    
  368.     SsLCD1602_h_displayPara.e_e_SendStep                         = Ce_SendAddr_00;
  369.     SsLCD1602_h_displayPara.e_e_WriteState                       = Ce_Stay_0;
  370.     SsLCD1602_h_displayPara.e_p_DisplayNextAddr   = &SeLCD1602_u_displayBuffer[0][0];
  371.    
  372.     SsLCD1602_h_displayPara.e_h_RowPara[0].e_p_DisplayStartAddr  = &SeLCD1602_u_displayBuffer[0][0];
  373.     SsLCD1602_h_displayPara.e_h_RowPara[0].e_p_DisplayEndAddr    = &SeLCD1602_u_displayBuffer[0][LCD1602_COLUMN_NUM-1];
  374.    
  375.     SsLCD1602_h_displayPara.e_h_RowPara[1].e_p_DisplayStartAddr  = &SeLCD1602_u_displayBuffer[1][0];
  376.     SsLCD1602_h_displayPara.e_h_RowPara[1].e_p_DisplayEndAddr    = &SeLCD1602_u_displayBuffer[1][LCD1602_COLUMN_NUM-1];
  377.    
  378.     memset(SeLCD1602_u_displayBuffer, 0x20, sizeof(SeLCD1602_u_displayBuffer));
  379.    
  380.     while(index < (sizeof(LeLCD1602InitCfg)/sizeof(uint8_t)))
  381.     {
  382.         if(Ce_Stay_0 == SsLCD1602_h_displayPara.e_e_WriteState)
  383.         {
  384.             if(false == MngLCD1602_IsBusy())
  385.             {
  386.                 LcdWriteCom(LeLCD1602InitCfg[index]);
  387.             }
  388.             else
  389.             {
  390.                 /* empty */
  391.             }
  392.         }
  393.         else if(Ce_SendOK == LcdWriteCom(LeLCD1602InitCfg[index]))
  394.         {
  395.             index++;
  396.         }
  397.         else
  398.         {
  399.             /* empty */
  400.         }
  401.     }
  402. }

  403. void MngLCD1602_Handle( void )
  404. {
  405.     MngLCD1602_Display();
  406. }

  407. void displayString( uint8_t x, uint8_t y, uint8_t* dat, uint8_t len)
  408. {
  409.     if(y >= 2)
  410.     {
  411.         return;
  412.     }
  413.     else
  414.     {
  415.         /* empty */
  416.     }
  417.     if(16 > (x + len))
  418.     {
  419.         memcpy(&SeLCD1602_u_displayBuffer[y][x], dat, len);
  420.     }
  421.     else
  422.     {
  423.         memcpy(&SeLCD1602_u_displayBuffer[y][x], dat, 15-x);
  424.     }
  425. }
復制代碼

你可以參考下我的,寫了一個小時才調好。51還是有點費勁,沒法直接仿真,而且對指針、宏定義的處理也有些問題。你移植到stm32的話就很簡單了。周期調用MngLCD1602_Handle();接口就ok了,1ms、5ms、10ms一次都可以,時間越長刷新的越慢。

keil.zip

135.3 KB, 下載次數: 4, 下載積分: 黑幣 -5

回復

使用道具 舉報

33#
ID:744809 發表于 2023-5-25 09:06 | 只看該作者
1、業務代碼和驅動代碼要分層,不論什么優化,都不要把業務代碼和驅動代碼放到一起去寫,改起來會要命的。
2、我上面那個代碼是一直循環發送,其實也可以改一改,每次重新設置了顯示數組,就讓驅動去循環顯示一下32個字節的數組就好了,這樣沒有新顯示內容的時候,就不會一直刷新。

回復

使用道具 舉報

34#
ID:41043 發表于 2023-5-25 09:20 | 只看該作者
yuxuesuixing 發表于 2023-5-22 11:29
狀態機嘛,就是狀態嘛,為什么不能有子狀態呢?
你這個提問的最大問題是沒有寫清楚系統功能和需求,根據系 ...

這個解釋的很好,很清楚。
回復

使用道具 舉報

35#
ID:326261 發表于 2023-5-30 09:49 | 只看該作者
123156fsadf 發表于 2023-5-24 22:55
你可以參考下我的,寫了一個小時才調好。51還是有點費勁,沒法直接仿真,而且對指針、宏定義的處理也有些 ...

寫的很好,程序我已經看了,使用輪詢模式,全局指針來寫數據,根據狀態位寫指令。如果直接把全部數據放入一個地方,直接使用寫全局指針的方式這樣就不需要區分了。和狀態機類似,用全局指針代替狀態位。感謝同仁支持!
回復

使用道具 舉報

36#
ID:326261 發表于 2023-5-30 09:52 | 只看該作者
123156fsadf 發表于 2023-5-25 09:06
1、業務代碼和驅動代碼要分層,不論什么優化,都不要把業務代碼和驅動代碼放到一起去寫,改起來會要命的。
...

對的!是這樣的,我目前的操作也是這么做,驅動和業務代碼還是需要區分,后來人希望能夠看到和代碼規范化,也感謝大家的支持。
無論是寫狀態機,還是寫操作系統,還是要把驅動層程序和應用層分開,這樣對驅動的校驗很好,也提高代碼閱讀性,可修改性。
回復

使用道具 舉報

37#
ID:326261 發表于 2023-5-30 09:55 | 只看該作者
mcu_xing 發表于 2023-5-25 09:20
這個解釋的很好,很清楚。

是的,層主解釋的很好,狀態機的學習可以參考一下,具體的解決方法層主也沒有說清楚,還是籠統了一點,看的懂就好,向各位學習。
回復

使用道具 舉報

38#
ID:404263 發表于 2023-5-31 15:29 | 只看該作者
工學院陳偉霆 發表于 2023-5-22 15:39
我的按鍵控制LCD1602刷新,在LCD刷新時不能影響IO口的接收判斷。

按照您的說法,設兩個狀態位,那么代 ...

這個我也有點沒理解,你這個屏幕刷新能占用的時間大概是多少,如果只是50ms內的話我感覺沒必要考慮你那種做法,畢竟不用延時的你很難做到說這1ms不動作然后1ms后我要馬上跳回發送這樣,按鍵和數據接收這些可以考慮放定時中斷里面
回復

使用道具 舉報

39#
ID:744809 發表于 2023-6-1 17:22 | 只看該作者
cokesu 發表于 2023-5-31 15:29
這個我也有點沒理解,你這個屏幕刷新能占用的時間大概是多少,如果只是50ms內的話我感覺沒必要考慮你那種 ...

有些系統是周期性的,比如所有函數都要求在5ms內完成,即5ms作為一個周期,那么ms級的延時就是致命的,在實時性要求高的系統中,不能夠有這種延時存在
回復

使用道具 舉報

40#
ID:514317 發表于 2023-8-1 21:32 | 只看該作者
都用F4了還用什么延時????直接上系統就行了    上個最簡單的時間片輪詢系統也能滿足要求   
你說的還有個主要問題是LCD1602匹配的問題   這個確實是太慢    可以用固定每次發送一個字節解決   不然是很占用時間   最好換成oled12864
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 99re国产视频 | 午夜小视频在线观看 | 成人高清在线 | 欧美一区二区三区四区视频 | av黄色片| 国产ts人妖系列高潮 | 国产成人精品久久 | 精品国产成人 | 伊人超碰| 天天玩天天干天天操 | 欧美九九九 | 成人亚洲网站 | 一区二区三区视频播放 | 欧美日在线 | 欧美韩一区二区 | 亚洲精品视频播放 | 亚洲欧美一区二区三区国产精品 | 国产偷自视频区视频 | 免费看黄视频网站 | 97日日碰人人模人人澡分享吧 | 久久欧美精品 | 成人午夜高清 | 在线中文av | 亚洲欧洲成人av每日更新 | 久久久久久免费看 | 男女羞羞视频在线 | 日韩免费电影 | 成人免费视频久久 | 精品国产乱码久久久久久图片 | 中文字字幕一区二区三区四区五区 | 欧美1区2区 | 国产精品免费看 | 成人综合一区 | 日本在线一区二区 | 亚洲精品久久久久中文字幕二区 | 欧美在线视频一区二区 | 麻豆精品国产91久久久久久 | 老司机狠狠爱 | 国产视频福利一区 | 在线观看亚洲精品 | 亚洲+变态+欧美+另类+精品 |