欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

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

請優化你的MCU代碼 Ver2.0

  [復制鏈接]
回帖獎勵 500 黑幣 回復本帖可獲得 5 黑幣獎勵! 每人限 1 次
跳轉到指定樓層
樓主
- 前言 -
⼤家,好久不⻅。
此⽂旨在于對2019年我發表在51黑論壇的貼⼦《請⼯程化,定制化你的單⽚機代碼http://www.raoushi.com/bbs/dpj-162218-1.html 進⾏更新,升級和完善。
雖說現在回頭⼀看,那篇貼⼦在⼀些細節確實有⼀些問題,不過最⼤的問題是——它不是⼀個完整的⽂章,我匆忙的把⼀些編程要點塞進了帖⼦⾥,然后就⼀直沒有更新了。


如今⼀晃就是6年,機緣巧合下,我突然⼜找到了那篇貼⼦。在不知不覺之間,帖⼦已經被置頂,有了這么多的評論和閱讀量,這令我甚是慚愧。
感謝⼤家的⽀持。
我近期剛好有時間,那就把帖⼦重寫⼀下,并更完⽂章吧。

- 第⼀章:設計⼀個時鐘基線 -
在⼀些初級MCU編程⼊⻔書中,經常能夠⻅到類似
  1. delay1s();  //延時1秒
復制代碼
或者
  1. while(K1 == 0); //等待⽤戶釋放鍵盤上的K1按鍵
復制代碼
這樣的代碼,表⾯上看沒有什么問題,但是本質上跟下⾯的邏輯差不多:
“已知⼩明同學從教室跑去⼩賣部買糖再回來,⼤概需要1分鐘,所以,當我們需要計時12⼩時的時候,讓⼩明同學這樣來回往返個720次,應該就差不多了。”
“你戳⼩明⼀下,他就會叫⼀聲但是如果你⼀直戳著不松開,他就會由于腦⼦不夠⽤,不但叫不出來,就算你跟他說話他也沒法聽進去。”


在這樣的代碼中,MCU的PC指針(這東⻄代表著程序當前在哪個代碼⾥運⾏)就如同悲催的⼩明同學,被困在那兩⾏代碼中。也許是巨⼤的資源浪費,也許是⽆法對外部的信息變化做出反應,都是很糟糕的事情。
那么,要怎么樣來編寫單⽚機程序,才可以⼜實現這些邏輯功能,⼜釋放PC指針呢這就是我們接下來要討論的內容。

1.1 帶有時基的主程序架構

正如“ 每個不得不跑去⼩賣部的⼩明,其實他去年買了個表,根本不⽤跑 ”的道理()。
即使是普通的51單⽚機,他也是有定時器的。
我們需要設置⼀個定時器,讓定時器會告訴單⽚機:“好的,現在已經過了1ms了(或者10ms這都沒關系)”單⽚機⼀拍腦袋:“啊,已經到了1ms了么,那我就把⼀些事情做⼀下吧”。就像下面這樣子:


在這個框架下,我們的代碼看起來會像下⾯這個樣⼦:
  1. unsigned char flag1ms; //全局變量

  2. void main()
  3. {
  4.     flag1ms = 0;
  5.     mcuConfig(); //MCU的⼀些其他初始化,此處略過
  6.     timer0Init(); //定時器0初始化,略過,需設置1ms定時周期

  7.     while(1)
  8.     {
  9.         if(flag1ms)
  10.         {
  11.             flag1ms = 0;
  12.             doAnything(); //單片機可以做任意事情
  13.         }
  14.     }
  15. }

  16. void timer0XINT() interrupt 1 //定時器T0中斷
  17. {
  18.     flag1ms = 1;
  19. }
復制代碼
為了⽅便理解,這⾥的“⼀系列⼯作”在代碼中只⽤了⼀個doAnything()來表⽰,實際應⽤中,往往會放好⼏個⼦程序,沒什么問題。
在本程序中,單⽚機每隔1ms會將⼀系列⼯作執⾏⼀遍。從現在開始,你的⼦程序必須要摒棄那些“傻等⾏為”,重新設計你的程序邏輯。

1.2 ⼀個按鍵程序的例⼦
我們先拿⼀個⾮常惡⼼的按鍵函數來開⼑,如果沒有時基,我們可能會寫出這樣的⼀個按鍵程序:
  1. /* 在沒有時基的時候,代碼是這樣寫的 */
  2. #define K1_PRESSING() ((P1&0x01)==0) //按鍵位于P1^0引腳
  3. void keyPress()
  4. {
  5.     unsigned int key_press_time = 0;
  6.     while(K1_PRESSING()) //等待松開
  7.     {
  8.         ++key_press_time;
  9.         if(key_press_time==300) //300*10ms = 3s
  10.         {
  11.             //⻓按3s時要做的事情
  12.         }
  13.         delay10ms(); //delay計時
  14.     }
  15.     if((key_press_time < 300) && (key_press_time >= 2))
  16.     {
  17.         //短按的處理
  18.     }
  19. }
復制代碼
這個函數兼具delay()語句和while(KEY==0)語句,可以說是⼆毒俱全了。
如果是在新的代碼架構中,它可以修改成這樣的形式
  1. /* 本程序將在每個1ms時基中被調⽤ */
  2. #define JUST_INC(x) if(++x<=0) --x //x在不能溢出的前提下++
  3. #define K1_PRESSING() ((P1&0x01)==0) //按鍵位于P1^0引腳
  4. void keyPress()
  5. {
  6.     static unsigned int key_press_time = 0;  // ……請標為靜態變量
  7.     if(K1_PRESSING())
  8.     {
  9.         JUST_INC(key_press_time);//計量按鍵時間,并避免數據溢出
  10.         if(key_press_time==3000)
  11.         {
  12.             //在此寫下按鍵⻓按3s時要做的事情
  13.         }
  14.     }
  15.     else
  16.     {
  17.         if((20<=key_press_time) && (key_press_time < 3000))
  18.         {
  19.             //20ms ~ 3s之間,視為短按,在此寫下寫短按的處理代碼
  20.         }
  21.         key_press_time=0;
  22.     }
  23. }
復制代碼
功能是⼀樣的,但實際⽤起來區別很⼤——舊程序跑1次⻓按功能,新程序已經跑了3000次,且后者不會⼀直占⽤PC指針。
新的程序有些細節,可以展開說⼀說:
①key_press_time現在是⼀個靜態變量,static關鍵字可以讓該變量在每次重新進⼊函數的時候不會重新賦值為0,⽽是保留上次退出函數時的值。
②“JUST_INC(key_press_time)”這⼀句,看起來⽤“++key_press_time”就能搞定,但是,誰也不能保證⽤戶真的不會按按鍵超過65秒的啊萬⼀他真的按了65576ms單⽚機還就真的以為⽤戶“短按”了⼀次呢(65576-65536=40ms,屬于短按范疇),下⾯那個短按程序段也會被執⾏現在這樣寫,哪怕你按100年也沒關系了,反正單⽚機就每隔1ms進來看⼀次,K1這個按鈕你想按多久就按多久,掉在范圍內就處理,超出范圍就⽆視
③“if(key_press_time==3000)”這⾥的3000只是隨便設置的⼀個3秒⻓按時間,如果需要做按鍵⻓短按功能,這⾥就是⻓按程序所放的位置也可以不⽤3000,⽤2000、50000都沒事,別超過65534就⾏。
④“if((20<=key_press_time) && (key_press_time < 3000))”這⾥,前⾯的>=20是短按的消抖設計——再強的⼈類也不可能1秒按⼀個按鍵超過20次,也就是不可能⼩于50ms的時間——這⾥⽤了20ms相當于兼容“超級快男”來按按鍵了。后⾯<3000是不能和⻓按的時間沖突,因為3s我們已經⼈為的設置成⻓按時間節點了。
⑤這⾥的參數遵循乘法原則。舉個例⼦,如果時基是10ms,那么這⾥的if(key_press_time==3000)就會代表30秒,如果想設置⻓按3秒的話,得把3000改成300。


1.3 關于時基的進階設置
回到剛剛討論的時鐘基線處,這⾥有⼏個⼩問題。
(1)假如,1ms的周期不夠把事情做完怎么辦
——那就把時基設置⼤⼀些,⽐如5ms,10ms,20ms,還不夠就該換芯⽚了,要不就是代碼還有很⼤的其他問題,需要好好排查。
(2)我該怎么⼤致估算代碼跑完“⼀系列⼯作”需要的時間,好讓我設置更合適的時基
——好問題這⾥有⼀個簡單且準確的⽅案任意找個閑置端⼝,⽐如P0^0,在⼲活之前先把這個端⼝拉⾼,⼲完活之后⻢上把它拉低,通過外接⽰波器或者邏輯分析儀,就能直觀的測量出耗時了。



(3)只有1個時基不⽅便,我的按鍵只需要10ms采集1次,但是LCD顯⽰我需要100ms才刷新⼀次。
——時基是可以擴展的!在1個時基的基礎上,只要你有需要,完全可以擴展出N多個時基匹配你的N個⼦程序。
⽐如下⾯的代碼,就在1ms的時基上分別擴展出了10ms和100ms的時基:
(4)我的各個函數應該放在哪個時基⾥⾯呢
——這要求我們要對⾃⼰的程序要有清楚的把握,以及⼀定的產品思維,以下是⼀⼰之⻅。
①⾸先,所有的函數都要寫得簡潔⼲凈,不要有任何模塊的delay()加起來超過0.2ms,⼦程序⾥⾯放⼏個nop倒是⽆傷⼤雅。
按鍵,檢測,通信這類的⼦程序放到10ms時基⾥。輸出,顯⽰這類的放100ms時基就OK了。
③1ms時基⾥⾯應該放什么呢可以什么也別放,空著就好。或者把主程序的基礎時基換成10ms也可以,其實很少有東⻄需要刷新得這么快的。如果基礎時基打算⽤10ms的話,可以將定時器的中斷設置為每10ms觸發(這樣可以刪掉flag1ms變量,此處不展開)也可以偷個懶,原本的程序框架改成這樣即可


④如果有特別需要關照的部分,⽐如說步進電機的驅動啥的,請放到另⼀個定時器中斷⾥(單⽚機基本都⾄少有倆定時器的,不⽤⽩不⽤),按你需要的來設置。
⑤定時器的中斷觸發時間建議不要少于0.5ms,不然進中斷就太頻繁了。
現在,我們的代碼已經初具雛形,⼤家可以⾃⼰搭⼀下這個框架來體驗⼀下。
(當當當,現在是中場休息時間,喝杯⽔吧)




1.截至2025年10月的點擊量.png (39.4 KB, 下載次數: 0)

1

1

請優化你的代碼-附件程序F103.7z

684.3 KB, 下載次數: 0, 下載積分: 黑幣 -5

請優化你的單片機代碼.pdf

3.22 MB, 下載次數: 0, 下載積分: 黑幣 -5

評分

參與人數 3黑幣 +45 收起 理由
yinds5092 + 15
1109 + 15 贊一個!
莫燁 + 15 很給力!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏20 分享淘帖 頂9 踩
回復

使用道具 舉報

沙發
ID:476527 發表于 2025-10-30 20:43 | 只看該作者
本帖最后由 Similarv 于 2025-11-3 08:57 編輯

全文共有3頁,請手動翻頁。
另外,附件的排版應該比網頁的排版干凈。
由于不可抗力,從第5個代碼塊開始,網頁端po出的代碼就開始出現亂碼,我重復編輯了很多次,都無法解決。因此后面很多代碼塊都變成了圖片格式,影響了大家的閱讀,望諒解。
回復

使用道具 舉報

板凳
ID:476527 發表于 2025-10-30 20:47 | 只看該作者
終于整完了,這次特意在換工作的空窗期,抽出時間完成了這個主題。大家萬圣節快樂。


祝我明天面試順利,嘻嘻。
回復

使用道具 舉報

地板
ID:190832 發表于 2025-10-30 22:40 | 只看該作者
這樣不好嗎void ledTask(void)
{
    static u32 usBck = 0, usNow = 0;
    static u8 sta = 0;

    usNow = mSysTimerGetUs();
    if((usNow - usBck) > 250000)
    {
      usBck = usNow;
      sta = (sta == 0) ? 1 : 0;
      if(sta == 0)
      {
        LED_RUN_OFF; LED_STOP_OFF;
      }
      else
      {
        LED_RUN_ON; LED_STOP_ON;
      }
}
回復

使用道具 舉報

5#
ID:476527 發表于 2025-10-30 22:59 | 只看該作者
hange_v 發表于 2025-10-30 22:40
這樣不好嗎void ledTask(void)
{
    static u3 ...

你好,請問這是在?
回復

使用道具 舉報

6#
ID:1097337 發表于 2025-10-31 04:49 來自觸屏版 | 只看該作者
非常不錯,謝謝小豬豬的指導:D,收藏了!
回復

使用道具 舉報

7#
ID:190832 發表于 2025-10-31 09:15 | 只看該作者
發表于 2025-10-30 22:59
你好,請問這是在?

閃爍LED  250ms
回復

使用道具 舉報

8#
ID:476527 發表于 2025-10-31 16:48 | 只看該作者

我推測,這段代碼只能保證“LED不會在250ms以內就翻轉電平”,并不能確保剛好總是250ms的時候就立馬翻轉。
回復

使用道具 舉報

9#
ID:190832 發表于 2025-10-31 20:46 | 只看該作者
Similarv 發表于 2025-10-31 16:48
我推測,這段代碼只能保證“LED不會在250ms以內就翻轉電平”,并不能確保剛好總是250ms的時候就立馬翻轉 ...

那得做到定時器中斷,且優先級最高
回復

使用道具 舉報

10#
ID:142652 發表于 2025-10-31 22:16 | 只看該作者
感謝大師分享
回復

使用道具 舉報

11#
ID:477512 發表于 2025-11-1 07:21 | 只看該作者
這得好好學習一下!
回復

使用道具 舉報

12#
ID:945281 發表于 2025-11-1 15:24 | 只看該作者
謝謝分享
回復

使用道具 舉報

13#
ID:945281 發表于 2025-11-1 15:28 | 只看該作者
真的學到很多知識,再次謝謝
回復

使用道具 舉報

14#
ID:22119 發表于 2025-11-2 14:57 來自觸屏版 | 只看該作者
寫的真好
回復

使用道具 舉報

15#
ID:77675 發表于 2025-11-2 18:07 | 只看該作者
大神終于有更新了,受教了。
回復

使用道具 舉報

16#
ID:308541 發表于 2025-11-2 20:45 | 只看該作者
寫得很好
回復

使用道具 舉報

17#
ID:86450 發表于 2025-11-2 23:33 | 只看該作者
[em05]
回復

使用道具 舉報

18#
ID:82399 發表于 2025-11-3 07:44 | 只看該作者
好貼,學習了!
回復

使用道具 舉報

19#
ID:1144680 發表于 2025-11-3 09:51 | 只看該作者
樓主說的全部中招
回復

使用道具 舉報

20#
ID:901570 發表于 2025-11-3 18:53 | 只看該作者
感謝大佬的教程,已保存,慢慢研究!
回復

使用道具 舉報

21#
ID:1157477 發表于 2025-11-6 16:45 | 只看該作者
非常感謝樓主的付出。雖然我是小小小小白,只會復制別人的代碼
回復

使用道具 舉報

22#
ID:277290 發表于 2025-11-7 08:19 | 只看該作者
非常不錯,謝謝指導!
回復

使用道具 舉報

23#
ID:277290 發表于 2025-11-7 08:20 | 只看該作者
非常不錯,謝謝指導!
回復

使用道具 舉報

24#
ID:60656 發表于 2025-11-8 22:27 | 只看該作者
真的學到很多知識,再次謝謝
回復

使用道具 舉報

25#
ID:46750 發表于 2025-11-9 20:24 | 只看該作者
大神終于有更新了,受教了。
回復

使用道具 舉報

26#
ID:158391 發表于 2025-11-13 02:22 | 只看該作者
菜鳥一枚,現在的能寫出的都是讓大神惡心的代碼
回復

使用道具 舉報

27#
ID:283207 發表于 2025-11-13 23:06 | 只看該作者

非常感謝樓主的付出。
回復

使用道具 舉報

28#
ID:34149 發表于 2025-11-13 23:42 | 只看該作者
學習了大師的編程思維!真的受益良多!非常感謝!
回復

使用道具 舉報

29#
ID:476527 發表于 2025-11-14 14:06 | 只看該作者
dyx811 發表于 2025-11-13 23:42
學習了大師的編程思維!真的受益良多!非常感謝!

大師不至于,這是一個面向新手的教程,目的是讓大伙起步的時候少走一些彎路。
至于怎么成為大師,我自己也在摸索前進。
回復

使用道具 舉報

30#
ID:1144680 發表于 2025-11-17 10:27 | 只看該作者
新手表示看不懂啊。
如果要設計一個短按與長按的函數,是直接讓定時器計時嗎?
回復

使用道具 舉報

31#
ID:476527 發表于 2025-11-17 11:46 | 只看該作者
dcc60 發表于 2025-11-17 10:27
新手表示看不懂啊。
如果要設計一個短按與長按的函數,是直接讓定時器計時嗎?

你的主程序是每10ms才跑一次的,靜態變量的計數值就是計時值
回復

使用道具 舉報

32#
ID:232366 發表于 2025-11-17 11:49 | 只看該作者
又學習了
回復

使用道具 舉報

33#
ID:98767 發表于 2025-11-17 14:07 | 只看該作者
謝謝分享,學習優化
回復

使用道具 舉報

34#
ID:366877 發表于 2025-11-17 22:47 | 只看該作者
有個問題想請教:文中提到“不要有任何模塊的delay()加起來超過0.2ms”,但對于數碼管動態掃描如何控制每個數碼管點亮2ms?
回復

使用道具 舉報

35#
ID:476527 發表于 2025-11-17 23:13 | 只看該作者
wdgao 發表于 2025-11-17 22:47
有個問題想請教:文中提到“不要有任何模塊的delay()加起來超過0.2ms”,但對于數碼管動態掃描如何控制每 ...

個人建議你用低優先級的定時器中斷,2ms周期掃一下
回復

使用道具 舉報

36#
ID:18961 發表于 2025-11-18 10:01 | 只看該作者
非常不錯,值得學習一下!
回復

使用道具 舉報

37#
ID:366877 發表于 2025-11-19 07:07 | 只看該作者
發表于 2025-11-17 23:13
個人建議你用低優先級的定時器中斷,2ms周期掃一下

謝謝指導,是個好辦法。
回復

使用道具 舉報

38#
ID:140411 發表于 2025-11-29 08:21 | 只看該作者
很好的編程思維,值得學習。
回復

使用道具 舉報

39#
ID:492633 發表于 2025-11-30 12:13 來自觸屏版 | 只看該作者
能不用阻塞延時就不用
回復

使用道具 舉報

40#
ID:1110792 發表于 2025-12-2 18:43 | 只看該作者
真不錯,學習了!
回復

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表