Seeeduino XIAOのDIをマルチプレクサで拡張する

組み込みエンジニア

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

Seeeduino XIAOは機能が充実しておりモジュールとして使い勝手がよいのですがサイズが小さくDIOピンが少なくなります。マルチプレクサを使うことでDIの不足を補うことができます。マルチプレクサでDIを拡張する方法をまとめました。マルチプレクサ74HC4051AP(東芝セミコン)を使用しています。

Seeeduino XIAOで動作確認したことについてリンクをまとめています。

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

DIをマルチプレクサで拡張する

マルチプレクサは8つのスイッチを切り替えて使用する部品です。スイッチの片方はコモン(COM)であり、COMとマイコンのDIOに接続することでDIOの拡張できます。

マルチプレクサのCOMにDIとDOのどちらでも接続はできますが、DOの場合信号の保持が難しいため外部機器がキープリレーなど状態を保持できるものに用途が限定されます。

マルチプレクサのスイッチ出力の切り替え

TC74HC4051APのピン配置と真理値表(引用:東芝セミコンダクターデータシート)
TC74HC4051APのピン配置と真理値表(引用:東芝セミコンダクターデータシート)

マルチプレクサはINHをLにするとデバイスが動作開始します。COMを共通としてA、B、Cの入力信号によって信号名0~7を切り替えながら出力します。COMをマイコンのDIに接続することでマルチプレクサの8本のDIをCOMのDI1本で受けることができます。

出力する信号名はAをLSB(最下位ビット)、CをMSB(最上位ビット)となるように接続するマイコンのDOの出力を調整する必要があります。

マルチプレクサを制御する

マルチプレクサを使用するための初期化とビット操作について説明します。

#define PIN_DI1 0
#define PIN_DOA 3
#define PIN_DOB 4
#define PIN_DOC 5

void setup(){
    
    pinMode(PIN_DI1,INPUT_PULLUP); //マルチプレクサのCOMに接続
    pinMode(PIN_DOA,OUTPUT); //マルチプレクサのAに接続
    pinMode(PIN_DOB,OUTPUT); //マルチプレクサのBに接続
    pinMode(PIN_DOC,OUTPUT); //マルチプレクサのCに接続
}

マルチプレクサの信号の切り替えはA、B、Cによって制御するためマイコンでDOを3つ準備します。マルチプレクサのCOMをマイコンのDIに取り込むためにマイコン内蔵のプルアップ付きのDIとして初期化を行います。

uint8_t di[MUL_MAX];

void loop(){
    uint8_t mulmode;
    bool loopflg = true;

    mulmode = MUL_0;

    do{ 
        MulDiCng(mulmode); //マルチプレクサの制御
        delayMicroseconds(10); //10us切り替え後の遅延

        di[ mulmode ] = digitalRead(PIN_DI1);
        
        if( ++mulmode >= MUL_MAX){
            loopflg = false;
        }
    }while(loopflg);
}

マルチプレクサの制御の例を示しています。関数MulDiCng()をコールしてマルチプレクサの制御を切り替えています。切り替えた後は信号安定化のため10usウェイトを置いています。

データシートによるとVCC=2.0V時で最大で1000nsのスイッチングになるため余裕をもって10usのウェイトにしています。

上の例では、ウェイト後に0ピン(PIN_DI1)を読み込んでいますが、外部信号を取り込む場合はチャタリングなどによって信号が安定しないことがあるためフィルタ処理を追加することがあります。チャタリング防止の方法については下記記事にまとめています。

Arduino環境でのタイマ管理とDIのチャタリング防止の方法

ソースコード全体では4回一致のフィルタを実装しています。

#define USE_BITS 3 //3ビット使用する

/* Led7Seg pattern function add */
void MulDiCng(uint8_t no){
    uint8_t i;
    uint8_t bit;    

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

        if( bit & no){ //ビットが立っているかの確認
            digitalWrite(i + PIN_DOA, HIGH);
        }
        else{
            digitalWrite(i + PIN_DOA, LOW); 
        }
    }
}

引数に0~7までの値を指定し引数の値に応じて下位ビットから順にビットが立っているかの確認を行い、ビットが立っている場合はDOにHをセットします。引数に5を指定した場合を例にして説明します。

例)引数に5を指定した場合
0x05と0x01の論理積は1なので3ピン(PIN_DO_A)がHとなる。
0x05と0x02の論理積は0なので4ピン(PIN_DO_B)がLとなる。
0x05と0x04の論理積は1なので5ピン(PIN_DO_C)がHとなる。

0x05と各ビットの論理積が1であれば対象のDOにHをセットし0であればLをセットするようにします。真理値表によるとAとCがHとなるため5が出力対象になりCOMと接続されます。

動作確認

マルチプレクサの動作確認の回路図
マルチプレクサの動作確認の回路図

Seeeduino XIAOの1ピンのDIにマルチプレクサのCOMを接続しています。4ピンから6ピンはマルチプレクサの切り替え制御に使用するためDOとして使用します。

C1はマルチプレクサの電源間のバイパスコンデンサとして使用しています。ロジックICなどは電源間に0.1uF程度のバイパスコンデンサを実装するのが一般的です。

SW1とSW2をONするとLになるようにしています。マルチプレクサによってSWの情報がSeeeduino XIAOのDIに入力されます。DIは内部プルアップ付きのDIとしているためSWをONしていない箇所においてはHとなります。

SWを操作したときの状態変化を検出してシリアルモニタに表示するようにしています。

シリアルモニタによる動作確認
シリアルモニタによる動作確認

SWを操作するとシリアルモニタに対象の番号の変化を表示していることでマルチプレクサの切り替えができていることが確認できました。

ソースコード全体

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

#include <TimerTC3.h>

#define PIN_DI1 0
#define PIN_DOA 3
#define PIN_DOB 4
#define PIN_DOC 5
#define DI_FILT_MAX 4
#define MUL_MAX 8
#define USE_BITS 3 //3ビット使用する
#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる
#define FILT_MIN 1

struct DIFILT_TYP{
    uint8_t wp;
    uint8_t buf[MUL_MAX][DI_FILT_MAX];
    uint8_t di[MUL_MAX];
    uint8_t olddi[MUL_MAX];
};

enum MUL_NO{
    MUL_0 = 0,
    MUL_1,
    MUL_2,
    MUL_3,
    MUL_4,
    MUL_5,
    MUL_6,
    MUL_7,
    MUL_NO_MAX
};

// application use
DIFILT_TYP difilt;
int16_t timdifilt = TIME_OFF;
int16_t cnt10ms;

/*** Local function prototypes */
void TimerCnt();
void mainTimer();
void DiFilter();

void setup(){
  
    Serial.begin(115200);

    pinMode(PIN_DI1,INPUT_PULLUP); //マルチプレクサのCOMに接続
    pinMode(PIN_DOA,OUTPUT); //マルチプレクサのAに接続
    pinMode(PIN_DOB,OUTPUT); //マルチプレクサのBに接続
    pinMode(PIN_DOC,OUTPUT); //マルチプレクサのCに接続

    TimerTc3.initialize(1000);
    TimerTc3.attachInterrupt(TimerCnt);
    timdifilt = FILT_MIN;

    for( uint8_t i=0; i < MUL_MAX; i++){
        difilt.olddi[i] = difilt.di[i];
    }
}

void loop(){

    mainTimer();
    DiFilter();
}
/* callback function add */
void TimerCnt(){
    ++cnt10ms;
}
/* Timer Management function add */
void mainTimer(){

    if( cnt10ms >= BASE_CNT ){
        cnt10ms -=BASE_CNT; //1msごとにここに遷移する

        if( timdifilt > TIME_UP ){
            timdifilt--;
        }
    }
}
/* DiFilter function add */
void DiFilter(){
    uint8_t mulmode;
    bool loopflg = true;

    if( timdifilt == TIME_UP ){
        mulmode = MUL_0;
        do{ 
            MulDiCng(mulmode); //マルチプレクサの制御
            delayMicroseconds(10); //10us切り替え後の遅延

            difilt.buf[mulmode][difilt.wp] = digitalRead(PIN_DI1);

            if( difilt.buf[mulmode][0] == difilt.buf[mulmode][1] &&
                difilt.buf[mulmode][1] == difilt.buf[mulmode][2] &&
                difilt.buf[mulmode][2] == difilt.buf[mulmode][3] ){ //4回一致を確認
                difilt.di[mulmode] = difilt.buf[mulmode][0];
                }

            if( ++mulmode >= MUL_MAX){
                loopflg = false;
            }

        }while(loopflg);

        if( ++difilt.wp >= DI_FILT_MAX ){
            difilt.wp = 0;
        }

        for( uint8_t i=0; i < MUL_MAX; i++){
            if( difilt.di[i] ^ difilt.olddi[i]){ //対象のDIに変化があるか
                Serial.print("Change DI:");
                Serial.println(i);
            }
            difilt.olddi[i] = difilt.di[i];
        }        

        timdifilt = FILT_MIN;
    }
}
/* Led7Seg pattern function add */
void MulDiCng(uint8_t no){
    uint8_t i;
    uint8_t bit;    

    for( i=0; i < USE_BITS; i++){ //ビットが立っているかの確認
        bit = 1 << i;

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

関連リンク

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

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

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

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

Code Camp完全オンラインのプログラミング個人レッスン【無料体験】

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

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