사실 별로 재미있지는 않습니다.
(이강좌는 8bit 마이크로컨트롤러에서 사용하는 것을 기준으로 char은 8bit int는 16bit 로 가정하고 진행 됩니다. )
그래도 중요하고 꼭 알고 넘어가야 하는 문제이기 때문에..
비트연산자에 대해 공부 해 보겠습니다.
비트연산자는
&
| (Shift + \ 입니다. 모음 ㅣ(이), L(엘)의 소문자, 대문자 I(아이) 가 아닙니다. )
^
~
<<
>>
이렇게 6가지가 있습니다.
& (AND) |
둘다 1이면 1 |
0 & 0 => 0 & 1 => 1 & 0 => 1 & 1 => |
0 0 0 1 |
| (OR) |
둘중 하나이상 1이면 1 |
0 | 0 => 0 | 1 => 1 | 0 => 1 | 1 => |
0 1 1 1 |
^ (XOR) |
둘이 서로 다르면 1 |
0 ^ 0 => 0 ^ 1 => 1 ^ 0 => 1 ^ 1 => |
0 1 1 0 |
~ (NOT) |
1이었으면 0, 0이었으면 1 |
~0 => ~1 => |
1 0 |
<< |
왼쪽으로 비트 이동 |
0b00000011 << 3 |
0b00011000 |
>> |
오른쪽으로 비트 이동 |
0b01100011 >> 2 |
0b00011000 |
어려운것은 없죠?
한가지 알고 넘어가야 할 부분은 << 나 >> 로 이동할때 이동한 만큼의 비트가 버려지고 0으로 채워 집니다.
잘 모르시겠다구요? 그럼 한번 예를 들어 보겠습니다.
unsigned char num=0xFF;
num= num<<4;
num은 얼마가 될까요??
바로 0xF0;
2진수로 풀어 보겠습니다.
0xFF==0b11111111 입니다.
0b 1111 1111 을 왼쪽으로 4번 비트 이동 하겠습니다.
0b 1111 1111 0000 입니다. 비게 되는 오른쪽은 0으로 채워집니다. 그리고 빨간 1111은 버려지게 됩니다.
왜 버려지냐구요? 바로 변수 선언을 8bit 로 했기 때문이죠.. 빨간 1111을 저장할 공간이 없기 때문에 버려집니다.
그래서 남은건 0b 1111 0000 == 0xF0
만약 num 가 int 형 이고 초기값이 0x00FF 였다면 어떻게 되었을까요??
그렇죠.. 0x0FF0 이 됩니다.
쉽죠??
그럼 각 연산자들이 어떤 상황일때 자주 쓰이는지 알아 보겠습니다.
먼저
& 연산자
& 연산자는 둘다 1이어야 1이 되기 때문에
1)특정 비트만 0으로 만들고 싶을때
2)특정 비트만 확인 하고 싶을때 (masking)
로 많이 사용됩니다.
예를 들어 보겠습니다.
1)특정 비트만 0으로 만들고 싶을때
PORTA 에 LED8개가 붙어 있고 모두 켜져 있습니다.(PORTA==0xFF)
그런데 다른 비트는 건드리지 않고 0번 1번 7번 비트의 LED 만 끄고 싶다면???
PORTA=PORTA & 0x7C;
라고 하면 됩니다.
쓰고 보니 PORTA=0x7C; 라고 하면 되지 않느냐? 라고 물으실 분들을 위해 다른 가정을 하나 더 넣겠습니다.
현재 LED 가 어떻게 켜져 있는지 모르는데 0번 1번 7번 비트의 LED 만 끄고 싶다면??? 그렇다면..
PORTA=PORTA & 0x7C; 이런식으로 & 연산자를 쓰는 것이 다른 비트는 건드리지 않게 됩니다.
2)특정 비트만 확인 하고 싶을때 (masking)
특정 비트만 확인하는 부분은 조건문에서 많이 들어 가는데..
예를 들어 PORTA 에 스위치가 8개 붙어 있는데
PORTA 3번 비트에 달려 있는 스위치가 눌렸는지 판단하고 싶다면?? (스위치는 안눌렀을때 1 눌리면 0으로 가정합니다. )
if((PINA & 0x08) == 0){ ... }
else
이런식으로 사용하게 되면 스위치가 눌렸다면 { ... } 의 동작을 수행하고 눌리지 않았다면 else 문을 수행 하겠죠??
| 연산자
| 연산자는 둘중 하나라도 1이면 1이 되기 때문에
1)특정 비트만 1으로 만들고 싶을때
로 많이 사용됩니다.
예를 들어 보겠습니다.
1)특정 비트만 1으로 만들고 싶을때
AVR의 ADC관련 레지스터 중에 ADCSRA 라는 레지스터가 있습니다.
그런데 이 레지스터의 6번 비트를 1로 set 하면 ADC를 시작하라는 명령을 내리는 비트입니다. 다른 비트들은 ADC 관련 설정 값이죠..
만약.. ADC를 시작하고 싶은데 ADC 의 다른 설정은 건드리지 말아야 할때.. 바로 | 를 쓰면 됩니다.
ADCSRA |= 0x40; 이렇게 사용하면 해당 비트만 1로 만들수 있습니다.
포트 출력할때 특정포트만 1로 만들고 싶다면 동일한 방법으로 사용하면 됩니다.
^ 연산자 (XOR 발음 익스클루시브오알 ㅡㅡ;)
두개가 다르면 1이 되기 때문에
1)특정 비트를 토글하고 싶을때
많이 사용됩니다.
예를 들어 보겠습니다.
PORTA 에 연결된 LED중 0번 비트에 연결된 LED만 켰다 껏다를 반복하고 싶습니다.
그럼 이렇게 코드는 어떻게 작성할까요??
while(1)
{
PORTA = 0x01;
delay_ms(1000);
PORTA = 0x00;
delay_ms(1000);
}
이렇게 작성해도 되지만 ^ 를 사용하면
while(1)
{
PORTA ^= 0x01;
delay_ms(1000);
}
코드가 간단해 집니다.
PORTA 의 값이 뭐든 다른 비트는 변화 없이 0번 비트만 토글됩니다.
<<, >> 연산자
& 연산자는 둘다 1이어야 1이 되기 때문에
1)두개의 8bit 정수를 합쳐서 int 형 변수에 넣어 줄때
2)나누기나 곱셈대신 사용할때
예를 들어 보겠습니다.
AVR 에는 10bit ADC 가 있습니다.
ADC 를 하게 되면 10bit 를 8bit씩 나눠 ADCH, ADCL 에 나눠 담기게 됩니다.
10bit 니까 8 , 2나 2 , 8로 나뉘게 됩니다. (나뉘는 방법은 ADMUX 레지스터 안에 있는 ADLAR 비트에 의해 설정됩니다.)
ADLAR==0 일때 shift 연산자를 이용해서 16비트 변수에 담아 보겠습니다.
원칙적으로 A/D 변환결과를 읽을 때는 ADCL 부터 읽어야 합니다.
unsigned int temp=0x0000;
temp = ADCL | (ADCH<<8); //이렇게 써도 됨 temp = ADCL + (ADCH*256);
ADLAR==1 일때도 한번 해보겠습니다.
unsigned int temp=0x0000;
temp = (ADCL >>6 ) | (ADCH<<2) ;
벌써 비트 연산자가 막 들어가기 시작하네요...
곱셈이나 나눗셈을 대신 할때는 몇가지 조건이 있습니다.
먼저 정수형만 되고 2의 지수승만 됩니다. 2,4,8,16,32,64,128,256,512,1024 .....
예를 들어보겠습니다.
0x0F 를 <<2 한것과 *4 것을 비교해 보겠습니다.
0x0F 는 10진수로 15입니다. *4하면 60 이죠
0x0F를 <<2 하면 0x3C 입니다. 십진수로 계산해 보면 60이죠
아래 표를 보면 << 와 *과의 관계가 이해가 되실 겁니다.
<<1 |
*2 |
<<2 |
*4 |
<<3 |
*8 |
<<4 |
*16 |
>> 와 / 도 마찬가지입니다.
>>1 |
/2 |
>>2 |
/4 |
>>3 |
/8 |
>>4 |
/16 |
* 나 / 대신 <<, >> 를 쓰는 이유는 연산하는 속도가 빠르기 때문입니다.
2,4,8,16,32,64,128,256,512,1024 ... 으로 나누거나 곱할때는 << 나 >> 를 쓰는 연습을 해 봅시다.
이것으로 비트연산 강좌를 마칩니다. ^^ 모두 열공~