Arduinoで7セグメントLEDを使ってみる

th_seg7-3

意外とこれまで使ってこなかった電子部品として、7セグメントLEDがあります。これのArduino制御についての情報はもう充分過ぎるほどにネット上にあると思いますが、せっかくなのでまとめておきます。

今回買ってきたのは、ローム製のLA-501VF-1というものです。確か1個50円。本当はカソードコモン(GND共通)型の方が理屈としてわかりやすいので良かったのですが、行った電子部品屋さんでは残念ながら手頃なものが見つからなかったので、アノードコモン型です。

こちらのデータシートに従って配線していきます。

th_seg7-1

うわあ、ビジー。アノードコモンは2つ(PIN.3とPIN.8)ありますが、どちらか一方だけ5Vに繋げばOKです。残りの8つは抵抗(ここでは適当に2.2kΩ)を挟んでIOピンに繋げます。

ではでは、続いてプログラミングです。ボタンを押している間だけ全部のセグメントが光る、とてもシンプルなプログラムです。

#define PIN_1   2
#define PIN_2   3
#define PIN_4   4
#define PIN_5   5
#define PIN_6   6
#define PIN_7   7
#define PIN_9   9
#define PIN_10 10
#define BUTTON_PIN 11

uint8_t last_state = HIGH; 

void setup(){
  pinMode(PIN_1, OUTPUT);
  pinMode(PIN_2, OUTPUT);
  pinMode(PIN_4, OUTPUT);
  pinMode(PIN_5, OUTPUT);
  pinMode(PIN_6, OUTPUT);
  pinMode(PIN_7, OUTPUT);
  pinMode(PIN_9, OUTPUT);
  pinMode(PIN_10, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  all_led(HIGH);
}

void loop(){
  uint8_t state = digitalRead(BUTTON_PIN);
  if(last_state == HIGH && state == LOW){
    all_led(LOW);
  }else if(last_state == LOW && state == HIGH){
    all_led(HIGH);
  }
  last_state = state;

  delay(20);  
}

void all_led(boolean state){
    digitalWrite(PIN_1,state);
    digitalWrite(PIN_2,state);
    digitalWrite(PIN_4,state);
    digitalWrite(PIN_5,state);
    digitalWrite(PIN_6,state);
    digitalWrite(PIN_7,state);
    digitalWrite(PIN_9,state);
    digitalWrite(PIN_10,state);   
}

アノードコモンなので、各ピンをLOWにして電位差を作り出してやれば、電流が流れて対応するセグメントが発光します。

次は、ボタンを押したら数字をカウントアップしていくプログラムです。

#include <avr/pgmspace.h>

#define PIN_1   2
#define PIN_2   3
#define PIN_4   4
#define PIN_5   5
#define PIN_6   6
#define PIN_7   7
#define PIN_9   9
#define PIN_10 10
#define BUTTON_PIN 11

/*
 * LA-501VFの各セグメントの対応
 * --a--         a:PIN_7
 * f   b    f:PIN_9   b:PIN_6
 * |-g-|         g:PIN_10
 * e   c    e:PIN_1   c:PIN_4
 * --d-- D       d:PIN_2       D:PIN_5
 */

#define ON  LOW
#define OFF HIGH

// PIN_1~PIN_10(3,8除く)で点灯箇所を示す配列を作成
const uint8_t seg[][8] PROGMEM = {
//Seg:  1   2   4   5   6   7   9   10
      {ON , ON, ON,OFF, ON, ON, ON,OFF}, // 0
      {OFF,OFF, ON,OFF, ON,OFF,OFF,OFF}, // 1
      {ON,  ON,OFF,OFF, ON, ON,OFF, ON}, // 2
      {OFF, ON, ON,OFF, ON, ON,OFF, ON}, // 3
      {OFF,OFF, ON,OFF, ON,OFF, ON, ON}, // 4
      {OFF, ON, ON,OFF,OFF, ON, ON, ON}, // 5
      {ON,  ON, ON,OFF,OFF, ON, ON, ON}, // 6
      {OFF,OFF, ON,OFF, ON, ON, ON,OFF}, // 7
      {ON,  ON, ON,OFF, ON, ON, ON, ON}, // 8
      {OFF, ON, ON,OFF, ON, ON, ON, ON}  // 9
};

uint8_t last_state = HIGH; 
uint8_t count = 0;

void setup(){
  pinMode(PIN_1, OUTPUT);
  pinMode(PIN_2, OUTPUT);
  pinMode(PIN_4, OUTPUT);
  pinMode(PIN_5, OUTPUT);
  pinMode(PIN_6, OUTPUT);
  pinMode(PIN_7, OUTPUT);
  pinMode(PIN_9, OUTPUT);
  pinMode(PIN_10, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  pattern_on(0);
}

void loop(){
  uint8_t state = digitalRead(BUTTON_PIN);
  if(last_state == HIGH && state == LOW){
    count++;
    count = (count>9 ? 0: count);
    pattern_on(count);
  }
  last_state = state;

  delay(20);  
}

void pattern_on(uint8_t num){
  digitalWrite(PIN_1,  pgm_read_byte(&(seg[num][0])));
  digitalWrite(PIN_2,  pgm_read_byte(&(seg[num][1])));
  digitalWrite(PIN_4,  pgm_read_byte(&(seg[num][2])));
  digitalWrite(PIN_5,  pgm_read_byte(&(seg[num][3])));
  digitalWrite(PIN_6,  pgm_read_byte(&(seg[num][4])));
  digitalWrite(PIN_7,  pgm_read_byte(&(seg[num][5])));
  digitalWrite(PIN_9,  pgm_read_byte(&(seg[num][6])));
  digitalWrite(PIN_10, pgm_read_byte(&(seg[num][7]))); 
}

PROGMEMとか使っているのは、できるだけArduinoのSRAMを節約するためです。7セグを光らせるだけが目的だと単に面倒なだけですが、7セグは実際には何かしらと組み合わせることが多いと思うので、節約できるものは節約するに越したことはないかと思います。これで80byteの節約になります。Arduino UNOだとトータル2Kbyteしか使えないので、そのうちの80byteは結構大きいと思います。

実際の動作はこんな感じです。

 

これで7セグ1個の時の基本的な動作はマスターできました。次は7セグ2個での二桁表示をしてみたいと思いますが、7セグ1個でIOピンを8個も使ってしまっていますので、2個だとそれだけでArduino UNOのIOピンをほぼ全て使ってしまいます。これだと他のセンサとかを組み合わせて使用することができないので、2つの7セグ表示を高速に切り替えることでIOピンの数を節約するダイナミック制御とやらをやってみます。

7セグが1つのときからは、配線が次のように変わります(アノードコモンの場合)。

  • アノードコモンを、5VピンではなくIOピン(OUTPUT)に繋ぐ(ON/OFFを切り替えられるようにするため)
  • 各位を表す7セグごとに、アノードコモンを接続するIOピンを分ける(例えば1の位は2ピン、10の位は3ピンに繋ぐ)
  • 各セグメントごとにIOピンに繋ぐ線は、すべての7セグで共通にする(例えば1の位のaセグメントと10の位のaセグメントは共通で4ピンに繋ぐ)

 multi7seg

ちょっと見づらいですが、論理的にはこんな感じです。IOピンの数としては、1桁のときと比べて2本しか増えていません(各位の7セグそのもののON/OFF用2本)。

そしてソースコードはこちら。

#include <avr/pgmspace.h>

#define PIN_1   2
#define PIN_2   3
#define PIN_4   4
#define PIN_5   5
#define PIN_6   6
#define PIN_7   7
#define PIN_9   9
#define PIN_10 10
#define BUTTON_PIN 11

#define DIGIT_1_PIN  12
#define DIGIT_10_PIN 13

/*
 * LA-501VFの各セグメントの対応
 * --a--         a:PIN_7
 * f   b    f:PIN_9   b:PIN_6
 * |-g-|         g:PIN_10
 * e   c    e:PIN_1   c:PIN_4
 * --d-- D       d:PIN_2       D:PIN_5
 */

#define ON  LOW
#define OFF HIGH

// PIN_1~PIN_10(3,8除く)で点灯箇所を示す配列を作成
const uint8_t seg[][8] PROGMEM = {
//Seg:  1   2   4   5   6   7   9   10
      {ON , ON, ON,OFF, ON, ON, ON,OFF}, // 0
      {OFF,OFF, ON,OFF, ON,OFF,OFF,OFF}, // 1
      {ON,  ON,OFF,OFF, ON, ON,OFF, ON}, // 2
      {OFF, ON, ON,OFF, ON, ON,OFF, ON}, // 3
      {OFF,OFF, ON,OFF, ON,OFF, ON, ON}, // 4
      {OFF, ON, ON,OFF,OFF, ON, ON, ON}, // 5
      {ON,  ON, ON,OFF,OFF, ON, ON, ON}, // 6
      {OFF,OFF, ON,OFF, ON, ON, ON,OFF}, // 7
      {ON,  ON, ON,OFF, ON, ON, ON, ON}, // 8
      {OFF, ON, ON,OFF, ON, ON, ON, ON}  // 9
};

uint8_t last_state = HIGH; 
uint8_t count = 0;
uint8_t digit = 0;

void setup(){
  pinMode(PIN_1, OUTPUT);
  pinMode(PIN_2, OUTPUT);
  pinMode(PIN_4, OUTPUT);
  pinMode(PIN_5, OUTPUT);
  pinMode(PIN_6, OUTPUT);
  pinMode(PIN_7, OUTPUT);
  pinMode(PIN_9, OUTPUT);
  pinMode(PIN_10, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  pinMode(DIGIT_1_PIN, OUTPUT);
  pinMode(DIGIT_10_PIN, OUTPUT);
}

void loop(){
  uint8_t state = digitalRead(BUTTON_PIN);
  if(last_state == HIGH && state == LOW){
    count++;
    count = (count>99 ? 0: count);
  }
  last_state = state;

  digit++;
  digit = (digit>1 ? 0: digit);

  pattern_on(digit, count);

  delay(10);  
}

void pattern_on(uint8_t digit, uint8_t num){
  uint8_t disp_num = 0;
  switch(digit){
  case 0:
    digitalWrite(DIGIT_1_PIN,  HIGH);
    digitalWrite(DIGIT_10_PIN, LOW);
    disp_num = num % 10;
    break;
  case 1:
    digitalWrite(DIGIT_1_PIN,  LOW);
    digitalWrite(DIGIT_10_PIN, HIGH);
    disp_num = num / 10;
    break;
  }
  digitalWrite(PIN_1,  pgm_read_byte(&(seg[disp_num][0])));
  digitalWrite(PIN_2,  pgm_read_byte(&(seg[disp_num][1])));
  digitalWrite(PIN_4,  pgm_read_byte(&(seg[disp_num][2])));
  digitalWrite(PIN_5,  pgm_read_byte(&(seg[disp_num][3])));
  digitalWrite(PIN_6,  pgm_read_byte(&(seg[disp_num][4])));
  digitalWrite(PIN_7,  pgm_read_byte(&(seg[disp_num][5])));
  digitalWrite(PIN_9,  pgm_read_byte(&(seg[disp_num][6])));
  digitalWrite(PIN_10, pgm_read_byte(&(seg[disp_num][7]))); 
}

実際に動いている様子はこんな感じです。

 

全体ループのdelayが10msのときは気になりませんが、20msぐらいになってくると表示のチラつきが目につき始めます。

 

ちなみに無理にダイナミックにしなくても、7セグ制御用のICを使えばもっと接続本数を減らせるはずです。例えば、

こんな感じのI2Cで制御できる7セグを買えば線は全部で4本で済みます。また逆の考え方で、

Arduino Megaを使ってしまえば使えるIOピンの数が大幅に増えるので、表示桁数が少ないなら各桁単独制御にしてしまっても良いかと思います。その方がチラつきとかも気にしなくて済みますし、制御も簡単だと思います。