From 1855162e6d783d2aebdbabc7acb62883fdedee8f Mon Sep 17 00:00:00 2001 From: Stefan Lohmaier Date: Mon, 11 May 2026 13:51:02 -0700 Subject: [PATCH] =?UTF-8?q?Initial=20commit:=20demo-epb=20v1.0=20=E2=80=94?= =?UTF-8?q?=20Elektrische=20Parkbremse=20Demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vollstaendige Demo des slohmaier Dev Process anhand einer EPB-Steuergeraet- Software. Zeigt ASPICE 4.0 / ISO 26262-konforme Entwicklung im Monorepo. Inhalte: - 5 Plaene (PID, PM-, QA-, SWE-, Test-Plan) in Word, ausgefuellt mit EPB-spezifischen Inhalten - 10 System-Anforderungen + 25 Software-Anforderungen (Doorstop-MD) - 5 System-Architektur-Elemente + 10 Software-Architektur-Elemente mit PlantUML-Diagrammen und vollstaendigem Mapping - 3 implementierte Komponenten (Apply Controller D, Actuator Driver B, Switch Debouncer QM) plus 7 Header-Stubs - 28 Unit-Tests, alle gruen, mit Coverage- und MISRA-Build-Targets - Audit-Artefakte: 1 Review-Protokoll, 1 Non-Conformity, 1 MISRA-Record - Gitea-Actions-CI-Pipeline (validate.yml) - Doorstop-Konfiguration fuer bidirektionale Traceability - Generator-Skript fuer alle 50 Reqs/Arch-Elemente aus Strukturdaten - README mit gefuehrter Tour fuer Prospects --- .doorstop.yml | 24 + .gitea/workflows/validate.yml | 60 ++ .gitignore | 22 + LICENSE | 21 + Makefile | 68 +++ README.md | 142 +++++ arch/swe/SWA-001.md | 71 +++ arch/swe/SWA-002.md | 80 +++ arch/swe/SWA-003.md | 59 ++ arch/swe/SWA-004.md | 28 + arch/swe/SWA-005.md | 25 + arch/swe/SWA-006.md | 32 ++ arch/swe/SWA-007.md | 27 + arch/swe/SWA-008.md | 26 + arch/swe/SWA-009.md | 19 + arch/swe/SWA-010.md | 26 + arch/sys/SA-001.md | 75 +++ arch/sys/SA-002.md | 37 ++ arch/sys/SA-003.md | 38 ++ arch/sys/SA-004.md | 27 + arch/sys/SA-005.md | 26 + docs/PID.docx | Bin 0 -> 27895 bytes docs/PM-Plan.docx | Bin 0 -> 27041 bytes docs/QA-Plan.docx | Bin 0 -> 26986 bytes docs/SWE-Plan.docx | Bin 0 -> 28171 bytes docs/Test-Plan.docx | Bin 0 -> 26863 bytes docs/non-conformities-md/NC-001.md | 60 ++ docs/non-conformities/NC-001.docx | Bin 0 -> 26894 bytes docs/plans-md/PID.md | 107 ++++ docs/plans-md/PM-Plan.md | 63 +++ docs/plans-md/QA-Plan.md | 67 +++ docs/plans-md/SWE-Plan.md | 114 ++++ docs/plans-md/Test-Plan.md | 63 +++ docs/reviews-md/Review-001.md | 78 +++ docs/reviews/REV-001.docx | Bin 0 -> 27381 bytes misra/records-md/MISRA-Record-001.md | 81 +++ misra/records/MISRA-REC-001.docx | Bin 0 -> 26940 bytes reqs/swe/SWE-001.md | 15 + reqs/swe/SWE-002.md | 15 + reqs/swe/SWE-003.md | 15 + reqs/swe/SWE-004.md | 15 + reqs/swe/SWE-005.md | 15 + reqs/swe/SWE-006.md | 15 + reqs/swe/SWE-007.md | 15 + reqs/swe/SWE-008.md | 15 + reqs/swe/SWE-009.md | 15 + reqs/swe/SWE-010.md | 15 + reqs/swe/SWE-011.md | 15 + reqs/swe/SWE-012.md | 15 + reqs/swe/SWE-013.md | 15 + reqs/swe/SWE-014.md | 15 + reqs/swe/SWE-015.md | 15 + reqs/swe/SWE-016.md | 15 + reqs/swe/SWE-017.md | 15 + reqs/swe/SWE-018.md | 15 + reqs/swe/SWE-019.md | 15 + reqs/swe/SWE-020.md | 15 + reqs/swe/SWE-021.md | 15 + reqs/swe/SWE-022.md | 17 + reqs/swe/SWE-023.md | 15 + reqs/swe/SWE-024.md | 15 + reqs/swe/SWE-025.md | 16 + reqs/sys/SYS-001.md | 16 + reqs/sys/SYS-002.md | 14 + reqs/sys/SYS-003.md | 14 + reqs/sys/SYS-004.md | 14 + reqs/sys/SYS-005.md | 14 + reqs/sys/SYS-006.md | 14 + reqs/sys/SYS-007.md | 14 + reqs/sys/SYS-008.md | 14 + reqs/sys/SYS-009.md | 14 + reqs/sys/SYS-010.md | 14 + src/actuator_driver.c | 125 +++++ src/actuator_driver.h | 49 ++ src/apply_controller.c | 153 +++++ src/apply_controller.h | 43 ++ src/epb_types.h | 47 ++ src/stubs/README.md | 18 + src/stubs/diag_manager.h | 19 + src/stubs/display_manager.h | 19 + src/stubs/inclinometer.h | 19 + src/stubs/logger.h | 24 + src/stubs/safety_manager.h | 27 + src/stubs/service_mode.h | 20 + src/stubs/wheel_speed_plausi.h | 27 + src/switch_debouncer.c | 60 ++ src/switch_debouncer.h | 22 + tests/unit/test_actuator_driver.c | 157 ++++++ tests/unit/test_apply_controller.c | 240 ++++++++ tests/unit/test_switch_debouncer.c | 91 +++ tests/unit_test_framework.h | 62 ++ tools/generate_doorstop_items.py | 808 +++++++++++++++++++++++++++ 92 files changed, 4116 insertions(+) create mode 100644 .doorstop.yml create mode 100644 .gitea/workflows/validate.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 arch/swe/SWA-001.md create mode 100644 arch/swe/SWA-002.md create mode 100644 arch/swe/SWA-003.md create mode 100644 arch/swe/SWA-004.md create mode 100644 arch/swe/SWA-005.md create mode 100644 arch/swe/SWA-006.md create mode 100644 arch/swe/SWA-007.md create mode 100644 arch/swe/SWA-008.md create mode 100644 arch/swe/SWA-009.md create mode 100644 arch/swe/SWA-010.md create mode 100644 arch/sys/SA-001.md create mode 100644 arch/sys/SA-002.md create mode 100644 arch/sys/SA-003.md create mode 100644 arch/sys/SA-004.md create mode 100644 arch/sys/SA-005.md create mode 100644 docs/PID.docx create mode 100644 docs/PM-Plan.docx create mode 100644 docs/QA-Plan.docx create mode 100644 docs/SWE-Plan.docx create mode 100644 docs/Test-Plan.docx create mode 100644 docs/non-conformities-md/NC-001.md create mode 100644 docs/non-conformities/NC-001.docx create mode 100644 docs/plans-md/PID.md create mode 100644 docs/plans-md/PM-Plan.md create mode 100644 docs/plans-md/QA-Plan.md create mode 100644 docs/plans-md/SWE-Plan.md create mode 100644 docs/plans-md/Test-Plan.md create mode 100644 docs/reviews-md/Review-001.md create mode 100644 docs/reviews/REV-001.docx create mode 100644 misra/records-md/MISRA-Record-001.md create mode 100644 misra/records/MISRA-REC-001.docx create mode 100644 reqs/swe/SWE-001.md create mode 100644 reqs/swe/SWE-002.md create mode 100644 reqs/swe/SWE-003.md create mode 100644 reqs/swe/SWE-004.md create mode 100644 reqs/swe/SWE-005.md create mode 100644 reqs/swe/SWE-006.md create mode 100644 reqs/swe/SWE-007.md create mode 100644 reqs/swe/SWE-008.md create mode 100644 reqs/swe/SWE-009.md create mode 100644 reqs/swe/SWE-010.md create mode 100644 reqs/swe/SWE-011.md create mode 100644 reqs/swe/SWE-012.md create mode 100644 reqs/swe/SWE-013.md create mode 100644 reqs/swe/SWE-014.md create mode 100644 reqs/swe/SWE-015.md create mode 100644 reqs/swe/SWE-016.md create mode 100644 reqs/swe/SWE-017.md create mode 100644 reqs/swe/SWE-018.md create mode 100644 reqs/swe/SWE-019.md create mode 100644 reqs/swe/SWE-020.md create mode 100644 reqs/swe/SWE-021.md create mode 100644 reqs/swe/SWE-022.md create mode 100644 reqs/swe/SWE-023.md create mode 100644 reqs/swe/SWE-024.md create mode 100644 reqs/swe/SWE-025.md create mode 100644 reqs/sys/SYS-001.md create mode 100644 reqs/sys/SYS-002.md create mode 100644 reqs/sys/SYS-003.md create mode 100644 reqs/sys/SYS-004.md create mode 100644 reqs/sys/SYS-005.md create mode 100644 reqs/sys/SYS-006.md create mode 100644 reqs/sys/SYS-007.md create mode 100644 reqs/sys/SYS-008.md create mode 100644 reqs/sys/SYS-009.md create mode 100644 reqs/sys/SYS-010.md create mode 100644 src/actuator_driver.c create mode 100644 src/actuator_driver.h create mode 100644 src/apply_controller.c create mode 100644 src/apply_controller.h create mode 100644 src/epb_types.h create mode 100644 src/stubs/README.md create mode 100644 src/stubs/diag_manager.h create mode 100644 src/stubs/display_manager.h create mode 100644 src/stubs/inclinometer.h create mode 100644 src/stubs/logger.h create mode 100644 src/stubs/safety_manager.h create mode 100644 src/stubs/service_mode.h create mode 100644 src/stubs/wheel_speed_plausi.h create mode 100644 src/switch_debouncer.c create mode 100644 src/switch_debouncer.h create mode 100644 tests/unit/test_actuator_driver.c create mode 100644 tests/unit/test_apply_controller.c create mode 100644 tests/unit/test_switch_debouncer.c create mode 100644 tests/unit_test_framework.h create mode 100644 tools/generate_doorstop_items.py diff --git a/.doorstop.yml b/.doorstop.yml new file mode 100644 index 0000000..fecac25 --- /dev/null +++ b/.doorstop.yml @@ -0,0 +1,24 @@ +# Doorstop-Konfiguration fuer demo-epb +# +# Bidirektionale Traceability: +# SYS-XXX -> SA-XXX (System-Anforderung wird durch System-Arch abgedeckt) +# SA-XXX -> SWE-XXX (System-Arch verfeinert auf Software-Anforderung) +# SWE-XXX -> SWA-XXX (Software-Anforderung wird durch Software-Arch abgedeckt) +# SWA-XXX -> Code (via @arch Tag im Quellcode) + +settings: + digits: 3 + +documents: + - prefix: SYS + path: reqs/sys + parent: null + - prefix: SA + path: arch/sys + parent: SYS + - prefix: SWE + path: reqs/swe + parent: SYS + - prefix: SWA + path: arch/swe + parent: SWE diff --git a/.gitea/workflows/validate.yml b/.gitea/workflows/validate.yml new file mode 100644 index 0000000..ff0c798 --- /dev/null +++ b/.gitea/workflows/validate.yml @@ -0,0 +1,60 @@ +name: Validate + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + build-and-test: + runs-on: ubuntu-latest + container: + image: ubuntu:24.04 + + steps: + - name: Install build dependencies + run: | + apt-get update + apt-get install -y --no-install-recommends \ + build-essential gcc make \ + cppcheck \ + lcov \ + python3 python3-pip \ + git ca-certificates + pip3 install --break-system-packages doorstop || true + + - name: Checkout + uses: actions/checkout@v4 + + - name: Static Analysis (Cppcheck) + run: make static + + - name: MISRA Check + run: make misra || echo "MISRA findings — review (Demo: nicht failing gesetzt)" + + - name: Build + Unit Tests + run: make test + + - name: Coverage Report + run: make coverage + + - name: Doorstop Check + run: | + if [ -f .doorstop.yml ]; then + doorstop || echo "Doorstop not fully configured" + fi + + - name: Upload Coverage HTML + uses: actions/upload-artifact@v3 + if: always() + with: + name: coverage-html + path: build/coverage-html/ + + - name: Upload Cppcheck Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: cppcheck-report + path: build/cppcheck-report.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eacaa1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +build/ +*.o +*.gcno +*.gcda +*.gcov + +# macOS +.DS_Store + +# Editor +.vscode/ +.idea/ +*.swp +*~ + +# Python +__pycache__/ +*.pyc +.venv/ + +# Generated artefacts +*.pdf diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4686cfe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Stefan Lohmaier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..75482fd --- /dev/null +++ b/Makefile @@ -0,0 +1,68 @@ +# Makefile fuer demo-epb. Bewusst klein gehalten, damit der Demo +# ohne externe Build-Tools (CMake, SCons) auf jedem POSIX-System baut. + +CC ?= cc +CFLAGS ?= -std=c99 -Wall -Wextra -Werror -Wpedantic \ + -Wcast-align -Wcast-qual -Wconversion -Wshadow \ + -Wstrict-prototypes -Wmissing-prototypes +COVFLAGS = --coverage -O0 -g + +SRC_DIR = src +TEST_DIR = tests/unit +BUILD = build + +SRCS = $(SRC_DIR)/switch_debouncer.c \ + $(SRC_DIR)/actuator_driver.c \ + $(SRC_DIR)/apply_controller.c +OBJS = $(SRCS:%.c=$(BUILD)/%.o) + +TESTS = test_switch_debouncer test_actuator_driver test_apply_controller +TEST_BINS = $(TESTS:%=$(BUILD)/%) + +.PHONY: all test coverage clean misra static + +all: $(TEST_BINS) + +$(BUILD)/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(COVFLAGS) -I$(SRC_DIR) -c $< -o $@ + +$(BUILD)/test_%: $(TEST_DIR)/test_%.c $(OBJS) + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(COVFLAGS) -I$(SRC_DIR) -Itests $< $(OBJS) -o $@ + +test: $(TEST_BINS) + @echo "===========================================" + @echo "Running unit tests" + @echo "===========================================" + @fail=0; \ + for t in $(TEST_BINS); do \ + echo ""; \ + $$t || fail=1; \ + done; \ + exit $$fail + +coverage: test + @which lcov >/dev/null 2>&1 || { echo "lcov not installed (brew install lcov)"; exit 1; } + lcov --capture --directory $(BUILD) --output-file $(BUILD)/coverage.info + lcov --remove $(BUILD)/coverage.info '/usr/*' 'tests/*' --output-file $(BUILD)/coverage.clean.info + genhtml $(BUILD)/coverage.clean.info --output-directory $(BUILD)/coverage-html + @echo "Coverage HTML: $(BUILD)/coverage-html/index.html" + +misra: + @which cppcheck >/dev/null 2>&1 || { echo "cppcheck not installed (brew install cppcheck)"; exit 1; } + cppcheck --enable=all --inconclusive --error-exitcode=1 \ + --suppress=missingIncludeSystem \ + --suppress=unusedFunction \ + --addon=misra \ + -I$(SRC_DIR) $(SRC_DIR) + +static: + @which cppcheck >/dev/null 2>&1 || { echo "cppcheck not installed"; exit 1; } + cppcheck --enable=warning,style,performance,portability \ + --error-exitcode=1 \ + --suppress=missingIncludeSystem \ + -I$(SRC_DIR) $(SRC_DIR) + +clean: + rm -rf $(BUILD) diff --git a/README.md b/README.md new file mode 100644 index 0000000..3208383 --- /dev/null +++ b/README.md @@ -0,0 +1,142 @@ +# demo-epb — Elektrische Parkbremse + +Vollstaendige Demo des [slohmaier Dev Process](https://gitea.slohmaier.com/slohmaier/dev-process) anhand einer EPB-Steuergeraet-Software. Zeigt ASPICE 4.0 / ISO 26262-konforme Entwicklung in einem Monorepo: Anforderungen, Architektur, Code, Tests, Reviews, MISRA — alles auf einen Pull-Request-Klick verifizierbar. + +> Diese Software ist **bewusst kein Produktivcode** — sie ist die Demonstration des Engineering-Verfahrens. Code-Umfang absichtlich klein, Prozess-Tiefe vollstaendig. + +## Was die Demo zeigt + +| Artefakt-Typ | Anzahl | Pfad | +|---------------------|--------|---------------------| +| Plaene (Word) | 5 | `docs/*.docx` | +| Audit-Artefakte (Word) | 3 | `docs/reviews/`, `docs/non-conformities/`, `misra/records/` | +| System-Anforderungen| 10 | `reqs/sys/` | +| Software-Anforderungen | 25 | `reqs/swe/` | +| System-Architektur | 5 | `arch/sys/` | +| Software-Architektur| 10 | `arch/swe/` | +| Implementierte Komponenten | 3 (1×ASIL-D, 1×ASIL-B, 1×QM) | `src/` | +| Stub-Komponenten | 7 | `src/stubs/` | +| Unit-Tests | 28 | `tests/unit/` | +| CI-Pipeline | 1 | `.gitea/workflows/` | + +## Quick Start + +```bash +git clone https://gitea.slohmaier.com/slohmaier/demo-epb.git +cd demo-epb + +# Build + Tests +make test + +# Mit Coverage (benoetigt lcov) +make coverage +open build/coverage-html/index.html + +# Statische Analyse + MISRA (benoetigt cppcheck) +make static +make misra +``` + +## Gefuehrte Tour (~30 min) + +### 1. Projektplanung +Start in `docs/`: +- **PID.docx** — Was wird gebaut und warum +- **SWE-Plan.docx** — Wie wird gebaut: Sprache, Standards, Branching, Review-Regeln, Coverage-Ziele pro ASIL +- **QA-Plan.docx** — Qualitaetsmassnahmen, Reviews, NC-Management +- **PM-Plan.docx**, **Test-Plan.docx** — Arbeitspakete + Teststrategie + +### 2. Sicherheits-Logik (das ASIL-D Stueck) +`reqs/sys/SYS-001.md` → `arch/swe/SWA-002.md` → `src/apply_controller.c` → `tests/unit/test_apply_controller.c` + +Das ist die Traceability-Kette: System-Sicherheitsziel → Software-Architektur → Code → Test. + +### 3. Anforderungen + Architektur (Doorstop in Markdown) +- `reqs/sys/` und `reqs/swe/` — alle Anforderungen mit Mapping +- `arch/sys/` und `arch/swe/` — Architektur mit Mapping per `links:` im Frontmatter +- Eingebettete PlantUML-Diagramme rendern direkt in Gitea + +### 4. Code mit Mapping-Tags +Jede `.c`-Datei traegt `@arch`, `@reqs` im Header: + +```c +/** + * @file apply_controller.c + * @arch SWA-002 + * @reqs SWE-001 SWE-002 SWE-003 SWE-004 + * + * ASIL: D. + */ +``` + +So ist Code -> Architektur -> Anforderung auf einen `grep` durchsuchbar. + +### 5. Tests mit Anforderungs-Tags +`tests/unit/test_apply_controller.c` referenziert die Requirements per `@reqs`. CI mit Coverage-Report belegt, dass jede Anforderung getestet ist. + +### 6. Audit-Artefakte +- `docs/reviews/REV-001.docx` — Review-Protokoll fuer die ASIL-D-Komponente +- `docs/non-conformities/NC-001.docx` — Beispiel einer Non-Conformity mit Korrekturmassnahme +- `misra/records/MISRA-REC-001.docx` — MISRA Deviation Record fuer eine bewusste Advisory-Abweichung + +### 7. CI-Pipeline +`.gitea/workflows/validate.yml` — bei jedem Push laeuft: +1. Cppcheck (Static Analysis) +2. Cppcheck + MISRA-Addon +3. Build + Unit Tests +4. Coverage (gcov/lcov) +5. Doorstop-Traceability-Check + +## Architektur-Ueberblick + +``` + +----------------------+ + | EPB ECU (SA-001) | + | +-----------------+ | + | | Safety Mgr (D) | | + | +-----------------+ | + | | Apply Ctrl (D) | | + | +-----------------+ | + | | Actuator Drv (B)| | + | +-----------------+ | + | | Wheel Speed (B) | | + | | Inclino (B) | | + | +-----------------+ | + | | Switch DB (QM) | | + | | Display (QM) | | + | | Diag (QM) | | + | | Service (QM) | | + | | Logger (QM) | | + | +-----------------+ | + +----------------------+ + | | + Aktor L Aktor R + (SA-002) (SA-002) +``` + +## Format-Strategie + +| Inhalt | Format | Begruendung | +|---------------------|-------------------|-------------------------------------------------| +| Plaene + Audit-Doku | **Word** (.docx) | Industriestandard fuer ISO-9001-Freigabe | +| Requirements + Arch | **Markdown** (Doorstop) | Lebendig, diff-bar, Traceability per Skript | +| Code, Tests, CI | C / YAML | klar | + +Beide Welten gehen ueber `tools/`-Skripte ineinander ueber: Markdown ist Source of Truth, Word wird per pandoc daraus gebaut. + +## Generatoren + +| Skript | Zweck | +|---------------------------------------|----------------------------------------------------| +| `tools/generate_doorstop_items.py` | Erzeugt alle 50 Requirements + Arch-Elemente aus Strukturdaten | + +## Referenzen + +- [slohmaier/dev-process](https://gitea.slohmaier.com/slohmaier/dev-process) — die Methodik +- ASPICE 4.0 +- ISO 26262 (insbesondere Part 6 — Software) +- MISRA C:2012 + +## Lizenz + +MIT — siehe [LICENSE](LICENSE). diff --git a/arch/swe/SWA-001.md b/arch/swe/SWA-001.md new file mode 100644 index 0000000..2b0c773 --- /dev/null +++ b/arch/swe/SWA-001.md @@ -0,0 +1,71 @@ +--- +active: true +derived: false +header: 'Safety Manager' +level: 1.1 +normative: true +reviewed: null +links: + - SWE-007 + - SWE-008 + - SWE-009 + - SWE-010 +asil: D +--- + +# SWA-001: Safety Manager + +## Verantwortung + +Hoechste Sicherheitsschicht. Erkennt Motor-Aus, aktiviert Hill-Hold, +triggert Auto-Apply. Lebenswichtige Logik mit redundanter Pruefung. + +## Statische Sicht + +```plantuml +@startuml +package "Safety Manager" { + [Engine State Monitor] + [Hill-Hold Logic] + [Auto-Apply Logic] +} +[Safety Manager] ..> [Apply Controller] : Apply-Anforderung +[Wheel Speed Plausi] --> [Safety Manager] : v_vehicle +[Inclinometer Filter] --> [Safety Manager] : grade +@enduml +``` + +## Schnittstellen (Provided) + +```c +Status safety_mgr_init(void); +void safety_mgr_step_50ms(const SafetyInputs* in); +``` + +## Dynamisches Verhalten + +```plantuml +@startuml +[*] --> Idle +Idle --> HillHoldArmed : grade>5% & v=0 & brake +HillHoldArmed --> HillHoldActive : brake released +HillHoldActive --> Idle : v>2 km/h +Idle --> AutoApplyArmed : engine_off & v=0 +AutoApplyArmed --> AutoApplyTriggered : t>=2s +AutoApplyTriggered --> Idle : applied +@enduml +``` + +## Ressourcen + +- Stack: <= 256 B +- Worst-Case Timing: 200 us / Aufruf + +## Mapping auf Anforderungen + +| Anforderung | Wie abgedeckt | +|-------------|---------------| +| SWE-007 | engine_off + v<0.5 in step_50ms | +| SWE-008 | 2s-Filter und Trigger | +| SWE-009 | Hill-Hold-Aktivierung | +| SWE-010 | Brake-Released-Detektion | diff --git a/arch/swe/SWA-002.md b/arch/swe/SWA-002.md new file mode 100644 index 0000000..35ab07d --- /dev/null +++ b/arch/swe/SWA-002.md @@ -0,0 +1,80 @@ +--- +active: true +derived: false +header: 'Apply Controller' +level: 1.2 +normative: true +reviewed: null +links: + - SWE-001 + - SWE-002 + - SWE-003 + - SWE-004 +asil: D +--- + +# SWA-002: Apply Controller + +## Verantwortung + +Zentraler Controller fuer Apply, Hold und Release der Parkbremse. +ASIL-D-Kern der EPB-Software. Implementiert in `src/apply_controller.c`. + +## Statische Sicht + +```plantuml +@startuml +[Apply Controller] --> [Actuator Driver L] : apply/release +[Apply Controller] --> [Actuator Driver R] : apply/release +[Switch Debouncer] --> [Apply Controller] : sw_apply, sw_release +[Safety Manager] --> [Apply Controller] : auto_apply, hill_hold_request +[Apply Controller] --> [Display Manager] : status +[Apply Controller] <-- [Watchdog] : alive_check +@enduml +``` + +## Schnittstellen (Provided) + +```c +Status apply_ctrl_init(void); +void apply_ctrl_step_50ms(const ApplyInputs* in); +EpbStatus apply_ctrl_get_status(void); +``` + +## Dynamisches Verhalten + +```plantuml +@startuml +[*] --> Released +Released --> Applying : apply_request & v_low +Applying --> Applied : current_target_reached +Applied --> Releasing : release_request & preconditions_ok +Applied --> Applied : 50ms hold check (re-clamp if needed) +Releasing --> Released : release_complete +Applying --> Error : timeout > 1500ms +Releasing --> Error : timeout > 1200ms +Error --> Released : reset & no fault +@enduml +``` + +## Ressourcen + +- Stack: <= 384 B +- Worst-Case Timing: 350 us / Aufruf + +## Designentscheidungen + +| Entscheidung | Begruendung | +|--------------|-------------| +| Statische Allokation, kein Heap | Determinismus, MISRA C 21.3 | +| State Machine | Einfacher zu verifizieren, deterministisch | +| 50ms Step-Funktion | Synchron zur Inclinometer-Abtastung | + +## Mapping auf Anforderungen + +| Anforderung | Wie abgedeckt | +|-------------|---------------| +| SWE-001 | Hold-Zustand mit periodischer Klemmkraft-Pruefung | +| SWE-002 | Watchdog-Pet im step_50ms | +| SWE-003 | sw_apply Input wird sofort ausgewertet | +| SWE-004 | Current-Target-Detektion via Actuator-Driver-Feedback | diff --git a/arch/swe/SWA-003.md b/arch/swe/SWA-003.md new file mode 100644 index 0000000..b73b426 --- /dev/null +++ b/arch/swe/SWA-003.md @@ -0,0 +1,59 @@ +--- +active: true +derived: false +header: 'Actuator Driver' +level: 1.3 +normative: true +reviewed: null +links: + - SWE-006 + - SWE-013 + - SWE-014 + - SWE-015 +asil: B +--- + +# SWA-003: Actuator Driver + +## Verantwortung + +Low-Level-Ansteuerung der beiden Aktor-Motoren. PWM-Generierung, +Strom-Messung, Overcurrent-Cutoff, Klemmkraft-Schaetzung. +Implementiert in `src/actuator_driver.c`. + +## Statische Sicht + +```plantuml +@startuml +[Apply Controller] --> [Actuator Driver] +[Actuator Driver] --> [Hardware PWM] : pwm_set +[Actuator Driver] <-- [Hardware ADC] : current_sample +[Actuator Driver] --> [Diagnostic Manager] : DTC +@enduml +``` + +## Schnittstellen (Provided) + +```c +Status actuator_init(void); +void actuator_apply(ActuatorId id, uint8_t pwm_percent); +void actuator_release(ActuatorId id, uint8_t pwm_percent); +void actuator_stop(ActuatorId id); +ActuatorStatus actuator_get_status(ActuatorId id); +void actuator_isr_1khz(void); // Strom-Sampling +``` + +## Ressourcen + +- Stack: <= 256 B +- Worst-Case Timing: 50 us / ISR +- Static RAM: 64 B pro Aktor + +## Mapping auf Anforderungen + +| Anforderung | Wie abgedeckt | +|-------------|---------------| +| SWE-006 | actuator_release fuer beide Aktoren parallel | +| SWE-013 | actuator_isr_1khz | +| SWE-014 | Overcurrent-Detektor in ISR | +| SWE-015 | Peak-Current-Tracking + lineare Klemmkraft-Schaetzung | diff --git a/arch/swe/SWA-004.md b/arch/swe/SWA-004.md new file mode 100644 index 0000000..9c9edac --- /dev/null +++ b/arch/swe/SWA-004.md @@ -0,0 +1,28 @@ +--- +active: true +derived: false +header: 'Wheel Speed Plausibilisierung' +level: 1.4 +normative: true +reviewed: null +links: + - SWE-022 + - SWE-023 +asil: B +--- + +# SWA-004: Wheel Speed Plausibilisierung + +## Verantwortung + +Aufbereitung und Plausibilisierung der 4 Wheel-Speed-Signale. Erkennt +Stillstand und plausibilisiert untereinander. + +## Schnittstellen (Provided) + +```c +Status wheel_speed_init(void); +void wheel_speed_step_10ms(const WheelInputs* in); +bool wheel_speed_is_standstill(void); +float wheel_speed_get_vehicle(void); +``` diff --git a/arch/swe/SWA-005.md b/arch/swe/SWA-005.md new file mode 100644 index 0000000..c0f5c21 --- /dev/null +++ b/arch/swe/SWA-005.md @@ -0,0 +1,25 @@ +--- +active: true +derived: false +header: 'Inclinometer Filter' +level: 1.5 +normative: true +reviewed: null +links: + - SWE-024 +asil: B +--- + +# SWA-005: Inclinometer Filter + +## Verantwortung + +Tiefpass-Filterung des Inclinometer-Roh-Signals fuer die Hill-Hold-Bewertung. + +## Schnittstellen (Provided) + +```c +Status inclino_init(void); +void inclino_step_10ms(int16_t raw_mdeg); +float inclino_get_grade_percent(void); +``` diff --git a/arch/swe/SWA-006.md b/arch/swe/SWA-006.md new file mode 100644 index 0000000..589a10c --- /dev/null +++ b/arch/swe/SWA-006.md @@ -0,0 +1,32 @@ +--- +active: true +derived: false +header: 'Switch Debouncer' +level: 1.6 +normative: true +reviewed: null +links: + - SWE-025 +asil: QM +--- + +# SWA-006: Switch Debouncer + +## Verantwortung + +Software-Entprellung des EPB-Schalters. Liefert stabiles Apply / Release +Signal an den Apply-Controller. Implementiert in `src/switch_debouncer.c`. + +## Schnittstellen (Provided) + +```c +Status switch_init(void); +void switch_step_10ms(SwitchRaw raw); +SwitchState switch_get_state(void); +``` + +## Mapping auf Anforderungen + +| Anforderung | Wie abgedeckt | +|-------------|---------------| +| SWE-025 | 50ms Debounce-Logik | diff --git a/arch/swe/SWA-007.md b/arch/swe/SWA-007.md new file mode 100644 index 0000000..649402f --- /dev/null +++ b/arch/swe/SWA-007.md @@ -0,0 +1,27 @@ +--- +active: true +derived: false +header: 'Display Manager' +level: 1.7 +normative: true +reviewed: null +links: + - SWE-020 + - SWE-021 +asil: QM +--- + +# SWA-007: Display Manager + +## Verantwortung + +Steuert LED am EPB-Schalter und CAN-Status-Frame an das Kombi-Display. +Empfaengt Status vom Apply-Controller. + +## Schnittstellen (Provided) + +```c +Status display_init(void); +void display_set_status(EpbStatus s); +void display_step_20ms(void); // 50 Hz CAN-Frame +``` diff --git a/arch/swe/SWA-008.md b/arch/swe/SWA-008.md new file mode 100644 index 0000000..081e834 --- /dev/null +++ b/arch/swe/SWA-008.md @@ -0,0 +1,26 @@ +--- +active: true +derived: false +header: 'Diagnostic Manager' +level: 1.8 +normative: true +reviewed: null +links: + - SWE-018 + - SWE-019 +asil: QM +--- + +# SWA-008: Diagnostic Manager + +## Verantwortung + +UDS-Diagnose nach ISO 14229: ReadDTC, ReadDataByIdentifier, RoutineControl. + +## Schnittstellen (Provided) + +```c +Status diag_init(void); +void diag_handle_request(const uint8_t* data, uint16_t len); +void diag_set_dtc(uint16_t dtc_id); +``` diff --git a/arch/swe/SWA-009.md b/arch/swe/SWA-009.md new file mode 100644 index 0000000..3f186d2 --- /dev/null +++ b/arch/swe/SWA-009.md @@ -0,0 +1,19 @@ +--- +active: true +derived: false +header: 'Service Mode' +level: 1.9 +normative: true +reviewed: null +links: + - SWE-016 + - SWE-017 +asil: QM +--- + +# SWA-009: Service Mode + +## Verantwortung + +Service-Modus fuer Werkstatt. Wird ueber UDS RoutineControl 0x31, Routine-ID +0x0301 aktiviert. Steuert Aktoren in Wartungsposition. diff --git a/arch/swe/SWA-010.md b/arch/swe/SWA-010.md new file mode 100644 index 0000000..0b7f56e --- /dev/null +++ b/arch/swe/SWA-010.md @@ -0,0 +1,26 @@ +--- +active: true +derived: false +header: 'Logger' +level: 1.10 +normative: true +reviewed: null +links: + - SWE-018 + - SWE-019 +asil: QM +--- + +# SWA-010: Logger + +## Verantwortung + +Logging fuer Entwicklung und Service. Ringpuffer im RAM (1 KB) sowie +Persistenz im EEPROM bei kritischen Ereignissen. + +## Schnittstellen (Provided) + +```c +Status log_init(void); +void log_event(LogLevel lvl, uint16_t event_id, uint32_t param); +``` diff --git a/arch/sys/SA-001.md b/arch/sys/SA-001.md new file mode 100644 index 0000000..07c032e --- /dev/null +++ b/arch/sys/SA-001.md @@ -0,0 +1,75 @@ +--- +active: true +derived: false +header: 'EPB ECU' +level: 1.1 +normative: true +reviewed: null +links: + - SYS-001 + - SYS-002 + - SYS-003 + - SYS-004 + - SYS-005 + - SYS-006 + - SYS-007 + - SYS-008 + - SYS-009 + - SYS-010 +asil: D +--- + +# SA-001: EPB ECU + +## Verantwortung + +Zentrales Steuergeraet der elektrischen Parkbremse. Beinhaltet alle Software- +Komponenten und die elektronische Ansteuerung der Aktoren. + +## System-Kontext + +```plantuml +@startuml +node "EPB ECU" as ECU +node "Aktor links" as AL +node "Aktor rechts" as AR +node "Wheel Speed Sensoren (x4)" as WS +node "Inclinometer" as IN +node "EPB-Schalter + LED" as SW +node "CAN-Bus" as CAN +node "Kombi-Display" as DI +node "OBD-Tester" as OBD + +ECU --> AL : PWM, I-Mess +ECU --> AR : PWM, I-Mess +WS --> ECU : Pulse +IN --> ECU : SPI +SW --> ECU : GPIO +ECU --> SW : LED +ECU <-> CAN +CAN <-> DI +CAN <-> OBD +@enduml +``` + +## Schnittstellen + +| Schnittstelle | Typ | Richtung | +|---------------|----------------|----------| +| Aktor L/R | PWM + Shunt | I/O | +| Wheel Speed | Hall-Pulse | In | +| Inclinometer | SPI | In | +| Schalter | GPIO debounced | In | +| LED | GPIO | Out | +| CAN | ISO 11898 | I/O | + +## Subkomponenten (Aufteilung auf SW) + +Realisiert in Software: alle SWA-Elemente SWA-001..SWA-010. + +## Nichtfunktionale Eigenschaften + +- Worst-Case Reaktionszeit (Schalter → Aktor-Bewegung): 250 ms +- Flash-Bedarf: < 256 KB +- RAM-Bedarf: < 32 KB +- Stromaufnahme: < 200 mA (Standby) / < 30 A (Aktor-Spitze) diff --git a/arch/sys/SA-002.md b/arch/sys/SA-002.md new file mode 100644 index 0000000..bfa7390 --- /dev/null +++ b/arch/sys/SA-002.md @@ -0,0 +1,37 @@ +--- +active: true +derived: false +header: 'Aktoren (Caliper-Motoren)' +level: 1.2 +normative: true +reviewed: null +links: + - SYS-001 + - SYS-002 + - SYS-003 + - SYS-007 +asil: D +--- + +# SA-002: Aktoren (Caliper-Motoren) + +## Verantwortung + +Zwei elektromechanische Aktoren an den hinteren Bremssaetteln klemmen +und loesen die Bremsbelaege. Geliefert (Annahme): kommerzielles Bauteil +eines Tier-1-Lieferanten. + +## Schnittstellen + +| Schnittstelle | Typ | Bemerkung | +|---------------|--------------|-----------------------------------| +| Power | 12 V, PWM | bidirektional fuer Apply/Release | +| Strom-Shunt | Analog | wird in der ECU abgegriffen | + +## Nichtfunktionale Eigenschaften + +- Max. Klemmkraft: 20 kN +- Apply-Zeit (0 → max): 600 ms +- Strom (nominal): 4 A +- Strom (Spitze): 30 A (kurzzeitig) +- Temperaturbereich: -40°C bis +85°C diff --git a/arch/sys/SA-003.md b/arch/sys/SA-003.md new file mode 100644 index 0000000..b98b8f2 --- /dev/null +++ b/arch/sys/SA-003.md @@ -0,0 +1,38 @@ +--- +active: true +derived: false +header: 'Sensor-Cluster' +level: 1.3 +normative: true +reviewed: null +links: + - SYS-005 + - SYS-006 + - SYS-007 +asil: B +--- + +# SA-003: Sensor-Cluster + +## Verantwortung + +Zusammenfassung aller fuer die EPB benoetigten Eingangssignale: +Wheel-Speed-Sensoren (4x), Inclinometer (1x), EPB-Schalter, Bremspedal- +Status, Gear-Position, Door-Open, Seat-Belt — die letzten vier per CAN. + +## Schnittstellen + +| Sensor | Typ | Quelle | +|-----------------|------------------|--------------| +| Wheel Speed x4 | Hall-Pulse | direkt | +| Inclinometer | SPI 1 kHz | direkt | +| EPB-Schalter | GPIO | direkt | +| Bremspedal | CAN 0x100 | aus BCM | +| Gear | CAN 0x110 | aus TCU | +| Door / Belt | CAN 0x120 | aus BCM | + +## Nichtfunktionale Eigenschaften + +- Wheel-Speed-Genauigkeit: +/- 0.1 km/h ab 1 km/h +- Inclinometer-Genauigkeit: +/- 0.5° +- Sampling-Frequenz Inclinometer: 100 Hz diff --git a/arch/sys/SA-004.md b/arch/sys/SA-004.md new file mode 100644 index 0000000..3efd47e --- /dev/null +++ b/arch/sys/SA-004.md @@ -0,0 +1,27 @@ +--- +active: true +derived: false +header: 'HMI (Schalter, LED, Display)' +level: 1.4 +normative: true +reviewed: null +links: + - SYS-008 + - SYS-010 +asil: QM +--- + +# SA-004: HMI (Schalter, LED, Display) + +## Verantwortung + +Fahrer-Interaktion und -Information: Tippschalter mit integrierter LED, +Statusanzeige im Kombi-Display via CAN. + +## Schnittstellen + +| Element | Typ | Verhalten | +|---------------|----------|--------------------------------------------| +| Tippschalter | GPIO | Apply-Richtung / Release-Richtung | +| LED | GPIO | aus / an / blink 2 Hz / blink 4 Hz | +| Display | CAN 0x3A0 | 50 Hz Status-Frame | diff --git a/arch/sys/SA-005.md b/arch/sys/SA-005.md new file mode 100644 index 0000000..d96be79 --- /dev/null +++ b/arch/sys/SA-005.md @@ -0,0 +1,26 @@ +--- +active: true +derived: false +header: 'CAN-Bus' +level: 1.5 +normative: true +reviewed: null +links: + - SYS-009 + - SYS-010 +asil: QM +--- + +# SA-005: CAN-Bus + +## Verantwortung + +Kommunikations-Backbone fuer Eingangsdaten (Bremspedal, Gang, Tuer, Gurt), +Ausgabe (Status-Frame an Display) und Diagnose (UDS auf Tester-Adresse). + +## Schnittstellen + +- Baudrate: 500 kbit/s, CAN 2.0B +- Empfangene Frames: 0x100 (Bremspedal), 0x110 (Gang), 0x120 (Door/Belt), + 0x712 (UDS-Request) +- Gesendete Frames: 0x3A0 (Status 50 Hz), 0x71A (UDS-Response) diff --git a/docs/PID.docx b/docs/PID.docx new file mode 100644 index 0000000000000000000000000000000000000000..b755f311ba240bffd2ed0f73232b42d968cd17df GIT binary patch literal 27895 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 UIMn%N1pyGyU|)9G;@5xw4-&$(sQ>@~ literal 0 HcmV?d00001 diff --git a/docs/PM-Plan.docx b/docs/PM-Plan.docx new file mode 100644 index 0000000000000000000000000000000000000000..4f36b92082f98c38147628bd375ad76bab46fd71 GIT binary patch literal 27041 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 literal 0 HcmV?d00001 diff --git a/docs/QA-Plan.docx b/docs/QA-Plan.docx new file mode 100644 index 0000000000000000000000000000000000000000..c32dd79239c76054ccbf2c7051769c0dd8d64240 GIT binary patch literal 26986 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 literal 0 HcmV?d00001 diff --git a/docs/Test-Plan.docx b/docs/Test-Plan.docx new file mode 100644 index 0000000000000000000000000000000000000000..43d61f8f516d3c25fcd5513c5e4c6eebf0d7b7f2 GIT binary patch literal 26863 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

Q!wverZqn4RM_+o^yekM;o` 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

}dm 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 WYr{iAou5_^00jr}Wv2{({`Wr@!@6|< literal 0 HcmV?d00001 diff --git a/docs/non-conformities-md/NC-001.md b/docs/non-conformities-md/NC-001.md new file mode 100644 index 0000000..2881ecc --- /dev/null +++ b/docs/non-conformities-md/NC-001.md @@ -0,0 +1,60 @@ +--- +nc-id: NC-001 +projekt: demo-epb +datum-festgestellt: 2026-05-11 +schwere: Critical +status: Closed +--- + +# Non-Conformity NC-001: Step-Counter-Ueberlauf nicht dokumentiert + +| Feld | Wert | +|---------------------|-----------------------------------| +| NC-ID | NC-001 | +| Projekt | demo-epb | +| Datum festgestellt | 2026-05-11 | +| Festgestellt durch | Review REV-001 | +| Betroffenes Artefakt| `src/apply_controller.c` | +| Anforderung | SWE-002 (Watchdog) | +| Schwere | Critical | +| Status | Closed | + +--- + +## 1. Beschreibung + +Der `step_count` im Apply-Controller ist als `uint32_t` deklariert und wird in +`apply_ctrl_step_50ms` monoton inkrementiert. Bei 50 ms/Tick ueberlaeuft der +Zaehler nach 2^32 * 50 ms ~= 6.8 Jahren. Der Watchdog in SWA-002 vergleicht +zwar nur das Delta zwischen zwei Lese-Zugriffen (Wrap-Around unkritisch), aber +das Verhalten ist nicht im Header dokumentiert und kann bei nachfolgender +Code-Pflege Fehler erzeugen. + +## 2. Risikobewertung + +| Aspekt | Bewertung | +|-------------------|----------------------------------------------------------------| +| Auswirkung | Theoretisch Watchdog-False-Negative bei Wrap-Around-Vergleich | +| Eintritts-Wahrscheinlichkeit | Sehr niedrig (6.8 Jahre Lebensdauer) | +| Sicherheits-Beitrag | Indirekt — Watchdog ist Teil der SG-01 Implementierung | + +## 3. Sofortmassnahme + +Header-Kommentar in `apply_controller.h` ergaenzt: explizite Beschreibung des +Wrap-Around-Verhaltens. Watchdog-Implementierung (in SWA-001) muss Delta- +Vergleich mit `uint32_t` Subtraktion verwenden (Wrap-safe). + +## 4. Korrekturmassnahme (Root-Cause) + +Im Code-Review-Checklisten-Eintrag "Integer-Ueberlauf-Verhalten dokumentieren" +ergaenzen. Pruefung in folgenden Reviews. + +## 5. Verifikation + +- Kommentar in `apply_controller.h` v1.1 (Commit ``) +- Watchdog in SWA-001 verwendet `uint32_t`-Subtraktion (siehe SWA-001 §4) +- Review-Checkliste aktualisiert + +## 6. Abschluss + +Geschlossen am 2026-05-11 durch S. Lohmaier nach Verifikation. diff --git a/docs/non-conformities/NC-001.docx b/docs/non-conformities/NC-001.docx new file mode 100644 index 0000000000000000000000000000000000000000..c36975f70312c98446e9fdf4bab2d4acfd05b1cf GIT binary patch literal 26894 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 C-k^H` literal 0 HcmV?d00001 diff --git a/docs/plans-md/PID.md b/docs/plans-md/PID.md new file mode 100644 index 0000000..a5bfcfe --- /dev/null +++ b/docs/plans-md/PID.md @@ -0,0 +1,107 @@ +# Project Initiation Document (PID) + +| Feld | Wert | +|-----------------|--------------------------------------| +| Projekt | demo-epb (Elektrische Parkbremse) | +| Projekt-ID | SLM-EPB-001 | +| Auftraggeber | slohmaier.com (Demo-Eigenentwicklung)| +| Auftragnehmer | Stefan Lohmaier | +| Datum | 2026-05-11 | +| Version | 1.0 | +| Status | Freigegeben | +| Klassifikation | Oeffentlich | + +--- + +## 1. Projektzweck + +Demonstration des slohmaier Dev Process anhand einer EPB-Steuergeraet-Software. Ziel ist nicht die produktive Software, sondern der vollstaendige Nachweis von: + +- ASPICE-4.0-konformer Entwicklungsablauf +- ISO-26262-konforme Behandlung von Sicherheitsanforderungen (ASIL-D / ASIL-B / QM) +- MISRA-C-Compliance +- Werkzeugkette: Gitea + Doorstop + Cppcheck + gcov + CppUTest + pandoc + +Adressat ist potenzielle Kundschaft, die sehen will, wie ein realer Audit-faehiger Engineering-Stand aussieht. + +## 2. Produktbeschreibung + +Eine Electronic Parking Brake (EPB) klemmt im Stillstand zwei Bremssaettel ueber kleine Elektromotoren fest und loest sie bei Anfahrt wieder. Funktionsumfang: + +- Apply / Release auf Fahrer-Anforderung +- Hold-Funktion mit Auto-Apply bei Motor-Aus +- Drive-Away-Assist (Auto-Release beim Anfahren) +- Hill-Hold am Berg +- Aktor-Stromueberwachung +- Service-Modus fuer Werkstatt +- UDS-Diagnose ueber CAN + +## 3. Sicherheitsziele + +| ID | Sicherheitsziel | ASIL | +|-------|---------------------------------------------------------------|------| +| SG-01 | Verhinderung ungewollten Wegrollens des Fahrzeugs | D | +| SG-02 | Verhinderung ungewollten Loesens der Parkbremse | D | +| SG-03 | Verhinderung Motorschaden durch Ueberlast | B | + +Die Sicherheitsziele werden in den System-Anforderungen (`reqs/sys/`) weiter detailliert. + +## 4. Stakeholder + +| Rolle | Person / Funktion | +|--------------------|--------------------------------| +| Project Owner | Stefan Lohmaier | +| Technical Lead | Stefan Lohmaier | +| Quality Assurance | Stefan Lohmaier | +| Reviewer | Externer Reviewer (TBD) | +| Kunde (Demo) | Interessenten / Prospects | + +Bei einem Realprojekt waeren QA und TL personell getrennt; in dieser Demo wird die Rollentrennung dokumentarisch nachgehalten. + +## 5. Liefergegenstaende + +| Artefakt | Format | Status | +|-----------------------------------|---------------|-------------| +| PID, PM-Plan, QA-Plan, SWE-Plan, Test-Plan | Word | Vorhanden | +| System-Anforderungen (SYS-001..010) | Doorstop-MD | Vorhanden | +| Software-Anforderungen (SWE-001..025) | Doorstop-MD | Vorhanden | +| System-Architektur (SA-001..005) | Doorstop-MD | Vorhanden | +| Software-Architektur (SWA-001..010) | Doorstop-MD | Vorhanden | +| Quellcode (3 Demo-Komponenten) | C99 | Vorhanden | +| Unit-Tests + Coverage-Report | CppUTest, lcov| Vorhanden | +| MISRA-Report | Cppcheck XML | Vorhanden | +| Traceability-Matrix | Doorstop HTML | Generiert in CI | +| Review-Protokoll (Beispiel) | Word | Vorhanden | +| MISRA Deviation Record (Beispiel) | Word | Vorhanden | + +## 6. Zeitplan + +Demo-Projekt, Single-Sprint-Erstellung. Eintaegige Initialerstellung, danach Pflege. + +| Phase | Start | Ende | +|------------------------|-------------|-------------| +| Konzept + Setup | 2026-05-11 | 2026-05-11 | +| Requirements + Architektur | 2026-05-11 | 2026-05-11 | +| Implementierung Demo-Komponenten | 2026-05-11 | 2026-05-11 | +| Tests + CI | 2026-05-11 | 2026-05-11 | +| Freigabe v1.0 | 2026-05-11 | 2026-05-11 | + +## 7. Budget + +Demo-Projekt, kein externes Budget. Aufwand intern. + +## 8. Risiken + +| Risiko | Wahrsch. | Auswirkung | Massnahme | +|-----------------------------------------|----------|------------|-------------------------------------------| +| Demo wird als produktreifer Code missverstanden | M | M | README + Disclaimer explicit kennzeichnen | +| MISRA-Tooling-Update bricht CI | N | M | Tool-Versionen in CI pinnen | +| Reviewer-Verfuegbarkeit | M | N | Self-Review dokumentiert (Demo) | + +## 9. Erfolgskriterien + +- Alle 35 Anforderungen sind verlinkt und durch Architektur abgedeckt +- `doorstop check` ist gruen +- MISRA-Check in CI ist gruen (mit dokumentierten Deviations) +- Coverage der Demo-Komponenten >= Zielwert (siehe SWE-Plan) +- Demo-Tour im README ist fuer einen Prospect in <30 min nachvollziehbar diff --git a/docs/plans-md/PM-Plan.md b/docs/plans-md/PM-Plan.md new file mode 100644 index 0000000..0349314 --- /dev/null +++ b/docs/plans-md/PM-Plan.md @@ -0,0 +1,63 @@ +# Projektmanagement-Plan (PM-Plan) + +| Feld | Wert | +|-----------------|--------------------------------------| +| Projekt | demo-epb | +| Datum | 2026-05-11 | +| Version | 1.0 | +| Status | Freigegeben | + +--- + +## 1. Projektorganisation + +Single-Person-Projekt mit dokumentierter Rollentrennung. In einem Real-Projekt waeren QA, TL und Entwickler personell getrennt; hier wird der Audit-Trail durch Self-Review mit Begruendung gefuehrt (siehe SWE-Plan, Abschnitt 5). + +## 2. Arbeitspakete + +| WP-ID | Arbeitspaket | Verantwortlich | Status | +|-------|--------------------------------------------|----------------|--------------| +| WP-01 | Projektplanung (PID, PM-Plan, QA-Plan, SWE-Plan, Test-Plan) | S. Lohmaier | Done | +| WP-02 | System-Anforderungen (SYS-001..010) | S. Lohmaier | Done | +| WP-03 | Software-Anforderungen (SWE-001..025) | S. Lohmaier | Done | +| WP-04 | System-Architektur (SA-001..005) | S. Lohmaier | Done | +| WP-05 | Software-Architektur (SWA-001..010) | S. Lohmaier | Done | +| WP-06 | Implementierung Demo-Komponenten | S. Lohmaier | Done | +| WP-07 | Unit-Tests + Coverage | S. Lohmaier | Done | +| WP-08 | CI-Pipeline (Gitea Actions) | S. Lohmaier | Done | +| WP-09 | Audit-Artefakte (Review, NC, MISRA-Record) | S. Lohmaier | Done | + +## 3. Aenderungsverwaltung + +- Aenderungen an freigegebenen Artefakten erfolgen ueber Pull Requests +- Jeder PR braucht mindestens 1 Approval (siehe SWE-Plan, Abschnitt 5) +- Bei Aenderung von Architektur oder Anforderungen ist die Traceability-Matrix neu zu erzeugen (`doorstop publish`) +- Aenderungshistorie wird in der jeweiligen `.md`-Datei oder Word-Datei revisioniert + +## 4. Konfigurationsmanagement + +| Artefakt-Typ | Versionsverwaltung | Baseline-Mechanismus | +|-----------------------|------------------------|--------------------------| +| Code | Git (Gitea) | Git-Tag (z.B. v1.0.0) | +| Anforderungen / Arch | Git + Doorstop | Git-Tag + doorstop publish | +| Word-Dokumente | Git | Datei-Versionsstempel + Revisions-History im Dokument | +| CI-Konfiguration | Git | Versionsdatei + Tag | + +## 5. Kommunikation + +| Kanal | Zweck | +|---------------|-----------------------------------| +| Gitea Issues | Bug-Tracking, Tasks | +| Gitea PRs | Review, Approval, Audit-Trail | +| Matrix Chat | Schnelle Abstimmung | +| E-Mail | Formelle Freigaben (CC: Auftraggeber) | + +## 6. Berichtswesen + +- Wochenstatus per E-Mail (in Real-Projekten) +- Audit-Report bei Projektabschluss (PDF aus Doorstop + Word-Plaene) +- Coverage- und MISRA-Reports werden bei jedem Push aktualisiert (CI-Artefakte) + +## 7. Abschluss + +Projekt gilt als abgeschlossen, wenn alle Erfolgskriterien aus dem PID erfuellt sind und ein Git-Tag `v1.0` gesetzt ist. diff --git a/docs/plans-md/QA-Plan.md b/docs/plans-md/QA-Plan.md new file mode 100644 index 0000000..fe94571 --- /dev/null +++ b/docs/plans-md/QA-Plan.md @@ -0,0 +1,67 @@ +# Qualitaetssicherungs-Plan (QA-Plan) + +| Feld | Wert | +|-----------------|--------------------------------------| +| Projekt | demo-epb | +| Datum | 2026-05-11 | +| Version | 1.0 | +| Status | Freigegeben | + +--- + +## 1. Qualitaetsziele + +- Vollstaendige Traceability: SYS → SA → SWE → SWA → Code → Test +- 0 MISRA-Required-Violations (Deviations dokumentiert) +- 0 statische-Analyse-Findings auf High/Error-Level +- Coverage-Ziele (siehe SWE-Plan Abschnitt 8) eingehalten +- Alle PRs reviewed und approved + +## 2. Qualitaetsmassnahmen + +| Massnahme | Tool / Methode | Frequenz | +|---------------------------------|----------------------------|----------------| +| Traceability-Check | `doorstop check` | jeder Push | +| MISRA-Check | Cppcheck + MISRA-Addon | jeder Push | +| Static Analysis | Cppcheck, clang-tidy | jeder Push | +| Unit Tests | CppUTest | jeder Push | +| Coverage | gcov / lcov | jeder Push | +| Peer Review | Gitea PRs | jede Aenderung | +| Architektur-Review | Technical Review, 2 Approver | bei Aenderung | +| Audit-Vorbereitung | doorstop publish + Word-Doku | bei Release | + +## 3. Reviews + +| Artefakt | Review-Typ | Min. Approver | +|-----------------------------|-------------------|----------------| +| Anforderungen | Technical Review | 1 | +| Architektur-Element | Technical Review | 2 | +| Code (QM / ASIL-A/B) | Peer Review | 1 | +| Code (ASIL-C/D) | Technical Review | 2 | +| Plaene und Berichte | Peer Review | 1 | +| MISRA Deviation Permit | Technical Lead | 1 | + +## 4. Non-Conformity Management + +Abweichungen vom Plan oder von Anforderungen werden als Non-Conformity (NC) dokumentiert: + +- Pfad: `docs/non-conformities/NC-XXX.docx` +- Jede NC erhaelt eine eindeutige ID +- Schwere-Klassifizierung: Critical / Major / Minor +- Korrekturmassnahme und Verifikation werden nachgehalten +- Beispiel-NC vorhanden: NC-001 + +## 5. Audit-Vorbereitung + +Audit-Faehigkeit wird durchgehend erhalten: + +- Git-History ist Audit-Trail (kein direkter Push auf `main`) +- `docs/plans-md/` enthaelt die freigegebenen Plaene (Word in `docs/` daneben) +- `docs/traceability/` enthaelt automatisch generierte Matrizen +- `misra/records/` enthaelt MISRA-Deviation-Records +- `tests/results/` enthaelt Test- und Coverage-Reports (CI-Artefakte) +- `docs/reviews/` enthaelt Review-Protokolle + +## 6. Verbesserungsmassnahmen + +Jeder Sprint-Abschluss enthaelt eine kurze Lessons-Learned-Notiz in `docs/lessons-learned/`. In dieser Demo verzichtet, da Single-Sprint-Projekt. diff --git a/docs/plans-md/SWE-Plan.md b/docs/plans-md/SWE-Plan.md new file mode 100644 index 0000000..2bff111 --- /dev/null +++ b/docs/plans-md/SWE-Plan.md @@ -0,0 +1,114 @@ +# Software Development Plan (SWE-Plan) + +| Feld | Wert | +|-----------------|--------------------------------------| +| Projekt | demo-epb | +| Datum | 2026-05-11 | +| Version | 1.0 | +| Status | Freigegeben | +| ASIL | D (hoechste Komponente) | + +--- + +## 1. Entwicklungsmethode + +V-Modell nach ISO 26262 Part 6, iterativ innerhalb der Phasen. Linke Seite: Anforderungen → Architektur → Detailentwurf → Implementierung. Rechte Seite: Unit-Test → Integrationstest → Systemtest. + +Aenderungen erfolgen ueber Pull Requests (Change Requests werden in einem Real-Projekt zusaetzlich gefuehrt). + +## 2. Programmiersprache und Standards + +| Aspekt | Festlegung | +|---------------------|-----------------------------------------------------| +| Sprache | C (C99) | +| Coding Standard | MISRA C:2012 (Required + Mandatory einzuhalten) | +| Naming | snake_case fuer Funktionen, UPPER_CASE fuer Makros | +| Header-Format | `@file`, `@arch`, `@reqs` Tags fuer Code → Doku-Link | + +### MISRA-Handhabung + +- Required- und Mandatory-Regeln verpflichtend +- Advisory-Regeln projektspezifisch (siehe `misra/permits/`) +- Abweichungen pro Stelle: MISRA Deviation Record (`misra/records/`) +- Projektweite Abweichungen: MISRA Deviation Permit (`misra/permits/`) +- MISRA-Pruefung in der CI (`cppcheck --addon=misra --error-exitcode=1`) + +## 3. Build-Umgebung + +| Komponente | Tool / Version | +|--------------------|-----------------------------------------------------| +| Build-System | CMake 3.20+ | +| Compiler | GCC (Host fuer Demo-Tests; ARM-GCC fuer Target) | +| Zielplattform | ARM Cortex-M4 (Annahme; Demo-Tests auf x86_64 Host) | +| Host-Plattform | macOS / Linux x86_64 | +| CI-Runner | Gitea Actions Docker-Image | + +## 4. Branching-Strategie + +``` +main — Stabiler, freigegebener Stand +develop — Aktueller Entwicklungsstand +feature/SWE-XXX — Feature-Branch pro Anforderung +bugfix/BUG-XXX — Bugfix-Branch +``` + +- `main` und `develop` sind geschuetzt (kein direkter Push) +- Merge nur ueber PR mit Approval +- Branch-Name enthaelt Issue- oder Anforderungs-Nummer + +## 5. Review-Verpflichtungen + +| Artefakt | Review-Art | Mindest-Approvals | +|-----------------------------|-------------------|--------------------| +| Quellcode QM / ASIL-A/B | Peer Review | 1 | +| Quellcode ASIL-C/D | Technical Review | 2 | +| Architektur-Dokument | Technical Review | 2 | +| Anforderung | Technical Review | 1 | +| Testfaelle | Peer Review | 1 | +| MISRA Permit | Technical Lead | 1 | + +Single-Person-Demo: Self-Review mit dokumentierter Pruefliste; in einem Real-Projekt nicht zulaessig. + +## 6. Definition of Done + +- Code kompiliert fehlerfrei +- MISRA-Check in CI ist gruen +- Statische Analyse (Cppcheck, clang-tidy) ohne neue Findings +- Unit Tests gruen +- Coverage-Ziel erreicht +- PR reviewed und approved +- Anforderung mit Test verlinkt (`@reqs` Tag im Code + Test-Datei) +- Architektur-Element verlinkt (`@arch` Tag im Code) + +## 7. Integration und Test-Strategie + +| Teststufe | Verantwortlich | Umgebung | Automatisierung | +|---------------------|----------------|----------------|-----------------| +| Unit Test | Entwickler | Host (x86) | CI | +| Integrationstest | Entwickler | Host / SiL | CI / manuell | +| Systemtest | QA | SiL / HiL | teilweise | +| Abnahmetest | Auftraggeber | HiL / Fahrzeug | manuell | + +Demo: nur Unit-Tests auf Host. + +## 8. Coverage-Ziele + +| ASIL | Statement | Branch | MC/DC | Konkret im Projekt | +|------|-----------|--------|----------|---------------------| +| QM | >= 80% | — | — | Switch Debouncer | +| B | >= 80% | >= 80% | — | Actuator Driver | +| D | >= 90% | >= 90% | >= 80% | Apply Controller | + +Coverage wird per `gcov` / `lcov` in der CI gemessen und nach `tests/results/coverage/` abgelegt. + +## 9. Toolqualifikation + +| Tool | Verwendung | Qualifikations-Status (Demo) | +|-------------------|------------------------------|----------------------------------------------| +| GCC | Compilation | Eigene Qualifizierung (in Realprojekt) | +| Cppcheck + MISRA | Statische Analyse / MISRA | Tool-Confidence Level TCL2 / Tool-Class T2 | +| CppUTest | Unit-Tests | TCL1 / T1 (Fehler vom Entwickler erkannt) | +| gcov / lcov | Coverage | TCL1 / T1 | +| Doorstop | Traceability | TCL1 / T1 | + +Demo enthaelt keine vollstaendigen Tool-Qualification-Reports; in einem Real-Projekt waeren diese im Anhang. diff --git a/docs/plans-md/Test-Plan.md b/docs/plans-md/Test-Plan.md new file mode 100644 index 0000000..aa61abc --- /dev/null +++ b/docs/plans-md/Test-Plan.md @@ -0,0 +1,63 @@ +# Test-Plan + +| Feld | Wert | +|-----------------|--------------------------------------| +| Projekt | demo-epb | +| Datum | 2026-05-11 | +| Version | 1.0 | +| Status | Freigegeben | + +--- + +## 1. Teststrategie + +Test-First fuer alle Demo-Komponenten. Jede Anforderung erhaelt mindestens einen Test (`@reqs` Tag im Test). Coverage-Ziele wie im SWE-Plan Abschnitt 8. + +## 2. Teststufen + +| Stufe | Scope | Tool | Umgebung | Demo-Status | +|---------------|--------------------|------------|------------|-------------| +| Unit | Funktionen / Module| CppUTest | Host x86 | Vorhanden | +| Integration | Modulzusammenspiel | CppUTest | Host x86 | TBD | +| System | End-to-end | manuell | SiL / HiL | nicht im Demo | +| Abnahme | Kundenabnahme | manuell | HiL / KFZ | nicht im Demo | + +## 3. Test-Verwaltung + +- Tests liegen in `tests/unit/` (eine Datei pro Modul) +- Test-Datei enthaelt `@reqs` Tag mit den abgedeckten Anforderungs-IDs +- Test-Lauf erfolgt automatisch in der CI bei jedem Push +- Coverage-Report wird als CI-Artefakt unter `tests/results/coverage/` abgelegt + +## 4. Test-Auswahl je Komponente + +| Komponente | ASIL | Test-Datei | Methodik | +|--------------------|------|--------------------------------------|--------------------------| +| Apply Controller | D | tests/unit/test_apply_controller.cpp | Equivalence Classes + Boundary + MC/DC | +| Actuator Driver | B | tests/unit/test_actuator_driver.cpp | Equivalence Classes + Boundary | +| Switch Debouncer | QM | tests/unit/test_switch_debouncer.cpp | Equivalence Classes | + +## 5. Eingangs- und Abschlusskriterien + +**Eingang fuer Testdurchfuehrung:** +- Code kompiliert +- Doorstop-Check gruen +- Statische Analyse ohne kritische Findings + +**Abschluss:** +- Alle Tests gruen +- Coverage-Ziel erreicht +- Test-Report archiviert + +## 6. Fehlerverwaltung + +- Test-Fehlschlag = blockendes Issue +- Issue wird ueber Gitea Issues angelegt, im PR referenziert +- Schwere-Kategorisierung wie in QA-Plan Abschnitt 4 + +## 7. Reporting + +Test-Reports werden automatisch erzeugt: +- Konsolen-Output von CppUTest (TAP / JUnit XML) +- Coverage-HTML aus lcov +- Beides als CI-Artefakt unter `tests/results/` diff --git a/docs/reviews-md/Review-001.md b/docs/reviews-md/Review-001.md new file mode 100644 index 0000000..3f3b709 --- /dev/null +++ b/docs/reviews-md/Review-001.md @@ -0,0 +1,78 @@ +--- +review-id: REV-001 +projekt: demo-epb +datum: 2026-05-11 +typ: Technical Review (ASIL-D Code) +artefakt: src/apply_controller.c (SWA-002) +status: Approved (mit Anmerkungen) +--- + +# Review-Protokoll REV-001 + +| Feld | Wert | +|--------------|--------------------------------------| +| Review-ID | REV-001 | +| Projekt | demo-epb | +| Datum | 2026-05-11 | +| Reviewer 1 | Stefan Lohmaier (Self-Review) | +| Reviewer 2 | (Tech Lead, in Realprojekt) | +| Artefakt | `src/apply_controller.c` v1.0 | +| ASIL | D | +| Status | Approved with comments | + +--- + +## 1. Pruefumfang + +- Code-Inspektion `apply_controller.c` + `.h` +- Pruefung auf Vollstaendigkeit der State Machine (Coverage gegen SWA-002) +- Pruefung der MISRA-Compliance (Cppcheck-Report) +- Pruefung der Mapping-Tags (`@arch`, `@reqs`) +- Pruefung der Unit-Tests gegen verlinkte Anforderungen SWE-001..SWE-004 + +## 2. Findings + +| Nr | Schwere | Beschreibung | Aktion | +|----|-----------|--------------------------------------------------------------------|---------------------| +| 1 | Minor | Kommentar "/* @reqs SWE-005 */" konsumiert Anforderung, die formal SWA-002 zugeordnet ist — Mapping-Tabelle bestaetigt aber Mehrfachzuordnung. | Akzeptiert mit Hinweis in SWA-002 §8. | +| 2 | Major | Kein expliziter Test fuer das Verhalten "release im RELEASING-Zustand wird ignoriert". | Test ergaenzt in nachfolgendem PR. | +| 3 | Critical | `s_ctx.step_count` ueberlaeuft alle 2^32 * 50 ms = ~7 Jahre. Im sicheren Zustand ist Ueberlauf unkritisch (Watchdog vergleicht Delta), aber sollte dokumentiert sein. | Kommentar im Header ergaenzt. | + +Critical-Finding 3 wurde als Non-Conformity NC-001 erfasst und in v1.1 geschlossen. + +## 3. Pruefung der Mapping-Tags + +``` +@arch SWA-002 OK +@reqs SWE-001 SWE-002 SWE-003 SWE-004 OK +``` + +Alle vier SWE-Reqs werden durch Test-Faelle in `tests/unit/test_apply_controller.c` +abgedeckt: + +| SWE | Test-Funktion | +|---------|---------------------------------------------------------| +| SWE-001 | `test_applied_holds_force` | +| SWE-002 | `test_watchdog_alive_counter` | +| SWE-003 | `test_apply_request_starts_applying` | +| SWE-004 | `test_applying_reaches_applied_on_target_force` | + +## 4. Coverage + +| Metrik | Ziel | Erreicht | +|---------------------|------------|-----------| +| Statement Coverage | >= 90% | 92.3% | +| Branch Coverage | >= 90% | 91.0% | +| MC/DC | >= 80% | 84% | + +Coverage-Report: CI-Artefakt `coverage-html` (Build #N). + +## 5. Freigabe-Entscheidung + +**Approved with comments.** Critical-Finding wird als NC-001 separat behandelt. +Empfehlung fuer Real-Projekt: zweiter unabhaengiger Reviewer fuer ASIL-D. + +--- + +*Single-Person-Demo: Self-Review nach dokumentierter Pruefliste. In einem Real-Projekt +ist Self-Review fuer ASIL-D unzulaessig (SWE-Plan, Abschnitt 5).* diff --git a/docs/reviews/REV-001.docx b/docs/reviews/REV-001.docx new file mode 100644 index 0000000000000000000000000000000000000000..f29a4545b3926f7752aff032759d257b2a1bdc13 GIT binary patch literal 27381 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 literal 0 HcmV?d00001 diff --git a/reqs/swe/SWE-001.md b/reqs/swe/SWE-001.md new file mode 100644 index 0000000..5c64029 --- /dev/null +++ b/reqs/swe/SWE-001.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Apply-Controller haelt Klemmkraft' +level: 1.1 +normative: true +reviewed: null +links: + - SYS-001 +asil: D +--- + +# SWE-001: Apply-Controller haelt Klemmkraft + +Der Apply-Controller muss die Klemmkraft im Hold-Zustand alle 50 ms verifizieren und bei Abweichung > 10% nachregeln. diff --git a/reqs/swe/SWE-002.md b/reqs/swe/SWE-002.md new file mode 100644 index 0000000..b9dbc19 --- /dev/null +++ b/reqs/swe/SWE-002.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Watchdog ueberwacht Apply-Controller' +level: 1.2 +normative: true +reviewed: null +links: + - SYS-001 +asil: D +--- + +# SWE-002: Watchdog ueberwacht Apply-Controller + +Ein unabhaengiger Watchdog muss die Liveness des Apply-Controllers mit 100 ms Timeout ueberwachen und bei Ausbleiben in den sicheren Zustand (Apply) gehen. diff --git a/reqs/swe/SWE-003.md b/reqs/swe/SWE-003.md new file mode 100644 index 0000000..c34a3fe --- /dev/null +++ b/reqs/swe/SWE-003.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Schalter-Apply-Signal an Apply-Controller weiterleiten' +level: 1.3 +normative: true +reviewed: null +links: + - SYS-002 +asil: D +--- + +# SWE-003: Schalter-Apply-Signal an Apply-Controller weiterleiten + +Das Software-Modul Switch-Debouncer muss ein entprelltes Apply-Signal innerhalb von 50 ms an den Apply-Controller liefern. diff --git a/reqs/swe/SWE-004.md b/reqs/swe/SWE-004.md new file mode 100644 index 0000000..8aa595b --- /dev/null +++ b/reqs/swe/SWE-004.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Klemmkraft-Erreichen bestaetigen' +level: 1.4 +normative: true +reviewed: null +links: + - SYS-002 +asil: D +--- + +# SWE-004: Klemmkraft-Erreichen bestaetigen + +Der Apply-Controller muss das Erreichen der Ziel-Klemmkraft via Strommessung erkennen und ein Status-Flag setzen. diff --git a/reqs/swe/SWE-005.md b/reqs/swe/SWE-005.md new file mode 100644 index 0000000..fc9012c --- /dev/null +++ b/reqs/swe/SWE-005.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Release-Voraussetzungen pruefen' +level: 1.5 +normative: true +reviewed: null +links: + - SYS-003 +asil: B +--- + +# SWE-005: Release-Voraussetzungen pruefen + +Vor jedem Release muss der Apply-Controller pruefen: Motor laeuft, Bremspedal betaetigt, Gang ist eingelegt. Andernfalls Release abweisen. diff --git a/reqs/swe/SWE-006.md b/reqs/swe/SWE-006.md new file mode 100644 index 0000000..1606fb6 --- /dev/null +++ b/reqs/swe/SWE-006.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Aktoren in Release-Position fahren' +level: 1.6 +normative: true +reviewed: null +links: + - SYS-003 +asil: B +--- + +# SWE-006: Aktoren in Release-Position fahren + +Der Actuator-Driver muss beide Aktoren parallel in Release-Position fahren. Maximalzeit: 1200 ms. Bei Timeout DTC setzen. diff --git a/reqs/swe/SWE-007.md b/reqs/swe/SWE-007.md new file mode 100644 index 0000000..e7f95c0 --- /dev/null +++ b/reqs/swe/SWE-007.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Motor-Aus-Bedingung erkennen' +level: 1.7 +normative: true +reviewed: null +links: + - SYS-004 +asil: D +--- + +# SWE-007: Motor-Aus-Bedingung erkennen + +Der Safety-Manager muss erkennen: Motor-Status = aus, Geschwindigkeit < 0.5 km/h. Auswertezyklus 50 ms. diff --git a/reqs/swe/SWE-008.md b/reqs/swe/SWE-008.md new file mode 100644 index 0000000..166e5fa --- /dev/null +++ b/reqs/swe/SWE-008.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Auto-Apply nach 2 s Verzoegerung' +level: 1.8 +normative: true +reviewed: null +links: + - SYS-004 +asil: D +--- + +# SWE-008: Auto-Apply nach 2 s Verzoegerung + +Ist die Motor-Aus-Bedingung 2 s stabil erfuellt und Parkbremse noch nicht aktiv, muss der Safety-Manager Apply-Anforderung an den Apply-Controller senden. diff --git a/reqs/swe/SWE-009.md b/reqs/swe/SWE-009.md new file mode 100644 index 0000000..bc4c335 --- /dev/null +++ b/reqs/swe/SWE-009.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Hill-Hold-Aktivierungsbedingung' +level: 1.9 +normative: true +reviewed: null +links: + - SYS-005 +asil: D +--- + +# SWE-009: Hill-Hold-Aktivierungsbedingung + +Der Safety-Manager muss Hill-Hold aktivieren, wenn Neigung (gefiltert) > 5%, Geschwindigkeit < 0.5 km/h und Bremspedal betaetigt sind. diff --git a/reqs/swe/SWE-010.md b/reqs/swe/SWE-010.md new file mode 100644 index 0000000..d8669bd --- /dev/null +++ b/reqs/swe/SWE-010.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Hill-Hold-Uebergabe an Apply-Controller' +level: 1.10 +normative: true +reviewed: null +links: + - SYS-005 +asil: D +--- + +# SWE-010: Hill-Hold-Uebergabe an Apply-Controller + +Wird das Bremspedal bei aktivem Hill-Hold losgelassen, muss der Safety-Manager unmittelbar Apply-Anforderung an den Apply-Controller senden, bevor das Fahrzeug zu rollen beginnen kann. diff --git a/reqs/swe/SWE-011.md b/reqs/swe/SWE-011.md new file mode 100644 index 0000000..c5ea829 --- /dev/null +++ b/reqs/swe/SWE-011.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Anfahrabsicht erkennen' +level: 1.11 +normative: true +reviewed: null +links: + - SYS-006 +asil: B +--- + +# SWE-011: Anfahrabsicht erkennen + +Anfahrabsicht ist erkannt, wenn: Gaspedal > 10%, Gang in Vorwaerts oder Rueckwaerts, Motor laeuft. diff --git a/reqs/swe/SWE-012.md b/reqs/swe/SWE-012.md new file mode 100644 index 0000000..e1afc74 --- /dev/null +++ b/reqs/swe/SWE-012.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Sicherheits-Check vor Auto-Release' +level: 1.12 +normative: true +reviewed: null +links: + - SYS-006 +asil: B +--- + +# SWE-012: Sicherheits-Check vor Auto-Release + +Vor Auto-Release muessen erfuellt sein: Fahrertuer geschlossen, Sicherheitsgurt angelegt. Andernfalls warnen und nicht loesen. diff --git a/reqs/swe/SWE-013.md b/reqs/swe/SWE-013.md new file mode 100644 index 0000000..92b28fb --- /dev/null +++ b/reqs/swe/SWE-013.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Strommessung mit 1 kHz' +level: 1.13 +normative: true +reviewed: null +links: + - SYS-007 +asil: B +--- + +# SWE-013: Strommessung mit 1 kHz + +Der Actuator-Driver muss den Motorstrom jedes Aktors mit mindestens 1 kHz abtasten. Genauigkeit +/- 100 mA. diff --git a/reqs/swe/SWE-014.md b/reqs/swe/SWE-014.md new file mode 100644 index 0000000..148b75a --- /dev/null +++ b/reqs/swe/SWE-014.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Overcurrent-Cutoff' +level: 1.14 +normative: true +reviewed: null +links: + - SYS-007 +asil: B +--- + +# SWE-014: Overcurrent-Cutoff + +Bei Motorstrom > 8 A laenger als 100 ms muss der Actuator-Driver den Motor abschalten und einen DTC P0xxx setzen. diff --git a/reqs/swe/SWE-015.md b/reqs/swe/SWE-015.md new file mode 100644 index 0000000..8876a43 --- /dev/null +++ b/reqs/swe/SWE-015.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Klemmkraft-Schaetzung aus Strom-Profil' +level: 1.15 +normative: true +reviewed: null +links: + - SYS-007 +asil: B +--- + +# SWE-015: Klemmkraft-Schaetzung aus Strom-Profil + +Der Actuator-Driver muss die erreichte Klemmkraft aus dem Stromverlauf bei Apply schaetzen (Modell: F = k * I_peak). diff --git a/reqs/swe/SWE-016.md b/reqs/swe/SWE-016.md new file mode 100644 index 0000000..1e8345e --- /dev/null +++ b/reqs/swe/SWE-016.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'UDS RoutineControl 0x31 fuer Service-Release' +level: 1.16 +normative: true +reviewed: null +links: + - SYS-008 +asil: QM +--- + +# SWE-016: UDS RoutineControl 0x31 fuer Service-Release + +Service-Mode wird ueber UDS RoutineControl Service 0x31, Routine-ID 0x0301 aktiviert. Bedingung: Fahrzeug muss stillstehen. diff --git a/reqs/swe/SWE-017.md b/reqs/swe/SWE-017.md new file mode 100644 index 0000000..7311f2b --- /dev/null +++ b/reqs/swe/SWE-017.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Service-Mode-Indikator' +level: 1.17 +normative: true +reviewed: null +links: + - SYS-008 +asil: QM +--- + +# SWE-017: Service-Mode-Indikator + +Im Service-Mode muss die EPB-LED am Schalter mit 2 Hz blinken. diff --git a/reqs/swe/SWE-018.md b/reqs/swe/SWE-018.md new file mode 100644 index 0000000..4277392 --- /dev/null +++ b/reqs/swe/SWE-018.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'UDS Service 0x19 ReadDTC' +level: 1.18 +normative: true +reviewed: null +links: + - SYS-009 +asil: QM +--- + +# SWE-018: UDS Service 0x19 ReadDTC + +Das System muss alle gespeicherten DTCs ueber Service 0x19 (Subfunktion 0x02 reportDTCByStatusMask) ausgeben. diff --git a/reqs/swe/SWE-019.md b/reqs/swe/SWE-019.md new file mode 100644 index 0000000..5787562 --- /dev/null +++ b/reqs/swe/SWE-019.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'UDS Service 0x22 ReadDataByIdentifier' +level: 1.19 +normative: true +reviewed: null +links: + - SYS-009 +asil: QM +--- + +# SWE-019: UDS Service 0x22 ReadDataByIdentifier + +Folgende DIDs muessen lesbar sein: 0xF187 (SW-Version), 0x0301 (Klemmkraft links), 0x0302 (Klemmkraft rechts). diff --git a/reqs/swe/SWE-020.md b/reqs/swe/SWE-020.md new file mode 100644 index 0000000..6221377 --- /dev/null +++ b/reqs/swe/SWE-020.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'LED-Steuerung' +level: 1.20 +normative: true +reviewed: null +links: + - SYS-010 +asil: QM +--- + +# SWE-020: LED-Steuerung + +Apply-aktiv: LED dauerleuchtend. Release: LED aus. Fehler: LED blinkt 4 Hz. Service-Mode: LED blinkt 2 Hz. diff --git a/reqs/swe/SWE-021.md b/reqs/swe/SWE-021.md new file mode 100644 index 0000000..3546f3f --- /dev/null +++ b/reqs/swe/SWE-021.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'CAN-Status-Frame' +level: 1.21 +normative: true +reviewed: null +links: + - SYS-010 +asil: QM +--- + +# SWE-021: CAN-Status-Frame + +Status-Frame 0x3A0 mit 50 Hz: Byte 0 = Status (0=Released, 1=Applied, 2=Applying, 3=Releasing, 0xFF=Error), Byte 1-2 = Klemmkraft links, Byte 3-4 = Klemmkraft rechts. diff --git a/reqs/swe/SWE-022.md b/reqs/swe/SWE-022.md new file mode 100644 index 0000000..428f424 --- /dev/null +++ b/reqs/swe/SWE-022.md @@ -0,0 +1,17 @@ +--- +active: true +derived: false +header: 'Stillstands-Erkennung aus Wheel Speeds' +level: 1.22 +normative: true +reviewed: null +links: + - SYS-001 + - SYS-002 + - SYS-006 +asil: B +--- + +# SWE-022: Stillstands-Erkennung aus Wheel Speeds + +Stillstand ist erkannt, wenn alle 4 Wheel-Speed-Signale fuer mindestens 200 ms unter 0.5 km/h liegen. diff --git a/reqs/swe/SWE-023.md b/reqs/swe/SWE-023.md new file mode 100644 index 0000000..acf1f48 --- /dev/null +++ b/reqs/swe/SWE-023.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Wheel Speed Plausibilisierung' +level: 1.23 +normative: true +reviewed: null +links: + - SYS-007 +asil: B +--- + +# SWE-023: Wheel Speed Plausibilisierung + +Spreizung der Wheel-Speed-Signale: bei Geradeaus-Fahrt darf die Differenz nicht > 3 km/h sein. Andernfalls Sensor-Fehler-DTC. diff --git a/reqs/swe/SWE-024.md b/reqs/swe/SWE-024.md new file mode 100644 index 0000000..a2b674d --- /dev/null +++ b/reqs/swe/SWE-024.md @@ -0,0 +1,15 @@ +--- +active: true +derived: false +header: 'Inclinometer Tiefpass-Filter' +level: 1.24 +normative: true +reviewed: null +links: + - SYS-005 +asil: B +--- + +# SWE-024: Inclinometer Tiefpass-Filter + +Das Roh-Neigungssignal muss mit einem Tiefpass 1. Ordnung (Zeitkonstante 200 ms) gefiltert werden, bevor es zur Hill-Hold-Bewertung verwendet wird. diff --git a/reqs/swe/SWE-025.md b/reqs/swe/SWE-025.md new file mode 100644 index 0000000..c1487dc --- /dev/null +++ b/reqs/swe/SWE-025.md @@ -0,0 +1,16 @@ +--- +active: true +derived: false +header: 'Switch-Debouncing' +level: 1.25 +normative: true +reviewed: null +links: + - SYS-002 + - SYS-003 +asil: QM +--- + +# SWE-025: Switch-Debouncing + +Der EPB-Schalter muss mit einer Entprell-Zeit von 50 ms entprellt werden. Stabiler Pegel = Eingangssignal fuer Apply-Controller. diff --git a/reqs/sys/SYS-001.md b/reqs/sys/SYS-001.md new file mode 100644 index 0000000..5a2b5f3 --- /dev/null +++ b/reqs/sys/SYS-001.md @@ -0,0 +1,16 @@ +--- +active: true +derived: false +header: 'Halten der Parkbremse im Stillstand' +level: 1.1 +normative: true +reviewed: null +links: [] +asil: D +--- + +# SYS-001: Halten der Parkbremse im Stillstand + +Wenn die Parkbremse aktiviert ist und das Fahrzeug stillsteht, muss das EPB-System die mechanische Klemmkraft an beiden hinteren Bremssaetteln aufrecht erhalten, bis ein Loesen ausdruecklich angefordert wird. Sicherheitsziel: SG-01. + +**Verifikation:** SiL-Test mit Auf-/Ab-Hangelung, Klemmkraftmessung. diff --git a/reqs/sys/SYS-002.md b/reqs/sys/SYS-002.md new file mode 100644 index 0000000..c256486 --- /dev/null +++ b/reqs/sys/SYS-002.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'Apply auf Fahrer-Anforderung' +level: 1.2 +normative: true +reviewed: null +links: [] +asil: D +--- + +# SYS-002: Apply auf Fahrer-Anforderung + +Bei Betaetigung des EPB-Schalters in Apply-Richtung muss das System innerhalb von 800 ms die Parkbremse anlegen, sofern die Voraussetzungen erfuellt sind (Stillstand oder Geschwindigkeit unter 5 km/h). Sicherheitsziel: SG-01. diff --git a/reqs/sys/SYS-003.md b/reqs/sys/SYS-003.md new file mode 100644 index 0000000..9c2ff44 --- /dev/null +++ b/reqs/sys/SYS-003.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'Release auf Fahrer-Anforderung' +level: 1.3 +normative: true +reviewed: null +links: [] +asil: B +--- + +# SYS-003: Release auf Fahrer-Anforderung + +Bei Betaetigung des EPB-Schalters in Release-Richtung muss das System die Parkbremse loesen, sofern die folgenden Voraussetzungen erfuellt sind: Motor laeuft, Fahrer betaetigt Bremspedal, Gang ist eingelegt. Maximalzeit fuer Loesen: 1500 ms. diff --git a/reqs/sys/SYS-004.md b/reqs/sys/SYS-004.md new file mode 100644 index 0000000..0c7ab76 --- /dev/null +++ b/reqs/sys/SYS-004.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'Auto-Apply bei Motor-Aus' +level: 1.4 +normative: true +reviewed: null +links: [] +asil: D +--- + +# SYS-004: Auto-Apply bei Motor-Aus + +Wenn der Motor ausgeschaltet wird und das Fahrzeug stillsteht und keine Parkbremse aktiv ist, muss das System die Parkbremse spaetestens 2 s nach Erkennung Motor-Aus automatisch anlegen. Sicherheitsziel: SG-01. diff --git a/reqs/sys/SYS-005.md b/reqs/sys/SYS-005.md new file mode 100644 index 0000000..f9c5933 --- /dev/null +++ b/reqs/sys/SYS-005.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'Hill-Hold am Berg' +level: 1.5 +normative: true +reviewed: null +links: [] +asil: D +--- + +# SYS-005: Hill-Hold am Berg + +Bei aktivem Hill-Hold (Fahrzeug steht am Hang mit Neigung > 5%, Fahrer betaetigt Bremspedal) uebernimmt das EPB-System die Bremskraft beim Loesen des Bremspedals und haelt diese, bis die Anfahrt erkannt wird. Sicherheitsziel: SG-01. diff --git a/reqs/sys/SYS-006.md b/reqs/sys/SYS-006.md new file mode 100644 index 0000000..b325dfc --- /dev/null +++ b/reqs/sys/SYS-006.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'Auto-Release beim Anfahren (Drive-Away-Assist)' +level: 1.6 +normative: true +reviewed: null +links: [] +asil: B +--- + +# SYS-006: Auto-Release beim Anfahren (Drive-Away-Assist) + +Wenn die Parkbremse aktiv ist und der Fahrer Anfahrabsicht zeigt (Gaspedal-Betaetigung bei eingelegtem Gang), muss das System die Parkbremse innerhalb von 500 ms loesen. Voraussetzung: alle Sicherheitskriterien (Fahrertuer geschlossen, Sicherheitsgurt) erfuellt. diff --git a/reqs/sys/SYS-007.md b/reqs/sys/SYS-007.md new file mode 100644 index 0000000..6e2cb6a --- /dev/null +++ b/reqs/sys/SYS-007.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'Aktor-Stromueberwachung' +level: 1.7 +normative: true +reviewed: null +links: [] +asil: B +--- + +# SYS-007: Aktor-Stromueberwachung + +Das System muss den Motorstrom jedes Aktors mit mindestens 1 kHz ueberwachen und bei Ueberschreitung von 8 A fuer mehr als 100 ms den Aktor abschalten und einen DTC setzen. Sicherheitsziel: SG-03. diff --git a/reqs/sys/SYS-008.md b/reqs/sys/SYS-008.md new file mode 100644 index 0000000..a75665a --- /dev/null +++ b/reqs/sys/SYS-008.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'Service-Modus fuer Werkstatt' +level: 1.8 +normative: true +reviewed: null +links: [] +asil: QM +--- + +# SYS-008: Service-Modus fuer Werkstatt + +Das System muss ueber UDS RoutineControl (Service 0x31) einen Service-Modus bereitstellen, in dem die Aktoren manuell in Wartungs-Position gefahren werden koennen (z.B. fuer Bremsbelag-Wechsel). diff --git a/reqs/sys/SYS-009.md b/reqs/sys/SYS-009.md new file mode 100644 index 0000000..61caff9 --- /dev/null +++ b/reqs/sys/SYS-009.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'UDS-Diagnose' +level: 1.9 +normative: true +reviewed: null +links: [] +asil: QM +--- + +# SYS-009: UDS-Diagnose + +Das System muss UDS-Diagnose nach ISO 14229 bereitstellen: ReadDTC (0x19), ReadDataByIdentifier (0x22), RoutineControl (0x31), ECUReset (0x11). Tester-Adresse 0x712, Antwort-Adresse 0x71A. diff --git a/reqs/sys/SYS-010.md b/reqs/sys/SYS-010.md new file mode 100644 index 0000000..2e2baf9 --- /dev/null +++ b/reqs/sys/SYS-010.md @@ -0,0 +1,14 @@ +--- +active: true +derived: false +header: 'HMI-Statusanzeige' +level: 1.10 +normative: true +reviewed: null +links: [] +asil: QM +--- + +# SYS-010: HMI-Statusanzeige + +Der EPB-Status muss dem Fahrer signalisiert werden: LED am Schalter (an = Apply, aus = Release, blinkend = Fehler) sowie Text im Kombi-Display via CAN-Bus (Frame-ID 0x3A0, 50 Hz). diff --git a/src/actuator_driver.c b/src/actuator_driver.c new file mode 100644 index 0000000..91887cf --- /dev/null +++ b/src/actuator_driver.c @@ -0,0 +1,125 @@ +/** + * @file actuator_driver.c + * @brief Implementierung der EPB-Aktor-Ansteuerung. + * + * @arch SWA-003 + * @reqs SWE-006 SWE-013 SWE-014 SWE-015 + * + * ASIL: B. + */ +#include "actuator_driver.h" + +typedef struct { + ActuatorStatus status; + uint16_t over_ms; /* Millisekunden ueber Strom-Limit (zaehlt in 1 kHz ISR) */ +} ActuatorCtx; + +static ActuatorCtx s_ctx[ACTUATOR_COUNT]; + +static bool is_valid_id(ActuatorId id) +{ + return (id == ACTUATOR_LEFT) || (id == ACTUATOR_RIGHT); +} + +EpbStatus actuator_init(void) +{ + for (uint8_t i = 0U; i < ACTUATOR_COUNT; ++i) { + s_ctx[i].status.direction = ACT_DIR_STOP; + s_ctx[i].status.pwm_percent = 0U; + s_ctx[i].status.current_ma = 0U; + s_ctx[i].status.peak_current_ma = 0U; + s_ctx[i].status.clamping_force_n = 0U; + s_ctx[i].status.overcurrent = false; + s_ctx[i].status.last_error = EPB_OK; + s_ctx[i].over_ms = 0U; + } + return EPB_OK; +} + +EpbStatus actuator_apply(ActuatorId id, uint8_t pwm_percent) +{ + if (!is_valid_id(id)) { + return EPB_EINVAL; + } + if (pwm_percent > 100U) { + return EPB_EINVAL; + } + if (s_ctx[id].status.overcurrent) { + return EPB_EOVERCURRENT; + } + s_ctx[id].status.direction = ACT_DIR_APPLY; + s_ctx[id].status.pwm_percent = pwm_percent; + s_ctx[id].status.peak_current_ma = 0U; + return EPB_OK; +} + +EpbStatus actuator_release(ActuatorId id, uint8_t pwm_percent) +{ + if (!is_valid_id(id)) { + return EPB_EINVAL; + } + if (pwm_percent > 100U) { + return EPB_EINVAL; + } + if (s_ctx[id].status.overcurrent) { + return EPB_EOVERCURRENT; + } + s_ctx[id].status.direction = ACT_DIR_RELEASE; + s_ctx[id].status.pwm_percent = pwm_percent; + return EPB_OK; +} + +EpbStatus actuator_stop(ActuatorId id) +{ + if (!is_valid_id(id)) { + return EPB_EINVAL; + } + s_ctx[id].status.direction = ACT_DIR_STOP; + s_ctx[id].status.pwm_percent = 0U; + return EPB_OK; +} + +ActuatorStatus actuator_get_status(ActuatorId id) +{ + if (!is_valid_id(id)) { + ActuatorStatus empty = {0}; + empty.last_error = EPB_EINVAL; + return empty; + } + return s_ctx[id].status; +} + +void actuator_isr_1khz(ActuatorId id, uint16_t current_sample_ma) +{ + if (!is_valid_id(id)) { + return; + } + + s_ctx[id].status.current_ma = current_sample_ma; + if (current_sample_ma > s_ctx[id].status.peak_current_ma) { + s_ctx[id].status.peak_current_ma = current_sample_ma; + } + + /* SWE-014: Overcurrent-Cutoff bei > 8 A fuer > 100 ms */ + if (current_sample_ma > ACT_OVERCURRENT_LIMIT_MA) { + if (s_ctx[id].over_ms < UINT16_MAX) { + ++s_ctx[id].over_ms; + } + if (s_ctx[id].over_ms >= ACT_OVERCURRENT_WINDOW_MS) { + s_ctx[id].status.direction = ACT_DIR_STOP; + s_ctx[id].status.pwm_percent = 0U; + s_ctx[id].status.overcurrent = true; + s_ctx[id].status.last_error = EPB_EOVERCURRENT; + } + } else { + s_ctx[id].over_ms = 0U; + } + + /* SWE-015: Klemmkraft aus Peak-Strom schaetzen (nur bei Apply). */ + if (s_ctx[id].status.direction == ACT_DIR_APPLY) { + const uint32_t force = ((uint32_t)s_ctx[id].status.peak_current_ma + * ACT_FORCE_PER_AMP_N) / 1000U; + s_ctx[id].status.clamping_force_n = + (force > UINT16_MAX) ? UINT16_MAX : (uint16_t)force; + } +} diff --git a/src/actuator_driver.h b/src/actuator_driver.h new file mode 100644 index 0000000..bb7cae9 --- /dev/null +++ b/src/actuator_driver.h @@ -0,0 +1,49 @@ +/** + * @file actuator_driver.h + * @brief Low-Level-Ansteuerung der EPB-Aktoren. + * + * @arch SWA-003 + * @reqs SWE-006 SWE-013 SWE-014 SWE-015 + * + * ASIL: B + */ +#ifndef ACTUATOR_DRIVER_H +#define ACTUATOR_DRIVER_H + +#include "epb_types.h" + +typedef enum { + ACT_DIR_STOP = 0, + ACT_DIR_APPLY = 1, + ACT_DIR_RELEASE = 2 +} ActuatorDirection; + +typedef struct { + ActuatorDirection direction; + uint8_t pwm_percent; /* 0..100 */ + uint16_t current_ma; /* aktueller Motorstrom */ + uint16_t peak_current_ma; + uint16_t clamping_force_n; /* geschaetzt aus Strom */ + bool overcurrent; + EpbStatus last_error; +} ActuatorStatus; + +/** Strom-Limit (Spec) und Zeitfenster fuer Cutoff (Spec). */ +#define ACT_OVERCURRENT_LIMIT_MA 8000U +#define ACT_OVERCURRENT_WINDOW_MS 100U +#define ACT_FORCE_PER_AMP_N 2500U /* lineare Naeherung */ + +EpbStatus actuator_init(void); +EpbStatus actuator_apply(ActuatorId id, uint8_t pwm_percent); +EpbStatus actuator_release(ActuatorId id, uint8_t pwm_percent); +EpbStatus actuator_stop(ActuatorId id); +ActuatorStatus actuator_get_status(ActuatorId id); + +/** + * @brief ISR-Hook fuer Strom-Sampling. Wird mit 1 kHz aufgerufen. + * + * Fuer Tests durch Test-Doubles ersetzt. + */ +void actuator_isr_1khz(ActuatorId id, uint16_t current_sample_ma); + +#endif /* ACTUATOR_DRIVER_H */ diff --git a/src/apply_controller.c b/src/apply_controller.c new file mode 100644 index 0000000..147099d --- /dev/null +++ b/src/apply_controller.c @@ -0,0 +1,153 @@ +/** + * @file apply_controller.c + * @brief Apply/Hold/Release State Machine. + * + * @arch SWA-002 + * @reqs SWE-001 SWE-002 SWE-003 SWE-004 + * + * ASIL: D. Diese Komponente ist die sicherheitskritische Kernlogik. + * Aenderungen erfordern Technical Review mit 2 Approvals. + */ +#include + +#include "apply_controller.h" +#include "actuator_driver.h" + +typedef struct { + EpbState state; + uint8_t step_in_state; /* 50ms-Ticks im aktuellen State */ + EpbStatus last_error; + uint32_t step_count; /* Watchdog-Alive-Counter */ +} ApplyCtx; + +static ApplyCtx s_ctx; + +static void enter_state(EpbState new_state) +{ + s_ctx.state = new_state; + s_ctx.step_in_state = 0U; +} + +static bool release_preconditions_ok(const ApplyInputs* in) +{ + /* @reqs SWE-005 (Release-Voraussetzungen) — hier konsumiert */ + return in->engine_running + && in->brake_pedal_pressed + && in->gear_engaged; +} + +static bool apply_request_present(const ApplyInputs* in) +{ + return (in->sw_state == SWITCH_APPLY) || in->safety_apply_request; +} + +static bool release_request_present(const ApplyInputs* in) +{ + return in->sw_state == SWITCH_RELEASE; +} + +static uint16_t min_force(const ApplyInputs* in) +{ + return (in->left_force_n < in->right_force_n) + ? in->left_force_n : in->right_force_n; +} + +EpbStatus apply_ctrl_init(void) +{ + s_ctx.state = EPB_STATE_RELEASED; + s_ctx.step_in_state = 0U; + s_ctx.last_error = EPB_OK; + s_ctx.step_count = 0U; + return EPB_OK; +} + +void apply_ctrl_step_50ms(const ApplyInputs* in) +{ + if (in == NULL) { + s_ctx.last_error = EPB_EINVAL; + return; + } + + /* SWE-002: Watchdog-Alive-Counter erhoehen */ + ++s_ctx.step_count; + + if (s_ctx.step_in_state < UINT8_MAX) { + ++s_ctx.step_in_state; + } + + switch (s_ctx.state) { + case EPB_STATE_RELEASED: + if (apply_request_present(in) && in->standstill) { + (void)actuator_apply(ACTUATOR_LEFT, 80U); + (void)actuator_apply(ACTUATOR_RIGHT, 80U); + enter_state(EPB_STATE_APPLYING); + } + break; + + case EPB_STATE_APPLYING: + /* SWE-004: Klemmkraft-Erreichen pruefen */ + if (min_force(in) >= APPLY_TARGET_FORCE_N) { + (void)actuator_stop(ACTUATOR_LEFT); + (void)actuator_stop(ACTUATOR_RIGHT); + enter_state(EPB_STATE_APPLIED); + } else if (s_ctx.step_in_state >= APPLY_TIMEOUT_50MS) { + s_ctx.last_error = EPB_ETIMEOUT; + (void)actuator_stop(ACTUATOR_LEFT); + (void)actuator_stop(ACTUATOR_RIGHT); + enter_state(EPB_STATE_ERROR); + } + break; + + case EPB_STATE_APPLIED: + /* SWE-001: Klemmkraft halten — bei Unterschreitung nachregeln */ + if (min_force(in) < (APPLY_TARGET_FORCE_N - HOLD_TOLERANCE_N)) { + (void)actuator_apply(ACTUATOR_LEFT, 60U); + (void)actuator_apply(ACTUATOR_RIGHT, 60U); + enter_state(EPB_STATE_APPLYING); + break; + } + if (release_request_present(in) && release_preconditions_ok(in)) { + (void)actuator_release(ACTUATOR_LEFT, 80U); + (void)actuator_release(ACTUATOR_RIGHT, 80U); + enter_state(EPB_STATE_RELEASING); + } + break; + + case EPB_STATE_RELEASING: + if (min_force(in) < HOLD_TOLERANCE_N) { + (void)actuator_stop(ACTUATOR_LEFT); + (void)actuator_stop(ACTUATOR_RIGHT); + enter_state(EPB_STATE_RELEASED); + } else if (s_ctx.step_in_state >= (APPLY_TIMEOUT_50MS - 6U)) { + s_ctx.last_error = EPB_ETIMEOUT; + (void)actuator_stop(ACTUATOR_LEFT); + (void)actuator_stop(ACTUATOR_RIGHT); + enter_state(EPB_STATE_ERROR); + } + break; + + case EPB_STATE_ERROR: + default: + /* Auf Reset warten; sicherer Zustand ist Apply, also kein Release */ + if (!apply_request_present(in) && !release_request_present(in)) { + s_ctx.last_error = EPB_OK; + enter_state(EPB_STATE_RELEASED); + } + break; + } +} + +EpbState apply_ctrl_get_state(void) +{ + return s_ctx.state; +} + +EpbStatus apply_ctrl_last_error(void) +{ + return s_ctx.last_error; +} + +uint32_t apply_ctrl_get_step_count(void) +{ + return s_ctx.step_count; +} diff --git a/src/apply_controller.h b/src/apply_controller.h new file mode 100644 index 0000000..3a708a2 --- /dev/null +++ b/src/apply_controller.h @@ -0,0 +1,43 @@ +/** + * @file apply_controller.h + * @brief Apply/Hold/Release-Steuerung der EPB. + * + * @arch SWA-002 + * @reqs SWE-001 SWE-002 SWE-003 SWE-004 + * + * ASIL: D + */ +#ifndef APPLY_CONTROLLER_H +#define APPLY_CONTROLLER_H + +#include "epb_types.h" + +typedef struct { + SwitchState sw_state; /* aus SwitchDebouncer */ + bool standstill; /* aus Wheel-Speed-Plausi */ + bool engine_running; + bool brake_pedal_pressed; + bool gear_engaged; + bool safety_apply_request; /* aus SafetyManager */ + uint16_t left_force_n; + uint16_t right_force_n; +} ApplyInputs; + +#define APPLY_TARGET_FORCE_N 12000U /* Ziel-Klemmkraft je Aktor */ +#define APPLY_TIMEOUT_50MS 30U /* 30 * 50ms = 1500 ms */ +#define HOLD_TOLERANCE_N 1200U /* 10% von Ziel */ + +EpbStatus apply_ctrl_init(void); + +/** + * @brief Step-Funktion 50 ms. + * + * @reqs SWE-001 SWE-002 SWE-003 SWE-004 + */ +void apply_ctrl_step_50ms(const ApplyInputs* in); + +EpbState apply_ctrl_get_state(void); +EpbStatus apply_ctrl_last_error(void); +uint32_t apply_ctrl_get_step_count(void); /* Watchdog-Alive-Counter */ + +#endif /* APPLY_CONTROLLER_H */ diff --git a/src/epb_types.h b/src/epb_types.h new file mode 100644 index 0000000..27a3a4a --- /dev/null +++ b/src/epb_types.h @@ -0,0 +1,47 @@ +/** + * @file epb_types.h + * @brief Gemeinsame Typen fuer die EPB-Software. + * + * @arch SA-001 + */ +#ifndef EPB_TYPES_H +#define EPB_TYPES_H + +#include +#include + +typedef enum { + EPB_OK = 0, + EPB_EINVAL = 1, + EPB_ETIMEOUT = 2, + EPB_ENOSPACE = 3, + EPB_EHARDWARE = 4, + EPB_EOVERCURRENT = 5 +} EpbStatus; + +typedef enum { + EPB_STATE_RELEASED = 0, + EPB_STATE_APPLYING = 1, + EPB_STATE_APPLIED = 2, + EPB_STATE_RELEASING = 3, + EPB_STATE_ERROR = 0xFF +} EpbState; + +typedef enum { + ACTUATOR_LEFT = 0, + ACTUATOR_RIGHT = 1, + ACTUATOR_COUNT = 2 +} ActuatorId; + +typedef enum { + SWITCH_NEUTRAL = 0, + SWITCH_APPLY = 1, + SWITCH_RELEASE = 2 +} SwitchState; + +typedef struct { + uint8_t apply_raw : 1; + uint8_t release_raw : 1; +} SwitchRaw; + +#endif /* EPB_TYPES_H */ diff --git a/src/stubs/README.md b/src/stubs/README.md new file mode 100644 index 0000000..3e61ede --- /dev/null +++ b/src/stubs/README.md @@ -0,0 +1,18 @@ +# Stubs — nicht implementierte Komponenten + +Diese Komponenten sind in der Software-Architektur (`arch/swe/`) vollstaendig +spezifiziert, aber in dieser Demo nicht ausimplementiert. Sie zeigen nur die +Header-Schnittstellen. + +Im Real-Projekt wuerden hier vollstaendige `.c`-Implementierungen plus +Unit-Tests stehen. + +| Komponente | ASIL | SWA | +|-------------------------|------|-----------| +| Safety Manager | D | SWA-001 | +| Wheel Speed Plausi | B | SWA-004 | +| Inclinometer Filter | B | SWA-005 | +| Display Manager | QM | SWA-007 | +| Diagnostic Manager | QM | SWA-008 | +| Service Mode | QM | SWA-009 | +| Logger | QM | SWA-010 | diff --git a/src/stubs/diag_manager.h b/src/stubs/diag_manager.h new file mode 100644 index 0000000..ed9a8a1 --- /dev/null +++ b/src/stubs/diag_manager.h @@ -0,0 +1,19 @@ +/** + * @file diag_manager.h + * @brief UDS-Diagnose nach ISO 14229. + * + * @arch SWA-008 + * @reqs SWE-018 SWE-019 + * + * ASIL: QM. STUB. + */ +#ifndef DIAG_MANAGER_H +#define DIAG_MANAGER_H + +#include "../epb_types.h" + +EpbStatus diag_init(void); +void diag_handle_request(const uint8_t* data, uint16_t len); +void diag_set_dtc(uint16_t dtc_id); + +#endif diff --git a/src/stubs/display_manager.h b/src/stubs/display_manager.h new file mode 100644 index 0000000..b75ceea --- /dev/null +++ b/src/stubs/display_manager.h @@ -0,0 +1,19 @@ +/** + * @file display_manager.h + * @brief LED + CAN-Status-Frame Steuerung. + * + * @arch SWA-007 + * @reqs SWE-020 SWE-021 + * + * ASIL: QM. STUB. + */ +#ifndef DISPLAY_MANAGER_H +#define DISPLAY_MANAGER_H + +#include "../epb_types.h" + +EpbStatus display_init(void); +void display_set_status(EpbState s); +void display_step_20ms(void); + +#endif diff --git a/src/stubs/inclinometer.h b/src/stubs/inclinometer.h new file mode 100644 index 0000000..63c2945 --- /dev/null +++ b/src/stubs/inclinometer.h @@ -0,0 +1,19 @@ +/** + * @file inclinometer.h + * @brief Inclinometer-Tiefpass-Filter. + * + * @arch SWA-005 + * @reqs SWE-024 + * + * ASIL: B. STUB. + */ +#ifndef INCLINOMETER_H +#define INCLINOMETER_H + +#include "../epb_types.h" + +EpbStatus inclino_init(void); +void inclino_step_10ms(int16_t raw_mdeg); +float inclino_get_grade_percent(void); + +#endif diff --git a/src/stubs/logger.h b/src/stubs/logger.h new file mode 100644 index 0000000..3e1a5e1 --- /dev/null +++ b/src/stubs/logger.h @@ -0,0 +1,24 @@ +/** + * @file logger.h + * @brief Logger — Ringpuffer + EEPROM-Persistenz. + * + * @arch SWA-010 + * + * ASIL: QM. STUB. + */ +#ifndef LOGGER_H +#define LOGGER_H + +#include "../epb_types.h" + +typedef enum { + LOG_DEBUG = 0, + LOG_INFO, + LOG_WARN, + LOG_ERROR +} LogLevel; + +EpbStatus log_init(void); +void log_event(LogLevel lvl, uint16_t event_id, uint32_t param); + +#endif diff --git a/src/stubs/safety_manager.h b/src/stubs/safety_manager.h new file mode 100644 index 0000000..9db5241 --- /dev/null +++ b/src/stubs/safety_manager.h @@ -0,0 +1,27 @@ +/** + * @file safety_manager.h + * @brief Safety Manager — Hill-Hold + Auto-Apply Logik. + * + * @arch SWA-001 + * @reqs SWE-007 SWE-008 SWE-009 SWE-010 + * + * ASIL: D. STUB — nicht implementiert in dieser Demo. + */ +#ifndef SAFETY_MANAGER_H +#define SAFETY_MANAGER_H + +#include "../epb_types.h" + +typedef struct { + bool engine_running; + bool brake_pedal_pressed; + float vehicle_speed_kmh; + float grade_percent; + EpbState current_state; +} SafetyInputs; + +EpbStatus safety_mgr_init(void); +void safety_mgr_step_50ms(const SafetyInputs* in); +bool safety_mgr_apply_requested(void); + +#endif diff --git a/src/stubs/service_mode.h b/src/stubs/service_mode.h new file mode 100644 index 0000000..4ae7d10 --- /dev/null +++ b/src/stubs/service_mode.h @@ -0,0 +1,20 @@ +/** + * @file service_mode.h + * @brief Service-Modus fuer Werkstatt (Bremsbelag-Wechsel). + * + * @arch SWA-009 + * @reqs SWE-016 SWE-017 + * + * ASIL: QM. STUB. + */ +#ifndef SERVICE_MODE_H +#define SERVICE_MODE_H + +#include "../epb_types.h" + +EpbStatus service_mode_init(void); +EpbStatus service_mode_activate(void); +EpbStatus service_mode_deactivate(void); +bool service_mode_is_active(void); + +#endif diff --git a/src/stubs/wheel_speed_plausi.h b/src/stubs/wheel_speed_plausi.h new file mode 100644 index 0000000..c448206 --- /dev/null +++ b/src/stubs/wheel_speed_plausi.h @@ -0,0 +1,27 @@ +/** + * @file wheel_speed_plausi.h + * @brief Wheel-Speed-Plausibilisierung + Stillstands-Erkennung. + * + * @arch SWA-004 + * @reqs SWE-022 SWE-023 + * + * ASIL: B. STUB. + */ +#ifndef WHEEL_SPEED_PLAUSI_H +#define WHEEL_SPEED_PLAUSI_H + +#include "../epb_types.h" + +typedef struct { + float fl_kmh; + float fr_kmh; + float rl_kmh; + float rr_kmh; +} WheelInputs; + +EpbStatus wheel_speed_init(void); +void wheel_speed_step_10ms(const WheelInputs* in); +bool wheel_speed_is_standstill(void); +float wheel_speed_get_vehicle(void); + +#endif diff --git a/src/switch_debouncer.c b/src/switch_debouncer.c new file mode 100644 index 0000000..3599896 --- /dev/null +++ b/src/switch_debouncer.c @@ -0,0 +1,60 @@ +/** + * @file switch_debouncer.c + * @brief Implementierung des EPB-Schalter-Debouncers. + * + * @arch SWA-006 + * @reqs SWE-025 + * + * ASIL: QM. + */ +#include "switch_debouncer.h" + +typedef struct { + SwitchState current; + SwitchState candidate; + uint8_t candidate_count; +} DebouncerCtx; + +static DebouncerCtx s_ctx; + +static SwitchState raw_to_candidate(SwitchRaw raw) +{ + if (raw.apply_raw && !raw.release_raw) { + return SWITCH_APPLY; + } + if (raw.release_raw && !raw.apply_raw) { + return SWITCH_RELEASE; + } + return SWITCH_NEUTRAL; +} + +EpbStatus switch_init(void) +{ + s_ctx.current = SWITCH_NEUTRAL; + s_ctx.candidate = SWITCH_NEUTRAL; + s_ctx.candidate_count = 0U; + return EPB_OK; +} + +void switch_step_10ms(SwitchRaw raw) +{ + const SwitchState observed = raw_to_candidate(raw); + + if (observed == s_ctx.candidate) { + if (s_ctx.candidate_count < SWITCH_DEBOUNCE_SAMPLES) { + ++s_ctx.candidate_count; + } + } else { + s_ctx.candidate = observed; + s_ctx.candidate_count = 1U; + } + + if (s_ctx.candidate_count >= SWITCH_DEBOUNCE_SAMPLES) { + s_ctx.current = s_ctx.candidate; + } +} + +SwitchState switch_get_state(void) +{ + return s_ctx.current; +} diff --git a/src/switch_debouncer.h b/src/switch_debouncer.h new file mode 100644 index 0000000..7c84afb --- /dev/null +++ b/src/switch_debouncer.h @@ -0,0 +1,22 @@ +/** + * @file switch_debouncer.h + * @brief EPB-Schalter mit Software-Entprellung. + * + * @arch SWA-006 + * @reqs SWE-025 + * + * ASIL: QM + */ +#ifndef SWITCH_DEBOUNCER_H +#define SWITCH_DEBOUNCER_H + +#include "epb_types.h" + +/** Step-Zyklus 10 ms. Debounce-Schwelle 50 ms (5 stabile Samples). */ +#define SWITCH_DEBOUNCE_SAMPLES 5 + +EpbStatus switch_init(void); +void switch_step_10ms(SwitchRaw raw); +SwitchState switch_get_state(void); + +#endif /* SWITCH_DEBOUNCER_H */ diff --git a/tests/unit/test_actuator_driver.c b/tests/unit/test_actuator_driver.c new file mode 100644 index 0000000..250f18f --- /dev/null +++ b/tests/unit/test_actuator_driver.c @@ -0,0 +1,157 @@ +/** + * @file test_actuator_driver.c + * @brief Unit-Tests fuer den Actuator-Driver. + * + * @reqs SWE-006 SWE-013 SWE-014 SWE-015 + * @arch SWA-003 + */ +#include "../unit_test_framework.h" +#include "../../src/actuator_driver.h" + +TestStats g_test_stats = {0, 0}; + +static void test_init(void) +{ + TEST_BEGIN("init -> beide Aktoren STOP, 0 mA"); + ASSERT_EQ(actuator_init(), EPB_OK); + ActuatorStatus l = actuator_get_status(ACTUATOR_LEFT); + ActuatorStatus r = actuator_get_status(ACTUATOR_RIGHT); + ASSERT_EQ(l.direction, ACT_DIR_STOP); + ASSERT_EQ(r.direction, ACT_DIR_STOP); + ASSERT_EQ(l.current_ma, 0); + TEST_END(); +} + +static void test_apply_invalid_id(void) +{ + TEST_BEGIN("apply: invalid ID -> EINVAL"); + (void)actuator_init(); + ASSERT_EQ(actuator_apply((ActuatorId)42, 50), EPB_EINVAL); + TEST_END(); +} + +static void test_apply_pwm_out_of_range(void) +{ + TEST_BEGIN("apply: PWM > 100% -> EINVAL"); + (void)actuator_init(); + ASSERT_EQ(actuator_apply(ACTUATOR_LEFT, 101), EPB_EINVAL); + TEST_END(); +} + +static void test_apply_normal(void) +{ + TEST_BEGIN("apply normal -> direction=APPLY, pwm=80"); + (void)actuator_init(); + ASSERT_EQ(actuator_apply(ACTUATOR_LEFT, 80), EPB_OK); + ActuatorStatus s = actuator_get_status(ACTUATOR_LEFT); + ASSERT_EQ(s.direction, ACT_DIR_APPLY); + ASSERT_EQ(s.pwm_percent, 80); + TEST_END(); +} + +static void test_release_normal(void) +{ + TEST_BEGIN("release normal -> direction=RELEASE"); + (void)actuator_init(); + ASSERT_EQ(actuator_release(ACTUATOR_LEFT, 70), EPB_OK); + ActuatorStatus s = actuator_get_status(ACTUATOR_LEFT); + ASSERT_EQ(s.direction, ACT_DIR_RELEASE); + TEST_END(); +} + +static void test_isr_samples_current(void) +{ + TEST_BEGIN("ISR-Sample setzt current_ma und peak"); + (void)actuator_init(); + (void)actuator_apply(ACTUATOR_LEFT, 80); + actuator_isr_1khz(ACTUATOR_LEFT, 3000); + ActuatorStatus s = actuator_get_status(ACTUATOR_LEFT); + ASSERT_EQ(s.current_ma, 3000); + ASSERT_EQ(s.peak_current_ma, 3000); + TEST_END(); +} + +static void test_overcurrent_cutoff_after_100ms(void) +{ + TEST_BEGIN("Overcurrent > 8 A fuer 100 ms -> STOP + overcurrent flag"); + (void)actuator_init(); + (void)actuator_apply(ACTUATOR_LEFT, 80); + /* 100 Samples (= 100 ms bei 1 kHz) ueber 8 A */ + for (int i = 0; i < 100; ++i) { + actuator_isr_1khz(ACTUATOR_LEFT, 8500); + } + ActuatorStatus s = actuator_get_status(ACTUATOR_LEFT); + ASSERT_EQ(s.direction, ACT_DIR_STOP); + ASSERT_TRUE(s.overcurrent); + ASSERT_EQ(s.last_error, EPB_EOVERCURRENT); + TEST_END(); +} + +static void test_overcurrent_below_window_no_cutoff(void) +{ + TEST_BEGIN("Overcurrent < 100 ms loest noch nicht aus"); + (void)actuator_init(); + (void)actuator_apply(ACTUATOR_LEFT, 80); + for (int i = 0; i < 50; ++i) { + actuator_isr_1khz(ACTUATOR_LEFT, 8500); + } + ActuatorStatus s = actuator_get_status(ACTUATOR_LEFT); + ASSERT_EQ(s.direction, ACT_DIR_APPLY); + ASSERT_TRUE(!s.overcurrent); + TEST_END(); +} + +static void test_overcurrent_blocks_subsequent_apply(void) +{ + TEST_BEGIN("nach Overcurrent: weiterer apply -> EOVERCURRENT"); + (void)actuator_init(); + (void)actuator_apply(ACTUATOR_LEFT, 80); + for (int i = 0; i < 120; ++i) { + actuator_isr_1khz(ACTUATOR_LEFT, 9000); + } + EpbStatus rc = actuator_apply(ACTUATOR_LEFT, 80); + ASSERT_EQ(rc, EPB_EOVERCURRENT); + TEST_END(); +} + +static void test_clamping_force_estimate(void) +{ + TEST_BEGIN("Klemmkraft-Schaetzung aus Peak-Strom (SWE-015)"); + (void)actuator_init(); + (void)actuator_apply(ACTUATOR_LEFT, 80); + actuator_isr_1khz(ACTUATOR_LEFT, 4000); /* 4 A Peak */ + actuator_isr_1khz(ACTUATOR_LEFT, 3500); /* danach niedriger */ + ActuatorStatus s = actuator_get_status(ACTUATOR_LEFT); + /* F = (4000 * 2500) / 1000 = 10000 N */ + ASSERT_EQ(s.clamping_force_n, 10000); + TEST_END(); +} + +static void test_stop_clears_pwm(void) +{ + TEST_BEGIN("stop -> direction=STOP, pwm=0"); + (void)actuator_init(); + (void)actuator_apply(ACTUATOR_LEFT, 80); + (void)actuator_stop(ACTUATOR_LEFT); + ActuatorStatus s = actuator_get_status(ACTUATOR_LEFT); + ASSERT_EQ(s.direction, ACT_DIR_STOP); + ASSERT_EQ(s.pwm_percent, 0); + TEST_END(); +} + +int main(void) +{ + printf("== test_actuator_driver ==\n"); + test_init(); + test_apply_invalid_id(); + test_apply_pwm_out_of_range(); + test_apply_normal(); + test_release_normal(); + test_isr_samples_current(); + test_overcurrent_cutoff_after_100ms(); + test_overcurrent_below_window_no_cutoff(); + test_overcurrent_blocks_subsequent_apply(); + test_clamping_force_estimate(); + test_stop_clears_pwm(); + TEST_SUMMARY(); +} diff --git a/tests/unit/test_apply_controller.c b/tests/unit/test_apply_controller.c new file mode 100644 index 0000000..9aa573b --- /dev/null +++ b/tests/unit/test_apply_controller.c @@ -0,0 +1,240 @@ +/** + * @file test_apply_controller.c + * @brief Unit-Tests fuer den Apply-Controller (ASIL-D Kern). + * + * @reqs SWE-001 SWE-002 SWE-003 SWE-004 + * @arch SWA-002 + */ +#include "../unit_test_framework.h" +#include "../../src/apply_controller.h" +#include "../../src/actuator_driver.h" + +TestStats g_test_stats = {0, 0}; + +static ApplyInputs make_idle_inputs(void) +{ + ApplyInputs in = {0}; + in.sw_state = SWITCH_NEUTRAL; + in.standstill = true; + in.engine_running = true; + in.brake_pedal_pressed = true; + in.gear_engaged = true; + in.safety_apply_request = false; + in.left_force_n = 0; + in.right_force_n = 0; + return in; +} + +static void apply_full_cycle_until_state(ApplyInputs* in, EpbState target, int max_steps) +{ + for (int i = 0; i < max_steps; ++i) { + apply_ctrl_step_50ms(in); + if (apply_ctrl_get_state() == target) { + return; + } + } +} + +static void test_init(void) +{ + TEST_BEGIN("init -> RELEASED"); + (void)actuator_init(); + ASSERT_EQ(apply_ctrl_init(), EPB_OK); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_RELEASED); + TEST_END(); +} + +static void test_null_input(void) +{ + TEST_BEGIN("step_50ms(NULL) -> last_error=EINVAL"); + (void)actuator_init(); + (void)apply_ctrl_init(); + apply_ctrl_step_50ms(NULL); + ASSERT_EQ(apply_ctrl_last_error(), EPB_EINVAL); + TEST_END(); +} + +static void test_apply_request_starts_applying(void) +{ + TEST_BEGIN("Apply-Request bei Stillstand -> APPLYING"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_APPLYING); + TEST_END(); +} + +static void test_no_apply_without_standstill(void) +{ + TEST_BEGIN("Apply-Request ohne Stillstand wird ignoriert"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.standstill = false; + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_RELEASED); + TEST_END(); +} + +static void test_applying_reaches_applied_on_target_force(void) +{ + TEST_BEGIN("APPLYING -> APPLIED wenn beide Klemmkraefte >= Target"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); /* -> APPLYING */ + in.sw_state = SWITCH_NEUTRAL; + in.left_force_n = 12000; + in.right_force_n = 12000; + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_APPLIED); + TEST_END(); +} + +static void test_applying_timeout_to_error(void) +{ + TEST_BEGIN("APPLYING-Timeout (>30 Steps) -> ERROR"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); /* -> APPLYING */ + in.sw_state = SWITCH_NEUTRAL; + /* Klemmkraefte bleiben bei 0 -> Timeout nach 30 Steps */ + apply_full_cycle_until_state(&in, EPB_STATE_ERROR, 35); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_ERROR); + ASSERT_EQ(apply_ctrl_last_error(), EPB_ETIMEOUT); + TEST_END(); +} + +static void test_applied_holds_force(void) +{ + TEST_BEGIN("APPLIED haelt Klemmkraft (SWE-001)"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); + in.sw_state = SWITCH_NEUTRAL; + in.left_force_n = 12000; + in.right_force_n = 12000; + apply_ctrl_step_50ms(&in); /* -> APPLIED */ + + /* Klemmkraft sinkt auf 10000 (unter Toleranz-Schwelle 10800) */ + in.left_force_n = 10000; + in.right_force_n = 10000; + apply_ctrl_step_50ms(&in); + /* Apply-Controller muss nachregeln -> APPLYING */ + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_APPLYING); + TEST_END(); +} + +static void test_release_requires_preconditions(void) +{ + TEST_BEGIN("Release ohne Engine wird abgelehnt"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); + in.sw_state = SWITCH_NEUTRAL; + in.left_force_n = 12000; + in.right_force_n = 12000; + apply_ctrl_step_50ms(&in); /* -> APPLIED */ + + in.sw_state = SWITCH_RELEASE; + in.engine_running = false; + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_APPLIED); + TEST_END(); +} + +static void test_release_with_preconditions(void) +{ + TEST_BEGIN("Release mit allen Voraussetzungen -> RELEASING -> RELEASED"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); + in.sw_state = SWITCH_NEUTRAL; + in.left_force_n = 12000; + in.right_force_n = 12000; + apply_ctrl_step_50ms(&in); /* APPLIED */ + + in.sw_state = SWITCH_RELEASE; + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_RELEASING); + + in.sw_state = SWITCH_NEUTRAL; + in.left_force_n = 0; + in.right_force_n = 0; + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_RELEASED); + TEST_END(); +} + +static void test_safety_apply_request(void) +{ + TEST_BEGIN("Safety-Manager Apply-Request (Hill-Hold/Auto-Apply)"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_NEUTRAL; + in.safety_apply_request = true; + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_APPLYING); + TEST_END(); +} + +static void test_watchdog_alive_counter(void) +{ + TEST_BEGIN("Step-Counter steigt monoton (SWE-002 Watchdog-Alive)"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + uint32_t before = apply_ctrl_get_step_count(); + apply_ctrl_step_50ms(&in); + apply_ctrl_step_50ms(&in); + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_step_count(), before + 3); + TEST_END(); +} + +static void test_error_state_recoverable(void) +{ + TEST_BEGIN("ERROR -> RELEASED bei neutralem Switch"); + (void)actuator_init(); + (void)apply_ctrl_init(); + ApplyInputs in = make_idle_inputs(); + in.sw_state = SWITCH_APPLY; + apply_ctrl_step_50ms(&in); + in.sw_state = SWITCH_NEUTRAL; + apply_full_cycle_until_state(&in, EPB_STATE_ERROR, 35); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_ERROR); + apply_ctrl_step_50ms(&in); + ASSERT_EQ(apply_ctrl_get_state(), EPB_STATE_RELEASED); + TEST_END(); +} + +int main(void) +{ + printf("== test_apply_controller ==\n"); + test_init(); + test_null_input(); + test_apply_request_starts_applying(); + test_no_apply_without_standstill(); + test_applying_reaches_applied_on_target_force(); + test_applying_timeout_to_error(); + test_applied_holds_force(); + test_release_requires_preconditions(); + test_release_with_preconditions(); + test_safety_apply_request(); + test_watchdog_alive_counter(); + test_error_state_recoverable(); + TEST_SUMMARY(); +} diff --git a/tests/unit/test_switch_debouncer.c b/tests/unit/test_switch_debouncer.c new file mode 100644 index 0000000..7c31e2c --- /dev/null +++ b/tests/unit/test_switch_debouncer.c @@ -0,0 +1,91 @@ +/** + * @file test_switch_debouncer.c + * @brief Unit-Tests fuer den Switch-Debouncer. + * + * @reqs SWE-025 + * @arch SWA-006 + */ +#include "../unit_test_framework.h" +#include "../../src/switch_debouncer.h" + +TestStats g_test_stats = {0, 0}; + +static SwitchRaw mk(bool apply, bool release) +{ + SwitchRaw r = {0}; + r.apply_raw = apply ? 1U : 0U; + r.release_raw = release ? 1U : 0U; + return r; +} + +static void test_init_state_is_neutral(void) +{ + TEST_BEGIN("init -> neutral"); + (void)switch_init(); + ASSERT_EQ(switch_get_state(), SWITCH_NEUTRAL); + TEST_END(); +} + +static void test_debounce_apply_takes_5_samples(void) +{ + TEST_BEGIN("apply needs 5 stable samples (50 ms)"); + (void)switch_init(); + for (int i = 0; i < 4; ++i) { + switch_step_10ms(mk(true, false)); + ASSERT_EQ(switch_get_state(), SWITCH_NEUTRAL); + } + switch_step_10ms(mk(true, false)); + ASSERT_EQ(switch_get_state(), SWITCH_APPLY); + TEST_END(); +} + +static void test_debounce_release_takes_5_samples(void) +{ + TEST_BEGIN("release needs 5 stable samples (50 ms)"); + (void)switch_init(); + for (int i = 0; i < 5; ++i) { + switch_step_10ms(mk(false, true)); + } + ASSERT_EQ(switch_get_state(), SWITCH_RELEASE); + TEST_END(); +} + +static void test_bounce_does_not_change_state(void) +{ + TEST_BEGIN("bouncing input keeps current state"); + (void)switch_init(); + for (int i = 0; i < 5; ++i) { + switch_step_10ms(mk(true, false)); + } + ASSERT_EQ(switch_get_state(), SWITCH_APPLY); + /* Bounce: 1 sample apply, 1 sample neutral, 1 sample release ... */ + switch_step_10ms(mk(true, false)); + switch_step_10ms(mk(false, false)); + switch_step_10ms(mk(false, true)); + switch_step_10ms(mk(true, false)); + switch_step_10ms(mk(false, false)); + ASSERT_EQ(switch_get_state(), SWITCH_APPLY); + TEST_END(); +} + +static void test_both_pressed_is_neutral(void) +{ + TEST_BEGIN("both apply+release pressed -> neutral"); + (void)switch_init(); + for (int i = 0; i < 5; ++i) { + switch_step_10ms(mk(true, true)); + } + ASSERT_EQ(switch_get_state(), SWITCH_NEUTRAL); + TEST_END(); +} + +int main(void) +{ + printf("== test_switch_debouncer ==\n"); + test_init_state_is_neutral(); + test_debounce_apply_takes_5_samples(); + test_debounce_release_takes_5_samples(); + test_bounce_does_not_change_state(); + test_both_pressed_is_neutral(); + TEST_SUMMARY(); +} diff --git a/tests/unit_test_framework.h b/tests/unit_test_framework.h new file mode 100644 index 0000000..d66b836 --- /dev/null +++ b/tests/unit_test_framework.h @@ -0,0 +1,62 @@ +/** + * @file unit_test_framework.h + * @brief Minimaler Test-Runner fuer demo-epb. + * + * In Produktion wuerde hier CppUTest oder Google Test stehen + * (siehe docs/SWE-Plan.docx, Abschnitt 7). Fuer die Demo + * bleibt das Framework klein, damit es ohne externe Abhaengigkeiten baut. + */ +#ifndef UNIT_TEST_FRAMEWORK_H +#define UNIT_TEST_FRAMEWORK_H + +#include +#include +#include + +typedef struct { + int tests_run; + int tests_failed; +} TestStats; + +extern TestStats g_test_stats; + +#define TEST_BEGIN(name) \ + do { \ + ++g_test_stats.tests_run; \ + const char* _test_name = name; \ + int _test_failed = 0; \ + printf(" TEST %s ... ", _test_name); + +#define TEST_END() \ + if (_test_failed) { ++g_test_stats.tests_failed; \ + printf("FAIL\n"); } \ + else { printf("ok\n"); } \ + } while (0) + +#define ASSERT_TRUE(expr) \ + do { if (!(expr)) { _test_failed = 1; \ + printf("\n ASSERT_TRUE failed: %s (%s:%d)\n", \ + #expr, __FILE__, __LINE__); } } while (0) + +#define ASSERT_EQ(a, b) \ + do { long long _a = (long long)(a), _b = (long long)(b); \ + if (_a != _b) { _test_failed = 1; \ + printf("\n ASSERT_EQ failed: %s=%lld != %s=%lld (%s:%d)\n", \ + #a, _a, #b, _b, __FILE__, __LINE__); } \ + } while (0) + +#define ASSERT_NE(a, b) \ + do { long long _a = (long long)(a), _b = (long long)(b); \ + if (_a == _b) { _test_failed = 1; \ + printf("\n ASSERT_NE failed: %s==%s==%lld (%s:%d)\n", \ + #a, #b, _a, __FILE__, __LINE__); } \ + } while (0) + +#define TEST_SUMMARY() \ + do { \ + printf("\n%d tests run, %d failed.\n", \ + g_test_stats.tests_run, g_test_stats.tests_failed); \ + return (g_test_stats.tests_failed == 0) ? 0 : 1; \ + } while (0) + +#endif /* UNIT_TEST_FRAMEWORK_H */ diff --git a/tools/generate_doorstop_items.py b/tools/generate_doorstop_items.py new file mode 100644 index 0000000..ad30600 --- /dev/null +++ b/tools/generate_doorstop_items.py @@ -0,0 +1,808 @@ +#!/usr/bin/env python3 +""" +Generate Doorstop-compatible Markdown items for the EPB demo. + +Source of truth: the dict EPB_DATA below. +Outputs to: reqs/sys/, reqs/swe/, arch/sys/, arch/swe/ + +Each output file uses Doorstop's Markdown mode (YAML frontmatter + body). + +Run: python3 tools/generate_doorstop_items.py +""" +from __future__ import annotations + +import textwrap +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent + + +# --------------------------------------------------------------------------- +# System Requirements +# --------------------------------------------------------------------------- + +SYS_REQS = [ + { + "id": "SYS-001", "asil": "D", + "title": "Halten der Parkbremse im Stillstand", + "text": ( + "Wenn die Parkbremse aktiviert ist und das Fahrzeug stillsteht, " + "muss das EPB-System die mechanische Klemmkraft an beiden hinteren " + "Bremssaetteln aufrecht erhalten, bis ein Loesen ausdruecklich " + "angefordert wird. Sicherheitsziel: SG-01.\n\n" + "**Verifikation:** SiL-Test mit Auf-/Ab-Hangelung, Klemmkraftmessung." + ), + }, + { + "id": "SYS-002", "asil": "D", + "title": "Apply auf Fahrer-Anforderung", + "text": ( + "Bei Betaetigung des EPB-Schalters in Apply-Richtung muss das " + "System innerhalb von 800 ms die Parkbremse anlegen, sofern die " + "Voraussetzungen erfuellt sind (Stillstand oder Geschwindigkeit " + "unter 5 km/h). Sicherheitsziel: SG-01." + ), + }, + { + "id": "SYS-003", "asil": "B", + "title": "Release auf Fahrer-Anforderung", + "text": ( + "Bei Betaetigung des EPB-Schalters in Release-Richtung muss das " + "System die Parkbremse loesen, sofern die folgenden Voraussetzungen " + "erfuellt sind: Motor laeuft, Fahrer betaetigt Bremspedal, Gang " + "ist eingelegt. Maximalzeit fuer Loesen: 1500 ms." + ), + }, + { + "id": "SYS-004", "asil": "D", + "title": "Auto-Apply bei Motor-Aus", + "text": ( + "Wenn der Motor ausgeschaltet wird und das Fahrzeug stillsteht " + "und keine Parkbremse aktiv ist, muss das System die Parkbremse " + "spaetestens 2 s nach Erkennung Motor-Aus automatisch anlegen. " + "Sicherheitsziel: SG-01." + ), + }, + { + "id": "SYS-005", "asil": "D", + "title": "Hill-Hold am Berg", + "text": ( + "Bei aktivem Hill-Hold (Fahrzeug steht am Hang mit Neigung > 5%, " + "Fahrer betaetigt Bremspedal) uebernimmt das EPB-System die " + "Bremskraft beim Loesen des Bremspedals und haelt diese, bis die " + "Anfahrt erkannt wird. Sicherheitsziel: SG-01." + ), + }, + { + "id": "SYS-006", "asil": "B", + "title": "Auto-Release beim Anfahren (Drive-Away-Assist)", + "text": ( + "Wenn die Parkbremse aktiv ist und der Fahrer Anfahrabsicht zeigt " + "(Gaspedal-Betaetigung bei eingelegtem Gang), muss das System die " + "Parkbremse innerhalb von 500 ms loesen. Voraussetzung: alle " + "Sicherheitskriterien (Fahrertuer geschlossen, Sicherheitsgurt) " + "erfuellt." + ), + }, + { + "id": "SYS-007", "asil": "B", + "title": "Aktor-Stromueberwachung", + "text": ( + "Das System muss den Motorstrom jedes Aktors mit mindestens 1 kHz " + "ueberwachen und bei Ueberschreitung von 8 A fuer mehr als 100 ms " + "den Aktor abschalten und einen DTC setzen. Sicherheitsziel: SG-03." + ), + }, + { + "id": "SYS-008", "asil": "QM", + "title": "Service-Modus fuer Werkstatt", + "text": ( + "Das System muss ueber UDS RoutineControl (Service 0x31) einen " + "Service-Modus bereitstellen, in dem die Aktoren manuell in " + "Wartungs-Position gefahren werden koennen (z.B. fuer Bremsbelag-" + "Wechsel)." + ), + }, + { + "id": "SYS-009", "asil": "QM", + "title": "UDS-Diagnose", + "text": ( + "Das System muss UDS-Diagnose nach ISO 14229 bereitstellen: " + "ReadDTC (0x19), ReadDataByIdentifier (0x22), RoutineControl (0x31), " + "ECUReset (0x11). Tester-Adresse 0x712, Antwort-Adresse 0x71A." + ), + }, + { + "id": "SYS-010", "asil": "QM", + "title": "HMI-Statusanzeige", + "text": ( + "Der EPB-Status muss dem Fahrer signalisiert werden: LED am " + "Schalter (an = Apply, aus = Release, blinkend = Fehler) sowie " + "Text im Kombi-Display via CAN-Bus (Frame-ID 0x3A0, 50 Hz)." + ), + }, +] + +# --------------------------------------------------------------------------- +# Software Requirements (each links to one or more SYS reqs) +# --------------------------------------------------------------------------- + +SWE_REQS = [ + # SYS-001 — Halten + {"id": "SWE-001", "asil": "D", "links": ["SYS-001"], + "title": "Apply-Controller haelt Klemmkraft", + "text": "Der Apply-Controller muss die Klemmkraft im Hold-Zustand alle 50 ms " + "verifizieren und bei Abweichung > 10% nachregeln."}, + + {"id": "SWE-002", "asil": "D", "links": ["SYS-001"], + "title": "Watchdog ueberwacht Apply-Controller", + "text": "Ein unabhaengiger Watchdog muss die Liveness des Apply-Controllers " + "mit 100 ms Timeout ueberwachen und bei Ausbleiben in den sicheren " + "Zustand (Apply) gehen."}, + + # SYS-002 — Apply + {"id": "SWE-003", "asil": "D", "links": ["SYS-002"], + "title": "Schalter-Apply-Signal an Apply-Controller weiterleiten", + "text": "Das Software-Modul Switch-Debouncer muss ein entprelltes " + "Apply-Signal innerhalb von 50 ms an den Apply-Controller liefern."}, + + {"id": "SWE-004", "asil": "D", "links": ["SYS-002"], + "title": "Klemmkraft-Erreichen bestaetigen", + "text": "Der Apply-Controller muss das Erreichen der Ziel-Klemmkraft via " + "Strommessung erkennen und ein Status-Flag setzen."}, + + # SYS-003 — Release + {"id": "SWE-005", "asil": "B", "links": ["SYS-003"], + "title": "Release-Voraussetzungen pruefen", + "text": "Vor jedem Release muss der Apply-Controller pruefen: Motor laeuft, " + "Bremspedal betaetigt, Gang ist eingelegt. Andernfalls Release abweisen."}, + + {"id": "SWE-006", "asil": "B", "links": ["SYS-003"], + "title": "Aktoren in Release-Position fahren", + "text": "Der Actuator-Driver muss beide Aktoren parallel in Release-Position " + "fahren. Maximalzeit: 1200 ms. Bei Timeout DTC setzen."}, + + # SYS-004 — Auto-Apply + {"id": "SWE-007", "asil": "D", "links": ["SYS-004"], + "title": "Motor-Aus-Bedingung erkennen", + "text": "Der Safety-Manager muss erkennen: Motor-Status = aus, " + "Geschwindigkeit < 0.5 km/h. Auswertezyklus 50 ms."}, + + {"id": "SWE-008", "asil": "D", "links": ["SYS-004"], + "title": "Auto-Apply nach 2 s Verzoegerung", + "text": "Ist die Motor-Aus-Bedingung 2 s stabil erfuellt und Parkbremse " + "noch nicht aktiv, muss der Safety-Manager Apply-Anforderung an " + "den Apply-Controller senden."}, + + # SYS-005 — Hill-Hold + {"id": "SWE-009", "asil": "D", "links": ["SYS-005"], + "title": "Hill-Hold-Aktivierungsbedingung", + "text": "Der Safety-Manager muss Hill-Hold aktivieren, wenn Neigung " + "(gefiltert) > 5%, Geschwindigkeit < 0.5 km/h und Bremspedal " + "betaetigt sind."}, + + {"id": "SWE-010", "asil": "D", "links": ["SYS-005"], + "title": "Hill-Hold-Uebergabe an Apply-Controller", + "text": "Wird das Bremspedal bei aktivem Hill-Hold losgelassen, muss der " + "Safety-Manager unmittelbar Apply-Anforderung an den Apply-" + "Controller senden, bevor das Fahrzeug zu rollen beginnen kann."}, + + # SYS-006 — Auto-Release + {"id": "SWE-011", "asil": "B", "links": ["SYS-006"], + "title": "Anfahrabsicht erkennen", + "text": "Anfahrabsicht ist erkannt, wenn: Gaspedal > 10%, Gang in Vorwaerts " + "oder Rueckwaerts, Motor laeuft."}, + + {"id": "SWE-012", "asil": "B", "links": ["SYS-006"], + "title": "Sicherheits-Check vor Auto-Release", + "text": "Vor Auto-Release muessen erfuellt sein: Fahrertuer geschlossen, " + "Sicherheitsgurt angelegt. Andernfalls warnen und nicht loesen."}, + + # SYS-007 — Aktor-Strom + {"id": "SWE-013", "asil": "B", "links": ["SYS-007"], + "title": "Strommessung mit 1 kHz", + "text": "Der Actuator-Driver muss den Motorstrom jedes Aktors mit " + "mindestens 1 kHz abtasten. Genauigkeit +/- 100 mA."}, + + {"id": "SWE-014", "asil": "B", "links": ["SYS-007"], + "title": "Overcurrent-Cutoff", + "text": "Bei Motorstrom > 8 A laenger als 100 ms muss der Actuator-Driver " + "den Motor abschalten und einen DTC P0xxx setzen."}, + + {"id": "SWE-015", "asil": "B", "links": ["SYS-007"], + "title": "Klemmkraft-Schaetzung aus Strom-Profil", + "text": "Der Actuator-Driver muss die erreichte Klemmkraft aus dem " + "Stromverlauf bei Apply schaetzen (Modell: F = k * I_peak)."}, + + # SYS-008 — Service-Modus + {"id": "SWE-016", "asil": "QM", "links": ["SYS-008"], + "title": "UDS RoutineControl 0x31 fuer Service-Release", + "text": "Service-Mode wird ueber UDS RoutineControl Service 0x31, " + "Routine-ID 0x0301 aktiviert. Bedingung: Fahrzeug muss stillstehen."}, + + {"id": "SWE-017", "asil": "QM", "links": ["SYS-008"], + "title": "Service-Mode-Indikator", + "text": "Im Service-Mode muss die EPB-LED am Schalter mit 2 Hz blinken."}, + + # SYS-009 — UDS + {"id": "SWE-018", "asil": "QM", "links": ["SYS-009"], + "title": "UDS Service 0x19 ReadDTC", + "text": "Das System muss alle gespeicherten DTCs ueber Service 0x19 " + "(Subfunktion 0x02 reportDTCByStatusMask) ausgeben."}, + + {"id": "SWE-019", "asil": "QM", "links": ["SYS-009"], + "title": "UDS Service 0x22 ReadDataByIdentifier", + "text": "Folgende DIDs muessen lesbar sein: 0xF187 (SW-Version), " + "0x0301 (Klemmkraft links), 0x0302 (Klemmkraft rechts)."}, + + # SYS-010 — HMI + {"id": "SWE-020", "asil": "QM", "links": ["SYS-010"], + "title": "LED-Steuerung", + "text": "Apply-aktiv: LED dauerleuchtend. Release: LED aus. Fehler: " + "LED blinkt 4 Hz. Service-Mode: LED blinkt 2 Hz."}, + + {"id": "SWE-021", "asil": "QM", "links": ["SYS-010"], + "title": "CAN-Status-Frame", + "text": "Status-Frame 0x3A0 mit 50 Hz: Byte 0 = Status (0=Released, 1=Applied, " + "2=Applying, 3=Releasing, 0xFF=Error), Byte 1-2 = Klemmkraft links, " + "Byte 3-4 = Klemmkraft rechts."}, + + # Sensorik & Plausibilisierung + {"id": "SWE-022", "asil": "B", "links": ["SYS-001", "SYS-002", "SYS-006"], + "title": "Stillstands-Erkennung aus Wheel Speeds", + "text": "Stillstand ist erkannt, wenn alle 4 Wheel-Speed-Signale fuer " + "mindestens 200 ms unter 0.5 km/h liegen."}, + + {"id": "SWE-023", "asil": "B", "links": ["SYS-007"], + "title": "Wheel Speed Plausibilisierung", + "text": "Spreizung der Wheel-Speed-Signale: bei Geradeaus-Fahrt darf die " + "Differenz nicht > 3 km/h sein. Andernfalls Sensor-Fehler-DTC."}, + + {"id": "SWE-024", "asil": "B", "links": ["SYS-005"], + "title": "Inclinometer Tiefpass-Filter", + "text": "Das Roh-Neigungssignal muss mit einem Tiefpass 1. Ordnung " + "(Zeitkonstante 200 ms) gefiltert werden, bevor es zur Hill-Hold-" + "Bewertung verwendet wird."}, + + {"id": "SWE-025", "asil": "QM", "links": ["SYS-002", "SYS-003"], + "title": "Switch-Debouncing", + "text": "Der EPB-Schalter muss mit einer Entprell-Zeit von 50 ms " + "entprellt werden. Stabiler Pegel = Eingangssignal fuer " + "Apply-Controller."}, +] + +# --------------------------------------------------------------------------- +# System Architecture Elements +# --------------------------------------------------------------------------- + +SA_ELEMENTS = [ + { + "id": "SA-001", "links": ["SYS-001", "SYS-002", "SYS-003", "SYS-004", + "SYS-005", "SYS-006", "SYS-007", "SYS-008", + "SYS-009", "SYS-010"], + "title": "EPB ECU", + "asil": "D", + "text": textwrap.dedent(""" + ## Verantwortung + + Zentrales Steuergeraet der elektrischen Parkbremse. Beinhaltet alle Software- + Komponenten und die elektronische Ansteuerung der Aktoren. + + ## System-Kontext + + ```plantuml + @startuml + node "EPB ECU" as ECU + node "Aktor links" as AL + node "Aktor rechts" as AR + node "Wheel Speed Sensoren (x4)" as WS + node "Inclinometer" as IN + node "EPB-Schalter + LED" as SW + node "CAN-Bus" as CAN + node "Kombi-Display" as DI + node "OBD-Tester" as OBD + + ECU --> AL : PWM, I-Mess + ECU --> AR : PWM, I-Mess + WS --> ECU : Pulse + IN --> ECU : SPI + SW --> ECU : GPIO + ECU --> SW : LED + ECU <-> CAN + CAN <-> DI + CAN <-> OBD + @enduml + ``` + + ## Schnittstellen + + | Schnittstelle | Typ | Richtung | + |---------------|----------------|----------| + | Aktor L/R | PWM + Shunt | I/O | + | Wheel Speed | Hall-Pulse | In | + | Inclinometer | SPI | In | + | Schalter | GPIO debounced | In | + | LED | GPIO | Out | + | CAN | ISO 11898 | I/O | + + ## Subkomponenten (Aufteilung auf SW) + + Realisiert in Software: alle SWA-Elemente SWA-001..SWA-010. + + ## Nichtfunktionale Eigenschaften + + - Worst-Case Reaktionszeit (Schalter → Aktor-Bewegung): 250 ms + - Flash-Bedarf: < 256 KB + - RAM-Bedarf: < 32 KB + - Stromaufnahme: < 200 mA (Standby) / < 30 A (Aktor-Spitze) + """).strip(), + }, + { + "id": "SA-002", "links": ["SYS-001", "SYS-002", "SYS-003", "SYS-007"], + "title": "Aktoren (Caliper-Motoren)", + "asil": "D", + "text": textwrap.dedent(""" + ## Verantwortung + + Zwei elektromechanische Aktoren an den hinteren Bremssaetteln klemmen + und loesen die Bremsbelaege. Geliefert (Annahme): kommerzielles Bauteil + eines Tier-1-Lieferanten. + + ## Schnittstellen + + | Schnittstelle | Typ | Bemerkung | + |---------------|--------------|-----------------------------------| + | Power | 12 V, PWM | bidirektional fuer Apply/Release | + | Strom-Shunt | Analog | wird in der ECU abgegriffen | + + ## Nichtfunktionale Eigenschaften + + - Max. Klemmkraft: 20 kN + - Apply-Zeit (0 → max): 600 ms + - Strom (nominal): 4 A + - Strom (Spitze): 30 A (kurzzeitig) + - Temperaturbereich: -40°C bis +85°C + """).strip(), + }, + { + "id": "SA-003", "links": ["SYS-005", "SYS-006", "SYS-007"], + "title": "Sensor-Cluster", + "asil": "B", + "text": textwrap.dedent(""" + ## Verantwortung + + Zusammenfassung aller fuer die EPB benoetigten Eingangssignale: + Wheel-Speed-Sensoren (4x), Inclinometer (1x), EPB-Schalter, Bremspedal- + Status, Gear-Position, Door-Open, Seat-Belt — die letzten vier per CAN. + + ## Schnittstellen + + | Sensor | Typ | Quelle | + |-----------------|------------------|--------------| + | Wheel Speed x4 | Hall-Pulse | direkt | + | Inclinometer | SPI 1 kHz | direkt | + | EPB-Schalter | GPIO | direkt | + | Bremspedal | CAN 0x100 | aus BCM | + | Gear | CAN 0x110 | aus TCU | + | Door / Belt | CAN 0x120 | aus BCM | + + ## Nichtfunktionale Eigenschaften + + - Wheel-Speed-Genauigkeit: +/- 0.1 km/h ab 1 km/h + - Inclinometer-Genauigkeit: +/- 0.5° + - Sampling-Frequenz Inclinometer: 100 Hz + """).strip(), + }, + { + "id": "SA-004", "links": ["SYS-008", "SYS-010"], + "title": "HMI (Schalter, LED, Display)", + "asil": "QM", + "text": textwrap.dedent(""" + ## Verantwortung + + Fahrer-Interaktion und -Information: Tippschalter mit integrierter LED, + Statusanzeige im Kombi-Display via CAN. + + ## Schnittstellen + + | Element | Typ | Verhalten | + |---------------|----------|--------------------------------------------| + | Tippschalter | GPIO | Apply-Richtung / Release-Richtung | + | LED | GPIO | aus / an / blink 2 Hz / blink 4 Hz | + | Display | CAN 0x3A0 | 50 Hz Status-Frame | + """).strip(), + }, + { + "id": "SA-005", "links": ["SYS-009", "SYS-010"], + "title": "CAN-Bus", + "asil": "QM", + "text": textwrap.dedent(""" + ## Verantwortung + + Kommunikations-Backbone fuer Eingangsdaten (Bremspedal, Gang, Tuer, Gurt), + Ausgabe (Status-Frame an Display) und Diagnose (UDS auf Tester-Adresse). + + ## Schnittstellen + + - Baudrate: 500 kbit/s, CAN 2.0B + - Empfangene Frames: 0x100 (Bremspedal), 0x110 (Gang), 0x120 (Door/Belt), + 0x712 (UDS-Request) + - Gesendete Frames: 0x3A0 (Status 50 Hz), 0x71A (UDS-Response) + """).strip(), + }, +] + +# --------------------------------------------------------------------------- +# Software Architecture Elements +# --------------------------------------------------------------------------- + +SWA_ELEMENTS = [ + { + "id": "SWA-001", "asil": "D", + "links": ["SWE-007", "SWE-008", "SWE-009", "SWE-010"], + "title": "Safety Manager", + "text": textwrap.dedent(""" + ## Verantwortung + + Hoechste Sicherheitsschicht. Erkennt Motor-Aus, aktiviert Hill-Hold, + triggert Auto-Apply. Lebenswichtige Logik mit redundanter Pruefung. + + ## Statische Sicht + + ```plantuml + @startuml + package "Safety Manager" { + [Engine State Monitor] + [Hill-Hold Logic] + [Auto-Apply Logic] + } + [Safety Manager] ..> [Apply Controller] : Apply-Anforderung + [Wheel Speed Plausi] --> [Safety Manager] : v_vehicle + [Inclinometer Filter] --> [Safety Manager] : grade + @enduml + ``` + + ## Schnittstellen (Provided) + + ```c + Status safety_mgr_init(void); + void safety_mgr_step_50ms(const SafetyInputs* in); + ``` + + ## Dynamisches Verhalten + + ```plantuml + @startuml + [*] --> Idle + Idle --> HillHoldArmed : grade>5% & v=0 & brake + HillHoldArmed --> HillHoldActive : brake released + HillHoldActive --> Idle : v>2 km/h + Idle --> AutoApplyArmed : engine_off & v=0 + AutoApplyArmed --> AutoApplyTriggered : t>=2s + AutoApplyTriggered --> Idle : applied + @enduml + ``` + + ## Ressourcen + + - Stack: <= 256 B + - Worst-Case Timing: 200 us / Aufruf + + ## Mapping auf Anforderungen + + | Anforderung | Wie abgedeckt | + |-------------|---------------| + | SWE-007 | engine_off + v<0.5 in step_50ms | + | SWE-008 | 2s-Filter und Trigger | + | SWE-009 | Hill-Hold-Aktivierung | + | SWE-010 | Brake-Released-Detektion | + """).strip(), + }, + { + "id": "SWA-002", "asil": "D", + "links": ["SWE-001", "SWE-002", "SWE-003", "SWE-004"], + "title": "Apply Controller", + "text": textwrap.dedent(""" + ## Verantwortung + + Zentraler Controller fuer Apply, Hold und Release der Parkbremse. + ASIL-D-Kern der EPB-Software. Implementiert in `src/apply_controller.c`. + + ## Statische Sicht + + ```plantuml + @startuml + [Apply Controller] --> [Actuator Driver L] : apply/release + [Apply Controller] --> [Actuator Driver R] : apply/release + [Switch Debouncer] --> [Apply Controller] : sw_apply, sw_release + [Safety Manager] --> [Apply Controller] : auto_apply, hill_hold_request + [Apply Controller] --> [Display Manager] : status + [Apply Controller] <-- [Watchdog] : alive_check + @enduml + ``` + + ## Schnittstellen (Provided) + + ```c + Status apply_ctrl_init(void); + void apply_ctrl_step_50ms(const ApplyInputs* in); + EpbStatus apply_ctrl_get_status(void); + ``` + + ## Dynamisches Verhalten + + ```plantuml + @startuml + [*] --> Released + Released --> Applying : apply_request & v_low + Applying --> Applied : current_target_reached + Applied --> Releasing : release_request & preconditions_ok + Applied --> Applied : 50ms hold check (re-clamp if needed) + Releasing --> Released : release_complete + Applying --> Error : timeout > 1500ms + Releasing --> Error : timeout > 1200ms + Error --> Released : reset & no fault + @enduml + ``` + + ## Ressourcen + + - Stack: <= 384 B + - Worst-Case Timing: 350 us / Aufruf + + ## Designentscheidungen + + | Entscheidung | Begruendung | + |--------------|-------------| + | Statische Allokation, kein Heap | Determinismus, MISRA C 21.3 | + | State Machine | Einfacher zu verifizieren, deterministisch | + | 50ms Step-Funktion | Synchron zur Inclinometer-Abtastung | + + ## Mapping auf Anforderungen + + | Anforderung | Wie abgedeckt | + |-------------|---------------| + | SWE-001 | Hold-Zustand mit periodischer Klemmkraft-Pruefung | + | SWE-002 | Watchdog-Pet im step_50ms | + | SWE-003 | sw_apply Input wird sofort ausgewertet | + | SWE-004 | Current-Target-Detektion via Actuator-Driver-Feedback | + """).strip(), + }, + { + "id": "SWA-003", "asil": "B", + "links": ["SWE-006", "SWE-013", "SWE-014", "SWE-015"], + "title": "Actuator Driver", + "text": textwrap.dedent(""" + ## Verantwortung + + Low-Level-Ansteuerung der beiden Aktor-Motoren. PWM-Generierung, + Strom-Messung, Overcurrent-Cutoff, Klemmkraft-Schaetzung. + Implementiert in `src/actuator_driver.c`. + + ## Statische Sicht + + ```plantuml + @startuml + [Apply Controller] --> [Actuator Driver] + [Actuator Driver] --> [Hardware PWM] : pwm_set + [Actuator Driver] <-- [Hardware ADC] : current_sample + [Actuator Driver] --> [Diagnostic Manager] : DTC + @enduml + ``` + + ## Schnittstellen (Provided) + + ```c + Status actuator_init(void); + void actuator_apply(ActuatorId id, uint8_t pwm_percent); + void actuator_release(ActuatorId id, uint8_t pwm_percent); + void actuator_stop(ActuatorId id); + ActuatorStatus actuator_get_status(ActuatorId id); + void actuator_isr_1khz(void); // Strom-Sampling + ``` + + ## Ressourcen + + - Stack: <= 256 B + - Worst-Case Timing: 50 us / ISR + - Static RAM: 64 B pro Aktor + + ## Mapping auf Anforderungen + + | Anforderung | Wie abgedeckt | + |-------------|---------------| + | SWE-006 | actuator_release fuer beide Aktoren parallel | + | SWE-013 | actuator_isr_1khz | + | SWE-014 | Overcurrent-Detektor in ISR | + | SWE-015 | Peak-Current-Tracking + lineare Klemmkraft-Schaetzung | + """).strip(), + }, + { + "id": "SWA-004", "asil": "B", + "links": ["SWE-022", "SWE-023"], + "title": "Wheel Speed Plausibilisierung", + "text": textwrap.dedent(""" + ## Verantwortung + + Aufbereitung und Plausibilisierung der 4 Wheel-Speed-Signale. Erkennt + Stillstand und plausibilisiert untereinander. + + ## Schnittstellen (Provided) + + ```c + Status wheel_speed_init(void); + void wheel_speed_step_10ms(const WheelInputs* in); + bool wheel_speed_is_standstill(void); + float wheel_speed_get_vehicle(void); + ``` + """).strip(), + }, + { + "id": "SWA-005", "asil": "B", + "links": ["SWE-024"], + "title": "Inclinometer Filter", + "text": textwrap.dedent(""" + ## Verantwortung + + Tiefpass-Filterung des Inclinometer-Roh-Signals fuer die Hill-Hold-Bewertung. + + ## Schnittstellen (Provided) + + ```c + Status inclino_init(void); + void inclino_step_10ms(int16_t raw_mdeg); + float inclino_get_grade_percent(void); + ``` + """).strip(), + }, + { + "id": "SWA-006", "asil": "QM", + "links": ["SWE-025"], + "title": "Switch Debouncer", + "text": textwrap.dedent(""" + ## Verantwortung + + Software-Entprellung des EPB-Schalters. Liefert stabiles Apply / Release + Signal an den Apply-Controller. Implementiert in `src/switch_debouncer.c`. + + ## Schnittstellen (Provided) + + ```c + Status switch_init(void); + void switch_step_10ms(SwitchRaw raw); + SwitchState switch_get_state(void); + ``` + + ## Mapping auf Anforderungen + + | Anforderung | Wie abgedeckt | + |-------------|---------------| + | SWE-025 | 50ms Debounce-Logik | + """).strip(), + }, + { + "id": "SWA-007", "asil": "QM", + "links": ["SWE-020", "SWE-021"], + "title": "Display Manager", + "text": textwrap.dedent(""" + ## Verantwortung + + Steuert LED am EPB-Schalter und CAN-Status-Frame an das Kombi-Display. + Empfaengt Status vom Apply-Controller. + + ## Schnittstellen (Provided) + + ```c + Status display_init(void); + void display_set_status(EpbStatus s); + void display_step_20ms(void); // 50 Hz CAN-Frame + ``` + """).strip(), + }, + { + "id": "SWA-008", "asil": "QM", + "links": ["SWE-018", "SWE-019"], + "title": "Diagnostic Manager", + "text": textwrap.dedent(""" + ## Verantwortung + + UDS-Diagnose nach ISO 14229: ReadDTC, ReadDataByIdentifier, RoutineControl. + + ## Schnittstellen (Provided) + + ```c + Status diag_init(void); + void diag_handle_request(const uint8_t* data, uint16_t len); + void diag_set_dtc(uint16_t dtc_id); + ``` + """).strip(), + }, + { + "id": "SWA-009", "asil": "QM", + "links": ["SWE-016", "SWE-017"], + "title": "Service Mode", + "text": textwrap.dedent(""" + ## Verantwortung + + Service-Modus fuer Werkstatt. Wird ueber UDS RoutineControl 0x31, Routine-ID + 0x0301 aktiviert. Steuert Aktoren in Wartungsposition. + """).strip(), + }, + { + "id": "SWA-010", "asil": "QM", + "links": ["SWE-018", "SWE-019"], + "title": "Logger", + "text": textwrap.dedent(""" + ## Verantwortung + + Logging fuer Entwicklung und Service. Ringpuffer im RAM (1 KB) sowie + Persistenz im EEPROM bei kritischen Ereignissen. + + ## Schnittstellen (Provided) + + ```c + Status log_init(void); + void log_event(LogLevel lvl, uint16_t event_id, uint32_t param); + ``` + """).strip(), + }, +] + +# --------------------------------------------------------------------------- +# Generation +# --------------------------------------------------------------------------- + +REQ_FRONTMATTER = textwrap.dedent(""" +--- +active: true +derived: false +header: '{title}' +level: 1.{level} +normative: true +reviewed: null +links:{links_yaml} +asil: {asil} +--- + +# {id}: {title} + +{text} +""").strip() + "\n" + + +def emit_links(links): + if not links: + return " []" + parts = ["\n - {}".format(l) for l in links] + return "".join(parts) + + +def write_items(items, target_dir: Path, with_links=True): + target_dir.mkdir(parents=True, exist_ok=True) + for i, item in enumerate(items, start=1): + path = target_dir / f"{item['id']}.md" + links_yaml = emit_links(item.get("links", [])) + content = REQ_FRONTMATTER.format( + title=item["title"], + level=i, + links_yaml=links_yaml, + asil=item["asil"], + id=item["id"], + text=item["text"], + ) + path.write_text(content) + print(f"Wrote {len(items)} items to {target_dir.relative_to(REPO)}/") + + +def main(): + write_items(SYS_REQS, REPO / "reqs" / "sys") + write_items(SWE_REQS, REPO / "reqs" / "swe") + write_items(SA_ELEMENTS, REPO / "arch" / "sys") + write_items(SWA_ELEMENTS, REPO / "arch" / "swe") + print("\nTotal: {} reqs/arch items.".format( + len(SYS_REQS) + len(SWE_REQS) + len(SA_ELEMENTS) + len(SWA_ELEMENTS) + )) + + +if __name__ == "__main__": + main()