Translate

ページ

2016年1月17日日曜日

nRF52832 + WS2812B その4 : というわけで、ライブラリ化しイルミネーションっぽいの作ってみました

安心してください!(電波)吹いてませんよ(^^♪

先日の作品は実現性の確認のための酷い出来でしたので、ライブラリ化して作品作ってみました。
演出は即興です。



これまでの経緯もご覧ください

注意!!電源について

万が一真似してひ弱な電源を壊す人がいると恐いので、最初にこれを書いておきます。

WS2812Bは最高輝度でドライブすると、1つ当たり約50mA消費します。
今回使用した240個のLEDテープですと、12A近く消費する計算となります(購入元のスイッチサイエンス社調べで12.72A)。

今回、私は試験段階で5V20Aの電源を用意して使用しました(秋月の5V 20A電源をケースに入れた自作品)。LED専門サイトの電源を使った方が簡単かもしれません。

今回作成したライブラリには、LED設定値と消費電流の最大値(mA)を設定して呼び出すと、その電流値を超えた場合に、全体を暗くして最大値を超えない値に設定しなおすサブルーチンを準備しています。(http://aid.her.jp/uratan/led/index.html#currentを参考にしました。uratanさん、すばらしい!)

void ws2812b_driver_current_cap(rgb_led_t * led_array, uint16_t num_leds, uint32_t limit);
led_array : LEDの輝度データ配列
num_leds : LEDの数
limit : 電流制限値(mA)

ただし、このルーチンの効果に頼りすぎないで下さい。
制御信号が乱れると、設定以上の輝度でLEDが光ることが結構あります。このような現象が発生した場合は、すぐにLED用の電源を切ってハードとソフトを確認して下さい(配線を伸ばそうとしたらいきなり高輝度でちらつき、消費電流がいきなり通常の5倍位になって慌てたことがあります)。

今回の作品は、十分動作確認を行ったのち、モバイルバッテリー(5V 2A供給タイプ)やPCのUSB端子から電源供給して動かしています(実測で0.5A以下)。

また、激しくドライブしたときに、LEDテープの後ろの方が追随しなかったり、異常点灯することがあります。
どうも、カスケードしている電源の電圧が揺らいでいるのではないかと思っています。
電源を供給するポイントを増やしてあげれば、安定動作するのではないかと思います。

ハードウェア概要

LEDテープを味付け海苔の空き容器に巻きつけています(一周約19個です)。
プログラムはこちらに公開しています。

  • ボードはnRF52 DK
  • P0..25 (SPI0のMOSIピン)をレベル変換IC(ブレッドボード上のTXB0104) ICを使ってLEDテープDinへ(ブレッドボードには、比較試験に利用したFET, 74LS00も乗っています)
  • 電源はLED用に別途準備必要です。
    • 試験段階は20Aのスイッチング電源、調整完了後モバイルバッテリー(5V2A)利用




ソフト概要


今回作ったプログラムはgithubに載せました。
https://github.com/takafuminaka/nrf52832-spi_ws2812b_flashing_random

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

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

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

処理の流れ

  • spi_buffer_t spi0_buffer; // ws2812b制御用のSPIバッファの定義
  • alloc_spi_buffer(&spi0_buffer, NUM_LEDS); // SPIバッファの初期化
  • rgb_led_t led_array_work[NUM_LEDS]; // 輝度設定用LED配列の準備
  • 以下をループで実行
    • led_array[*].green, led_array[*].red, led_array[*].blueに各要素の輝度(0~255)を設定
    •  ws2812b_driver_current_cap(led_array_work, NUM_LEDS, CURRENT_LIMIT); // 電流制限のための輝度調整
    • ws2812b_driver_xfer(led_array_work, spi0_buffer, spi0); // LED更新
    • nrf_delay_ms(DELAY_MS); // 次の更新までの待ち


調整パラメータ
・DELAY_MS                 (5) LEDの更新待ち時間(m秒)(小さいほど激しく更新)
・NUM_LEDS (240) LEDの数
・MAX_INTENSE (16) 暗いLEDの最大輝度
・MAX_INTENSE2 (255) 明るい流れる効果のLEDの最大輝度
・MIN_INTENSE (1) 各LEDの最低輝度
・DECAY_STEP (30) 明るい流れるLEDの更新毎の減衰ステップ
・PRAB_FLASH (5000) 明るい流れるLEDの発生頻度(確率の逆数に比例。小さくすると出現頻度が上がる)
・ROW_SIZE (19) 一行当たりのLEDの数
・STEP_SRIDE1 (-(ROW_SIZE+1)) 明るい流れるLEDの更新毎の移動ステップ1
・STEP_SRIDE2 (-(ROW_SIZE-1)) 明るい流れるLEDの更新毎の移動ステップ2
・CURRENT_LIMIT (1000) 最大電流制限値 

* 当初の公開版から、拡張しました。デモンストレーションを複数用意し、時間差で切り替えるようにしました。また、SPIを8Mhzで動かすように変更しました。

今後の予定

制御ルーチンについて以下を改良していきたいと考えています。
  • ルーチン名、構造体の名称の調整(ws2812b_drv_*に統一するなど)
  • 割り込み処理を工夫し、伝送終了まで待たずにメインルーチンに戻るようにする
  • 3系統のSPIをすべてWS2812B点灯に使えるようにする
  • (日本国内で電波を吹けるnRF52モジュールがにゅうしゅできたら)BLE通信と連携しながらドライブできるようにする



2016年1月11日月曜日

nRF52832 + WS2812B その3: nRF52832での設計

nRF52832での基本設計

それでは、これと同様のことをnRF52832で実現するにはどうしたら良いでしょうか?Objective Production SpecificationSPIMのセクションを読んでみます。
 
 まず、先例でやっているような10bitモードのSPIは使えないようです。8bit固定です。
また、 nRF52832ではSPIのクロックも先例のような3MHzという設定はなく、その前後で使えるのは1MHz, 2MHz, 4MHz, 8MHzです。

 先ほどのタイミングチャートから、
0codeの場合、Hiを250~550ns, Lowを700~1000ns,
1codeの場合、Hiを700~1000ns, Lowを300ns~600ns
を作る必要があります。さらに、基本周期は1250ns (800kHz)となっています。
実は、これに従った信号を作ろうとすると、結構プログラムが複雑になってしまいます。

 例えば、4MHz(250ns単位制御)でやろうとした場合、
0codeにHiが1~2ビット、Lowが3~4ビット、
1codeをHiが3~4ビット、Lowが2ビット、
となり、1ビットの送信に最低5ビットが必要になります。

 8MHz(125ns単位制御)でやろうとした場合、
0codeにHiが2~4ビット、Lowが6~8ビット、
1codeをHiが6~8ビット、Lowが3~4ビット、
となり、1ビットの送信に最低9ビットが必要になります。

1バイトは8ビットなので、これではデータの生成が非常に面倒になります。

 ただ、http://aid.her.jp/uratan/led/の記載では
  • 432nsec までのパルスは '0' として判別される。 (132nsec のパルスでも '0')
  • 536nsec 以上のパルスは '1' として判別される。
  • DOUT 出力は判別された論理に従って新規のパルスが出力される。
  • その DOUT 出力においては、T0H=330nsecT1H=660nsec である。
  • インターバル 6.7μsec 程度を境に Treset として判別され、 中継処理が中断される。    (しかしながら これは誤点灯になります)
とのこと。

 これに従うのであれば、
4MHz(250ns単位制御)でやろうとした場合、
0codeにHiに1ビット、Lowに3ビット、
1codeをHiに3ビット、Lowに1ビット、
とし、1ビットの送信にきりの良い4ビットを割り当てることが出来ます。

 もう1つ、22個以上制御するときに限りますが、easyDMAによるSPIMで1度に送信できるのは256バイト(2048ビット)までということに注意が必要です。
WS2812Bの1ビットに4ビットを使うと、1つのLED向けのデータ24ビットはSPIの96ビットとなり、一度に送れるのは21LED分のデータが最大となります。
 これ以上のLEDを制御したい場合は、素早く次の伝送を行う必要があります。が、上記の通り6.7μsec以内に伝送が開始されなければなりません。
実は、このタイミングが難しく、SPI伝送とSPI伝送の間に、現在丁度6.2μsecかかっています。最終データの再開ビットがゼロの場合、Lowが3ビット分、つまり750nsec(0.75μsec)がかかってしまい、結果6.95μsecのLowが発生し、中継処理が中断してしまいます。
 これを解決するために、最後のデータをLSB側に寄せる必要があります。ただし、SPI伝送直後のMOSIがダラーんと残るので、最後のビットは0にする必要があります。
 まとめると、0codeであれば通常0B1000を送るところを0B0010に、1codeであれば0B1110をそのまま送ります。

 Labtoolで取得した波形は次のようになります。(今回からプローブ使用。10xで測定。)
7μsec超えているので、苦しいですね。固体によっては、動かないかもしれませんね。だめだな時は、踏み込んで対応することにしましょう。













ドライブ回路

 nRF52 DKのVccは3.3V、WS2812BのVccは5Vですので、MOSI信号のレベル変換が必要です。
今回、次の3つを試してみました。
 ちなみに、元の波形は次の通り。


MOS FET

 2N7000を使って、こちらの回路を試してみました。
動きましたが、波形はこんな感じです。動くには動きますが、遅延が大きくかなり悲惨ですね.....


TTL (74LS00)

丁度手元にあった74LS00をバッファとして使ってみました。2V以上をHiとみなすので、これでも3.3V系のシグナルを5V系に変換することが出来ます。
 バッファは、NANDゲートをNOTとして2つ組み合わせています。新たに準備するのであれば、こちらを参考に最新のロジックICを用意するのが良いでしょう。
 
 波形は綺麗です。



レベル変換IC(TXB0104)

レベル変換IC TXB0104を使った波形です。製品はこちら。nRF52側の電圧が低くても安定して動作するので、準備できるのであれば、ここまで試した中では一番よさそうです。ただ、双方向である必要性は無いので、もっと良いソリューションがあると思います。


サンプルプログラム

 とりあえず、ここまでの試行錯誤をまとめ、240個のLEDテープを点滅させることに成功したプログラムをGITHUBに公開しました。といっても、easyDMAで点灯させることが出来ることを確認するだけのプログラムであり、データの自由度など何もない、汚くて恥ずかしいプログラムです。
 もう少し使いやすく改良する予定です。

(安心してください(^^♪電波吹いてません)



2016年1月10日日曜日

nRF52832 + WS2812B その2 : nRF52832のeasyDMA

easyDMAについて

 当初、SoCの中に汎用のDMAユニットが存在していることをイメージしていました。実はそれは間違いの様でBlockdiagramにあるように、いくつかのハードウェアユニットがeasyDMAを内蔵しているということのようです。
 つまり、easyDMAを使ってWS2812Bを使うという場合、easyDMAを持ったユニットのどれかを流用しなければならないということです。

もう1つ悩ましいのは、タイミングチャートを見ると、SPI伝送後のMOSIの値が不定となっていることです。


これは、実験で確認したところ、次のことが分かりました。
  • SPI伝送終了直後、500ns程度最終ビットの状態が維持される。
  • その後、次のSPI伝送開始までのMOSIの状態はCPOLの値(SDKではSPIのモードとしてNRF_DRV_SPI_MODE_#を設定)に依存する。
    • CPOLが0の場合は、SPI伝送間のMOSIはHとなる。
    • CPOLが1の場合は、SPI伝送間のMOSIはLとなる。←WS2812B制御時はこれが必要
これらを考慮して、SPI伝送のモードとSPI伝送最終データのビット列を調整せねばなりません。つまり、モードとしてはNRF_DRV_SPI_MODE_1あるいはNRF_DRV_SPI_MODE_3を使用し、伝送最終ビットが0となるようにする必要があります。

 この辺の仕様外の挙動はSoCのリビジョンによって変わる可能性があるので注意が必要かもしれません。


SPI使用WS2812Bドライブの例

 WS2812B, DMA等で検索をかけると、SPIインタフェース用にDMAを持ったSoCでドライブする例がいくつか見つかりました。

要は、SPIの複数ビットのHi(1)とLow(0)を複数組み合わせて 0code, 1codeを実現しようというものです。
 
SPIMのeasyDMAを使えば、nRF52832でもWS2812Bの制御が出来そうです。

nRF52832 + WS2812B その1 : nRF52に関する情報

NORDIC Semiconductor Global Tech Tour 2015

もう1ヶ月ほど前の話になりますが、NORDIC Semiconductor社のGlobal Tech Tour 2015に参加してきました。

お目当ては、同社が2015年6月に発表したnRF52832に関する情報収集と参加者に配布された開発者向けボード(おそらく、これ→nRF52 DK)。

当日の内容は以下参照

nRF52 DK

nRF52 DKはいわゆる技適の取得は行われておらず、国内での販売も行われていません(Digi-Keyなどからの輸入は可能のようです)。
 ちなみにGlobal Tech Tourで配布されたDK搭載チップはEngineering B (QFAA-BA0)でした。ここで確認できるとおり、まだまだエラーが残っています。Errataのv1.1, v1.2が該当します。そういったことが気になる方は、量産版が出回るまで待った方が良いかもしれません。(ICのmarkingの情報はこちら参照。)



上記Global Tech Tourの直後に発表されたSDK Ver.11以降、nRF51とnRF52向けのSDKが統合され、nRF5 SDKとなりました。


easyDMA と WS2812B

先行のnRF51シリーズからの機能強化点の1つに、easyDMAを使ったI/Oや定型処理能力の強化(CPU負荷の低減)があります。

 その話を聞いているときに、ふと思い出したのが、家で眠っているWS2812Bを使ったLEDテープでした。フルカラーのLEDとして広く普及しており、バリエーションもいろいろあります
ドライブのタイミングが難しいため、CPUの負荷が高くなると聞いています。

 これのドライブにもしeasyDMAが使えれば、軽はずみなことを考えてしまいました。そこでいろいろ調べてみることにしました。