Adafruit TrinketでWS2822Sをアニメーション点灯

目的

  1. WS2822SをAdafruit Trinketから制御する
  2. アニメーションパターンを自分で定義して様々な色/早さで光らせる
  3. アニメーションを簡単に切り替えられるようにする
    (最終的にはヘリポートにLEDを埋め込みRCヘリコプターの距離に応じてパターンを色々変化させるため)

出来上がった物

回路図

  • Trinket 0Pin
    WS2822SのADDRI (Address Input) に接続 → 他のWS2822Sへチェーン接続
  • Trinket 1Pin
    各WS2822SのDAI (Data Input)に接続
  • Trinket 2Pin
    点灯パターン切り替わりテストのためGNDへ接続出来るようにジャンパケーブルを接続
  • その他
    TrinketのGNDはWS2822Sと共有

注意点 WS2822Sの電源はTrinketから出さないこと

ソースコード

#include <Ws2822s.h>
#include <avr/interrupt.h>

// WS2822Sセットアップ
#define NUM_PIXELS 3   // 使用するWS2822Sの数
#define LED_PIN 1      // WS2822SのDAIピンにつなげるArduinoのピン番号
#define ADR_PIN 0      // WS2822SのADRIピンにつなげるArduinoのピン番号

// スイッチセットアップ
#define SWITCH 2

Ws2822s LED(LED_PIN , NUM_PIXELS);

// アニメーションパターンの定義
#define RGB 3
#define FRAME_MAX 3
PROGMEM const uint8_t displayAnim[][RGB*NUM_PIXELS*FRAME_MAX] = {  
  // R    G    B
  // LED1
  // LED2
  // ...
  // アニメーションパターン0
  {
    0x00,0x00,0x11,
    0x00,0x00,0x00,
    0x00,0x00,0x00,

    0x00,0x00,0x00,
    0x00,0x00,0x11,
    0x00,0x00,0x00,

    0x00,0x00,0x00,
    0x00,0x00,0x00,
    0x00,0x00,0x11,
  },

  // アニメーションパターン1
  {
    0x11,0x00,0x00,
    0x00,0x00,0x00,
    0x11,0x00,0x00,

    0x00,0x00,0x00,
    0x11,0x00,0x00,
    0x00,0x00,0x00,

    0x11,0x00,0x00,
    0x00,0x00,0x00,
    0x00,0x00,0x00,
  },
};

// interrupt関数で使いまわす変数
volatile uint8_t  animPattern;  // 実行するアニメーションのパターン  
volatile uint8_t  animFrameNum; // タイマー割り込みが toggleCount 回実行されたらアニメーションフレームが1進む  
volatile uint16_t animWaitTime; // タイマー割り込みが toggleCount 回実行されたらアニメーションフレームが1進む  
volatile uint16_t toggleCount;  // タイマー割り込み時にカウントアップされるカウンタ

void setup()  
{
  // put your setup code here, to run once:

  // スイッチ入力を受付
  pinMode(SWITCH, INPUT);
  digitalWrite(SWITCH, HIGH);

  // tnx for information
  // http://w8bh.net/avr/TrinketTimers.pdf
  // http://tetunori.hatenablog.com

  // Disable interrupts
  cli();

  // Clear Timer on Compare
  TCCR1 = (1<<CTC1);
  // all of the prescaler bits are set to 1. The prescaler divides the 8 MHz clock by 16384
  // resulting in a counter frequency of 488 Hz
  TCCR1 |= (1<<CS13) | (1<<CS12) | (1<<CS11) | (1<<CS10);
  // The output frequency for CTC mode is: freq(Hz) = 8 MHz/2/prescaler/(OCR1C+1)
  // resulting a calling TIM1_COMPA_vect frequency is 4.8 Hz
  OCR1C = 50;
  // Enable Timer1 interrupt (compare mode)
  TIMSK = (1 << OCIE1A);

  // init WS2822S Address (EEPROMの寿命を考えていません)
  LED.setAddress(0, NUM_PIXELS-1);

  //色配列の初期化
  setAnimationPatternToBlank();
  setAnimationPattern(1, 1);

  // Enable interrupts
  sei();
}

void loop()  
{
  // put your main code here, to run repeatedly:
  // スイッチが押された際に点灯パターンを切り替え
  if (! digitalRead(SWITCH)) {
    setAnimationPattern(1, 1);
  } else {
    setAnimationPattern(0, 10);
  }
}

void setAnimationPatternToBlank()  
{
  // 全てのWS2822Sピクセルの色情報をR,G,B=0,0,0とする
  for (int ledAddr=0; ledAddr<NUM_PIXELS; ledAddr++) {
    LED.setColor(ledAddr, 0, 0, 0);
  }
  LED.send();
}

void setAnimationPattern(uint8_t _animPattern, uint16_t _animWaitTime)  
{
  // アニメーションのパターンが現在の物と異なったら初期化
  if (animPattern != _animPattern) {
    animWaitTime = _animWaitTime;
    animPattern  = _animPattern;
    animFrameNum = 0;
  }
}

// LEDの色情報を1秒以内に再送信
ISR(TIM1_COMPA_vect)  
{
  // 設定されたアニメーションのパターンとフレームをLEDに設定 & 送信
  for (int ledAddr=0; ledAddr<NUM_PIXELS; ledAddr++) {
    LED.setColor(ledAddr, pgm_read_byte_near(&displayAnim[animPattern][(animFrameNum*NUM_PIXELS*RGB)+(ledAddr*RGB)]),
                          pgm_read_byte_near(&displayAnim[animPattern][(animFrameNum*NUM_PIXELS*RGB)+(ledAddr*RGB)+1]),
                          pgm_read_byte_near(&displayAnim[animPattern][(animFrameNum*NUM_PIXELS*RGB)+(ledAddr*RGB)+2]));
  }
  LED.send();

  // タイマー割り込みのタイミング * animWaitTimeの値で アニメーションのフレームを進める
  if (toggleCount < animWaitTime) {
    toggleCount++;
  } else {
    if (animFrameNum < (FRAME_MAX-1)) {
      animFrameNum++;
    } else {
      animFrameNum = 0;
    }
    toggleCount = 0;
  }
}

使い方

  1. SwitchScienceのWS2822Sライブラリを読み込めるようにしてください
    https://github.com/SWITCHSCIENCE/WS2822S

  2. WS2822Sの個数を定義
    LEDの個数が代わっても対応できるようになっております
    NUM_PIXELS = [WS2822S接続個数]
    として下さい。

  3. アニメーションパターンの登録
    displayAnimに自分の好みの点灯パターンをメモリが許す限り登録して下さい。

注意事項

  • LED個数分アニメーションパターンを定義する事
  • FRAME_MAX分アニメーションパターンを定義する事
  // アニメーションパターン0
  {
  //アニメーション1フレーム目
  //Rの強さ, Gの強さ, Bの強さ
    0x00,0x00,0x11, // LED Address 1
    0x00,0x00,0x00, // LED Address 2
    0x00,0x00,0x00, // LED Address 3
  },

  // アニメーションパターン1
  {
  //アニメーション1フレーム目
  //Rの強さ, Gの強さ, Bの強さ
    0x00,0x00,0x11, // LED Address 1
    0x00,0x00,0x00, // LED Address 2
    0x00,0x00,0x00, // LED Address 3

  //アニメーション2フレーム目
  //Rの強さ, Gの強さ, Bの強さ
    0x00,0x00,0x00, // LED Address 1
    0x00,0x00,0x11, // LED Address 2
    0x00,0x00,0x00, // LED Address 3
  },
  ...


4. アニメーションパターンの切り替え関数を呼び出し

アニメーションを切り替えたい際に下記で呼び出して下さい

// アニメーション1 をアニメーション切替速度 1で設定
// 1が一番早い
setAnimationPattern(1, 1);  

WS2822Sのアドレス設定を起動時に毎回やっておりますので起動時に眩しく白色に光ります、その際にWS2822S内のEEPROMにアドレス書き込みが行われております、よって書き換え回数等寿命を考慮していません(データシートには寿命についての記載特になし)

アニメーション切り替え速度を1以上に高速にするためにはタイマー割り込み速度をより高速にする必要があります http://kouzu.info/using-timer-interrupt-on-adafruit-trinket/

利用した物

  • Adafruit Trinket 5V Logic

https://www.switch-science.com/catalog/1501/

  • WS2822SシリアルLEDタイルモジュール(5個入り)
    フルカラーシリアルLED(色・明るさ情報を1線で伝送できるLED)
    PICマイコン時代からRGBの3色LED制御に嫌気がさしていましたが
    今やLEDにマイコンが内蔵される時代なんですね、、、びっくりです。

バラで販売している所に比べてだいぶ割高ですがブログ等で利用方法やライブラリ公開がされているので、Trinketと合わせて購入させて頂きました。

https://www.switch-science.com/catalog/2206/

  • ブレッドボード
    多少余裕のある大きさをおすすめします
    自分は秋月電子で600円の物を利用しています。

http://akizukidenshi.com/catalog/g/gP-00319/

  • ジャンパキット 使い捨てでも良ければ安いものでも全然いけます

お高いですがヘタリが少なく繰り返し使え、ブレッドボードに刺さらない部品もクリップで接続できたり便利です

参考情報

http://tetunori.hatenablog.com/entry/2015/05/17/120000
とても参考になりました、ありがとうございました。

takashi