ArduinoのDO制御で7セグメントLEDを表示する

組み込みエンジニア

こんにちは、ENGかぴです。

Arduinoの標準ライブラリでDOを制御してLEDなどを簡単に点灯消灯させることができます。DO制御の応用例として7セグメントLEDに数値を表示できます。複数のDOの制御をパターン化して7セグメントLEDに表示する方法をまとめています。

応用例として温度センサーの情報を7セグメントLEDに表示して動作確認した方法についてまとめています。

Arduinoと7セグメントLEDで温度を表示する

Arduino UNOを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

7セグメントLEDを表示する

7セグメントLEDは小数点を含めると8つのLED(8セグメント)で構成されています。数値を表示するためには該当する番号のLEDを点灯させる必要があります。

7セグメントLEDの構造

7セグメントLEDの構造
7セグメントLEDの構造

7セグメントLEDはa~gまでの7つのLEDとDPのドット部分にそれぞれLEDが実装されているモジュールです。a~g及びDPやDGをArduinoのDOに接続し制御することで7セグメントLEDを点灯消灯できます。

説明図はLEDのK(カソード)側が共通(コモン)のパッケージ例を示しています。この場合はa~g及びDPをHレベルにするとLEDが点灯するため正論理の動作となります。

LEDのA(アノード)側が共通のパッケージではa~g及びDPをLレベルにするとLEDが点灯するため負論理での動作となります。

コモン側は基本的に全LEDを点灯させたときの総電流が流れ込むためマイコンで直接受ける際はシンク電流や出力電流に注意する必要があります。各LEDに抵抗を接続して合計の電流を制限する方法もありますが、トランジスタなどによってコモン側の電流を流す方が安全です。

負論理のパッケージの場合は電源の+側をLEDのアノードに流すことになるのでトランジスタの場合はコレクタに電源を接続し、エミッタにLEDのコモンを接続するエミッタフォロワで接続するとよいと思います。(ベースはDGに接続する)

4桁の7セグメントLEDの構造と考え方

ダイナミック・ドライブ型の7セグメントLEDの構造(A側コモン:正論理)
ダイナミック・ドライブ型の7セグメントLEDの構造(A側コモン:正論理)

4桁の7セグメントLEDには主に以下の2つのドライブ方式によるものがあります。

  1. スタティック・ドライブ型
  2. ダイナミック・ドライブ型

スタティック・ドライブ型はLEDの桁を個別に制御できるものです。桁数が多くなるとDOの数が増えてしまう欠点がありますが、表示が鮮明になります。

ダイナミックドライブはLEDのAまたはKが共通になっており表示する桁を制御するDO-1からDO-4を制御してLEDパターンを表示するものでDOの数を減らすことができます。

DO-1からDO-4を高速で切り替えて表示しているため切り替えのタイミングなどによって表示がちらつくなどの欠点がありますが、DOの数を押さえられるためダイナミック・ドライブ型が多く使用されます。以下に特徴をまとめています。

種類特徴
スタティック・ドライブ型LED毎にDOが必要でありDO数が増えてしまう。
4桁の場合は合計28本のDOが必要となる。
LED表示が鮮明なのがメリット。
ダイナミック・ドライブ型表示を切り替えるためDO数が削減できる。
4桁の場合はセグメント用の7本と桁切り替えの4本、合計11本のDOとなる。
LED表示が切り替えによるタイミングによってはちらつくのがデメリット。
7セグメントLEDの種類と特徴

7セグメントで数値を表現する方法

数値を表現するパターンの考え方
数値を表現するパターンの考え方

7セグメントLEDで数値を表現するDOを7本準備する必要があります。a~gで0~9を表現するパターンを作るため1バイトデータになるようにピンを配置します。DOT(DP)を使用する場合はDOT分のDOも準備が必要となります。

#define PIN_DO_A 2 
#define PIN_DO_B 3
#define PIN_DO_C 4
#define PIN_DO_D 5
#define PIN_DO_E 6
#define PIN_DO_F 7
#define PIN_DO_G 8

#define PIN_DP1 9
#define PIN_DP2 10

void setup(){

    pinMode(PIN_DP1,OUTPUT); //1桁目の表示(右側)
    pinMode(PIN_DP2,OUTPUT); //2桁目の表示(右から2番目)

    pinMode(PIN_DO_A,OUTPUT); //7セグメントのa部分のDO
    pinMode(PIN_DO_B,OUTPUT); //7セグメントのb部分のDO
    pinMode(PIN_DO_C,OUTPUT); //7セグメントのc部分のDO
    pinMode(PIN_DO_D,OUTPUT); //7セグメントのd部分のDO
    pinMode(PIN_DO_E,OUTPUT); //7セグメントのe部分のDO
    pinMode(PIN_DO_F,OUTPUT); //7セグメントのf部分のDO
    pinMode(PIN_DO_G,OUTPUT); //7セグメントのg部分のDO
}

#defineでa~gまでの番号を定義していますがLED点灯のためのパターンを判定する際に順番に制御するために2~8は連番にしています。PIN_DP1とPIN_DP2は桁数の切り替えに使用するDOです。

void loop(){
    //7セグメントLEDが正論理であるとする
    digitalWrite(PIN_DPB, HIGH); //7セグメントLEDのa部分のDOをH
    digitalWrite(PIN_DPC, HIGH); //7セグメントLEDのc部分のDOをH
}

例のようにaとcのDOを制御することによって数値を表現することができます。例では1を表現していますが、a~gまでのDOを制御することで0~9の数値を表現します。16進数まで表現するのであればA~Fまでを表現するようにDOを制御します。

数値を表現するパターンを変数として宣言し対応するビットがセットされていた場合にLEDを点灯させる仕組みを考えていきます。

#define LED7SEG_NEGATIVE //負論理の時コメントアウトを外す

//正論理の場合のパターン配列の順に0~9までのパターンを格納
byte Seg7Set[10]={ 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x6F};

void setup(){
    #ifdef LED7SEG_NEGATIVE
        for(uint8_t i=0; i < sizeof(Seg7Set); i++){
            Seg7Set[i] = ~Seg7Set[i] & 0x7F; //負論理の場合はパターンの論理を反転する
        }
    #endif
}

配列としてSeg7Set[]を宣言し0~9を1バイトデータとしてパターン化した値を初期値として格納しています。パターンの生成は正論理でa~gにおいて点灯する場合に1をセットし消灯する場合に0をセットしたときの値を並べたものになっています。

例)2をパターン化する場合
7セグメントLEDにおいて2を表現する場合はa,b,d,e,gに1をセットする必要があるため2進数で表現すると0b01011011となり16進数で表すと0x5Bになります。(最上位ビットは未使用なので0としている。)

7セグメントLEDが負論理のものであれば#define LED7SEG_NEGATIVEを有効にしてSeg7Set()のパターンを反転した値をパターンとして準備しています。

void loop(){
    Led7SegSet(Seg7Set[5]); //7セグメントLEDに0を表示する場合
}
/* Led7Seg pattern function add */
void Led7SegSet(uint8_t no){
    uint8_t i;
    uint8_t bit;    

    for( i=0; i < 7; i++){
        bit = 1 << i; //1ずつシフト

        if( bit & no){ //論理積をとってビットが立っているか確認
            digitalWrite(i + PIN_DO_A, HIGH);
        }
        else{
            digitalWrite(i + PIN_DO_A, LOW); 
        }
    }
}

Seg7Set()のパターンから点灯するLEDの制御を行う関数としてLed7SegSet()を準備しています。

Led7SegSet()の引数にSeg7Set()で選択したパターンをセットし下位ビットから順にビットが立っているかの確認を行い、ビットが立っている場合はDOにHをセットします。Seg7Set[5]=0x6Dを例にして説明します。

例)a~cまでのDOの判定(5を表示する)
0x6Dと0x01の論理積は1なので2ピン(PIN_DO_A)がHとなる。
0x6Dと0x02の論理積は0なので3ピン(PIN_DO_B)がLとなる。
0x6Dと0x04の論理積は1なので4ピン(PIN_DO_C)がHとなる。

0x6Dと各ビットの論理積が1であれば対象のDOにHをセットし0であればLをセットするようにします。0から7ビットまで繰り返すことで5が表示されます。

動作確認

7セグメントLEDによるカウンタの回路図
7セグメントLEDによるカウンタの回路図

7セグメントLEDに0~99を表示するカウンタを実装します。1秒ごとにカウントアップして表示します。7セグメントLEDは負論理のものだったのでトランジスタをエミッタフォロワとして使用しています。

7セグメントLEDは右から2つの桁を使用し0~99が繰り返して表示されているのが確認できましたが、LEDが少し薄く点灯していました。

ダイナミック・ドライブ型の欠点よりLED表示が薄くなっているため角度が悪いと表示が見えにくくなってしまいます。それでも正面から見る分にはカウント値が確認できるので問題ないと思います。

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。チェックはしておりますがコメントなど細かな部分で間違っている可能性があります。参考としてお使いいただければと思います。

#include <MsTimer2.h>

#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる
#define CNT_MAX 100

#define PIN_DO_A 2 
#define PIN_DO_B 3
#define PIN_DO_C 4
#define PIN_DO_D 5
#define PIN_DO_E 6
#define PIN_DO_F 7
#define PIN_DO_G 8

#define PIN_DP1 9
#define PIN_DP2 10

#define LED7SEG_NEGATIVE //負論理の時コメントアウトを外す

typedef enum{
    COL_NO1 = 0,
    COL_NO2,
    COL_NO3,
    COL_NO4,
    COL_MAX
}COL_NO;

byte Seg7Set[10]={ 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x6F};
int8_t cnt10ms;
uint8_t count;
int8_t timcount = TIME_OFF;
COL_NO col;

/*** Local function prototypes */
void TimerCnt();
void mainTimer();
void Led7SegClr();
void Led7SegSet(uint8_t no);
void Led7SegMng();

void setup(){

    pinMode(PIN_DP1,OUTPUT); //1桁目の表示(右側)
    pinMode(PIN_DP2,OUTPUT); //2桁目の表示(右から2番目)

    pinMode(PIN_DO_A,OUTPUT); //7セグメントのa部分のDO
    pinMode(PIN_DO_B,OUTPUT); //7セグメントのb部分のDO
    pinMode(PIN_DO_C,OUTPUT); //7セグメントのc部分のDO
    pinMode(PIN_DO_D,OUTPUT); //7セグメントのd部分のDO
    pinMode(PIN_DO_E,OUTPUT); //7セグメントのe部分のDO
    pinMode(PIN_DO_F,OUTPUT); //7セグメントのf部分のDO
    pinMode(PIN_DO_G,OUTPUT); //7セグメントのg部分のDO

    MsTimer2::set(1,TimerCnt); //1msごとに関数へ遷移
    MsTimer2::start();

    #ifdef LED7SEG_NEGATIVE
        for(uint8_t i=0; i < sizeof(Seg7Set); i++){
            Seg7Set[i] = ~Seg7Set[i] & 0x7F;
        }
    #endif

    Led7SegClr();
    timcount = CNT_MAX;
}

void loop(){
    
    mainTimer();
    Led7SegMng();

    if( timcount == TIME_UP){
        timcount = CNT_MAX;
        if( ++count>= 100){
            count=0;
        }
    }
}
/* callback function add */
void TimerCnt(){
    ++cnt10ms;
}
/* Timer Management function add */
void mainTimer(){

    if( cnt10ms >= BASE_CNT ){
        cnt10ms -=BASE_CNT; //10msごとにここに遷移する
        if( timcount > TIME_UP ){
            timcount--;
        }
    }
}
/* Led7Seg Management function add */
void Led7SegMng(){
    uint8_t no;
    uint8_t no10;
    uint8_t out_off;
    uint8_t out_on;

    #ifdef LED7SEG_NEGATIVE
        out_on = HIGH;
        out_off = LOW;
    #else
        out_on = LOW;
        out_off = HIGH
    #endif

    no10 = count / 10; //10の位
    no = count % 10; //1の位

    switch (col)
    {
        case COL_NO::COL_NO1:
            digitalWrite(PIN_DP1,out_on);
            Led7SegSet(Seg7Set[no]);
            digitalWrite(PIN_DP1,out_off);
            col = COL_NO::COL_NO2;
            break;
        case COL_NO::COL_NO2:
            digitalWrite(PIN_DP2,out_on);
            Led7SegSet(Seg7Set[no10]);
            digitalWrite(PIN_DP2,out_off);
            col = COL_NO::COL_NO1;
            break;
        default:
            break;
    }
    Led7SegClr();
}
/* Led7Seg Clear function add */
void Led7SegClr(){
    uint8_t i;
    uint8_t out;

    #ifdef LED7SEG_NEGATIVE
        out = HIGH;
    #else
        out = LOW;
    #endif
    
    for( i = 0; i < 7; i++){
        digitalWrite(i + PIN_DO_A, out);
    }
}
/* Led7Seg pattern function add */
void Led7SegSet(uint8_t no){
    uint8_t i;
    uint8_t bit;    

    for( i=0; i < 7; i++){
        bit = 1 << i;

        if( bit & no){
            digitalWrite(i + PIN_DO_A, HIGH);
        }
        else{
            digitalWrite(i + PIN_DO_A, LOW); 
        }
    }
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

TECH::CAMPプログラミング教養【無料体験会】

最後まで、読んでいただきありがとうございました。

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