Translate

ページ

2016年2月20日土曜日

nRF52832 + WS2812B その5: I2S で制御

(動画、BGMあるので注意。うるさくてすいません。)

I2SでWS2812Bをドライブ

これまでやってきたSPIを使う方法を振り返ってみます。
nRF52のSPIベースのeasyDMAでは1度のDMA伝送で送れるバッファのサイズが256バイトだったので、約20個ずつ連続して信号を送り出す必要がありました。しかも本来の仕様では1.25μ秒で送るべき1ビット分のデータを1μ秒で送っているためか、電源電圧が下がった時などにうまく制御しきれずに後半のLEDの幾つかが明るい色で光りだすという現象が発生していました。
まぁ、なんとか制御してみたものの、少し行き詰りを感じていました。

そこへ、I2SでWS2812Bをドライブしているよ!って話を聞きました。



nRF52832にも、easyDMAを使ったI2Sが備わっています。
調べてみると、次のことが分かりました。
  • 伝送レートの設定が幅広く備わっており、3.2MHzのビットレートが作れる。これは、4ビットが1.25μ秒となり、WS2812Bの仕様に合致したシグナルが生成可能(Ref. CONFIG.MCKFREQ)
  • easyDMAのサイズ指定バッファが最大16384あり、LED 1365個分のデータを一気に送ることができる(SPIの時のように分割する細かく分割する必要がない。Ref. RXTXD.MAXCNT)
実際にやってみたら、SPIをだましだまし使った時よりも処理も安定し、制御が乱れることがなくなりました。プログラムもシンプルです。

WS2812Bドライブ方法

WS2812Bのドライブ信号は、次の通りです。

3.2MHzの信号の1ビットは312.5n秒ですので、 
0codeは312.5*1のHと312*3のLを組み合わせて、
1codeは312.5*3のHと312*1のLを組み合わせて、
作成することができます。

レベル変換IC(TXB0104)

nRF52832のGPIOは3.3V, WS2812Bのシグナルレベルは5Vですので、レベル変換が必要です。

SPIでドライブしていた時に調子の良かったレベル変換IC TXB0104を引き続き使いました。製品はこちら。双方向である必要性は無いので、もっと良いソリューションがあると思います。


ハードウェア概要

LEDテープを味付け海苔の空き容器に巻きつけています(前回からまき直し、一周17個です。4mのLEDテープは先に行くほど電圧が落ちて輝度が落ちるので、縦方向に電源のバイパス配線を追加しています)。
  • ボードはnRF52 DK
  • P0..25 をレベル変換IC(ブレッドボード上のTXB0104) ICを使ってLEDテープDinへ
  • 電源はLED用に別途準備必要です。
    • 試験段階は20Aのスイッチング電源、調整完了後モバイルバッテリー(5V2A)利用



その後、バッファはシールド形式のブレッドボードに搭載しnRF52-DKに被せました。
撮影時の状態は以下の通り。



ソフト概要


今回作ったプログラムはgithubに載せました。
https://github.com/takafuminaka/nRF52832/tree/master/i2s_ws2812b_demostration_planB
(https://github.com/takafuminaka/nRF52832.git)

nRF5 SDK 11.0.0-2.alpha_bc3f6a で動作させています。

W2812Bの制御に必要なルーチンはws2812b_drive, i2s_ws2812b_driveディレクトリに集約しています。

main.cで、led_array_work配列にRGBの各輝度を設定して、更新処理を繰り返し呼び出しています。

処理の流れ

  • 処理全体はデモパターン3種の繰り返し
  • 各デモは次を設定時間ループで実行
    • led_array[*].green, led_array[*].red, led_array[*].blueに各要素の輝度(0~255)を設定
    • ws2812b_drive_current_cap(led_array_work, NUM_LEDS, CURRENT_LIMIT); // 電流制限のための輝度調整
    • ws2812b_drive_dim(led_array, NUM_LEDS, dim); // フェードイン、アウトの輝度調整
    • i2s_ws2812b_drive_xfer(led_array, NUM_LEDS, I2S_STDO_PIN); // LED更新
    • nrf_delay_ms(DELAY_MS); // 次の更新までの待ち
  • LED更新処理は、ハードウェアの初期化、転送バッファメモリの確保、実際の伝送、伝送完了後のハードウェアの終了処理をすべて1つの関数にまとめました。通常の利用では、この方が使いやすいと思います。
  • ただし、毎回メモリの確保・開放を行うなど無駄が多いので、効率や処理速度を重視する場合は、当該関数の処理を展開してください。
  • 電流制限はWS2812B の消費電流調査のデータを参考に作成しました。上記動画中、USB電流計が電圧と電流を交互に表示しています。1.5A制限のプログラムで1.3A位に収まっていますので、うまく機能しているようです。

調整パラメータ

ファイルproject.h

#define NUM_LEDS (240) // Number of LEDs (LED総数)
#define MAX_INTENSE (64) // Max intense of random LEDs for "flashing_random" (flashing_random用のランダム色LEDの最大輝度)
#define MAX_INTENSE2 (255) // Max intense of "shooting start" running bright LEDs for all demos.
#define MAX_INTENSE3 (64) // Max intense of "rainbow LEDs" for "running_rainbow" and "running_rainbowv" demos.
#define MIN_INTENSE (1) // Minimum intense of randaom LEDs for "flashing_random" (flashing_random用のランダム色LEDの最小輝度)
#define ROW_SIZE (17) // Count of LEDs for each line (1巻き分のLEDの数)
#define CURRENT_LIMIT (1500) // Current limit of LEDs (mA) (総電流制限値)
#define FADE_IN_MS (6000) // Fade in/out period (フェードイン・アウトの秒数)


ファイルmain.c

新しいデモを追加する場合は、demo_list_t demo_listに定義を追加してください。
各デモは初期化処理、更新処理、終了処理を準備し、1回更新後の待ち時間(ms)、デモの継続時間(ms)、デモの更新処理の所要時間(継続時間調整用、ms)をパラメータ設定しています。


0 件のコメント:

コメントを投稿