구조체, 공용체, 비트필드
레지스터 묶음과 통신 패킷을 구조체로 표현하고, 메모리 정렬과 패딩이 왜 생기는지 확인합니다.
구조체
관련된 값을 하나로 묶습니다. MCU 헤더 파일은 주변장치의 레지스터를 주소 순서대로 구조체 멤버로 선언하고, 그 구조체 포인터를 주변장치 시작 주소에 맞춥니다. TIM2->CNT가 바로 이 방식입니다.
정렬과 패딩
32비트 CPU는 4바이트 값을 4의 배수 주소에 두려 합니다. 그래서 컴파일러가 멤버 사이에 빈 바이트를 넣습니다.
struct { uint8_t a; uint32_t b; }; // sizeof는 5가 아니라 8
통신 패킷을 구조체로 그대로 보내면 이 빈 바이트까지 나가서 상대가 엉뚱하게 해석합니다. 패킷은 바이트 단위로 직렬화하거나 packed 속성을 쓰되 정렬되지 않은 접근의 비용을 알고 씁니다.
공용체
같은 메모리를 여러 자료형으로 봅니다. float를 4바이트 배열로 쪼개 보낼 때 자주 씁니다.
비트필드
uint32_t en : 1;처럼 비트 단위로 멤버를 나눕니다. 읽기는 편하지만 비트 순서가 컴파일러마다 달라서 레지스터나 통신 포맷을 정의할 때는 마스크와 시프트를 쓰는 편이 안전합니다.