Devin.KR

기록과 LED 연결

140분 안팎

학습 목표

한 번의 눌림으로 기록 모드를 바꾸고 LED에 활성 상태를 표시합니다.

개념

버튼의 상태와 기록기의 상태를 연결합니다

앞 모듈 solution을 출발점으로 삼아 센서 기록 시작·정지를 추가합니다. hal.h, Buffer와 기존 recorder_capture를 유지하고 새 controller.c를 붙입니다. 이전 recorder_capture는 버튼 레벨을 LED로 복사한 뒤 샘플을 저장하므로 이번 모드 경로에서 호출하지 않습니다. 옛 실행 파일과 검사는 회귀용으로 남기고 새 Controller가 디바운서 이벤트, 기록 모드, 샘플 추가를 연결합니다. 기존 코드를 지워서 새 시험만 통과시키지 않습니다.

Controller는 Button 객체와 recording 정수를 소유합니다. recording은 0 또는 1이며 초기값은 정지 0입니다. 버튼이 안정 눌림일 때 recording을 그대로 유지하며 BUTTON_PRESS 순간에만 반전합니다. 버튼을 놓는다고 기록이 중단되지 않습니다. 눌림 에지를 사용자 요청으로 해석하고 모드를 지속 상태로 저장하면 사용자가 버튼에서 손을 떼고도 기록을 계속할 수 있습니다.

한 호출의 처리 순서를 정합니다

controller_init은 LED 소등을 요청하고 성공하면 현재 HAL 시각으로 Button을 초기화합니다. 실패한 초기화 객체를 그대로 사용하지 않고 오류를 처리한 뒤 다시 초기화합니다. HAL의 방향 구성은 앞선 모형에서 고정되어 있고 여기서는 방향 레지스터를 새로 만들지 않습니다. 실제 보드 전용 초기화가 생기면 핀 mux·기본 출력 상태·활성 레벨 계약도 함께 검토해야 합니다.

controller_step은 버튼 읽기, 현재 시각 조회, 다음 상태 계산, LED 쓰기, 상태 확정, 선택적 샘플 저장 순서로 동작합니다. 로컬 복사본 next에서 button_update를 수행하고 PRESS면 next.recording을 반전합니다. LED가 성공한 뒤에만 next를 원본에 대입합니다. 출력 실패 전에 원본 모드를 반전하면 사용자는 불이 꺼져 있는데 기록 중이라고 오해할 수 있고 같은 눌림을 재시도할 근거도 사라집니다.

LED는 원시 버튼이 아니라 next.recording을 표시합니다. 해제 확정 뒤에도 recording=1이면 출력은 A2입니다. 기존 0xA0의 다른 비트 보존은 hal_gpio_write가 담당하므로 controller.c에서 출력 레지스터를 직접 변경하지 않습니다. 모드 토글은 정책 계층의 일이고 핀 bit1 계산은 HAL의 일입니다. hal_sim.h는 테스트와 demo만 포함하며 컨트롤러는 hal.h만 사용합니다.

같은 호출에서 정지가 확정되면 샘플을 추가하지 않습니다. 시작이 확정된 호출에서는 raw가 유효하고 여유 공간이 있으면 첫 샘플을 저장합니다. fixture는 28ms에 시작, 170ms에 정지하므로 28ms raw는 들어가고 170ms raw는 빠집니다. 경계 호출의 순서를 문서에 쓰지 않으면 사용자 기대와 시험 기대가 갈릴 수 있습니다. 이 기준은 센서의 실제 측정 시점 보장이 아니라 이번 API의 호출 규칙입니다.

오류와 수집 생략을 다르게 읽습니다

반환값 -1은 HAL 또는 시각 오류이고 상태를 확정하지 않으며 샘플도 추가하지 않습니다. 0은 이번 호출에서 샘플을 넣지 않았다는 뜻입니다. 정지 중, raw 범위 위반, 버퍼 가득 참에서 0이 가능합니다. 1은 저장 성공입니다. 버튼 이벤트와 저장 성공은 다른 정보이므로 저장 0을 곧 버튼 읽기 실패라고 출력하지 않습니다. 모드 변화는 센서 값이 잘못되었어도 정상적으로 수행될 수 있습니다.

raw는 0~4095가 유효하며 0도 정상 값입니다. recording이 1이고 공간이 있으면 raw 0을 timestamp_ms와 함께 저장합니다. raw -1이나 4096에서는 기존 Buffer 길이를 유지합니다. 센서 입력이 잘못되었다고 버튼 폴링까지 건너뛰면 사용자가 정지 요청을 할 수 없습니다. 컨트롤러는 버튼·LED를 먼저 처리하고 샘플 검사만 뒤에서 수행합니다. 범위 오류와 제어 입력의 책임을 분리한 순서입니다.

용량 16이 가득 차도 버튼 처리와 LED 표시를 계속합니다. 추가 raw 저장은 0을 반환하지만 다음 안정 눌림은 recording을 0으로 바꾸어 소등할 수 있습니다. 앞 모듈 recorder_capture는 가득 참을 먼저 검사했지만 새 제어 경로에 그 순서를 그대로 옮기면 정지 요청이 막힙니다. 기존 함수 계약을 유지하면서 새로운 함수의 정책을 명시적으로 바꾼 사례입니다. 과거 샘플은 정지·재시작 때 지우지 않습니다.

읽기나 쓰기 실패 뒤 같은 시각에 재시도할 수 있습니다. 복사본만 변경했으므로 실패한 PRESS가 원본에서 소비되지 않고 성공 호출에서 한 번 반영됩니다. 모형의 실패 HAL은 출력도 보존합니다. 실제 장치가 부분 출력 뒤 실패했다면 메모리 상태 보존만으로 물리 부작용을 되돌릴 수는 없습니다. 이 시험은 소프트웨어 상태와 새 기록의 보존을 보이며 장치 전체 트랜잭션을 입증하지 않습니다.

fixture를 누적 프로젝트의 근거로 남깁니다

fixtures/bounce.csv에는 time_ms, level, raw 열과 11개 관측 행을 둡니다. 0·3·8ms 튐, 27ms 경계, 28ms 시작, 100ms 긴 누름, 101·121ms 해제, 150·170ms 재눌림과 200ms 유지가 들어 있습니다. gpio_demo.c는 동일한 관측을 배열로 실행합니다. fixture_check.py는 CSV 값이 배열 시나리오와 일치하는지 확인하고 실행 출력 전체를 독립 기대값과 비교합니다.

gpio-demo의 count는 저장한 샘플 수이고 stored는 현재 호출의 반환값입니다. 28·100·101·121·150ms 다섯 호출에서 저장해 최종 count=5입니다. 해제 중에도 모드가 켜져 있으므로 101·121ms에 샘플이 들어갑니다. 이는 일정한 100ms 주기 수집이 아닙니다. 호출당 샘플 경로를 먼저 완성하고 다음 모듈에서 타이머 이벤트와 수집 주기를 도입합니다.

실제 확인에서는 단위 button 검사와 controller 검사를 분리합니다. button 28개 조건이 통과한 뒤 controller 36개 조건과 fault 8개 조건을 실행합니다. 컨트롤러는 시작·정지·재시작·raw 경계·용량 초과·NULL을 검사하고 실패 HAL은 read/write 실패와 재시도를 넣습니다. 정상 demo만 보고 완료했다면 오류 시 모드가 바뀌지 않는다는 근거가 빠집니다. 요약 숫자는 논리 검사 수이며 보드 인증 수치가 아닙니다.

실패한 검사로 수정 위치를 고릅니다

starter에는 앞 모듈의 완성 HAL과 이번 디바운서 완성본이 들어 있습니다. controller.c의 TODO는 PRESS를 받아도 모드를 그대로 두어 press toggles and stores 검사가 실패합니다. BUTTON_PRESS 비교는 유지하면서 모드 반전을 구현합니다. RELEASE까지 함께 반전하면 release not toggle이 실패하고 raw 저장을 모드 검사 앞에 두면 idle excludes sample이 실패합니다. 실패 이름과 정책 문장을 연결해 수정합니다.

FAIL full still polls press가 나오면 용량 초과 반환을 버튼 처리보다 앞에 두었는지 살펴봅니다. FAIL write no commit이면 next 복사본 대신 원본을 먼저 변경했는지 봅니다. fixture 출력이 A0 대신 00이면 controller가 아니라 HAL 출력 보존 구현을 확인합니다. 기존 HAL 테스트가 같이 실패한다면 새 정책 오류와 별개로 이전 경계가 깨진 것입니다. 테스트 파일에서 기대값을 낮춰 증상을 감추지 않습니다.

make test의 Error 숫자만 보고 환경 문제라고 판단하지 않습니다. 컴파일 진단인지 개별 FAIL인지 Python AssertionError인지 위쪽 출력을 읽습니다. fixture 대조 실패는 출력 문자열과 CSV의 해당 행을 비교하고, 링크 오류는 controller.c·button.c·HAL 구현이 해당 타깃에 한 번씩 들어가는지 확인합니다. 동일 이름의 HAL 두 개를 한 실행 파일에 연결하지 않습니다. fault 타깃은 자체 실패 HAL만 연결합니다.

다음 모듈에 넘길 계약을 문서화합니다

README에는 핀·활성 레벨, 20ms 후보 정책, 초기 정지, 부팅 눌림 처리, 저장 반환값, 가득 찬 상태에서도 정지 가능한 이유를 적습니다. 실행한 make test와 gpio-demo 결과를 붙이고 호출당 수집과 주기 수집의 차이를 설명합니다. 기존 DEBUG.md에 남은 외부 디버거 관찰 기록은 이번 논리 시험 통과로 완료 처리하지 않습니다. 이번 채점 명령은 make test이며 기존 check.sh의 추가 관찰 범위를 문서에서 구분합니다.

미션의 완료 기준은 앞 모듈 회귀 통과, 단위 경계 통과, fixture 전체 대조, 실패 뒤 재시도, 과거 기록 보존입니다. LED 논리 요청은 증명하지만 전류와 전압 및 실제 버튼 파형은 미측정입니다. 실물로 옮길 때에는 해당 부품 문서와 파형 관측으로 입력 임계·저항·디바운스 기준을 재검토합니다. 다음 작성자가 이 solution에서 타이머를 붙일 수 있도록 controller_step의 순서와 객체 소유권을 남깁니다.

따라하기

누적 회귀와 새 실패 확인

미션 starter에서 실행합니다. 기존 HAL 회귀는 통과하고 새 컨트롤러 정책 검사가 실패하는지 확인합니다.

make test

모드 토글 구현

controller.c에서 확정 PRESS만 recording을 반전하도록 구현합니다. LED 성공 뒤 복사본 확정 순서를 유지합니다.

make -s gpio-demo

시작과 정지 경계 관찰

solution에서 실행한 결과입니다. 121ms 해제 뒤에도 mode=1이고 170ms에는 현재 샘플이 빠집니다.

./gpio-demo

실행 결과

t=0 level=1 mode=0 led=A0 stored=0 count=0
t=3 level=0 mode=0 led=A0 stored=0 count=0
t=8 level=1 mode=0 led=A0 stored=0 count=0
t=27 level=1 mode=0 led=A0 stored=0 count=0
t=28 level=1 mode=1 led=A2 stored=1 count=1
t=100 level=1 mode=1 led=A2 stored=1 count=2
t=101 level=0 mode=1 led=A2 stored=1 count=3
t=121 level=0 mode=1 led=A2 stored=1 count=4
t=150 level=1 mode=1 led=A2 stored=1 count=5
t=170 level=1 mode=0 led=A0 stored=0 count=5
t=200 level=1 mode=0 led=A0 stored=0 count=5

단위·실패·fixture 함께 확인

solution에서 실제로 실행한 단위·실패·fixture 검사 출력입니다. 최종 제출에서는 make test로 기존 회귀까지 실행합니다.

make -s button-test fixture-test

실행 결과

button: 28 checks, 0 failures
controller: 36 checks, 0 failures
controller fault: 8 checks, 0 failures
fixture: 11 rows passed

미션 근거 정리

README에 모드/샘플 반환값을 설명하고 fixtures/bounce.csv를 28ms 시작·170ms 정지·최종 5개 기록 결과와 대조합니다.

make test

확인 문제

실습

직전 m03 미션 solution 전체와 완성 디바운서를 포함한 누적 미션입니다. controller.c의 TODO에서 PRESS만 기록 모드를 토글합니다. LED는 모드를 표시하고 가득 참·raw 오류에도 버튼 폴링을 계속합니다. 기존 회귀를 보존하여 make test를 통과하고 CSV fixture와 gpio-demo의 시작·정지·5개 저장을 README에 설명합니다.

시작 코드·테스트 내려받기

실행 명령

make test

기대 결과

기존 회귀 통과, button 28·controller 36·controller fault 8개 검사 0 failures, fixture 11 rows passed

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 버튼을 한 번 눌렀는데 여러 번 감지되는 이유를 설명해 주시면 됩니다.