本文隸屬於AVR單片機教程系列。
背景(一)
寒假里做了一個燈帶控制器:
理想情況下我應該在一個星期內完成這個項目,但實際上它耗費了我幾乎一整個寒假,因為涉及到很多未曾嘗試的方案。在這種不是很趕時間的、可以自定目標、自由發揮的項目中,我喜歡這么做。
簡要介紹一下這個項目。硬件部分:
-
12V直流電源供電,開關降壓到4V,LDO降壓到3.3V,三路供電;
-
使用STM32G030KBT6單片機,基於Cortex-M0+;
-
12V燈帶用異步降壓驅動;
-
WS2812B燈帶的數據用SPI信號加上一定的邏輯運算生成;
-
OLED屏通過I²C連接單片機;
-
無定位旋轉編碼器連接定時器的兩個通道。
軟件部分:
-
用多項式擬合把ADC讀到的值轉換成溫度;
-
指令與數據調度、兩級流水線式OLED更新策略;
-
完全自主設計的簡單GUI,包括繪圖與控制邏輯。
大約一半時間花在兩種燈帶的方案驗證上,我制作了兩塊PCB才最終確定以上硬件方案;另有一部分時間花在STM32和HAL的調試上,但這本來就在我的STM32學習計划中;剩下的主要是OLED和GUI,以及寫不出來時的摸魚行為。
我趕在去學校當天的上午完成了項目,在砍掉一點完成度的前提下。有那么幾個小時,我很后悔把整個寒假都花在了這個項目上,沒有學該學的數學、寫該寫的博客。但是現在回想起來,這個項目讓我對單片機開發有了新的認識。
背景(二)
曾經AVR單片機教程唯一的讀者要開始獨立做項目了,這是對我文章質量的一次考驗。從我們之間的交流來看,我沒有通過這次考驗——
-
我說從供電開始思考:通常USB可以提供5V1A,如果需要5V2A,就應該考慮從12V1A轉換。她問如何轉換,於是我給她介紹了降壓轉換器。那么如何確定電流呢?主要耗電器件是32個LED。按我的思路,也可以說是絕大多數業內人士的思路,每個燈不會超過20mA,加起來640mA,其余器件耗電忽略,1A肯定是夠的。而她的思路是,算出每個LED的等效電阻,並聯起來,再計算電流。她又問,如果電源是5V2A,但負載只有5V1A,會燒壞嗎?
-
然后決定用什么燈。兩年半以前我送給她一個禮物,用32個LED組成一顆心,燈是紅藍交錯的。她想做得比這個高級一點,用32個RGB燈,但這么多燈的連接是個問題,以及RGB三種顏色之間的亮度匹配。又想到WS2812B,它在功能上很強,但是控制稍微麻煩一點。我提示她可以用PWM來生成控制信號,她又搞不清這里的PWM和PWM呼吸燈的區別。
-
可能是因為在第二期里學過OLED屏和u8g2,在電賽中又用過一遍,又收到了帶OLED屏的禮物,她覺得OLED屏很簡單,想在項目中也放一塊。
暫時不作評價,繼續說事。后來又有兩人從博客園后台聯系我,以成本價拿到了開發板,成為了教程的讀者(至此我已經沒有多余的開發板了)。一位讀者剛開始學C,他常常問我兩類我不希望他問的問題(請這位讀者不要生氣):一類是與單片機無關的純C語言問題,另一類是與教程進度不符的、我想隱藏的實現細節問題。
他們讓我開始反思教程的內容是否充足、順序編排是否合理。
回顧(一)
他們問我的這些問題,很多也是我曾經困惑的。
預初的那個暑假,我正式學習了C語言。此后,我想寫一個C程序來加密文件,把若干字節作為一組處理一下,寫進新的文件,就算是加密了。文件長度不一定是整組的,最后一組可能要加入幾個空的字節,這種現在看來很基礎的控制邏輯,我當時想了很久都寫不出來。
我也長期以來不知道電池充電的電流是如何控制的,在我看來把5V電壓加到3.7V鋰電池上根本不可控啊!后來才知道降壓、參考電流、反饋控制之類的。某些營銷號還說5V2A充電器給5V1A手機充電會造成損壞,也難怪非專業人士容易受誤導了。
高一的那個暑假,我完成了第一個像樣的項目,就是前面提到的那顆心。32個LED分別串聯470Ω排阻,接到4片級聯的74HC595上,用GPIO驅動。放到現在,如果不改變設計目標,我會選擇8*4動態掃描的連接方式,還可能用上一片TM16xx來驅動這個陣列。
按下按鍵切換模式,這個很簡單的功能在當時實現得非常亂:先定義一個宏,每次如果按下按鍵就return
,然后在循環中多次調用它。現在我會在定時器中斷里處理按鍵,具體來講還有多種方法。
這些不懂的問題、不好的實現,都是經驗。有些不好的實現,比如上面這個按鍵,不值得嘗試,所以我會在回答相關問題時提供我認為合理的方法。類似的有很多,所以我一直盡力回答每個問題,幫人避雷,也好讓我審視一下自己的方法。
回顧(二)
AVR單片機教程的一期介紹了簡單外設的用法。我假設讀者有良好的C語言基礎,所以沒有把基礎單片機和基礎C語言結合起來講,而是在前幾講的作業題中對讀者的C功底提出了較高的要求。針對當時讀者的問題,我專門寫了一篇元教程,強調了要廣泛地了解相關知識。
從按鍵開始我加快了節奏,否則按照一個LED都能寫4篇的速度我現在大概剛剛講到LCD1602。原計划一期結束后還有大量內容,但是我當時已經不大想寫了,所以挑了點必要的組成了第二期的6講。
現在有讀者反映第二期太難,其實不是太難而是兩期之間有明顯斷層。在與讀者交流的過程中,我還發現有很多我強調過的點還強調得不夠:有些是我沒講清楚,有些是讀者真的做不到,比如完成作業——我把很多關鍵的、實際應用中需要的問題放在了作業中,它們容易被忽略,至少沒有被足夠嚴肅地對待。
這些問題,以及上面帶有責怪情感的讀者提問,都是我的失職。
展望
我決定開始寫AVR單片機教程第三期。
首先是查漏補缺,這還得再細分幾類:
-
外設。I²C和定時器輸入在前兩期的正文中沒有涉及到,會在第三期補上。
-
語言。我不應假設讀者有良好的C基礎,但我真的沒法面對完完全全的新手,所以我將補充一些非單片機編程中不常用的特性,以及高級但實用的用法。
第三期的很多內容用C++講起來會更加方便,所以我會簡單介紹那些需要用的C++語法,它們也是我在自己項目中常用的C++功能子集。
-
作業。如前所述,很多重要的內容都在作業里,我將提供解題思路和優化建議。
然后是結構:
-
與C++綁定起來的對象的概念,包括基於對象與面向對象;
-
第二期最后提到的事件驅動范式,原本是單獨的一期,將成為第三期的一部分;
-
有些硬件結構可以啟發設計(有的可能最初來自軟件),包括雙緩沖、FIFO、狀態機、流水線等;
-
大型程序和庫的結構。
此外,單片機項目的共同問題,比如供電、制板、測試等,也會集中講解一下。
由此可見,這一期“AVR單片機教程”實際上可以把前三個字去掉,盡管我們還是基於AVR平台講解。
我設計了新的開發板,與第一版相比縮小了尺寸,去掉了部分擴展資源,使它更加核心。擴展資源被移動到了單獨的模塊上,多個模塊提供不同方向的器件。
原來的開發板仍然提供支持。新開發板有開放小批量訂購的計划,目前還在原型設計階段。因此,我將從不依賴於AVR的C和C++開始。
希望第三期能為讀者建立完善的概念,能讓讀者學會必要的單片機編程方法,在例子中積累經驗,獨立設計並制作出與文首項目難度相當的單片機系統。