謝作如 李琦
呼吸燈,顧名思義,就是燈光在單片機的控制下完成由亮到暗的逐漸變化,感覺像是在呼吸。呼吸燈廣泛應用于手機上,并曾經成為各大品牌手機的賣點之一。如果你的手機里有未處理的通知,如未接來電、未查收的短信等,呼吸燈就會不斷由暗到亮變化,像呼吸一樣有節奏,起到提醒的作用。
學習Arduino,呼吸燈是一個典型的案例。筆者編寫的《Arduino創意機器人》(人民郵電出版社出版)一書中就有“呼吸燈”一課,位于第一章的LED專題。通過研究呼吸燈效果,來學習Arduino的PWM輸出,并且學習Arduino的循環語句和全局變量。但從效果看,這還未達到理想的效果,因為燈光強弱的過渡不夠柔和。
為什么呼吸燈的效果不夠真實
《Arduino創意機器人》中是使用兩個循環來實現0到255和255到0的數值變換。程序是用ArduBlock編寫,參考代碼如圖1所示。
在這兩個循環中,變量K的值是遞增到255,然后再降到0。如何讓呼吸接近自然、真實?單純看代碼自然看不出什么。我們可以試著用Excel輸出從0到255再到0的一組數字,再用圖表畫出變量K的變化,如圖2所示。
這時,我們就能明顯看出線條是筆直的,成尖峰狀。這樣的數據變化,當然很難體現出“呼吸”這一張一弛的自然變化。那么,如何引入一個數學表達式,讓圖2的線條變得圓滑起來呢?
正弦函數的作用分析
在初中數學中,三角函數是一個重要的知識點,而正弦曲線就是一條圓滑的波浪線。嵌入式系統中許多應用都是對正弦函數進行采樣,得到按正弦規則變化的數組。舉個比較簡單的例子,如呼吸燈、警報聲等。
關于正弦函數,其定義如下:在直角坐標系中,給定單位圓,對任意角α,使角α的頂點與原點重合,始邊與x軸非負半軸重合,終邊與單位圓交于點P(u,v),那么點P的縱坐標v叫作角α的正弦函數,記作v=sin(α)。通常,我們用x表示自變量,即x表示角的大小,用y表示函數值,這樣我們就定義了任意角的三角函數y=sin(x),它的定義域為全體實數,值域為[-1,1]。
這段文字看起來有些專業,其實我們只需要了解一句話:在y=sin(x)的表達式中,不管x的值是什么,y的值總在-1到1之間,而且是小數。那么如何生成這組數據,并且數據呈現的線條是怎樣的,我們還是用Excel軟件來研究一下。
筆者先在Excel中產生0~360的一組角度數字,然后用RADIANS函數轉換為弧度,再用Sin函數輸出數據。因為Arduino的PWM在0和255之間,就乘以255,這樣得到了-255到255之間的數字,如下頁表1所示。
同樣用圖表來繪制(如下頁圖3)。看起來是不是很平滑啊?
至于如何將-255到255之間的數轉化為0~255的數,有幾種方案:
①取絕對值法。直接取絕對值,即使用ABS函數。這樣一來,繪出來的線條就變成了有圓頂的山丘狀,如圖4所示。
②數據映射法。將-255到255之間的數映射為0~255之間。采用的辦法也不復雜,除以2后,加上128即可,生成的數值如圖5所示。Arduino編程就更簡單了,可以直接調用map函數。
代碼編寫和效果實現
研究了正弦函數后,接下來就是編寫代碼了。Arduino中提供了radians、sin、int、abs、map等函數,因此寫這個表達式非常容易。這些函數的簡介如下。
radians:使用范例為radians(value)。將度數value轉換為弧度,如果不太在乎誤差,可以用“乘以3.14159再除以180”來替代。
sin:使用范例為sin(value)。返回角度value(radians形式)的三角函數sine值,數據類型為double。
int:使用范例為int(value)。返回向下取整為最接近value的整數。
abs:使用范例為abs(value)。返回value的絕對值,可以將負數轉正數。
map:使用范例為map(value, fromLow、fromHigh、toLow、 toHigh)。按照fromLow與fromHigh范圍,對等轉換value至toLow與toHigh范圍。
具體的參考代碼如圖6所示。
經過比較,直接采用絕對值形式的效果不如第二種數據映射的方式,在變暗的時候有跳動感。而方式2和方式3的表達式基本上是等價的,如表2所示。
需要強調的是,調試程序要善于利用串口輸出功能,即Serial.print。這樣可以幫助我們了解變量的數據變化,如圖7所示。
應用了正弦函數后,Arduino做出來的呼吸燈就很有“呼吸”的感覺了。當然,實現呼吸效果除了用單片機編程來實現外,也可以用模擬電路來實現。希望這個案例能讓大家更加深入了解數學知識在編程中的應用。