シフトレジスタと4桁7セグメントLED
UNO

概要

シフトレジスタ(74HC595)を利用して、Arduinoのピンを節約します。

目的

先日作成した、4桁7セグメントLEDの制御に、シフトレジスタを使い、Arduinoのピンが節約できることを確認します。また、シフトレジスタの動作の確認も行います。

Arduinoのピンの数は節約されますが、ArduinoとLEDの間にICをはさむため、全体としてはジャンプワイヤーの数は増えます。

必要なもの

このページで必要なものは以下の通りです。

  • Arduino Uno
  • 4桁7セグメントLED(OSL40562-LB、カソードコモン)
  • 抵抗x8(たとえば240Ω)
  • シフトレジスタ(74HC595)

注意

Arduino UnoのI/Oピンの最大電流は40mAです。また、ATmega328Pの5Vのピンに流すことのできる最大の電流は200mAです。また、74HC595のピンごとの最大電流は35mA、全体では70mAです。これ以上の電流を流すとチップが壊れる可能性があるので注意してください。

カソードを接続するピンには、前述の抵抗を用いた場合、1.7V/240Ω8=56.7mA(小数点を含めて全て点灯した場合)の電流が流れることになります。小数点を使わない場合でも、1.7V/240Ω7=49.6mAの電流が流れる可能性があるので注意してください。

7セグメントLED(OSL40562-LB)のデータシート

OSL40562-LBについては、こちらを参照してください。

シフトレジスタ

今回利用する4桁7セグメントLED(OSL40562-LB)は、カソードが4本、アノードが8本あるので、Arduinoで制御しようとすると、合計12本のピンを利用することになります。4桁7セグメントLEDだけを制御するのであればこれでも問題はありませんが、他にセンサを利用するとなると、Arduinoのピンが不足するかもしれません。

このような場合に登場するのがシフトレジスタです。シフトレジスタにはさまざまな種類がありますが、今回利用するのは、直列入力を並列出力に変換する74HC595というシフトレジスタです。74HC595のピンは以下の通りです。データシートによって異なる記号が書いてあったので、とりあえず2種類書いておきます。

ピン番号 記号 意味
1 QB/Q1 パラレルデータ出力1
2 QC/Q2 パラレルデータ出力2
3 QD/Q3 パラレルデータ出力3
4 QE/Q4 パラレルデータ出力4
5 QF/Q5 パラレルデータ出力5
6 QG/Q6 パラレルデータ出力6
7 QH/Q7 パラレルデータ出力7
8 GND 接地(0V)
9 QH’/Q7S シリアルデータ出力(74HC595を複数接続する際に利用します)
10 SCLR/MR マスターリセット(LOWのときアクティブ)
11 SCK/SHCP シフトレジスタクロック入力
12 RCK/STCP 記憶レジスタクロック入力
13 G/OE 出力可能(LOWのときアクティブ)
14 SER/DS シリアルデータ入力
15 QA/Q0 パラレルデータ出力0
16 Vcc +5V

具体的な使い方は以下の通りです。

  • RCK/STCPをLOWにする(RCK/STCPがHIGHになるときに、それまでのデータが出力されます)。
  • データを送信する。具体的には以下を繰り返す。今回は8ビットなので8回繰り返します。
    • SER/DSにデータを送信する。
    • SCK/SHCPをLOWからHIGHにする。これにより、SER/DSの値が順次入力されます。
  • RCK/STCPをHIGHにする。これにより、8ビットのデータが、送信された順に、QH/Q7からQA/Q0に出力されます。

以下に概念図を示します。理解を助けるための図なので、実際のタイミングとは異なると思います。

Arduinoからのデータ送信

Arduinoからシフトレジスタを使うには、shiftOut()関数か標準ライブラリのSPIの機能を使います。shiftOut()は、上記の「データを送信する」をソフトウェア(C言語)で記述したものです。SPIは、それをArduinoが使っているATmega328Pのハードウェアの機能を利用したものです。ハードウェアの機能を利用する分高速に動作しますが、特定のピンでしか利用できません。今回は、SPIを利用します。SPIでは、8ビットの出力が行われます。

SPIを利用するには、初期化時に以下を行います。

  • SPI.begin()で、ピンのモードの設定や、SPI関連のレジスタの設定を行う。
  • SPI.setBitOrder()で、送信するデータのビットオーダを設定する。今回は上位ビットから送信します。
  • SPI.setClockDivider()で、クロックの分周比を設定する。
  • SPI.setDataMode()で、クロック極性やクロック位相を選択する。74HC595を使うときは、SPI_MODE0です。

その後、データを送るには、以下を行います。

  • RCK/STCPにLOWを出力する。
  • SPI.transfer()で、データを送信する。SPI.transfer()では、一度に8ビットのデータを送信します。SPI.transfer()は、SER/DSへのデータ送信とSCK/SHCPへのクロックの送信を行います。
  • RCK/STCPSTCPにHIGHを出力する。

ハードウェア

以下のようにArduinoと74HC595、OSL40562-LBを接続します。74HC595はピン番号(意味)、OSL40562-LBはピン番号(セグメント/桁)を示しています。Arduinoのピン番号のうち、11/12/14は、それぞれ、SCK/SS/MOSIという名称(変数名)が、pins_arduino.hで定義されています。

Arduino 74HC595 OSL40562-LB
1(Q1) 2(D)
2(Q2) 3(DP)
3(Q3) 4(C)
4(Q4) 5(G)
5(Q5) 7(B)
6(Q6) 10(F)
7(Q7) 11(A)
GND 8(GND)
9(Q7S)
+5V 10(MR)
13(SCK) 11(SHCP)
10(SS) 12(STCP)
GND 13(OE)
11(MOSI) 14(DS)
15(Q0) 1(E)
+5V 16(VCC)
2 6(DIG4)
3 8(DIG3)
4 9(DIG2)
5 12(DIG1)

スケッチ

考え方

各数字を表示するときに点灯するセグメントは以下の通りとします。

上記の図をまとめると以下の表のようになります。

数字 A(Q7) B(Q5) C(Q3) D(Q1) E(Q0) F(Q6) G(Q4)
0
1
2
3
4
5
6
7
8
9

Q7からQ0の順に並べなおすと以下のようになります。それらを2進数で表記したものを一番右の列に示します。一番右の列の数字をArduinoから送信すると、セグメントに数字が表示されます。

数字 Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0 表示(2進表記)
0 1 1 1 0 1 0 1 1 0b11101011
1 0 0 1 0 1 0 0 0 0b00101000
2 1 0 1 1 0 0 1 1 0b10110011
3 1 0 1 1 1 0 1 0 0b10111010
4 0 1 1 1 1 0 0 0 0b01111000
5 1 1 0 1 1 0 1 0 0b11011010
6 1 1 0 1 1 0 1 1 0b11011011
7 1 1 1 0 1 0 0 0 0b11101000
8 1 1 1 1 1 0 1 1 0b11111011
9 1 1 1 1 1 0 1 0 0b11111010

スケッチ

4桁7セグメントLEDの制御の4桁の数字を表示する(表示装置)で作成したプログラムを改造します。改造するのは、digits[]とdisplay_number()、clear_number()、setup()です。

GitHubにもコードを置いてあります。

 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "SPI.h"
 
const int cathode_pins[] = {2, 3, 4, 5};  // カソードに接続するArduinoのピン
const int number_of_cathode_pins = sizeof(cathode_pins) / sizeof(cathode_pins[0]);
int numbers_to_display = 0; // LEDに表示する数字を保持する変数
 
const byte digits[] = {
  0b11101011, // 0
  0b00101000, // 1
  0b10110011, // 2
  0b10111010, // 3
  0b01111000, // 4
  0b11011010, // 5
  0b11011011, // 6
  0b11101000, // 7
  0b11111011, // 8
  0b11111010, // 9
};
 
// 1桁の数字(n)を表示する
void display_number (int n) {
  digitalWrite(SS, LOW);
  SPI.transfer(digits[n]);
  digitalWrite(SS, HIGH);
}
 
// アノードをすべてLOWにする
void clear_segments() {
  digitalWrite(SS, LOW);
  SPI.transfer(0);
  digitalWrite(SS, HIGH);
}
 
void display_numbers () {
  int n = numbers_to_display;  // number_to_displayの値を書き換えないために変数にコピー
  for (int i = 0; i < number_of_cathode_pins; i++) {
    digitalWrite(cathode_pins[i], LOW);
    display_number(n % 10); // 最後の一桁を表示する
    delayMicroseconds(100);
    clear_segments();
    digitalWrite(cathode_pins[i], HIGH);
    n = n / 10; // 10で割る
  }
}
 
void set_numbers(int n) {
  noInterrupts();
  numbers_to_display = n;
  interrupts();
}
 
void setup() {
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setDataMode(SPI_MODE0);
   
  for (int i = 0; i < number_of_cathode_pins; i++) {
    pinMode(cathode_pins[i], OUTPUT);  // cathode_pinを出力モードに設定する
    digitalWrite(cathode_pins[i], HIGH);
  }
 
  // f = クロック周波数 / ( 2 * 分周比 * ( 1 + 比較レジスタの値))
  // 分周比=32, 比較レジスタの1値=255 -> f = 16000000 / (2 * 32 * 256) = 976 Hz
  OCR2A = 255; // 255クロックごとに割り込みをかける
  TCCR2B = 0b100; // 分周比を32に設定する
  bitWrite(TIMSK2, OCIE2A, 1); // TIMER2を
}
 
void loop () {
  for (int i = 0; i < 10000; i++) {
    set_numbers(i);
    delay(100);
  }
}
 
ISR(TIMER2_COMPA_vect)
{
  display_numbers();
}

バージョン

Hardware:Arduino Uno
Software:Arduino 1.0.1

最終更新日

November 1, 2022

inserted by FC2 system