74HC595を使ってArduinoの出力ピンを拡張する方法(5)

このページをスマートフォンなどでご覧になる場合は、画面を横長にする方が読みやすくなる事があります。
目次へ  前のページへ (1) (2) (3) (4) (5) (6) (7) (8) 次のページへ
2019年09月29日 公開。

4-1-4.digitalWrite関数を使って1つの74HC595を制御するスケッチを作る方法(MSBファースト)

前のページでは、Arduinoにつながった74HC595LSBファーストでデータを送信する方法を説明しましたが、今度はMSBファーストでデータを送信する方法について説明します。

広告

3ページの説明のおさらいになりますが、Arduinoと74HC595は図12に示す配線図でつなぎます。

図12(再掲)、Arduinoに1つの74HC595を接続する場合の配線図
↑ 画像をクリックすると拡大
図12(再掲)、Arduinoに1つの74HC595を接続する場合の配線図

この回路において、MSBファーストで8ビットのデータを送信するには、図18のタイミングチャートに従って信号を送ります。

図18(再掲)、1つの74HC595を制御する場合のタイミングチャート(MSBファースト)
↑ 画像をクリックすると拡大
図18(再掲)、1つの74HC595を制御する場合のタイミングチャート(MSBファースト)

MSBファーストでデータを送信する場合にも、初期化用のinitHC595関数と、8ビットをMSBファーストで送信する関数outputOneByteMsbFirst関数(関数名がoutputOneByteLsbFirst関数から変わっている事に注意)の2つを作る事にします。

まずinitHc595関数は、リスト12の様になります。

リスト12initHc595関数(MSBファースト)COPY
// 74HC595の制御用ピンおよび74HC595を初期化する
void initHc595()
{
  // 74HC595制御用ピンを出力モードにする
  pinMode(SER,OUTPUT);
  pinMode(SRCLK,OUTPUT);
  pinMode(RCLK,OUTPUT);

  // SRCLKとRCLKをLOWにする(これらのピンはデータを送信していない時はLOWであるべき)
  digitalWrite(SRCLK,LOW);
  digitalWrite(RCLK,LOW);

  // 0を送信して74HC595のパラレル出力ピンを全て0にする(74HC595の初期状態を気にしない場合は省略可能)
  outputOneByteMsbFirst(0);
} // initHc595

LSBファーストの場合のinitHC595関数(前のページリスト6)と比較すると、リスト6でoutputOneByteLsbFirst(0);と書いた部分が、送信方法がMSBファーストに変わった事により、リスト12ではoutputOneByteMsbFirst(0);に書き換わっているのが分かります。

一方で、outputOneByteMsbFirst関数は、リスト13の様になります。

リスト13outputOneByteMsbFirst関数COPY
// MSBからLSBの順に74HC595に1バイトを送信する
void outputOneByteMsbFirst(uint8_t val)
{
  const int DATA_BITS=8; // 送信するデータのビット数

  // シフトレジスタにデータを送信
  for(int i=0; i<DATA_BITS; i++) {
    digitalWrite(SER,(val & 0x80) ? HIGH : LOW); // SERにvalの最上位ビットを出力
    digitalWrite(SRCLK,HIGH); // SRCLKを立ち上げる(この時74HC595がシフト動作)
    digitalWrite(SRCLK,LOW ); // SRCLKを立ち下げる(次にSRCLKを立ち上げるため)
    val<<=1; // valを1ビットだけ左シフト
  } // for i
  
  // ストレージレジスタにデータを転送
  digitalWrite(RCLK,HIGH); // RCLKを立ち上げる(この時に74HC595のシフトレジスタのパラレル出力がストレージレジスタに転送され、74HC595のしパラレル出力端子にデータが出力される)
  digitalWrite(RCLK,LOW ); // RCLKを立ち下げる(次にRCLKを立ち上げるため)
} // outputOnebyteMsbFirst

リスト13から、74HC595の内蔵シフトレジスタにデータを送信する部分を抜き出したのが、リスト14です。

リスト14、SER信号にデータを送信しながらSRCLK信号にクロックを送信するループ(MSBファースト)COPY
  // シフトレジスタにデータを送信
  for(int i=0; i<DATA_BITS; i++) {
    digitalWrite(SER,(val & 0x80) ? HIGH : LOW); // SERにvalの最上位ビットを出力
    digitalWrite(SRCLK,HIGH); // SRCLKを立ち上げる(この時74HC595がシフト動作)
    digitalWrite(SRCLK,LOW ); // SRCLKを立ち下げる(次にSRCLKを立ち上げるため)
    val<<=1; // valを1ビットだけ左シフト
  } // for i

for文で8回ループを回しています。

ループの内部を見ると、まず

digitalWrite(SER,(val & 0x80) ? HIGH : LOW);

val最上位ビットをSER信号に出力します。

ちなみに、0x80は2進数で表記すると10000000(最上位ビットだけ1)ですから、val & 0x80という式は、valの最上位ビットが1なら0x80を、最上位ビットが0なら0を返します。

よって、(val & 0x80) ? HIGH : LOWという式は、valの最上位ビットが1ならHIGHを、最上位ビットが0ならLOWを返します。

参考:定数LOWが0に定義されており、定数HIGHが1に定義されている事を考えると、(val & 0x80) ? HIGH : LOWという式は、(val & 0x80)!=0とも書けます。これを!!(val & 0x80)と書くプログラマーもいますが、直感的でないので筆者は好きではありません。いずれの書き方でも、コンパイラの最適化により、おそらく同じ機械語に変換されると思います。

valの最上位ビットをSER信号に出力した次は、

digitalWrite(SRCLK,HIGH);
digitalWrite(SRCLK,LOW );

によりSRCLK信号に1クロック送信して、74HC595内蔵のシフトレジスタを、1ビットだけシフトします。

その次に

val<<=1;

によってvalを1ビット左シフトして、次のループに備えます。

画像をクリックすると、Amazonの購入ページが別窓で開きます。

LSBファーストの場合のスケッチ(前のページリスト11)をMSBファースト用に書き換えるには、initHc595関数をリスト12の物に置き換え、さらにoutputOneByteLsbFirst関数をリスト13outputOneByteMsbFirst関数に置き換え、さらにloop関数内でoutputOneByteLsbFirst関数を呼び出している部分をoutputOneByteMsbFirst関数の呼び出しに書き換えればいいので、結局リスト15に示すスケッチになります。

リスト15、1つの74HC595を制御するテストスケッチ(MSBファースト)COPY
// oneByteMsbFirst.ino

const int SER  =2; // SER制御用にするピンの番号を宣言
const int SRCLK=3; // SRCLK制御用にするピンの番号を宣言
const int RCLK =4; // RCLK制御用にするピンの番号を宣言

void setup()
{
  initHc595();
} // setup

void loop() {
  static uint8_t cnt=0; // 74HC595へ出力する値を格納するカウンタ変数。uint8_tは8ビット符号なし整数。staticを付けるとloop関数を抜けても値を保持し続ける

  outputOneByteMsbFirst(cnt++); // cntの値を74HC595に送信してからcntをカウントアップ
  delay(100); // 0.1秒待つ
} // loop

// 74HC595の制御用ピンおよび74HC595を初期化する
void initHc595()
{
  // 74HC595制御用ピンを出力モードにする
  pinMode(SER,OUTPUT);
  pinMode(SRCLK,OUTPUT);
  pinMode(RCLK,OUTPUT);

  // SRCLKとRCLKをLOWにする(これらのピンはデータを送信していない時はLOWであるべき)
  digitalWrite(SRCLK,LOW);
  digitalWrite(RCLK,LOW);

  // 0を送信して74HC595のパラレル出力ピンを全て0にする(74HC595の初期状態を気にしない場合は省略可能)
  outputOneByteMsbFirst(0);
} // initHc595

// MSBからLSBの順に74HC595に1バイトを送信する
void outputOneByteMsbFirst(uint8_t val)
{
  const int DATA_BITS=8; // 送信するデータのビット数

  // シフトレジスタにデータを送信
  for(int i=0; i<DATA_BITS; i++) {
    digitalWrite(SER,(val & 0x80) ? HIGH : LOW); // SERにvalの最上位ビットを出力
    digitalWrite(SRCLK,HIGH); // SRCLKを立ち上げる(この時74HC595がシフト動作)
    digitalWrite(SRCLK,LOW ); // SRCLKを立ち下げる(次にSRCLKを立ち上げるため)
    val<<=1; // valを1ビットだけ左シフト
  } // for i
  
  // ストレージレジスタにデータを転送
  digitalWrite(RCLK,HIGH); // RCLKを立ち上げる(この時に74HC595のシフトレジスタのパラレル出力がストレージレジスタに転送され、74HC595のしパラレル出力端子にデータが出力される)
  digitalWrite(RCLK,LOW ); // RCLKを立ち下げる(次にRCLKを立ち上げるため)
} // outputOnebyteMsbFirst

前のページリスト11で紹介したLSBファースト用のスケッチの場合、QH信号の状態を示すLED8(3ページ図15の回路図や写真9で一番右のLED)の点滅が一番速かったのに対し、リスト15に示したMSBファースト用のスケッチの場合、QA信号の状態を示すLED1(図15の回路図や写真9で一番左側のLED)の点滅が一番速くなります。

写真9(再掲)、74HC595の動作確認用の基板のおおまかな部品配置
↑ 画像をクリックすると拡大
写真9(再掲)、74HC595の動作確認用の基板のおおまかな部品配置
広告

これは、LSBファーストの場合、QH信号が最下位ビット(LSB)になるのに対し、MSBファーストの場合、QA信号が最下位ビットになる事によります。

リスト15のMSBファーストのスケッチをArduino Unoで動作させた場合の波形を、ロジックアナライザで測定した結果を、図20に示します。

図20、Arduino Unoでリスト15のスケッチを動作させた場合の波形
図20、Arduino Unoでリスト15のスケッチを動作させた場合の波形

ロジックアナライザの画面をハードコピーしたものです。ただし、水色のLHの文字や、上側中央やや右寄りの水色の長方形は、筆者が書き加えた物です。

図20のSER信号を見ると、8クロックのSRCLK信号に同期して、LLHLLLLLの順にデータを送信している事が分かります。MSBファーストで送信していますから、これらのデータを2進数で表わすと、00100000となります。

次にQA~QHの信号の波形を見ると、これらの信号はQA信号がLSBでQH信号がMSBですから、RCLK信号の立ち上がりの前後で00011111から00100000に変化している事が分かります。

この様に、SER信号で送ったデータが、RCLK信号の立ち上がりと同時にQA~QHの信号に出力されている様子が分かります。また、リスト15のスケッチは、送信するデータを1ずつカウントアップする物ですが、実際に00011111の次には、1だけ大きい00100000が出力されている事が確認できます。

SER信号にMSBを出力し始めてからRCLK信号を立ち下げる所までが、データ送信の一連の処理になりますが、図20より、この処理には108μsかかっている事が分かりました。(図20の上側中央やや右寄りの水色の長方形の中の、"A − T = 108us"と表示されている部分に注目してください) 前のページで説明したとおり、LSBファーストの場合はデータ送信に107μsかかっていましたが、MSBファーストの場合でも108μsと、ほぼ同じ時間がかかる事が分かります。

次のページでは、LSBファーストによるデータ送信でも、MSBファーストによるデータ送信でも、同じ関数で行えるように、outputOneByteLsbFirst関数とoutputOneByteMsbFirst関数を統合したoutputOneByte関数を作ります。

目次へ  前のページへ (1) (2) (3) (4) (5) (6) (7) (8) 次のページへ

このページで使われている用語の解説

関連ページ

PCBgogoのバナー
Arduino 電子工作
このサイトの記事が本になりました
ISBN:978-4-7775-1941-5
工学社の書籍の内容の紹介ページ
本のカバーの写真か書名をクリックすると、Amazonの書籍購入ページに移動します。
電子工作で学ぶ論理回路入門
このサイトの中の人が書いた本です。
ISBN:978-4-7775-2280-4
工学社の書籍の内容の紹介ページ
この本の紹介記事
本のカバーの写真か書名をクリックすると、Amazonの書籍購入ページに移動します。