項(xiàng)目概述
我們用Tiny6410開發(fā)板宠哄,利用本學(xué)期的學(xué)習(xí)內(nèi)容完成一個(gè)數(shù)碼相冊(cè)的設(shè)計(jì),希望它可以用LCD顯示圖片,有多張圖片可以用來切換伶椿,可以定時(shí)播放圖片,可以用小鍵盤來控制圖片的播放氓侧,如播放前一張脊另、播放后一張、開始自動(dòng)播放约巷、停止自動(dòng)播放等功能偎痛。經(jīng)過我們兩個(gè)人的努力,設(shè)想的功能已經(jīng)全部實(shí)現(xiàn)独郎,并測(cè)試通過了踩麦。
項(xiàng)目人員組成及分工
- 王佳琦:負(fù)責(zé)LCD繪圖和打印字符
- 謝昌秦:負(fù)責(zé)處理中斷
項(xiàng)目效果
- 開機(jī)默認(rèn)自動(dòng)播放模式
- 切換到第二張圖
- 第二張圖
- 切換到第三張圖
- 第三張圖
- 又回到第一張圖
項(xiàng)目開發(fā)過程
我們的開發(fā)過程主要是從幀入手的,一開始我們實(shí)現(xiàn)的是定時(shí)器氓癌,使我們指定的一個(gè)函數(shù)可以每秒都被調(diào)用一定次數(shù)谓谦,然后插入圖片數(shù)據(jù)到代碼中,再實(shí)現(xiàn)動(dòng)畫過渡效果贪婉。最后反粥,我們?cè)黾恿税存I中斷,從而可以通過按鍵進(jìn)行播放暫停疲迂、上一張和下一張等功能才顿。
關(guān)于圖像,首先計(jì)算出每張圖片的大小尤蒿,然后用畫圖工具畫出3張符合的BMP圖片郑气。然后根據(jù)BMP文件格式,24-bits的BMP文件腰池,除了前54位文件頭尾组,剩下的都是像素信息,又因?yàn)橛?jì)算出來的長度剛好是4的倍數(shù)示弓,所以不用考慮填充值演怎。用C++語言代碼將獲取到的像素值用十六進(jìn)制打印出來,然后存成二維數(shù)組避乏,為以后繪圖做準(zhǔn)備。
了解并學(xué)習(xí)第十九章LCD繪圖和打印字符中提供的參考代碼22.lcd.在lcd_init()對(duì)lcd控制器進(jìn)行了初始化甘桑,配置相關(guān)引腳用于LCD功能拍皮,配置MIFPCON寄存器歹叮,選擇normal mode,配置SPCON寄存器,選擇RGB I/F铆帽,配置VIDCONx咆耿,設(shè)置接口類型、時(shí)鐘爹橱、極性和使能LCD控制器萨螺,配置VIDTCONx, 設(shè)置時(shí)序和長寬等,配置WINCON0愧驱,設(shè)置window0的數(shù)據(jù)格式慰技,配置VIDOSDOA/B/C,設(shè)window0的坐標(biāo)系组砚,配置VIDWOOADD0B0和VIDWOOADD1B0吻商,設(shè)置framebuffer的地址。
項(xiàng)目總結(jié)
本次項(xiàng)目的需求看起來是非常簡(jiǎn)單的糟红,涉及到的基本都是學(xué)過的內(nèi)容艾帐,但是具體到實(shí)現(xiàn)過程中,卻發(fā)現(xiàn)遇到了很多問題盆偿。接下來列舉出我們遇到的問題柒爸。
首先遇到的問題是圖片放置的問題。由于我們沒有學(xué)怎么讀寫SD卡事扭,而資料里捎稚、網(wǎng)絡(luò)上都很難找到例子。對(duì)于我們這樣的嵌入式開發(fā)初學(xué)者來說句旱,沒有實(shí)例代碼阳藻,從文檔入手寫出實(shí)現(xiàn)某個(gè)功能的代碼是很困難的,因?yàn)槲臋n不一定完整谈撒,很容易遇到坑腥泥,而我們對(duì)嵌入式開發(fā)的經(jīng)驗(yàn)也幾乎為零。于是啃匿,我們打算曲線救國蛔外,將圖片的顏色數(shù)據(jù)直接作為常量硬編碼到代碼中。一開始溯乒,我們按照LCD顯示屏的大小夹厌,準(zhǔn)備了3張480*272
像素的圖片,并分別用工具轉(zhuǎn)換成對(duì)應(yīng)的數(shù)組數(shù)據(jù)裆悄。但是矛纹,當(dāng)我們嘗試將生成的bin文件寫入到開發(fā)板的時(shí)候,卻發(fā)現(xiàn)MiniTools的日志中爆了錯(cuò)誤光稼,而啟動(dòng)之后發(fā)現(xiàn)程序也沒有發(fā)現(xiàn)更改(我們一開始使用的是純色作為圖片)或南。后來我們發(fā)現(xiàn)孩等,大概是bin文件過大的緣故,燒寫直接失敗了采够,所以程序才沒有發(fā)生改變肄方。接下來,我們刪除了一張照片蹬癌,發(fā)現(xiàn)仍然無法燒寫权她。于是,我們將尺寸縮小到長逝薪、寬各為原來大小的四分之一隅要,發(fā)現(xiàn)此時(shí)3張照片的大小之和還不如原來一張照片!我們還計(jì)算了變?yōu)樵瓉砣种坏那闆r翼闽,但是發(fā)現(xiàn)這種情況下照片的數(shù)據(jù)空間減少不明顯拾徙,于是我們最終的解決方案是使用120*68
大小的三張照片,并在每個(gè)頁面里將照片平鋪成4*4
的矩陣感局。恰好我們選擇的照片都是紋理類的尼啡,平鋪之后效果還是不錯(cuò)的!
然而询微,接下來我們有遇到了新的問題:原生無法進(jìn)行浮點(diǎn)數(shù)和整數(shù)除法運(yùn)算崖瞭。發(fā)現(xiàn)這個(gè)問題的情況十分有趣,因?yàn)橐婚_始的我們并沒有發(fā)現(xiàn)渲染一屏幕的圖像需要很長時(shí)間撑毛,于是我們打算設(shè)計(jì)一段炫酷的照片過渡動(dòng)畫书聚,我們計(jì)劃每秒25幀,然后使用25幀的時(shí)間用來顯示過渡動(dòng)畫藻雌,于是我們據(jù)此進(jìn)行了一番計(jì)算雌续,得出了每一幀里每個(gè)像素填充的是來自原來照片里哪個(gè)像素的顏色,這其中涉及到了向量胯杭、矩陣變換等驯杜,但是還沒有等我們驗(yàn)證是否我們的計(jì)算正確,問題就來了:一運(yùn)行到過場(chǎng)動(dòng)畫的時(shí)候做个,程序就卡死了鸽心。我們通過注釋代碼的方式查找問題所在,最后發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:雖然我進(jìn)行了大量的浮點(diǎn)數(shù)運(yùn)算居暖,但是只要涉及到對(duì)這些運(yùn)算結(jié)果的使用(包括繪制到LCD上顽频、通過printf
輸出等),就會(huì)卡死太闺。最終糯景,我們綜合網(wǎng)絡(luò)上查找的結(jié)果,得出的結(jié)論是:需要使用特殊的配置才能利用CPU中的浮點(diǎn)數(shù)運(yùn)算,而如果沒使用這些結(jié)果莺奸,那么在編譯的過程中丑孩,這些內(nèi)容會(huì)被優(yōu)化掉而避免了卡死。
這就很尷尬了灭贷,因?yàn)槿绻苊馐褂酶↑c(diǎn)數(shù),用整數(shù)運(yùn)算代替的話略贮,運(yùn)算結(jié)果將會(huì)產(chǎn)生大量誤差甚疟,這會(huì)招致效果很糟糕,于是逃延,在苦苦搜索一番無果之后览妖,我們放棄了這個(gè)方案。事實(shí)證明揽祥,放棄這個(gè)方案是正確的讽膏,因?yàn)榧词笴PU支持浮點(diǎn)數(shù)運(yùn)算,后面仍然有問題等著我們拄丰。
我們接下來遇到的問題最讓人頭疼府树,我們被迫做了很多妥協(xié),那就是這塊屏幕(也可能是CPU)的性能實(shí)在堪憂料按!我們發(fā)現(xiàn)奄侠,繪制整個(gè)屏幕的內(nèi)容的話,需要幾乎一秒鐘時(shí)間载矿,能夠明顯看到這一屏幕的圖案是按照代碼中的遍歷順序刷新的垄潮,這導(dǎo)致我們后來放棄了其他不涉及浮點(diǎn)數(shù)但是需要重新繪制整個(gè)屏幕的過渡動(dòng)畫(如顏色漸變),被迫只能使用能最大化優(yōu)化的過渡動(dòng)畫闷盔。最終弯洗,我們選擇的動(dòng)畫是滑動(dòng)覆蓋,類似下一張照片是一幅畫卷逢勾,從兩側(cè)或一側(cè)慢慢反卷展開牡整,這種過渡動(dòng)畫的優(yōu)勢(shì)在于每個(gè)相鄰幀之間,只有一小段圖案需要重繪敏沉,這就給我們大大增加了效率果正,從而使得動(dòng)畫勉強(qiáng)有了點(diǎn)動(dòng)畫的樣子……然而,即使在此時(shí)盟迟,我們也遇到了問題:我們發(fā)現(xiàn)編譯的時(shí)候無法進(jìn)行整數(shù)除法秋泳!我們經(jīng)過一番排除之后,發(fā)現(xiàn)原因在于編譯器原生不支持硬件的整數(shù)除法攒菠。但是我們的動(dòng)畫會(huì)在25幀中進(jìn)行迫皱,我們會(huì)涉及到除以24的情況,這就沒有辦法用位移解決了。我們這次終于在網(wǎng)絡(luò)上找到了解決方案:我們需要使用一個(gè)編譯器提供的lib文件卓起,里面包含了除法的軟件實(shí)現(xiàn)和敬。然而引入之后發(fā)現(xiàn)還缺少了一個(gè)函數(shù)的定義,原來這個(gè)函數(shù)只是一個(gè)空函數(shù)戏阅,于是我們新增了一個(gè)文件特意定義了這個(gè)函數(shù)昼弟,終于通過了編譯并能夠成功使用這些功能了。
最后奕筐,還需要提一提我們一開始遇到的定時(shí)器的問題舱痘。由于決策失誤,我們一開始計(jì)劃使用RTC的方式提供定時(shí)功能离赫,通過每秒觸發(fā)若干次中斷的方式驅(qū)動(dòng)每一幀的前進(jìn)芭逝,但是范例中只提供了如何獲取BCD碼的年月日時(shí)分秒,并不能精確到毫秒渊胸,我們折騰了好久旬盯,最后還是放棄了,更換了PWM定時(shí)器翎猛。雖然一開始我們計(jì)劃的是每秒25幀胖翰,但是由于PWM的頻率要求,我們只能設(shè)置為了每秒32次中斷办成。當(dāng)然泡态,這個(gè)影響是微乎其微的,因?yàn)閷?shí)際上迂卢,由于LCD的效率低下某弦,每一幀的時(shí)間都被拉長了……
最后一個(gè)問題是按鍵中斷的抖動(dòng)問題。在我們最開始接觸按鍵中斷的時(shí)候而克,我們就已經(jīng)發(fā)現(xiàn)靶壮,不論使用哪種觸發(fā)方式,都不可避免大量毛刺员萍,導(dǎo)致依次按下會(huì)引發(fā)多次中斷腾降,這對(duì)于上一張、下一張這樣的功能是沒有問題的碎绎,因?yàn)槲覀冊(cè)诖a中進(jìn)行了判斷螃壤,如果當(dāng)前沒有播放動(dòng)畫,才會(huì)開始播放動(dòng)畫(設(shè)置相應(yīng)的變量來區(qū)分)筋帖;如果當(dāng)前正在播放過渡動(dòng)畫奸晴,那么會(huì)直接結(jié)束中斷的處理。然而日麸,對(duì)于播放和暫停而言寄啼,卻沒有做區(qū)分,而我們又在用一個(gè)按鍵上設(shè)置了播放、暫停的切換墩划,這就導(dǎo)致按下的時(shí)候很容易變成連續(xù)按下多次的效果涕刚,而使得播放和暫停失效,這肯定是無法忍受的乙帮,于是我們查找了很久如何濾波杜漠。然后,雖然我們進(jìn)行了相應(yīng)的設(shè)置蚣旱,但是完全沒有效果碑幅,依然會(huì)有多次觸發(fā)。最后塞绿,我們?cè)僖淮巍巴讌f(xié)”:通過軟件進(jìn)行過濾。具體的實(shí)現(xiàn)方法是增加一個(gè)布爾變量恤批,我們實(shí)現(xiàn)決定一幀只能接受一次按鍵中斷异吻,所以這個(gè)布爾變量每一幀的開始會(huì)被置為真,但每當(dāng)按鍵中斷到來的時(shí)候喜庞,我們就會(huì)把這個(gè)變量置為假诀浪,以此屏蔽后續(xù)的抖動(dòng)帶來的額外的中斷信號(hào)。實(shí)踐發(fā)現(xiàn)延都,效果非常好雷猪!
說完了我們遇到的問題,我們就說說我們整個(gè)項(xiàng)目的結(jié)構(gòu)晰房。我們?cè)谠O(shè)計(jì)之初的時(shí)候求摇,是這樣想的:計(jì)劃好每秒會(huì)運(yùn)轉(zhuǎn)若干幀,而每一幀都會(huì)觸發(fā)一個(gè)函數(shù)殊者,對(duì)相關(guān)的變量的值進(jìn)行刷新与境,并按需要刷新LCD。而按鍵帶來的中斷也會(huì)進(jìn)行相應(yīng)的操作猖吴。最重要的是摔刁,我們將LCD的刷新設(shè)計(jì)為根據(jù)變量的內(nèi)容來變化,換言之海蔽,我們維護(hù)了一系列變量共屈,這些變量表示了當(dāng)前LCD的狀態(tài),他們只會(huì)在每一幀到來時(shí)党窜、按鍵中斷到來的時(shí)候被修改拗引,因此對(duì)應(yīng)了LCD的內(nèi)容的改變。而每一幀再通過這些狀態(tài)的值來決定如何重繪刑然,這大大降低了開發(fā)成本寺擂,提高了效率。為什么這么說呢?因?yàn)槲覀円婚_始的時(shí)候怔软,只設(shè)計(jì)了“下一張”一個(gè)功能垦细。后來,我們計(jì)劃追加上一張挡逼、播放/暫停功能括改。對(duì)于前者,我們只要修改代表下一張照片編號(hào)的變量即可家坎,而對(duì)于后者嘱能,我們只需要限制代表當(dāng)前幀序號(hào)的變量不再繼續(xù)遞增,即可很容易控制LCD的更新虱疏。
除此以外惹骂,還有一點(diǎn)值得說的是,為了提高效率做瞪,我們特意實(shí)現(xiàn)了一個(gè)程序对粪,讀入BMP照片并生成對(duì)應(yīng)的C語言語法的靜態(tài)數(shù)組變量語句,這為我們插入圖片數(shù)據(jù)帶來了極大的便利装蓬,可見有時(shí)候著拭,為了主要的開發(fā)任務(wù)而進(jìn)行的額外的開發(fā)任務(wù)也是很有意義的。
我們?cè)偬接懸幌潞竺娴母倪M(jìn)方向牍帚。我們認(rèn)為儡遮,我們目前的項(xiàng)目的改善方向主要是性能和自定義方面的改善。對(duì)于前者暗赶,我認(rèn)為我們應(yīng)該更多的發(fā)掘一些更加酷炫但是能夠盡可能減少渲染像素的過渡動(dòng)畫鄙币。我們認(rèn)為,CPU的運(yùn)力應(yīng)該是遠(yuǎn)超過LCD的繪制效率的忆首,因此爱榔,我們可以盡可能盡量增加運(yùn)算,從而得出最少的需要重繪的像素糙及,達(dá)到性能的最優(yōu)化详幽。對(duì)于后者,我們的想法是浸锨,雖然我們目前無法從SD卡讀取圖片唇聘,但是我們可以嘗試改進(jìn)我們的小程序,能讀取任意照片并等比例縮放到小的尺寸柱搜,并計(jì)算出相應(yīng)的像素?cái)?shù)據(jù)迟郎。這樣一來,我們就可以通過很少的步驟替換程序中的照片聪蘸,甚至通過分析bin文件結(jié)構(gòu)的方式宪肖,不需要重新編譯就能進(jìn)行熱替換表制。當(dāng)然,正道還是探討如何讀取SD卡的內(nèi)容……
最后控乾,是我們經(jīng)過這次項(xiàng)目所獲得的感受么介。嵌入式開發(fā)和平時(shí)我們的程序非常不一樣,因?yàn)榍度胧较到y(tǒng)往往都是實(shí)時(shí)的蜕衡,我們的程序除了中斷以外就不會(huì)被暫停(不像其他系統(tǒng)壤短,如Windows,CPU是被分割成很多時(shí)間片來使用的)慨仿,這也是為什么幾乎所有程序都要在main函數(shù)中寫一個(gè)死循環(huán)久脯,避免程序結(jié)束。另外镰吆,嵌入式平臺(tái)的性能也非常非常有限帘撰,限制隨處可見:LCD的繪制速度、CPU的運(yùn)算速度万皿、編譯器(CPU)支持的運(yùn)算甚至到bin文件的大小等骡和,都有很多嚴(yán)苛的限制,開發(fā)的難度比普通的PC程序開發(fā)要大得多相寇,挑戰(zhàn)也更加大。
Enjoy!