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