1. 程式人生 > >深入淺出,聊聊嵌入式低功耗開發

深入淺出,聊聊嵌入式低功耗開發

低功耗設計例項合集.zip  (13.3 MB)
微控制器低功耗設計例項合集.zip  (13.2 MB)
   說說低功耗開發的那些事兒
  文/Gorgon_Meducer
  不知從什麼時候開始,隨便做個什麼電子產品,至少是電池供電的,都要求低功耗特性了。好在市面上隨便什麼晶片都敢在自己的資料手冊的第一頁赫然寫著低功耗。究竟怎樣算低功耗?小於5mA?小於1ms?小於100uA?離開了應用場合,似乎數值也失去了單純的意義,總之越小越好。但感覺上,能用水果點亮的應用應該就是低功耗了吧。
  認真說來,有點懷念當年隨便一個應用500mA,晶片微微發燙,用手一摸只要還能放得住就大手一揮“沒問題”的時代了。最近總是和uA打交道,超過100uA,周圍的人臉色就不好看了,好容易達到了傳說中的20uA以內,也會覺得沾沾自喜,哎……uA啊……情何以堪啊,傷不起啊……

  久病成醫,漸漸的也就有了一些心得,似乎低功耗開發的過程也可以一板一眼,按部就班,似乎不單純是一些零散的“看情況而定”“只可意會”的東西了。於是忍不住,將這些似是而非的步驟記錄下來,以簞初來者。
   事半功倍還是事倍功半——思路決定成敗

   “肚子餓的時候,睡著了也就不覺得餓了……於是乎,難得的雙休日宅在家中補覺,往往也就一天只吃一餐飯了”——技術宅人_大體如此。
  應該沒有人能在夢遊的時候幹活吧?所以,平常工作的時候,飯還是要吃的。休眠和幹活應該是一對矛盾體。於是乎,晶片資料手冊上那些“小的出奇”的休眠功耗,似乎大部分時候只是用來擺設的;而工作功耗才是實實在在的東西。有時候,為了體現所謂的低功耗,還要在應用中設計一種所謂的低功耗模式——當系統確認沒有事情可做一段時間以後就乾脆回家睡覺了——這大體就是現在市面上常見的低功耗應用的某種程度上的現狀吧。於是乎,降低工作頻率這種“馬兒跑,馬兒不吃草”的邏輯,就成為降低正常工作模式下系統功耗的常規選擇。苦啊……多少人在工作頻率和功耗間糾結……又有多少功能實現的本身對對頻率擁有最低要求……苦啊——我說的是寫程式碼的程式設計師。

  說起來,降低功耗似乎是一個軟體和硬體協同工作才能解決的問題。比如AD取樣時候的分壓電阻,如果直接接了地,那麼就會一直消耗電流,如果通過一個IO口來控制其接地的方式,只在需要取樣的時候接地,取樣完成以後就懸浮或者拉高,就可以將這部分開銷降低的最小。
  顯然,將低功耗完全化作硬體設計的工作或者軟體設計的工作都是不合適的。
  
  從硬體角度來說,找到所有可能的消耗電流的迴路,一一確定哪些是可以通過軟體控制的方式來優化功耗的,哪些是不可避免的,並給程式的編寫人員提供一個所有IO口狀態對功耗影響的關係(通常用簡單的表格說明一下高電平會怎樣,低電平會怎樣,懸浮會怎樣就足夠了,並不需要精細到具體的數值。)做到這一點,基本上硬體的工作就告完成,剩下的就是軟體開發人員的發揮空間。而基於軟體的功耗降低策略,正是本文所要討論的重點。

  說到軟體功耗優化,說簡單也簡單,說複雜也複雜。簡單總結過來就是:應用模組化、功能任務化、任務週期化、功耗自理化、休眠一票否決化。還不夠簡單?再濃縮就是:能休眠就休眠,怎麼休眠投票選。呵呵……估計簡單過頭了,失去了資訊量,下面將這幾個方面一一展開:
   1、應用模組化、功能任務化、任務週期化
  一個具體的應用,通常由很多子功能,子任務組成。對嵌入式系統軟體構架有所瞭解的人更能理解:一個應用是由對若干服務(servie)的呼叫實現的。這裡服務可以是硬體服務,比如AD取樣,比如串列埠通訊,比如外中斷觸發,比如定時器服務;也可以是軟體服務,比如各種通訊協議棧,FAT檔案系統,佇列,軟體濾波等等。一個服務通常實現一個或多個功能(好的任務劃分不會讓一個服務包含多於2種以上不相關的功能)。簡單的功能,比如CRC校驗這樣函式進去函數出來基本上可以立即獲得結果的,我就不說了;複雜的功能最好都使用任務的方式來實現。說到任務,就要牽涉到作業系統、排程器或者乾脆是簡單的狀態機了。總之,可以將任務的實現理解為一個流程。既然是流程,那麼任務所要做的工作就是週期性的。舉例來說,AD取樣任務由至少3個步驟組成:通道選擇和啟動取樣,取樣以及等待取樣完成,資料的處理。這樣三個步驟共同組成了一個任務週期,當三個步驟完成以後,我們可以認為一個週期結束了。再舉一個例子,I2C通訊,一個完整的資料包的傳送通常包含了若干的狀態,這一系列狀態構成了一個任務,當最後一個狀態(或者某些異常退出狀態)結束後,一個任務週期就結束了。
  總結來說,應用模組化,功能任務化,任務週期化的最終目的就是任務週期化。只有實現了週期化,一件事情才有始有終。有始有終,就可以根據需要發放“工資”,避免浪費。而做到任務週期化的最常見辦法就是通過模組化的服務將功能獨立出來從而便於管理,便於找到一件事情的開頭和結尾。
  找到不拉馬的士兵,是功耗管理的起點。而做到這一點,是需要對嵌入式系統擁有全域性的概念,需要有基於模組化開發,面向服務和介面開發的經驗的。經驗的積累和全域性的概念,是最複雜的一個部分。
   2、功耗自理化、休眠一票否決化
  一旦實現了任務週期化,也就等於將整個系統分成了很多週期性工作的小任務,他們可能看起來是交錯的、並行的或者毫無先後關係的,但從本質上說,每個小任務只要關心自己的起始和中止就好了。系統的功耗管理最後就化簡為每個任務的功耗管理——只要每個任務做到了功耗最小,那麼系統整體在一個有效的協調方式下,就能做到功耗最小。
  根據上面的描述,基於任務的功耗管理實際上就被人為的分成兩個部分:微觀角度任務自身的功耗管理和巨集觀角度多工休眠的協調。
  我們先從微觀來看。一個任務,首先肯定能獨立完成自己的功能,這一點看似不起眼,其實很關鍵,他保證了任務中所有的步驟都是確定的,都是“自己說了算的”,對外界來說“都是黑盒子”的——簡而言之就是“自治”的。在這一基礎上,如果要求任務滿足低功耗的要求,不外乎以下幾種情形:
  1)任務執行的過程中,是不允許休眠的,因此任務的開頭和結尾處要設定標誌——告知協調系統,“只要我還沒有說OK就不允許休眠”“人在任務在”;
  2)任務執行的過程中,某一些階段允許休眠,而另外一些步驟是不允許休眠的;如果我們將 “不允許休眠”看作是休眠的最低等級,那麼根據功耗的大小,休眠可以由低到高分為若干各等級。這種情況下,我們可以修改上面的定義為:任務的執行過程中,不同階段允許不同的休眠等級。
  3)任務執行的過程中,不在乎是否有休眠。
  顯然,如果這三類任務同時存在於系統中,第三類任務基本上是“空氣”可以無視的,而第一種任務是相當霸道的,只要他在執行,就根本不允許休眠;對於第二類任務,即完成了任務,又兼顧了休眠,是一個值得表揚的“好同志”。我們在系統任務設計的時候,應該儘可能編寫後兩類任務,而避免或者嘗試拆分第一類任務。
  從巨集觀角度來看,任意時刻,可能有多個任務同時在執行,因此每個任務對休眠的需求都是不同的。如果要設立一個協調機制,該怎麼辦呢?難道這個協調機制需要了解每個任務的細節,然後“智慧”的找到合適的時間點使用合適的等級來休眠麼?太繁雜了吧?其實很簡單,讓每個任務都選派一個代表來開會好了——每次這個協調機構想休眠了,就召集所有的代表投票,大家每個人提供一個自己覺得能夠容忍的最高休眠等級,最後會議的仲裁者從這些投票中找到一個最小的休眠等級——也就是水桶的最矮一環作為“會議共識”,然後進入相應的休眠等級。顯然,如果有人投了“不休眠”得票,仲裁就只能無奈的選擇放棄休眠。所以,每一個“任務”都應該是一個負責的任務,而不能因為一己私利,為了保證自己任務的執行就草率的選擇自己在執行的期間“不允許休眠”。正確的做法就是,每個任務都應該根據自己的不同步驟及時的更新自己對休眠的容忍度,從而保證開會的時候,能夠達成有意義的結果。
  總結來說,如果每一個做事的人都很負責任,任務的劃分又很合理,那麼通過這種協商機制,體統自然就不存在“不拉馬的士兵”,也能做到“能休眠時就休眠”了。這種情況下,晶片資料手冊上那些休眠的功耗數字對你就有了實實在在的意義。怎麼樣?如果你已經領悟到了,就偷著樂吧。如果你還不知道如何具體去實現這些步驟,彆著急,隨後的章節我們一一為您展開。
   低功耗設計第一步:自底向上,順藤摸瓜

  假想一下,現在有一個新的專案放在你的手上,具體的專案需求包含了AD多通道資料取樣,AD取樣後資料的處理(較為複雜),同時還要支援I2C通訊,I2C的通訊協議較為複雜。這樣一個裝置必須做到功耗最低。應對這樣一個需求,如果已經有一個建議的晶片,比如AVR Mega或者Tiny我們應該怎麼做呢?
   "先搞清楚我們是怎麼睡的"
  第一步,查閱資料手冊,找到可被喚醒的最大休眠模式,編寫一個測試工程——按照資料手冊所說的要求,實現一個純粹的什麼都不做的休眠模式,同時,關閉所有能關閉的功耗——比如掐斷某些外設的時鐘,比如正確設定IO口狀態。這樣我們就獲得了一個極限功耗,也就是正常情況下你通過系統設計可以無限接近的一個功耗。
  這個時候,你需要的是,根據硬體設計工程師提供的IO口設定建議配置IO狀態,以獲得一個子人為的最低功耗。下載程式碼到目標電路,測量功耗。如果將電路板上無法優化的固有功耗——比如某些固定消耗電流的電阻功耗——消除以後,仍然沒有達到資料手冊上所說的對應休眠模式下的最大值時,你就要找硬體設計的兄弟喝茶了,兩個人一起同時從硬體的角度和軟體的角度找原因。直到我們獲得一個滿意的可自行喚醒的“純休眠功耗”。
  這個過程非常關鍵,他直接決定了日後你能達到的最好效果。多花費一點時間是值得的,因為在這一過程中,你能非常細微的“初步”瞭解到哪些外設和配置影響功耗,如何影響,有多大的影響。千里之行始於足下,多花點時間,值!
  切忌,做軟體的同學不要一口咬定說問題一定出在硬體設計上。舉一個例子:我在這個步驟地時候,始終達不到資料手冊上標註的休眠模式下的最大允許功耗,而根據資料手冊,我認為我已經將所有能關斷的外設都關閉了。於是我開始聯絡IC設計部門抱怨。最後的結果是,有一個外設使用了不同於CPU的獨立時鐘——也就是可以理解為非同步時鐘,在這種情況下,CPU對他的暫存器設定需要一定的時鐘週期才能完成同步。而我在系統中簡單的設定暫存器將其“關閉”後立即就去休眠了,可想而知由於沒有等待非同步始終同步,也就是這個操作還沒有生效,我就去休眠了,這個外設還處於開啟狀態,功耗自然居高不下。哎……臉紅啊……
   "再搞清楚我們是怎麼醒的"
  第二步,確認系統的脈搏。所謂確認系統的脈搏就是要總體審查整個應用的工作方式,找到一個系統時鐘的最大節拍,並根據這一要求確認晶片所使用的喚醒源。也許很多人都有使用定時器溢位中斷或者比較匹配中斷產生一個系統毫秒級時鐘的習慣,然而,除使用外部手錶晶振的RTC或者非同步時鐘源的定時器以外,普通定時器的正常工作都需要系統主時鐘提供時鐘源,這是我們所追求的低功耗模式所不允許的。有時候仔細想一想,一個毫秒級別的系統時鐘真的是必須的麼?
  在AVR中,在低功耗模式下能提供系統時鐘的通常就是看門狗了,通過設定,看門狗能夠以一個固定的時間間隔(16ms / 32ms / 64ms / 128ms)將系統從最大的休眠模式下喚醒。因此在那些必須要用到系統節拍的應用中,如果16ms是你所需系統節拍週期的約數,你就可以考慮採用看門狗來提供一個並不是那麼準確但是非常穩定的時鐘源;否則你就要面對以下的選擇:
  a. 我的系統工作模式決定了我必須要一個小於16ms的系統時鐘;那麼整個設計是否允許使用外部時鐘源給非同步定時器(Timer2的非同步模式)或者某些專門的RTC提供時鐘源——這些外設通常支援將系統從最大的休眠模式下喚醒,就像看門狗做到的那樣。
  b. 如果我的系統不允許增加外部時鐘源,系統是否允許通過外部觸發的模式來工作——也就是通過外中斷或者引腳電平變化中斷來喚醒系統,並開始一次工作流程,完成後系統再次陷入永久睡眠。
  c. 如果以上都不行,你可以考慮換晶片或者修改系統的需求的——至少系統需求在功耗的部分需要做一些妥協。
  以上“確認系統脈搏”的步驟實際上是一個“例子”——系統設計的例子,或者說系統工作模式設計的例子,這是一個系統構架師或者說像我這樣“自覺的”系統構架師應該要認真學會並經常實踐的工作。一個籠統而完整的描述如下:
  1) 對一塊目標晶片進行調查和確認:確認其所有的休眠模式,以及對應休眠模式關閉的時鐘源,這些時鐘源涉及到的外設——巧婦難為無米之炊,這一步首先搞清楚系統設計的時候手上有哪些材料。
  2)研究具體應用的需求,明確系統的工作模式(以取樣類的模式來說就是取樣,休息,再取樣,再休息,整個系統是一個狀態機並以取樣事件作為驅動;取樣不僅提供資訊,也提供系統的脈搏。即便這類系統還涉及到LCD重新整理或者I2C/串列埠通訊,由於資訊的本源是取樣,因此取樣週期本身決定了資訊的有效性,那麼LCD的重新整理週期,或者通訊的緩衝跟新週期沒有理由必須大於取樣的更新週期)。在這一前提下,明確系統對喚醒源以及喚醒模式的需求,由此便確定了系統的基礎休眠模式,進一步說,比較這一基礎休眠模式功耗和應用所需的功耗,便可給出系統設計的一個初步評估結果。有時候甚至能給出一些系統功耗的直接預期資料。
  舉例來說:
  假如通過評估,我們發現“實現應用所涉及到的外設”可以將系統從某個最大允許的SLEEP模式喚醒(例如Power-Save Mode),則這個SLEEP模式就是基礎Sleep模式,我們評估系統功耗的方法就是估算,在完成一次任務的情況下(任務節拍),sleep所佔的時間是百分之幾,系統工作的時間是百分之幾(active time),然後用下面的公式就可以估算出系統的正常功耗:
     Equation A:    normal power-consumption =          sleep consumption * sleep time(%) + active consumption * active time(%)  3)根據研究報告,討論系統的可行性。如果不可行,則根據已經明確的系統工作模式,對應的喚醒源要求重新選擇晶片,並返回步驟1)。如果可行,則可以進行後續的設計。
  Figure 1.1 一個能體現低功耗節拍設計的系統狀態圖

  
  這種系統設計模式看似有點本末倒置——還沒有搞清楚需求就已經把晶片定了——實際上,這種方式非常符合我們通常的開發模式:先有了一個初步的概念或者備選晶片方案,這些方案可能來自一個已有的方案,一個已有方案的相容方案,一個老闆或者老員工/經驗者提出的方案——總之有了一個基礎或者說原形,我們開始調查和研究具體低功耗的可行性,並形成了一個研究報告,這一報告將直接指導下一步的行為:由於需求非常明確,我們可以決定是否更換晶片或者直接進入下一步的開發。上面三個步驟實際上形成了一個LOOP,這一loop其實來自於“快速原型法”這一古老的“敏捷”開發模式自然,規範,高效。
  完成了以上兩個步驟,可以說這一階段就結束了,至此我們對系統的關鍵效能資料已經成竹在胸:系統最低能實現多大的功耗;外設某些敏感的引數設定將如何影響功耗——當我們需要在外設效能和功耗之間做妥協和權衡的時候這些資訊就將發揮巨大的作用;我們甚至對系統外來如何工作,或者說系統最基本的問題:“時間問題,整個複雜的多米諾骨牌是從哪裡被什麼推倒第一張牌的”大致有了瞭解。我們甚至知道,如果不出意外系統將會達到怎樣的功耗。
  簡單說,我們已經知道系統是能夠實現的,功耗會在什麼範圍也是大體有所概念的。萬事俱備,只欠東風。欲知後事,請聽下回分解。
    本小結的一個附錄:試探功耗底限的系統配置方式——分享一些實實在在的經驗
   A. 對AVR來說,不用的引腳怎麼辦?
   >>  如果這個引腳屬於ADC取樣引腳
  i) 通過DIDRn暫存器關閉對應引腳的數字輸入,這個時候PINX對應位將永遠讀取到0
  ii)通過DDRx和PORTx暫存器將對應的引腳設定為輸入,“關閉”上拉電阻
   >>  如果這個引腳是普通的GPIO
  官方推薦的方式是給這個引腳一個確定的電平,比如:
  i) 通過DDRx和PORTx暫存器將對應的引腳設定為輸入狀態,並“開啟”上拉電阻
  ii)通過DDRx和PORTx暫存器將對應的引腳設定為輸出狀態,並輸出低電平,PCB設計上,將該引腳接地。
   >>  如果這個引腳是RESET引腳當電路要求保證穩定的情況下儘可能簡化,同時,VCC電壓在上電時刻電壓升高速度不會很緩慢,則接外部上拉電阻到VCC,這樣雖然對功耗影響不大,但是可以適當提高一點抗干擾性。
   >>  如果這個引腳是擴充套件的ADC引腳基本可以不管,或者接地。
   B. 對AVR來說,需要使用的引腳怎麼辦?
   >>  如果這個引腳是開漏輸出/線與輸入的引腳,比如TWI,同時需要與外部裝置連線,而這一連線是允許拔插的
  i) 如果邏輯上允許,接下拉電阻。
  ii)如果是外中斷引腳或者因腳電平變化中斷引腳,避免懸浮,相比上拉來說,儘可能選擇下拉。這樣設定的目的是避免不確定電平經常將系統喚醒。選擇下拉的原因是將高電平的選擇權力交給外部裝置,同時更傾向於盜電( ^_^ )。
   >>  對於ADC引腳
  i) 如果永遠不會用於數字訊號的輸入用途,請參考空閒引腳的處理方式。
  ii)如果需要用作數字訊號的輸入用途,請在通過PINx讀取電平前,通過DIDRn暫存器開啟對應引腳的數字輸入,並插入兩個NOP後讀取電平,完成讀取後,立即關閉數字輸入功能。
   >>  對於控制訊號引腳
  i) 直接驅動LED總是悲劇的開始,別忘記加入限流電阻,儘可能使用高亮LED。
  ii)如果非要輸出電平的控制訊號,請認真考量有沒有漏出電流的可能,如果有,請儘可能在不需要輸出控制訊號的情況下,將IO口處理為無電流漏出的狀態(或最小小電流漏出的狀態,關於出還是入的語言文字問題,你懂的),具體狀態圖應該由硬體設計人員給建議。總原則就是按需分配。
   C. 說說PRR暫存器
   >>  如果某個外設的供電通過PRR暫存器的設定被掐斷了
  i) 除非特殊說明,否則這個外設的所有暫存器都是無法正確讀寫的
  ii)如果在外設供電關閉前,外設的中斷標誌沒有被清零;或者說正在執行這個中斷處理程式的時候該外設被斷電了,則中斷標誌不會被清除。表現症狀就是中斷持續被觸發。其實想想很簡單,參考i)就知道了。
  iii) 很多時候,關閉定時器這樣的外設並不能減少多少功耗,基本上都是小於5uA的。經驗上關閉那些和模擬特性相關度較大,或者擁有獨立相關的時鐘源的模組,通常會減少不小的功耗。
  iv) 謹慎使用該功能,除非你認真閱讀了資料手冊。這也是很多人所謂休眠醒不過來或者工作不正常的主要原因之一。
   D. 說說工作頻率         
   >>  在正常的工作模式下,頻率越高功耗越高。
  i) 推論1:對於同樣一個工作,頻率越高,完成該工作的時間越短
  ii)推論2:根據Equation A,對於同樣一個工作,Active的時間越短,則Sleep的時間越長。
   >>  假設頻率翻倍,功耗增加4倍,則:
  i) 推論3:通常Sleep功耗至少為某一個頻率功耗的4分之一甚至更大。當頻率翻倍的時候,工作時間縮短一半,假設功率翻倍時功耗為4倍,則Active部分的實際功率翻倍(50% * 4);同時,節省出來的時間成為Sleep功耗,此時,系統的功耗變化為:
  假設頻率翻倍前的Active功耗認作 C,工作時間為T,則:翻倍前Active部分的功耗為  C * T頻率翻倍後,Active的功耗為 4C,工作時間為 T/2;同時,假設Sleep的功耗為 C/4,則翻倍後的實際功耗為4C * (T/2) + (C/4) * (T/2) = 4.25C * T / 2 = 2.125 (C * T)可見頻率翻倍後實際功率為增加前的2.125倍。顯然從功耗的角度來說並不划算。
  但請注意,Sleep功耗通常遠遠小於這裡的1/4,以ATmega88為例,1MHz VCC = 2V的情況下最大功耗是550uA,而Sleep模式(Power-down VCC=3V)最大功耗是15uA,差不多36倍。在這種情況下,顯然頻率翻倍,功耗接近2倍。
  有意思的是,如果對以上公式進行迭代,你會發現,頻率越翻倍,Sleep功耗和翻倍前的Active功耗差距越大。也就是說基本上每次翻倍,功耗基本上可以認為是翻倍的。同時頻率增高帶來的好處卻是運算速度每次都翻倍的。從這個角度來說,是否頻率越高越好呢?(速度快,功耗增加小)。再聯絡到那些與外部引腳操作直接相關的功耗,頻率越高,外部引腳操作中可控的功耗部分就小(請聯絡前面AD的例子),那麼從這個角度來說,似乎功耗還會減小一些。
  再次強調,以上結論建立在工作完成後立即休眠的工作模式下,同時Sleep模式的功耗必須遠遠小於Active功耗(通常8~10倍),使用外部時鐘源,以及晶體振盪器所需的額外功耗,及從休眠中喚醒時時鐘穩定期間所需功耗都未作考慮。
   >>  實際上,MCU的功耗分為兩個部分,一個與頻率有關,一個與工作電壓有關,即
  E = E(V) + E(f)
  當頻率翻倍時,影響的只是E(f),而這部分功耗並不會最終導致4倍的功耗,其值往往在2倍甚至更低。在這種情況下,Sleep+Active的工作模式會帶來更低的功耗。證明略。
   >>  通常,我們測量功耗的方式就是測量電流
  i) 推論4:在非積分式功耗測定方式中,如果工作頻率越高,則Sleep時間越長,在電流測量的時候,Active電流越接近於“毛刺”,這就導致了頻率越高,功耗越低的假象。因為大部分的電流取樣點都落在了Sleep上。
  ii)推論5:採用積分式功耗測定方法才能正確的測定實際功耗。當然,如果你想騙騙普通客戶,高精度電流表的電流結果(甚至是平均電流)都將反饋給你一個“非常理想的結果”。
   低功耗設計第二步:讓我們來下棋,很大很大的一盤棋

  通過上一節的討論,假設在您的設計中對應的步驟已經完成,則我們至少獲得了以下資訊和結果:
  

  • 一個極限功耗

  • 一個可行的系統工作模式(滿足應用需求同時兼顧低功耗的系統節拍)

  • 當使用2中提及的系統工作模式時,通過公式計算獲得的理論系統功耗(系統正常工作下的功耗)

  顯然,萬事俱備,只欠東風,就只剩下具體實現了。彆著急,我們先來搞清楚幾個定義。
   a. 普通工作模式(Normal Mode / Active Mode)
  這裡的普通工作模式是指能夠實現正常系統功能的模式,在這種模式下,MCU並非一直處於工作狀態,而是根據前面一章提到的功耗公式,結合了Active和Sleep兩種MCU狀態的模式。簡而言之,一個保證了應用基本功能的同時能休眠就休眠的工作模式。
   b. 低功耗模式(Idle Mode / Sleep Mode)
  這裡所說的低功耗模式是指根據使用者的應用需求,在使用者指定或者系統自動偵測滿足某些特定條件的情況下,儘可能關閉不需要的功能,僅保留某些應用必須的任務以降低功耗為目的的模式。(例如一段時間沒有使用者操作就關閉LCD和LED背光)
  總結來說,這裡的普通工作模式和低功耗模式都不直接對應MCU提供的Active和Sleep模式,而是兩個根據使用者功能需求定義的具有不同功能配置的功耗模式。他們可能都牽涉到MCU的Active和Sleep狀態。明確了這兩個定義,我們後續討論中牽涉到的很多說法才不會混淆。
   2.1 工欲善其事,必先利其器
  在實踐低功耗系統設計前,我們必須要有一個有效的手段來檢測或者說觀察系統當前的工作模式,通俗的說就是要能夠隨時知道系統什麼時候工作,什麼時候休眠,並且最好能夠準確的知曉工作和休眠的時間比例。對於缺乏高精度電流表的場合來說,這種觀測手段就更為重要了。
  簡而言之,我們需要一種實時追蹤(trace)系統功耗模式的除錯(Debug)手段。既然是實時追蹤,意味著斷點、單步這種常見的手段是不行的,系統必須保證處於正常的非間斷工作狀態下。同時,作為Debug,必須能夠準確顯示休眠和工作的狀態,並能夠將這些資訊儲存下來,大家可以在腦海裡想像有這麼一個類似地震記錄儀的裝置,一直不停的在紙帶上記錄著功耗變化的資訊。說的這麼複雜,其實做起來很簡單。Debug階段,我們關係的是系統如何休眠的,其實際功耗可能由於Debug手段的存在而並不準確,但這一資訊已經足夠了,因為在隨後的檢測中,我們可以生成一個release版本(移除了debug相關的程式碼)直接通過電流表檢驗系統功耗。Code 2.1就是一個很好的例子。程式碼在休眠前在某一個訊號引腳上輸出高電平,在退出休眠模式後輸出低電平。此時,藉助示波器或者邏輯分析儀的幫助,我們就可以記錄並檢測系統的功耗模式,甚至根據資料手冊提供的Active電流和Sleep電流計算出一個非常接近實際結果的理論_功耗。
   Code 2.1 一個Tiny下進入Sleep模式的程式碼例子
  
static void enter_sleep_mode(uint8_t chLevel) {     /*! \brief sleep mode select bits      *!        MODE                 SM2  SM1 SM0      *!        idle                 0    0   0      *!        ADC Noise Reduction  0    0   1      *!        Power-save           0    1   1      *!        Power-off            1    0   0      */     static FLASH uint8_t c_chSleepLevel[] = {                             ((0x00 << SM0) | _BV(SE)),                             ((0x01 << SM0) | _BV(SE)),                             ((0x02 << SM0) | _BV(SE)),                             ((0x03 << SM0) | _BV(SE)) };     uint8_t chSleepMode = c_chSleepLevel[chLevel] /* | _BV(SE)*/;         SAFE_ATOM_CODE(         MCUCR = (MCUCR & ~(_BV(SM0) | _BV(SM1) | _BV(SE))) | chSleepMode;         ENABLE_GLOBAL_INTERRUPT();      //! enable interrupt           set_signal_a();    //! 即將進入sleep前將訊號引腳置高           __sleep();                      //! sleep         //!< clear sleep control register         MCUCR &= ~(_BV(SM0) | _BV(SM1) | _BV(SE));           clear_signal_a();   //!< 退出sleep模式後將訊號引腳拉低            )    }  有了這樣一個手段,我們就能很方便的進行實時除錯,設計出“能休眠就休眠”的好演算法。這也是我們後續討論的基礎。
  Figure 2.1 一個可能的系統功耗模式監測的樣例

附1:作者後續發表的有關電池供電嵌入式系統設計的論文  http://iopscience.iop.org/article/10.1088/1742-6596/490/1/012115/meta
  原帖地址:http://www.amobbs.com/forum.php?mod=viewthread&tid=4932393&highlight=%E4%BD%8E%E5%8A%9F%E8%80%97