Arduino程序main函數結構分析

Arduino程序main函數結構分析

在做arduino開發的時候,很多人直接使用的是Arduino IED,打開IDE的時候,我們看到的是這樣的界面:

Arduino程序main函數結構分析

我們會看到setup和loop函數,還有幾句註釋,提示我們把運行一遍的程序放在setup中,把重複運行的部分放在loop中。但是,好多人都疑問main函數呢?程序的入口函數呢?

這就需要我們來好好看看arduino的framework了,先看一下framework的部分程序的位置:

Arduino程序main函數結構分析

其實main函數存在於Arduino核心庫中,且仍然是程序的入口。在Arduino核心庫中可見main.cpp文件:

Arduino程序main函數結構分析

這個main()就是整個arduino程序的入口。

進入main函數之後,進行了兩項初始化工作。init和initVariant。所有單片機共同的初始化部分放在init中,將不同型號單片機的初始化部分放在initVariant中,目前initVariant函數是空函數,是為以後可能的升級預留的。

init()

這個函數的實現是在wiring.c文件中。具體可看該文件:

Arduino程序main函數結構分析

先普及個知識點:

這個裡面用到了很多sbi()和cbi()函數?這函數啥意思呢?

其實就是AVR裡面的幾個簡單的彙編指令。

sbi():sbi p,b 置位i/o 位,例如:sbi(DDRB,7);等價於DDRB |= 0x80;

cbi():cbi p,b 清零i/o 位,例如:cbi(DDRB,7);等價於DDRB &= ~0x80;

init函數初始化了所有定時計數器,還初始化了AD轉換模塊,最後將管腳0和1於bootloader斷開,這樣0,1號引腳能作為普通數字引腳使用了。

init函數中還包含了大量的預編譯命令來判斷設備中是否有某個硬件,從而決定是否初始化。

在init函數中,將所有定時計數器的頻率都設為cpu頻率的64分頻。將0號定時計數器用來記錄系統運行時間,並對其他依賴時間的函數提供支持,比如millis,micros,delay等函數。所以在開發中timer0其實已經被系統佔用了,就不要在用了。也將其他定時計數器全都設定為相位修正(phase correct)PWM模式。

Arduino是如何通過timer0來精確計時的呢?

arduino用兩個變量來記錄時間,用timer0_millis來記錄毫秒數,用timer0_fract來記錄微秒,這裡比較巧妙後面解釋。假設每過a毫秒b微秒0號計數器溢出中斷,則將a毫秒累加到timer0_millis上,b微妙累加到timer0_fract上,當timer0_fract累加到1毫秒時,則將timer0_millis加1。

timer0_millis是unsigned long類型的,4個字節,最多能記錄4294967295毫秒,49天。而timer0_fract是unsigned char類型,只有1個字節,並不能記錄下1000個微秒,怎麼辦呢?實際上,timer0_fract記錄的是微秒數除以8。因為如果晶振為16MHz的話,那麼每1毫秒24微妙中斷一次,晶振為8MHz的話,每2毫秒48微妙中斷一次,將微秒數除以8並不會損失精度。

setup()

控制器通電或者復位後,就會開始執行setup()函數中的程序,並且程序只會執行一次。通常會在setup()函數中完成Arduino的初始化設置,如配置I/O口狀態和初始化串口。

loop()

setup()函數執行完畢後,就會執行loop()函數中的程序,並且loop()函數是一個死循環,裡面的程序會不斷的執行。通常loop()函數中完成主函數功能。

serialEventRun

Arduino程序main函數結構分析

這是串口事件。主要實現位置在這裡

Arduino程序main函數結構分析

每個if中,先判斷串口緩衝區是否有數據,再判斷用戶是否定義了SerialEvent函數,如果都為真的話,則調用用戶定義的SerialEvent函數。

SerialEvent函數的屬性為:

void serialEvent() __attribute__((weak));

也就是可以定義也可以不定義他,如果不定義這個函數,編譯器將會給這個函數指針賦0。

以Serial0為例。假如Serial0_available()函數定義了的話,那麼在第三個判斷條件中調用這個函數不會有問題,如果未定義的話,那麼不會進行第三個判斷,不會調用這個函數,也不會有問題。對於SerialEvent函數,如果用戶未定義它,那麼判斷條件為假,不會調用。只有用戶定義了它才會調用。

從main函數中可以看到,SerialEvent不是中斷實現的,僅僅是在loop後面調用的一個函數,因此要使用它的話,就不能在loop中再寫一個死循環了!SerialEvent要等到每次loop執行完了才會執行,因此速度會比較慢,尤其是在loop中用delay函數延遲了幾秒的,這麼長時間的延遲可能會使得串口的接收緩衝區填滿。

總結一下:通過以上程序可見,Arduino程序中編寫的setup和loop函數,都在main函數中調用了。loop的循環執行,是通過for循環實現的,且每次loop結束後,都會進行串口事件判斷,也正是因為這種設計,串口事件不能實時響應。



分享到:


相關文章: