기초
5단원 · Keil·자료형/진수·비트연산·포인터/MMIO·함수
01. 임베디드 C와 Keil μVision 시작하기
기초 · 선수: 없음
PC에서 도는 C와 MCU에서 도는 C는 같은 언어지만 사고방식이 다릅니다. OS도 화면도 없는 칩 위에서 코드가 직접 하드웨어를 다뤄야 하죠. 임베디드 C가 무엇인지, 24편 내내 쓸 도구인 Keil μVision(C51/MDK-ARM)이 코드를 어떻게 빌드·디버그·시뮬레이션하는지, 그리고 임베디드 프로그램의 표준 골격(main + while(1))을 익힙니다. 보드가 없어도 PC의 gcc로 따라올 수 있습니다.
02. 자료형·진수·stdint·sizeof
기초 · 선수: 01강
MCU의 레지스터는 폭이 정확히 정해져 있고(8/16/32비트) RAM은 수 KB뿐입니다. "이 변수가 정확히 몇 비트인가"가 곧 레지스터에 제대로 들어가는지를 결정합니다. stdint.h 의 폭 고정 타입이 왜 임베디드의 사실상 표준인지, 10진·16진·2진 표기, sizeof, 그리고 부호 없는 값이 최댓값을 넘을 때의 랩어라운드까지 PC의 gcc 로 직접 확인합니다.
03. 비트 연산과 레지스터 조작 (set·clear·toggle)
기초 · 선수: 02강
하드웨어를 다룬다는 건 결국 레지스터의 특정 비트를 켜고·끄고·읽는 일입니다. 이때 나머지 비트는 절대 건드리면 안 됩니다(옆 비트가 다른 핀을 제어할 수 있으니까요). 비트 연산자(& | ^ ~ << >>)와 세 가지 황금 관용구 set(|=)·clear(&= ~)·toggle(^=), 그리고 test(>> &)를 PC의 gcc 로 익힙니다. 여기서 익힌 관용구는 6편 이후 8051·STM32 레지스터 제어에 그대로 쓰입니다.
04. 포인터·volatile·메모리 맵드 I/O
기초 · 선수: 03강
임베디드에서 레지스터는 곧 메모리의 특정 주소입니다(예: STM32 GPIOC->ODR 은 0x4001100C). 그 주소에 쓰면 핀이 움직이고, 읽으면 핀 상태가 들어옵니다 — 이것이 메모리 맵드 I/O(MMIO)입니다. 주소를 다루는 포인터와, 하드웨어가 멋대로 바꾸는 값을 안전하게 읽는 volatile 을 PC의 gcc 로 흉내 내며 익힙니다. (*(volatile uint32_t *)주소) 패턴은 14편 이후 STM32 제어의 토대입니다.
05. 함수·헤더 분리·#define 매크로
기초 · 선수: 04강
펌웨어는 "GPIO 다루는 부분", "UART 다루는 부분", "응용 로직" 식으로 모듈로 나눕니다. 모듈을 나누는 도구인 함수(동작 단위), 헤더(.h)/소스(.c) 분리(인터페이스/구현), 전처리기(#include·#define·헤더 가드)를 익힙니다. 비트 조작 로직을 ledbank 모듈로 떼어내고, 헤더 가드와 매크로 괄호 함정도 다룹니다. 모듈로 나누는 감각은 23편(HAL 추상화)까지 이어집니다.
8051
7단원 · SFR·GPIO·타이머·인터럽트·UART·7세그먼트
06. 8051 구조·SFR·reg52.h
8051 · 선수: 05강
1~5편의 토대 위에서 드디어 실제 칩에 올라섭니다. 첫 주인공은 임베디드 교육의 고전 8051(AT89C52)입니다. 8051의 주변장치(포트·타이머·시리얼·인터럽트)는 모두 SFR(특수 기능 레지스터)로 제어합니다. Keil C51의 <reg52.h> 가 SFR을 이름으로 미리 선언해 둬서 P1 = 0x0F; 처럼 변수에 대입하듯 하드웨어를 다룹니다. 포트에 쓰는 두 방법(전체 / sbit 비트)을 익힙니다.
07. GPIO 출력 — LED 점멸
8051 · 선수: 06강
임베디드의 "Hello, World"는 LED 깜빡이기입니다. 포트에 어떤 값을 써야 LED가 켜지는지(액티브 로우/하이 배선), 얼마나 기다려야 보이는지(지연), 여러 LED로 패턴을 만드는 법(비트 마스크)까지 GPIO 출력의 핵심이 다 들어 있습니다. P1에 연결된 LED 8개로 흐르는 불빛(running light)을 만들고 Logic Analyzer로 핀 파형을 관찰합니다.
08. GPIO 입력 — 버튼/스위치 읽기
8051 · 선수: 07강
7편이 핀으로 신호를 내보냈다면 이번엔 핀으로 신호를 읽어들입니다. 8051 특유의 함정은 포트가 준쌍방향(quasi-bidirectional)이라, 입력으로 읽기 전에 그 핀에 먼저 1을 써 두어야 외부 신호를 제대로 읽는다는 점입니다. P3.2 버튼을 읽어 P1.0 LED를 제어하고, "누르는 순간마다 토글"하는 에지 검출 로직을 익힙니다. 채터링은 21편에서 다룹니다.
09. 타이머0/1 과 정확한 지연
8051 · 선수: 08강
7편의 소프트웨어 지연은 부정확하고 그동안 CPU가 묶입니다. 정확한 시간은 하드웨어 타이머로 만듭니다 — CPU와 독립적으로 클럭을 세는 카운터죠. 8051의 Timer0/1을 16비트 모드(Mode1)로 설정해 정확한 지연을 만듭니다. 핵심은 TMOD로 모드 선택, TH/TL에 리로드 값(65536-카운트), TF(오버플로 플래그) 폴링입니다. 리로드 계산을 PC로 검증합니다.
10. 외부·타이머 인터럽트
8051 · 선수: 09강
폴링은 정확하지만 기다리는 동안 CPU가 묶입니다. 인터럽트는 평소 메인 코드가 제 일을 하다가 사건(버튼·타이머 오버플로)이 생기면 CPU가 잠깐 끼어들어 ISR을 실행하고 돌아옵니다. "사건이 생길 때만 반응"하는 이 방식이 임베디드의 핵심 패러다임입니다. IE 레지스터로 인터럽트를 켜고, C51의 interrupt N 문법으로 ISR을 쓰며, 공유 변수에 volatile이 왜 필요한지 익힙니다.
11. UART 시리얼 통신
8051 · 선수: 10강
값을 "사람이 읽는 글자"로 PC와 주고받으려면 통신이 필요합니다. 가장 기본이 UART(시리얼)입니다 — 선 두 가닥(TXD/RXD)으로 텍스트를 주고받죠. UART는 비동기라 양쪽이 보율(baud)을 똑같이 약속해야 합니다. 8051은 SCON으로 모드를, Timer1로 보율을, SBUF로 한 바이트를 넣고 뺍니다. 9600bps·8N1로 문자열을 보내고, 보율 정확도를 위해 11.0592MHz를 쓰는 이유를 익힙니다.
12. 7-세그먼트 표시 (멀티플렉싱)
8051 · 선수: 11강
사람이 읽을 수 있는 숫자를 표시합니다. 7-세그먼트는 막대 LED 7개(a~g)+소수점으로 0~9를 그립니다. 핵심은 ① 숫자를 "어떤 막대를 켤지"의 비트 패턴으로 바꾸는 룩업 테이블, ② 자리가 여러 개일 때 핀을 아끼며 모두 켜 보이게 하는 멀티플렉싱(동적 구동)입니다. "1234"를 한 자리씩 빠르게 번갈아 켜 잔상으로 네 자리가 동시에 켜진 것처럼 보이게 만듭니다.
STM32
8단원 · CMSIS·GPIO·SysTick·PWM·EXTI·USART·ADC
13. Cortex-M·CMSIS·클럭 트리
STM32 · 선수: 12강
8051을 졸업하고 32비트 Arm Cortex-M(STM32F103)으로 무대를 옮깁니다. CMSIS 표준 덕분에 어느 Cortex-M이든 비슷한 방식으로 레지스터에 접근합니다. 이 트랙은 HAL/SPL 없이 레지스터를 직접 제어합니다. 앞으로 7편을 떠받칠 세 토대 — Cortex-M 구조, CMSIS 헤더(stm32f10x.h), 클럭 트리(HSE→PLL→SYSCLK→AHB/APB) — 를 잡고, "주변장치를 쓰기 전에 RCC로 클럭부터 켠다"는 철칙을 익힙니다.
14. RCC·GPIO 레지스터로 LED
STM32 · 선수: 13강
13편의 토대(CMSIS·RCC·클럭)를 처음으로 실전에 씁니다. STM32의 "Hello, World"인 LED 점멸을 HAL 없이 레지스터만으로 구현합니다. Blue Pill의 온보드 LED는 PC13에 액티브 로우로 달려 있죠. 핵심은 STM32F1 특유의 GPIO 설정 — 핀마다 4비트(MODE 2 + CNF 2)를 CRL(핀0~7)·CRH(핀8~15)에 쓰는 방식입니다. 이 4비트 조합을 이해하면 F1의 모든 GPIO 설정이 같은 패턴으로 풀립니다.
15. GPIO 입력·풀업/풀다운
STM32 · 선수: 14강
14편이 핀으로 신호를 내보냈다면 이번엔 핀으로 신호를 읽습니다. 초보자가 꼭 걸리는 함정은 플로팅 입력 — 아무것도 연결 안 된 핀은 0도 1도 아닌 떠 있는 상태라 노이즈에 흔들립니다. 이를 막는 것이 풀업/풀다운 저항이고, STM32는 내부에 내장합니다. PA0를 입력+내부 풀업으로 두고 IDR로 버튼을 읽어 PC13 LED를 제어합니다. F1은 풀업/풀다운 선택을 ODR로 한다는 특이점이 핵심입니다.
16. SysTick 타이머와 정확한 지연
STM32 · 선수: 15강
빈 루프 delay()는 시간이 부정확합니다. Cortex-M 코어에 내장된 SysTick 타이머로 해결합니다 — 모든 Cortex-M에 똑같이 들어 있는 24비트 카운터라 칩이 바뀌어도 같은 코드가 돕니다. 핵심 패턴은 "1ms마다 인터럽트 → 전역 밀리초 카운터 증가 → 그 카운터로 시간 측정"입니다. 이 한 패턴으로 정확한 지연·주기 실행·타임아웃을 모두 구현합니다. 부호 없는 뺄셈으로 래핑에 안전한 delay_ms도 익힙니다.
17. 범용 타이머·PWM 출력
STM32 · 선수: 16강
범용 타이머(TIM)는 채널·PWM·입력 캡처를 제공합니다. 가장 많이 쓰는 PWM은 켜진 시간의 비율(듀티)을 빠르게 조절해 LED 밝기나 모터 속도를 "아날로그처럼" 제어합니다. TIM2 채널1(PA0)로 1kHz PWM을 만들고 듀티를 조절합니다. 세 핵심 레지스터 PSC(분주)·ARR(주기)·CCR(듀티)의 관계와, APB1 타이머 클럭이 PCLK1의 2배가 되는 함정을 익힙니다.
18. NVIC 인터럽트와 EXTI
STM32 · 선수: 17강
버튼을 폴링으로 읽으면 CPU가 묶이고 짧은 입력을 놓칠 수 있습니다. 인터럽트는 평소 메인이 제 일을 하다가 사건이 생기면 즉시 핸들러(ISR)로 점프했다 돌아옵니다. Cortex-M에서 인터럽트를 총괄하는 NVIC와, 외부 핀 변화를 인터럽트로 바꾸는 EXTI를 다룹니다. PA0 버튼을 EXTI0 인터럽트로 받아 LED를 토글하고, ISR에서 펜딩 플래그(PR)를 반드시 클리어하는 패턴을 익힙니다.
19. USART 시리얼 통신
STM32 · 선수: 18강
이제 칩이 사람·PC와 텍스트로 대화합니다. UART(범용 비동기 직렬)는 디버그 메시지, 센서 모듈, 부트로더 등 어디에나 쓰입니다. USART1을 9600bps로 설정해 인사 문자열을 보내고, 받은 문자를 그대로 되돌리는 에코를 구현합니다. μVision Serial 창에서 송수신을 직접 볼 수 있어, UART는 시뮬레이터로 검증하기 가장 좋은 주변장치입니다.
20. ADC 아날로그 입력
STM32 · 선수: 19강
지금까지 다룬 핀은 모두 디지털(0/1)이었습니다. 세상의 신호 대부분은 온도·밝기·소리처럼 연속적인 아날로그입니다. ADC(Analog-to-Digital Converter)는 아날로그 전압을 숫자로 바꿔 MCU가 다룰 수 있게 합니다. STM32F103의 12비트 ADC로 PA0의 전압을 0~4095로 읽고 밀리볼트로 환산합니다. STM32 주변장치 시리즈(13~20편)의 마지막으로, 클럭·핀·폴링이 한 번에 쓰입니다.
응용
4단원 · 디바운싱/FSM·링버퍼·HAL·종합 프로젝트
21. 디바운싱·유한 상태 머신(FSM) 패턴
응용 · 선수: 20강
주변장치 사용법을 익혔으니 이제 펌웨어를 짜는 패턴으로 넘어갑니다. 거의 모든 프로젝트에 등장하는 두 가지 — 디바운싱과 유한 상태 머신(FSM)입니다. 기계식 버튼은 누르는 순간 접점이 수 ms 튀어(채터링) 한 번이 여러 번으로 읽힙니다. 디바운싱은 떨림을 걸러 진짜 입력만 남기고, FSM은 "상태+입력→다음 상태" 규칙으로 동작을 깔끔하게 표현합니다. 하드웨어 비의존 순수 로직이라 gcc로 정확히 검증합니다.
22. 링버퍼와 인터럽트 기반 UART 수신
응용 · 선수: 21강
UART로 데이터가 쏟아지면 수신 인터럽트가 매 바이트마다 걸립니다. ISR에서 다 처리하려 들면 ISR이 길어지고, 메인이 느리면 다음 바이트가 이전 바이트를 덮어써 유실됩니다. 해법은 ISR은 버퍼에 넣기만 하고 처리는 한가한 메인이 맡는 분업입니다. 그 핵심 자료구조가 링버퍼(원형 버퍼)입니다. 생산자(ISR)와 소비자(메인)가 서로 다른 인덱스만 건드리면 인터럽트를 끄지 않고도 안전합니다.
23. HAL 추상화·모듈 분리·이식성
응용 · 선수: 22강
레지스터 코드(GPIOC->BRR=...)가 응용 로직 곳곳에 박혀 있으면 칩을 바꿀 때 모든 곳을 고쳐야 합니다. HAL(Hardware Abstraction Layer)은 "무엇을 한다"(LED를 켠다)는 응용과 "어떻게 한다"(특정 레지스터를 쓴다)는 구현을 갈라놓습니다. 같은 app.c 를 세 HAL 구현 — PC(printf)·STM32(레지스터)·아두이노 우노(AVR ATmega328P) — 로 돌려, 아키텍처가 ARM에서 AVR로 바뀌어도 응용 코드는 한 줄도 안 바뀜을 봅니다.
24. 종합 — UART 명령으로 LED·타이머 제어
응용 · 선수: 23강
마지막 캡스톤. 따로 배운 조각들 — USART(19)·인터럽트(18)·링버퍼(22)·SysTick(16)·GPIO(14)·명령 파싱/모듈 분리(22·23) — 을 하나의 펌웨어로 엮습니다. PC 터미널에서 on/off/blink/help 명령을 타이핑하면 보드가 해석해 LED와 타이머를 제어합니다. 설계 핵심은 역할 분리 — 인터럽트는 받기만, 메인은 처리만, 파서는 해석만. 각 모듈은 독립 테스트 가능하고, 명령 파서는 PC에서 검증합니다.