Contenu connexe Similaire à Arduino 底層原始碼解析心得 (20) Arduino 底層原始碼解析心得12. 原始碼目錄介紹
改過的 Processing IDE 原始碼 (Arduino 相關)
Processing IDE 原始碼
存放編譯結果的目錄
Arduino Bootloader, Standard API 原始碼
Arduino Library 原始碼 13. app → src → processing → app → debug
處理 Arduino 韌體燒錄動作
處理 Arduino 韌體燒錄動作
處理 .ino 編譯動作
今天不談這裡 14. hardware → arduino
各種版本 bootloader 原始碼
Standard API 原始碼(共用部分)
各種周邊配套的處理器韌體
Standard API 不共用部分
定義 Board 選單及編譯參數
定義 Programmers 選單及燒錄參數
今天只談它 16. Windows 下編譯 Arduino 原始碼
下載 JAVA JDK 並完成安裝
下載 cygwin並完成安裝
◦Linux-like environment for Windows
◦安裝 cygwin 過程中, 選擇安裝下列套件
git
make, gcc-mingw, and g++
perl
unzip, zip 17. Windows 下編譯 Arduino 原始碼
下載 Apache Ant 程式
◦JAVA base 編譯器
設定 Apache Ant 和 JAVA JDK 的環境變 數
執行 cygwin, 使用 git 指令下載 Arduino 最新原始碼 22. .ino: 偽裝過的 C++
一切都是幻覺, 嚇不倒我滴!!
◦Arduino 會先將 .ino 檔轉換為 .cpp 檔再進 行編譯
◦可在 .ino 檔中使用 C++ 語法 (但不能使用 C++ standard library 內的某些物件或函式, 例如: cout, cin)
◦可使用 avr-gcc 的所有語法 23. IDE 會將 .ino 轉換成 .cpp
加入 include ” Arduino.h”
加入所有.ino 內函式的原型宣告
加入編譯指示詞#line, 重新定義與原始.ino 檔一致的 行號 24. .ino 的編譯流程概觀
Arduino IDE 會先建立一個暫存目錄
把 .ino 轉成 .cpp, 複製到暫存目錄下並 進行編譯
掃描並編譯 .ino 所 include 到的每個 library, 編譯結果輸出到暫存目錄下
所有編譯結果連結成一個 .hex 的韌體 燒錄檔
若編譯過程出錯, 會直接停止編譯並 show 出錯誤訊息 26. Standard API 概觀
Digital I/O
◦pinMode( )
◦degitalWrite( )
◦degitalRead( )
Analog I/O
◦analogReference()
◦analogRead()
◦analogWrite() 27. Standard API 概觀
Advanced I/O
◦tone ( )
◦noTone ( )
◦shiftOut( )
◦shiftIn( )
◦pulseln( ) 28. Standard API 概觀
時間函式
◦millis( )
◦micros( )
◦delay( )
◦delayMicroseconds( ) 29. Standard API 概觀
基本數學函式
◦min( )
◦max( )
◦abs( )
◦constrain( )
◦map( )
◦pow( )
◦sqrt( ) 30. Standard API 概觀
三角函式
◦sin( )
◦cos( )
◦tan( )
隨機數函式
◦randomSeed( )
◦random( ) 31. Standard API 概觀
位元操作
◦lowByte( )
◦highByte( )
◦bitRead( )
◦bitWrite( )
◦bitSet( )
◦bitClear( )
◦bit( ) 32. Standard API 概觀
中斷相關函式
◦attachInterrupt( )
◦detachInterrupt( )
◦Interrupts( )
◦noInterrupts( ) 33. Standard API 概觀
串列通訊
◦serial.begin( )
◦serial.available( )
◦serial.read( )
◦serial.write( ) 34. 從進入的觀點看 Standard API
接著重新用嘿客的眼光來分類 Standard API
直接來自 C/C++ standard library 的函式
◦pow( )
◦sqrt( )
◦sin( )
◦cos( )
◦tan( ) 36. 從進入的觀點看 Standard API
直接使用 C 語言巨集定義的函式
◦lowByte( )
◦highByte( )
◦bitRead( )
◦bitWrite( )
◦bitSet( )
◦bitClear( )
◦bit( )
Arduino.h 38. 從進入的觀點看 Standard API
硬體相關函式
◦pinMode( )
◦degitalWrite( )
◦degitalRead( )
◦analogReference()
◦analogRead()
◦analogWrite()
◦tone ( )
◦noTone ( ) 39. 從進入的觀點看 Standard API
硬體相關函式
◦shiftOut( )
◦shiftIn( )
◦pulseln( )
◦millis( )
◦micros( )
◦delay( )
◦delayMicroseconds( ) 40. 從進入的觀點看 Standard API
硬體相關函式
◦attachInterrupt( )
◦detachInterrupt( )
◦Interrupts( )
◦noInterrupts( )
◦serial.begin( )
◦serial.available( )
◦serial.read( )
◦serial.write( ) 41. 硬體相關函式實作解析
pinMode (pin, mode)
◦函式功能:
設定腳位為輸出或輸入模式
◦函式實作內容:
1. 由指定的 pin 編號來取得對應的 port
2. 由 port 找到對應的暫存器
3. 依照輸入的 mode, 修改暫存器設定 43. 硬體相關函式實作解析
digitalWrite (pin, value)
◦函式功能:
設定腳位輸出電位為 HIGH/LOW
◦函式實作內容:
1. 由指定的 pin 編號來取得對應的 port
2. 檢查指定 pin 上的硬體 PWM 是否正在被使用, 如果是則停止它
3. 由 port 找到對應的暫存器
4. 依照輸入的 value, 修改暫存器設定 45. 硬體相關函式實作解析
digitalRead (pin)
◦函式功能:
讀取指定腳位的輸入電位, 回傳 HIGH/LOW
◦函式實作內容:
1. 由指定的 pin 編號來取得對應的 port
2. 檢查指定 pin 的硬體 PWM 是否正在被使用, 如 果是則停止它
3. 由 port 找到對應的暫存器, 讀取並判斷暫存器 值, 回傳 HIGH/LOW 48. 硬體相關函式實作解析
analogRead (pin)
◦函式功能:
讀取 A/D 的電壓數值
◦函式實作內容:
1. 依照不同的 Arduino 版本, 由輸入的 pin 編號來 取得對應的 analog IN 腳位
2. 設定 analog reference 和 PIN
3. 開始做 A/D 轉換並等待完成
4. 回傳轉換後的值 51. 硬體相關函式實作解析
analogWrite (pin, val)
◦函式功能:
使用 Arduino 的 PWM 硬體送出指定 duty 的 PWM
◦函式實作內容:
1. 先將指定 pin 切成 OUTPUT
2. 再從指定 pin 編號, 找到相對應的 timer
3. 把 val 設定到 timer 暫存器 (藉由 timer 和 PWM generator 的硬體行為輸出指定 duty 的 PWM) 53. 硬體相關函式實作解析
tone (pin, frequency, duration)
◦函式功能:
從指定的 pin 送出 frequency 頻率的 pulse (duty 為 50%) 並持續一段 duration 時間
◦函式實作內容:
1. 依據輸入的 pin 編號, 初始化對應的 timer
2. 計算輸入的 frequency 並將結果填入 timer 暫 存器
3. 將 duration 與 frequency 換算成 timer counter
4. 開啟 timer 中斷, 在 ISR 中進行/停止 pulse 輸出 56. 硬體相關函式實作解析
noTone (pin)
◦函式功能:
停止指定 pin 的 pulse 輸出
◦函式實作內容:
1. 依據輸入的 pin 編號, 取得正在使用中的 timer
2. 關閉 timer 中斷
3. 將輸出 pin 的電位設定為 0 58. 硬體相關函式實作解析
shiftOut (datapin, clockpin, bitorder, val)
◦函式功能:
選用兩個 pin 作為 datapin 與 clockpin, 將 8-bit val 用指定的 bitorder 送出
◦函式實作內容:
1. 依照指定的 bitorder 將 val 從 datapin 輸出
2. 改變 clockpin 電位, 使其 high、low 各一次 60. 硬體相關函式實作解析
shiftIn (datapin, clockpin, bitorder)
◦函式功能:
選用兩個 pin 作為 datapin 與 clockpin, 用指定的 bitorder 讀取 8-bit 的 value
◦函式實作內容:
1. 設定 clockpin 電位為 high
2. 用指定的 bitorder 從 datapin 讀取 1bit value
3. 設定 clockpin 電位為 low
4. 以上三個步驟重複 8 次 62. 硬體相關函式實作解析
PulseIn (pin, state, timeout)
◦函式功能:
對指定的 pin 去計算輸入 state (high/low) 的持續時間
◦函式實作內容:
1. 由指定的 pin 編號來取得對應的 port
2. 設定 state 的 timeout 時間
3. 等待 pin 的電位變化到非指定的 state
4. 等待 pin 的電位變化到指定的 state 並開始計時
5. 等待 pin 的電位變化到非指定的 state 並停止計時
6. 回傳指定 state 持續的總時間 64. 硬體相關函式實作解析
millis ( )
◦函式功能:
回傳 Arduino 運行後所經過的時間, 單位是 millisencond, resolution 是 1ms
◦函式實作內容:
1. 關中斷
2. 讀取目前的 timer0_millis 變數值
3. 開中斷
4. 回傳其值 66. 硬體相關函式實作解析
micros ( )
◦函式功能:
回傳 Arduino 運行後所經過的時間, 單位是 microsencond, resolution 是 4us (for 16MHz)
◦函式實作內容:
1. 關中斷
2. 先讀取 timer overflow 的次數
3. 判斷此時 timer 是否 overflow, 若是則加一次
4. 讀取目前的 timer counter
5. 開中斷
6. 計算 timer overflow 次數與 counter 值
7. 將計算結果回傳 68. 硬體相關函式實作解析
delay (ms)
◦函式功能:
延長一段指定的時間, 單位是 ms, resolution 是 1ms
◦函式實作內容:
1. 調用 micros ( ) 得到目前的時間 count 值
2. 每經過 1ms 後把指定的 ms 值減一, 直到 ms 等於 0 為止 72. 硬體相關函式實作解析
interrupts ( )
◦函式功能:
打開 global 中斷
◦函式實作內容:
對 SREG 的第 7 bit (Global Interrupt Enable) 填 1
Arduino.h
interrupt.h 73. 硬體相關函式實作解析
nointerrupts ( )
◦函式功能:
關閉 global 中斷
◦函式實作內容:
對 SREG 的第 7 bit (Global Interrupt Enable) 填 0
Arduino.h
interrupt.h 78. 硬體相關函式實作解析
Serial.begin (baud)
◦函式功能:
設定串列傳輸的 buadrate
◦函式實作內容:
依據使用的 Arduino 版本, 把指定的 baudrate 設 定到 baudrate 暫存器中
啟用 RX、 TX、RX complete 中斷、Data Register Empty 中斷 83. 硬體相關函式實作解析
Serial.write ( )
◦函式功能:
寫入 data 到 COM port
◦函式實作內容:
判斷 tx buffer 是否為已滿, 如果是, 則等到有空 間時把一個 data 丟進 tx buffer, 如果不是則立 刻把一個 data 丟進去 86. 86Duino硬體配置
多功能 外部中 斷 I/O
USB 2.0
Arduino Leonardo 相容 I/O
Arduino Leonardo 相容 I/O
Arduino Leonardo 相容 I/O 88. 86Duino 軟體設計概觀
IDE 設計原則
◦不改變 Arduino IDE 原有功能的前提下, 加 入對 86Duino 的編譯及燒錄支援
移植 coreboot + SeaBIOS 做為 86Duino 的開源 BIOS
韌體使用 FreeDOS 做為 OS
◦快速開機: 通電 2 秒內 run 起使用者程式
◦中斷掛載容易實現
◦架構上最接近 Arduino 韌體架構 89. 86Duino 軟體設計概觀
採用 DJGPP 做為 86Duino 的編譯系統
◦DJGPP: 第一款出現在 x86 上的 GUN gcc
◦相容大部分 avr-gcc 的語法
◦執行於 x86 保護模式下, 無記憶體使用限制
使用 DJGPP 的問題
◦DJGPP 為 DOS 程式, 無法直接在 Linux, Mac, 64-bit Win7/Win8 下執行
◦目前解決方法: 86Duino IDE 調用 DOSBOX 執行 DJGPP 90. 軟體開發背後堅持的原則
在軟體系統每個環節, 只使用歐噴壽司 工具
◦BIOS: coreboot + SeaBIOS (open source)
◦OS: FreeDOS (open source)
◦編譯系統: DJGPP & DOSBOX (open source)
◦程式庫: DJGPP & Arduino上各種第三方開 源程式庫 (ex: Allegro)
◦IDE: Processing/Arduino IDE (open source)
◦燒錄軟體: 自己寫 (open source) 91. Arduino Standard API 在 86Duino 上的移植
直接來自 C/C++ standard library 的 API
◦DJGPP 與 avr-gcc 相容, 無需移植
直接使用 C 語言巨集定義的 API
◦直接沿用 Arduino 原始碼
平台獨立 API
◦直接沿用 Arduino 原始碼
硬體相關 API
◦重新改寫至 x86 平台 92. 硬體相關 API 在 86Duino 上的實作
pinMode( )
◦函式功能:
用以配置腳位為輸出或輸入模式
◦程式設計流程:
與 Arduino 流程相同
改填屬於 86Duino 自己的暫存器
94. 硬體相關 API 在 86Duino 上的實作
digitalWrite (pin, value)
◦函式功能:
設定腳位輸出電位為 HIGH/LOW
◦函式實作內容:
1. 檢查指定 pin 上的硬體 PWM 是否正在被使用, 如果是則停止它
2. 由 pin 編號找到對應的暫存器
3. 依照輸入的 value, 修改暫存器設定 96. digitalRead (pin)
◦函式功能:
讀取指定腳位的輸入電位, 回傳 HIGH/LOW
◦函式實作內容:
1. 檢查指定 pin 的硬體 PWM 是否正在被使用, 如 果是則停止它
2. 由 pin 編號找到對應的暫存器, 讀取並判斷暫存 器值後, 回傳 HIGH/LOW 硬體相關 API 在 86Duino 上的實作 98. 硬體相關 API 在 86Duino 上的實作
analogRead (pin)
◦函式功能:
讀取 A/D 的電壓數值
◦函式實作內容:
1. 初始化 A/D
2. 設定 PIN
3. 開始做 A/D 轉換
4. 回傳轉換後的值 100. 硬體相關 API 在 86Duino 上的實作
analogWrite (pin, val)
◦函式功能:
使用 86duino 中的 MCM 之 PWM 硬體送出指定 duty 的 PWM
◦函式實作內容:
1. 從指定 pin 編號, 找到相對應的 MCM
2. 將指定 pin 切成 PWM 輸出
3. 把 val 設定至 PWM 相關暫存器
4. Enable PWM 103. 硬體相關 API 在 86Duino 上的實作
tone (pin, frequency, duration)
◦函式功能:
從指定的 pin 送出 frequency 頻率的 pulse (duty 為 50%) 並持續一段 duration 時間
◦函式實作內容:
1. 計算輸入的 frequency 並將結果填入設定 PWM 相關暫存器
2. 將 duration 與 frequency 換算成 PWM period 的總個數
3. 開啟 MCM 中斷, 在 ISR 中進行/停止 pulse 輸出
Tone ( ) 的 PWM 主要作為 timer 用, 指定腳位上不會輸出 PWM pulse 106. 硬體相關 API 在 86Duino 上的實作
noTone (pin)
◦函式功能:
停止指定 pin 的 pulse 輸出
◦函式實作內容:
1. 關閉 MCM PWM
2. 將輸出 pin 的電位設定為 0 108. 硬體相關 API 在 86Duino 上的實作
shiftOut (datapin, clockpin, bitorder, val)
◦函式功能:
選用兩個 pin 作為 datapin 與 clockpin, 將 8-bit val 用指定的 bitorder 送出
◦函式實作內容:
內容與 Arduino 相同, 未做任何修改 109. 硬體相關 API 在 86Duino 上的實作
shiftIn (datapin, clockpin, bitorder)
◦函式功能:
選用兩個 pin 作為 datapin 與 clockpin, 用指定的 bitorder 讀取 8-bit 的 value
◦函式實作內容:
內容與 Arduino 相同, 未做任何修改 110. 硬體相關 API 在 86Duino 上的實作
PulseIn (pin, state, timeout)
◦函式功能:
對指定的 pin 去計算輸入 state (high/low) 的持續時間
◦函式實作內容:
1. 設定硬體 PWM 參數
2. 等待 pin 的電位變化到非指定的 state
3. 等待 pin 的電位變化到指定的 state 並開始計時
4. 等待 pin 的電位變化到非指定的 state 並停止計時
5. 回傳硬體 PWM 的 sample cycle
6. 計算時間並回傳數值 112. 硬體相關 API 在 86Duino 上的實作
millis ( )
◦函式功能:
回傳 86duino 運行後所經過的時間, 單位是 millisencond, resolution 是 1ms
◦函式實作內容:
回傳 timer_nowtime ( ) 的值
timer_nowtime() 函式實作內容: 在 DOS DJGPP 環境底下調用 uclock(), 取得的時間換算成 millisecond 後回傳 114. 硬體相關 API 在 86Duino 上的實作
micros ( )
◦函式功能:
回傳 86duino 運行後所經過的時間, 單位是 microsencond, resolution 是 1us
◦函式實作內容:
1. 取得 CPU clock count
2. 將 count 以 CPU 時脈換算後回傳 116. 硬體相關 API 在 86Duino 上的實作
delay (ms)
◦函式功能:
延長一段指定的時間, 單位是 ms, resolution 是 1ms
◦函式實作內容:
1. 將 timer_nowtime ( ) 得到的數值加上輸入的 ms 計算出目標時間
2. 無限等待, 直到超過/到達目標時間 118. 硬體相關 API 在 86Duino 上的實作
delayMicroseconds (us)
◦函式功能:
延長一段指定的時間, 單位是 us, resolution 是 1us
◦函式實作內容:
將目前時間減去進入此 function 的時間, 將結果 轉換成 microsecond 單位後與輸入的 us 值比較, 直到值大於 us 為止 120. 硬體相關 API 在 86Duino 上的實作
interrupts ( )
◦函式功能:
打開 global 中斷
◦函式實作內容:
對 EFLAGS 的 IF bit 填 1
Arduino.h
io.c 121. 硬體相關 API 在 86Duino 上的實作
nointerrupts ( )
◦函式功能:
關閉 global 中斷
◦函式實作內容:
對 EFLAGS 的 IF bit 填 0
Arduino.h
io.c 122. 硬體相關 API 在 86Duino 上的實作
attachInterrupt(intnum, userFunc, mode)
◦函式功能:
掛載使用者的 callback function, 並依據輸入 mode 決定觸發外部中斷的條件
◦函式實作內容:
1. 掛載 user function
2. 設定觸發 mode
3. 依據指定的 intnum, 啟用可作為外部觸發功能 的腳位 124. 硬體相關 API 在 86Duino 上的實作
detachInterrupt(intnum)
◦函式功能:
卸載指定 intnum 的外部中斷功能
◦函式實作內容:
關閉指定的 intnum 中斷 126. 硬體相關 API 在 86Duino 上的實作
Serial.begin (baud)
◦函式功能:
設定串列傳輸的 buadrate
◦函式實作內容:
1. 設定鮑率。
2. 設定傳輸資料長度、同位元檢查、停止位元。
3. 清空 TX、RXQueue。
4. 設定 timeout。 128. 硬體相關 API 在 86Duino 上的實作
Serial.available ( )
◦函式功能:
判斷 COM port 是否收到數據
◦函式實作內容:
回傳 rx queue中的 data 個數
HardwareSerial.cpp 129. 硬體相關 API 在 86Duino 上的實作
Serial.read ( )
◦函式功能:
讀取 COM port 讀到的數據
◦函式實作內容:
調用 com lib 中的 com_Read ( )
讀取一個 rx buffer 中的值並回傳
HardwareSerial.cpp 130. 硬體相關 API 在 86Duino 上的實作
Serial.write ( )
◦函式功能:
寫入 data 到 COM port
◦函式實作內容:
調用 com lib 中的 com_Write ( ), 傳送一個 8-bit 值
HardwareSerial.cpp 134. Arduino 原始碼根目錄
改過的 Processing IDE 原始碼 (Arduino 相關)
Processing IDE 原始碼
存放編譯結果的目錄
Arduino Bootloader, Standard API 原始碼
Arduino Library 原始碼 135. hardware → arduino
各種版本 bootloader 原始碼
Standard API 原始碼(共用部分)
各種周邊配套的處理器韌體
Standard API 不共用部分
定義 Board 選單及編譯參數
定義 Programmers 選單及燒錄參數 136. hardware → arduino → bootloaders
Duemilanove , Diecimila , Nano , Fio .......
Arduino NG or older w/ ATmega8
BT ATmega328 , BT ATmega168
Arduino Robot
LilyPad Arduino USB
Leonardo , Micro , Esplora
LilyPad Arduino ATmega168
Uno , Mini ATmega328 , Ethernet
Mega 2560 , Mega ADK
本次原始碼解析目標
(其它bootloaders可以此類推) 140. Arduino UNO Bootloader
Arduino UNO 使用了 optiboot,優點:
◦佔用空間只有1.5kB
◦鮑率115200,上傳程序速度較舊版 ATmega bootloader 快
◦程式碼進行了優化,運行效率較舊版提高, 並且無看門狗問題
◦支持較多的 ATmega 晶片 142. 與 Bootloader 有關的電路 Serial TX/RX 資料傳輸線
Serial DTR
(用於 reset
Arduino)
Arduino
主晶片 USB to Serial bridge 143. UNO Bootloader 程式流程
UART init
Watchdog init
是否由 RESET Pin 引起
執行 Arduino F/W
否
是
依命令把 Arduino F/W寫入 Flash
把 watchdog 設定成 16ms, 並等待系統自行 reset
reset
每接收一個字元都會 重設 watchdog
Watchdog 預設 1s (timeout 自行 reset)
TX, RX 燈號是由 Atmega16U2(USB to Serial bridge) 控制
由 serial port 接收 Host 命令
是
否
收到 exit bootloader 命令 144. Bootloader 原始碼重要細節
判斷 reset 來源, 如果不是 reset button 或 serial DTR reset, 就呼叫 appStart() 直 接執行 Arduino F/W
初始化 watchdog timer = 1s, (如果一秒內 bootloader 沒有從 serial 收到任何資 料, 將會自動跳出並執行 Arduino F/W) 145. Bootloader 原始碼重要細節
從 serial port 讀取字元 Bootloader main loop
進行 STK500 通訊協定的命令處理 (STK500協定規範請自行參考 Atmel 文件: http://www.atmel.com/Images/doc2591.pdf)
如果收到 exit 命令, 則設定 watchdog timer = 16ms, 並 呼叫 verifySpace() 等待系 統自行 reset 146. Bootloader 重要函式註解
getch ( )
◦從 serial port 讀取一字元
putch ( )
◦由 serial port 送出一字元
verifySpace ( )
◦接受並回應 STK500 命令結尾 token 148. Arduino IDE 對 Bootloader 的操作
IDE 將編譯程 式, 並透過 bootloader 將 編譯結果 上傳到 Arduino 板子上 149. Arduino IDE 對 Bootloader 的操作
IDE 上傳程式的流程
取得要燒錄的檔案所在路徑 和檔案名稱
由板子版本決定燒錄參數
取得燒錄程式路徑與檔名
執行燒錄程式 avrdude.exe
是否燒錄成 功?
回傳失敗
是 (由 avrdude.exe 的回傳值決定)
否
(IDE 這部分的原始碼等之後 的讀書會再進行詳細解析)
回傳成功
Avrdude 會自行透過 DTR 重啟 Arduino, 以進入 bootlaoder 151. Arduino Leonardo Bootloader
Arduino Leonardo 使用 caterina bootloader
◦透過 USB 直接與 PC 通訊, 省掉 USB to Serial bridge, 降低成本
◦使用 LUFA library 來進行 USB 通訊
LUFA 是一套 AVR 系列微處理機專用的通訊程式 庫, 支援各種 USB Class
caterina 只用到 CDC Class 的功能 154. Leonardo Bootloader 程式流程
HW init
Timer
LUFA init
是否有 POWER-ON reset
Detach USB 並執行 Arduino F/W
否
是
Timer 中斷設定成每 1ms 觸發 一次, 裡面處理 TX/RX LED 與 bootloader timeout
依命令把 Arduino F/W 寫入 flash, 並重置 timeout count
(timeout count > 8000)
把 timeout count 設 定為 7500
等待 timeout
timeout count 不累加
Arduino F/W 是否存 在?
timeout count ++
是
否
點滅 TX/RX LED
是否 timerout
是
否
Timer 中斷副程式
Bootloader 主程式
由 USB 接收 Host 命令, 點亮 TX/RX LED
收到 exit bootloader 命令
是
否 155. Bootloader 原始碼重要細節
如果是由 reset button 引 起的 reset, 則進入 bootloader
如果不是軟體 reset, 則直接執行 Arduino F/W
如果是 POWER-ON reset, 則直接執行 Arduino F/W 156. Bootloader 原始碼重要細節 Bootloader main loop
執行 AVR910 通訊協定命令
LUFA library 的 USB 通訊處理
超過 8 秒沒從 host 收到資 料, 則跳出 bootloader 執行 Arduino F/W
執行 Arduino F/W
切斷 USB 連結 (Arduino F/W 會自己再一次進 行 USB 連接行為) 158. Bootloader 原始碼重要細節
AVR910 通訊協定處理函式
指定 USB 資料讀取通道
從 USB 讀取字元
判斷 USB 通道是否有資料存在
如果收到 exit 命令, 則等待 0.5 秒再跳出執行 Arduino F/W
進行 AVR910 通訊協定 的命令處理 (AVR910協定請自行參 考 Atmel 文件: http://www.atmel.com/images/doc0943.pdf) 160. Bootloader 重要函式註解
StartSketch ( )
◦執行使用者的 Arduino 韌體程式
EVENT_USB_Device_ConfigurationChanged ( )
◦USB 事件處理 callback
EVENT_USB_Device_ControlRequest ( )
◦USB 事件處理 callback 161. Arduino IDE 對 Bootloader 的操作
IDE 上傳程式的流程
取得要燒錄的檔案所在路徑 和檔案名稱
由板子版本決定燒錄參數
取得燒錄程式路徑與檔名
執行燒錄程式 avrdude.exe
是否燒錄成 功?
對目前的 USB serial port 設定 1200 bps baudrate 後再關閉
等待 bootloader USB serial port 出現
Timeout?(5s)
等待 Arduino sketch USB serial port 出現
將 Arduino sketch USB serial port baudrate 改 成正常值, 並回傳燒錄 成功
Timeout?(2s)
回傳燒錄成功
回傳失敗
回傳失敗
是
否
是 (由 avrdude.exe 的回傳值決定)
否
是
否
Arduino 自行切斷 USB 再重新連線
(IDE 這部分的原始碼等之後的讀 書會再進行詳細解析)
(對 Arduino 做軟體 reset) 162. Leonardo 軟體 RESET 機制的實作
在 IDE 上傳 Arduino F/W 之前, 會先將 USB serial port 設定成 1200bps baudrate, 然後再關閉 serial port
上述行為會讓 Leonardo 上的 F/W 對一個 指定記憶體空間填入 bootkey, 然後再自己 reset
reset 後進入 bootloader, 會去判斷是否為 軟體 reset (檢查 bootkey), 如果是, 則進入 bootloader main loop 開始接收資料 171. 86Duino Bootloader 行為
86Duino Bootloader 只是開機第一個執行 的 DOS 執行檔
行為大部分與 Arduino Leonardo 相同
只有軟體 reset 可以啟動 Arduino 韌體燒 錄機制
◦Arduino Leonardo 則是軟體 reset 和 reset button 皆會啟動 Adruino 韌體燒錄機制 172. 86Duino Bootloader 行為
86Duino F/W 先整個被接收到記憶體內, 再一次寫入 flash
◦當傳輸過程出錯, 不會破壞原有韌體程式
86Duino F/W 會先燒錄至暫存空間, 成功 後再映射至韌體存放空間
◦當寫入過程出錯, 不會破壞原有韌體程式 181. 初版程式燒錄的 protocol
沒有使用 protocol (未來會新增)
初版是直接傳送特定格式的資料
TYPE
Data length
Data
1 Byte
4 Bytes
N Bytes
1: 檔案是 bootloader 2: 檔案是 user program 182. 86Duino 軟體 RESET 機制的實作
由 IDE 把 USB serial port 開啟為 1200bps baudrate 後再關閉 (與 Arduino Leonardo 相同)
86Duino 收到上述行為後, 會對一個 I/O 空間寫入特定值, 然後自己 reset
reset 後進入 bootloader, 判斷上次是否 為軟體 reset, 如果是, 則開始接收新的 F/W 185. 取得 Arduino Source Code
第一種方式:
◦使用 git 軟體
下載: http://git-scm.com/downloads
◦指令:
git clone https://github.com/arduino/Arduino.git 187. ●Arduino IDE Source Code
https://github.com/arduino/Arduino
●Cygwin
http://cygwin.com/install.html
●Sun Java JDK(Java SE Development Kit)
http://www.oracle.com/technetwork/java/javase/downloads/index.html
●Apache Ant Binary Distributions
http://ant.apache.org/bindownload.cgi
●git
http://windows.github.com/
Windows-下載相關應用程式 190. Setup steps(3)
在使用者變數裡新增
變數名稱
變數值
ANT_HOME
C:Program Filesapache-ant-1.9.2
(apache ant資料夾路徑)
JAVA_HOME
C:Program FilesJavajdk1.7.0_45
(Java JDK資料夾路徑)
194. Linux(Ubuntu)所需的應用程式
●Sun Java JDK
$sudo add-apt-repository ppa:webupd8team/java $sudo apt-get update
$sudo apt-get install oracle-java8-installer
●Apache Ant
$ sudo apt-get install ant
●avr-gcc, avr-g++, avr-libc
$ sudo apt-get install arduino
●Make
$ sudo apt-get install make
●(git)
$ sudo apt-get install git 195. Setup steps
●$ git clone git://github.com/arduino/Arduino.git
●$ cd ./Arduino/build/
●$ ant
●$ ant run 196. Mac OSX所需要的應用程式
●Java for OSX
http://support.apple.com/kb/dl1572
●(git)
–$ sudo port selfupdate
–$ sudo port install git-core 197. Setup steps
●$ git clone git://github.com/arduino/Arduino.git
●$ cd ./Arduino/build/
●$ ant
●$ ant run 201. Arduino 原始碼根目錄
改過的 Processing IDE 原始碼 (Arduino 相關)
Processing IDE 原始碼
存放編譯結果的目錄
Arduino Bootloader, Standard API 原始碼
Arduino Library 原始碼 202. App →src →processing →app
Arduino 編譯、燒錄相關程式碼
前置處理的相關程式碼, 例如: 字串轉換…
Arduino IDE 選單、按鈕功能程式碼
處理Sketch.ino 相關程式碼 209. 在下拉式選單新增選項
新增 Burn Bootloader 子選單
新增 Arduino Bootloader 項目
選項動作 callback
修改 Editor.java 的 buildToolsMenu( )
Example:
新增 86Duino Bootloader 項目
修改結果: 210. 在 hardware 下新增資料夾。 Ex: 86Duinox86
資料夾底下需要這幾個資料夾和檔案
以上這些檔案不需重新編譯 Arduino IDE,只需新 增檔案並且重新開啟 Arduino IDE 即可看到效果。 添加 Arduino 相容板
bootloaders
libraries
編譯、上傳參數 設定
standardAPI
Arduino 相容板 子的各種參數 215. 更換 IDE 的啟動 logo
直接修改或更換此檔案,並且重新編譯Arduino IDE,編 譯完成即可看到更改後圖案。 223. IDE 視窗
按下編譯按鈕主要做了三個動作: 1. 產生暫存資料夾 (Sketch.java) 2. 前處理 Sketck.ino 檔 (PdePreprocessor.java) 3. 編譯 Sketck.ino 檔 (Compiler.java) 224. App →src →processing →app
Arduino 編譯、燒錄相關程式碼
處理字串問題相關程式碼
Arduino IDE 選單、按鈕功能程式碼
處理Sketch.ino 相關程式碼 226. App →src →processing →app
Arduino 編譯、燒錄相關程式碼
處理字串問題相關程式碼
Arduino IDE 選單、按鈕功能程式碼
處理Sketch.ino 相關程式碼 227. 前處理 Sketch.ino檔案
找出 .ino 和 .pde 檔
在 .ino/.pde 檔中加入檔頭修正 (請參考第 1 次讀書會內容)
Sketch.java - preprocess( )
修改Sketch.ino 檔案 231. 找出 sketch 內呼叫的 libraries
找出 include 的 library
PdePreprocessor.java – writePrefix( ) 236. Sketch 編譯流程重點講解
Compiler.java - compileLibraries( )
讀取所有被 include 的 libraries
在 tmp 建立 library 資料夾
在 library 資料夾 底下建立 utility 資料夾
編譯 library
編譯 library 底下 utility 237. Sketch 編譯流程重點講解
Compiler.java - compileCore( )
讀取 standaAPI 路徑
讀取 variant 資料夾路徑
編譯 standaAPI
編譯 variant 資料夾下的 檔案
取得 .a 檔編譯 pattern
編譯 .a 檔
加入編譯 .a 檔參數 242. 86Duino 編譯系統
DOSBox + DJGPP
DOSBox 是一個跨平台的 DOS 模擬軟體
◦在 IDE 的路徑: buildwindowsworkDOSBox-0.74
DJGPP 是一個可在 DOS 下編譯程式的 GNU gcc
◦在 IDE 的路徑: buildwindowsworkDJGPP 251. 實作燒錄的 IDE 原始碼: app→src → processing → app → debug
本次解析目標
處理 Arduino 程式燒錄動作
處理 Arduino 程式燒錄動作 253. 燒錄設定檔的用途
Arduino IDE 在燒錄程式之前, 會從燒錄設定 檔讀取與板子相關的燒錄參數
這些設定檔中, 使用一種特定的格式, 記錄了 每塊 Arduino 板子的差異, 例如:
◦CPU時脈、燒錄的 protocol、燒錄檔案的最大 size 等等
只要推出一片新的板子, 依照指定的格式加入 新參數, 就可以直接套用到目前的 IDE, 不需 要重新編譯程式 254. boards.txt 格式: 以 Leonardo 為例
要在 Boards 選單上顯示的名稱
燒錄工具程式檔名
燒錄用的 protocol
允許燒錄的 binary 最大 size
燒錄用的 baudrate
板子名稱
燒錄 sketch 的參數設定
燒錄 bootloader 的參數設定
設定燒錄時 IDE 清空 serial data 值
設定燒錄時用 1200 baudrate 來 reset Arduino
設定 reset Arduino 後要等待 upload port 出現才可進行燒錄
這次讀書會不講燒錄 bootloader 部分 255. boards.txt 格式: 以 Leonardo 為例
板子名稱
編譯 sketch 的相關參數
Leonardo 的 CPU 型號( 燒錄會用到的參數) 256. platform.txt 的格式: 以 Leonardo 為例
在 linux 下, 燒錄工具程式的位置以及燒錄 config 檔的位置
在 windows 和 Mac 下, 燒錄工具程式的位置以及燒錄 config 檔的位置 259. IDE 執行燒錄的機制
執行 avrdude 燒錄命令
取得 sketch binary 檔路徑
Reset Arduino
使用 serial bootloader 的板子, 例如: UNO …
使用 usb bootloader 的 板子, 例如: Leonardo
詳細流程請參考第 2 次 Arduino 原始碼讀書會內容
設定正確的板子 燒錄參數
BasicUploader.java
是否需要 reset Arduino board
Uploader.java
是
否
等待 upload port 出現 260. 燒錄工具程式 Avrdude 簡介
普遍用來燒錄 Atmel AVR 的工具程式
跨多種平台, Windows, FreeBSD, linux, UNIX …
使用命令列來完成燒錄動作
參考資料
◦馬大的 Avrdude GUI 教學
http://www.coopermaa2nd.blogspot.tw/2011_06_01_archive.html
◦詳細的 avrdude 命令, 可見 AVR Tutorial:
http://www.ladyada.net/learn/avr/avrdude.html
◦Avrdude 原始碼
https://github.com/arduino/avrdude 264. BasicUploader.java → Class BasicUploader → uploadUsingPreferences()
取得目前 Serial USB port
對 upload port 設定 1200 baud 再關閉 (soft-reset Arduino)
如果設定要等待 upload port 出現
如果設定 1200 baudrate reset → 執行 USB bootloader 燒錄流程
取得 user 設定的 upload port
等待 Arduino reset 完畢, 重新 取得 upload port (見下頁) 265. BasicUploader.java → Class BasicUploader → waitForUploadPort()
Timeout 時間: 20 秒
找出 Arduino reset 後, 重 新連線的 upload port
如果找到 upload port
如果沒有找到 upload port, delay 250ms 再重新尋找
如果超過時限未找到新 upload port (win: 10 秒, 其他: 500ms), 且 user 選擇的 upload port 並未消失, 則回傳 user 選擇的 upload port
將找到的 upload
port 回傳 266. 回到 BasicUploader.java → Class BasicUploader → uploadUsingPreferences()
取得燒錄程式用的命令列pattern
設定正確的命令列參數
執行 avrdude 燒錄工具 程式(見下頁) 267. Uploader.java → Class Uploader→ executeUploadCommand ()
開新的 process 執行燒錄命令
將編譯結果顯示到 IDE 訊息框內
等待燒錄 process 執行完畢
檢查燒錄是否燒 錄成功 268. 回到 BasicUploader.java → Class BasicUploader → uploadUsingPreferences()
如果設定要等待 upload port 出現
檢查 upload port 是否出現, timeout = 2s
如果找到 upload port
將 upload port 設定 回 9600 baudrate 274. 將燒錄工具程式放到 IDE 指定資料夾下
燒錄工具程式必須依不同的平台而放在不同 的目錄(下頁說明)
在 build IDE source code 的過程, 燒錄工具程 式會自動被移到 IDE 規定的執行時期位置 ( 詳見 build.xml 中的設定) 276. Case 2: Mac OS X
將 MAC 版的 86Duino 燒錄工具 壓縮至 avr_tool.zip 裡面 278. 燒錄工具程式的一點開發經驗
在 ubuntu 遇到的問題
◦在 ubuntu 11.04 版本及之後的版本, 內建的 modem manager 會干擾 USB CDC 裝置的傳輸
◦https://bugs.launchpad.net/ubuntu/+source/modemmanager/+bug/1153632/+activity
◦這會造成燒錄程序被干擾而失敗
◦解決方式: 將 USB CDC 裝置的 PID 和 VID 加入 modem manager 的忽略清單 279. 燒錄工具程式的一點開發經驗
在 Mac OS X 遇到的問題
◦USB CDC 裝置的 Call Management Functional Descriptor 中的 最後一個 data 必須為 0x01, 否則 Mac 會認不到 USB CDC 裝 置
◦http://stackoverflow.com/questions/5009593/acessing- a-serial-to-usb-device-with-i-o-kit 280. 燒錄工具程式的一點開發經驗
在 Mac OS X 遇到的問題 (cont.)
◦USB CDC 裝置的 Configuration Descriptor 中不可 宣告 remote wakeup 功能, 否則會大大延長 Mac 辨 識此 USB CDC 裝置的時間 283. TWI 介面
TWI 全名: Two Wire Interface
Atmel 當初是為了避免侵犯 I2C 的註冊 商標, 特將此介面命名為 TWI
在傳輸資料上仍為 I2C 協定 284. 認識 I2C 介面
它是由 Philips 公司在 1980 年時期,為 了方便同一個電路板上的各個組件互相 通信,而開發出來的一種介面
全名: Inter-Integrated Circuit
I2C 介面只用兩條訊號線來連接其他元 件:
◦SDA (資料線)
◦SCL (時脈線) 286. 詳細的 I2C 通訊協定
I2C 的通訊協定較為複雜, 此次讀書會 不詳細說明, 有興趣的朋友可以參考下 面的連結內容:
◦Wiki: http://en.wikipedia.org/wiki/I%C2%B2C 288. Arduino Uno 的 TWI 輸出腳位
R3 版本後, 額外拉出 SDA 和 SCL 腳位 (與 A/D第 A4, A5 腳 位共用) 292. Wire.begin(address)
函式功能:
◦初始化 I2C 硬體
◦除了把 Arduino 當 master 之外, 也啟動 I2C slave 功能
Slave 位址使用傳入的 address 參數 (注意必需為 7-bit 位址)
當 Arduino 在 I2C Bus 上收到 address 時, 會進入 slave 模式 293. Wire.beginTransmission(address)
函式功能:
◦用來啟始 I2C master 的資料傳輸
◦address 指定外部 I2C slave 裝置的位址 (7- bit)
注意: Wire.beginTransmission() 的參數必須輸入 7-bit 形式的 slave address, 但市面上有些產品開發商只提供 8-bit slave address, 使用者必須自行轉換成正確的形式 例如: 廠商提供的 8-bit address 是 0x6A, 轉換成 7-bit 後, 值是 0x35, 然後將 0x35 填入 Wire.beginTransmission() 中 294. Wire.write(value) Wire.write(data, length) Wire.write(string)
函式功能:
◦當 Arduino 是 I2C master 時: 傳送資料給 slave 端
資料會先寫入內部 master queue, 再由中斷副程 式傳送出去
◦當 Arduino 是 I2C slave 時: 回應資料給 master 端
資料會先寫入內部 slave queue, 再由中斷副程式 傳送出去 301. Master 端
Slave 端 使用範例: 透過 I2C 連接兩片 Arduino UNO
Slave 端要設 定位址
Master 端要 送出去的位址
兩 者 要 一 樣 302. 把程式個別燒入 UNO 後, 可以在 slave 端的 serial monitor 上看到 master 端傳過來的字串 使用範例: 透過 I2C 連接兩片 Arduino UNO
圖片出處:Arduino 互動設計入門 旗標出版 305. Wire 函式庫實作架構
Wire.cpp:
◦實作對外的 API 介面
◦透過 twi.c 傳送和接收資料
twi.c:
◦操縱 ATmega TWI (I2C)硬體 暫存器, 以中斷服務常式 (ISR) 處理傳送/接收資料的行為
Wire lib
TWI lib
Arduino TWI hardware
ISR
queue 306. Wire 函式庫原始碼重要細節
設定 Arduino I2C 裝置 的 slave 位址
掛載 callback function
初始化 Arduino I2C 硬體
Wire.cpp:begin() 314. twi.c:中斷副程式
不同的 I2C 硬體狀態 (狀態暫存 器內的值)
掛載中斷副程式
中斷副程式主要運作方式:
發生某種狀態, 就做對應的事情
繼續送下一個 byte
發生錯誤的處理方式
送 stop condition
送 restart condition 316. Wire 在 86Duino 上的移植
與硬體無關的 code:
◦例如: Wire.cpp
◦可以直接拿來用, 不用修改 (或者做些把 queue buffer 加大等小改進)
與硬體相關 code:
◦例如: twi.c
◦根據硬體暫存器之間的規格差異, 移植難度也不同
◦86Duino I2C 硬體暫存器設定與 ATmega TWI 頗為 相似, 因此移植時需要修改的地方並不多 317. 86Duino twi.c:中斷副程式
Master 完成 傳送位址、 資料的狀態
取得 I2C 狀態 暫存器的值
假如要從 I2C Bus 上讀值
讀值的
操作
過程
使 I2C 硬體開始 讀值
送 stop condition
等待讀值過程結束
讀最後一筆 318. 86Duino twi.c:中斷副程式 (續)
把 1 byte 寫到 I2C BUS 上
寫值的
操作
過程
其它部分的移植方法都是相同概念:
把硬體暫存器的存取改成 Vortex86EX 的版本
送 stop condition
送 restart condition
假如是最後一筆 320. RC Servo 簡介
Radio Control Servo
Servo 是一個整合馬達、減速機、控制 驅動的系統, 可根據外部命令, 控制馬達 轉到目標角度或速度 321. RC Servo 的應用領域
在遙控模型(遙控車、 遙控飛機)上, 用來控制 遙控模型的姿態和移動 方向
RC Servo
目前也廣泛應用在機器 人領域, 做為機器人的 關節致動器
322. RC Servo 的組成
圖片出處:Arduino 開發實戰指南 機械工業出版社
基本功能: 接收主控器傳過來的目標位置信號, 與目前馬達位 置比較後, 以 PID 方式, 控制使馬達轉到目標位置
用來測量馬達 的轉動角度
用來放大直流馬達的扭力 323. 常見的 RC Servo 控制信號
主流為 PWM, 部分機器人專用伺服機則 採用 RS232 或 RS485
Arduino 的 Servo 函式庫只以 PWM 命 令的 RC Servo 為控制對象
生產公司
RC Serco 的通訊方式
Robotics(韓國)
RS232、RS485
KONDO (日本)
PWM、RS232
祥儀 (台灣)
PWM
廣營 (台灣)
PWM
TOWERPRO (大陸)
PWM 324. PWM 信號(Pulse Width Modulation)
全名為脈波寬度調變
如下圖所示:
Duty Cycle:
在一個週期中, HIGH 電位的 時間所占的比 例
Period: 即一個 PWM pulse 的週期 325. PWM 信號與 RC Servo 位置關係圖
使用 PWM duty 的寬度來控制 servo 的轉動角度:
duty 範圍通常介於 700us ~ 2300us 之間
PWM 週期通常為 20ms
圖片出處:Arduino 開發實戰指南 機械工業出版社 331. Servo 函式庫實作
實作概要:
◦使用軟體設定 I/O pin HIGH/LOW 的方式模擬 PWM 信號
◦HIGH/LOW 時間長短由 Timer 中斷所控制
◦Arduino 1 個 Timer 最多控制 12 個 PWM channels
◦超過 12 channels, 則需要用到更多 Timer (某些 Arduino 版子只能支援 12 channels)
Servo 函式庫會與 analogWrite() 衝突, 通常不 能同時使用
Servo Lib
每個 channel 結構
Timer 中斷副程式
GPIO 輸出 332. 多個 channels 的 PWM 輸出
你可能以為是這樣:
‧ ‧ ‧
Channel 1
Channel 2
Channel n
20ms (PWM period) 334. 假設平均 duty 是 1500us (Servo 中點), 則有 20ms / (1500us+ISR overhead) 12
如果所有 12 channels 都輸出最大 duty 2400us, 則 PWM period 會超過 12 x 2400us = 28.8ms
◦若你使用的 RC Servo 不允許超過 20ms 的 PWM 週期, 則需注意 Servo 函式庫此特性 335. Servo Lib 的運作架構
建構 Servo 物件
設定 channel
Timer 中斷副程式
初始化 Timer
Servo.attach()
更新 channel 的 duty 值
Servo.write()
有下一個 channel?
目前時間是 否超過 20ms?
將此 channel 設 HIGH 並且設定下一次進入 中斷的時間 = duty
設定下次進入中斷的 時間為 2us 後
設定下一次進入中斷的 時間 = 20ms – 目前時間
新的 20ms 周期開始?
將原 channel 設 LOW
否
是
否
否
是
是
宣告 Servo 336. Servo Lib 原始碼重要細節
Servo.cpp 中的 attach() 函式
若是第一次使用此 Timer
初始化Timer 中斷
取得 servo pin 對應的 timer 編號 337. Servo Lib 原始碼重要細節
Servo.cpp 中的 write() 函式
若輸入的值小於 544
把值 map 到 544 ~ 2400
調用 writeMicroseconds
把值傳給中斷副程式 中會用到的變數
把 us 轉成 Timer 單位 338. Servo Lib 原始碼重要細節
將原 channel 輸出 LOW
若是新的周期開始, 就重置 Time counter
指向下一個 channel
Servo.cpp 中的 Timer 中斷程式碼
將此 channel 設 HIGH
假如沒有可用的 channel
設定 Timer = duty
目前時間超過 20ms, 將下次中斷時間設成 2us 後
若目前時間還小於 20ms, 將下次中斷時間設成 20ms 339. 因為用軟體模擬 PWM, 所以實際輸出的 PWM duty 時間會有抖動現象 (jitter)
當使用的 PWM channel 超過 12 組, Arduino 會啟用更多組 Timer, 多個 Timer 中斷彼此干擾, 有時會惡化 PWM jitter 現象 341. Servo 函式庫在 86Duino 上的 實作改良
用新的算法在 20ms 內模擬更多 PWM channels
支援硬體 PWM 功能的 I/O pin, 直接以硬體功 能輸出 PWM 信號
以一個 Timer 中斷支持最多 45 個 PWM channels 342. 接上 32 顆 RC Servo 的 Demo
+
=
32 channels
RC Servo
Demo 影片:
86Duino ONE
Arduino sensor shield
18 channels
14 channels
https://www.youtube.com/watch?v=1H72d62AB08 344. PWM 模擬算法概要
排序所有 channels 的 duty 值
根據 duty 大小的排序, 依序在對應的 I/O pin 上輸出 PWM
同時在兩個 I/O pin 上輸出 PWM 波型
使用 Vortex86EX event trigger 功能縮小由於 interrupt latency 造成的 PWM jitter 348. Servo.cpp 中的中斷副程式(續)
若是最後的 pin, 將它設定為 LOW, 結束 PWM 週期
若是倒數第二 pin, 將自己設定為 LOW, 不啟動下一個 pin
若都以上條件都不滿足, 就把目前 pin 設定為 LOW, 把下一個 pin 設定為 HIGH 349. PWM Duty 抖動現象
由於在 Servo 函式庫裡 PWM 是用軟體模擬 的方式來實現, 所以實際輸出的 duty 會有抖 動現象
因抖動造成的誤差範圍與 CPU 特性及軟體模 擬算法有關
抖動現象 351. PWM 抖動現象的影響
在命令解析度比較高的 RC Servo 上, 會造成 servo 輸出軸實際的抖動
一般模型用的低價 RC Servo 解析度較低, 受 PWM 抖動現象的影響不大
Arduino UNO
+
KONDO
KRS4014 servo
Servo抖動 實況影片 352. 在 Arduino 和 86Duino 上只使用 1 個 servo pin, 並量測輸出的 PWM duty 與目標值的誤 差, 所測得的數據如下表所示: 各板子的 PWM Duty 抖動實測
板子
目標 duty
實際量測值
duty 誤差範圍
最小
最大
Arduino UNO
1000 us
1000.04 us
1006.42 us
約 6 ~ 7 us
Arduino Leonardo
1000 us
1000.04 us
1007.92 us
約 7 ~ 8 us
Arduino DUE
1000 us
998.200 us
998.280 us
約 1 ~ 2 us
Arduino Mega2560
1000 us
1001.12 us
1008.87 us
約 8 ~ 9 us
86Duino
1000 us
998.64 us
1001.1 us
約 1 ~ 2 us **
** 在 86Duino 有標註硬體 PWM 功能的 I/O pin 上, 誤差則是 0 353. 在 Arduino DUE / Mega2560 和 86Duino 上啟 用 45 組 servo pins, 並量測其中一個 pin 輸出 的 PWM duty 與目標值的誤差, 所測得的數據 如下表所示:
各板子的 PWM Duty 抖動實測
板子
目標 duty
實際量測值
duty 誤差範圍
最小
最大
Arduino DUE
1000 us
998.05 us
1004.68 us
約 2 ~ 5 us
Arduino Mega2560
1000 us
1001.09 us
1076.96 us
約 1 ~ 77 us
86Duino
1000 us
998.70 us
1001.31 us
約 1 ~ 2 us
Arduino 的 Servo 函式庫在超過 12 組 channels 時, 會啟用 2 組以上 Timer 中斷, 以上表格可以看出多組 Timer 中斷互相影響所造成的 jitter 惡化情形 382. Firmata @ 86Duino
•Examples
–Simple Analog Firmata
–Servo Firmata
•Firmata.h
•Firmata.cpp
•Boards.h 385. SD卡的傳輸模式
SPI
◦Chip Select, Clock, MISO, MOSI
1-bit SD data transfer mode
◦Command, Clock, DATA0
4-bit SD data transfer mode
◦Command, Clock, DATA0, DATA1, DATA2, DATA3 404. SD @ 86Duino
86Duino透過作業系統運作,SD卡為系統磁 碟
86Duino的作業系統內就有FAT檔案系統
86Duino利用stdio.h內的FILE structure來存取 檔案
86Duino使用4-bit模式與SD卡連接 413. EEPROM 簡介
EEPROM, 或寫作E2PROM, 全稱電子 抹除式可複寫唯讀記憶體, 是一種可以 通過電子方式多次複寫的半導體存儲設 備
Arduino EEPROM 大小:
◦ATmega328 : 1024 Byte
◦ATmega168, ATmega8 : 512 Byte
◦ATmega1280, ATmega2560 : 4K Byte 417. EEPROM 函式庫原始碼重要細節
呼叫 AVR EEPROM 函式庫從 EEPROM 讀值
引用 Atmel 官方提供的 AVR EEPROM 函式庫
呼叫 AVR EEPROM 函式庫寫值到EEPROM
EEPROM 是 Arduino 標準函式庫裡實作最簡單的一個 但不代表容易移植 423. 以檔案模擬 EEPROM 的問題
若 86Duino 在寫入檔案時, 被斷電或重 置…
◦輕則檔案資料遺失
◦重則整個檔案系統損毀
需要設計一個不 怕斷電與重置的 演算法…
苦主 RD
嗚嗚, 只好
重寫了~~ 424. 86Duino 兩種實現 EEPROM 等價功能的 方法
方法一: 利用板上 CMOS 記憶體
◦優點: 速度快, 程式簡單, 讀寫時不怕 CPU 斷 電
◦缺點: 容量只有 200 bytes, 移除 86Duino 板 上 RTC 電池會使資料遺失
方法二: 利用 BIOS flash 的剩餘空間
◦優點: 容量可達 16 KB, 不需電池仍可保存資 料
◦缺點: 速度較慢, 需設計容錯算法避免讀寫 時斷電的資料損毀 實作較複雜 431. EEPROM
初始化
初始化物件 SPI Flash / DRAM
SPI Flash (c)
清除 SPI Flash (c)
選擇
SPI Flash (a/b)
將 Flash 資料讀 取到 DRAM
結束
滿
未滿 432. EEPROM
write
SPI Flash(a /b)
清除 SPI Flash (a/b)
將 DRAM 內容寫回 SPI Flash (a/b) (1k)
SPI Flash (c)
清除 SPI Flash (c)
填 1-bit 0 到
SPI Flash (c)
更改 SPI Flash (a/b) 開始寫資料的位置
寫值到 DRAM
寫值到 SPI Flash(a/b)
結束
滿
滿
未滿
未滿 434. Flash 方法原始碼重要細節
宣告 SPI Flash ( a )
宣告 SPI Flash ( b )
宣告 SPI Flash ( c )
宣告 DRAM 4k
判斷 SPI Flash ( c ) 是否 滿了
判斷目前使用
SPI Flash (a/b)
選擇目前使用
SPI Flash (a/b)
將SPI Flash 填到DRAM
計算DRAM 正確的值 435. Flash 方法原始碼重要細節
判斷SPI Flash ( a / b ) 是否滿 了
Reset SPI Flash ( a / b )
將數值寫回SPI Flash ( a / b )
前面 1k 部分
寫 0 到 SPI Flash ( c )
寫入位置回到初始狀態 438. EEPROM 讀取性能測試
板子
平均讀取時間
Arduino Leonardo 實體 EEPROM
1 us
86Duino (CMOS bank)
2 us
86Duino (Flash bank)
< 0.1us
測試程式:
連續讀取200 次,
計算平均讀取時間 439. EEPROM 寫入性能測試
板子
平均寫入時間
Arduino Leonardo 實體 EEPROM
3395 us
86Duino (CMOS bank)
3 us
86Duino (Flash bank)
125 us (沒有跨 1K 邊界時的情況)
510 us (跨 1K 邊界時的情況)
測試程式:
連續寫入200 次,
計算平均寫入時間 440. Arduino 原始碼讀書會(V) : Arduino Standard Libraries 重點解析 (下)
DMP Electronics Inc. (瞻營全電子)
robotics@dmp.com.tw 442. SPI
全名: Serial Peripheral Interface
許多電子裝置都有用到它,例如:
◦SD 記憶卡
◦數位/類比轉換 IC (例如 AD7928)
◦LED 控制晶片 (例如 MAX7219)
◦還有很多 這裡無法一一列出… 443. SPI 介面
SPI 採用四條線連接主機和周邊設備, 這四條 線的名稱和用途如下:
◦SS:周邊選擇線(Slave Select),指定要連接哪一個 周邊設備。這條線也稱為 CS (Chip Select 晶片選 擇線)
圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版 444. SPI 介面 (續)
◦MOSI:從主機(master)送往周邊(slave)的資料線, (Master Output Slave Input)
◦MISO:從周邊(slave)送往主機(master)的資料線, (Master Input Slave Output)
◦SCK:序列時脈線
圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版 445. Arduno UNO 上的 SPI 腳位
有用 UNO 燒錄過 Arduino bootloader 的人,對 SPI 腳位應該 不陌生。如下圖所示:
圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版 446. UNO 上的 SPI 腳位 (電路圖)
圖片出處:Arduino UNO 官方電路圖
Reset
有 4 支腳
有 3 支腳 448. Leonardo 上的 SPI 腳位 (電路圖)
圖片出處:Arduino Leonardo 官方電路圖
SS 與 RXLED 是 共用腳位 450. SPI 的通訊格式
SPI 是一種同步全雙工的序列埠,主機和周邊之間的 資料傳遞,都要跟著時脈的 High、Low 一同進行
SPI 介面沒有強制規範時脈訊號的標準,大部分是由 SPI 介面晶片來決定使用哪一種時脈訊號格式
一般來說,SPI 可以由 CPOL 和 CPHA 來組成四種不 同的格式:
◦CPOL:時脈極性,時脈信號的電位基準
圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版 451. SPI 的通訊格式 (續)
◦CPHA:時脈相位,資料在時脈的上升階段或者下降階段被讀 取/送出
圖片出處:http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
兩個模式的 共同之處:
資料在時脈 上升或下降 階段,同時 收或送資料
SS 要先設為 Low 來啟用裝置
接收與發送 1 個 byte 的過程 452. SPI 的通訊格式 (續)
CPOL 和 CPHA 配合後,四種組合如下:
與裝置通訊前的確認事項
◦資料傳遞的格式
◦訊號傳遞位元順序 (bit order):分成高位元先傳 (MSBFRIST) 和低位元先傳 (LSBFIRST) 兩種
◦裝置所能接收的最高時脈頻率
圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版
Arduino 函 式庫中的 命名 454. SPI.begin()
函式功能:
◦初始化 SPI 硬體
設定為 Master 端
預設資料格式是 CPOL = 0,CPHA = 0
預設傳輸速度是 4MBps
預設傳遞位元順序是高位元先傳 (MSBFIRST) 458. SPI. setClockDivider()
函式功能:
◦設定傳輸速度,可輸入七種參數:
SPI_CLOCK_DIV2 :8MBps
SPI_CLOCK_DIV4 :4MBps
SPI_CLOCK_DIV8 :2MBps
SPI_CLOCK_DIV16 :1MBps
SPI_CLOCK_DIV32 :500KBps
SPI_CLOCK_DIV64 :250KBps
SPI_CLOCK_DIV128 :125KBps 460. SPI 使用範例
傳送/接收資料
Int data; void setup() { SPI.begin(); } void loop() { digitalWrite(SS, LOW); // 把 SS 設定為 LOW,開始傳送資料 SPI.transfer(0x01); // 送 0x01 給 slave data = SPI.transfer(0x00); // 讀取 slave 回傳的值 digitalWrite(SS, HIGH); // 把 SS 設定為 HIGH,結束資料傳送 Serial.println(data); delay(10); }
這裡傳送的資料是隨便給的 實際要看 slave 晶片而定 461. SPI Library 常常被引用
它們都是 SPI 介面:
◦Ethernet Library
◦TFT Library
◦SD Library
◦Wifi Library
◦SpiderL3S Library
Arduino Ethernet shield
Arduino TFT
SD shield
Arduino Wifi shield
SpiderL3S (CC3000 Wifi module) 464. SPI 函式庫原始碼重要細節
SPI.cpp:begin()
將 SS pin 設定為 output HIGH 確保等一下 enable SPI 後會是 Master 狀態
將 SPI 設定為 Master 然後 enable
SPI enable 後,SCK 和 MOSI pin 的方向需自行定義 469. SPI 在 86Duino 上的移植
移植重點:
◦將填 ATmega CPU 內的 SPI 暫存器的行為,改成填 86Duino CPU 內的 SPI 暫存器
◦其它與硬體無關的程式碼幾乎無需改動,可直接 延用 470. 86Duino spi.cpp:begin()
在 86Duino 中,SPI 是一個 PCI 裝置, 所以需要先取得 I/O address
設定 SPI 為全雙工
預設速度為 4MHz
預設傳輸格式為 SPI_MODE0
開啟 FIFO 功能
將 SS (實際上是 SPICS) 設定為 HIGH 474. 不同 Arduino 板子的 SPI 速度差異
在 Arduino UNO 上,SPI 速度只有固定那七 種
在 Arduino Due 及 86Duino 上,可允許更快 的 SPI 速度
◦在新版的 Arduino IDE 1.5.x 新增了beginTransaction() 來使用更快的 SPI 速度
但目前 Arduino 網站尚未提供關於此函式的使用文件~冏rz
◦另一種使用更快 SPI 速度的方式是直接對 setClockDivider() 輸入對應的 divider 數值 (而不是 像 SPI_CLOCK_DIV2 這樣的常數)
但使用此法需要先知道不同 Arduino 板子上 SPI 速度的計 算方式 475. 86Duino SPI clock 的計算方式
在 86Duino 上,計算 SPI 速度的公式如下:
當 div 設成 1 時,86Duino 最快可輸出的 SPI clock 速度:50MHz
100MHz / (2 div)
div 是 divider 值,範圍:1 ~ 4095 477. Arduino Ethernet shield 簡介
操作電壓:5V
控制晶片:W5100
速度:10/100 Mbps
通訊介面:SPI
WIZnet W5100 datasheet: http://www.wiznet.co.kr/UpLoad_Files/ReferenceFiles/W5100_Datasheet_v1.2.2.pdf
Ethernet shield 外觀
WIZnet W5100 478. Arduino Ethernet shield 電路
對 Arduino 的通 訊介面:SPI
輸出:LAN 的 差動信號
圖片出處:http://arduino.cc/en/uploads/Main/arduino-ethernet-shield-06-schematic.pdf 479. Arduino Ethernet shield 工作原理
OSI 模型: http://linux.vbird.org/linux_server/0110network_basic.php#route_route
將每個 frame 轉換為 0、1 信號
網路接頭
使用 IEEE 802.3 封包協議 來傳送 MAC
支援的通訊協定
軟體處理部分
硬體處理部分 480. Ethernet 使用範例:ChatServer
把 Arduino 當作 WebServer,等待 client 連線
設定網路卡實 體位址、IP位 址、子網路遮 罩和閘道器位 址
設定伺服器的 port 為 23,預 設使用 Telnet 服務
啟動乙太網路連線
啟動伺服器 485. Ethernet 資料夾
從 DHCP 伺服器取得動態 IP
從 DNS 伺服器將網址轉換成實際位址
Ethernet 初始化函式
建立 client 端,以 TCP/IP 方式連線
建立 server 端,以 TCP/IP 方式連線
以 UDP 方式連線
https://github.com/arduino/Arduino/tree/master/libraries/Ethernet 486. Ethernet / utility 資料夾
https://github.com/arduino/Arduino/tree/master/libraries/Ethernet/utility
透過 SPI 介面將命令送給 W5100 晶片
Arduino 提供的底層 API (部分遵循 BSD Socket 標準,但沒有完全相容) 487. Arduino Ethernet Library 的架構
Socket
W5100 命令
Arduino Ethernet Shield
Server
Client
UDP
Ethernet
SPI Bus
初始化函式
呼叫
取得/設定 IP、子網 路遮罩、閘道器位 址等等 489. 86Duino 內建的 Ethernet 電路
網路卡內建在 CPU 中 (含 PHY)
Vortex86EX
CPU LAN
差動信號輸出
Transformer
86Duino 底板
(PCI 裝置)
網路接頭 490. DOS 下的網路驅動程式
內建在 86Duino 韌體裡的 NDIS driver
NDIS: Network Driver Interface Specification, 參考資料:
◦http://en.wikipedia.org/wiki/Network_Driver_Interface_Specification 491. DOS 下的 Socket Library
BSD Socket 規範: http://web.mit.edu/macdev/Development/MITSupportLib/SocketsLib/Documentation/sockets.html
DOS 下常用的 Socket Library:
◦Watt32: http://www.watt-32.net/
◦SwsSock (86Duino 使用的 Socket Library): http://www.softsystem.co.uk/products/swssock.htm
◦… 492. Ethernet Library 移植方式
Socket
W5100 命令
Ethernet Shield
Server
Client
UDP
Ethernet
沿用大部分 Arduino 的 API 名稱,但內容 全部用標準 Socket API (SwsSock library 提供) 來實作
虛擬層 (NDIS to Package)
NDIS driver
86Duino LAN
Arduino Ethernet Library
86Duino Ethernet Library
移植
替換 495. WiFi shield 簡介
工作電壓:3.3V
控制晶片:AT32UC3A1256
無線模組:HDG104
無線網路通訊標準:802.11/g
資料加密方式:WEP 或 WPA2
與 Arduino 的通訊介面:SPI
WiFi shield 外觀
AT32UC3A1256 datasheet:
http://www.gaw.ru/pdf/Atmel/AVR_32/AT32UC3A0512_0256_0128_1512_1256_1128s.pdf
HDG104 datasheet:
http://datasheet.octopart.com/HDG104-DN-3-H%26D-Wireless-datasheet-11793609.pdf
AT32UC3A1256
HDG104 496. Arduino WiFi shield 電路
圖片出處:http://arduino.cc/en/uploads/Main/arduino-wifi-shield-schematic.pdf
AT32UC3A1256
電壓準位從 5V 轉成 3.3V
對 Arduino 的通訊介 面:SPI
透過內部 第二組 SPI Bus 和無線模 組通訊
無線模組
HDG104 501. WiFi 原始碼概觀
功能皆類似 Ethernet Library
https://github.com/arduino/Arduino/tree/master/libraries/WiFi
Ethernet 503. WiFi Library 在 86Duino 上的移植
移植重點:WiFi Library 與硬體相關的 只有 SPI 的部分,其它部分程式碼與硬 體無關,可以在 86Duino 上直接延用
◦將 spi_drv.cpp 存取方式換成 86Duino SPI 的 存取方式
◦上層的 wifi_drv、server_drv、WIFIClient 等等都不用動 506. GSM 簡介
GSM 全名 Global System for Mobile Communications (全球行動通訊系統)
在台灣,GSM 的頻段是 900、1800MHz,由 於在同一頻段內要讓多人使用又要抗干擾, 所以實際上傳送數據的速度只達到 9.6Kbps( 相當於看一個 200KB 的網頁要需要等 20 幾 秒) 507. GPRS 簡介
GPRS 全名 General Packet Radio Service,在 現有 GSM 技術上,加上數據交換節點(具有 處理封包的能力),配合動態分配頻段來增 加使用率,在連線人數不多的情況下,速度 可達 56Kbps ~ 100多Kbps,用來看網頁和圖 片,已經綽綽有餘。 508. Arduino GSM shield 簡介
使用 M10 晶片: 有 GSM + GPRS 功能
支援 4 種頻段: GSM850MHz GSM900MHz DCS1800MHz DCS1900MHz
支援 TCP/UDP 和 HTTP 網路通訊協定 上傳和下載的速度 最高可達 85.6Kbps
Arduino 透過 AT command 控制 GSM shield
GSM shield 外觀
圖片出處: http://arduino.cc/en/Main/ArduinoGSMShield 509. Arduino GSM shield 電路
使用 PIN2 和 PIN3 做為 Serial 腳位
增加訊號的 驅動能力
M10
圖片出處:http://arduino.cc/en/uploads/Main/arduino-gsm-shield-schematic.pdf 510. 使用 GSM shield 前應注意的事項
GSM Library 中預設使用 software serial 來傳送 AT command:
◦M10 晶片支援的 AT command: http://arduino.cc/en/uploads/Main/Quectel_M10_AT_commands. pdf
◦GSM shield 使用 PIN2 和 PIN3 做為預設的 software serial 輸 出腳位,不使用 hardware serial (PIN0 和 PIN 1) 來傳輸資料, 因為這會與 Arduino 燒錄程式的腳位衝突。
GSM shield 在使用 modem 傳送資料的時候用電量稍 大,建議接上外部電源 (700mA ~ 1A),而不要只使 用 USB 供電。
需要一張 SIM 卡才能撥打電話、發簡訊以及上網( 必須先向電信業者開通上網功能) 511. 使用 GSM shield 前應注意的事項(續)
在 Atmega 2560 上,GSM shield 需要額外的跳線:
第一步:把 PIN2 的腳往外扳
第二步:將 PIN2 與 PIN10 相連
第三步:插上 GSM shield
這是因為具有 toggle trigger 的中斷腳位,在 UNO 上是 PIN2 ,在 2560 上是 PIN10 的緣故。(在 Leonardo 上則要換成 PIN8)
圖片出處: http://arduino.cc/en/Main/ArduinoGSMShield 513. 準備
首先,拿一張可用的 SIM 卡,插入 GSM shield
圖片出處: http://arduino.cc/en/Guide/ArduinoGSMShield#toc4
用的是 mini SIM 卡,手機 用的是 micro SIM 卡,需要 使用轉卡才不 會掉出來 514. MakeVoiceCall 範例程式
使用者可透過 Serial monitor 輸入電話 號碼來撥打給對方
#include <GSM.h>
#define PINNUMBER “"
GSM gsmAccess;
GSMVoiceCall vcs;
String remoteNumber = “";
char charbuffer[20];
void setup()
{
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
如果已經在手機上已取消 PIN 碼,這 裡就不用輸入 PIN 碼
初始化 GSMAccessProvider class
初始化 GSMVoiceProvider class 515. MakeVoiceCall:setup()
… // Start GSM shield // If your SIM has PIN, pass it as a parameter of begin() in quotes while(notConnected) { if(gsmAccess.begin(PINNUMBER)==GSM_READY) notConnected = false; else { Serial.println("Not connected"); delay(1000); } } Serial.println("GSM initialized."); Serial.println("Enter phone number to call."); …
begin() 完成基本的初始化
回傳結果都沒問題,就可 以開始打電話了 516. 上傳 MakeVoiceCall 之後…
3. 撥出電話
4. 對方接通,開始通話
5. 對方掛斷電話,結束通話
1.打開 serial monitor
2.輸入要撥打的電話 (前面需要加上台灣區碼:+886),輸 入完後按 Send 517. 用 GSM shield 接電話
ReceiveVoiceCall 範例程式 (內容與 MakeVoiceCall 類似,所以 略過)
上傳後打開 serial monitor:
接到一通電話,並顯示電話號碼
雙方通話中
初始化完成,等待別人撥電話進來
Send ‘n’ 後結束通話 519. GSM Library
到 Arduino 的 GitHub 網站,看看 GSM 資料 夾內的檔案:
◦https://github.com/arduino/Arduino/blob/master/libraries/GSM