2017年4月29日 星期六

[NodeMCU] Lab1 點亮 8-Bit 2812 RGB LED

之前我們介紹過如何用 Arduino 點亮 8-Bit 2812 RGB LED 燈板,詳

http://pizgchen.blogspot.tw/2017/04/ws2812-8-bit-2812-rgb-led.html

這兒我們要改用 NodeMCU 點亮它。



準備材料

1. 8-Bit 2812 RGB LED 燈板 *1
2. 公排針(間距2.54mm) 4P *1
請先依 http://pizgchen.blogspot.tw/2017/04/ws2812-8-bit-2812-rgb-led.html 文中描述焊妥排針。
3. NodeMCU 開發板 *1
4. 杜邦線母母頭 3P *1


電路接線

用杜邦線依下表將 NodeMCU 與燈板連接起來:

NodeMCU      燈板
5V                   VDC
Gnd                 Gnd
D1                   DI


下載函式庫

1. 點選右側網址下載函式庫 https://github.com/adafruit/Adafruit_NeoPixel
2. 將下載的檔案解壓縮,複製到 Arduino IDE 的 libraries 資料夾裏。
3. 更改名稱為「Adafruit_NeoPixel」。


程式碼

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#define PIN D1  //GPIO5

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(8, PIN, NEO_GRB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.  Avoid connecting
// on a live circuit...if you must, connect GND first.

void setup() {
  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code


  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
  // Some example procedures showing how to display to the pixels:
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
  // Send a theater pixel chase in...
  theaterChase(strip.Color(127, 127, 127), 50); // White
  theaterChase(strip.Color(127, 0, 0), 50); // Red
  theaterChase(strip.Color(0, 0, 127), 50); // Blue

  rainbow(20);
  rainbowCycle(20);
  theaterChaseRainbow(50);
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}


相關連結

NodeMCU 文件 https://nodemcu.readthedocs.io/en/master/en/modules/pwm/
Adafruit https://www.adafruit.com/product/1426


採購資訊

1. 8-Bit 2812 RGB LED http://goods.ruten.com.tw/item/show?21715791439863
2. NodeMCU Lua Wifi 開發板 http://goods.ruten.com.tw/item/show?21716910606278
3. 公排針 http://goods.ruten.com.tw/item/show?21646385415847
4. 杜邦線 母母頭 http://goods.ruten.com.tw/item/show?21629159825718


[NodeMCU] Lab0 安裝與設定

有別於 Arduino 開發板,NodeMCU 開發套件本身就可以連網,它是一款以 ESP8266 模塊作為基礎的開發板,它將 ESP8266 所有的腳位分別引出,可以讓您作為多種用途使用。

目前的 NodeMCU 開發板有兩種版本,其 USB 轉 TTL 一款是 CP2102,如下圖


另一款是 CH340G,這一款號稱 V3 版,如下圖




NodeMCU 與 ESP8266 的主要差異

  • NodeMCU 包含一個 USB to serial 晶片,可隨插即用
  • NodeMCU firmware 可使用 Lua script 撰寫程式

NodeMCU 開發板使用 Lua 來程式設計,而 ESP8266 系列模塊的使用者,只要把 firmware 更新為 NodeMCU,也可以使用 Lua 來撰寫程式。


本文是使用 NodeMCU 基於 CP2102 開發板來作說明。

如果你是首次使用 NodeMCU,在您將 NodeMCU 插入電腦時,它會要求你安裝 CP2102 驅動程式,因為它使用 CP2102 晶片當作 USB 與 UART 的溝通橋樑。

下載 CP2102 驅動程式 http://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers

下載後解壓縮,並且依照您的作業系統分別執行

CP210xVCPInstaller_x86.exe (32位元)

CP210xVCPInstaller_x64.exe (64位元)


檢查 NodeMCU 是否已和電腦正確連接

開啟「控制台」>「裝置管理員」>「連接埠(COM 和 LPT)」。
檢視序列埠是否有被啟用(你我的埠可能會不同),如下圖



使用 Arduino IDE 撰寫程式

為了讓我們可以使用 Arduino IDE 來編寫程式,需要做一些設定,這個步驟只需做一次即可,以後啟動 Arduino IDE 都無需重覆設定。

Step1 開啟 Arduino IDE。

Step2 點擊「檔案」>「偏好設定」,將底下那一行字貼到「Additional Boards Manager URLs:」右側欄位內

http://arduino.esp8266.com/stable/package_esp8266com_index.json

Step3 點擊「確定」鈕,並關閉 Arduino IDE。

Step4 再次開啟 Arduino IDE。

Step5 安裝 ESP8266 工具,點擊「工具」>「板子」>「Boards Manager...」。
(出現「Boards Manager」對話窗)

Step6 捲動右側拉桿到下方,點擊「esp8266 by ESP8266 Community」那一欄,再點擊「Install」鈕。

Step7 點擊「關閉」鈕。



開啟範例程式

在自己編寫程式之前,我們先來執行內建的範例程式。這個程式會讓 NodeMCU 板載的 LED 閃爍。

Step1 開啟 Arduino IDE。

Step2 點擊「工具」>「板子」,選擇「NodeMCU 1.0 (ESP-12E Module)」。

Step3 點擊「序列埠」,選擇正確的埠。

Step4 點擊「檔案」>「範例」>「ESP8266」>「Blink」。

Step5 點擊「上傳」。

此時你可以看到 Arduino IDE 下方的訊息欄有陸續出現許多紅點與上傳的百分比,等待顯示上傳完畢,你就可以看到 NodeMCU 板子上面的 LED 閃爍。



圖片中 NodeMCU 下方那一個是電機驅動板,它可以驅動 2 只馬達。


程式說明

1. 我們可以看到 NodeMCU 板子上面的 LED 在閃爍,可是為什麼我們在程式中的腳位是寫著 "LED_BUILTIN" 呢? 這是因為 "LED_BUILTIN" 是 ESP8266 的關鍵字,它其實是等於 "D0",也就是 D0 腳。


接著我們來看看底下這張 NodeMCU 的腳位圖


由圖中我們可以看到 D0 就是 GPIO16,所以如果我們把程式中的「LED_BUILTIN」改成「GPIO16」,你猜 LED 還會正常閃爍嗎?

答案是否定的。正確的寫法如下:

int ledPin = 16; // GPIO16

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  digitalWrite(ledPin, LOW);
  delay(1000);
  digitalWrite(ledPin, HIGH);
  delay(2000);
}


2. 不知您是否有發現 LED 閃爍的頻率有點怪怪的,程式中明明寫著 LOW 的時間是 1秒,HIGH 的時間是 2 秒,可為什麼 LED 卻是亮 1 秒、暗 2 秒?那是因為 NodeMCU 板子在電路設計上就是 LOW 時才可以點亮該腳位的 LED。

3. D9 腳位可以點亮 ESP8266 板載 LED,但它也是輸出 LOW 時被點亮,HIGH 就熄滅。


Lua 編輯器

除了使用 Arduino IDE 來編寫程式之外,我們也可以在 ESPlorer 編輯器上用 Lua 語言來寫程式。

點擊右側網址下載並安裝 ESPlorer http://esp8266.ru/esplorer-latest/?f=ESPlorer.zip

用滑鼠右鍵點擊「ESPlorer.bat」。此時會出現命令列畫面,隨後就出現 ESPlorer IDE 視窗。



選擇正確的 COM 埠,並設定 UART 通訊的 Baudrate 為 9600。


點擊「Open」鈕







下方清單框內會出現連線訊息,



我的第一支 Lua 程式

NodeMCU 的特色之一,就是能使用 Lua 來撰寫程式。在 ESPlorer 畫面左側的編輯區,輸入第一個 Hello World 程式碼如下:

print("hello world")

完成後,按下 Send to ESP 按紐,即可將程式碼傳送至 NodeMCU 上執行。


相關連結

NodeMCU 官網 http://nodemcu.com/index_cn.html
NodeMCU 參考文件 https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_cn
Instruceables http://www.instructables.com/id/Programming-ESP8266-ESP-12E-NodeMCU-Using-Arduino-/
ESPlorer 編輯器 http://esp8266.ru/esplorer/
Lua 編譯器的用法 http://yhhuang1966.blogspot.tw/2015/07/lua.html


採購資訊

NodeMCU Lua Wifi 開發板基於 CP2102 http://goods.ruten.com.tw/item/show?21716910606278
NodeMCU Lua Wifi 開發板基於 CP2102 電機驅動板 http://goods.ruten.com.tw/item/show?21716910640647
NodeMCU Lua Wifi 開發板基於 CH340G http://goods.ruten.com.tw/item/show?21721367978126
NodeMCU Lua Wifi 開發板基於 CH340G 底板 http://goods.ruten.com.tw/item/show?21718168558530










2017年4月13日 星期四

[WS2812] 8-Bit 2812 RGB LED

傳統方式要控制多顆 RGB LED 在電路接線和程式控制方面是非常煩雜的,然而使用內建 WS2812 晶片的 RGB LED 卻是簡單又方便,不管你要控制幾顆 RGB LED,都只要使用 Arduino 3 支腳位就足夠了。

在這兒我使用 2 串 8-Bit RGB LED 燈條來作示範



準備材料

1. 8-Bit 2812 RGB LED *2



2. 排針 4P(腳距2.54mm) *2
3. 排母 4P(腳距2.54mm) *2


4. 杜邦線母母頭 3P *1
5. Arduino Uno or Nano *1


焊接

1. 將排針焊到燈條的 DI 端。
2. 將排母焊到燈條的 DO 端。
3. 如步驟1 & 2 焊接另一燈條。



電路接線

1. 將兩只燈條連接起來,也就是將燈條的公排針插入另一燈條的母排針。
2. 用杜邦線依下表將 Arduino 與燈條連接起來:

Arduino      燈條
5V               VDC
Gnd             Gnd
D6               DI


下載函式庫

1. 點選右側網址下載函式庫 https://github.com/adafruit/Adafruit_NeoPixel
2. 將下載的檔案解壓縮,複製到 Arduino IDE 的 libraries 資料夾裏。
3. 更改名稱為「Adafruit_NeoPixel」。


執行範例程式

1. 啟動 Arduino IDE。
2. 點擊「檔案」>「範例」>「Adafruit_NeoPixel」>「strandtest」。
3. 找到這一行
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
修改為
Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, PIN, NEO_GRB + NEO_KHZ800);

4. 上傳程式。


影片

video


採購資訊

1. 8-Bit 2812 RGB LED http://goods.ruten.com.tw/item/show?21715791439863
2. 排針 http://goods.ruten.com.tw/item/show?21646385415847
3. 排母 http://goods.ruten.com.tw/item/show?21646384889087
4. 杜邦線 母母頭 http://goods.ruten.com.tw/item/show?21629159825718
5. Arduino Nano http://goods.ruten.com.tw/item/show?21642069565073
6. Arduino Nano & Uno 兩用擴展板 http://goods.ruten.com.tw/item/show?21628077602506


















2017年3月17日 星期五

[玩轉光立方] LED Cube 4x4x4 for Arduino Nano -- 組裝教學

本文要介紹的這是 Arduino Nano 版本的光立方,優點是體積更小更簡潔,但功能比起 Arduino UNO 卻毫不遜色。


來看看影片先

video



準備材料與工具


材料:
1. LED Cube PCB底板 *1
2. 3mm LED * 64
3. M3 25+6mm 尼龍柱 * 4
4. M3x8mm 螺絲 * 4
5. 圓孔排針 * 20
6. 15P排母 * 2
7. 10cm長電線 * 4

另需準備:
1. Arduino Nano 開發板 * 1
2. USB mini 數據線 * 1

工具:
1. 烙鐵 + 焊錫
2. 斜口鉗
3. 尖嘴鉗
4. 鑷子
5. 厚紙板
6. 電鑽 + 3mm 鑽頭
7. 原子筆或細簽字筆
8. 橡皮筋
9. 電阻 100~220R
10. 麵包板


開始動手做

一、檢查所有的 LED 是否有正常發亮

您購買的 LED 即使廠商如何口沫橫飛地述說它們品質是如何地好,建議您也必須要自己詳細查驗,否則您可能有機會遇到懊惱的時候。

1. 計算限流電阻。

藍色 LED 順向電壓約 3.0~3.4V (注意:顏色不同,可承受的電壓也不同),我們取 3.2V 來計算:

(5 - 3.2) / 15mA = 120R

可採用 100R 或 110R 電阻。

2. 將電阻與 5V 電源插入麵包板,逐一插入 LED,檢查 LED 是否正常發光。
將 LED 插入麵包板時要注意 LED 的正負極。



二、彎折 LED

1. 先來看看我們要做出怎樣的東西



2. 取出尖嘴鉗輔助彎折 LED,我們需要彎折出 3 種型式的 LED,下圖右側是未彎折前。
在彎折時須注意 LED 的正負腳,正腳在彎 2 折後仍保持與燈珠同方向,並且每一彎折應盡量保持90度角,如下:

Type A * 48只


Type B * 12只


Type C * 4只


小聲地說...這個工作我是一邊看電視一邊彎折的,不然要彎折 64 只 LED 還真是挺無聊的事情。


三、製作固定 LED 的模板

這個模板可以用來插入 LED,如此可以讓 LED 排列整齊,在焊接時不會移位。

1. 準備一張厚約 1~3mm 的硬紙板。我隨手找了個 68x68mm 的蛋糕盒。



2. 在上面畫上間距 14mm 的九宮格。14mm * 3 = 42mm,九宮格邊長是 42mm。
我先畫上交叉線找出盒子中心點,再由中心點往外畫出九宮格。


3. 用電鑽在交點處鑽出 16 個 3mm 圓孔。


四、焊接 LED

我把紙模寫上座標以利辨識


1. 將 Type A 插到座標(0,0)~(2,2)之間區塊,共 12 只。
注意 LED 的腳位方向。


2. 將 Type B 插到座標(0,3)~(2,3)之間直線,共 3 只。
注意 LED 的腳位方向。



3. 將 Type C 插到座標(3,3),共 1 只。
注意 LED 的腳位方向。


4. 將 LED 所有的負腳全部焊接在一起。
圖片中是焊接好取出的樣子。


5. 您需要做 4 組(L0~L3)上面圖片的 LED 層面。

6. 將 4 組 LED 層面的正腳都焊起來。
注意正腳搭接長度大約是 8mm 左右,盡量讓你的 LED 排列呈現出正方形。



五、將圓孔母排針焊到電路板上

1. 取出 Cube 底板,將 20 支圓孔母排針插到電路板上的 A0~A15 和 D1~D4 位置。
板子有兩面,要注意方向。



2. 為了焊接前翻轉電路板時避免排針掉落,我隨手取一塊 5x7cm 的電路板蓋住排針,小心地用橡皮筋將他們綁在一起。
然後可用鑷子調整排針,讓排針垂直站立,避免歪斜。



3. 翻面,準備焊接。


4. 焊好後移除橡皮筋與電路板。


六、焊接15P排母

1. 將 2 只 15P 排母插入 Cube 底板





2. 為了讓 15P 排母垂直站立,並且以後讓 Arduino Nano 容易插入排母,在此我們將 Arduino Nano 開發板插到排母。
請注意,Arduino Nano 的 USB 頭須朝外。


3. 翻面焊接排母。


七、組立 LED Cube

1. 在 Cube 底板四個角落用 M3 螺絲鎖住尼龍柱。


2. 將 LED 腳謹慎插入 A0~A15 排針圓孔。
可用鑷子或尖嘴鉗輔助,夾住 LED 腳插入排針圓孔。


3. 在 4 個層焊上電線,並將電線另一端分別插入 D1~D4 排針圓孔。
同樣地,可用鑷子或尖嘴鉗輔助,夾住電線末端插入排針圓孔。



程式

//2017-01-01 LED_Cube4_01.ino
//從頂層到底層逐一點亮 LED

#define CUBE_SIZE 4
#define PLANE_SIZE CUBE_SIZE*CUBE_SIZE
#define PLANE_TIME 20
#define LED_TIME 500

int LEDPin[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, A0, A1};
int PlanePin[] = {A2 , A3, A4, A5};

void setup()
{
  int pin;
  for (pin = 0; pin < PLANE_SIZE; pin++) {
    pinMode(LEDPin[pin], OUTPUT);
    digitalWrite(LEDPin[pin], LOW);
  }
  for (pin = 0; pin < CUBE_SIZE; pin++) {
    pinMode(PlanePin[pin], OUTPUT);
    digitalWrite(PlanePin[pin], HIGH);
  }
}

void loop()
{
  for (int plane = 0; plane < CUBE_SIZE; plane++)
  {
    digitalWrite(PlanePin[plane], LOW);
    for (int led = 0; led < PLANE_SIZE; led++)
    {
      digitalWrite(LEDPin[led], HIGH);
      delay(LED_TIME);
      digitalWrite(LEDPin[led], LOW);
    }
    digitalWrite(PlanePin[plane], HIGH);
    delay(PLANE_TIME);
  }
}


程式說明

1. UNO 腳位 D0~D13 & A0~A1 分別控制每一顆 LED,D0 控制的 LED 是靠近 D0 腳位那一顆,A1 腳位則是控制距離 D0 最遠的那一顆 LED。A2 控制頂層 LED,A5 則是控制底層 LED。

2. 把所有的腳位都設定成 OUTPUT,才可以指定它要 HIGH 或 LOW。當指定它為 HIGH 時電位是 5V;當指定它為 LOW時電位是 0V,相對於 5V 而言可以把它視為是 GND。

3. 在初始階段為了讓所有的 LED 都不亮,所以在 setup() 階段將 16只 LED 腳位都設為 LOW,並且將控制層面的 4 只腳位都設為 HIGH。



相關連結

1. LED 限流電阻計算 http://gc.digitw.com/Program/Resistor4LED/Resistor4LED.htm
2. Arduino UNO 開發板 https://www.arduino.cc/en/Main/ArduinoBoardUno
3. 玩轉光立方 LED Cube 4x4x4 -- 程式2 http://pizgchen.blogspot.tw/2017/01/led-led-cube-4x4x4-2.html
4. 圖形編輯器 https://www.robota.nl/en/blog/led-cube-4x4x4-pattern-designer/led-cube-simulation-and-pattern-generator.html
5. 免費的專業圖形編輯器 http://www.solderlab.de/index.php/software/glediator


採購資訊

光立方 LED Cube 4x4x4 for Arduino Nano http://goods.ruten.com.tw/item/show?21711527533658



2017年3月13日 星期一

[Arduino] 淺談記憶體2 -- PROGRAM

在前篇文中( http://pizgchen.blogspot.tw/2017/03/arduino-1.html )我們說明了 Arduino 有哪些類型的記憶體,其中有提到一個關鍵字「PROGRAM」,本文旨在說明如何使用 PROGRAM 這個變數修飾符。


使用 PROGRAM 的時機

Arduno UNO 只有 2k bytes 的 SRAM 空間讓您存放及運作變數,若是您要將大量的訊息送到序列監控視窗顯示出來,或是您要設置一個供給查表法使用的大資料量表格,這時您有可能會使用超過 2k bytes 的空間。

在您使用超過 2k bytes 的 SRAM 空間時,也許編譯時沒有異狀,但這並不表示您的程式能夠正常運作。

要解決 SRAM 空間不足的問題,我們可以將這些大量資料從 SRAM "搬到" Flash (程式碼就是儲存在 Flash),在需要運作變數時再將這些資料從 Flash "搬回" SRAM 內。那麼如何讓 Arduino 知道哪些資料將會被這樣使用,答案就是使用 PROGRAM 這個變數修飾符來宣告。


如何使用 PROGRAM

PROGRAM 變數修飾符是被定義在 pgmspace.h 之中,它是 AVR 架構中 pgmspace.h 函式庫眾多函式的其中之一。所以您要使用 PROGRAM 之前,必須在程式的開頭包含入這個函式庫,如下:

#include <avr/pgmspace.h>

然後,使用下列句法給值

const dataType variableName [ ] PROGRAM = {data0, data1, data2...};

dataType - 表資料型態。
variableName - 表變數名稱。


PROGRAM 只是一個變數修飾符,它沒有被規定應該放在哪個位置,所以下列三種宣告方式都可以通過編譯器的解析:

const dataType variableName [ ] PROGRAM = {data0, data1, data2...};
const PROGRAM dataType variableName [ ] = {data0, data1, data2...};
const dataType PROGRAM variableName [ ] = {data0, data1, data2...};

雖然 PROGRAM 可以使用在單一變數上,但大多情況我們會用它來處理大量的資料,尤其是大量的陣列資料。下列表格讓您瞭解與比較不同的資料型態會佔用多少記憶體:



再提醒您一次,我們需要兩個步驟來使用 PROGRAM,首先用 PROGRAM 宣告一個變數並給值,然後要使用這些變數的值時再將它從 Flash 讀回到 SRAM。


範例

下列程式片段在示範如何讀寫 char (1 byte) 和 int (2 bytes) 到 PROGRAM。

#include <avr/pgmspace.h>


// save some unsigned ints
const PROGMEM  uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};

// save some chars
const char signMessage[] PROGMEM  = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

unsigned int displayInt;
int k;    // counter variable
char myChar;


void setup() {
  Serial.begin(9600);
  while (!Serial);

  // put your setup code here, to run once:
  // read back a 2-byte int
  for (= 0; k < 5; k++)
  {
    displayInt = pgm_read_word_near(charSet + k);
    Serial.println(displayInt);
  }
  Serial.println();

  // read back a char
  int len = strlen_P(signMessage);
  for (= 0; k < len; k++)
  {
    myChar =  pgm_read_byte_near(signMessage + k);
    Serial.print(myChar);
  }

  Serial.println();
}

void loop() {
  // put your main code here, to run repeatedly:

}


字串陣列


將資料設置為字串陣列通常是最方便使用的,尤其是要將大量的文字顯示到 LCD 之中時。因為字串本身就是一個字元陣列,而字串陣列實際上就是一個二維的字元陣列。

傾向於將大型的結構化資料放到 Flash 之中,往往是比較好的方式。下列程式碼就在說明這樣的概念:

#include <avr/pgmspace.h>


// save some unsigned ints
const PROGMEM  uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};

// save some chars
const char signMessage[] PROGMEM  = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

unsigned int displayInt;
int k;    // counter variable
char myChar;


void setup() {
  Serial.begin(9600);
  while (!Serial);

  // put your setup code here, to run once:
  // read back a 2-byte int
  for (= 0; k < 5; k++)
  {
    displayInt = pgm_read_word_near(charSet + k);
    Serial.println(displayInt);
  }
  Serial.println();

  // read back a char
  int len = strlen_P(signMessage);
  for (= 0; k < len; k++)
  {
    myChar =  pgm_read_byte_near(signMessage + k);
    Serial.print(myChar);
  }

  Serial.println();
}

void loop() {
  // put your main code here, to run repeatedly:

}


注意

請注意,為了要使用 PROGRAM,您必須將變數指定為全域變數(Global)或靜態變數(Static)。

下列程式碼若是被放在函式裏面,它將不會運作

const char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n";

下列程式碼若是被放在函式裏面,它也會運作

const static char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"


F() 函式

如果您在程式中有使用這樣的句法

Serial.print("Write something on  the Serial Monitor");

這串要印出的文字通常是被存放在記憶體之中,如果您印出非常大量這樣的文字到 Serial Monitor,將會很容易把記憶體耗光。如果您有閒置的 Flash 記憶體空間,您可以很輕易地將要印出的文字存放到 Flash 裏,如下:

Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));


相關連結

淺談記憶體1 -- Memory http://pizgchen.blogspot.tw/2017/03/arduino-1.html

Arduino PROGRAM https://www.arduino.cc/en/Reference/PROGMEM