Initial commit: demo-epb v1.0 — Elektrische Parkbremse Demo
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
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
build/
|
||||
*.o
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.gcov
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
|
||||
# Generated artefacts
|
||||
*.pdf
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Stefan Lohmaier <stefan@slohmaier.com>
|
||||
|
||||
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.
|
||||
@@ -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)
|
||||
@@ -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).
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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);
|
||||
```
|
||||
@@ -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);
|
||||
```
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
```
|
||||
@@ -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);
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
```
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 |
|
||||
@@ -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)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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 `<hash>`)
|
||||
- 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.
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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/`
|
||||
@@ -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).*
|
||||
Binary file not shown.
@@ -0,0 +1,81 @@
|
||||
---
|
||||
record-id: MISRA-REC-001
|
||||
projekt: demo-epb
|
||||
datum: 2026-05-11
|
||||
status: Approved
|
||||
---
|
||||
|
||||
# MISRA Deviation Record MISRA-REC-001
|
||||
|
||||
| Feld | Wert |
|
||||
|-------------------|---------------------------------------------|
|
||||
| Record-ID | MISRA-REC-001 |
|
||||
| Datum | 2026-05-11 |
|
||||
| Datei | `src/apply_controller.c` |
|
||||
| Funktion | `apply_ctrl_step_50ms` |
|
||||
| Zeile | 64 |
|
||||
| Standard | MISRA C:2012 |
|
||||
| Regel | Rule 15.5 (Advisory) — "A function should have a single point of exit" |
|
||||
| ASIL | D |
|
||||
| Status | Approved |
|
||||
|
||||
---
|
||||
|
||||
## 1. Code-Ausschnitt
|
||||
|
||||
```c
|
||||
void apply_ctrl_step_50ms(const ApplyInputs* in)
|
||||
{
|
||||
if (in == NULL) {
|
||||
s_ctx.last_error = EPB_EINVAL;
|
||||
return; /* <-- frueher Exit */
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Begruendung
|
||||
|
||||
NULL-Pointer-Check als frueher Exit-Punkt verbessert die Lesbarkeit deutlich
|
||||
gegenueber einer geschachtelten Variante mit einem einzigen `return` am Ende.
|
||||
MISRA Rule 15.5 ist **Advisory**, nicht **Required**.
|
||||
|
||||
Der frueh-Exit hat eine klar definierte Semantik (Input-Validierung) und
|
||||
beeintraechtigt nicht die Verifizierbarkeit; im Gegenteil, der separate
|
||||
Pfad ist im Unit-Test `test_null_input` eindeutig abgedeckt.
|
||||
|
||||
## 3. Alternative geprueft
|
||||
|
||||
Variante mit einzigem Exit:
|
||||
|
||||
```c
|
||||
void apply_ctrl_step_50ms(const ApplyInputs* in)
|
||||
{
|
||||
if (in == NULL) {
|
||||
s_ctx.last_error = EPB_EINVAL;
|
||||
} else {
|
||||
/* gesamte Step-Logik in else-Branch geschachtelt */
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Verworfen, weil die zusaetzliche Schachtelung die State-Machine schwerer
|
||||
lesbar macht und keine Funktionsaequivalenz mit der frueh-Exit-Variante
|
||||
gewinnt.
|
||||
|
||||
## 4. Auswirkung auf Sicherheit
|
||||
|
||||
Keine. Frueher Exit ist deterministisch und im Unit-Test abgedeckt.
|
||||
|
||||
## 5. Freigabe
|
||||
|
||||
| Rolle | Name | Datum | Signatur |
|
||||
|-----------------|------------------|-------------|----------|
|
||||
| Technical Lead | Stefan Lohmaier | 2026-05-11 | (Demo) |
|
||||
| Safety Manager | (im Realprojekt) | 2026-05-11 | (Demo) |
|
||||
|
||||
## 6. Geltungsbereich
|
||||
|
||||
Nur fuer diese eine Code-Stelle. Andere Stellen mit frueh-Exit benoetigen
|
||||
separate Records.
|
||||
Binary file not shown.
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 <stddef.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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 */
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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 */
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user