アフィリエイト広告
アフィリエイト広告

Arduino Nano Every を使ってみる – Larson Scanner

先日、LED を 8 個並べて、小さなイルミネーションを作ってみました。

Arduino Nano Every を使ってみる - L チカ (2)
前回は、Arduino Nano Every の内蔵 LED を点滅させてみました。 今回は、Arduino Nano Every 自身の動作ではないのですが、ちょっと初心に返って、外につけた LED を点滅させるみることにします。 LED...

その後、ネットで見つけたのが Larson Scanner (ラーソンスキャナ) っていうもの。なんかねぇ、キットも売られているし、Arduino で作ったという記事もある。なに?人気者?
簡単に言うと、Knight Rider というアメリカのテレビドラマに出てくるドリーム・カー Knight 2000 に装備されていたイルミネーションのこと。詳しくは、ググって下さい m(__)m

Larson Scanner

並べた LED を順番に点灯させるだけなんだけど、行ったり来たりバウンスするってところが、L チカ応用編って感じで面白そう。それと、微妙なんだけど、残像エフェクトというかフェーディングというか、単純にオンオフしているだけじゃない感じもいい。

で、できあがったのが左の動画。

なんだか大掛かりな感じになっちゃってるけど、ほとんどは LED を駆動しているトランジスタなんで、回路自体は単純。Arduino Nano Every でシフトレジスタ 74HC595 を制御し、LED 16 個を点滅させているだけです。

回路図

基本的な構成は、制御用の Arduino Nano Every からシフトレジスタ 74HC595 2 台へデータを送り、その出力を 2SC1815 の駆動回路を使って LED を点滅させている、という流れ。

いつも面倒臭がって電源回路とか書いてないんだけど、そこんとこ、これからはきちんと書いておこうと思ってます。三日坊主の虞もあるが (;´Д`)
その電源回路が左下のブロックです。電源は 9〜12V の AC アダプタ。三端子レギュレータ 7805 で 5V にしています。Arduino Nano Every へは、AC アダプタの出力を Vin に与えます。

左上の 2 つのボリュームでそれぞれ、点灯速度と輝度の調整を行ないます。

2SC1815 を使った LED の駆動回路に、となりあったトランジスタのコレクタどうしを繋ぐ 3.3KΩ の抵抗があります。おかしなつなぎ方をしてます。
これ、ひとつのトランジスタがオンすると、それにつながった LED を点灯させると同時に、両側の LED もボンヤリと点灯させるための小細工です。これでフェーディング効果を作っています。

シフトレジスタの上、2 個の MOSFET を使った回路は、電源投入時の不要な点灯を抑止するための回路です。
シフトレジスタは電源投入時に出力が不定 (HIGH か LOW か定まらない状態) になるので、LED が不規則に点灯してしまうことがあります。そこで、Arduino Nano Every の起動後、シフトレジスタを初期化してから LED 用の電源を立ち上げるようにします。シフトレジスタのクリア入力 SRCLR も、このラインにつないでいます。
機能に、直接には関係ないので、まぁお好みでどうにでも。

スケッチ

下手くそなんですが、使わないと上手にならないよなぁってんで、クラス、使ってます。
以下、毎度の自分自身のための備忘録的解説です。

  1. // Larson Scanner v.0.2 2022.07.08 meyon
  2. //#define DEBUG_ON
  3. class Srinit {
  4.     byte powOn;
  5.     byte rclkPin;
  6.     byte oePin;
  7.   public:
  8.     Srinit() {
  9.       powOn = 2;
  10.       rclkPin = 9;
  11.       oePin = 10;
  12.       pinMode(powOn, OUTPUT);
  13.       pinMode(rclkPin, OUTPUT);
  14.       pinMode(oePin, OUTPUT);
  15.     }
  16.   void srclr() {
  17.     digitalWrite(rclkPin, HIGH);
  18.     delayMicroseconds(5);
  19.     digitalWrite(rclkPin, LOW);
  20.     digitalWrite(powOn, HIGH);
  21.   }
  22. };

シフトレジスタを初期化するクラスです。

powOn は、LED 用の電源を制御すると同時に、シフトレジスタの SRCLR にもつながっています。LED 用電源がオフのときは、SRCLR も LOW です。

シフトレジスタ 74HC595 の出力を初期化するには、OESRCLR とを LOW の状態にして、RCLK を立ち上げる必要があります。
そのために、まず powOn 、rclkPin 、oePin を OUTPUT にセットすることでローインピーダンスにし、次に rclkPin を 5μs だけ HIGH にします。データシートによると 500ns でいいようなのですが、delayMicroseconds() の最小値が 3μs なので、余裕をとって 5μs にしました。
次に、powOn を HIGH にすることで、LED 用電源をオン、同時に SRCLR も HIGH となります。

これで、電源投入時の不規則な点灯は起こらず、きれいにスキャンが始まります。

なお、3 行目のコメントを外すと、LED 制御用のビット列をシリアルモニタに出力して確認することができます。

  1. class Lscanner {
  2.     byte inpPin;
  3.     byte brtPin;
  4.     byte serPin;
  5.     byte srclkPin;
  6.     byte rclkPin;
  7.     byte oePin;
  8.     int brt;
  9.     int ledPos;
  10.     unsigned long ledBit;
  11.     unsigned int bitPtn;
  12.     int incr;
  13.     unsigned int intvl;
  14.     unsigned long prevMillis;

LED を制御する Lscanner クラスの、宣言とメンバ変数です。

  1.   public:
  2.     Lscanner() {
  3.       inpPin = A0;
  4.       brtPin = A1;
  5.       serPin = 7;
  6.       srclkPin = 8;
  7.       rclkPin = 9;
  8.       oePin = 10;
  9.       brt = 0;
  10.       ledPos = 0;
  11.       ledBit = 0;
  12.       bitPtn = 0;
  13.       incr = 1;
  14.       intvl = 0;
  15.       prevMillis = 0;
  16.       pinMode(serPin, OUTPUT);
  17.       pinMode(srclkPin, OUTPUT);
  18.       pinMode(rclkPin, OUTPUT);
  19.     }

コンストラクタ。メンバ変数の初期化と、出力ピンのモード設定を行ないます。

入力ピンは、スピード調整 inpPin と輝度調整 brtPin として使います。

シフトレジスタは、データ入力 SER 、シフトクロック SRCLK 、ラッチクロック RCLK 、出力制御 OE を使用します。OE は PWM 制御しますので、モード設定は行なっていません。SRCLR は LED 用電源に連動しています。

  1.   void update() {
  2.     brt = map(analogRead(brtPin), 0, 1023, 255, 0);
  3.     analogWrite(oePin,brt);
  4.     intvl = map(analogRead(inpPin),0, 1023, 300, 5);
  5.     if(intvl < millis() - prevMillis) {
  6.       ledBit = 0x800001f0 << ledPos;
  7.       bitPtn = ledBit >>8;
  8.       digitalWrite(rclkPin, LOW);
  9.       shiftOut(serPin, srclkPin, MSBFIRST, bitPtn >> 8);
  10.       shiftOut(serPin, srclkPin, MSBFIRST, bitPtn);
  11.       digitalWrite(rclkPin, HIGH);
  12.       ledPos += incr;
  13.       if(0 >= ledPos || 19 <= ledPos) incr *= -1;
  14.       prevMillis = millis();
  15. #ifdef DEBUG_ON
  16.   Serial.print(ledPos);
  17.   Serial.print(" ");
  18.   Serial.print(ledBit, BIN);
  19.   Serial.print(" ");
  20.   Serial.print(bitPtn, BIN);
  21.   Serial.print("\n");
  22. #endif
  23.     }
  24.   }
  25. };

LED の点滅を制御するメンバ関数。

67 、68 行は輝度調整です。輝度調整は、シフトレジスタの OE を PWM 制御することで行なっています。70 行はスキャンスピードの入力です。

72〜83 行で LED の制御を行ないます。

ledBit は点灯する LED を規定するためのビット列で、初期値を 0x800001f0 (73 行) としています。これをビット列で表すと、

1000 0000 0000 0000 0000 0001 1111 0000

このビット列を 8 ビット右シフト (74 行) して、

0000 0000 1000 0000 0000 0000 0000 0001

太字部分の 16 ビットをシフトレジスタに送り LED を点灯させる (76〜79 行) と、一番右の 1 個だけが点灯します。
なお、先頭 32 ビット目の「1」については後述します。

次に、点灯位置 ledPos を 1 増加させる (81 行) と ledBit は左へ 1 ビットシフト、右へ 8 ビットシフトして、

0000 0000 0000 0000 0000 0000 0000 0011

になり、LED は右 2 個が点灯。ledPos が 2 になると LED は右 3 個が点灯します。
同様にして ledPos を 19 まで増加させると

0000 0000 0000 1111 1000 0000 0000 0000

となって、一番左の LED が 1 個点灯します。ここまで行ったら、今度は ledPos を漸減して、1 の並びを右へ移動させていきます。

しかし、じつはこの一連のビット操作は、うまくいかない可能性を含んでいます。ビットシフトは基本的に、int 型のデータに対して機能するものだそうです。今回のように long 型のデータをシフトさせると、想定したような動作をしないことがある、と。
じっさい、今回の場合でも、ledBit の初期値を、32 ビット目を立てずに 0x000001f0 としたとき、7 ビット左シフトして 16 ビット目が「1」になると、17ビット以上がすべて「1」になるという挙動をします。

0000 0000 0000 0000 0000 0001 1111 0000 << 7
                   ↓
1111 1111 1111 1111 1111 1000 0000 0000

そこで、32ビット目を「1」にする、つまり初期値を 0x800001f0 にすることで、17 ビット以上を明示的に「0」にしています。

そんなわけで、このビット操作は、結果オーライ。うまくいったのでそのまま採用してます。ちなみに、Arduino NANO でも、このスケッチは同様に動作します。

  1. Srinit srinit1;
  2. Lscanner lscanner1;
  3. void setup() {
  4.   srinit1.srclr();
  5. #ifdef DEBUG_ON
  6.   Serial.begin(9600);
  7. #endif
  8. }
  9. void loop() {
  10.   lscanner1.update();
  11. }

setup() で、シフトレジスタの初期化を行なっています。

#ifdef 〜 #endif に囲まれた部分は、3 行目の「#define DEBUG_ON」を有効にしたときにだけコンパイルされます。
蛇足ですが、シリアルモニタに出力される変数の値のうち、ledPos はインクリメントされたあとの数値ですので、1 引いた値として考える必要があります。ご注意下さい m(__)m

ブレッドボード

Larson Scanner ブレッドボード

ブレッドボードです。

上に LED 列、その左に速度と輝度調整用のボリュームがあり、その間に LED 電源用の MOSFET が見えます。左下は電源部。中央のトランジスタ群は LED の駆動用。2 個ある IC はシフトレジスタ 74HC595 、右下が Arduino Nano Every です。

これだけ組むと、壊すの、ちょっともったいない (^_^;)
でも、まぁ 1 週間もしたら跡形もなく消えます。必要になってまた組むことがあったなら、もっと改善した回路に、きっとなっていることでしょう。

製作後記

LED の点灯制御のこと

やっていくうちに、もっと違うパターンで動かすのもいいな、とか、スピードに変化をつけるといいな、とか、いろいろな考えがでてきます。そうしたことをやるなら、不確実なビット操作ではなく、点灯パターンをテーブルに規定するのが良いと思います。
一般的にテーブルを参照する方法は、複雑な計算処理をするより、確実で高速な処理ができると言われています。

C++クラスのこと

「あ、クラスでプログラミングするって、こーゆーことなんだ。」

電源投入時に不規則な点灯をするので、シフトレジスタを初期化することにしました。そのスケッチをクラスで追加しようとしたときに、気付いたことがあります。

オブジェクト指向プログラミング (OOP) って何? 教科書を読んでもチンプンカンプンわかりません。
でも、俺、わかったんですよ。初期化のスケッチを追加するとき、クラスにすると、その他の部分には関係なく、初期化する処理のことだけを考えればいいんだ、って。

わかっている人にはあったり前のこと。でも、初心者は、毎回毎回が勉強です (^_^;)

タイトルとURLをコピーしました