비트 연산: 켜고, 끄고, 뒤집기
AND로 지우고, OR로 켜고, XOR로 뒤집고, 시프트로 자리를 옮깁니다. 레지스터 제어의 기본 문법입니다.
레지스터 하나에는 기능이 다른 비트가 여러 개 들어 있습니다. 원하는 비트만 바꾸고 나머지는 그대로 두는 것이 비트 연산의 목적입니다.
네 가지 패턴
reg |= (1u << n); // n번 비트 켜기
reg &= ~(1u << n); // n번 비트 끄기
reg ^= (1u << n); // n번 비트 뒤집기
if (reg & (1u << n)) // n번 비트 확인
여러 비트짜리 필드 바꾸기
모드처럼 2비트 이상인 필드는 먼저 지우고 새 값을 넣습니다.
reg = (reg & ~(0x3u << 4)) | (mode << 4);
시프트 주의점
1 << 31은 부호 있는 int라 정의되지 않은 동작이 될 수 있습니다.1u를 씁니다.- 부호 있는 음수를 오른쪽으로 밀면 컴파일러에 따라 부호 비트가 채워집니다.
읽고-바꾸고-쓰기
|=는 읽고, 바꾸고, 다시 쓰는 세 단계입니다. 중간에 인터럽트가 같은 레지스터를 바꾸면 그 변경이 사라집니다. STM32의 BSRR처럼 쓰기만으로 비트를 바꾸는 레지스터가 따로 있는 이유입니다.
네 가지 기본 동작
레지스터의 다른 비트는 건드리지 않고 원하는 비트만 바꾸는 것이 목표입니다.
| 하려는 일 | 코드 | 원리 |
|---|---|---|
| 켜기 | reg |= (1u << n); |
OR는 1과 만나면 1 |
| 끄기 | reg &= ~(1u << n); |
AND는 0과 만나면 0 |
| 뒤집기 | reg ^= (1u << n); |
XOR는 1과 만나면 반대 |
| 읽기 | (reg >> n) & 1u |
원하는 비트를 맨 아래로 내려서 확인 |
여러 비트로 된 필드 바꾸기
GPIO 모드처럼 두 비트 이상이 한 묶음인 필드는 "지우고 나서 쓰기" 순서로 다룹니다. 지우지 않고 OR만 하면 예전 값과 섞입니다.
#define MODE_POS 10u
#define MODE_MASK (0x3u << MODE_POS)
uint32_t tmp = GPIOA->MODER;
tmp &= ~MODE_MASK; // 1) 해당 필드를 00으로
tmp |= (0x1u << MODE_POS); // 2) 새 값 01(출력)을 써 넣기
GPIOA->MODER = tmp; // 3) 한 번에 반영
임시 변수에서 계산을 끝내고 한 번에 쓰는 이유는, 중간 상태(00)가 실제 핀에 잠깐이라도 나가는 것을 막기 위해서입니다.
그림으로 보기
현업에서 자주 터지는 문제
1 << 31:1은 부호 있는int라서 31칸 밀면 정의되지 않은 동작입니다. 항상1u를 씁니다.- 읽고-바꾸고-쓰기 사이의 인터럽트: 메인 루프가
reg |= A를 하는 도중 인터럽트가 같은 레지스터의 다른 비트를 바꾸면 그 변경이 사라집니다. 그래서 많은 MCU가 쓰기 전용인 SET, CLEAR 레지스터(BSRR 등)를 따로 둡니다. - 쓰면 지워지는 플래그: 상태 레지스터 중에는 1을 써야 지워지는 비트가 있습니다.
|=로 쓰면 다른 플래그까지 함께 지워질 수 있으니 데이터시트의 rc_w1 같은 표기를 확인합니다. - 연산자 우선순위:
reg & MASK == 0은reg & (MASK == 0)으로 계산됩니다. 괄호를 씁니다.
스스로 확인
- 8비트 값
0xA5에서 상위 4비트만 뽑아 내려면 어떤 식을 쓰면 될까요? x & (x - 1)이 0이면 x는 어떤 수일까요?
영상으로 이어 보기공부법과 진로 시리즈 15편2025 임베디드 프로그래밍 공부 순서 | 공부 방법 | 로드맵PROJECT COURSEC 문법을 실제 펌웨어 코드로 써 보고 싶다면밸런싱 로봇 하나를 요구사항, 회로와 PCB, 펌웨어, 앱까지 실무 순서대로 만드는 강의입니다. 10파트 165강.강의 살펴보기 →FREE PDF무료 전자책 3권 받기이 주제를 한 권으로 이어서 읽고 싶다면. BLDC 모터 제어, 직렬통신, 센서를 책 분량으로 묶었습니다. 이메일만 남기면 바로 받습니다.무료로 받기 →