フォト
2018年8月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
無料ブログはココログ

« ゲームボーイ(旧タイプ)のパーツ その2 | トップページ | NeoPixel(WS2812B)の制御 その2 »

2018年5月20日 (日)

NeoPixel(WS2812B)の制御

Arduino UnoでNeoPixel(WS2812B)の制御をライブラリ無しで行ってみました。
まずは手持ちのリング形状16個LEDのタイプを制御してみました。

Dscn7924

動いている様子



スケッチ
2018/05/30 修正)

//
// Neopixelの制御 by たま吉さん  2018/05/20
//

//***************
// 定数
//***************

#define PIXCELNUM   16        // Neopixel ピクセル数(LED数)
#define PIN         2         // Neopixel 制御用ピン番号

//***************
// グローバル変数
//***************
uint8_t buf[PIXCELNUM*3];     // Nexpixel用ピクセル色データ(ピクセル数 x 24ビット)
volatile uint8_t * NeoOutReg; // Neopixcel出力レジスタ
uint8_t  NeoBitOut;           // Neopixcelセットビット
uint8_t  NeoBitMask;          // Neopixcelクリア用マスク

//***************
// 関数
//***************

// Neopixel初期化
void NeoInit() {
  memset(buf, 0, PIXCELNUM*3); // バッファの初期化

  // 出力ピンの初期化
  pinMode(PIN, OUTPUT);
  digitalWrite(PIN, LOW);
  NeoOutReg  = portOutputRegister(digitalPinToPort(PIN));  // Neopixcel出力レジスタ
  NeoBitOut  = digitalPinToBitMask(PIN);  // Neopixcelセットビット
  NeoBitMask = ~NeoBitOut;                // Neopixcelクリア用マスク
 }

// Neopixelへ1を出力
inline void NeoOut_1() {
  *NeoOutReg |= NeoBitOut;   // HIGHの出力
  asm volatile(
     "nop"    "\n\t"
     "nop"    "\n\t"     
     "nop"    "\n\t"
     "nop"    "\n\t"     
  );
  *NeoOutReg &= NeoBitMask;  // LOWの出力
}

// Neopixelへ0を出力
inline void NeoOut_0() {
  *NeoOutReg |= NeoBitOut;   // HIGHの出力
  *NeoOutReg &= NeoBitMask;  // LOWの出力
  asm volatile(
     "nop"    "\n\t"
     "nop"    "\n\t"     
     "nop"    "\n\t"
     "nop"    "\n\t"     
     "nop"    "\n\t"
     "nop"    "\n\t"     
     "nop"    "\n\t"     
     "nop"    "\n\t"
  );  
}

// Neopixelへのデータ送信
void NeoUpdate() {
  uint8_t testbit = 0b10000000;
  *NeoOutReg &= NeoBitMask;  // LOWの出力
  delayMicroseconds(50);
  cli(); 
  for (uint8_t i = 0; i < PIXCELNUM*3; i++) {
    if (buf[i] & 128) NeoOut_1(); else  NeoOut_0();
    if (buf[i] &  64) NeoOut_1(); else  NeoOut_0();
    if (buf[i] &  32) NeoOut_1(); else  NeoOut_0();
    if (buf[i] &  16) NeoOut_1(); else  NeoOut_0();
    if (buf[i] &   8) NeoOut_1(); else  NeoOut_0();
    if (buf[i] &   4) NeoOut_1(); else  NeoOut_0();
    if (buf[i] &   2) NeoOut_1(); else  NeoOut_0();
    if (buf[i] &   1) NeoOut_1(); else  NeoOut_0();
  }
 sei();
}

// Neopixelの表示クリア
void NeoCLS() {
    memset(buf, 0, PIXCELNUM*3); // バッファの初期化
    NeoUpdate();                 // 表示更新
}

// 指定したピクセルの色を設定
void NeoSetRGB(uint8_t no, uint8_t R, uint8_t G, uint8_t B, uint8_t flgUpdate=false) {
  if (no < PIXCELNUM) {
    buf[no*3+0] = G;
    buf[no*3+1] = R;
    buf[no*3+2] = B;    
  }
  if (flgUpdate)
    NeoUpdate();
}

// ピクセルのシフト
void ShiftPixel() {
  uint8_t tmpbuf[3];
  memmove(tmpbuf,buf,3);
  memmove(buf, buf+3, (PIXCELNUM-1)*3);
  memmove(buf+(PIXCELNUM-1)*3,tmpbuf,3);
  NeoUpdate();
}

void setup() {
  NeoInit();  // Neopixcelの初期化
  NeoCLS();   // Neopixcelの表示クリア
  
  NeoSetRGB(0, 128,0,0,true); // No.0のピクセルを赤
  NeoSetRGB(1, 0,128,0,true); // No.1のピクセルを緑
  NeoSetRGB(2, 0,0,128,true); // No.2のピクセルを青
}

void loop() {
 delay(80);
 ShiftPixel(); // ピクセルをシフトして更新表示
}

制御を行うためのプロトコル自体は非常に簡単です。
1ポートからLOW or HIGHを指定したタイミングで出力するだけです。

リセット(RET)コード送信後、1つのLED(ピクセル)毎に24ビット分の0 or 1の送信、
複数のLEDの場合、24ビットxLED数分のデータを送信します。

02

リセット(RET)コードは、Treset(50μ秒)間 LOWを出力、
1ビット 0 を送信(0 code)は、T0H間HIGHを出力後、T0L間LOWを出力、
1ビット 1 を送信(1 code)は、T1H間HIGHを出力後、T1L間LOWを出力、
します。

だだし、このタイミングがシビアです。

03

作成したスケッチでは、
   T0H、T1L : 0.375μ秒 (6クロック分)
   T0L、T1H : 0.625μ秒 (10クロック分)

をとしました。
値としては、「Data transfer time」の表の有効範囲から少々ずれていますが、
問題無いようです。

Arduino Unoはシステムクロックが16MHzで動作しています。
1クロックは 1/16000000 = 0.0625μ秒 となります。

わずか、6~10クロックというシビアなタイミングで信号を出力する必要があります。
今回は6~10クロックの待ち時間の調整はインラインアセンブラ命令でNOPを入れて
調整しました。

  *NeoOutReg |= NeoBitOut;   // HIGHの出力
が6クロック
  *NeoOutReg &= NeoBitMask;  // LOWの出力
が3クロック
要していることを考慮しています。

時間待ち中は割り込みを禁止しています。
割り込みが入ると動作に外乱が入り、データが化けます。
また、デジタル出力をdigitalWire()で行うと処理が間に合いません。
そこで、出力レジスタに直接、値を書き込んでいます。

まあ、これだとシリアル通信等、他に何も出来ません。
そこで、次の対策としてSPIを使って何とかしようと思います。

関連記事
 NeoPixel(WS2812B)の制御 その3(2018.05.30)  ・・・ 8x8マトリックスの制御
 NeoPixel(WS2812B)の制御 その2(2018.05.22)   ・・・ SPIを使った制御
 NeoPixel(WS2812B)の制御(2018.05.20)            ・・・ GPIOを使った制御(この記事です)

参考にしたサイト
uratan - WS2812B の駆動タイミングの限界調査
PICマイコンの小部屋 - 秋月でWS2812B買ってしまったので動かないかも知れないけれど作ってみた

« ゲームボーイ(旧タイプ)のパーツ その2 | トップページ | NeoPixel(WS2812B)の制御 その2 »

arduino」カテゴリの記事

表示器制御関連」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/571408/66740123

この記事へのトラックバック一覧です: NeoPixel(WS2812B)の制御:

« ゲームボーイ(旧タイプ)のパーツ その2 | トップページ | NeoPixel(WS2812B)の制御 その2 »