先安裝好 binutils-avr gcc-avr avr-libc:
sudo apt-get install binutils-avr gcc-avr avr-libc
使用 open source 的 grbl 當範例程式(官網 https://github.com/gnea/grbl/releases), 下載解壓縮後, 進入主目錄並編譯 :
wget https://github.com/grbl/grbl/archive/master.zip && unzip master.zip
cd grbl-master && make
安裝燒程式:錄
sudo apt-get install avrdude
參考資料:https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266AVRISP
空的 avr uP 必須先透過 ICP (in-circuit programing) 的方式預先燒錄一段內建 ISP(n-System programing)的 bootloader, 之後才能透過 serial port 來更新軔體(Firmaware), 另一種是花錢買 ICSP (in-circuit serial programming)燒錄器, 而 AVR 的 uP 像是 ATmega328P 能透過 SPI 介面來燒內部的 flash錄.
我之前買過一個 ESP8266 的開發板, 本身也可以拿來當作 AVR ICSP 的燒, 安裝完 ESP8266 的 arduino 後, 打開位在 ~/home/mint/.arduino15/packages/esp8266/hardware/esp8266/2.7.4/libraries/ESP8266AVRISP, 可以將他複製到專案裏,稍作修改, 編譯後再燒錄器錄到 ESP8266, 他就變成一個名副其實的無線 AVRISP 燒. 燒錄器流程要使用 avrdude 命令, 把 Hex 檔案傳送給 ESP8266, 由 ESP8266 用自身的 SPI 介面燒寫到 ATmega328P錄 AVR uP 的 flash 上, 命令列執行格式:
avrdude -C avrdude.conf -c avrisp -p ATmega328P -P net:avrisp -U flash:w:grbl.hex:i
上述 -C avrdude.conf 是 avrisp 是 esp8266 執行 mdns 的 localhost:port(例如用 -P net:esp8266.local:328), 或是直接用 -P net:ip:port, 例如 -P net:192.168.1.12:328, 至於 grblavrdude 的事前設定檔(可以將參數放在檔案裏面), 而 -P net: 參數中的.hex 正是前面編譯 grbl 完, 要燒錄的軔體程式(hex 格式檔),至於 esp8266 與 ATmega328P 接線方式要參考 原始碼裡面的 README.rst 說明AVRISP
-----------------+-------------
|ESP8266 IO/JTAG |AVR SPI|ICSP/
|================+============|
| GPIO14(D5/TMS)>| SCK |
|----------------+------------|
| GPIO12(D6/TDI)<| MISO |
|----------------+------------|
| GPIO13(D7/TCK)>| MOSI |
|----------------+------------|
| GPIO16(D0) >| RESET |
-----------------+-------------
AVR ICSP SPI(Slave)/ESP8266 SPI(Master):
MISO --|1 2|-- VCC
SCK --|3 4|-- MOSI
RESET --|5 6|-- GND
ESP8266 的 GPIO12(TDI), GPIO13(TCK), GPIO14(TMS), GPIO15(TDO) 是內建的 SPI/JTAG 硬體介面, RESET 腳, 要注意的是 ESP8266 是 3.3V 的裝置, 介面之間的邏輯準位若電壓太高恐會把 IC 弄壞或是造成無法開機. 我改用 GPIO16(D0) 作為 另外因為在 ESP8266 來說是輸入接腳, 可以簡單用 1k 串聯 2k 下地電阻的分壓器(Voltage divider)作為 Level shifter 來連接, 而 MISO (GPIO12)對於 UNO(Arduino 板子上的 RESET 接腳有個 10k Ohm 提升電阻拉到 5V, ATmega328P)為免影響 ESP8266 開機,在 ESP8266 端加個電晶體可以(buffer)隔開, 亦或是把它簡單用 1.5k 下地電阻把把它拉到 0.7V 以下.
以下執行命令不使用 -C avrdude.conf, 而是直接下命令燒錄 grbl.hex, 假設 esp8266 在區域網路獲得的 ip 位址是 192.168.12.106, 而 service tcp port 是 328, 執行 avrdude 燒錄:
avrdude -c avrisp -p ATmega328P -P net:192.168.12.106:328 -U flash:w:grbl.hex:i
警告:使用 avrdude 直接燒錄若下的參數錯誤, 恐會造成無法再燒錄的悲劇, 因為可能會清掉原來的 bootloader, 若是內部的 fuse 燒入錯誤, 將造成無法透過 SPI 再更新軔體!若真的想要燒入內部 fuse 可先參考
https://www.engbedded.com/fusecalc
燒錄程序畫面截錄:
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.01s
avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "grbl.hex"
avrdude: writing flash (29864 bytes):
Writing | ################################################## | 100% 8.66s
avrdude: 29864 bytes of flash written
avrdude: verifying flash memory against grbl.hex:
avrdude: load data flash data from input file grbl.hex:
avrdude: input file grbl.hex contains 29864 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 7.34s
avrdude: verifying ...
avrdude: 29864 bytes of flash verified
avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF)
avrdude done. Thank you.
後記:
使用 arduino SDK 修改 AVRISP 程式啟用 TCP port 328 當 sevice port, 以及 GPIO16 作為 RESET 腳:
#include <ESP8266AVRISP.h>
ESP8266AVRISP avrISP(328, 16);
void setup(){
Serial.begin(115200);
// ...
avrISP.begin();
}
void loop() {
// ...
static AVRISPState_t last_state = AVRISP_STATE_IDLE;
AVRISPState_t new_state = avrISP.update();
if (last_state != new_state) {
switch (new_state) {
case AVRISP_STATE_IDLE: Serial.printf("[AVRISP] idle\n");
break;
case AVRISP_STATE_PENDING:Serial.printf("[AVRISP] pending\n");
break;
case AVRISP_STATE_ACTIVE:
Serial.printf("[AVRISP] programming\n");
break;
default: break;
}
last_state = new_state;
}
if (last_state != AVRISP_STATE_IDLE) avrISP.serve();
}
沒有留言:
張貼留言