ビット演算


注意!このページを読んだことによる如何なる損害に対しても筆者は責任を取りません。
参考にされる場合は全て自己責任で行ってください。



マイコンの制御プログラムを書いていると頻繁にビット演算を行うことがある。
ここでは簡単なビット演算とその用法について記述する。



■はじめに
■変数の型

■シフト演算
■アンド演算
■オア演算
■排他的論理和
■反転

■ひとつだけビットを立てる
■ひとつだけビットを下げる
■ひとつだけビットを反転する
■複数のビットを立てる
■複数のビットを下げる

■入力の判定
■ビットを逆順にする


■はじめに
まず、一般的なC言語について考える。

C言語では数字は基本的に10進数で書かれる。
通常のC言語では、16進数は先頭に「0x」を付けることで表現可能だが、
2進数を表現する方法はない。

しかし、AVRのコンパイラWinAVRでは2進数の表現がサポートされている。
2進数で表現する場合は先頭に「0b」を付ける。

例)
  30 = 0x1E = 0b00011110

数字として理解しやすいのはやはり10進数だが、2進数表記のほうが便利なこともある。
時と場合によって使い分けると良いと思う。
しかし、2進数表記は「0」と「1」だけで表現するぶん間違いが生じやすいので、
あまり多用しないほうが無難だろう。

ビット演算は2進数で考えると一番分かりやすい。
というか2進数はビットそのものを表現しているのだから当然と言えば当然。


また、プログラム上での変数は2進数だとか10進数だとかの区別はない。
これは単に表現方法が違うだけ。


char i = 0x0E;

i = i + 3;

i = i - 0b00000110;

だからこんな事をやってもなんら問題はない。


■変数の型
ビット演算をする際、その変数が何ビットの変数なのかという事をしっかりと考えて無いと
痛い目を見る時がある。(ビット演算でなくてもそうだが)
特にシフト演算なんかをやる時には桁あふれしたビットは完全に忘れ去られてしまう。

また、その変数が負の数字まで扱えるかどうかというのも大切。
特に負の数字を使う必要がない変数は全て「unsigned」修飾子を付けて負数を使わないようにする。

一般的なC言語での変数の型は以下のようになっている。

/* 8bits */
char
unsigned char

/* 16bits */
int
unsigned int

/* 32bits */
long
unsigned long

WinAVRではこれ以外に以下のような使いやすい型が用意されている。

/* 8bits */
int8_t
uint8_t

/* 16bits */
int16_t
uint16_t

/* 32bits */
int32_t
uint32_t





■シフト演算
演算子:「<<」または「>>」

各ビットをそれぞれ右または左に指定した数だけ移動させる。

0b0100 >> 2 = 0b0001
0b1101 << 3 = 0b1000

あふれたビットはその時点で忘れ去られる。
新たに入ってくるビットは全てゼロで埋められる。
(ただし、負数の場合は1で埋められるらしい。)


■アンド演算
演算子:「&」

ビットごとに論理積を行う。

0b1010 & 0b0011 =0b0010


■オア演算
演算子:「|」  (キーボードではバックスペースの左辺りにある)

ビットごとに論理和を行う。

0b0101 | 0b0011 =0b0111


■排他的論理和(反一致)
演算子:「^」

ビットごとに排他的論理和を行う。
比較して一致すれば0、一致しなければ1

0b0101 ^ 0b0011 = 0b0110


■反転
演算子:「~」

全てのビットを反転する。

~ 0b0110 = 0b1001





■ひとつだけビットを立てる

uint8_t bits = 0;
bits |= (1<<4);
上の結果、bits = 0b00010000 となる


■ひとつだけビットを下げる

uint8_t bits = 0xFF;
bits &= ~(1<<3);
上の結果、bits = 0b11110111 となる


■ひとつだけビットを反転する

uint8_t bits = 0;
bits ^= (1<<2);
上の結果、bits = 0b00000100 となる

プログラム中に任意のビットを反転させたいことは良くあるので覚えておくと便利。
LEDの点滅なども条件分岐せずに行える。


■複数のビットを立てる

uint8_t bits = 0;
bits |= (1<<3) | (1<<5);
上の結果、bits = 0b00101000 となる


■複数のビットを下げる

uint8_t bits = 0xFF;
bits &= ~( (1<<3) | (1<<5) );
上の結果、bits = 0b11010111 となる

別にこんなことしなくても

uint8_t bits = 0xFF;
bits &= 0b11010111;
これでも同じことが出来る。
でもぱっと見た感じ、何ビット目が下げられているのかが一目瞭然なのは上の例。
プログラムは書くときの楽よりも、可読性を重視して書くべき。
プログラムの改良・改変は日常茶飯事に行うから、いかに理解しやすいプログラムを書けるかが重要。


■スイッチ入力の判定
マイコン使用時、仮にポートAを全てスイッチの入力としている場合。
スイッチ入力に変化があったかどうかを判別する時に排他的論理和を使ってみる。


//約100Hzの割り込み関数の内部等

new_sw = PINA;

if( old_sw ^ new_sw ){
	old_sw = new_sw;
	
	/*スイッチ入力の処理*/
	
}



■ビットを逆順にする
ある変数のビットを逆順に並べ替えたい時がある。
以下のような関数で対処できる。


uint8_t reversalBits(uint8_t bits){
	bits=( bits         << 4) | ( bits         >> 4);
	bits=((bits & 0x33) << 2) | ((bits & 0xcc) >> 2);
	bits=((bits & 0x55) << 1) | ((bits & 0xaa) >> 1);
	return bits;
}



2010/01/30  一部修正
2009/12/06  作成

HOME /電子工作_TOP inserted by FC2 system