跟濤哥一起學嵌入式 第05集:一道程序改錯題,測出你的嵌入式功底
中斷,是嵌入式開發中經常使用的一個功能,也是嵌入式工程師必須要掌握的一個概念:CPU和外設通信時,一般都采用中斷的形式異步通信,可以大大提高CPU資源的利用率。而你對中斷的理解,到底有多少呢?不要急,一道程序改錯題,就可以測出你的嵌入式系統功底。
比如,我們在嵌入式ARM裸機平臺上,要實現一個MP3播放器,要求實現如下功能:當按鍵按下時,可以播放、暫停、播放下一首、上一首。為此,我們設計一個按鍵中斷服務程序,當有按鍵發生時,我們去讀取按鍵的值,然後再根據按鍵值去執行不同的操作,設計的按鍵中斷函數如下:
int keyboard_isr(int irq_num) { char *buf =(char *)malloc(512); int key_value = 0, key_value = keyboard_scan(); if(key_value == 1) { mp3_decode(buf,"xx.mp3"); sleep(10); mp3_play(buf);//play } else if(key_value == 2) mp3_pause(buf);//pause else if(key_value == 3) mp3_next(buf);//next song else if(key_value == 4) mp3_prev(buf);//prev song else { printf("UND key !"); return -1; } return 0; }
這個中斷函數差不多有8處錯誤或設計不合理的地方,先不用急著往下翻,你可以自己先思考下,看自己能找出來幾處。
(此處應有掌聲,暫停2分鐘)...
好,我們接下來繼續分析,看看這個中斷函數到底有哪些錯誤。
錯誤1:中斷關鍵字irq
中斷函數沒有使用關鍵字標記。C標準中沒有定義中斷函數,對於普通的函數調用,編譯器會幫我們自動實現函數的調用棧及出棧入棧管理,而對於中斷函數,我們知道,中斷是可以隨時發生的,編譯器無法確定你中斷函數在哪裏發生,所以就無法幫你實現調用棧。那怎麽辦?難道我們程序員自己實現?沒關系,各大編譯器廠商一般會通過增加一些關鍵字,來幫助程序員實現中斷的現場保護代碼。因此,我們可以使用interrupt或
__irq int keyboard_isr(int irq_num);
錯誤2:malloc/free
對於MP3解碼的數據,我們使用malloc函數申請了一個動態堆內存來存放,但是函數退出後沒有及時釋放,造成了內存泄露。關於函數的調用棧、調用傳參過程、程序的內存分配,如果不是很明白,可以參考《C語言嵌入式Linux高級編程》第4期:堆棧管理。
錯誤3:C標準庫
嵌入式裸機開發中,一般很少使用C標準庫的。很多嵌入式編譯廠商、IDE開發商並沒有完全實現C標準庫,或者只是實現了一部分。所以在一般在嵌入式系統中,我們可以使用一個全局數組來代替malloc申請的動態內存。
錯誤4:中斷返回值
中斷函數由於沒有獨立的函數棧,是不能有返回值的。想一想:中斷函數在哪裏被調用、何時被調用,我們全都不知道,中斷函數的返回值應該傳給誰?它會破壞當前被中斷函數的函數棧。
錯誤5:中斷傳參
同上,中斷函數也是不能傳參的,傳給誰?誰調用了它?都是未知的。如果你給中斷函數傳參,也會破壞當前被中斷函數的函數棧。所以中斷函數正確的聲明形式應該為:
__irq void keyboard_isr(void);
錯誤6:延時
避免耗時、延時、可重入函數,比如sleep、printf等。
錯誤7:短小精悍、快速反應
中斷函數要求快速響應,短小精悍。要速戰速決,快速撤離中斷現場,然後等待下一次中斷。如果你在中斷程序中,要做大量工作:播放、解碼、延時,十分不合理。這些耗時的工作應該放到中斷外去做。如Linux中的中斷處理,就放到了中斷下半部去做。
好了,以上都是我們在編寫中斷函數時必須要掌握的一些基礎知識和技能,接下來就賣個關子:想一想,上面這個函數的設計,還有什麽不合理之處?思考的越多,你就得到的越多。老司機要下車了,先帶你到這裏,剩下的就靠你了~
本文根據《C語言嵌入式Linux高級編程》視頻教程改編。《跟濤哥一起學嵌入式》,會持續跟大家分享嵌入式相關技術、學習方法、學習路線、求職面試等,有興趣可加入嵌入式技術交流群:475504428,或微信公眾號:宅學部落(armlinuxfun)。如果想系統學習嵌入式C語言進階,可關註51CTO學院,我的個人主頁:http://edu.51cto.com/lecturer/10824150.html
跟濤哥一起學嵌入式 第05集:一道程序改錯題,測出你的嵌入式功底