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 開發板插到排母。


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