Arduinoを使った7セグメントLEDの制御②
はじめに
今回はArduinoを使って7セグメントLEDの制御を行います。
7セグメントLEDの制御は私自身、一度試して記事にしているのですが、ビットフィールドを使うことでより簡単に制御することができそうなのでその方法を紹介します。
前回記事(回路等改めて説明するので、読まなくてもいいです)
7セグメント回路
7セグメントは516ASを使用します。
7セグメントとArduinoは以下の回路図のように接続します。抵抗値はすべて220Ωを使用します。
7セグメントの制御方法
7セグメントの制御はピンaからピンgに与える出力によって行います。この時、ピンに与える出力のパターンとして以下の手法が考えられます。
配列を使った手法
7セグメントのそれぞれのピンの制御には一般的に配列が使用されます。簡単な例を以下に示します。
#define pin_A 2
#define pin_B 3
#define pin_C 4
#define pin_D 5
#define pin_E 6
#define pin_F 7
#define pin_G 8
const int seg_one[] = {0, 1, 1, 0, 0, 0, 0}; //配列で出力パターンを指定
void setup() {
pinMode(pin_A, OUTPUT);
pinMode(pin_B, OUTPUT);
pinMode(pin_C, OUTPUT);
pinMode(pin_D, OUTPUT);
pinMode(pin_E, OUTPUT);
pinMode(pin_F, OUTPUT);
pinMode(pin_G, OUTPUT);
}
void loop() {
digitalWrite(pin_A, seg_one[0]);
digitalWrite(pin_B, seg_one[1]);
digitalWrite(pin_C, seg_one[2]);
digitalWrite(pin_D, seg_one[3]);
digitalWrite(pin_E, seg_one[4]);
digitalWrite(pin_F, seg_one[5]);
digitalWrite(pin_G, seg_one[6]);
}
配列seg_oneの要素にて、ピンaからgそれぞれの出力パターンの指定を行います。その後、loop内の出力関数で実際に出力を行います。(for文を使って配列の要素にアクセスすることで、より短く書くことができます)
配列によって出力パターンの指定が簡単にできるます。一方で、シフト演算子やAND、ORのような処理が難しいです。
数式を使った方法
配列ではなく、2進数を使用することで出力パターンを指定することもできます。
例)
0x0110000 → ピンbとcのみHIGH
この方法では、出力のために2進数の任意の桁の値を取り出す必要があります。そのために、前回記事では2進数の任意の桁の値を取り出す式を作りました。
$$
\frac{(x \% 2^{n+1}) - (x \% 2^{n})}{2^{n}}
$$
これによって、論理式をはじめとする処理を可能にしました。
一方で、可読性の低下や、割り算を使うことによる誤差が問題になりました。
ビットフィールドを使った方法
ビットフィールドの実装は以下の本を参考にしました。
ビットフィールドを使うことで型定義で指定した変数の領域を分割することができます。
この図のように、大枠とする変数A(1byte)を1bitごとに分割し、分割後の各bitにb0からb7の名前を付けています。これによって、大枠とする変数Aと分割後の変数b0からb7の両方にアクセスすることができます。
ビットフィールドは以下のように定義することができます。
//ビットフィールドの定義
union one_byte_union{
unsigned char one_byte;
struct one_bit_field{
unsigned b0: 1; //one_byte内の1bit確保
unsigned b1: 1;
unsigned b2: 1;
unsigned b3: 1;
unsigned b4: 1;
unsigned b5: 1;
unsigned b6: 1;
unsigned b7: 1;
} bit_fild;
};
union one_byte_union segments; //ビットフィールドの呼び出し
ビットフィールドは共用体(union)の中で構造体(struct)を呼び出すことで定義することができます。変数の大枠は共用体を使って、変数の分割は構造体を使っていることが分かります。
呼び出しは以下のように行います。ビットフィールドでは、大枠とする変数と分割後の変数両方にアクセスすることができます。メンバの呼び出し方が異なる点に注意してください。
segments.one_byte = 0x00000000; //変数(大枠)に値を代入
segments.bit_fild.b0 //変数の0番目のビットにアクセス
ちなみに、C++のbool型はTrue、Falseの2値を対象とする型ですが、1byteが確保されます。ですので、フラグ管理を行う際は、複数のフラグをビットフィールドでまとめて管理することでメモリの節約が期待できます。
7セグメントを順番に光らせる
7セグメントの表示を順番に点灯させます。
シフト演算子
順番に点灯させるために、シフト演算子を使用します。シフト演算子は以下の図のような処理を行います。
右にシフトでは、2進数の元の値のそれぞれの桁が右に移動していることが分かります。この処理は以下の式に相当します。
$$
X = \frac{A}{2^{n}}
$$
ここで、nは右シフトの移動量を示します。
左にシフトでは、2進数の元の値のそれぞれの桁が右に移動していることが分かります。この処理は以下の式に相当します。
$$
X = A*2^{n}
$$
今回は、7セグメントのLEDを順番に光らせたいので、左シフトを使用して、1(HIGH)の位置を1bitずつ移動させます。
ソースコード
ソースコードを以下に示します。
#define pin_A 2
#define pin_B 3
#define pin_C 4
#define pin_D 5
#define pin_E 6
#define pin_F 7
#define pin_G 8
union one_byte_union{
unsigned char one_byte;
struct one_bit_field{
unsigned b0: 1;
unsigned b1: 1;
unsigned b2: 1;
unsigned b3: 1;
unsigned b4: 1;
unsigned b5: 1;
unsigned b6: 1;
unsigned b7: 1;
} bit_fild;
};
union one_byte_union segments;
void setup() {
Serial.begin(115200);
segments.one_byte = 0x00000000;
pinMode(pin_A, OUTPUT);
pinMode(pin_B, OUTPUT);
pinMode(pin_C, OUTPUT);
pinMode(pin_D, OUTPUT);
pinMode(pin_E, OUTPUT);
pinMode(pin_F, OUTPUT);
pinMode(pin_G, OUTPUT);
}
void loop() {
for(short int i = 0; i < 8; i++){
segments.one_byte = 1 << i;
digitalWrite(pin_A, segments.bit_fild.b0);
digitalWrite(pin_B, segments.bit_fild.b1);
digitalWrite(pin_C, segments.bit_fild.b2);
digitalWrite(pin_D, segments.bit_fild.b3);
digitalWrite(pin_E, segments.bit_fild.b4);
digitalWrite(pin_F, segments.bit_fild.b5);
digitalWrite(pin_G, segments.bit_fild.b6);
delay(1000);
}
}
実行結果
実行結果の動画を以下に示します。
実行結果から、7セグメントのLEDが順番に点灯していることが確認できます。
まとめ
今回はC言語の機能であるビットフィールドを用いることで、2進数の任意の桁にアクセスすることができました。
ビットフィールドは組み込み分野との相性が良いので、これからも応用方法を探っていけたらと思っています。
現段階では、PICマイコンをビットフィールドを使うことで、簡単に制御できるのでは?と考えています。
この記事が気に入ったらサポートをしてみませんか?