文章程式碼顯示

2017年12月23日 星期六

《高階》寫程式Arduino教學 - 03:解析 pinMode, digitalWrite 底層

延續《高階》寫程式Arduino教學 - 02:Arduino 定時器 輸出/入捕獲&溢位中斷 操作 TCCR1A, TCCR1B, TCNT1, TIMSK1 暫存器

Arduino IDE 怎麼看的懂 TCCR1A 這種東西呢?

我們知道像是 TCCR1A, TCNT1 這種東西都是 AVR 暫存器的名稱,而為什麼我們可以在 Arduino IDE 中直接打上暫存器的名字就修改暫存器內的値呢?

事實上這些暫存器就直接代表他所在的記憶體位置,而 bootloader 已經幫我們處理好暫存器名稱跟暫存器位置之間的關係。

以 TCCR1A 舉例來說,由前一章的第一張圖我們就發現 TCCR1A 下面有一串文字寫著 OFFSET 0x80 ,這就代表著這個暫存器的記憶體位置(基於某個基準點往上 offset 0x80 的位子)


上圖為 datasheet 中整理好的暫存器與其記憶體位置的一覽表。




在 Arduino 的底層已經定義了 TCCR1 為 _SFR_MEM8(0x80)





pinMode 以及 digitalWrite 底層

我們換看看 pinMode 以及 digitalWrite 底層是怎麼運作的







隨便抓一個 digitalWrite 第142行來看,裡面有一個 port = digitalPinToPort(pin) ,我們繼續細找它做了什麼事,但在這我們把 pin 輸入一個數字,這樣跟結果會比較好比對。

比如說 pin = 13  (也就是 digitalWrite(13 ...   )  ) ,試圖操作 pin13 腳位 (我們知道 pin13 對應到 ATMEGA328P 是 PB5)



digitalPinToPort 裡面還有 pgm_read_byte 以及 digital_pin_to_port_PGM (第 169 行)

我們先看 digital_pin_to_port_PGM 裡面是什麼



原來是存放著 PD, PB, PC (名稱?)的陣列

那麼這個陣列配合 pgm_read_byte 使用是要做什麼呢?



pgm_read_byte 裡面含有 pgm_read_byte_near



pgm_read_byte_near 裡面含有 __LPM,而 __LPM 又含有 __LPM_classic__(或__LPM_tiny__)



最後發現,哇 ... __LPM 裡面竟然是一些組語,我的探索之旅就到此結束了。但有發現裡面有 addr 的字眼出現,表示在這邊輸入已經是一個記憶體位置了

雖然最後還是沒完全搞懂他在做什麼,但由 digital_pin_to_port_PGM 第13個元素為 PB 可以知道他大概是在告訴後面的程式碼 pin13 的 PORT 是  PB

在回頭看到 digitalWrite 的第151行(圖在上一章),裡面有一個 out = portOutputRegister(port)



portOutputRegister 裡面又出現 pgm_read_word 以及 port_to_output_PGM 這種東西,看到這裡心涼了一下,感覺追到最後又要跑出組語了

還是先看一下 port_to_output_PGM 裡面是什麼



port_to_output_PGM 裡面出現了 PORTB, PORTC, PORTD 這種暫存器名稱。到這裡我就停了下來,原因在於我發現像是 digitalWrite 這種函數,說穿了就是用一個很 "通用" 的函式然後不斷的一層一層剝開檢索,去操作暫存器。

而又因為他很通用,所以函數內必須在這之中一層一層的去抽絲剝繭,才有辦法將這些地址轉變為一個數字,告訴 Arduino 到底要操作哪一個(些)暫存器

話說回來 Arduino IDE,怎麼看懂 PORTB, PORTC 這種暫存器名稱的呢










↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引