diff --git a/.gitignore b/.gitignore index a576a01..a9850e0 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ desktop.ini # ── 로그 / 임시 ── *.log *.tmp + +# ── Claude Code 로컬 설정 ── +.claude/ diff --git a/CLAUDE.md b/CLAUDE.md index e70b04e..c1504ae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,14 @@ +■ 작업 기록 규칙 (필수) +- 코드/사양 작업을 하면 그 내용을 `doc` 폴더에 마크다운으로 기록할 것. +- 파일명: `YYMMDD_제목` 형식 (예: `doc/260617_수정내용.md`). +- 같은 날 추가 작업은 그 날짜 파일에 이어서 append. +- 기록 항목: 변경 파일/이유/핵심 결정, 빌드 결과, 미해결/후속 사항. + +■ 펌웨어 위치/작업 규칙 +- **펌웨어 폴더: `SOURCE/HECO2/`** (User, Library, Makefile, build.sh). 구 `program/`에서 2026-06-17 이동됨 — 이전 문서/메모의 `program/User/...`는 `SOURCE/HECO2/User/...`로 읽을 것. +- `SOURCE/HECO2/User/My_RJ2.c`(룸컨), `My_bunbaegi.c`(각실분배기)는 수정 금지 — 검증된 코드. 문제는 `My_Homenet.c`/`MyMotor.c`/`My_system.c`/`My_Hood.c` 또는 대시보드 쪽에서 해결. +- 펌웨어 수정 후 반드시 빌드 확인: `cd SOURCE/HECO2 && bash build.sh all` (경고/오류 0). + DL 각실제어 시스템 구성 HERV (전열교환기) -(485통신) BUNBAGI -(485통신) 배기(RA) DIFFUSER -(485통신) 급기(SA) DIFFUSER @@ -49,18 +60,19 @@ HERV 사양 특이사항 : 기저운전/장비보호 (꺼짐예약) : 0 ~ 8 시간, 1시간 단위, 0은 꺼짐 -VSP 설정값 (1바이트 0~255, 사양서 DL 대덕연구소 H-ERV 실측표) +VSP 설정값 (1바이트 0~255, 개발사양서 p.12 휴벤 ECO2/좌타입 HRD1-150EPI = 펌웨어 MyControl.c 기본값) +※ 환기 터보-4(250CMH)는 사양서 자동(각실제어) 250 행 값. 기저(50CMH, SA52/EA53)는 대시보드 VSP 9엔트리에서 제외. | 구분 | 단수 | 풍량(CMH) | VSP SA | VSP EA | 전압 SA(V) | 전압 EA(V) | |----------|--------|-----------|--------|--------|-----------|-----------| -| 환기 | 약-1 | 100 | 57 | 56 | 3.347 | 3.296 | -| 환기 | 중-2 | 150 | 63 | 61 | 3.634 | 3.571 | -| 환기 | 강-3 | 200 | 69 | 67 | 3.968 | 3.893 | -| 환기 | 터보-4 | 250 | 86 | 85 | 4.391 | 4.125 | -| 바이패스 | 기본 | 150 | 66 | 80 | 3.776 | 4.539 | -| 공청 | 약-1 | 80 | 66 | - | 3.778 | - | -| 공청 | 중-2 | 100 | 70 | - | 4.018 | - | -| 공청 | 강-3 | 120 | 77 | - | 4.354 | - | +| 환기 | 약-1 | 100 | 56 | 57 | 3.30 | 3.35 | +| 환기 | 중-2 | 150 | 63 | 63 | 3.65 | 3.65 | +| 환기 | 강-3 | 200 | 70 | 70 | 4.00 | 4.00 | +| 환기 | 터보-4 | 250 | 86 | 85 | 4.80 | 4.75 | +| 바이패스 | 기본 | 150 | 67 | 75 | 3.85 | 4.25 | +| 공청 | 약-1 | 80 | 65 | - | 3.75 | - | +| 공청 | 중-2 | 100 | 72 | - | 4.10 | - | +| 공청 | 강-3 | 120 | 78 | - | 4.40 | - | | 공청 | 터보-4 | 150 | 80 | - | 4.50 | - | VSP 테이블 (VSP ↔ 실측전압, V = VSP × 0.05 + 0.5) @@ -103,7 +115,46 @@ VSP 테이블 (VSP ↔ 실측전압, V = VSP × 0.05 + 0.5) 회로도 : HERV_MAIN_REV1.1_20240826(회로도).PDF -EEPROM 저장 데이터 (My_define.h EEP_*, page 128B / 엔트리 약 122) +■ NANO100 메모리 맵 (MCU: Nano100SE3BN — LDROM 4K / APROM 115K / Data Flash 8K / SRAM 16K) +출처: DS_Nano100(B)_Series_EN_Rev1.09.pdf (5.2 Memory Organization p.106, 5.6 FMC p.111) + SOURCE/HECO2/Library .../fmc.h, EEPROM_Emulate.h + +(1) 시스템 주소공간 (Cortex-M0, 리틀엔디안, 4GB) + +| 주소 범위 | 영역 | 비고 | +|---|---|---| +| 0x0000_0000 ~ 0x0001_FFFF | FLASH (코드) | FLASH_BA, 최대 128KB 어드레싱(실제 칩은 123K 프로그램+4K LDROM+Config) | +| 0x2000_0000 ~ 0x2000_3FFF | SRAM 16KB | SRAM_BA | +| 0x4000_0000 ~ 0x401F_FFFF | APB1/APB2 주변장치 | WDT0x40004000·RTC0x40008000·TMR01 0x40010000·I2C0 0x40020000·PWM0 0x40040000·UART0 0x40050000·USBD 0x40060000·ADC12 0x400E0000 / TMR23 0x40110000·UART1 0x40150000·SC0 0x40190000·SC1 0x401B0000·SC2 0x401C0000 | +| 0x5000_0000 ~ 0x5001_FFFF | AHB 주변장치 | GCR 0x50000000·CLK 0x50000200·INT 0x50000300·GPIO 0x50004000·DMA 0x50008000·FMC 0x5000C000·EBI 0x50010000 | +| 0xE000_E000 ~ 0xE000_EFFF | 시스템 제어 | SysTick·NVIC·SCS | + +(2) 온칩 플래시 내부 구조 (FMC, 페이지 512B 단위, fmc.h) + +| 주소 범위 | 크기 | 영역 | 용도 | +|---|---|---|---| +| 0x0000_0000 ~ 0x0001_CC00 | 115KB | **APROM** (응용 프로그램) | 펌웨어 코드 + EEPROM 에뮬레이션이 여기 안에 있음 | +| 0x0001_CC00 ~ 0x0001_EC00 | 8KB | **Data Flash** | 시작주소 = Config1(DFBA) = 0x0001CC00. **현재 펌웨어 미사용** | +| 0x0010_0000 ~ 0x0010_1000 | 4KB | LDROM (ISP 로더) | 미사용 | +| 0x0030_0000 ~ | - | User Config | CONFIG0=0xFFFFFFFE(CBS 부팅선택 등), CONFIG1=0x0001CC00(DFBA) | +| (APROM+DataFlash 합) 0x0 ~ 0x1EC00 | 123KB | 전체 프로그램 플래시 | DS의 "123K APROM"은 Data Flash 포함 합계(공유) | + +(3) 우리가 실제로 쓰는 영역 + +| 주소 | 크기 | 무엇 | 근거 | +|---|---|---|---| +| 0x0000_0000 ~ 0x0000_B338 | ~46KB | 펌웨어(벡터/코드/rodata/data-init) | build/HERV.bin 크기 (text 43.9K+data 1.9K) | +| 0x0000_B338 ~ 0x0000_FC00 | ~18KB | (빈 APROM) | 코드와 EEPROM 사이 여유 | +| **0x0000_FC00 ~ 0x0000_FFFF** | **1KB (512B×2페이지)** | **EEPROM 에뮬레이션** | `EEP_FLASH_BASE=0xFC00`, `EEP_PAGE_COUNT=2` (EEPROM_Emulate.h). **APROM 내부이며 칩 Data Flash 아님** | +| 0x0000_FFFF ~ 0x0001_CC00 | ~68KB | (빈 APROM) | 미사용 | +| 0x0001_CC00 ~ 0x0001_EC00 | 8KB | 칩 Data Flash | **현재 미사용** (사장님이 FF로 쓰는 곳이 여기 — EEPROM과 다른 영역) | +| 0x2000_0000 ~ 0x2000_3FFF | 16KB | SRAM (gcc_arm.ld) | 스택/힙/.data/.bss | + +⚠ 핵심 주의 +- **펌웨어 EEPROM(0xFC00, APROM 내부) ≠ 칩 Data Flash(0x1CC00).** 그래서 Nu-Link ICP로 Data Flash에 FF를 써도 EEPROM은 안 지워짐. EEPROM을 비우려면 **칩 이레이즈** 또는 0xFC00 포함 APROM 이레이즈 필요. (`build.sh flash`=program은 코드 섹터만 지워 0xFC00 유지 → 구 EEPROM 잔존) +- **링커 `gcc_arm.ld`는 EEPROM/Data Flash 영역을 예약/보호하지 않음** (FLASH를 0x0~0x20000 통짜로 잡음, 길이 표기도 칩과 불일치). 펌웨어가 0xFC00(63KB)을 넘으면 코드가 EEPROM 위에 얹혀 충돌 — 현재 ~46KB라 여유 있음. +- EEPROM 에뮬레이션 = 2페이지 wear-leveling(EEPROM_Emulate.c). 페이지 매직 0x55AA0001, 엔트리 4B(TAG 0xE5 | idx | ~idx | val), 가득 차면 compact()로 다른 페이지 이동 후 옛 페이지 erase. 논리 인덱스 0~127(128B)을 아래 표대로 사용. + +EEPROM 저장 데이터 (논리 인덱스, My_define.h EEP_*, page 128B / 엔트리 약 122) | 인덱스 | 항목 | 비고 | |---|---|---| diff --git a/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/App.xaml b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/App.xaml new file mode 100644 index 0000000..d7a8198 --- /dev/null +++ b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/App.xaml @@ -0,0 +1,7 @@ + + + + diff --git a/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/App.xaml.cs b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/App.xaml.cs new file mode 100644 index 0000000..5953878 --- /dev/null +++ b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/App.xaml.cs @@ -0,0 +1,7 @@ +using System.Windows; + +namespace CvnetPacketProgram; + +public partial class App : Application +{ +} diff --git a/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/CvnetPacketProgram.csproj b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/CvnetPacketProgram.csproj new file mode 100644 index 0000000..c44309b --- /dev/null +++ b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/CvnetPacketProgram.csproj @@ -0,0 +1,22 @@ + + + + WinExe + net10.0-windows + enable + enable + true + CvnetPacketProgram + CvnetPacketProgram + app.manifest + + win-x64 + true + true + + + + + + + diff --git a/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/FrameParser.cs b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/FrameParser.cs new file mode 100644 index 0000000..45f7120 --- /dev/null +++ b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/FrameParser.cs @@ -0,0 +1,56 @@ +namespace CvnetPacketProgram; + +/// +/// 수신 바이트 스트림에서 0xF7 0x32 로 시작하는 완전한 프레임을 추출한다. +/// 프레임 길이 = 5(Header~Len) + Len + 2(XOR,ADD). +/// +public sealed class FrameParser +{ + private readonly List _buf = new(); + + public void Append(byte[] data, int len) + { + for (int i = 0; i < len; i++) _buf.Add(data[i]); + } + + /// 버퍼에서 추출 가능한 모든 완전 프레임을 반환한다. + public List Extract() + { + var result = new List(); + + while (true) + { + // Header(0xF7) + Device(0x32) 동기화 + int sync = FindSync(); + if (sync < 0) + { + // 동기 패턴 없음 — 마지막 1바이트(0xF7 가능성)만 남기고 버림 + if (_buf.Count > 1) _buf.RemoveRange(0, _buf.Count - 1); + break; + } + if (sync > 0) _buf.RemoveRange(0, sync); // 앞쪽 쓰레기 제거 + + if (_buf.Count < 5) break; // Len 까지 못 받음 + int len = _buf[4]; + int frameLen = 5 + len + 2; + if (_buf.Count < frameLen) break; // 프레임 미완성 + + var frame = _buf.GetRange(0, frameLen).ToArray(); + _buf.RemoveRange(0, frameLen); + result.Add(frame); + } + return result; + } + + private int FindSync() + { + for (int i = 0; i + 1 < _buf.Count; i++) + if (_buf[i] == Cvnet.Header && _buf[i + 1] == Cvnet.Device) + return i; + // 마지막 바이트가 Header 면 다음 바이트 대기 + if (_buf.Count > 0 && _buf[^1] == Cvnet.Header) return _buf.Count - 1; + return -1; + } + + public void Clear() => _buf.Clear(); +} diff --git a/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/MainWindow.xaml b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/MainWindow.xaml new file mode 100644 index 0000000..faf1d04 --- /dev/null +++ b/Protocol/CVNET_DL_230824/CVNET_PACKET_PROGRAM/MainWindow.xaml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + +