목차
1. 전체 그림
처리부는 NXP i.MX 95 입니다. 환자에 닿는 측정부(환자 섬)는 절연되어 있고, 디지털 절연기(ISO7762)를 거쳐 처리부와 이어집니다. 처리부 칩 구성(i.MX 95 · AFE159RP8 · AFE4490 · 2단계 RM44L920)은 확정 사양이고, 아래 코어 배치는 기본값입니다.
환자 섬(절연, CF) │ 비환자 측 (i.MX 95 SoM)
──────────────────────────────────────┼───────────────────────────────────────
AFE159RP8 (ECG 8ch, 전극 10, 페이스 칩내) ─ISO7762#1─ LPSPI-A ┐
AFE4490 (SpO2, PRF 500 Hz) ─ISO7762#2─ LPSPI-B ├─► Cortex-M7 800 MHz (FreeRTOS)
V1/V5 스위치 · sim/signal 스위치 상태 ─(절연 GPIO)────────┘ 취득·필터·QRS·페이스·SpO2
(+옵션) NIBP 모듈 UART · Resp(AFE 내장) ·경보 판정·경보음
│ 공유 DDR 링(파형) + RPMsg(명령·사건)
▼
Cortex-A55 ×6 (Linux, Yocto) 화면(Qt)·홀터·트렌드·클라우드·OTA·로그·권한
Cortex-M33 (NXP SM) 전원·클록·리셋·PMIC·고장 반응(LM 단위 리셋)
외부: TPS3430 윈도 워치독(M7 이 찬다) → WDO → 백업 부저 + A55 GPIO [2단계] RM44L920 안전 MCU (SPI)
LTC1760 + 스마트 팩 A/B (SMBus, M7 소유) · VSYS 저전압 비교기 → M7 IRQ
코어 배치 원칙 세 줄 기본값
- 환자 안전에 걸리는 경로(취득 → 판정 → 경보음)는 M7 하나로 닫는다. Linux 가 죽어도 소리는 난다.
- Linux 는 「보여주기·저장·보내기」만 한다. Linux 가 멈추면 M7 이 알아채고 고우선 「표시 고장」을 울린다.
- M7 이 멈추면 System Manager(SM)가 M7 만 리셋하고, 그동안 TPS3430 이 백업 부저를 울리며, A55 는 파형을 회색으로 바꾼다.
2. 코어 배치와 M33
설계 검토에서 바뀐 점 — M33 에는 감시·경보 백업을 올릴 수 없다. i.MX 95 의 M33 은 NXP System Manager 전용 코어다. NXP imx-sm 문서(sm/doc/arch.md) 원문:
- 「SM owns the M33 core and does not support customers adding workloads to this core.」
- 「the Cortex-M33 is not available to run customer applications. … additional customer functionality may interfere with the real-time response of the SM.」
그래서 기본값을 이렇게 잡았다. M33 은 NXP SM 그대로 두고(코드는 건드리지 않고 설정 파일만), SM 의 고장 반응(FCCU·워치독 → 해당 LM 만 리셋)을 「1차 하드웨어 감시」로 쓴다. SM 설정에 이미 M7_LOCKUP → M7(LM1) 리셋, WDOG3/WDOG4 → AP(LM2) 리셋 반응이 들어 있다. 감시·경보 백업은 1단계: M7 ↔ A55 상호 감시 + 외부 윈도 워치독 TPS3430 + 백업 부저, 2단계: RM44L920 이 넘겨받는다.
코어별 담당 기본값
| 코어 | OS | 담당 |
|---|---|---|
| M7 (800 MHz, SoM 공급사 표기) | FreeRTOS + MCUXpresso SDK + RPMsg-Lite (2안 Zephyr) | AFE159RP8·AFE4490 취득, 공통 시간축, 페이스 검출, 데시메이션, 2중 적응형 노치, HPF/LPF, QRS·HR, SpO2·PR·PI, (옵션) 호흡·NIBP, 경보 조건 판정·우선순위·경보음 합성, 리드오프·센서오프, 전원 감시(LTC1760·팩·VSYS), 외부 워치독 차기, A55 감시 |
| M33 | NXP SM (베어메탈, 자체 코드 없음) | 전원·클록·리셋·PMIC, TRDC 자원 격리, FCCU 고장 반응, 리셋 사유 기록 |
| A55 ×6 | Linux (NXP 벤더 커널) | Qt 6 화면, 홀터 기록, 트렌드 DB, 경보 표시·조작·로그, 설정·권한, 클라우드 동기화, OTA·USB 갱신, 서비스 모드, 보안 |
| RM44L920 (2단계) | TI HALCoGen + 자체 (미정) | 경보 판정 2중 대조, 백업 경보음, 외부 워치독 관리, 페일세이프 결정 |
자원 소유 (SM 설정으로 고정)
| 자원 | 소유 | 비고 |
|---|---|---|
| LPSPI ×2 (ECG·PPG 전용 버스) | M7 | AFE4490 은 비선택 때 3상태가 안 돼서 버스 공유 금지 |
| DRDY·ADC_RDY 인터럽트 GPIO, 스위치 상태 GPIO | M7 | |
| 범용 타임스탬프 타이머 1개 | M7 | ECG·PPG 공통 시간축 |
| SAI 또는 PWM + 경보음 앰프 | M7 | Linux 와 무관하게 소리가 나야 한다 |
| I²C(SMBus) → LTC1760·팩 A/B | M7 | 전원 경보가 M7 판정이라서 |
| (옵션) NIBP UART | M7 | 펌프 동작 중 ECG 잡음 표시를 M7 이 알아야 한다 |
| WDOG5 | M7 (SM 고장 반응) | WDOG5 → LM_RESET lm 1 |
| PMIC I²C, 클록, 리셋 | SM | |
| 디스플레이(LVDS)·GPU·eMMC·USB·Wi-Fi/BT·GbE ×2·백업 부저 GPIO | A55 | |
| ELE(EdgeLock Secure Enclave) | 부트 체인·A55 |
자원 예산 (계산·기본값)
| 항목 | 값 | 비고 |
|---|---|---|
| M7 ITCM / DTCM | 256 KB / 256 KB | Zephyr dts 기준. NXP 참조 매뉴얼 원문은 미확인 |
| M7 코드 위치 | ITCM (DSP·ISR), 큰 표는 DDR | 기본값 |
| M7 부하 추정 | 8 kHz 인터럽트(1회 ≤ 5 µs → 4 %) + 데시메이션 FIR 약 0.75 M MAC/s + 노치·QRS(500 SPS) → 10 % 미만 | 계산. DWT 사이클 카운터로 실측 예정 |
| 공유 DDR 예약 영역 | 32 MiB (링 약 23.2 MiB + 여유) | 계산 |
3. 샘플레이트와 신호 경로
왜 8 kSPS 인가
- IEC 60601-2-27 은 페이스 펄스를 HR 계산에서 빼도록 요구한다. 범위 ±2~±700 mV, 0.1~2 ms (폭 하한은 넓은 쪽 0.1 ms 로 잡음)
- TI ADS1298 데이터시트: 소프트웨어 검출은 「8 kSPS or more」. 논문 수치 8 kHz Se 95.5 % · 16 kHz 97.1 % · 32 kHz 99.3 % (CinC 2018)
- AFE159RP8 은 칩 안에 4리드 페이스 알고리즘 + 128 kSPS 페이스 출력이 있다. 다만 공개본은 요약판이라 판정 출력 형식·폭·진폭 범위는 미확인
기본값 ECG 전 채널을 8 kSPS 24비트로 받는다. 페이스는 칩 내 검출(1차) + M7 소프트 검출(2차 확인). 상세 데이터시트로 칩 내 검출이 요구를 다 만족하는 게 확인되면 2 kSPS 로 내리는 것을 검토하고, 그 전에는 8 kSPS 고정.
M7 안의 경로
AFE159RP8 8 kSPS × 8ch × 24bit ── DRDY ISR → LPSPI DMA ─┐
├─ ① 시각 붙이기 (범용 타이머 32bit, 표본번호 u64)
AFE4490 500 SPS × 6 스트림 ── ADC_RDY ISR → LPSPI ───────┘
├─ ② 페이스: 칩 플래그 + 8 kSPS 기울기·폭 확인 → 사건 {표본번호, 리드, 폭}
├─ ③ 페이스 블랭킹: 펄스 구간을 8 kSPS 에서 직선 보간으로 메움
├─ ④ 데시메이션 8000 → 2000 → 500 SPS (선형위상 FIR 2단, ÷4·÷4)
│ 군지연 추정: 31탭(8k) 1.9 ms + 63탭(2k) 15.5 ms ≈ 17.4 ms (계산)
├─ ⑤ 「원자료」 분기 → 홀터 저장(500 SPS 24bit, 노치 전·HPF 전) + calib 행
├─ ⑥ HPF → 2중 적응형 노치(60·120 Hz) → LPF (노치 전·후를 둘 다 남김 — 비교 화면용)
├─ ⑦ QRS 검출 (별도 대역 5~15 Hz, 노치 상태와 무관)
│ 페이스 사건은 「Paced=Yes」일 때만 HR 에서 제외·마커
└─ ⑧ PPG: 500 SPS → 5표본 평균 100 SPS → 대역필터 → R·SpO2·PR·PI. 1초 주기
500 SPS 로 내리는 이유: 앞서 설계한 2중 노치 계수가 fs = 500 Hz 기준이라 그대로 쓸 수 있다. 블랭킹 길이와 FIR 탭 수는 신호처리 설계에서 실측으로 정한다.
채널 배정 기본값
| AFE 채널 | 기본 5전극 | 12유도 옵션 |
|---|---|---|
| CH1 | I (LA−RA) | I |
| CH2 | II (LL−RA) | II |
| CH3 | III (LL−LA) 직접 확정 | V1 |
| CH4 | V (V1/V5 스위치) | V2 |
| CH5~8 | 자가진단 순회(내부 시험 신호 → calib 행, 전원·온도) | V3~V6 |
표시용 다운샘플
- 열(px)마다 min/max 세로선으로 그려 QRS 꼭짓점을 보존한다. 단순 솎아내기 금지
- PPG 100 SPS 는 이웃 표본 선형 보간
- 화면 행 순서는 6행 고정: calib · lead1 · lead2 · lead3 · SpO2 · pulse 확정. V 유도(V1/V5)의 화면 위치는 미정, 기록에는 항상 들어간다
4. 코어 사이 통신 — 공유 메모리 링 + RPMsg
| 경로 | 무엇 | 이유 |
|---|---|---|
| 공유 DDR 링 (SPSC, M7 쓰기·A55 읽기) | 파형(원자료·처리후·PPG)·박동/페이스 사건 | 양이 많다(초당 수십 kB). 복사 0. A55 가 vsync 마다 쓰기 인덱스를 폴링 → 지연 ≤ 1프레임 |
| RPMsg | 명령·설정·경보 상태·수치·하트비트 | 메시지 단위·순서 보장. Linux 기본 버퍼 512 B |
RPMsg 공통 머리 (24 B, 리틀엔디언) 기본값
| 필드 | 형 | 설명 |
|---|---|---|
| magic | u16 | 0xEC61 |
| ver | u8 | 프로토콜 판 (HELLO 에서 대조, 다르면 장치 고장) |
| type | u8 | 메시지 종류 |
| len | u16 | 본문 길이 (≤ 472 B = 512 − RPMsg 머리 16 − 24) |
| flags | u16 | bit0 응답 요구, bit1 재전송 |
| seq | u32 | 보내는 쪽 일련번호. 빠짐·역순 감지 |
| t_idx | u64 | 500 SPS 기준 전역 표본번호 (공통 시간축) |
| crc | u32 | CRC-32C (crc 칸 0 으로 두고 머리+본문) |
엔드포인트 2개: ekg-ctrl (A55 → M7 명령, M7 응답) · ekg-evt (M7 → A55 사건·상태).
주요 메시지 기본값
| type | 이름 | 방향 · 주기 | 핵심 |
|---|---|---|---|
| 0x01/0x02 | HELLO / HELLO_ACK | 기동 시 | M7 펌웨어 판·프로토콜 판·링 배치표 ↔ 릴리스 번호. 다르면 고우선 「소프트웨어 불일치」 |
| 0x10 | HB_M7 | M7→A55 10 Hz | 가동시간, 장치 상태, 링 쓰기 인덱스, CPU 부하, 표본 손실 수, 노치 상태 |
| 0x11 | HB_UI | A55→M7 10 Hz | 실제로 플립된 프레임 카운터, 지금 화면에 띄운 경보 ID 목록 |
| 0x20~0x22 | CFG_SET / ACK / SNAPSHOT | 요청·응답·1분 | 안전 설정을 보내고 적용된 값을 되읽어 확인. 허용 범위 밖이면 거부 |
| 0x30~0x32 | ALARM_STATE / CMD / CMD_ACK | 변경 즉시 + 1 Hz | 판정은 M7. A55 는 일시정지(기본 2분)·확인·래치 해제 조작만 |
| 0x40 | NUMERICS | 1 Hz | HR(출처 ECG/PPG/SIM), PR, SpO2(「참고값·미교정」 플래그), PI, 파라미터별 유효/무효 |
| 0x50~0x52 | DEVICE_STATE · POWER_STATUS · SELFTEST | 변경 시 · 1 Hz · POST | 장치 상태, 팩 A/B 잔량·온도·SOH, 자가시험 결과 |
| 0x60 | LEAD_STATUS | 변경 시 | 전극별 리드오프, V1/V5 스위치 위치, sim/signal 위치, SpO2 센서 상태 |
| 0x70 · 0x80/0x81 | FAULT · SHUTDOWN_REQ/READY | 발생 시 | 고장 코드 · 종료 전 홀터 파일 닫힘 확인 |
| 0x90 | SERVICE_CMD | 서비스 모드만 | 측정 모드에서는 거부 |
| 0xA0 | TIME_SYNC | 1분 | UTC ↔ t_idx 대응 (기록용. 경보 판정은 t_idx 만 씀) |
공유 메모리 링 (계산·기본값)
모든 링은 2의 거듭제곱 길이, 인덱스는 전역 표본번호 u64 마스킹. M7 은 데이터를 다 쓴 뒤 메모리 장벽 → 쓰기 인덱스 갱신. 블록(20 ms = 10 표본)마다 CRC-32C.
| 링 | 형식 | 길이 | 크기 | 용도 |
|---|---|---|---|---|
| R_RAW | int32 × 8ch, 500 SPS (노치 전·HPF 전) | 2¹⁹ = 17.5 분 | 16 MiB | 홀터·calib. Linux 가 다시 뜨는 동안 기록을 붙잡는다 |
| R_PRE / R_POST | float32 × 8ch × 2 | 2¹⁶ = 131 s | 4 MiB | 화면, 노치 비교 화면, 정지 |
| R_PPG | {red, ir int32, pleth float}, 100 SPS | 2¹⁷ = 21.8 분 | 2.5 MiB | 5·6행, 홀터 |
| R_EVT | 32 B 사건 | 2¹⁴ | 512 KiB | 하트 아이콘 동기, 주석 |
| 제어 블록 | 배치표·쓰기 인덱스·덮어쓰기 카운터 | — | 4 KiB | |
| 합계 | 약 23.2 MiB → 예약 32 MiB |
예약 영역은 non-cacheable. A55 만 리셋될 때 이 DDR 영역이 그대로 남는지는 미확인 — 실측 항목 S-01.
5. 지연 예산
전극 → 화면 (계산·기본값)
| 단계 | 지연 |
|---|---|
| AFE159RP8 내부 디지털 필터 | 미확인 |
| 데시메이션 FIR 2단 | ≈ 17.4 ms |
| M7 블록 처리 (20 ms 블록) | ≤ 20 ms |
| 공유 링 → A55 폴링 | ≤ 16.7 ms |
| 고정 표시 지연 D (QRS 검출 지연 흡수, 하트 아이콘 동기) | 300 ms 초기값 |
| 렌더 + 페이지 플립 (더블버퍼 2프레임) | ≤ 33 ms |
| 합계 | 약 390 ms + AFE 지연 |
경보 기본값
| 경로 | 예산 |
|---|---|
| 심박 상·하한, 무수축 (조건 지연 + 신호 생성 지연) | 합 ≤ 10 s (조건 판정 ≤ 8 s + 소리 시작 ≤ 1 s + 여유) — 가정용엔 비적용인 IEC 60601-2-27 기준을 자진 채택 |
| 리드오프·포화 표시 | ≤ 1 s (규격 ≤ 10 s) |
| SpO2 갱신 정지 | > 30 s 면 저우선 |
| 배터리 소진 | 남은 20 분에 고우선 (규격 「최소 5분 전」) |
| M7 판정 → A55 화면 표시 | ≤ 50 ms |
| M7 판정 → 클라우드·보호자 알림 | 최선 노력(보장 없음) — 경보 시스템의 일부로 치지 않는다 |
6. 홀터 저장 · 트렌드
원칙
- 기기 안 기록이 정본, 클라우드는 사본
- 저장 신호는 노치·HPF 전 원자료(500 SPS 24비트). 필터는 볼 때 다시 건다 → 다시 분석할 수 있다
- 기본 5전극 4채널 하루 518.4 MB, 12유도 8채널 하루 1.04 GB (계산) → eMMC pSLC 32 GB 이상
1초 블록 기본값
고정 크기 슬롯: 4채널 8 KiB, 8채널 16 KiB (eMMC 4 KiB 정렬). 1시간 = 3600 슬롯짜리 세그먼트 파일을 미리 할당해 쓰는 동안 파일 메타데이터가 바뀌지 않게 한다.
| 위치 | 필드 | 크기 |
|---|---|---|
| 머리 | magic 'EKGB', 형식 판, 세션 UUID, 블록 일련번호, 첫 표본번호, UTC + 단조 시각, 표본화(500), 채널 수·맵·스케일, 필터 설정 사본, 노치 상태, 플래그(SIM·리드오프·Paced·충전 중) | 128 B |
| ECG | 500 × N채널 × 3 B (24비트 패킹) | 6,000 B (4ch) / 12,000 B (8ch) |
| PPG | 100 × 2 × 3 B (red·IR) | 600 B |
| 트렌드 | 이 1초의 HR·PR·SpO2·PI·(RR·NIBP) + 유효 플래그 | 32 B |
| 주석 TLV | 박동(종류 「참고용」), 페이스 시점(8 kSPS 해상도), 리드 변화, V1/V5 전환, 경보 시작·끝, 환자 버튼, 시각 보정 | 가변 |
| 꼬리 | CRC-32C + 앞 블록 SHA-256 을 넣은 해시 체인 | 36 B |
- 페이스는 원파형을 고속 저장하지 않고 시점만 적는다 (128 kSPS 원파형이면 하루 약 88 GB)
- 쓰기: 1초마다
pwrite1슬롯 +fdatasync. 쓰기 스레드는 렌더 스레드와 락을 공유하지 않는다 - 파일시스템: ext4
data=ordered+ 미리 할당 + 블록마다 fdatasync 를 기본값, F2FS 를 대안으로. 최종 선택은 전원 강제 차단 반복 후 해시 체인 검사로 - 잃는 양: 두 배터리 팩이 동시에 끊기는 급정전에서만 ≤ 1~2 초. 정상 종료 경로에서는 0
- 기동 시 복구: 마지막 세그먼트를 끝에서부터 CRC·해시 체인 확인 → 깨진 꼬리는 「미기록」 표시 + 「기록 끊김」 주석. 끊긴 구간을 채워 넣지 않는다
- 내보내기: EDF+/BDF(24비트) 또는 WFDB-FLAC 기본, 호환용 ISHNE·DICOM Ambulatory(16비트). 정본 블록은 그대로
트렌드 DB 기본값
- 정본은 홀터 블록 안 1초 트렌드 레코드. DB 가 깨져도 블록에서 다시 만든다
- 조회용 SQLite (WAL, synchronous=FULL) — 1초 표 + 1분·5분·1시간 집계 표
- 보관 1년 (5~6 파라미터 1초 2 B → 315~378 MB, 계산). 창 30 min ~ 7일
- SpO2 유래 지표는 「참고값·미교정」 표기
7. 경보 엔진 (IEC 60601-1-8 우선순위)
- M7
AlarmCore: 조건 판정, 우선순위, 래칭, 일시정지 타이머, 경보음 합성. 설정이 안 왔으면 컴파일된 기본 한계로 돈다 - A55
AlarmUI: 시각 신호(색·깜박임·글자), 조작, 경보 로그, 원격 보조 알림. 판정은 하지 않는다 - 시각 대조: M7 은 HB_UI 의 「띄운 경보 ID 목록」을 받아, 활성 경보가 2 s 넘게 화면에 없으면 「표시 고장」(고)
- 2단계: RM44L920 이 같은 입력으로 따로 판정해 M7 결과와 대조
우선순위·신호 기본값
| 항목 | 고 | 중 | 저 |
|---|---|---|---|
| 색 | #FF0000 채움 | 노랑 채움 + 검정 글자 | 청록 채움 + 검정 글자 |
| 깜박임 | 현행 1 Hz 유지 (규격 원문 확인 전) | 같음 | 없음 |
| 소리 | 10펄스 버스트 | 3펄스 | 1~2펄스 |
- 소리 주파수: 주 에너지 500~1000 Hz, 스피커 + 합성 파형 (노인성 난청을 고려해 4 kHz 압전 단독은 쓰지 않음)
- 일시정지 기본 2분, 끝나면 자동 복귀. 기술 경보 비래칭, 생리 경보는 설정(기본 래칭)
- 색만으로 구분하지 않는다: 채움 + 글자 + 아이콘 + 점멸 + 소리
경보 목록 (발췌) 기본값
| 종류 | 경보 | 우선순위 |
|---|---|---|
| 생리 | HR 상·하한 (기본 50~120, 조정 단계 ≤ 5 bpm) | 중 |
| 생리 | 무수축 | 고 |
| 생리 | SpO2 하한 (기본 90 %, 85 % 미만 기본값 금지) — 「참고값·미교정」 함께 표시 | 중 |
| 생리 | PR 상·하한 (기본 50~120) | 중 |
| 기술 | 리드오프(전극 이름 표시) · 증폭기 포화 · SpO2 센서 오프 | 중 |
| 기술 | AC 끊김(정전 모드 진입) · SOC 10 % · 홀터 기록 실패 | 중 |
| 기술 | 남은 시간 ≤ 20분 · 장치 고장 · 표시 고장(A55 정지) | 고 |
| 기술 | SpO2 갱신 정지 > 30 s · 저장 공간 90 % · 클라우드 연결 끊김 | 저 |
| 정보 | SIMULATION(전 화면) · 충전 중 측정 불가 · AC 복귀 | — |
부정맥 분류 결과는 경보에 쓰지 않는다. 기록·참고용이다.
8. 상태기계
세 축이 겹친다: 장치 상태(S) × 파라미터별 신호 유효성 × 전원 상태(P).
장치 상태 기본값
| 상태 | 들어가는 조건 | 화면 | 소리 |
|---|---|---|---|
| BOOT/POST | 전원 켬, M7 기동 | 흰 스플래시 + 진행 | POST 끝에 짧은 자가 발음 1회 |
| S0 정상 | POST 통과 | 6행 파형·수치 | 생리 경보 시 |
| S1 신호 무효 (파라미터별) | 리드오프·센서오프·포화·NaN·자가진단 이탈·링 데이터 0.5 s 끊김 | 해당 행 점선/회색, 수치 「---」(지난 값 안 남김) | 기술 경보 중/저 |
| S2 장치 고장 | POST 실패, 워치독 리셋 이력, AFE 통신 끊김, 판 불일치, 하트비트 상실 | 「장치 고장 — 측정 불가」 전 화면 | 고 (M7 스피커 또는 백업 부저) |
| S3 안전 정지 | 환자섬 전원 이상, 기판 과열, 남은 시간 ≤ 5분 | 「측정 중지」 + 이유 | 고 |
| SERVICE | 환자 비연결 + 서비스 권한 | 서비스 화면 | — |
원칙 두 개: 정지된 정상 파형을 보여주지 않는다 · 주 처리부가 죽어도 경보음은 산다.
전원 상태 기본값
| 상태 | 조건 | 동작 |
|---|---|---|
| P0 AC | AC 있음 | 충전. 충전 중 측정은 2단계 해제 규칙 — 1단계 동안은 「충전 중 측정 불가」 표시 + 하드웨어 연동 |
| P1 정전 모드 | AC 끊김 | 중우선 경보 1회, 백라이트 30 %, DVFS, 업로드 간격 늘림. 측정·기록·경보는 그대로 |
| P2 부족 | 남은 ≤ 20분 | 고우선, 홀터 1 s 주기 유지, 업로드 중지 |
| P3 종료 시퀀스 | 남은 ≤ 5분 또는 셀 문턱 | M7 → SHUTDOWN_REQ → A55 홀터 닫기·fsync·로그 → SHUTDOWN_READY → 섬 전원 끔 → 백라이트·패널·SoM 순 |
| P4 급정전 | VSYS 비교기 | 쓰기 중단. 손실 ≤ 1~2 s |
9. 부팅 자가시험 (POST) · 주기 시험
| 순서 | 항목 | 담당 | 실패 시 |
|---|---|---|---|
| 1 | AHAB 서명 검증 체인 (ROM → 컨테이너) | ELE / ROM | 부팅 안 함 |
| 2 | SM 리셋 사유 읽기 — 직전 워치독·고장 여부 | A55 | 로그 |
| 3 | M7 자체: TCM 코드 CRC, RAM 패턴, 클록 대조(범용 타이머 vs AFE DRDY 주기) | M7 | S2 |
| 4 | AFE159RP8: 칩 ID, 내부 시험 신호, 입력 단락 잡음, 전원·온도, 리드오프 비교기 (기능 목록 미확인) | M7 | S2 |
| 5 | AFE4490: SPI 쓰고 읽기 대조, ADC_RDY = PRF, LED·PD 진단 | M7 | S2 |
| 6 | 배터리 팩 A/B 상태·SOH, LTC1760 상태 | M7 | 경보 등급대로 |
| 7 | 경보음 짧게 1회 | M7 | S2 |
| 8 | DDR 인라인 ECC, eMMC 수명 레지스터, 데이터 파티션 마운트·홀터 복구 | A55 | 경보 등급대로 |
| 9 | 설정 CRC, M7 ↔ A55 릴리스 번호 대조 | A55·M7 | S2 |
| 10 | 백업 부저 경로 (서비스 모드에서만) | — | — |
주기 시험 (측정 중): calib 행 = 내부 시험 신호를 연속 자가진단으로 겸용, 진폭이 ±2 % 밖이면 「AFE 이득 이상」. 전원·온도는 남는 채널을 수 초마다 순회.
10. 클라우드 · 보안 · 업데이트
클라우드 — 오프라인 저장 후 동기화 기본값
- 보낼함(SQLite 큐)에 쌓았다가 연결되면 밀린 것부터 순서대로
- 실시간 채널: MQTT 5 QoS1, TLS 1.3 상호 인증 — 상태·이벤트·1분 트렌드·경보만 (메시지 한도 때문에 파형은 안 보냄)
- 파형: 1시간 세그먼트 파일 이어받기 업로드. 서버가 해시 체인 확인 후 「수신 확인」
- 지우기: 수신 확인된 것만, 14일 지난 뒤. 미확인분은 자동으로 절대 지우지 않는다
- 기기 인증: 기기별 X.509, 개인키는 보안 칩. 서버는 국내 리전, 첫 설정 때 건강정보 별도 동의 화면
- 보호자 앱 알림은 경보 시스템의 일부로 치지 않는 보조 알림. 본체 소리·화면이 1차
- 방화벽: 들어오는 연결 0. 서비스용 로컬 포트는 서비스 모드에서만
부트 체인 (AHAB)
Boot ROM ─(ELE 검증)─► 부트 컨테이너 [SM(M33) · U-Boot SPL · ATF · OP-TEE · U-Boot]
U-Boot ─(AHAB 인증)─► M7 펌웨어 컨테이너 (A/B 슬롯 안) → M7 기동 → 취득 시작
U-Boot ─(서명 FIT)──► 커널 + DTB + initramfs (A/B 슬롯) → rootfs dm-verity
M7 펌웨어를 A/B 슬롯에 두어 Linux 와 한 번들로 같이 갱신·같이 롤백한다. 대가는 M7 시작이 U-Boot 단계까지 늦어지는 것(수 초, 실측 예정). i.MX 95 용 AHAB 서명 절차·롤백 카운터는 미확인.
OTA (RAUC A/B, 서명 필수) 기본값
- 번들 = rootfs + 커널 FIT + M7 펌웨어 + 설정 이전 스크립트 = 릴리스 번호 1개
- 설치 조건: 환자 비연결(전 전극 리드오프 + SpO2 센서 오프) + AC 연결 + 홀터 세션 닫힘 + 권한 있는 사람의 확인. 측정 중에는 내려받기만
- 건강 확인: 새 슬롯 기동 → POST 통과 + M7 HELLO 판 일치 + UI 첫 프레임 → good 표시. 3회 실패 → 이전 슬롯
- USB 오프라인 업데이트도 같은 서명 번들. 서명 검증 전에는 아무것도 실행하지 않는다
- 데이터 보호: /data 암호화, DDR 인라인 암호화, 통신 TLS 1.3
사용자 권한 기본값
| 역할 | 할 수 있는 것 | 인증 |
|---|---|---|
| 사용자 | 보기, 이벤트 버튼, 소리 일시정지(2분), 밝기 | 없음 |
| 보호자 | + 기록 시작·정지, 원격 알림 연결, 내보내기 | PIN |
| 의료진 | + 경보 한계·지연, 필터 모드, Paced 설정, 옵션 켜기 | PIN |
| 서비스 | + 서비스 모드, 로그 내보내기, 업데이트, 자가진단 | PIN + 서명된 서비스 토큰 |
| 제조 | 시리얼·키 주입·교정값 기록 → 출하 후 잠금 | 공장 전용 |
안전 설정(경보 한계·최소 음량·Paced)은 M7 에 CFG_SET → 되읽기 확인이 끝나야 「적용됨」. M7 은 확인된 마지막 값으로 계속 돈다.
11. 소프트웨어 등급 분리 (IEC 62304) · 모듈
| 소프트웨어 항목 | 등급 (기본값) | 분리 근거 |
|---|---|---|
| M7: 취득·필터·QRS·페이스·SpO2·AlarmCore·경보음·전원 감시·A55 감시 | C | 위해로 바로 이어지는 판정이 여기 있다 |
| A55: 파형·수치 표시(Qt) | B | M7 이 프레임 카운터·띄운 경보 목록을 감시 → 멈추면 M7 이 고우선 소리 |
| A55: AlarmUI | B | 판정·소리는 M7. 일시정지 시간 상한은 M7 이 강제 |
| A55: 홀터·트렌드·클라우드·OTA·권한·로그 | B | 실패는 기술 경보로 드러나거나 롤백. 경보 경로와 무관 |
| M33: NXP SM | SOUP | 코어 격리의 하드웨어 근거 |
| RM44L920 (2단계) | C |
분리의 근거: 별개 코어 + SM 의 자원 격리(LM 단위) + M7 이 Linux 없이 돈다 + 외부 윈도 워치독. 이 분리가 규격상 인정되는지는 시험기관 판단이라 미확인.
모듈 (발췌)
| 모듈 | 위치 | 입력 → 출력 |
|---|---|---|
acq_ecg · acq_ppg | M7 | AFE 인터럽트·SPI → 8 kSPS 프레임 / 500 SPS 6스트림 + 시각 |
pace · decim · notch2 · filt | M7 | 8 kSPS → 페이스 사건 · 500 SPS · 노치 상태 |
qrs · spo2 | M7 | 박동 사건·HR / SpO2·PR·PI·품질 |
power · alarm_core · supervisor | M7 | 전원 상태 · 경보 상태·경보음 · 워치독 차기·표시 고장 |
ipc | M7·A55 | 링·RPMsg (단일 정의에서 C/C++/파이썬 헤더 자동 생성) |
ekg-ui · ekg-alarmui | A55 | 링·수치·경보 상태 → 화면, HB_UI, 경보 조작 |
ekg-recorder · ekg-trend · ekg-sync | A55 | 1초 블록 · SQLite · MQTT·업로드 |
ekg-config · ekg-update · ekg-service | A55 | CFG_SET·감사 로그 · RAUC · 서비스 명령 |
신호처리 코드(dsp/)는 하드웨어와 무관한 C 로 두어 PC 에서도 빌드하고, 공개 심전도 데이터베이스 재생으로 단위 시험한다. RTOS 를 바꾸더라도 비용이 작다.
12. 개발 순서 · 실측 목록 기본값
| 단계 | 내용 | 끝났다고 보는 기준 |
|---|---|---|
| 0 (보드 없이) | 프로토콜 정의, 신호처리를 PC 에서 — 데시메이션·노치·QRS·경보 판정을 기록 파형·시뮬레이터 파형으로 | 검출 성능 수치 출력, 노치 시험, 경보 계단 시험(80 → 40·120·0 bpm) |
| 1 | i.MX 95 평가보드로 바로 시작. Yocto 기본 이미지, SM 설정, M7 hello + RPMsg + 공유 링 | HELLO/HB 왕복, A55 강제 리셋 실측 S-01 |
| 2 | AFE 연결, 8 kSPS 취득, Qt 6행 화면 | 표본 손실 0 (24 h), 화면 지연 실측 |
| 3 | AlarmCore + 경보음 + 상태기계 + POST + 워치독 | 결함 주입(M7 멈춤·A55 멈춤·AFE 선 뽑기) |
| 4 | 홀터·트렌드·전원 연동 | 전원 강제 차단 반복 → 해시 체인 손실 ≤ 1~2 s |
| 5 | 보안: AHAB 서명, dm-verity, RAUC, USB 갱신 | 잘못된 서명 거부, 3회 실패 롤백 |
| 6 | 클라우드(국내 리전 시험 서버) | 통신 1주 끊김 후 순서대로 복구, 미확인분 안 지움 |
| 7 | 옵션(12유도·호흡·NIBP), RM44L920 연동 | 2중 판정 대조 |
| 실측 ID | 무엇 |
|---|---|
| S-01 | A55 리셋 때 공유 DDR·M7 계속 동작 여부, Linux 재부팅 시간 |
| S-02 | M7 리셋 후 재기동 시간 (그동안 기록 끊김 길이) |
| S-03 | M7 부하, 8 kHz 인터럽트 지터 |
| S-04 | 전극 → 화면 지연, AFE159RP8 필터 지연 |
| S-05 | ext4 vs F2FS 전원 차단 시험, eMMC Reliable Write 동작 |
| S-06 | U-Boot 에서 M7 기동까지 시간 |
미결정 항목: 12유도 옵션 때 lead III 처리(계산 또는 AFE 2개), V1/V5 파형의 화면 위치.
13. 시험대 펌웨어 — ESP32-S3 + ADS1293 + 심박·산소 모듈
본 제품(AFE159RP8 · AFE4490 · i.MX 95)과는 다른 시험대 전용 구성이다. 시중 모듈로 신호 경로·프레임·수신 화면을 먼저 확인하려는 목적이다.
배선 (ESP32-S3 N16R8)
| 신호 | ESP32-S3 | 비고 |
|---|---|---|
| ADS1293 SCLK / SDI / SDO / CSB | GPIO 12 / 11 / 13 / 10 | HSPI 버스 (2.0" 화면용 버스·핀과 분리) |
| ADS1293 DRDYB | GPIO 2 | 하강 에지 인터럽트, 내부 풀업 |
| ADS1293 ALARMB | GPIO 1 (선택) | 상태 줄에만 표시 |
| MAX30102 SDA / SCL | GPIO 8 / 9 | 400 kHz, INT 미사용 · 10 ms 폴링 |
| 전원 | 3V3 · GND | ADS1293 모듈에 5 V 금지 — VDDIO 최대 3.6 V |
피한 핀: 4·5·6·7·15(2.0" 화면) · 19·20(USB) · 43·44(UART0) · 26~32(플래시) · 33~37(OPI PSRAM) · 0·3·45·46(부팅 스트래핑) · 38·48(보드에 따라 RGB LED).
ADS1293 — lead I·II·III 를 모두 직접 측정
| 채널 | + | − | 리드 | FLEX 값 |
|---|---|---|---|---|
| CH1 | LA (IN2) | RA (IN1) | I | 0x11 (데이터시트 예시와 같음) |
| CH2 | LL (IN3) | RA (IN1) | II | 0x19 (예시와 같음) |
| CH3 | LL (IN3) | LA (IN2) | III (직접) | 0x1A — 예시는 CH3 를 끄고 III = II − I 로 계산 |
전극 배정 기본: RA→IN1, LA→IN2, LL→IN3, RL←IN4(RLD 출력). 모듈 잭 배선이 미확인이라 명령 e 로 IN 번호를 바꿀 수 있다.
주요 레지스터
| 주소 | 값 | 뜻 |
|---|---|---|
| 0x06 · 0x07 · 0x08 | 0x00 · 0x07 · 0x01 | DC 리드오프 켬, IN1~3 에 전류 주입, 8 nA. 비교 기준 VDD − 0.5 V. RL 귀환이 없으면 전부 「탈락」이 정상 |
| 0x0A · 0x0B · 0x0C | 0x07 · 0x00 · 0x04 | 공통모드 = RA·LA·LL 평균 → RLD 출력 → IN4(RL) |
| 0x11 · 0x13 · 0x14 | 0x00 · 0x07 · 0x00 | 내부 2.4 V 기준, 세 채널 고분해능, 모두 켬 |
| 0x27 · 0x2F | 0x08 · 0x71 | DRDYB = CH1 ECG, 스트리밍 = 상태 + 3채널 (1+9 바이트) |
| 0x12 · 0x00 | 0x04 · 0x01 | 외부 4.096 MHz 수정 → 디지털, 15 ms 기다린 뒤 변환 시작. 모듈에 수정이 있는지 미확인 — 없으면 펌웨어가 1초마다 경고 |
RL 탈락은 리드오프로 잡히지 않아 RLD 출력이 레일 근처인지(RLDRAIL)를 따로 내보낸다. 오류 레지스터는 32표본마다, 또는 ALARMB 비트가 서면 바로 읽는다.
샘플레이트 (데이터시트 표 8, SDM 102.4 kHz, R1 = 4)
| 명령 | ODR | 대역 | ADCMAX | 잡음 (µV rms, 표 값) | 1 LSB | 1 mV 당 코드 |
|---|---|---|---|---|---|---|
r 533 기본 | 533.33 Hz | 105 Hz | 0xF30000 | 2.07 | 0.0861 µV | 11,612 |
r 853 | 853.33 Hz | 175 Hz | 0xB964F0 | 2.67 | 0.1129 µV | 8,859 |
r 400 | 400 Hz | 80 Hz | 0x800000 | 1.81 | 0.1635 µV | 6,117 |
533 을 기본으로 둔 것은 500 SPS 기준 노치 설계에 가장 가까워서다(노치 계수는 533.33 Hz 로 다시 계산해야 한다). 진단 대역 150 Hz 를 덮는 건 853 뿐이고, 모니터 대역 40 Hz 는 셋 다 덮는다. 입력 범위는 INA 차동 ±400 mV.
내장 시험신호
| 명령 | 신호 | 기대 코드 (533 모드) |
|---|---|---|
t + | +VTEST = VREF/12 = +200 mV DC | 10,285,056 |
t - | −200 mV DC | 5,640,192 |
t 0 | 입력 단락 (잡음 측정) | 7,962,624 (= ADCMAX/2) |
t x | 끔 (전극) | — |
VTEST 와 VREF 가 같은 기준에서 나오므로 이득 교정은 아니고, 자가시험·채널 간 이득 차이 확인용이다. 1 mV 교정 신호(calib)는 외부 배터리 시뮬레이터 몫이다.
심박·산소 모듈 (MAX30102 추정, I2C 0x57)
- PART_ID 가 0x15 일 때만 쓰고, 아니면 「없음」으로 보고 합성 맥파를 낸다 (칩이 MAX30102 인지 미확인)
- SpO2 모드(적색+IR), 100 sps, 18비트, 표본당 6 바이트. 레지스터 값은 공개 라이브러리 정의 기준이며 원문 데이터시트 대조는 미확인
- SpO2 숫자는 내지 않는다. 교정 곡선이 없다. 수신 화면은 R 비율만 「참고」로 보인다
- 반사형 모듈이라 파형 경로 시험용일 뿐이다. 본 제품은 클립형 투과 프로브 + AFE4490
시리얼 프로토콜
533 SPS × 16 B + 100 × 12 B ≈ 9.7 kB/s (853 SPS 면 14.9 kB/s). 이진 프레임 + # 로 시작하는 글자 줄(정보·1초 상태·경고), CSV 모드도 있다.
ECG 프레임 16 B : A5 5A 'E' seq(u16 LE) CH1 CH2 CH3(u24, MSB 먼저) flags crc8
PPG 프레임 12 B : A5 5A 'P' seq(u16 LE) RED IR(u24) crc8
crc8 = 다항식 0x07, 초기 0, 'E'/'P' 부터 flags 까지
flags: 1 RA 탈락 · 2 LA 탈락 · 4 LL 탈락 · 8 RLD 레일 · 16 공통모드 범위밖
32 입력 범위밖 · 64 합성 · 128 시험신호
CRC 계산 부분 발췌(펌웨어와 PC 시험 코드가 같은 헤더를 쓴다):
static inline uint8_t ekg_crc8(const uint8_t *p, size_t n) { // 다항식 0x07, 초기값 0
uint8_t c = 0;
for (size_t i = 0; i < n; i++) {
c ^= p[i];
for (int b = 0; b < 8; b++) c = (c & 0x80) ? (uint8_t)((c << 1) ^ 0x07) : (uint8_t)(c << 1);
}
return c;
}
코드 → 전압 변환은 데이터시트 식 13: V = (ADC_OUT/ADCMAX − ½)·2·VREF/3.5.
| 명령 | 뜻 |
|---|---|
m a / m s | ADS1293 실측 / 합성 (모듈이 없으면 저절로 합성) |
o b / o c | 이진 / CSV |
r 533 · r 853 · r 400 | 샘플레이트 |
t + · t - · t 0 · t x | 내장 시험신호 |
e 1 2 3 4 | RA LA LL RL 의 IN 번호 |
l 1 · g 1 / g 0 | 리드오프 전류 ×8 nA (0 = 끔) · RLD 켬/끔 |
h 72 · n 50 · w 2 · c 1 | 합성: 심박 · 60 Hz 주입 µV(RA 0배·LA 1배·LL 2배) · 백색잡음 µV rms · calib 사각파 |
d | ADS1293 레지스터 덤프 |
합성 신호는 ADS1293 과 같은 ADC 코드 꼴로 나가므로, 받는 쪽은 실측과 합성을 구분할 필요가 없다(flags 64 만 다르다). 합성 모형은 사지 전위 φRA=0, φLA=I, φLL=II 로 두고 리드를 전위 차로 다시 계산해 III = II − I 가 정의상 성립한다. 전기축 50° 는 합성용 임의값이다.
수신·표시 스크립트 (PC · 라즈베리파이 공용)
- 받은 바이트 전부를 손실 없이 저장(raw) + ECG·PPG CSV + 로그
- 화면: 흰 바탕, I · II · III · 잔차 II−(I+III) · 맥파 적색 · 맥파 IR. 제목에 심박(단순 문턱법) · R 비율 · fs · 끊김 · 경보
- 저장본을 다시 풀어 검사하는 재생·검사 모드가 있다
안전 — 사람 몸에 닿기 전에
이 시험대는 절연이 없다. 모듈 → ESP32 → USB → 컴퓨터가 전기적으로 한 덩어리다.
- 노트북은 배터리로만. 충전기를 뽑는다
- 노트북에 다른 선(HDMI 모니터, 이더넷, 전원 어댑터 쓰는 외장 디스크, 오디오)을 꽂지 않는다
- 어댑터 전원을 쓰는 라즈베리파이 + 화면은 몸 전극 시험에 쓰지 않는다 — 합성·단락·시뮬레이터 단계에서만
- 접지된 데스크톱에 직접 꽂고 몸에 전극을 붙이지 않는다
- 붙이는 동안 금속 책상·라디에이터·수도관에 닿지 않는다
- 젖은 피부·상처 위에 붙이지 않는다. 전극은 일회용 1회
- 심장박동기·삽입형 기기를 쓰는 사람에게 붙이지 않는다
- 이 장치의 파형·숫자로 건강을 판단하지 않는다 — 시험대다
- 모듈에 5 V 를 넣지 않는다 (VDDIO 최대 3.6 V)
시험 순서
| 단계 | 무엇을 | 통과 기준 |
|---|---|---|
| ① 합성 | ESP32 만 USB 로. 모듈 없이 받는 쪽 전체 경로 확인 (m s · n 50 · c 1) | 끊김 0 · 잔차 ≤ 2 LSB · 심박 72 · 60 Hz 크기가 I·III 은 같고 II 는 그 2배 |
| ② 단락 잡음 | 모듈 연결, 전극 없음. REVID=0x01 확인 후 t 0 10초 · t + · t -, 이어서 잭에서 네 전극을 한 점에 묶고 t x | t 0 잡음 rms ≈ 표 값(533 모드 2.07 µV rms) · t ± 코드가 기대값과 같음 · DRDYB 경고 없음 · 리드오프 비트 0 |
| ③ 시뮬레이터 | 외부 배터리 시뮬레이터(1 V p-p → 1000:1 → 1 mV). 아직 미제작 | 1 mV 가 1 mV 로 · 잔차가 잡음 수준(III 직접 측정 성립 판정) · 60 Hz 노치 전/후 원자료 저장 · 전극 하나씩 빼서 리드오프 반응 |
| ④ 사람 전극 | 위 안전 항목을 모두 지킨 뒤 RA·LA·LL·RL 4개 | 리드오프 0 · RLD 레일 0 · QRS 보임 · 심박이 손목 맥과 맞음 · 60 Hz 잔류 크기 기록 |
④ 전에 ②·③ 을 반드시 거친다. ② 에서 DRDYB 경고가 나면(수정 없음 의심) 거기서 멈춘다.
14. 시험대 검증 결과 (2026-10-11)
- 컴파일 통과 2종, 시험대 소스 경고 0건 (
--warnings all, arduino-cli 1.5.2-rc.1 · esp32 코어 3.3.12)- 기본 ESP32-S3 — 플래시 365,710 B (27 %) · RAM 24,336 B (7 %)
- 네이티브 USB CDC + OPI PSRAM + 16 MB — 366,348 B (27 %) · 25,016 B (7 %)
- 보드 업로드는 하지 않았다 (부품 미도착)
- 프레임 교차 검증: 펌웨어와 같은 헤더(프레임·합성 신호)를 PC 에서 컴파일해 20초 스트림을 만들고 파이썬 수신 쪽으로 풀었다
- ECG 10,666 프레임 중 고의 CRC 손상 1개를 정확히 버림(끊김 1 로 집계), PPG 2,000 프레임 손실 0
- 0xA5 0xA5 바이트를 품은 한글 글자 줄이 섞여도 동기를 잃지 않음
- 아인트호벤 잔차 최대 0.086 µV = 1 LSB, rms 0.042 µV → 검사 통과
- CSV 경로(줄 파싱·seq 끊김·flags) 별도 확인
남은 미확인 (부품 도착 후 확인)
- ADS1293 모듈의 전원 구성(VDD·VDDIO), 4.096 MHz 수정 유무, 잭 핀 배정, 입력 보호·RLD 부품값
- 한 입력 핀을 CH1 + 와 CH3 − 에 동시에 거는 것의 허용 여부 (잔차로 판정)
- 센서 모듈이 MAX30102 인지, 적색·IR 이 바뀌었는지
- MAX30102 원문 데이터시트 대조
- ESP32-S3 보드의 RGB LED 핀, USB 포트 방식
- 리드오프 8 nA 가 케이블 용량에서 몇 초에 반응하는지
- 의료 규격의 전극 DC 전류 한계 수치