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

シフトレジスタ 74HC595 を使う (2) スケッチ

前回は、シフトレジスタ 74HC595 を使って LED を点滅させる「LED Scanner」の回路を作りました。

シフトレジスタ 74HC595 を使う (1) 回路図
これまでにも何回か使っているシフトレジスタ 74HC595 。先日の Larson Scanner でも使っているのですが、ちょっと気になることがあったので、再度、回路を組んでみました。 Larson Scanner の記事はこちら。 気に...

今回は、この回路を動かすためのスケッチを書きましょう。

スケッチ

毎度の、俺自身のための備忘録的解説です。

  1. // LED Scanner v.1 2022.07.22 meyon
  2. const byte scanPatterns[] = {
  3.   // Pattrens - Larson Scanner
  4.   0x00, 0x01, 0x03, 0x07, 0x0f, 0x1e,
  5.   0x3c, 0x78, 0xf0, 0xe0, 0xc0, 0x80,
  6.   0x00, 0x80, 0xc0, 0xe0, 0xf0, 0x78,
  7.   0x3c, 0x1e, 0x0f, 0x07, 0x03, 0x01
  8. };

LED の点滅パターンを規定している配列です。

16 進数で書いてあるのでわかりにくいですが、ビット列と考えて下さい。たとえば、0x3c であれば「00111100」、0 が消灯、1 が点灯です。これは、4 個の LED がバウンスする Laeson Scanner のパターンです。ちなみに、フェーディング効果は、ないです。
ここを、0〜255 の整数列とすると、2 進数をカウントするようになります。お好みでどうぞ。

  1. class LedScanner {
  2.   private:
  3.     const byte SER_Pin = 7;
  4.     const byte SRCLK_Pin = 8;
  5.     const byte RCLK_Pin = 9;
  6.     const byte OE_Pin = 10;
  7.     const byte SRCLR_Pin = 11;
  8.     const byte setSpeed_Pin = A0;
  9.     const byte setBrightness_Pin = A1;

クラス宣言とメンバ変数の定義です。

使用するピン番号を、定数として初期化しています。OE_Pin は PWM の出力できるピンにします。アナログ入力ピンは、速度、輝度調整用ボリュームの入力です。

  1.   public:
  2.     LedScanner() {
  3.       pinMode(SRCLR_Pin, OUTPUT);
  4.       pinMode(OE_Pin, OUTPUT);
  5.       pinMode(SER_Pin, OUTPUT);
  6.       pinMode(SRCLK_Pin, OUTPUT);
  7.       pinMode(RCLK_Pin, OUTPUT);
  8.       // Initialze the shift registert
  9.       digitalWrite(RCLK_Pin, HIGH);
  10.       delayMicroseconds(5);
  11.       digitalWrite(RCLK_Pin, LOW);
  12.       digitalWrite(SRCLR_Pin, HIGH);
  13.     }

コンストラクタです。

23〜27 行は、ピンモードの設定です。SRCLR ピンは、シフトレジスタの初期化のためにローインピーダンスに設定します。OE ピンはアナログ出力なのでモードを指定する必要はないのですが、これも初期化のためにローインピーダンスに設定しています。PWM 出力には影響はないです。
ちなみに、明示的に設定していませんが、ローインピーダンス (OUTPUT) に設定すると、I/O 出力は Low になります。

30〜32 行で、クロック RCLK を送ってシフトレジスタの出力を初期化します。その後、33 行で SRCLR を High にし、シフトレジスタを稼働させます。これに連動して、LED 駆動電源が立ち上がります。

  1.     void setLedBrightness() {
  2.       int brightnessInput = analogRead(setBrightness_Pin);
  3.       int brightness = map(brightnessInput, 0, 1023, 255, 0);
  4.       analogWrite(OE_Pin, brightness);
  5.     }

LED の輝度設定のためのメンバ関数です。

ボリュームからの電圧を読み取り、OE_Pin へ 255 (暗) 〜 0 (明) の PWM 信号を出力します。

  1.     int setScanSpeed() {
  2.       int speedInput = analogRead(setSpeed_Pin);
  3.       int scanSpeed = map(speedInput, 0, 1023, 500, 10);
  4.       return scanSpeed;
  5.     }

点滅速度の設定のためのメンバ関数です。

ボリュームからの電圧を読み取り、LED の状態更新の間隔値 500 (遅) 〜 10 (速) を返します。

  1.     void updateScan(int scanSpeed) {
  2.       static int numberOfScanPatterns = sizeof(scanPatterns) / sizeof(byte);
  3.       static unsigned long previousMillis = 0;
  4.       static int counterOfScanPattern = 0;
  5.       if(scanSpeed < millis() - previousMillis) {
  6.         digitalWrite(RCLK_Pin, LOW);
  7.         shiftOut(SER_Pin, SRCLK_Pin, MSBFIRST, scanPatterns[counterOfScanPattern]);
  8.         digitalWrite(RCLK_Pin, HIGH);
  9.         counterOfScanPattern++;
  10.         if(numberOfScanPatterns <= counterOfScanPattern) counterOfScanPattern = 0;
  11.         previousMillis = millis();
  12.       }
  13.     }
  14. };

LED の状態を更新するメンバ関数です。

49 行で、点滅パターンの配列の数を計算しています。50 行は前回更新時刻、51 行は配列のループカウンタです。いずれも static をつけていますので、関数が呼び出された最初の一回だけ実行されます。

シフトレジスタへのデータ出力は shiftOut() を使っています。データ出力後に RCLK を立ち上げます。

経過時間が、引数 scanSpeed で渡された更新間隔を超えたら (53 行)、データをシフトレジスタへ送り (54〜56 行)、カウンタを加算 (58〜59 行)、更新時刻を更新 (60 行) します。

  1. void setup() {
  2. }

ピンモードの設定などはコンストラクタが行ないますので、setup() では何もすることがありません。

  1. void loop() {
  2.   static LedScanner scanner;
  3.   scanner.setLedBrightness();
  4.   int scanSpeed = scanner.setScanSpeed();
  5.   scanner.updateScan(scanSpeed);
  6. }

69 行、loop() のなかでオブジェクトの生成を行なっています。
普通は loop() の外でやります? C++ なら main() のなかですよね。要するにスコープの問題なので、loop() 内のローカル変数って感じです。そのために、static をつけています。

LED の輝度調整して (71 行)、スキャンスピードの値を受け取り (72 行)、LED の状態を更新 (73 行) しています。

ブレッドボード

LED Scanner with shift register 74HC595

完成したブレッドボードのようすです。

LED 部分をですねぇ、バーにするとか、テープにするとか、チューブにするとか、ネットにするとか、いろいろあります。
シフトレジスタを増やして、LED の系統を増やすとともに、一組の LED の数も増やして、色ミックスして、ってやると、けっこう賑やかになることでしょう、たぶん。

製作後記

今回は、シフトレジスタの OESRCLR について確認しました。

この 2 つの入力は、直接の動作に関係がないので、考慮しないことが多いです。でも、電源投入時に、チカチカと無用な点滅するのを放置するってのは、やっぱり、ない。LED チカチカは無視すれば終わるけど、別の回路に影響を与えるようなことがあると問題です。
いろんなところに配慮した回路が作れるようになれたら、エクセレント、ですよね。

「コメント書かなくても、処理内容がわかる関数名にする。」

最近どこかのブログだったかで読んで、心に残った言葉。変数も、用途がわかる名前にすること。
リモートワークでソースコードの確認をしていたプログラマの息子が「変数 f ってなに?」とつぶやきました。file の f だそうです。「なら file と書けよ。」
for() 文のループカウンタ「i」ってなんでしょ? だれもが普通に使っているから i って書いちゃうけど、意味不明? FORTRAN の整数型表記に由来するとか。数学にも i はよくでてくる。あちこちに i やら j やら k やらでてきて、わけわかんなくなりませんか? 用途をちゃんと書けって。

下手くそなりにも、クラスでスケッチが書けるようになってきました。あわせてそんなところにも配慮できたら、エクセレント、ですよね。

などと、思う、今日この頃です。

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