Top Banner
Arduino 原始碼讀書會 (I) : Overview
564

Arduino 底層原始碼解析心得

Nov 22, 2014

Download

Technology

roboard

投影片講解視訊影片網址:
http://www.youtube.com/playlist?list=PLFL0ylDooClTXfy-cFbq7rV1iwP57JFaF

This slide is made by the RoBoard team of DMP Electronics Inc.:
https://www.facebook.com/roboard.fans
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Arduino 底層原始碼解析心得

Arduino 原始碼讀書會

(I) : Overview

Page 2: Arduino 底層原始碼解析心得

簡單複習 Arduino

Page 3: Arduino 底層原始碼解析心得

Arduino 硬體配置

Page 4: Arduino 底層原始碼解析心得

開啟 .ino 程式檔

Arduino IDE 的使用

Page 5: Arduino 底層原始碼解析心得

編輯程式

Page 6: Arduino 底層原始碼解析心得

編譯

Page 7: Arduino 底層原始碼解析心得

燒錄到 Arduino 上執行

Page 8: Arduino 底層原始碼解析心得

複習完畢! 收工…(毆)

Page 9: Arduino 底層原始碼解析心得

進入 Arduino 原始碼的世界

Page 10: Arduino 底層原始碼解析心得

Q:

Arduino 0023、1.0.5、1.5.2 原始碼, 要推倒哪一個?

A:

我們將以 Arduino 1.0.5 為主要推倒對象

Page 11: Arduino 底層原始碼解析心得

原始碼住哪裡?

https://github.com/arduino/Arduino

Page 12: Arduino 底層原始碼解析心得

原始碼目錄介紹

改過的 Processing IDE 原始碼 (Arduino 相關)

Processing IDE 原始碼

存放編譯結果的目錄

Arduino Bootloader, Standard API 原始碼

Arduino Library 原始碼

Page 13: Arduino 底層原始碼解析心得

app → src → processing → app → debug

處理 Arduino 韌體燒錄動作

處理 Arduino 韌體燒錄動作

處理 .ino 編譯動作

今天不談這裡

Page 14: Arduino 底層原始碼解析心得

hardware → arduino

各種版本 bootloader 原始碼

Standard API 原始碼(共用部分)

各種周邊配套的處理器韌體

Standard API 不共用部分

定義 Board 選單及編譯參數

定義 Programmers 選單及燒錄參數

今天只談它

Page 15: Arduino 底層原始碼解析心得

如何編譯 Arduino 原始碼?

http://code.google.com/p/arduino/wiki/BuildingArduino

Page 16: Arduino 底層原始碼解析心得

Windows 下編譯 Arduino 原始碼

下載 JAVA JDK 並完成安裝

下載 cygwin並完成安裝

◦ Linux-like environment for Windows

◦ 安裝 cygwin 過程中, 選擇安裝下列套件

git

make, gcc-mingw, and g++

perl

unzip, zip

Page 17: Arduino 底層原始碼解析心得

Windows 下編譯 Arduino 原始碼

下載 Apache Ant 程式

◦ JAVA base 編譯器

設定 Apache Ant 和 JAVA JDK 的環境變數

執行 cygwin, 使用 git 指令下載 Arduino

最新原始碼

Page 18: Arduino 底層原始碼解析心得

Windows 下編譯 Arduino 原始碼

使用指令 ant/ant run 開始編譯 Arduino

原始碼

Page 19: Arduino 底層原始碼解析心得

Windows 下編譯 Arduino 原始碼

編譯完成的結果會存放在 (DIR)\

\build\windows\work 資料夾內

Page 20: Arduino 底層原始碼解析心得

Linux、Mac 下編譯

Arduino 原始碼

Orz…

目前還沒時間試… (哭~~~)

Page 21: Arduino 底層原始碼解析心得

.ino 程式的編譯原理概觀

Page 22: Arduino 底層原始碼解析心得

.ino: 偽裝過的 C++

一切都是幻覺, 嚇不倒我滴!!

◦ Arduino 會先將 .ino 檔轉換為 .cpp 檔再進行編譯

◦ 可在 .ino 檔中使用 C++ 語法 (但不能使用

C++ standard library 內的某些物件或函式,

例如: cout, cin)

◦ 可使用 avr-gcc 的所有語法

Page 23: Arduino 底層原始碼解析心得

IDE 會將 .ino 轉換成 .cpp

加入 include ” Arduino.h”

加入所有.ino 內函式的原型宣告

加入編譯指示詞#line, 重新定義與原始.ino 檔一致的行號

Page 24: Arduino 底層原始碼解析心得

.ino 的編譯流程概觀

Arduino IDE 會先建立一個暫存目錄

把 .ino 轉成 .cpp, 複製到暫存目錄下並進行編譯

掃描並編譯 .ino 所 include 到的每個

library, 編譯結果輸出到暫存目錄下

所有編譯結果連結成一個 .hex 的韌體燒錄檔

若編譯過程出錯, 會直接停止編譯並 show 出錯誤訊息

Page 25: Arduino 底層原始碼解析心得

進入 Arduino Standard API

(樓還沒歪…無誤)

Page 26: Arduino 底層原始碼解析心得

Standard API 概觀

Digital I/O

◦ pinMode( )

◦ degitalWrite( )

◦ degitalRead( )

Analog I/O

◦ analogReference()

◦ analogRead()

◦ analogWrite()

Page 27: Arduino 底層原始碼解析心得

Standard API 概觀

Advanced I/O

◦ tone ( )

◦ noTone ( )

◦ shiftOut( )

◦ shiftIn( )

◦ pulseln( )

Page 28: Arduino 底層原始碼解析心得

Standard API 概觀

時間函式

◦ millis( )

◦ micros( )

◦ delay( )

◦ delayMicroseconds( )

Page 29: Arduino 底層原始碼解析心得

Standard API 概觀

基本數學函式

◦ min( )

◦ max( )

◦ abs( )

◦ constrain( )

◦ map( )

◦ pow( )

◦ sqrt( )

Page 30: Arduino 底層原始碼解析心得

Standard API 概觀

三角函式

◦ sin( )

◦ cos( )

◦ tan( )

隨機數函式

◦ randomSeed( )

◦ random( )

Page 31: Arduino 底層原始碼解析心得

Standard API 概觀

位元操作

◦ lowByte( )

◦ highByte( )

◦ bitRead( )

◦ bitWrite( )

◦ bitSet( )

◦ bitClear( )

◦ bit( )

Page 32: Arduino 底層原始碼解析心得

Standard API 概觀

中斷相關函式

◦ attachInterrupt( )

◦ detachInterrupt( )

◦ Interrupts( )

◦ noInterrupts( )

Page 33: Arduino 底層原始碼解析心得

Standard API 概觀

串列通訊

◦ serial.begin( )

◦ serial.available( )

◦ serial.read( )

◦ serial.write( )

Page 34: Arduino 底層原始碼解析心得

從進入的觀點看 Standard API

接著重新用嘿客的眼光來分類 Standard

API

直接來自 C/C++ standard library 的函式

◦ pow( )

◦ sqrt( )

◦ sin( )

◦ cos( )

◦ tan( )

Page 35: Arduino 底層原始碼解析心得

從進入的觀點看 Standard API

直接使用 C 語言巨集定義的函式

◦ min( )

◦ max( )

◦ constrain( )

◦ abs( )

Arduino.h

Page 36: Arduino 底層原始碼解析心得

從進入的觀點看 Standard API

直接使用 C 語言巨集定義的函式

◦ lowByte( )

◦ highByte( )

◦ bitRead( )

◦ bitWrite( )

◦ bitSet( )

◦ bitClear( )

◦ bit( )

Arduino.h

Page 37: Arduino 底層原始碼解析心得

從進入的觀點看 Standard API

平台獨立函式

◦ randomSeed( )

◦ random( )

◦ map( )

WMath.cpp

Page 38: Arduino 底層原始碼解析心得

從進入的觀點看 Standard API

硬體相關函式

◦ pinMode( )

◦ degitalWrite( )

◦ degitalRead( )

◦ analogReference()

◦ analogRead()

◦ analogWrite()

◦ tone ( )

◦ noTone ( )

Page 39: Arduino 底層原始碼解析心得

從進入的觀點看 Standard API

硬體相關函式

◦ shiftOut( )

◦ shiftIn( )

◦ pulseln( )

◦ millis( )

◦ micros( )

◦ delay( )

◦ delayMicroseconds( )

Page 40: Arduino 底層原始碼解析心得

從進入的觀點看 Standard API

硬體相關函式

◦ attachInterrupt( )

◦ detachInterrupt( )

◦ Interrupts( )

◦ noInterrupts( )

◦ serial.begin( )

◦ serial.available( )

◦ serial.read( )

◦ serial.write( )

Page 41: Arduino 底層原始碼解析心得

硬體相關函式實作解析

pinMode (pin, mode)

◦ 函式功能:

設定腳位為輸出或輸入模式

◦ 函式實作內容:

1. 由指定的 pin 編號來取得對應的 port

2. 由 port 找到對應的暫存器

3. 依照輸入的 mode, 修改暫存器設定

Page 42: Arduino 底層原始碼解析心得

wiring_digital.c

Page 43: Arduino 底層原始碼解析心得

硬體相關函式實作解析

digitalWrite (pin, value)

◦ 函式功能:

設定腳位輸出電位為 HIGH/LOW

◦ 函式實作內容:

1. 由指定的 pin 編號來取得對應的 port

2. 檢查指定 pin 上的硬體 PWM 是否正在被使用,

如果是則停止它

3. 由 port 找到對應的暫存器

4. 依照輸入的 value, 修改暫存器設定

Page 44: Arduino 底層原始碼解析心得

wiring_digital.c

Page 45: Arduino 底層原始碼解析心得

硬體相關函式實作解析

digitalRead (pin)

◦ 函式功能:

讀取指定腳位的輸入電位, 回傳 HIGH/LOW

◦ 函式實作內容:

1. 由指定的 pin 編號來取得對應的 port

2. 檢查指定 pin 的硬體 PWM 是否正在被使用, 如果是則停止它

3. 由 port 找到對應的暫存器, 讀取並判斷暫存器值, 回傳 HIGH/LOW

Page 46: Arduino 底層原始碼解析心得

wiring_digital.c

Page 47: Arduino 底層原始碼解析心得

硬體相關函式實作解析

analogReference (mode)

◦ 函式功能:

設定 A/D 的參考電壓

◦ 函式實作內容:

把指定的 mode 傳給內部宣告的變數

wiring_analog.c

Page 48: Arduino 底層原始碼解析心得

硬體相關函式實作解析

analogRead (pin)

◦ 函式功能:

讀取 A/D 的電壓數值

◦ 函式實作內容:

1. 依照不同的 Arduino 版本, 由輸入的 pin 編號來取得對應的 analog IN 腳位

2. 設定 analog reference 和 PIN

3. 開始做 A/D 轉換並等待完成

4. 回傳轉換後的值

Page 49: Arduino 底層原始碼解析心得

wiring_analog.c

Page 50: Arduino 底層原始碼解析心得

wiring_analog.c

Page 51: Arduino 底層原始碼解析心得

硬體相關函式實作解析

analogWrite (pin, val)

◦ 函式功能:

使用 Arduino 的 PWM 硬體送出指定 duty 的

PWM

◦ 函式實作內容:

1. 先將指定 pin 切成 OUTPUT

2. 再從指定 pin 編號, 找到相對應的 timer

3. 把 val 設定到 timer 暫存器 (藉由 timer 和

PWM generator 的硬體行為輸出指定 duty 的

PWM)

Page 52: Arduino 底層原始碼解析心得

wiring_analog.c

Page 53: Arduino 底層原始碼解析心得

硬體相關函式實作解析

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 輸出

Page 54: Arduino 底層原始碼解析心得

Tone.cpp

Page 55: Arduino 底層原始碼解析心得

Tone.cpp

Page 56: Arduino 底層原始碼解析心得

硬體相關函式實作解析

noTone (pin)

◦ 函式功能:

停止指定 pin 的 pulse 輸出

◦ 函式實作內容:

1. 依據輸入的 pin 編號, 取得正在使用中的 timer

2. 關閉 timer 中斷

3. 將輸出 pin 的電位設定為 0

Page 57: Arduino 底層原始碼解析心得

Tone.cpp

Page 58: Arduino 底層原始碼解析心得

硬體相關函式實作解析

shiftOut (datapin, clockpin, bitorder, val)

◦ 函式功能:

選用兩個 pin 作為 datapin 與 clockpin, 將 8-bit val

用指定的 bitorder 送出

◦ 函式實作內容:

1. 依照指定的 bitorder 將 val 從 datapin 輸出

2. 改變 clockpin 電位, 使其 high、low 各一次

Page 59: Arduino 底層原始碼解析心得

wiring_shift.c

Page 60: Arduino 底層原始碼解析心得

硬體相關函式實作解析

shiftIn (datapin, clockpin, bitorder)

◦ 函式功能:

選用兩個 pin 作為 datapin 與 clockpin, 用指定的

bitorder 讀取 8-bit 的 value

◦ 函式實作內容:

1. 設定 clockpin 電位為 high

2. 用指定的 bitorder 從 datapin 讀取 1bit value

3. 設定 clockpin 電位為 low

4. 以上三個步驟重複 8 次

Page 61: Arduino 底層原始碼解析心得

wiring_shift.c

Page 62: Arduino 底層原始碼解析心得

硬體相關函式實作解析

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 持續的總時間

Page 63: Arduino 底層原始碼解析心得

wiring_pulse.c

Page 64: Arduino 底層原始碼解析心得

硬體相關函式實作解析

millis ( )

◦ 函式功能:

回傳 Arduino 運行後所經過的時間, 單位是

millisencond, resolution 是 1ms

◦ 函式實作內容:

1. 關中斷

2. 讀取目前的 timer0_millis 變數值

3. 開中斷

4. 回傳其值

Page 65: Arduino 底層原始碼解析心得

wiring.c

Page 66: Arduino 底層原始碼解析心得

硬體相關函式實作解析

micros ( )

◦ 函式功能:

回傳 Arduino 運行後所經過的時間, 單位是 microsencond, resolution 是 4us (for 16MHz)

◦ 函式實作內容:

1. 關中斷

2. 先讀取 timer overflow 的次數

3. 判斷此時 timer 是否 overflow, 若是則加一次

4. 讀取目前的 timer counter

5. 開中斷

6. 計算 timer overflow 次數與 counter 值

7. 將計算結果回傳

Page 67: Arduino 底層原始碼解析心得

wiring.c

Page 68: Arduino 底層原始碼解析心得

硬體相關函式實作解析

delay (ms)

◦ 函式功能:

延長一段指定的時間, 單位是 ms, resolution 是

1ms

◦ 函式實作內容:

1. 調用 micros ( ) 得到目前的時間 count 值

2. 每經過 1ms 後把指定的 ms 值減一, 直到 ms

等於 0 為止

Page 69: Arduino 底層原始碼解析心得

wiring.c

Page 70: Arduino 底層原始碼解析心得

硬體相關函式實作解析

delayMicroseconds (us)

◦ 函式功能:

延長一段指定的時間, 單位是 us, resolution 是

1us

◦ 函式實作內容:

1. 依據 CPU 時脈與輸入的 us, 算出需要的

count 總數

2. 將 count 減一, 直到 0 為止

Page 71: Arduino 底層原始碼解析心得

wiring.c

Page 72: Arduino 底層原始碼解析心得

硬體相關函式實作解析

interrupts ( )

◦ 函式功能:

打開 global 中斷

◦ 函式實作內容:

對 SREG 的第 7 bit (Global Interrupt Enable) 填 1

Arduino.h

interrupt.h

Page 73: Arduino 底層原始碼解析心得

硬體相關函式實作解析

nointerrupts ( )

◦ 函式功能:

關閉 global 中斷

◦ 函式實作內容:

對 SREG 的第 7 bit (Global Interrupt Enable) 填 0

Arduino.h

interrupt.h

Page 74: Arduino 底層原始碼解析心得

硬體相關函式實作解析

attachInterrupt(intnum, userFunc, mode)

◦ 函式功能:

掛載使用者的 callback function, 並依據輸入

mode 決定觸發外部中斷的條件

◦ 函式實作內容:

1. 掛載 user function

2. 設定觸發 mode

3. 依據指定的 intnum, 啟用可作為外部觸發功能的腳位

Page 75: Arduino 底層原始碼解析心得

WInterrupts.c

Page 76: Arduino 底層原始碼解析心得

硬體相關函式實作解析

detachInterrupt(intnum)

◦ 函式功能:

卸載指定 intnum 的外部中斷功能

◦ 函式實作內容:

依據使用的 Arduino 版本, 關閉指定的外部中斷

Page 77: Arduino 底層原始碼解析心得

WInterrupts.c

Page 78: Arduino 底層原始碼解析心得

硬體相關函式實作解析

Serial.begin (baud)

◦ 函式功能:

設定串列傳輸的 buadrate

◦ 函式實作內容:

依據使用的 Arduino 版本, 把指定的 baudrate 設定到 baudrate 暫存器中

啟用 RX、 TX、RX complete 中斷、Data

Register Empty 中斷

Page 79: Arduino 底層原始碼解析心得

HardwareSerial.cpp

Page 80: Arduino 底層原始碼解析心得

硬體相關函式實作解析

Serial.available ( )

◦ 函式功能:

判斷 COM port 是否收到數據

◦ 函式實作內容:

回傳 rx buffer 中的 data 個數

HardwareSerial.cpp

Page 81: Arduino 底層原始碼解析心得

硬體相關函式實作解析

Serial.read ( )

◦ 函式功能:

讀取 COM port 讀到的數據

◦ 函式實作內容:

判斷 rx buffer 是否為空, 如果是則回傳 -1, 如果不是則讀取一個 data, 回傳給使用者

Page 82: Arduino 底層原始碼解析心得

HardwareSerial.cpp

Page 83: Arduino 底層原始碼解析心得

硬體相關函式實作解析

Serial.write ( )

◦ 函式功能:

寫入 data 到 COM port

◦ 函式實作內容:

判斷 tx buffer 是否為已滿, 如果是, 則等到有空間時把一個 data 丟進 tx buffer, 如果不是則立刻把一個 data 丟進去

Page 84: Arduino 底層原始碼解析心得

HardwareSerial.cpp

Page 85: Arduino 底層原始碼解析心得

下一個推倒對象: 86Duino

Page 86: Arduino 底層原始碼解析心得

86Duino硬體配置

多功能外部中斷 I/O

USB 2.0

Arduino Leonardo 相容 I/O

Arduino Leonardo 相容 I/O

Arduino Leonardo 相容 I/O

Page 87: Arduino 底層原始碼解析心得

86Duino硬體配置

MicroSD

LAN

PCI-e target

Page 88: Arduino 底層原始碼解析心得

86Duino 軟體設計概觀

IDE 設計原則

◦ 不改變 Arduino IDE 原有功能的前提下, 加入對 86Duino 的編譯及燒錄支援

移植 coreboot + SeaBIOS 做為 86Duino

的開源 BIOS

韌體使用 FreeDOS 做為 OS

◦ 快速開機: 通電 2 秒內 run 起使用者程式

◦ 中斷掛載容易實現

◦ 架構上最接近 Arduino 韌體架構

Page 89: Arduino 底層原始碼解析心得

86Duino 軟體設計概觀

採用 DJGPP 做為 86Duino 的編譯系統

◦ DJGPP: 第一款出現在 x86 上的 GUN gcc

◦ 相容大部分 avr-gcc 的語法

◦ 執行於 x86 保護模式下, 無記憶體使用限制

使用 DJGPP 的問題

◦ DJGPP 為 DOS 程式, 無法直接在 Linux, Mac,

64-bit Win7/Win8 下執行

◦ 目前解決方法: 86Duino IDE 調用 DOSBOX

執行 DJGPP

Page 90: Arduino 底層原始碼解析心得

軟體開發背後堅持的原則

在軟體系統每個環節, 只使用歐噴壽司工具

◦ 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)

Page 91: Arduino 底層原始碼解析心得

Arduino Standard API 在 86Duino

上的移植 直接來自 C/C++ standard library 的 API

◦ DJGPP 與 avr-gcc 相容, 無需移植

直接使用 C 語言巨集定義的 API

◦ 直接沿用 Arduino 原始碼

平台獨立 API

◦ 直接沿用 Arduino 原始碼

硬體相關 API

◦ 重新改寫至 x86 平台

Page 92: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

pinMode( )

◦ 函式功能:

用以配置腳位為輸出或輸入模式

◦ 程式設計流程:

與 Arduino 流程相同

改填屬於 86Duino 自己的暫存器

Page 93: Arduino 底層原始碼解析心得

wiring_digital.cpp

Page 94: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

digitalWrite (pin, value)

◦ 函式功能:

設定腳位輸出電位為 HIGH/LOW

◦ 函式實作內容:

1. 檢查指定 pin 上的硬體 PWM 是否正在被使用,

如果是則停止它

2. 由 pin 編號找到對應的暫存器

3. 依照輸入的 value, 修改暫存器設定

Page 95: Arduino 底層原始碼解析心得

wiring_digital.cpp

Page 96: Arduino 底層原始碼解析心得

digitalRead (pin)

◦ 函式功能:

讀取指定腳位的輸入電位, 回傳 HIGH/LOW

◦ 函式實作內容:

1. 檢查指定 pin 的硬體 PWM 是否正在被使用, 如果是則停止它

2. 由 pin 編號找到對應的暫存器, 讀取並判斷暫存器值後, 回傳 HIGH/LOW

硬體相關 API 在 86Duino 上的實作

Page 97: Arduino 底層原始碼解析心得

wiring_digital.cpp

Page 98: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

analogRead (pin)

◦ 函式功能:

讀取 A/D 的電壓數值

◦ 函式實作內容:

1. 初始化 A/D

2. 設定 PIN

3. 開始做 A/D 轉換

4. 回傳轉換後的值

Page 99: Arduino 底層原始碼解析心得

wiring_analog.cpp

Page 100: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

analogWrite (pin, val)

◦ 函式功能:

使用 86duino 中的 MCM 之 PWM 硬體送出指定

duty 的 PWM

◦ 函式實作內容:

1. 從指定 pin 編號, 找到相對應的 MCM

2. 將指定 pin 切成 PWM 輸出

3. 把 val 設定至 PWM 相關暫存器

4. Enable PWM

Page 101: Arduino 底層原始碼解析心得

wiring_analog.cpp

Page 102: Arduino 底層原始碼解析心得

wiring_analog.cpp

Page 103: Arduino 底層原始碼解析心得

硬體相關 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

Page 104: Arduino 底層原始碼解析心得

Tone.cpp

Page 105: Arduino 底層原始碼解析心得

Tone.cpp

Page 106: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

noTone (pin)

◦ 函式功能:

停止指定 pin 的 pulse 輸出

◦ 函式實作內容:

1. 關閉 MCM PWM

2. 將輸出 pin 的電位設定為 0

Page 107: Arduino 底層原始碼解析心得

Tone.cpp

Page 108: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

shiftOut (datapin, clockpin, bitorder, val)

◦ 函式功能:

選用兩個 pin 作為 datapin 與 clockpin, 將 8-bit val

用指定的 bitorder 送出

◦ 函式實作內容:

內容與 Arduino 相同, 未做任何修改

Page 109: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

shiftIn (datapin, clockpin, bitorder)

◦ 函式功能:

選用兩個 pin 作為 datapin 與 clockpin, 用指定的

bitorder 讀取 8-bit 的 value

◦ 函式實作內容:

內容與 Arduino 相同, 未做任何修改

Page 110: Arduino 底層原始碼解析心得

硬體相關 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. 計算時間並回傳數值

Page 111: Arduino 底層原始碼解析心得

wiring_pulse.cpp

Page 112: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

millis ( )

◦ 函式功能:

回傳 86duino 運行後所經過的時間, 單位是

millisencond, resolution 是 1ms

◦ 函式實作內容:

回傳 timer_nowtime ( ) 的值

timer_nowtime() 函式實作內容: 在 DOS DJGPP 環境底下調用 uclock(), 取得的時間換算成 millisecond 後回傳

Page 113: Arduino 底層原始碼解析心得

wiring.cpp

common.cpp

Page 114: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

micros ( )

◦ 函式功能:

回傳 86duino 運行後所經過的時間, 單位是

microsencond, resolution 是 1us

◦ 函式實作內容:

1. 取得 CPU clock count

2. 將 count 以 CPU 時脈換算後回傳

Page 115: Arduino 底層原始碼解析心得

wiring.cpp

common.cpp

Page 116: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

delay (ms)

◦ 函式功能:

延長一段指定的時間, 單位是 ms, resolution 是

1ms

◦ 函式實作內容:

1. 將 timer_nowtime ( ) 得到的數值加上輸入的

ms 計算出目標時間

2. 無限等待, 直到超過/到達目標時間

Page 117: Arduino 底層原始碼解析心得

wiring.cpp

common.cpp

Page 118: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

delayMicroseconds (us)

◦ 函式功能:

延長一段指定的時間, 單位是 us, resolution 是

1us

◦ 函式實作內容:

將目前時間減去進入此 function 的時間, 將結果轉換成 microsecond 單位後與輸入的 us 值比較,

直到值大於 us 為止

Page 119: Arduino 底層原始碼解析心得

wiring.cpp

common.cpp

Page 120: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

interrupts ( )

◦ 函式功能:

打開 global 中斷

◦ 函式實作內容:

對 EFLAGS 的 IF bit 填 1

Arduino.h

io.c

Page 121: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

nointerrupts ( )

◦ 函式功能:

關閉 global 中斷

◦ 函式實作內容:

對 EFLAGS 的 IF bit 填 0

Arduino.h

io.c

Page 122: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

attachInterrupt(intnum, userFunc, mode)

◦ 函式功能:

掛載使用者的 callback function, 並依據輸入

mode 決定觸發外部中斷的條件

◦ 函式實作內容:

1. 掛載 user function

2. 設定觸發 mode

3. 依據指定的 intnum, 啟用可作為外部觸發功能的腳位

Page 123: Arduino 底層原始碼解析心得

WInterrupts.cpp

Page 124: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

detachInterrupt(intnum)

◦ 函式功能:

卸載指定 intnum 的外部中斷功能

◦ 函式實作內容:

關閉指定的 intnum 中斷

Page 125: Arduino 底層原始碼解析心得

WInterrupts.cpp

Page 126: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

Serial.begin (baud)

◦ 函式功能:

設定串列傳輸的 buadrate

◦ 函式實作內容:

1. 設定鮑率。

2. 設定傳輸資料長度、同位元檢查、停止位元。

3. 清空 TX、RXQueue。

4. 設定 timeout。

Page 127: Arduino 底層原始碼解析心得

HardwareSerial.cpp

Page 128: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

Serial.available ( )

◦ 函式功能:

判斷 COM port 是否收到數據

◦ 函式實作內容:

回傳 rx queue中的 data 個數

HardwareSerial.cpp

Page 129: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

Serial.read ( )

◦ 函式功能:

讀取 COM port 讀到的數據

◦ 函式實作內容:

調用 com lib 中的 com_Read ( )

讀取一個 rx buffer 中的值並回傳

HardwareSerial.cpp

Page 130: Arduino 底層原始碼解析心得

硬體相關 API 在 86Duino 上的實作

Serial.write ( )

◦ 函式功能:

寫入 data 到 COM port

◦ 函式實作內容:

調用 com lib 中的 com_Write ( ), 傳送一個 8-bit

HardwareSerial.cpp

Page 131: Arduino 底層原始碼解析心得

Arduino 原始碼讀書會

(II) : Bootloader 解析

Page 132: Arduino 底層原始碼解析心得

找出 bootloader 原始碼

Page 133: Arduino 底層原始碼解析心得

讓我們再次回到熟悉的地方

https://github.com/arduino/Arduino

Page 134: Arduino 底層原始碼解析心得

Arduino 原始碼根目錄

改過的 Processing IDE 原始碼 (Arduino 相關)

Processing IDE 原始碼

存放編譯結果的目錄

Arduino Bootloader, Standard API 原始碼

Arduino Library 原始碼

Page 135: Arduino 底層原始碼解析心得

hardware → arduino

各種版本 bootloader 原始碼

Standard API 原始碼(共用部分)

各種周邊配套的處理器韌體

Standard API 不共用部分

定義 Board 選單及編譯參數

定義 Programmers 選單及燒錄參數

Page 136: Arduino 底層原始碼解析心得

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可以此類推)

Page 137: Arduino 底層原始碼解析心得

UNO Bootloader

optiboot source

code

已編譯好的各種版本 16 進位檔

Page 138: Arduino 底層原始碼解析心得

Leonardo Bootloader

caterina source

code

已編譯好的各種版本 16 進位檔

各種版本

bootloader 說明

Page 139: Arduino 底層原始碼解析心得

Serial Bootloader 解析

--- 以 UNO 為例

Page 140: Arduino 底層原始碼解析心得

Arduino UNO Bootloader

Arduino UNO 使用了 optiboot,優點:

◦ 佔用空間只有1.5kB

◦ 鮑率115200,上傳程序速度較舊版 ATmega

bootloader 快

◦ 程式碼進行了優化,運行效率較舊版提高,並且無看門狗問題

◦ 支持較多的 ATmega 晶片

Page 141: Arduino 底層原始碼解析心得

與 Bootloader 有關的電路

USB to Serial bridge Arduino 主晶片

USB 接頭

PC USB Serial

Page 142: Arduino 底層原始碼解析心得

與 Bootloader 有關的電路

Serial TX/RX 資料傳輸線

Serial DTR (用於 reset

Arduino)

Arduino

主晶片

USB to Serial

bridge

Page 143: Arduino 底層原始碼解析心得

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

命令

Page 144: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

判斷 reset 來源, 如果不是

reset button 或 serial DTR

reset, 就呼叫 appStart() 直接執行 Arduino F/W

初始化 watchdog timer = 1s, (如果一秒內 bootloader

沒有從 serial 收到任何資料, 將會自動跳出並執行

Arduino F/W)

Page 145: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

從 serial port 讀取字元

Bootloader

main loop

進行 STK500 通訊協定的命令處理

(STK500協定規範請自行參考 Atmel 文件:

http://www.atmel.com/Images/doc2591.pdf)

如果收到 exit 命令, 則設定

watchdog timer = 16ms, 並呼叫 verifySpace() 等待系統自行 reset

Page 146: Arduino 底層原始碼解析心得

Bootloader 重要函式註解

getch ( )

◦ 從 serial port 讀取一字元

putch ( )

◦ 由 serial port 送出一字元

verifySpace ( )

◦ 接受並回應 STK500 命令結尾 token

Page 147: Arduino 底層原始碼解析心得

Bootloader 重要函式註解

watchdogReset ( )

◦ Reset watchdog timer

watchdogConfig ( )

◦ 設定 watchdog timer

appStart ( )

◦ 執行使用者燒錄的 Arduino 韌體程式

Page 148: Arduino 底層原始碼解析心得

Arduino IDE 對 Bootloader 的操作

IDE 將編譯程式, 並透過

bootloader 將編譯結果

上傳到

Arduino

板子上

Page 149: Arduino 底層原始碼解析心得

Arduino IDE 對 Bootloader 的操作

IDE 上傳程式的流程

取得要燒錄的檔案所在路徑和檔案名稱

由板子版本決定燒錄參數

取得燒錄程式路徑與檔名

執行燒錄程式 avrdude.exe

是否燒錄成功?

回傳失敗

是 (由 avrdude.exe 的回傳值決定)

(IDE 這部分的原始碼等之後的讀書會再進行詳細解析)

回傳成功

Avrdude 會自行透過 DTR

重啟 Arduino, 以進入

bootlaoder

Page 150: Arduino 底層原始碼解析心得

USB Bootloader 解析

--- 以 Leonardo 為例

Page 151: Arduino 底層原始碼解析心得

Arduino Leonardo Bootloader

Arduino Leonardo 使用 caterina

bootloader

◦ 透過 USB 直接與 PC 通訊, 省掉 USB to

Serial bridge, 降低成本

◦ 使用 LUFA library 來進行 USB 通訊

LUFA 是一套 AVR 系列微處理機專用的通訊程式庫, 支援各種 USB Class

caterina 只用到 CDC Class 的功能

Page 152: Arduino 底層原始碼解析心得

與 Bootloader 有關的電路

USB 通訊線

Arduino 主晶片

Page 153: Arduino 底層原始碼解析心得

與 Bootloader 有關的電路

USB 通訊線

USB 電源輸入

USB 接頭

Page 154: Arduino 底層原始碼解析心得

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

命令

Page 155: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

如果是由 reset button 引起的 reset, 則進入

bootloader

如果不是軟體 reset, 則直接執行 Arduino F/W

如果是 POWER-ON reset, 則直接執行 Arduino F/W

Page 156: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

Bootloader

main loop

執行 AVR910 通訊協定命令

LUFA library 的 USB 通訊處理

超過 8 秒沒從 host 收到資

料, 則跳出 bootloader 執行

Arduino F/W

執行 Arduino F/W

切斷 USB 連結 (Arduino F/W 會自己再一次進行 USB 連接行為)

Page 157: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

Bootloader timer 中斷副程式,

每隔 1ms觸發執行一次

Bootloader 使用的 timerout count 變數

Page 158: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節 AVR910 通訊協定處理函式

指定 USB 資料讀取通道

從 USB 讀取字元

判斷 USB 通道是否有資料存在

如果收到 exit 命令, 則等待 0.5

秒再跳出執行 Arduino F/W

進行 AVR910 通訊協定的命令處理

(AVR910協定請自行參考 Atmel 文件:

http://www.atmel.com/im

ages/doc0943.pdf)

Page 159: Arduino 底層原始碼解析心得

Bootloader 重要函式註解

FetchNextCommandByte ( )

◦ 從 USB port 讀取一字元

WriteNextResponseByte ( )

◦ 由 USB port 送出一字元

SetupHardware ( )

◦ 硬體初始化函式

Page 160: Arduino 底層原始碼解析心得

Bootloader 重要函式註解

StartSketch ( )

◦ 執行使用者的 Arduino 韌體程式

EVENT_USB_Device_ConfigurationChanged ( )

◦ USB 事件處理 callback

EVENT_USB_Device_ControlRequest ( )

◦ USB 事件處理 callback

Page 161: Arduino 底層原始碼解析心得

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)

Page 162: Arduino 底層原始碼解析心得

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 開始接收資料

Page 163: Arduino 底層原始碼解析心得

輕鬆小品, 休息一下~~

複習 Arduino Bootloader 燒錄

Page 164: Arduino 底層原始碼解析心得

接線路(Arduino to Arduino)

Page 165: Arduino 底層原始碼解析心得

接線路(Arduino to MCU)

Page 166: Arduino 底層原始碼解析心得

開啟 ArduinoISP程式檔

Page 167: Arduino 底層原始碼解析心得

將程式上傳到Arduino板子上

Page 168: Arduino 底層原始碼解析心得

選擇板子種類

Page 169: Arduino 底層原始碼解析心得

開始燒錄bootloader

Page 170: Arduino 底層原始碼解析心得

86Duino 的 Bootloader 實作

Page 171: Arduino 底層原始碼解析心得

86Duino Bootloader 行為

86Duino Bootloader 只是開機第一個執行的 DOS 執行檔

行為大部分與 Arduino Leonardo 相同

只有軟體 reset 可以啟動 Arduino 韌體燒錄機制

◦ Arduino Leonardo 則是軟體 reset 和 reset

button 皆會啟動 Adruino 韌體燒錄機制

Page 172: Arduino 底層原始碼解析心得

86Duino Bootloader 行為

86Duino F/W 先整個被接收到記憶體內,

再一次寫入 flash

◦ 當傳輸過程出錯, 不會破壞原有韌體程式

86Duino F/W 會先燒錄至暫存空間, 成功後再映射至韌體存放空間

◦ 當寫入過程出錯, 不會破壞原有韌體程式

Page 173: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

86Duinio F/W 最大 SIZE

Bootloader timeout 時間

可燒錄的程式類型

Page 174: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

初始化 I/O port

決定 bootloader 的運作模式

判斷是否有軟體 reset

Page 175: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

初始化 USB device port

配置存放 86Duino F/W 的記憶體陣列

Page 176: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

Bootloader main

loop 開始

Page 177: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

讀取要燒錄的 86Duino

F/W 大小

Page 178: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

從 USB 接收 86Duino F/W 檔案

Page 179: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

86Duino F/W 燒錄

Page 180: Arduino 底層原始碼解析心得

Bootloader 原始碼重要細節

若是執行 Bootloader 燒錄則 reboot, 反之則執行 86Duino F/W

Page 181: Arduino 底層原始碼解析心得

初版程式燒錄的 protocol

沒有使用 protocol (未來會新增)

初版是直接傳送特定格式的資料

TYPE Data length Data

1 Byte 4 Bytes N Bytes

1: 檔案是 bootloader

2: 檔案是 user program

Page 182: Arduino 底層原始碼解析心得

86Duino 軟體 RESET 機制的實作

由 IDE 把 USB serial port 開啟為

1200bps baudrate 後再關閉 (與 Arduino

Leonardo 相同)

86Duino 收到上述行為後, 會對一個 I/O

空間寫入特定值, 然後自己 reset

reset 後進入 bootloader, 判斷上次是否為軟體 reset, 如果是, 則開始接收新的

F/W

Page 183: Arduino 底層原始碼解析心得

Arduino 原始碼讀書會(III) :

Arduino IDE 解析

Page 184: Arduino 底層原始碼解析心得

Arduino IDE 的編譯

Page 185: Arduino 底層原始碼解析心得

取得 Arduino Source Code

第一種方式:

◦ 使用 git 軟體

下載: http://git-scm.com/downloads

◦ 指令: git clone https://github.com/arduino/Arduino.git

Page 186: Arduino 底層原始碼解析心得

取得 Arduino Source Code

第二種方式:

◦ 直接下載 source code

點選這裡可直接下載

Page 187: Arduino 底層原始碼解析心得

● 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-下載相關應用程式

Page 188: Arduino 底層原始碼解析心得

Setup steps(1)

安裝 Cygwin

安裝 Java JDK

解壓縮apache-ant-x.x.x-bin.zip至ProgrmFiles

Page 189: Arduino 底層原始碼解析心得

Setup steps(2)

控制台\所有控制台項目\系統\進階系統設定\

進階\環境變數

Page 190: Arduino 底層原始碼解析心得

Setup steps(3)

在使用者變數裡新增

變數名稱 變數值

ANT_HOME C:\Program Files\apache-ant-1.9.2 (apache ant資料夾路徑)

JAVA_HOME C:\Program Files\Java\jdk1.7.0_45 (Java JDK資料夾路徑)

Page 191: Arduino 底層原始碼解析心得

Setup steps(4)

在系統變數中

◦ 將apache-ant中的bin資料夾路徑加入Path變數值中

Page 192: Arduino 底層原始碼解析心得

Setup steps(5)

開啟cmd,移至Arduino底下的build

>ant

>ant run

Page 193: Arduino 底層原始碼解析心得

Setup steps(6)

將…\build\windows中的jre.zip裡面的java

資料夾解壓縮到…\build\windows\work

Page 194: Arduino 底層原始碼解析心得

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

Page 195: Arduino 底層原始碼解析心得

Setup steps

● $ git clone git://github.com/arduino/Arduino.git

● $ cd ./Arduino/build/

● $ ant

● $ ant run

Page 196: Arduino 底層原始碼解析心得

Mac OSX所需要的應用程式 ● Java for OSX

http://support.apple.com/kb/dl1572

● (git)

– $ sudo port selfupdate

– $ sudo port install git-core

Page 197: Arduino 底層原始碼解析心得

Setup steps

● $ git clone git://github.com/arduino/Arduino.git

● $ cd ./Arduino/build/

● $ ant

● $ ant run

Page 198: Arduino 底層原始碼解析心得

開胃小菜:

Arduino IDE Hacking Tips

Page 199: Arduino 底層原始碼解析心得

讓我們再次回到熟悉的地方

https://github.com/arduino/Arduino

Page 200: Arduino 底層原始碼解析心得

切換到 1.5.x 分支

1.5.x 支援 Arduino Due, Yun, …

Page 201: Arduino 底層原始碼解析心得

Arduino 原始碼根目錄

改過的 Processing IDE 原始碼 (Arduino 相關)

Processing IDE 原始碼

存放編譯結果的目錄

Arduino Bootloader, Standard API 原始碼

Arduino Library 原始碼

Page 202: Arduino 底層原始碼解析心得

App →src →processing →app

Arduino 編譯、燒錄相關程式碼

前置處理的相關程式碼, 例如: 字串轉換…

Arduino IDE 選單、按鈕功能程式碼

處理Sketch.ino 相關程式碼

Page 203: Arduino 底層原始碼解析心得

App →src →processing →app →preproc

處理字串問題程式碼

Page 204: Arduino 底層原始碼解析心得

App→src →processing→app→ debug

Arduino 編譯相關程式碼

Page 205: Arduino 底層原始碼解析心得

語言設定

如何客製化呢?

Page 206: Arduino 底層原始碼解析心得

App →src →processing →app

語言設定檔的位置

中英說明文件 (修改後沒有效果)

英轉中 編碼轉換設定檔

Page 207: Arduino 底層原始碼解析心得

修改中文顯示訊息

Resources_zh_tw.po Resources_zh_tw.properties

英文字串 Open… 替換成 開啟… (unicode 編碼)

Page 208: Arduino 底層原始碼解析心得

修改範例

修改後重新編譯 IDE 即可看到結果

中文: 我愛Fablab (unicode)

Page 209: Arduino 底層原始碼解析心得

在下拉式選單新增選項

新增 Burn Bootloader 子選單

新增 Arduino Bootloader 項目

選項動作 callback

修改 Editor.java 的 buildToolsMenu( )

Example:

新增 86Duino Bootloader 項目

修改結果:

Page 210: Arduino 底層原始碼解析心得

在 \hardware 下新增資料夾。 Ex: 86Duino\x86

資料夾底下需要這幾個資料夾和檔案

以上這些檔案不需重新編譯 Arduino IDE,只需新增檔案並且重新開啟 Arduino IDE 即可看到效果。

添加 Arduino 相容板

bootloaders

libraries

編譯、上傳參數設定

standardAPI

Arduino 相容板子的各種參數

Page 211: Arduino 底層原始碼解析心得

添加 Arduino 相容板

boards.txt platform.txt

設定板子名稱

設定板子相關參數

設定板子選單名稱

Page 212: Arduino 底層原始碼解析心得

添加 Arduino 相容板

尚未添加板子前

boards 選單

添加後的 boards 選單

Page 213: Arduino 底層原始碼解析心得

存放 UI 外觀圖檔及設定檔的路徑:\build\shared\lib\theme

修改 UI 外觀顯示

Page 214: Arduino 底層原始碼解析心得

UI 設定檔

theme.txt 文件內容

Page 215: Arduino 底層原始碼解析心得

更換 IDE 的啟動 logo

直接修改或更換此檔案,並且重新編譯Arduino IDE,編譯完成即可看到更改後圖案。

Page 216: Arduino 底層原始碼解析心得

修改 IDE 顯示版本號

修改此字串並重新編譯, 即可更換 IDE 顯示的版本號

build.xml 內容

Page 217: Arduino 底層原始碼解析心得

修改 IDE 顯示版本號

修改的版本號:~1.5.4^^

Page 218: Arduino 底層原始碼解析心得

修改 IDE 視窗左上角小圖示

打開 Papplet.java 檔

Page 219: Arduino 底層原始碼解析心得

修改 IDE 視窗左上角小圖示

把 ICON_IMAGE 陣列內容換成想換的小圖示, 格式必須為 GIF

Page 220: Arduino 底層原始碼解析心得

修改 IDE 視窗左上角小圖示

IDE 視窗 Serial monitor 視窗

修改結果:

修改後重新編譯 IDE 即可看到效果

Page 221: Arduino 底層原始碼解析心得

第一道主菜:

Sketch 編譯機制

Page 222: Arduino 底層原始碼解析心得

Arduino 編譯流程

Page 223: Arduino 底層原始碼解析心得

IDE 視窗 按下編譯按鈕主要做了三個動作:

1. 產生暫存資料夾

(Sketch.java)

2. 前處理 Sketck.ino 檔

(PdePreprocessor.java)

3. 編譯 Sketck.ino 檔

(Compiler.java)

Page 224: Arduino 底層原始碼解析心得

App →src →processing →app

Arduino 編譯、燒錄相關程式碼

處理字串問題相關程式碼

Arduino IDE 選單、按鈕功能程式碼

處理Sketch.ino 相關程式碼

Page 225: Arduino 底層原始碼解析心得

產生存放編譯結果的暫存資料夾

處理 Sketch.ino檔 並開始編譯

Editor.java – DefaultPresentHandler( )

Page 226: Arduino 底層原始碼解析心得

App →src →processing →app

Arduino 編譯、燒錄相關程式碼

處理字串問題相關程式碼

Arduino IDE 選單、按鈕功能程式碼

處理Sketch.ino 相關程式碼

Page 227: Arduino 底層原始碼解析心得

前處理 Sketch.ino檔案

找出 .ino 和 .pde 檔

在 .ino/.pde 檔中加入檔頭修正

(請參考第 1 次讀書會內容)

Sketch.java - preprocess( )

修改Sketch.ino 檔案

Page 228: Arduino 底層原始碼解析心得

App →src →processing →app →preproc

Sketch 前處理的程式碼

Page 229: Arduino 底層原始碼解析心得

前處理 Sketch.ino檔案 PdePreprocessor.java – writeProgram( )

在 .ino/.pde 檔中加入

#include “Arduino.h ”

加入函式原型宣告

加入行號修正

Page 230: Arduino 底層原始碼解析心得

原始 skecth

被 IDE 改寫的 skecth

Sketch 處理前後之差異

Page 231: Arduino 底層原始碼解析心得

找出 sketch 內呼叫的 libraries

找出 include 的 library

PdePreprocessor.java – writePrefix( )

Page 232: Arduino 底層原始碼解析心得

App→src →processing→app→ debug

Arduino 編譯相關程式碼

Page 233: Arduino 底層原始碼解析心得

Sketch 編譯流程重點講解

取得暫存資料夾路徑

取得libraries路徑

Compiler.java - compile( )

Page 234: Arduino 底層原始碼解析心得

Sketch 編譯流程重點講解

編譯Sketch.ino

編譯libraries

編譯standardAPI

產生.elf檔

產生.eep檔

產生.hex 執行檔 並結束編譯

Page 235: Arduino 底層原始碼解析心得

Sketch 編譯流程重點講解

Compiler.java - compileSketch( )

呼叫 CompileFiles 執行 sketch 編譯

Page 236: Arduino 底層原始碼解析心得

Sketch 編譯流程重點講解

Compiler.java - compileLibraries( )

讀取所有被 include 的 libraries

在 tmp 建立 library 資料夾

在 library 資料夾 底下建立 utility 資料夾

編譯 library

編譯 library 底下 utility

Page 237: Arduino 底層原始碼解析心得

Sketch 編譯流程重點講解 Compiler.java - compileCore( )

讀取 standaAPI 路徑

讀取 variant 資料夾路徑

編譯 standaAPI

編譯 variant 資料夾下的檔案

取得 .a 檔編譯 pattern

編譯 .a 檔

加入編譯 .a 檔參數

Page 238: Arduino 底層原始碼解析心得

產生編譯.s檔案的 gcc 命令 執行avrgcc

Compiler.java - compileFiles( )

Sketch 編譯流程重點講解

Page 239: Arduino 底層原始碼解析心得

Sketch 編譯流程重點講解

產生編譯 .c檔案的 gcc 命令

產生編譯 .cpp檔案的 gcc 命令

執行 avrgcc

執行 avrgcc

Page 240: Arduino 底層原始碼解析心得

Sketch 編譯流程重點講解

將編譯結果顯示到 IDE 訊息框內

開新的 process 執行avrgcc 命令

Compiler.java - execAsynchronously( )

Page 241: Arduino 底層原始碼解析心得

配菜:

加入 86Duino 程式編譯

對 IDE 所做的修改

Page 242: Arduino 底層原始碼解析心得

86Duino 編譯系統

DOSBox + DJGPP

DOSBox 是一個跨平台的 DOS 模擬軟體

◦ 在 IDE 的路徑: \build\windows\work\DOSBox-0.74

DJGPP 是一個可在 DOS 下編譯程式的 GNU

gcc

◦ 在 IDE 的路徑: \build\windows\work\DJGPP

Page 243: Arduino 底層原始碼解析心得

86Duino 編譯流程

Page 244: Arduino 底層原始碼解析心得

86Duino原始碼重要細節

如果使用者選的板子是 86Duino, 則跳到

duinocompiler.java 編譯

Compiler.java - compile( )

Page 245: Arduino 底層原始碼解析心得

86Duino原始碼重要細節

設定所需程式的路徑參數

DuinoCompiler.java - compile( )

Page 246: Arduino 底層原始碼解析心得

86Duino原始碼重要細節

Makefile 檔案路徑、編譯出檔案路徑設定

DuinoCompiler.java - compile( )

Page 247: Arduino 底層原始碼解析心得

86Duino原始碼重要細節

Dosbox config設定

Makefile 設定

設定 DOSBox 執行命令

將編譯訊息寫到MESSAGE.TXT

DuinoCompiler.java - compile( )

Page 248: Arduino 底層原始碼解析心得

86Duino原始碼重要細節

Dosbox參數設定

Dosbox執行編譯參數設定

DuinoCompiler.java - writeDosboxconf( )

Page 249: Arduino 底層原始碼解析心得

86Duino原始碼重要細節

設定編譯命令參數

將之前Makefile設定讀取進來並編譯

DuinoCompiler.java - writeMakefile( )

Page 250: Arduino 底層原始碼解析心得

第二道主菜:

Arduino 程式的燒錄機制

Page 251: Arduino 底層原始碼解析心得

實作燒錄的 IDE 原始碼:

app→src → processing → app → debug

本次解析目標

處理 Arduino 程式燒錄動作

處理 Arduino 程式燒錄動作

Page 252: Arduino 底層原始碼解析心得

Arduino 程式燒錄設定檔

hardware→arduino→avr

分別簡要介紹

定義 boards 選單、編譯及燒錄參數

定義編譯及燒錄參數

本次解析目標

Page 253: Arduino 底層原始碼解析心得

燒錄設定檔的用途

Arduino IDE 在燒錄程式之前, 會從燒錄設定檔讀取與板子相關的燒錄參數

這些設定檔中, 使用一種特定的格式, 記錄了每塊 Arduino 板子的差異, 例如:

◦ CPU時脈、燒錄的 protocol、燒錄檔案的最大 size

等等

只要推出一片新的板子, 依照指定的格式加入新參數, 就可以直接套用到目前的 IDE, 不需要重新編譯程式

Page 254: Arduino 底層原始碼解析心得

boards.txt 格式: 以 Leonardo 為例

要在 Boards 選單上顯示的名稱

燒錄工具程式檔名

燒錄用的 protocol 允許燒錄的 binary 最大 size

燒錄用的 baudrate

板子名稱 燒錄 sketch 的參數設定

燒錄 bootloader 的參數設定

設定燒錄時 IDE 清空 serial data 值

設定燒錄時用 1200 baudrate 來 reset Arduino

設定 reset Arduino 後要等待 upload port 出現才可進行燒錄

這次讀書會不講燒錄

bootloader 部分

Page 255: Arduino 底層原始碼解析心得

boards.txt 格式: 以 Leonardo 為例

板子名稱

編譯 sketch 的相關參數 Leonardo 的 CPU 型號(

燒錄會用到的參數)

Page 256: Arduino 底層原始碼解析心得

platform.txt 的格式: 以 Leonardo 為例

在 linux 下, 燒錄工具程式的位置以及燒錄 config 檔的位置

在 windows 和 Mac 下, 燒錄工具程式的位置以及燒錄 config 檔的位置

Page 257: Arduino 底層原始碼解析心得

platform.txt 格式: 以 Leonardo 為例

IDE 燒錄程式時下 的命令列參數

燒錄過程中會被替換成正確參數

Page 258: Arduino 底層原始碼解析心得

實際燒錄一次, 看看輸出訊息

燒錄程式 Avrdude config 檔

Leonardo CPU COM port 燒錄 baudrate

要燒錄的檔案

protocol

Page 259: Arduino 底層原始碼解析心得

IDE 執行燒錄的機制

執行 avrdude 燒錄命令

取得 sketch binary 檔路徑

Reset Arduino

使用 serial bootloader 的板子, 例如: UNO …

使用 usb bootloader 的板子, 例如: Leonardo

詳細流程請參考第 2 次 Arduino 原始碼讀書會內容

設定正確的板子燒錄參數

BasicUploader.java 是否需要 reset

Arduino board

Uploader.java

等待 upload

port 出現

Page 260: Arduino 底層原始碼解析心得

燒錄工具程式 Avrdude 簡介

普遍用來燒錄 Atmel AVR 的工具程式

跨多種平台, Windows, FreeBSD, linux, UNIX …

使用命令列來完成燒錄動作

參考資料

◦ 馬大的 Avrdude GUI 教學

http://www.coopermaa2nd.blogspot.tw/2011_06_01_archive.h

tml

◦ 詳細的 avrdude 命令, 可見 AVR Tutorial:

http://www.ladyada.net/learn/avr/avrdude.html

◦ Avrdude 原始碼

https://github.com/arduino/avrdude

Page 261: Arduino 底層原始碼解析心得

Avrdude 程式放在哪?

Windows/Mac : hardware\tools\avr\bin

Page 262: Arduino 底層原始碼解析心得

Avrdude 程式放在哪?

Linux : hardware\tools

Page 263: Arduino 底層原始碼解析心得

Arduino 燒錄機制原始碼重點講解

Page 264: Arduino 底層原始碼解析心得

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 (見下頁)

Page 265: Arduino 底層原始碼解析心得

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 回傳

Page 266: Arduino 底層原始碼解析心得

回到 BasicUploader.java → Class

BasicUploader → uploadUsingPreferences()

取得燒錄程式用的命令列pattern

設定正確的命令列參數

執行 avrdude 燒錄工具程式(見下頁)

Page 267: Arduino 底層原始碼解析心得

Uploader.java → Class Uploader→

executeUploadCommand ()

開新的 process 執行燒錄命令

將編譯結果顯示到 IDE 訊息框內

等待燒錄 process 執行完畢

檢查燒錄是否燒錄成功

Page 268: Arduino 底層原始碼解析心得

回到 BasicUploader.java → Class

BasicUploader → uploadUsingPreferences()

如果設定要等待 upload port 出現

檢查 upload

port 是否出現,

timeout = 2s

如果找到 upload port

將 upload port 設定回 9600 baudrate

Page 269: Arduino 底層原始碼解析心得

點心:

加入 86Duino 程式燒錄

對 IDE 所做的修改

Page 270: Arduino 底層原始碼解析心得

加入 86Duino 板子的資料夾結構

Page 271: Arduino 底層原始碼解析心得

建立 boards.txt

Page 272: Arduino 底層原始碼解析心得

建立 platform.txt

Page 273: Arduino 底層原始碼解析心得

修改 BasicUploader.java

加入板子判斷

根據 86Duino 修改燒錄參數

Page 274: Arduino 底層原始碼解析心得

將燒錄工具程式放到 IDE 指定資料夾下

燒錄工具程式必須依不同的平台而放在不同的目錄(下頁說明)

在 build IDE source code 的過程, 燒錄工具程式會自動被移到 IDE 規定的執行時期位置 (

詳見 build.xml 中的設定)

Page 275: Arduino 底層原始碼解析心得

Case 1: Windows XP/7/8

將 window 版的 86Duino 燒錄工具壓縮至 avr_tool.zip 裡面

Page 276: Arduino 底層原始碼解析心得

Case 2: Mac OS X

將 MAC 版的 86Duino 燒錄工具壓縮至 avr_tool.zip 裡面

Page 277: Arduino 底層原始碼解析心得

Case 3: Linux

將 Linux 版的 86Duino 燒錄工具複製到此資料夾下

Page 278: Arduino 底層原始碼解析心得

燒錄工具程式的一點開發經驗

在 ubuntu 遇到的問題

◦ 在 ubuntu 11.04 版本及之後的版本, 內建的 modem

manager 會干擾 USB CDC 裝置的傳輸

◦ https://bugs.launchpad.net/ubuntu/+source/modemmanager/+bug

/1153632/+activity

◦ 這會造成燒錄程序被干擾而失敗

◦ 解決方式: 將 USB CDC 裝置的 PID 和 VID 加入

modem manager 的忽略清單

Page 279: Arduino 底層原始碼解析心得

燒錄工具程式的一點開發經驗

在 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

Page 280: Arduino 底層原始碼解析心得

燒錄工具程式的一點開發經驗

在 Mac OS X 遇到的問題 (cont.)

◦ USB CDC 裝置的 Configuration Descriptor 中不可宣告 remote wakeup 功能, 否則會大大延長 Mac 辨識此 USB CDC 裝置的時間

Page 281: Arduino 底層原始碼解析心得

Arduino 原始碼讀書會(IV) :

Arduino Standard Libraries

重點解析 (上)

Page 282: Arduino 底層原始碼解析心得

Wire 函式庫

Page 283: Arduino 底層原始碼解析心得

TWI 介面

TWI 全名: Two Wire Interface

Atmel 當初是為了避免侵犯 I2C 的註冊商標, 特將此介面命名為 TWI

在傳輸資料上仍為 I2C 協定

Page 284: Arduino 底層原始碼解析心得

認識 I2C 介面

它是由 Philips 公司在 1980 年時期,為了方便同一個電路板上的各個組件互相通信,而開發出來的一種介面

全名: Inter-Integrated Circuit

I2C 介面只用兩條訊號線來連接其他元件:

◦ SDA (資料線)

◦ SCL (時脈線)

Page 285: Arduino 底層原始碼解析心得

I2C 元件的連接方式

圖片出處:http://en.wikipedia.org/wiki/I%C2%B2C

Master 端:通常是微處理器,負責發送時脈和slave 位址

Slave 端:通常是感測器元件 (或其他微處理器),每個元件都有自己的位址

Pull-up 電阻:由於 I2C 採 open-drain 設計,master 端若需要送出 HIGH 信號,必須靠上拉電阻接 Vdd,讓信號線電位呈現

HIGH 狀態

Page 286: Arduino 底層原始碼解析心得

詳細的 I2C 通訊協定

I2C 的通訊協定較為複雜, 此次讀書會不詳細說明, 有興趣的朋友可以參考下面的連結內容:

◦ Wiki: http://en.wikipedia.org/wiki/I%C2%B2C

Page 287: Arduino 底層原始碼解析心得

Arduino Leonardo 的 TWI 輸出腳位

SCL 和 SDA 與第 2,

3 腳位共用

Page 288: Arduino 底層原始碼解析心得

Arduino Uno 的 TWI 輸出腳位

R3 版本後, 額外拉出

SDA 和 SCL 腳位

(與 A/D第 A4, A5 腳位共用)

Page 289: Arduino 底層原始碼解析心得

I2C 連接範例: 使兩塊 Arduino Uno 可以互相傳輸資料

圖片出處:Arduino 互動設計入門 旗標出版

Page 290: Arduino 底層原始碼解析心得

Wire 函式功能與使用範例

Page 291: Arduino 底層原始碼解析心得

Wire.begin()

函式功能:

◦ 初始化 I2C 硬體

◦ 把 Arduino 當 I2C master

Page 292: Arduino 底層原始碼解析心得

Wire.begin(address)

函式功能:

◦ 初始化 I2C 硬體

◦ 除了把 Arduino 當 master 之外, 也啟動 I2C

slave 功能

Slave 位址使用傳入的 address 參數 (注意必需為

7-bit 位址)

當 Arduino 在 I2C Bus 上收到 address 時, 會進入

slave 模式

Page 293: Arduino 底層原始碼解析心得

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() 中

Page 294: Arduino 底層原始碼解析心得

Wire.write(value)

Wire.write(data, length)

Wire.write(string)

函式功能:

◦ 當 Arduino 是 I2C master 時: 傳送資料給

slave 端

資料會先寫入內部 master queue, 再由中斷副程式傳送出去

◦ 當 Arduino 是 I2C slave 時: 回應資料給

master 端

資料會先寫入內部 slave queue, 再由中斷副程式傳送出去

Page 295: Arduino 底層原始碼解析心得

Wire.endTransmission()

Wire.endTransmission(stop)

函式功能:

◦ 結束資料傳輸動作

啟動中斷副程式, 把 master queue 中的資料傳送出去

◦ 若沒有 stop 參數, 則資料送完後發 I2C

STOP 信號

◦ 若 stop 參數為 true, 則資料送完後改發 I2C

RESTART 信號

Page 296: Arduino 底層原始碼解析心得

Wire.requestFrom(address, quantity)

Wire.requestFrom(address, quantity, stop)

函式功能:

◦ 用來起始 I2C master 的資料讀取程序

address 是要讀取的 I2C slave 裝置位址

quantity 是要讀取的位元組個數

◦ 將啟動中斷副程式讀取所需要的資料並存放在內部的 rx queue 中

使用者可調用 Wire.read() 取出 rx queue 中的值

◦ stop 參數功能與上頁相同

Page 297: Arduino 底層原始碼解析心得

Wire.read()

函式功能:

◦ 從 rx queue 中取出一個 byte

◦ 假如 rx queue 中沒有值, 會得到 -1

Page 298: Arduino 底層原始碼解析心得

Wire.avairlable()

函式功能:

◦ 回傳 rx queue 中還未讀的 byte 數

Page 299: Arduino 底層原始碼解析心得

Wire.onReceive(myHandler)

函式功能:

◦ 用來掛載 I2C slave 模式下的 callback

function (myHandler)

◦ 當 Arduino 在 I2C slave 模式下收到 master

端的資料後, 會調用此 callback function

Page 300: Arduino 底層原始碼解析心得

使用範例:

透過 I2C 連接兩片 Arduino UNO

連接方式:

Page 301: Arduino 底層原始碼解析心得

Master 端 Slave 端

使用範例:

透過 I2C 連接兩片 Arduino UNO

Slave 端要設定位址

Master 端要送出去的位址

兩者要一樣

Page 302: Arduino 底層原始碼解析心得

把程式個別燒入 UNO 後, 可以在 slave 端的

serial monitor 上看到 master 端傳過來的字串

使用範例:

透過 I2C 連接兩片 Arduino UNO

圖片出處:Arduino 互動設計入門 旗標出版

Page 303: Arduino 底層原始碼解析心得

Wire 函式庫原始碼解析

Page 304: Arduino 底層原始碼解析心得

Wire 函式庫資料夾內容

Wire 資料夾

utility 資料夾

Wire 函式庫的原始碼

硬體相關的程式原始碼

Page 305: Arduino 底層原始碼解析心得

Wire 函式庫實作架構

Wire.cpp:

◦ 實作對外的 API 介面

◦ 透過 twi.c 傳送和接收資料

twi.c:

◦ 操縱 ATmega TWI (I2C)硬體

暫存器, 以中斷服務常式 (ISR)

處理傳送/接收資料的行為

Wire lib

TWI lib

Arduino TWI

hardware

ISR

queue

Page 306: Arduino 底層原始碼解析心得

Wire 函式庫原始碼重要細節

設定 Arduino I2C 裝置的 slave 位址

掛載 callback function

初始化 Arduino I2C 硬體

Wire.cpp:begin()

Page 307: Arduino 底層原始碼解析心得

Wire 函式庫原始碼重要細節 Wire.cpp:requestFrom()

調用 twi.c 中的 function, 將取得的值放到 rxbuffer 陣列中

回傳讀到的總數

Page 308: Arduino 底層原始碼解析心得

Wire 函式庫原始碼重要細節

Wire.cpp:write()

Wire.cpp:read() Master 模式

Slave 模式

把資料寫到 txbuffer 陣列

從 rxbuffer 陣列中讀值

Page 309: Arduino 底層原始碼解析心得

Wire 函式庫原始碼重要細節 Wire.cpp:beginTransmission() 以及 endTransmission()

此時才真正把 txbuffer 中的值寫出去

賦予變數初值

預設為 master 模式

Page 310: Arduino 底層原始碼解析心得

Wire 函式庫原始碼重要細節

Wire.cpp:onReceiveService()

掛載 callback function

把中斷副程式中收到的陣列內容拷貝到 Wire.cpp

中的 buffer

重置 read() 會用到的變數

調用 user 的 callback function

Page 311: Arduino 底層原始碼解析心得

Wire 函式庫原始碼重要細節 Wire.cpp:onRequestService()

掛載 callback function

調用 user 的

callback function

Page 312: Arduino 底層原始碼解析心得

twi.c:twi_readForm()

操作 I2C 暫存器, 開始從 I2C Bus 上讀值

等待讀值過程結束

把讀到的值存到目標陣列

初始化相關變數

I2C 硬體仍忙碌時, 循環等待

設置外部 slave 位址以及讀取命令

Page 313: Arduino 底層原始碼解析心得

twi.c:twi_writeTo()

操作 I2C 暫存器, 開始對 I2C Bus 上寫值

等待寫值過程結束

回傳寫值過程的結果

初始化相關變數

I2C 硬體仍忙碌時, 循環等待

設置外部 slave 位址以及寫命令

Page 314: Arduino 底層原始碼解析心得

twi.c:中斷副程式

不同的 I2C

硬體狀態

(狀態暫存器內的值)

掛載中斷副程式

中斷副程式主要運作方式:

發生某種狀態, 就做對應的事情

繼續送下一個 byte

發生錯誤的處理方式

送 stop condition

送 restart condition

Page 315: Arduino 底層原始碼解析心得

Wire 函式庫的移植:

以 86Duino 為例

Page 316: Arduino 底層原始碼解析心得

Wire 在 86Duino 上的移植

與硬體無關的 code:

◦ 例如: Wire.cpp

◦ 可以直接拿來用, 不用修改

(或者做些把 queue buffer 加大等小改進)

與硬體相關 code:

◦ 例如: twi.c

◦ 根據硬體暫存器之間的規格差異, 移植難度也不同

◦ 86Duino I2C 硬體暫存器設定與 ATmega TWI 頗為相似, 因此移植時需要修改的地方並不多

Page 317: Arduino 底層原始碼解析心得

86Duino twi.c:中斷副程式

Master 完成傳送位址、資料的狀態

取得 I2C 狀態暫存器的值

假如要從 I2C Bus 上讀值

讀值的

操作

過程

使 I2C 硬體開始讀值

送 stop condition

等待讀值過程結束

讀最後一筆

Page 318: Arduino 底層原始碼解析心得

86Duino twi.c:中斷副程式 (續)

把 1 byte 寫到 I2C BUS 上

寫值的

操作

過程

其它部分的移植方法都是相同概念:

把硬體暫存器的存取改成 Vortex86EX 的版本

送 stop condition

送 restart condition

假如是最後一筆

Page 319: Arduino 底層原始碼解析心得

Servo 函式庫

Page 320: Arduino 底層原始碼解析心得

RC Servo 簡介

Radio Control Servo

Servo 是一個整合馬達、減速機、控制驅動的系統, 可根據外部命令, 控制馬達轉到目標角度或速度

Page 321: Arduino 底層原始碼解析心得

RC Servo 的應用領域

在遙控模型(遙控車、遙控飛機)上, 用來控制遙控模型的姿態和移動方向

RC Servo

目前也廣泛應用在機器人領域, 做為機器人的關節致動器

Page 322: Arduino 底層原始碼解析心得

RC Servo 的組成

圖片出處:Arduino 開發實戰指南 機械工業出版社

基本功能: 接收主控器傳過來的目標位置信號, 與目前馬達位置比較後, 以 PID 方式, 控制使馬達轉到目標位置

用來測量馬達的轉動角度

用來放大直流馬達的扭力

Page 323: Arduino 底層原始碼解析心得

常見的 RC Servo 控制信號

主流為 PWM, 部分機器人專用伺服機則採用 RS232 或 RS485

Arduino 的 Servo 函式庫只以 PWM 命令的 RC Servo 為控制對象

生產公司 RC Serco 的通訊方式

Robotics(韓國) RS232、RS485

KONDO (日本) PWM、RS232

祥儀 (台灣) PWM

廣營 (台灣) PWM

TOWERPRO (大陸) PWM

Page 324: Arduino 底層原始碼解析心得

PWM 信號(Pulse Width Modulation)

全名為脈波寬度調變

如下圖所示:

Duty Cycle:

在一個週期中,

HIGH 電位的時間所占的比例

Period:

即一個 PWM

pulse 的週期

Page 325: Arduino 底層原始碼解析心得

PWM 信號與 RC Servo 位置關係圖

使用 PWM duty 的寬度來控制 servo 的轉動角度:

duty 範圍通常介於

700us ~ 2300us 之間

PWM 週期通常為 20ms

圖片出處:Arduino 開發實戰指南 機械工業出版社

Page 326: Arduino 底層原始碼解析心得

用 Arduino 連接 RC Servo

電源線

接地

PWM 信號線

PWM servo 連接線:

Page 327: Arduino 底層原始碼解析心得

Servo.attach(pin)

函式功能:

◦ 指定要做為輸出 PWM 信號的 pin 編號

Page 328: Arduino 底層原始碼解析心得

Servo.write(angle)

函式功能

◦ 輸出指定 duty 寬度的 PWM 信號

◦ 參數 angle

可以輸入 0 ~180, 代表轉動角度

也可以輸入實際的 duty 時間值, 例如: 800, 1500

等數值, 單位是 us

Page 329: Arduino 底層原始碼解析心得

Servo Lib 使用範例:

讓 RC Servo 來回轉動

實際運行結果影片:

Page 330: Arduino 底層原始碼解析心得

Servo 函式庫原始碼解析

Page 331: Arduino 底層原始碼解析心得

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 輸出

Page 332: Arduino 底層原始碼解析心得

多個 channels 的 PWM 輸出

你可能以為是這樣:

‧ ‧ ‧

Channel 1

Channel 2

Channel n

20ms (PWM period)

Page 333: Arduino 底層原始碼解析心得

實際上, Arduino 是這樣輸出的:

多個 channels 的 PWM 輸出

‧ ‧ ‧

20ms

Channel 1

Channel 2

Channel 3

Page 334: Arduino 底層原始碼解析心得

假設平均 duty 是 1500us (Servo 中點),

則有 20ms / (1500us+ISR overhead) 12

如果所有 12 channels 都輸出最大 duty

2400us, 則 PWM period 會超過

12 x 2400us = 28.8ms

◦ 若你使用的 RC Servo 不允許超過 20ms 的

PWM 週期, 則需注意 Servo 函式庫此特性

Page 335: Arduino 底層原始碼解析心得

Servo Lib 的運作架構

建構 Servo 物件

設定 channel

Timer 中斷副程式

初始化 Timer

Servo.attach()

更新 channel 的

duty 值 Servo.write()

有下一個

channel?

目前時間是否超過 20ms?

將此 channel 設 HIGH

並且設定下一次進入中斷的時間 = duty

設定下次進入中斷的時間為 2us 後

設定下一次進入中斷的時間 = 20ms – 目前時間

新的 20ms

周期開始?

將原 channel

設 LOW

否 是

宣告 Servo

Page 336: Arduino 底層原始碼解析心得

Servo Lib 原始碼重要細節 Servo.cpp 中的 attach() 函式

若是第一次使用此 Timer 初始化Timer 中斷

取得 servo pin 對應的 timer 編號

Page 337: Arduino 底層原始碼解析心得

Servo Lib 原始碼重要細節

Servo.cpp 中的

write() 函式

若輸入的值小於 544

把值 map 到 544 ~ 2400

調用 writeMicroseconds

把值傳給中斷副程式中會用到的變數

把 us 轉成 Timer 單位

Page 338: Arduino 底層原始碼解析心得

Servo Lib 原始碼重要細節

將原 channel 輸出 LOW

若是新的周期開始, 就重置 Time counter

指向下一個 channel

Servo.cpp 中的 Timer 中斷程式碼

將此 channel 設 HIGH

假如沒有可用的 channel

設定 Timer = duty

目前時間超過 20ms, 將下次中斷時間設成 2us 後

若目前時間還小於 20ms, 將下次中斷時間設成 20ms

Page 339: Arduino 底層原始碼解析心得

因為用軟體模擬 PWM, 所以實際輸出的

PWM duty 時間會有抖動現象 (jitter)

當使用的 PWM channel 超過 12 組,

Arduino 會啟用更多組 Timer,

多個 Timer 中斷彼此干擾, 有時會惡化

PWM jitter 現象

Page 340: Arduino 底層原始碼解析心得

Servo 函式庫的移植:

以 86Duino 為例

Page 341: Arduino 底層原始碼解析心得

Servo 函式庫在 86Duino 上的

實作改良

用新的算法在 20ms 內模擬更多 PWM

channels

支援硬體 PWM 功能的 I/O pin, 直接以硬體功能輸出 PWM 信號

以一個 Timer 中斷支持最多 45 個 PWM

channels

Page 342: Arduino 底層原始碼解析心得

接上 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

Page 343: Arduino 底層原始碼解析心得

86Duino Servo 函式庫的 PWM 輸出

‧ ‧ ‧

20ms

Channel 1

Channel 2

Channel 3

Channel n

Page 344: Arduino 底層原始碼解析心得

PWM 模擬算法概要

排序所有 channels 的 duty 值

根據 duty 大小的排序, 依序在對應的 I/O pin

上輸出 PWM

同時在兩個 I/O pin 上輸出 PWM 波型

使用 Vortex86EX event trigger 功能縮小由於

interrupt latency 造成的 PWM jitter

Page 345: Arduino 底層原始碼解析心得

Servo.cpp 中的 attach() 函式

初始化硬體 PWM pin

初始化以軟體模擬的 PWM pin

Page 346: Arduino 底層原始碼解析心得

Servo.cpp 中的 writeMicroseconds() 函式

更新排序內容, 然後把結果複製到 A 排序陣列中

若是第一次更新, 初始化 B 排序陣列

啟動 Timer

Page 347: Arduino 底層原始碼解析心得

Servo.cpp 中的中斷副程式

外部非正在更新排序

排序方式有被更新過

交換排序陣列 A, B 的

指標, 完成資料更新

週期結束

啟動下一個週期

Page 348: Arduino 底層原始碼解析心得

Servo.cpp 中的中斷副程式(續)

若是最後的 pin, 將它設定為 LOW, 結束 PWM 週期

若是倒數第二 pin, 將自己設定為 LOW, 不啟動下一個 pin

若都以上條件都不滿足, 就把目前 pin 設定為

LOW, 把下一個 pin 設定為 HIGH

Page 349: Arduino 底層原始碼解析心得

PWM Duty 抖動現象

由於在 Servo 函式庫裡 PWM 是用軟體模擬的方式來實現, 所以實際輸出的 duty 會有抖動現象

因抖動造成的誤差範圍與 CPU 特性及軟體模擬算法有關

抖動現象

Page 350: Arduino 底層原始碼解析心得

以 Arduino UNO 為例, 觀察 PWM 抖動情況

+

示波器 Arduino UNO

波型抖動 實況影片:

Page 351: Arduino 底層原始碼解析心得

PWM 抖動現象的影響

在命令解析度比較高的 RC Servo 上, 會造成

servo 輸出軸實際的抖動

一般模型用的低價 RC Servo 解析度較低, 受

PWM 抖動現象的影響不大

Arduino UNO

+

KONDO KRS4014 servo

Servo抖動 實況影片

Page 352: Arduino 底層原始碼解析心得

在 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

Page 353: Arduino 底層原始碼解析心得

在 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 惡化情形

Page 354: Arduino 底層原始碼解析心得

FIRMATA

Page 355: Arduino 底層原始碼解析心得

Command Message

Page 356: Arduino 底層原始碼解析心得

Firmata

Examples

◦ Simple Analog Firmata

◦ Servo Firmata

Firmata.h

Firmata.cpp

Boards.h

Page 357: Arduino 底層原始碼解析心得

Firmata

• Examples

–Simple Analog Firmata

–Servo Firmata

• Firmata.h

• Firmata.cpp

• Boards.h

Page 358: Arduino 底層原始碼解析心得

Firmata.attach(); //掛載要執行的函式

Firmata.processInput(); //接收並執行來自HOST的指令

Page 359: Arduino 底層原始碼解析心得

Firmata

• Examples

–Simple Analog Firmata

–Servo Firmata

• Firmata.h

• Firmata.cpp

• Boards.h

Page 360: Arduino 底層原始碼解析心得
Page 361: Arduino 底層原始碼解析心得

Firmata

• Examples

–Simple Analog Firmata

–Servo Firmata

• Firmata.h

• Firmata.cpp

• Boards.h

Page 362: Arduino 底層原始碼解析心得

#include “Boards.h” //依照不同晶片,套用不同的腳位定義

Page 363: Arduino 底層原始碼解析心得
Page 364: Arduino 底層原始碼解析心得
Page 365: Arduino 底層原始碼解析心得
Page 366: Arduino 底層原始碼解析心得

Firmata

• Examples

–Simple Analog Firmata

–Servo Firmata

• Firmata.h

• Firmata.cpp

• Boards.h

Page 367: Arduino 底層原始碼解析心得
Page 368: Arduino 底層原始碼解析心得

Firmata.begin(); //即為Serial.begin()

Page 369: Arduino 底層原始碼解析心得

Firmata.available(); // 即為Serial.available()

Page 370: Arduino 底層原始碼解析心得

將一包訊息接收完成後,依照不同的command做不同的處理

Page 371: Arduino 底層原始碼解析心得

利用function pointer來執行先前attach的function

Page 372: Arduino 底層原始碼解析心得

switch依照不同的command來做不同的設定

Page 373: Arduino 底層原始碼解析心得

回傳至HOST的訊息也用相同的格式

Page 374: Arduino 底層原始碼解析心得
Page 375: Arduino 底層原始碼解析心得
Page 376: Arduino 底層原始碼解析心得

Firmata

• Examples

–Simple Analog Firmata

–Servo Firmata

• Firmata.h

• Firmata.cpp

• Boards.h

Page 377: Arduino 底層原始碼解析心得

不同的晶片有不同的腳位定義,利用Boards.h來設定

Page 378: Arduino 底層原始碼解析心得

readPort();

Page 379: Arduino 底層原始碼解析心得

writePort();

Page 380: Arduino 底層原始碼解析心得
Page 381: Arduino 底層原始碼解析心得

Firmata @ 86Duino

Page 382: Arduino 底層原始碼解析心得

Firmata @ 86Duino

• Examples

–Simple Analog Firmata

–Servo Firmata

• Firmata.h

• Firmata.cpp

• Boards.h

Page 383: Arduino 底層原始碼解析心得
Page 384: Arduino 底層原始碼解析心得

SD CARD

Page 385: Arduino 底層原始碼解析心得

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

Page 386: Arduino 底層原始碼解析心得

FAT檔案系統

Page 387: Arduino 底層原始碼解析心得

SD

./Examples

◦ Files.ino

◦ Datalogger.ino

./utility →檔案系統

SD.h

SD.cpp →開關存取SD卡

File.cpp →開關存取檔案

Page 388: Arduino 底層原始碼解析心得

SD

• ./Examples

–Files.ino

–Datalogger.ino

• ./utility

• SD.h

• SD.cpp

• File.cpp

Page 389: Arduino 底層原始碼解析心得

#include <SPI.h> // Arduino透過SPI介面連接SD卡

File myFile; //建立一個自己的File物件

SD.begin(); //開啟SD卡

Page 390: Arduino 底層原始碼解析心得

SD.open(“example.txt”, FILE_READ); //從頭開始讀

SD.open(“example.txt”, FILE_WRITE); //從尾繼續寫

Page 391: Arduino 底層原始碼解析心得
Page 392: Arduino 底層原始碼解析心得

SD

• ./Examples

–Files.ino

–Datalogger.ino

• ./utility

• SD.h

• SD.cpp

• File.cpp

Page 393: Arduino 底層原始碼解析心得
Page 394: Arduino 底層原始碼解析心得
Page 395: Arduino 底層原始碼解析心得

dataFile.println(dataString); //將字串寫入檔案

dataFile.close(); //儲存並關閉檔案

Page 396: Arduino 底層原始碼解析心得

SD

• ./Examples

–Files.ino

–Datalogger.ino

• ./utility

• SD.h

• SD.cpp

• File.cpp

Page 397: Arduino 底層原始碼解析心得

SdFile.cpp

SdVolume.cpp

FAT檔案系統

Sd2Card.cpp SD卡讀/寫指令、寫入資料

SPI.cpp 透過SPI模式對SD卡存取

Page 398: Arduino 底層原始碼解析心得

SD

• ./Examples

–Files.ino

–Datalogger.ino

• ./utility

• SD.h

• SD.cpp

• File.cpp

Page 399: Arduino 底層原始碼解析心得
Page 400: Arduino 底層原始碼解析心得
Page 401: Arduino 底層原始碼解析心得
Page 402: Arduino 底層原始碼解析心得
Page 403: Arduino 底層原始碼解析心得

SD @ 86Duino

Page 404: Arduino 底層原始碼解析心得

SD @ 86Duino

86Duino透過作業系統運作,SD卡為系統磁碟

86Duino的作業系統內就有FAT檔案系統

86Duino利用stdio.h內的FILE structure來存取檔案

86Duino使用4-bit模式與SD卡連接

Page 405: Arduino 底層原始碼解析心得

SD @ 86Duino

• File.cpp

• SD.h

• SD.cpp

Page 406: Arduino 底層原始碼解析心得

fwrite 寫入檔案

Page 407: Arduino 底層原始碼解析心得

fgetpos 取得目前在檔案內的位置

fread 從檔案讀出

Page 408: Arduino 底層原始碼解析心得

fflush 寫入檔案

Page 409: Arduino 底層原始碼解析心得

fseek 移至檔案的某個特定位置

ftell 取得目前在檔案內的位置

fclose 關閉檔案

Page 410: Arduino 底層原始碼解析心得
Page 411: Arduino 底層原始碼解析心得

fopen 開啟檔案 (r,w,a為開啟模式)

Page 412: Arduino 底層原始碼解析心得

EEPROM Library

Page 413: Arduino 底層原始碼解析心得

EEPROM 簡介

EEPROM, 或寫作E2PROM, 全稱電子抹除式可複寫唯讀記憶體, 是一種可以通過電子方式多次複寫的半導體存儲設備

Arduino EEPROM 大小:

◦ ATmega328 : 1024 Byte

◦ ATmega168, ATmega8 : 512 Byte

◦ ATmega1280, ATmega2560 : 4K Byte

Page 414: Arduino 底層原始碼解析心得

EEPROM 函式

EEPROM.read(address)

◦ 從 address (位址)讀取值出來

EEPROM.write(addr, val)

◦ 將 val (數值)寫到 addr (位址)

Page 415: Arduino 底層原始碼解析心得

EEPROM 函式庫範例

從EEPROM讀值

寫值到EEPROM

Page 416: Arduino 底層原始碼解析心得

EEPROM 函式庫原始碼解析

Page 417: Arduino 底層原始碼解析心得

EEPROM 函式庫原始碼重要細節

呼叫 AVR EEPROM 函式庫從 EEPROM 讀值

引用 Atmel 官方提供的 AVR

EEPROM 函式庫

呼叫 AVR EEPROM 函式庫寫值到EEPROM

EEPROM 是 Arduino 標準函式庫裡實作最簡單的一個

但不代表容易移植

Page 418: Arduino 底層原始碼解析心得

EEPROM 函式庫的移植:

以 86Duino 為例

Page 419: Arduino 底層原始碼解析心得

叫我移植這麼簡單的EEPROM Lib ???

我覺得我被輕視了…

苦主 RD

Page 420: Arduino 底層原始碼解析心得

什麼! 86Duino 沒有

EEPROM!

這麼爛的板子是誰做的啊!

苦主 RD

Page 421: Arduino 底層原始碼解析心得

開個檔案模擬

EEPROM 讀寫,

十分鐘搞定, 嘿嘿...

苦主 RD

這樣是嚇不倒我滴!

Page 422: Arduino 底層原始碼解析心得

結果在測試了 EEPROM 的官方範例之後…86Duino

就葛屁了(DOS 檔案系統損毀) Orz

Page 423: Arduino 底層原始碼解析心得

以檔案模擬 EEPROM 的問題

若 86Duino 在寫入檔案時, 被斷電或重置…

◦ 輕則檔案資料遺失

◦ 重則整個檔案系統損毀 需要設計一個不怕斷電與重置的

演算法…

苦主 RD

嗚嗚, 只好

重寫了~~

Page 424: Arduino 底層原始碼解析心得

86Duino 兩種實現 EEPROM 等價功能的方法

方法一: 利用板上 CMOS 記憶體

◦ 優點: 速度快, 程式簡單, 讀寫時不怕 CPU 斷電

◦ 缺點: 容量只有 200 bytes, 移除 86Duino 板上 RTC 電池會使資料遺失

方法二: 利用 BIOS flash 的剩餘空間

◦ 優點: 容量可達 16 KB, 不需電池仍可保存資料

◦ 缺點: 速度較慢, 需設計容錯算法避免讀寫時斷電的資料損毀 實作較複雜

Page 425: Arduino 底層原始碼解析心得

CMOS bank 方法的實作

Page 426: Arduino 底層原始碼解析心得

CMOS 方法原始碼重要細節

切換 CMOS 分頁(分為兩頁各128 bytes)

設定要讀取的 CMOS 位址

從 CMOS 讀值

Page 427: Arduino 底層原始碼解析心得

CMOS 方法原始碼重要細節

切換 CMOS 分頁(分為兩頁各128 bytes)

設定要寫入的 CMOS 位址

寫值到CMOS

Page 428: Arduino 底層原始碼解析心得

Flash bank 方法的實作

Page 429: Arduino 底層原始碼解析心得

算法原理

Page 430: Arduino 底層原始碼解析心得

算法原理

Page 431: Arduino 底層原始碼解析心得

EEPROM

初始化

初始化物件

SPI Flash /

DRAM

SPI

Flash (c) 清除 SPI Flash (c)

選擇

SPI Flash (a/b)

將 Flash 資料讀取到 DRAM

結束

滿

未滿

Page 432: Arduino 底層原始碼解析心得

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)

結束

滿

滿

未滿

未滿

Page 433: Arduino 底層原始碼解析心得
Page 434: Arduino 底層原始碼解析心得

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 正確的值

Page 435: Arduino 底層原始碼解析心得

Flash 方法原始碼重要細節

判斷SPI Flash ( a / b ) 是否滿了

Reset SPI Flash ( a / b )

將數值寫回SPI Flash ( a / b )

前面 1k 部分

寫 0 到 SPI Flash ( c )

寫入位置回到初始狀態

Page 436: Arduino 底層原始碼解析心得

Flash 方法原始碼重要細節

填值到 DRAM

決定填值到哪一組 SPI Flash ( a / b )

填值到SPI Flash ( a / b )

Page 437: Arduino 底層原始碼解析心得

Flash 方法原始碼重要細節

從DRAM 讀值

Page 438: Arduino 底層原始碼解析心得

EEPROM 讀取性能測試

板子 平均讀取時間

Arduino Leonardo 實體 EEPROM 1 us

86Duino (CMOS bank) 2 us

86Duino (Flash bank) < 0.1us

測試程式:

連續讀取200 次,

計算平均讀取時間

Page 439: Arduino 底層原始碼解析心得

EEPROM 寫入性能測試

板子 平均寫入時間

Arduino Leonardo 實體 EEPROM 3395 us

86Duino (CMOS bank) 3 us

86Duino (Flash bank) 125 us (沒有跨 1K 邊界時的情況)

510 us (跨 1K 邊界時的情況)

測試程式:

連續寫入200 次,

計算平均寫入時間

Page 440: Arduino 底層原始碼解析心得

Arduino 原始碼讀書會(V) :

Arduino Standard Libraries

重點解析 (下)

DMP Electronics Inc. (瞻營全電子)

[email protected]

Page 441: Arduino 底層原始碼解析心得

SPI 函式庫

Page 442: Arduino 底層原始碼解析心得

SPI

全名: Serial Peripheral Interface

許多電子裝置都有用到它,例如:

◦ SD 記憶卡

◦ 數位/類比轉換 IC (例如 AD7928)

◦ LED 控制晶片 (例如 MAX7219)

◦ 還有很多

這裡無法一一列出…

Page 443: Arduino 底層原始碼解析心得

SPI 介面

SPI 採用四條線連接主機和周邊設備, 這四條線的名稱和用途如下:

◦ SS:周邊選擇線(Slave Select),指定要連接哪一個周邊設備。這條線也稱為 CS (Chip Select 晶片選擇線)

圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版

Page 444: Arduino 底層原始碼解析心得

SPI 介面 (續)

◦ MOSI:從主機(master)送往周邊(slave)的資料線,(Master Output Slave Input)

◦ MISO:從周邊(slave)送往主機(master)的資料線,

(Master Input Slave Output)

◦ SCK:序列時脈線

圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版

Page 445: Arduino 底層原始碼解析心得

Arduno UNO 上的 SPI 腳位 有用 UNO 燒錄過 Arduino bootloader 的人,對 SPI 腳位應該不陌生。如下圖所示:

圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版

Page 446: Arduino 底層原始碼解析心得

UNO 上的 SPI 腳位 (電路圖)

圖片出處:Arduino UNO 官方電路圖

Reset

有 4 支腳

有 3 支腳

Page 447: Arduino 底層原始碼解析心得

Arduno Leonardo 上的 SPI 腳位

圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版

SS pin 為什麼在這裡!?

(看下頁)

Page 448: Arduino 底層原始碼解析心得

Leonardo 上的 SPI 腳位 (電路圖)

圖片出處:Arduino Leonardo 官方電路圖

SS 與

RXLED 是共用腳位

Page 449: Arduino 底層原始碼解析心得

電路圖中搜尋 RXLED 關鍵字

心中 OS:或許選一支 GPIO 來當 SS 會比較方便

直接與 LED

相連

一路上都是 SS

Page 450: Arduino 底層原始碼解析心得

SPI 的通訊格式

SPI 是一種同步全雙工的序列埠,主機和周邊之間的資料傳遞,都要跟著時脈的 High、Low 一同進行

SPI 介面沒有強制規範時脈訊號的標準,大部分是由

SPI 介面晶片來決定使用哪一種時脈訊號格式

一般來說,SPI 可以由 CPOL 和 CPHA 來組成四種不同的格式:

◦ CPOL:時脈極性,時脈信號的電位基準

圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版

Page 451: Arduino 底層原始碼解析心得

SPI 的通訊格式 (續)

◦ CPHA:時脈相位,資料在時脈的上升階段或者下降階段被讀取/送出

圖片出處:http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus

兩個模式的共同之處:

資料在時脈上升或下降階段,同時收或送資料

SS 要先設為 Low

來啟用裝置

接收與發送 1 個 byte 的過程

Page 452: Arduino 底層原始碼解析心得

SPI 的通訊格式 (續)

CPOL 和 CPHA 配合後,四種組合如下:

與裝置通訊前的確認事項

◦ 資料傳遞的格式

◦ 訊號傳遞位元順序 (bit order):分成高位元先傳

(MSBFRIST) 和低位元先傳 (LSBFIRST) 兩種

◦ 裝置所能接收的最高時脈頻率

圖片出處:超圖解 Arduino 互動設計入門第一版 旗標出版

Arduino 函式庫中的命名

Page 453: Arduino 底層原始碼解析心得

SPI 函式功能與使用範例

Page 454: Arduino 底層原始碼解析心得

SPI.begin()

函式功能:

◦ 初始化 SPI 硬體

設定為 Master 端

預設資料格式是 CPOL = 0,CPHA = 0

預設傳輸速度是 4MBps

預設傳遞位元順序是高位元先傳 (MSBFIRST)

Page 455: Arduino 底層原始碼解析心得

SPI.end()

函式功能:

◦ 關閉 SPI 硬體功能

Page 456: Arduino 底層原始碼解析心得

SPI. setBitOrder()

函式功能:

◦ 設定傳遞位元順序,可輸入兩種參數:

MSBFIRST

LSBFIRST

Page 457: Arduino 底層原始碼解析心得

SPI. setDataMode()

函式功能:

◦ 設定資料格式,可輸入四種參數:

SPI_MODE0

SPI_MODE1

SPI_MODE2

SPI_MODE3

Page 458: Arduino 底層原始碼解析心得

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

Page 459: Arduino 底層原始碼解析心得

SPI. transfer()

函式功能:

◦ 傳送並同時接收資料

Page 460: Arduino 底層原始碼解析心得

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 晶片而定

Page 461: Arduino 底層原始碼解析心得

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)

Page 462: Arduino 底層原始碼解析心得

SPI 函式庫原始碼解析

Page 463: Arduino 底層原始碼解析心得

SPI 函式庫資料夾內容

https://github.com/arduino/Arduino/tree/master/libraries/SPI

Page 464: Arduino 底層原始碼解析心得

SPI 函式庫原始碼重要細節 SPI.cpp:begin()

將 SS pin 設定為 output HIGH

確保等一下 enable SPI 後會是

Master 狀態

將 SPI 設定為 Master 然後 enable

SPI enable 後,SCK 和 MOSI pin

的方向需自行定義

Page 465: Arduino 底層原始碼解析心得

SPI 函式庫原始碼重要細節

SPI.cpp:end()

SPI.cpp:setBitOrder()

LSBFIRST

MSBFIRST

Page 466: Arduino 底層原始碼解析心得

SPI 函式庫原始碼重要細節

SPI.cpp: setDataMode()

SPI.cpp: setClockDivider()

設定 divider 暫存器

Page 467: Arduino 底層原始碼解析心得

SPI 函式庫原始碼重要細節

SPI.h: transfer()

資料送完的時候,SPIF 會設 1

(設 1 後第一次對它讀取會清 0)

將要送的資料填入 SPDR 暫存器

回傳收到的值

Page 468: Arduino 底層原始碼解析心得

SPI 函式庫的移植:

以 86Duino 為例

Page 469: Arduino 底層原始碼解析心得

SPI 在 86Duino 上的移植

移植重點:

◦ 將填 ATmega CPU 內的 SPI 暫存器的行為,改成填

86Duino CPU 內的 SPI 暫存器

◦ 其它與硬體無關的程式碼幾乎無需改動,可直接延用

Page 470: Arduino 底層原始碼解析心得

86Duino spi.cpp:begin()

在 86Duino 中,SPI 是一個 PCI 裝置,所以需要先取得 I/O address

設定 SPI 為全雙工

預設速度為 4MHz

預設傳輸格式為

SPI_MODE0

開啟 FIFO 功能

將 SS (實際上是 SPICS) 設定為 HIGH

Page 471: Arduino 底層原始碼解析心得

86Duino spi.cpp:end()

86Duino spi.cpp:setBitOrder()

設定為 LSBFIRST

設定為 MSBFIRST

Page 472: Arduino 底層原始碼解析心得

86Duino spi.cpp:setDataMode()

86Duino spi.cpp:setClockDivider()

限制值的範圍在 1 ~ 4095,因為 divider register 只有 12 bit

設定 SPI 傳輸模式

Page 473: Arduino 底層原始碼解析心得

86Duino spi.cpp:transfer()

檢查 Output FIFO 是否為空

檢查 Input FIFO 是否為空

回傳收到的值

送出值

Page 474: Arduino 底層原始碼解析心得

不同 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 速度的計算方式

Page 475: Arduino 底層原始碼解析心得

86Duino SPI clock 的計算方式

在 86Duino 上,計算 SPI 速度的公式如下:

當 div 設成 1 時,86Duino 最快可輸出的 SPI

clock 速度:50MHz

100MHz / (2 div) div 是 divider 值,範圍:1 ~ 4095

Page 476: Arduino 底層原始碼解析心得

Ethernet 函式庫

Page 477: Arduino 底層原始碼解析心得

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

Page 478: Arduino 底層原始碼解析心得

Arduino Ethernet shield 電路

對 Arduino 的通訊介面:SPI

輸出:LAN 的差動信號

圖片出處:http://arduino.cc/en/uploads/Main/arduino-ethernet-shield-06-schematic.pdf

Page 479: Arduino 底層原始碼解析心得

Arduino Ethernet shield 工作原理

OSI 模型: http://linux.vbird.org/linux_server/0110network_basic.php#route_route

將每個 frame

轉換為 0、1

信號

網路接頭

使用 IEEE

802.3 封包協議來傳送 MAC

支援的通訊協定

軟體處理部分

硬體處理部分

Page 480: Arduino 底層原始碼解析心得

Ethernet 使用範例:ChatServer

把 Arduino 當作 WebServer,等待 client 連線

設定網路卡實體位址、IP位址、子網路遮罩和閘道器位址

設定伺服器的 port 為 23,預設使用 Telnet 服務

啟動乙太網路連線

啟動伺服器

Page 481: Arduino 底層原始碼解析心得

ChatServer:loop()

聆聽用戶的連線請求

如果收到用戶的連線請求

第一個用戶連線,送出

Hello 字串

檢查用戶是否有送字元過來,如果有,則返回相同的字元給所有已連結上的用戶

Page 482: Arduino 底層原始碼解析心得

ChatServer 執行結果

PC:putty.exe 86Duino :Serial Monitor

Server 端 Client 端

Page 483: Arduino 底層原始碼解析心得

Ethernet Library 原始碼概觀

Page 484: Arduino 底層原始碼解析心得

由於 Ethernet Library 相當龐大,這裡不深入討論上面每個 .cpp 裡的函式細節

只討論和移植有關的大架構

Page 485: Arduino 底層原始碼解析心得

Ethernet 資料夾

從 DHCP 伺服器取得動態 IP

從 DNS 伺服器將網址轉換成實際位址

Ethernet 初始化函式

建立 client 端,以 TCP/IP 方式連線

建立 server 端,以 TCP/IP 方式連線

以 UDP 方式連線

https://github.com/arduino/Arduino/tree/master/libraries/Ethernet

Page 486: Arduino 底層原始碼解析心得

Ethernet / utility 資料夾

https://github.com/arduino/Arduino/tree/master/libraries/Ethernet/utility

透過 SPI 介面將命令送給 W5100 晶片

Arduino 提供的底層 API

(部分遵循 BSD Socket 標準,但沒有完全相容)

Page 487: Arduino 底層原始碼解析心得

Arduino Ethernet Library 的架構

Socket

W5100 命令

Arduino Ethernet Shield

Server Client UDP Ethernet

SPI Bus

初始化函式

呼叫

取得/設定 IP、子網路遮罩、閘道器位址等等

Page 488: Arduino 底層原始碼解析心得

Ethernet Library

在 86Duino 上的移植

Page 489: Arduino 底層原始碼解析心得

86Duino 內建的 Ethernet 電路

網路卡內建在 CPU 中 (含 PHY)

Vortex86EX

CPU LAN

差動信號輸出

Transformer

86Duino

底板

(PCI 裝置)

網路接頭

Page 490: Arduino 底層原始碼解析心得

DOS 下的網路驅動程式

內建在 86Duino 韌體裡的 NDIS driver

NDIS: Network Driver Interface Specification,參考資料:

◦ http://en.wikipedia.org/wiki/Network_Driver_Interfac

e_Specification

Page 491: Arduino 底層原始碼解析心得

DOS 下的 Socket Library

BSD Socket 規範: http://web.mit.edu/macdev/Development/MITSupportLi

b/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

◦ …

Page 492: Arduino 底層原始碼解析心得

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

移植

替換

Page 493: Arduino 底層原始碼解析心得

移植後的差異:

以 Ethernet Server:begin() 為例

86Duino

Arduino 呼叫 Socket API

Page 494: Arduino 底層原始碼解析心得

WiFi 函式庫

Page 495: Arduino 底層原始碼解析心得

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

Page 496: Arduino 底層原始碼解析心得

Arduino WiFi shield 電路

圖片出處:http://arduino.cc/en/uploads/Main/arduino-wifi-shield-schematic.pdf

AT32UC3

A1256

電壓準位從

5V 轉成 3.3V

對 Arduino 的通訊介面:SPI

透過內部第二組

SPI Bus

和無線模組通訊

無線模組

HDG104

Page 497: Arduino 底層原始碼解析心得

WiFi 使用範例:WifiCharSever

輸入你的 SSID

輸入 WEP/WAP2 形式的密碼

檢查 WiFi shield

是否存在

Page 498: Arduino 底層原始碼解析心得

WifiCharSever:setup()

begin() 會不斷嘗試連線

假如尚未連線成功

連線成功後,啟動伺服器

印出目前的 SSID,取得的 IP,以及訊號強度

Page 499: Arduino 底層原始碼解析心得

WifiCharSever:loop()

聆聽用戶的連線請求

如果收到用戶的連線請求

第一個用戶連線,送出

Hello 字串

檢查用戶是否有送字元過來,如果有,則返回相同的字元給所有已連結上的用戶

Page 500: Arduino 底層原始碼解析心得

WifiChatServer 執行結果

PC:putty.exe 86Duino :Serial Monitor

Server 端

Client 端

Page 501: Arduino 底層原始碼解析心得

WiFi 原始碼概觀

功能皆類似

Ethernet Library

https://github.com/arduino/Ardu

ino/tree/master/libraries/WiFi

Ethernet

Page 502: Arduino 底層原始碼解析心得

WiFi 原始碼概觀

https://github.com/arduino/Arduin

o/tree/master/libraries/WiFi/utility

Ethernet/utility

底層的 SPI

driver

底層的 SPI

driver

Socket

Interface

Socket

Interface

Page 503: Arduino 底層原始碼解析心得

WiFi Library 在 86Duino 上的移植

移植重點:WiFi Library 與硬體相關的只有 SPI 的部分,其它部分程式碼與硬體無關,可以在 86Duino 上直接延用

◦ 將 spi_drv.cpp 存取方式換成 86Duino SPI 的存取方式

◦ 上層的 wifi_drv、server_drv、WIFIClient

等等都不用動

Page 504: Arduino 底層原始碼解析心得

移植後的 spi_drv.cpp 差異 86Duino Arduino

替換

替換

Page 505: Arduino 底層原始碼解析心得

GSM 函式庫

Page 506: Arduino 底層原始碼解析心得

GSM 簡介

GSM 全名 Global System for Mobile

Communications (全球行動通訊系統)

在台灣,GSM 的頻段是 900、1800MHz,由於在同一頻段內要讓多人使用又要抗干擾,所以實際上傳送數據的速度只達到 9.6Kbps(相當於看一個 200KB 的網頁要需要等 20 幾秒)

Page 507: Arduino 底層原始碼解析心得

GPRS 簡介

GPRS 全名 General Packet Radio Service,在現有 GSM 技術上,加上數據交換節點(具有處理封包的能力),配合動態分配頻段來增加使用率,在連線人數不多的情況下,速度可達 56Kbps ~ 100多Kbps,用來看網頁和圖片,已經綽綽有餘。

Page 508: Arduino 底層原始碼解析心得

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

Page 509: Arduino 底層原始碼解析心得

Arduino GSM shield 電路

使用 PIN2 和

PIN3 做為

Serial 腳位

增加訊號的驅動能力

M10

圖片出處:http://arduino.cc/en/uploads/Main/arduino-gsm-shield-schematic.pdf

Page 510: Arduino 底層原始碼解析心得

使用 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 卡才能撥打電話、發簡訊以及上網(必須先向電信業者開通上網功能)

Page 511: Arduino 底層原始碼解析心得

使用 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

Page 512: Arduino 底層原始碼解析心得

用 Arduino + GSM shield

打電話和接電話

Page 513: Arduino 底層原始碼解析心得

準備

首先,拿一張可用的 SIM 卡,插入 GSM shield

圖片出處: http://arduino.cc/en/Guide/ArduinoGSMShield#toc4

用的是 mini

SIM 卡,手機用的是 micro

SIM 卡,需要使用轉卡才不會掉出來

Page 514: Arduino 底層原始碼解析心得

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

Page 515: Arduino 底層原始碼解析心得

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() 完成基本的初始化

回傳結果都沒問題,就可以開始打電話了

Page 516: Arduino 底層原始碼解析心得

上傳 MakeVoiceCall 之後…

3. 撥出電話

4. 對方接通,開始通話

5. 對方掛斷電話,結束通話

1. 打開 serial monitor

2. 輸入要撥打的電話 (前面需要加上台灣區碼:+886),輸入完後按 Send

Page 517: Arduino 底層原始碼解析心得

用 GSM shield 接電話

ReceiveVoiceCall 範例程式 (內容與 MakeVoiceCall 類似,所以略過)

上傳後打開 serial monitor:

接到一通電話,並顯示電話號碼

雙方通話中

初始化完成,等待別人撥電話進來

Send ‘\n’ 後結束通話

Page 518: Arduino 底層原始碼解析心得

GSM Library 原始碼概觀

Page 519: Arduino 底層原始碼解析心得

GSM Library

到 Arduino 的 GitHub 網站,看看 GSM 資料夾內的檔案: ◦ https://github.com/arduino/Arduino/blob/master/libraries/GSM

Page 520: Arduino 底層原始碼解析心得

GSM Library 檔案

GSM Library 裡面的檔案很多,我們可以大致分類如下:

負責管理 SoftSerial

使用的 buffer

負責送出 PIN 碼、偵測 GPRS 網路等等

負責撥號和建立語音連線

Page 521: Arduino 底層原始碼解析心得

GSM Library 檔案 (續)

負責收發簡訊

在 GPRS 網路上,建立 server/client 端

以 Software Serial 的方式收送 AT command

Page 522: Arduino 底層原始碼解析心得

GSM Library 檔案 (續)

PIN 碼管理工具

Modem 測試工具

GPRS 網路掃描工具

Page 523: Arduino 底層原始碼解析心得

GSM Library 架構

GSM class (負責送出初始化命令)

GSM Soft Serial class

GSM Shield

GSM_SMS

class

GSMClient

Class

GPRS class (負責送出初始化命令)

GSMClient

class

GSMServer

class

1. 每個 class 包含自己專用的 AT

command

2. 當使用者呼叫

GSM API 時,該

API 會把自己的處理程序註冊到一個特殊列表中,讓

SoftSerial 中斷副程式以 callback 的方式來呼叫

Page 524: Arduino 底層原始碼解析心得

GSM Library 運作方式:

以 GSM.begin() 為例

Begin()

ModemConfiguration()

ModemConfigurationContinue()

manageResponse()

GSM3AccessProvider.cpp

GSM3SoftSerial.cpp

Recv() 呼叫 callback 函式

openCommand()

GSM3ShieldV1ModemCore.cpp

manageMsgNow()

中斷副程式

API

其他功能的運作方式皆大同小異

Page 525: Arduino 底層原始碼解析心得

ModemConfigurationContinue()

在其他功能的 .cpp 中,看到以

Continue 結尾的函式,函式內容都差不多,只差在 AT command

不同

第一個 AT

指令

檢查回傳結果 第二個 AT 指令

檢查回傳結果 第三個指令

檢查回傳結果 第四個指令

此函式內容可以說是 AT

command 的腳本。

它最後會被 SoftSerial 的中斷副程式呼叫。

Page 526: Arduino 底層原始碼解析心得

GSM Soft Serial 運作方式:recv() GSM 以既有的 lib 為基礎,再寫一個新的 SoftSerial

用 digitalRead() +

delay() 讀取 8bit 值

假如 buffer 滿了

就送 XOFF 回去

請對方暫緩傳送

檢查是不是特殊字元 ‘w’

假如不是,就放入 buffer 中

這部分是

新增的

Page 527: Arduino 底層原始碼解析心得

GSM Soft Serial 運作方式 (續)

buffer 滿了,則呼叫

callback 處理函式

收到 LF 符號,則呼叫

callback 處理函式

收到 space 符號,則呼叫

callback 處理函式

這部分是

新增的

Page 528: Arduino 底層原始碼解析心得

GSM Library

在 86Duino 上的移植

Page 529: Arduino 底層原始碼解析心得

GSM Library 在 86Duino 上的移植

移植重點:

GSM Library 與硬體相關的只有

SoftSerial 的部分,其它部分程式碼與硬體無關,可以在 86Duino 上直接延用

Page 530: Arduino 底層原始碼解析心得

GSM Library 在 86Duino 上的移植

GSM3SoftSerial.cpp

◦ 直接套用 86Duino softserial Library 的實作方式,然後再加入 GSM SoftSerial 新增的那些 code

◦ 預設的腳位更動,RX 是用 PIN42 (有

attachInterrupt 功能的),TX 是用 PIN3

小改進:

◦ 新增 Hardware Serial 的通訊方式,可以選用

COM1 ~ COM3 其中一組來通訊。

◦ 使用 Hardware Serial 可以保證 GSM Library 工作更穩定

Page 531: Arduino 底層原始碼解析心得

加入 Hardware Serial 功能

不使用中斷

◦ 因為 SoftSerial 已經與各項功能的 callback function

和軟體 flow control 寫在一起,為了實現相同的功能,可能要大改既有的 Hardware Serial library。

改用 polling

◦ 經過觀察,主要 class 在運行過程中,都會持續呼叫名為 ready 的函式,用來得到送出 AT command

的回傳結果

◦ 因此就把 hardware serial 函式加入 ready(),實現原來 SoftSerial 會做的事

Page 532: Arduino 底層原始碼解析心得

GSM3ShieldV1ModemCore.cpp

: manageReceivedData() GSM3SoftSerial.cpp:recv()

添加的

Hardware

Serial code

呼叫

行為相同

原來的 Software

Serial code

GSM3ShieldV1BaseProvider.cpp:ready()

Page 533: Arduino 底層原始碼解析心得

小插曲-遇到 Arduino GSM

Library 的 Bug GSM3VoiceCallService.cpp

GSM3ShieldV1VoiceProvider.cpp

呼叫建構子

Page 534: Arduino 底層原始碼解析心得

小插曲-遇到 Arduino GSM

Library 的 Bug

Bug 來由:

◦ 靜態物件 GSM3VoiceCallService 的實作方式要求另一個靜態物件 GSM3MobileVoiceProvider 先被初始化

◦ 但這兩個物件的初始化順序與編譯器有關,不同平台的編譯器會得到不一樣的結果

◦ 錯誤的初始化順序會造成 GSM3VoiceCallService 使用到尚未給定初值的 theGSM3MobileVoiceProvider

變數而當機

Page 535: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫

Page 536: Arduino 底層原始碼解析心得

SoftwareSerial 簡介

簡單的說,就是用 GPIO 來模擬硬體 UART

的 TX、RX 行為

在 Arduino UNO 上,預設使用 PIN2 和 PIN3

做為 RX 和 TX。

SoftwareSerial 函式庫不允許隨意指定腳位,因為 RX 的行為需要由具有 toggle 中斷的

PIN 腳來模擬。

Page 537: Arduino 底層原始碼解析心得

具有 toggle 中斷的 PIN 腳

Arduino UNO

◦ PIN2

Arduino Mega2560:

◦ PIN10 ~ 15,PIN50 ~ 53,A8 ~ A15

Leonardo:

◦ PIN8 ~ 11,PIN14 ~16

Page 538: Arduino 底層原始碼解析心得

Start bit Stop bit 8 bits data

Arduino 上,RX 行為的模擬方法:

中斷觸發,進入中斷副程式

delay

digitalRead()

delay delay delay delay delay

……

delay delay

digitalRead() digitalRead()

delay

Page 539: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼解析

Page 540: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫資料夾內容

https://github.com/arduino/Arduino/tree/master/libraries/SoftwareSerial

Page 541: Arduino 底層原始碼解析心得

鮑率延遲時間表

這些數值在不同的 CPU 都不一樣,需要實際 tune 過才能確定

SoftwareSerial.cpp: SoftwareSerial()

Start bit 的 delay 資料和 Stop bit 的 delay TX 的 delay

Page 542: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼重要細節

SoftwareSerial.cpp: SoftwareSerial()

初始化數值

設定 Tx, Rx 腳位

Page 543: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼重要細節

設定鮑率

設定傳輸, 接收 延遲時間

等待中斷發生 開始接收資料

SoftwareSerial.cpp: begin()

Page 544: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼重要細節

等待接受訊號觸發中斷

觸發中斷執行 recv()

SoftwareSerial.cpp: ISR()

SoftwareSerial.cpp: handle_interrupt()

Page 545: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼重要細節 SoftwareSerial.cpp: available()

SoftwareSerial.cpp: read()

回傳 buffer 資料數量

接收資料

Page 546: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼重要細節

傳送資料

傳送延遲

傳送延遲

傳送延遲

SoftwareSerial.cpp: write()

Page 547: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼重要細節

讀取 8bit 資料

Start bit 延遲

Stop bit 延遲

SoftwareSerial.cpp: recv ()

Page 548: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫原始碼重要細節

判斷 buffer 是否滿了

將資料丟進 buffer

SoftwareSerial.cpp: recv ()

Page 549: Arduino 底層原始碼解析心得

SoftwareSerial 函式庫的移植:

以 86Duino 為例

Page 550: Arduino 底層原始碼解析心得

SoftwareSerial在 86Duino 上的移植

移植重點:

◦ 在 86Duino 上,要使用具有 attachInterrupt() 功能的 PIN 腳來模擬 RX(TX 與原來一樣),掛中斷的方式與 Arduino 不同

◦ 鮑率延遲的時間表需要實際 tune 過一次

◦ 其餘部分不用動到

Page 551: Arduino 底層原始碼解析心得

86Duino 的鮑率延遲時間表

Page 552: Arduino 底層原始碼解析心得

SoftwareSerial函式庫原始碼重要細節

SoftwareSerial.cpp: listen()

中斷觸發, 執行 handle_interrupt

判斷中斷觸發腳位

Page 553: Arduino 底層原始碼解析心得

SoftwareSerial函式庫原始碼重要細節 SoftwareSerial.cpp: write()

傳送資料

傳送延遲

傳送延遲

傳送延遲

Page 554: Arduino 底層原始碼解析心得

SoftwareSerial函式庫原始碼重要細節 SoftwareSerial.cpp: recv ()

將腳位切換成Encoder

判斷 buffer 是否滿了

將資料丟進 buffer

Page 555: Arduino 底層原始碼解析心得

TFT 函式庫

Page 556: Arduino 底層原始碼解析心得

TFT shield 簡介

工作電壓:5V

螢幕解析度:160 x 128 pixels

色彩深度:16bit (R-5bit, G-5bit, B-6bit)

通訊介面:SPI

附帶 microSD 插槽

TFT 外觀

Page 557: Arduino 底層原始碼解析心得

TFT 函式庫原始碼概觀

https://github.com/arduino/Arduino/tree/master/libraries/TFT

畫圖函式 (點、線、圓等等)

初始化函式

Page 558: Arduino 底層原始碼解析心得

TFT函式庫原始碼重要細節

TFT.h

初始化自定義腳位

初始化 TFT 螢幕

高階的畫圖函式

低階的繪圖函式

(含初始化命令)

Page 559: Arduino 底層原始碼解析心得

TFT 函式庫原始碼重要細節

設定螢幕寬

設定螢幕高

初始化 TFT (送出連續的初始化命令)

設定畫面旋轉方向

TFT.cpp

初始化自定義腳位

Page 560: Arduino 底層原始碼解析心得

TFT 函式庫原始碼重要細節

以 drawFastVLine() 為例:

透過 SPI 函式送命令,其它函式也是一樣

CS 設為 LOW,RS 設為 HIGH

CS 設為 HIGH

utility / Adafruit_ST7735.cpp

Page 561: Arduino 底層原始碼解析心得

TFT Library 在 86Duino 上的移植

移植重點:

◦ 將直接存取 ATmega 暫存器的程式,用

digitalWrite()、 digitalRead() 函式替換

◦ 延用 86Duino 既有的 SPI 函式庫,相關的

函式如 SPI.write() 不需要改動

Page 562: Arduino 底層原始碼解析心得

TFT Library 在 86Duino 上的移植

86Duino: Ada_ST77.cpp Arduino: Adafruit_ST7735.cpp

移植

Page 563: Arduino 底層原始碼解析心得

TFT demo 影片

https://www.youtube.com/watch?v=eZefqL6FtOE

Page 564: Arduino 底層原始碼解析心得

Thank You 感謝大家來參與 Arduino 原始碼讀書會

接下來問題討論時間

DMP Electronics Inc. (瞻營全電子)

[email protected]