DMA駆動でLチカ ~Adafruit Itsy Bitsy M0 Express~
先日購入したAdafruit Itsy Bitsy M0 ExpressがDMAをサポートしているということで、DMA駆動でNeoPixelを光らせてみました。
目次
DMAとは
Direct Memory Access の略で、CPUの介在なしに周辺機器とメモリ間で直接データを移動するソリューションのこと。
これによってNeoPixelとサーボやmillis関数との共存が可能になるんだって。
NeoPixelとサーボを同時に使うとサーボがジジッと変な動きになるんですよね。。
DMAの詳しいことは以下を参照ください。
てことで、Adafruit Itsy Bitsy M0 ExpressでそのDMAとやらのご利益を確認してみました。
Adafruit Itsy Bitsy M0 Express
デジタル5ピンがDMAに対応しています。この5ピンがちょっと特殊で、他のピンは 3.3V系なのですが、5ピンのハイサイド電源はVhiピンとなっています。Vhiピンの電圧はUSBピン(USB供給電圧)かBATピン(外部電源電圧)のいずれか高い電圧となります。
NeoPixelが5V駆動のため デジタル5ピンが内蔵レベルシフタでUSB 5Vもしくは外部電源電圧まで出力できるようにしているようです。過去気にせずちょくちょく3.3VでNeoPixel駆動してきたが。。
Adafruit Itsy Bitsy M0 Expressについては以下でも記載しておりますのでご参照ください。
DMA 比較実験
LEDマトリクスを10msecおきに1つづつ点灯させて、millis関数で一定時間測って正確な数のLEDが光るかでDMAの効能を確認してみました。
構成
LEDデータ入力はDMA対応のデジタル5ピン。電源はVhiピンを接続。
部品
- マイコン Adafruit Itsy Bitsy M0 Express
- フレキシブル 8×8 RGB LEDマトリクス
従来方法
まずは比較のために従来のAdafruit NeoPixelライブラリを使用します。
https://github.com/adafruit/Adafruit_NeoPixel
Arduinoコード
LEDマトリクスを10msecおきに1つづつ点灯させて、millis関数で630msec数えたら点灯ループを抜けて370msec消灯するプログラムです。理論上は63個のLEDが点灯して消灯を繰り返すはずです。
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 |
#include <Adafruit_NeoPixel.h> #define PIN 5 #define NUM_PIXELS 64 Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800); uint32_t t; void setup() { strip.begin(); strip.setBrightness(20); strip.show(); // Initialize all pixels to 'off' } void loop() { t = millis(); for(int i=0; i< strip.numPixels(); i++) { if(millis() - t >= 630) break; strip.setPixelColor(i, strip.Color(255, 0, 0)); strip.show(); delay(10); } strip.clear(); strip.show(); delay(370); } |
動作結果
Itsy Bitsy M0 Express
Adafruit NeoPixelライブラリ使用 pic.twitter.com/vPgHCKImV6— HomeMadeGarbage (@H0meMadeGarbage) 2018年11月6日
63個まで点灯してほしいところ、58個までしか光っておりませんでした。やはり従来NeoPixelライブラリでは、データ送信中にすべての割り込みを無効にしてしまうため millis関数の精度が落ちてしまっているようです。
DMA駆動
DMA駆動のため以下のライブラリを使用します。
https://github.com/adafruit/Adafruit_ZeroDMA
https://github.com/adafruit/Adafruit_NeoPixel_ZeroDMA
Arduinoコード
従来のコードの2箇所変更するだけです。
- 1行目 以下に変更
1#include <Adafruit_NeoPixel_ZeroDMA.h> - 6行目 以下に変更
1Adafruit_NeoPixel_ZeroDMA strip(NUM_PIXELS, PIN, NEO_GRB);
動作結果
Itsy Bitsy M0 Express
Adafruit NeoPixel ZeroDMAライブラリ使用 pic.twitter.com/Aj3FjmPUjs— HomeMadeGarbage (@H0meMadeGarbage) 2018年11月6日
きっちり63個まで点灯しています!最後の64個目の直前まで光っています。やるねぇDMA。
LEDマトリクスのDMAお試し
NeoPixelのマトリクス用のライブラリもDMAに対応してるものがあったので試してみました。以下のライブラリです。
https://github.com/adafruit/Adafruit_NeoMatrix_ZeroDMA
構成は比較実験と同じです。
Arduinoコード
以下のライブラリも使用しています。
https://github.com/adafruit/Adafruit-GFX-Library
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 32 33 34 35 36 37 38 39 40 41 42 |
#include <Adafruit_GFX.h> #include <Adafruit_NeoMatrix_ZeroDMA.h> #define PIN 5 uint32_t t, passTime = 0; int cnt = 0; int i = 0; Adafruit_NeoMatrix_ZeroDMA matrix(8, 8, PIN, NEO_MATRIX_TOP + NEO_MATRIX_RIGHT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG, NEO_GRB); const uint16_t colors[] = { matrix.Color(0, 0, 0), matrix.Color(133, 77, 65), matrix.Color(255, 0, 0), matrix.Color(255, 135, 0), matrix.Color(255, 255, 0), matrix.Color(0, 255, 0), matrix.Color(0, 0, 255), matrix.Color(255, 0, 255), matrix.Color(80, 80, 80), matrix.Color(255, 255, 255) }; String num[] = {"0", "1", "2", "3","4", "5", "6","7", "8", "9"}; void setup() { matrix.begin(); matrix.setTextWrap(false); matrix.setBrightness(40); matrix.setTextColor(colors[0]); } void loop() { t = millis(); if(t - passTime >= 1000){ passTime = t; matrix.fillScreen(0); matrix.setCursor(2, 0); matrix.setTextColor(colors[i]); matrix.print(num[i]); matrix.show(); i++; if(i > 9) i = 0; } } |
1秒ごとにカウントアップして数字を表示します。
10〜13行目でマトリクスのサイズや始点や配置方向を指定してます。設定方法は以下参照ください。
動作
Itsy Bitsy M0 Express
Adafruit NeoMatrix ZeroDMAライブラリ使用 pic.twitter.com/iYXovisaOc— HomeMadeGarbage (@H0meMadeGarbage) 2018年11月6日
いいですねぇ。精度良く1秒毎にカウントアップできてます。てかDMAどうこうよりマトリクスに簡単に文字表示できるライブラリに感動しちゃった。
ちょっと今度もう少しNeopixelマトリクスのライブラリいじってみようと思います。