こんにちは、ENGかぴです。
Seeeduino XIAOは機能が充実しておりモジュールとして使い勝手がよいのですがサイズが小さくDIOピンが少なくなります。マルチプレクサを使うことでDIの不足を補うことができます。マルチプレクサでDIを拡張する方法をまとめました。マルチプレクサ74HC4051AP(東芝セミコン)を使用しています。
Seeeduino XIAOで動作確認したことについてリンクをまとめています。
Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方
DIをマルチプレクサで拡張する
マルチプレクサは8つのスイッチを切り替えて使用する部品です。スイッチの片方はコモン(COM)であり、COMとマイコンのDIOに接続することでDIOの拡張できます。
マルチプレクサのCOMにDIとDOのどちらでも接続はできますが、DOで使用する場合は出力の保持ができないので注意が必要です。
マルチプレクサのスイッチ出力の切り替え
マルチプレクサはINHをLにするとデバイスが動作開始します。A、B、Cで0~7のチャンネルを切り替えて共通のCOMに入力します。マイコンのDIにCOMを接続するとA、B、Cで指定したチャンネルのDI情報をCOMを介して受けることができます。
マルチプレクサを制御する
マルチプレクサを使用するための初期化とビット操作について説明します。
#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ウェイトを置いています。
上の例では、ウェイト後に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で学べるソフト開発と標準ライブラリの使い方
PR:アクセンチュアの転職なら【コンサルアクシスコンサルティング】
最後まで、読んでいただきありがとうございました。