C言語

ラズパイでビットフィールドを練習しよう!

スポンサーリンク

こんにちは!駆け出しエンジニアのまっきーです。

前回、ビットフィールドについて解説を行いました。そこで今回は実際にビットフィールドを利用して、LEDとスイッチの制御を行います。

前回の記事はこちら

目標

タクタルスイッチを押すとLEDが光るプログラムを作成する。(ビットフィールドを利用)

使用するもの

  • RaspberryPi3B+
  • 赤色LED
  • タクタルスイッチ
  • 抵抗(今回は1kΩを使用します)

https://amzn.to/2Fxqzv8

スイッチなどの部品はこちらに付属していたものです。

*スイッチが5つしか入っていませんので注意してください。

回路図

上の表のように、スイッチとLEDを対応付けます。

ビットフィールド復習

前回、共用体とビットフィールドを使うことで、ビットまたはバイト単位でデータを操作できるということを学びました。詳しくはこちらの記事を参照してください

共用体とビットフィールドの定義はこのように簡潔に行うことができます。

union {
 unsigned char BYTE;
 struct{
 unsigned char B0:1; //SW0
 unsigned char B1:1; //SW1
 unsigned char B2:1; //SW2
 unsigned char B3:1; //SW3
 unsigned char B4:1; //SW4
 unsigned char B5:1; //SW5
 unsigned char B6:1; //SW6
 unsigned char B7:1; //SW7
 }BIT;
}SW; 

このビットフィールドのビット、バイトのアクセス方法はこのようにできます。

・ ビット: SW.BIT.B0 SW0 のビット参照。
・ バイト: SW.BYTE SW0 から SW7 のバイト参照。

ソースコード1

#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h> 

const int led[4] = {23,24,20,21};//LED0,LED1,LED2,LED3
const int sw[8] = {25,17,27,5,6,13,19,26};//SW0~SW7

/* LED ビットフィールド */
union {
 unsigned char BYTE;
 struct {
 unsigned char B0:1; //LED0
 unsigned char B1:1; //LED1
 unsigned char B2:1; //LED2
 unsigned char B3:1; //LED3
 unsigned char :4; //?? 4bit
 }BIT;
}LED; 

/* SW ビットフィールド*/ 
union {
 unsigned char BYTE;
 struct{
 unsigned char B0:1; //SW0
 unsigned char B1:1; //SW1
 unsigned char B2:1; //SW2
 unsigned char B3:1; //SW3
 unsigned char B4:1; //SW4
 unsigned char B5:1; //SW5
 unsigned char B6:1; //SW6
 unsigned char B7:1; //SW7
 }BIT;
}SW;

void Led4bit(int ledData);
void Sw8bit(void);

int main(void){
	
	int i;
	//wiringPi初期設定
	if(wiringPiSetupGpio() == -1) return 1;

	//led,sw初期設定
	for(i = 0;i < 4;i++){
		pinMode(led[i],OUTPUT);}
	for(i = 0;i < 8;i++){
		pinMode(sw[i],INPUT);
		pullUpDnControl(sw[i],PUD_DOWN);}

  while(1){
  Sw8bit(); //SW入力
  LED.BIT.B0 = SW.BIT.B0 | SW.BIT.B4;
  LED.BIT.B1 = SW.BIT.B1 | SW.BIT.B5;
  LED.BIT.B2 = SW.BIT.B2 | SW.BIT.B6;
  LED.BIT.B3 = SW.BIT.B3 | SW.BIT.B7;
  Led4bit(LED.BYTE); //LED 出力
 
 }
 return 0;
}
void Led4bit(int ledData)
{
 int i; 
 for (i=0; i<4; i++){ 
 digitalWrite(led[i], ledData&1); 
 ledData = ledData >> 1; 
 }
}
void Sw8bit(void){
 int i,k;
 SW.BYTE=0xff;
 for(i=7; i>-1; i--){
 k=digitalRead(sw[i]);
 SW.BYTE = SW.BYTE << 1;
 SW.BYTE = SW.BYTE + k;
 
 }
 printf("%d%d%d%d%d\n",SW.BIT.B4,SW.BIT.B3,SW.BIT.B2,SW.BIT.B1,SW.BIT.B0);
} 


著者のソースコードを参考にして記述しました。しかし、うまくいきませんでした。

そこで、79行目のようにデバッグをしてみると、

00000
00000
00000
..
..

スイッチをいくら押しても反応することはありませんでした。

ビットフィールドはコンパイラによってメモリ割り付けの順序が変わります。これが原因かもと思ったのですが、どのビットも1がたたなかったので違います。

そこで、プルアップ抵抗(PUD_UP)にしてみたところ

11111
11110
11101
10111
..
..

スイッチを押すことによって反応することが確かめられました。

プルアップ抵抗(負論理)のバージョンにして書き直してみます。

ソースコード2

#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h> 

const int led[4] = {23,24,20,21};//LED0,LED1,LED2,LED3
const int sw[8] = {25,17,27,5,6,13,19,26};//SW0~SW7

/* LED ビットフィールド */
union {
 unsigned char BYTE;
 struct {
 unsigned char B0:1; //LED0
 unsigned char B1:1; //LED1
 unsigned char B2:1; //LED2
 unsigned char B3:1; //LED3
 unsigned char :4; // 4bit
 }BIT;
}LED; 

/* SW ビットフィールド*/ 
union {
 unsigned char BYTE;
 struct{
 unsigned char B0:1; //SW0
 unsigned char B1:1; //SW1
 unsigned char B2:1; //SW2
 unsigned char B3:1; //SW3
 unsigned char B4:1; //SW4
 unsigned char B5:1; //SW5
 unsigned char B6:1; //SW6
 unsigned char B7:1; //SW7
 }BIT;
}SW;

void Led4bit(int ledData);
void Sw8bit(void);

int main(void){
	
	int i;
	//wiringPiエラー処理
	if(wiringPiSetupGpio() == -1) return 1;

	//led,sw初期設定 swプルアップ抵抗
	for(i = 0;i < 4;i++){
		pinMode(led[i],OUTPUT);}
	for(i = 0;i < 8;i++){
		pinMode(sw[i],INPUT);
		pullUpDnControl(sw[i],PUD_UP);}

  while(1){
  Sw8bit(); //SW入力
  LED.BIT.B0 =~ (SW.BIT.B0 & SW.BIT.B4);
  LED.BIT.B1 =~ (SW.BIT.B1 & SW.BIT.B5);
  LED.BIT.B2 =~ (SW.BIT.B2 & SW.BIT.B6);
  LED.BIT.B3 =~ (SW.BIT.B3 & SW.BIT.B7);
  Led4bit(LED.BYTE); //LED出力
 
 }
 return 0;
}
void Led4bit(int ledData)
{
 int i; 
 for (i=0; i<4; i++){ 
 digitalWrite(led[i], ledData&1); 
 ledData = ledData >> 1; //1bit シフト
 }
}
void Sw8bit(void){
 int i,k;
 SW.BYTE=0xff;
 for(i=7; i>-1; i--){
 k=digitalRead(sw[i]);
 SW.BYTE = SW.BYTE << 1;
 SW.BYTE = SW.BYTE + k;
 
 }
 printf("%d%d%d%d%d\n",SW.BIT.B4,SW.BIT.B3,SW.BIT.B2,SW.BIT.B1,SW.BIT.B0);
} 


このコードで点灯させることができました。

ソースコード解説

・Sw8bit()関数

変数kに読み取ったスイッチの状態(0or1)を代入しています。

for文を使って、最上位ビットのsw[7]からSW.BYTEに格納しています。

イメージとしては下のような感じです。

00000000<< +k
00000001<< +k
00000010<< +k
...

・無限ループの中身

Sw8bit()関数を呼び出し、SWビットの状態をLEDのビットに代入しています。

負論理にしたのですこし計算がややこしくなっています。(LEDは1で点灯、0で消灯のため)

論理演算ですが、実際に適当な値を当てはめてみると考えやすいです!

まとめ

ビットフィールドを用いてLEDの点灯消灯を行いました。

確実にこの時のようにシンプルに実装した方がわかりやすいですね。ビットフィールドについては、ここまで実装できればもう問題なく使えると思います。

先ほども少し書きましたが、ビットフィールドのメモリ割り付けはコンパイラに依存しますので移植性があまり高くありません。このような理由からあまり使われていないようです。

今回は以上です。ありがとうございました!

参考サイトはこちら 参考書籍「C言語ではじめる」 RaspberryPi  徹底入門」

スポンサーリンク

-C言語