2020年11月10日 星期二

在 Linux mint 上玩 ATmega328P

先安裝好 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 燒錄器. 燒錄 AVR uP流程要使用 avrdude 命令, 把 Hex 檔案傳送給 ESP8266, 由 ESP8266 用自身的 SPI 介面燒寫到 ATmega328P 的 flash 上, 命令列執行格式:

  avrdude -C avrdude.conf -c avrisp -p ATmega328P -P net:avrisp -U flash:w:grbl.hex:i

上述 -C avrdude.conf avrdude 的事前設定檔(可以將參數放在檔案裏面), 而 -P net: 參數中的 avrisp 是 esp8266 執行 mdns 的 localhost:port(例如用 -P net:esp8266.local:328), 或是直接用 -P net:ip:port, 例如 -P net:192.168.1.12:328, 至於 grbl.hex 正是前面編譯 grbl 完, 要燒的軔體程式(hex 格式檔),至於 esp8266 與 ATmega328P 接線方式要參考 AVRISP 原始碼裡面的 README.rst 說明

-----------------+-------------
|ESP8266 IO/JTAG |AVR
ICSP/SPI|
|================+============|
| 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 硬體介面, 我改用 GPIO16(D0) 作為 RESET 腳, 要注意的是 ESP8266 是 3.3V 的裝置, 介面之間的邏輯準位若電壓太高恐會把 IC 弄壞或是造成無法開機. 而 MISO (GPIO12)對於 ESP8266 來說是輸入接腳, 可以簡單用 1k 串聯 2k 下地電阻的分壓器(Voltage divider)作為 Level shifter 來連接, 另外因為在 Arduino UNO( ATmega328P) 板子上的 RESET 接腳有個 10k Ohm 提升電阻拉到 5V, 為免影響 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();

  }

2020年11月3日 星期二

C++ 在 static 函數內使用本體

當在 class 裏面放置一個 static 函數時, 其實它只算是一個符號聯結, static 的函數實體會搬離到類型之外, 同時它已俱備唯一的函式本體,可以被呼叫, 但無法在 static 函式裏面使用 this 指標, 主因是它已脫離類型的範疇(out of scope), 但可以透過參數傳遞的方式, 利用別名 &變數, 或是指標 * 變數接收類型的本體(instance), 例如:

class  A_new_Class {

      int    aVar = 10;

      int    bVar = 5;

      static   void  function_with_instance(A_new_Class  &this_instance){

         printf("%d\n",  this_instance.aVar);// ok, it's  a parameter of instance's alias

          // printf("%d\n",  *this.aVar); // compile error!  this is invalid

      }

      static   void  function_with_this(A_new_Class  *this_pointer){

         printf("%d\n",  this_pointer -> bVar);// ok, it's a parameter of pointer

        // printf("%d\n",  this -> bVar); // compile error!  this is invalid

      }

    void call_function_with_this( ) {

            function_with_this(this); // call static function to use 'this' pointer

    } 

   void call_function_with_this_instance( ){

           function_with_instance(*this); // call static function to use 'this' instance

  }

      A_new_Class ( ){

          printf("instance of A\n");

      }

} ;


int main( ){

     class a;  // instance of A

     a.function_with_instance(a);// 10

     a.call_function_with_this_instance( );// 10


     a.function_with_this(&a);  // 5

     a.call_function_with_this( );  // 5

}


     

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...