Add full automotive RTOS project
Add kernel (Cortex-M0/M3/M4, Tricore, S32K, RISC-V ports), drivers, middleware (CAN stack, diagnostics, safety), applications, board support, build/test tooling, and documentation.
This commit is contained in:
+152
@@ -0,0 +1,152 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# Auto detect text files and normalize line endings
|
||||||
|
# ============================================================================
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Source code files
|
||||||
|
# ============================================================================
|
||||||
|
*.c text diff=cpp
|
||||||
|
*.h text diff=cpp
|
||||||
|
*.cpp text diff=cpp
|
||||||
|
*.hpp text diff=cpp
|
||||||
|
*.cc text diff=cpp
|
||||||
|
*.cxx text diff=cpp
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Script files
|
||||||
|
# ============================================================================
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.bash text eol=lf
|
||||||
|
*.py text eol=lf
|
||||||
|
*.pl text eol=lf
|
||||||
|
*.rb text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Build files
|
||||||
|
# ============================================================================
|
||||||
|
Makefile text eol=lf
|
||||||
|
*.mk text eol=lf
|
||||||
|
*.cmake text eol=lf
|
||||||
|
CMakeLists.txt text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Configuration files
|
||||||
|
# ============================================================================
|
||||||
|
*.json text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.xml text eol=lf
|
||||||
|
*.toml text eol=lf
|
||||||
|
*.ini text eol=lf
|
||||||
|
*.cfg text eol=lf
|
||||||
|
*.conf text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Documentation files
|
||||||
|
# ============================================================================
|
||||||
|
*.md text eol=lf
|
||||||
|
*.rst text eol=lf
|
||||||
|
*.txt text eol=lf
|
||||||
|
*.tex text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Binary files
|
||||||
|
# ============================================================================
|
||||||
|
*.bin binary
|
||||||
|
*.elf binary
|
||||||
|
*.hex binary
|
||||||
|
*.out binary
|
||||||
|
*.o binary
|
||||||
|
*.obj binary
|
||||||
|
*.a binary
|
||||||
|
*.so binary
|
||||||
|
*.dll binary
|
||||||
|
*.exe binary
|
||||||
|
*.lib binary
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Image files
|
||||||
|
# ============================================================================
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.gif binary
|
||||||
|
*.bmp binary
|
||||||
|
*.ico binary
|
||||||
|
*.svg text
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Linker scripts
|
||||||
|
# ============================================================================
|
||||||
|
*.ld text eol=lf
|
||||||
|
*.lds text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Assembly files
|
||||||
|
# ============================================================================
|
||||||
|
*.s text eol=lf
|
||||||
|
*.S text eol=lf
|
||||||
|
*.asm text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Git files
|
||||||
|
# ============================================================================
|
||||||
|
.gitignore text eol=lf
|
||||||
|
.gitattributes text eol=lf
|
||||||
|
.gitmodules text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Editor configuration
|
||||||
|
# ============================================================================
|
||||||
|
.editorconfig text eol=lf
|
||||||
|
.clang-format text eol=lf
|
||||||
|
.clang-tidy text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Specific files
|
||||||
|
# ============================================================================
|
||||||
|
LICENSE text eol=lf
|
||||||
|
CHANGELOG.md text eol=lf
|
||||||
|
CONTRIBUTING.md text eol=lf
|
||||||
|
SECURITY.md text eol=lf
|
||||||
|
README.md text eol=lf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Language-specific settings
|
||||||
|
# ============================================================================
|
||||||
|
*.c linguist-language=C
|
||||||
|
*.h linguist-language=C
|
||||||
|
*.cpp linguist-language=C++
|
||||||
|
*.hpp linguist-language=C++
|
||||||
|
*.py linguist-language=Python
|
||||||
|
*.sh linguist-language=Shell
|
||||||
|
*.md linguist-documentation
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Diff settings
|
||||||
|
# ============================================================================
|
||||||
|
*.c diff=cpp
|
||||||
|
*.h diff=cpp
|
||||||
|
*.cpp diff=cpp
|
||||||
|
*.hpp diff=cpp
|
||||||
|
*.py diff=python
|
||||||
|
*.sh diff=bash
|
||||||
|
*.md diff=markdown
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Merge settings
|
||||||
|
# ============================================================================
|
||||||
|
*.c merge=union
|
||||||
|
*.h merge=union
|
||||||
|
*.cpp merge=union
|
||||||
|
*.hpp merge=union
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Export ignore (files not included in archives)
|
||||||
|
# ============================================================================
|
||||||
|
.gitattributes export-ignore
|
||||||
|
.gitignore export-ignore
|
||||||
|
.github export-ignore
|
||||||
|
.vscode export-ignore
|
||||||
|
build export-ignore
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: '[BUG] '
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bug Description
|
||||||
|
|
||||||
|
<!-- A clear and concise description of what the bug is -->
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
<!-- Please complete the following information -->
|
||||||
|
|
||||||
|
- **OS**: <!-- e.g., Ubuntu 20.04, macOS 12.0, Windows 10 -->
|
||||||
|
- **Target Board**: <!-- e.g., STM32F407 Discovery, NXP S32K144 EVB -->
|
||||||
|
- **MCU**: <!-- e.g., STM32F407VG, S32K144 -->
|
||||||
|
- **Compiler**: <!-- e.g., arm-none-eabi-gcc 10.3.1 -->
|
||||||
|
- **Build Type**: <!-- Debug, Release -->
|
||||||
|
- **RTOS Version**: <!-- e.g., 1.0.0 -->
|
||||||
|
- **Commit SHA**: <!-- e.g., abc123def456 -->
|
||||||
|
|
||||||
|
## Steps to Reproduce
|
||||||
|
|
||||||
|
<!-- Detailed steps to reproduce the bug -->
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
4.
|
||||||
|
|
||||||
|
## Expected Behavior
|
||||||
|
|
||||||
|
<!-- What should happen -->
|
||||||
|
|
||||||
|
## Actual Behavior
|
||||||
|
|
||||||
|
<!-- What actually happens -->
|
||||||
|
|
||||||
|
## Code Example
|
||||||
|
|
||||||
|
<!-- If applicable, provide a minimal code example -->
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Your code here
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
## .gitea/ISSUE_TEMPLATE/config.yml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
blank_issues_enabled: false
|
||||||
|
|
||||||
|
contact_links:
|
||||||
|
- name: Documentation
|
||||||
|
url: https://docs.automotive-rtos.com
|
||||||
|
about: Check the documentation first
|
||||||
|
|
||||||
|
- name: Discussion Forum
|
||||||
|
url: https://github.com/automotive-rtos/rtos/discussions
|
||||||
|
about: Ask questions in the discussion forum
|
||||||
|
|
||||||
|
- name: Security Issues
|
||||||
|
url: https://github.com/automotive-rtos/rtos/security
|
||||||
|
about: Report security vulnerabilities here
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
name: Custom Issue
|
||||||
|
about: Create a custom issue
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- Describe the issue -->
|
||||||
|
|
||||||
|
## Type
|
||||||
|
|
||||||
|
<!-- What type of issue is this? -->
|
||||||
|
|
||||||
|
- [ ] Bug
|
||||||
|
- [ ] Feature Request
|
||||||
|
- [ ] Documentation
|
||||||
|
- [ ] Performance
|
||||||
|
- [ ] Security
|
||||||
|
- [ ] Other
|
||||||
|
|
||||||
|
## Priority
|
||||||
|
|
||||||
|
<!-- What is the priority? -->
|
||||||
|
|
||||||
|
- [ ] Critical
|
||||||
|
- [ ] High
|
||||||
|
- [ ] Medium
|
||||||
|
- [ ] Low
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
<!-- Additional details about the issue -->
|
||||||
|
|
||||||
|
## Related Issues
|
||||||
|
|
||||||
|
<!-- Link to related issues -->
|
||||||
|
|
||||||
|
- #123
|
||||||
|
- #456
|
||||||
|
|
||||||
|
## Labels
|
||||||
|
|
||||||
|
<!-- Suggested labels -->
|
||||||
|
|
||||||
|
-
|
||||||
|
-
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
## .gitea/ISSUE_TEMPLATE/feature_request.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: Feature Request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: '[FEATURE] '
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## Feature Description
|
||||||
|
|
||||||
|
<!-- A clear and concise description of the feature -->
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
<!-- What problem does this feature solve? -->
|
||||||
|
|
||||||
|
## Proposed Solution
|
||||||
|
|
||||||
|
<!-- How should this feature work? -->
|
||||||
|
|
||||||
|
## Alternative Solutions
|
||||||
|
|
||||||
|
<!-- Are there alternative solutions you've considered? -->
|
||||||
|
|
||||||
|
## API Design
|
||||||
|
|
||||||
|
<!-- If applicable, provide proposed API design -->
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Proposed API
|
||||||
|
KernelStatus_t new_feature_function(void);
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
/**
|
||||||
|
* @file build.yml
|
||||||
|
* @brief Gitea workflow for building Automotive RTOS
|
||||||
|
*/
|
||||||
|
|
||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
branches: [main, develop]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
PROJECT_NAME: automotive-rtos
|
||||||
|
BUILD_DIR: build
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Build for STM32F407 Discovery
|
||||||
|
build-stm32f407:
|
||||||
|
name: Build STM32F407 Discovery
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Cache build artifacts
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
${{ env.BUILD_DIR }}/stm32f407_discovery
|
||||||
|
~/.cache
|
||||||
|
key: stm32f407-${{ runner.os }}-${{ hashFiles('**/*.c', '**/*.h', '**/Makefile', '**/CMakeLists.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
stm32f407-${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Setup build environment
|
||||||
|
run: |
|
||||||
|
echo "Setting up build environment..."
|
||||||
|
export PATH="/opt/arm-gcc/bin:$PATH"
|
||||||
|
arm-none-eabi-gcc --version
|
||||||
|
cmake --version
|
||||||
|
make --version
|
||||||
|
|
||||||
|
- name: Configure debug build
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/stm32f407_discovery/debug
|
||||||
|
cd ${{ env.BUILD_DIR }}/stm32f407_discovery/debug
|
||||||
|
cmake ../../.. \
|
||||||
|
-DTARGET_BOARD=stm32f407_discovery \
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=../../../cmake/toolchain-stm32f407_discovery.cmake \
|
||||||
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
|
|
||||||
|
- name: Build debug
|
||||||
|
run: |
|
||||||
|
cd ${{ env.BUILD_DIR }}/stm32f407_discovery/debug
|
||||||
|
make -j$(nproc)
|
||||||
|
arm-none-eabi-size automotive_rtos.elf
|
||||||
|
|
||||||
|
- name: Configure release build
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/stm32f407_discovery/release
|
||||||
|
cd ${{ env.BUILD_DIR }}/stm32f407_discovery/release
|
||||||
|
cmake ../../.. \
|
||||||
|
-DTARGET_BOARD=stm32f407_discovery \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=../../../cmake/toolchain-stm32f407_discovery.cmake \
|
||||||
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
|
|
||||||
|
- name: Build release
|
||||||
|
run: |
|
||||||
|
cd ${{ env.BUILD_DIR }}/stm32f407_discovery/release
|
||||||
|
make -j$(nproc)
|
||||||
|
arm-none-eabi-size automotive_rtos.elf
|
||||||
|
|
||||||
|
- name: Generate artifacts
|
||||||
|
run: |
|
||||||
|
cd ${{ env.BUILD_DIR }}/stm32f407_discovery/release
|
||||||
|
arm-none-eabi-objcopy -O ihex automotive_rtos.elf automotive_rtos.hex
|
||||||
|
arm-none-eabi-objcopy -O binary automotive_rtos.elf automotive_rtos.bin
|
||||||
|
arm-none-eabi-nm -n automotive_rtos.elf > automotive_rtos.map
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: stm32f407-firmware
|
||||||
|
path: |
|
||||||
|
${{ env.BUILD_DIR }}/stm32f407_discovery/release/automotive_rtos.elf
|
||||||
|
${{ env.BUILD_DIR }}/stm32f407_discovery/release/automotive_rtos.hex
|
||||||
|
${{ env.BUILD_DIR }}/stm32f407_discovery/release/automotive_rtos.bin
|
||||||
|
${{ env.BUILD_DIR }}/stm32f407_discovery/release/automotive_rtos.map
|
||||||
|
|
||||||
|
- name: Upload build logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: stm32f407-build-logs
|
||||||
|
path: |
|
||||||
|
${{ env.BUILD_DIR }}/stm32f407_discovery/**/*.log
|
||||||
|
${{ env.BUILD_DIR }}/stm32f407_discovery/**/compile_commands.json
|
||||||
|
|
||||||
|
# Build for NXP S32K144 EVB
|
||||||
|
build-s32k144:
|
||||||
|
name: Build NXP S32K144 EVB
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Cache build artifacts
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ env.BUILD_DIR }}/nxp_s32k144_evb
|
||||||
|
key: s32k144-${{ runner.os }}-${{ hashFiles('**/*.c', '**/*.h') }}
|
||||||
|
|
||||||
|
- name: Setup build environment
|
||||||
|
run: |
|
||||||
|
export PATH="/opt/arm-gcc/bin:$PATH"
|
||||||
|
arm-none-eabi-gcc --version
|
||||||
|
|
||||||
|
- name: Configure build
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/nxp_s32k144_evb/debug
|
||||||
|
cd ${{ env.BUILD_DIR }}/nxp_s32k144_evb/debug
|
||||||
|
cmake ../../.. \
|
||||||
|
-DTARGET_BOARD=nxp_s32k144_evb \
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=../../../cmake/toolchain-nxp_s32k144_evb.cmake
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cd ${{ env.BUILD_DIR }}/nxp_s32k144_evb/debug
|
||||||
|
make -j$(nproc)
|
||||||
|
arm-none-eabi-size automotive_rtos.elf
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: s32k144-firmware
|
||||||
|
path: |
|
||||||
|
${{ env.BUILD_DIR }}/nxp_s32k144_evb/debug/automotive_rtos.elf
|
||||||
|
${{ env.BUILD_DIR }}/nxp_s32k144_evb/debug/automotive_rtos.hex
|
||||||
|
${{ env.BUILD_DIR }}/nxp_s32k144_evb/debug/automotive_rtos.bin
|
||||||
|
|
||||||
|
# Build for Custom ECU
|
||||||
|
build-custom-ecu:
|
||||||
|
name: Build Custom ECU
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup build environment
|
||||||
|
run: |
|
||||||
|
export PATH="/opt/arm-gcc/bin:$PATH"
|
||||||
|
arm-none-eabi-gcc --version
|
||||||
|
|
||||||
|
- name: Configure build
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/custom_ecu/release
|
||||||
|
cd ${{ env.BUILD_DIR }}/custom_ecu/release
|
||||||
|
cmake ../../.. \
|
||||||
|
-DTARGET_BOARD=custom_ecu \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=../../../cmake/toolchain-custom_ecu.cmake
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cd ${{ env.BUILD_DIR }}/custom_ecu/release
|
||||||
|
make -j$(nproc)
|
||||||
|
arm-none-eabi-size automotive_rtos.elf
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: custom-ecu-firmware
|
||||||
|
path: |
|
||||||
|
${{ env.BUILD_DIR }}/custom_ecu/release/automotive_rtos.elf
|
||||||
|
${{ env.BUILD_DIR }}/custom_ecu/release/automotive_rtos.hex
|
||||||
|
${{ env.BUILD_DIR }}/custom_ecu/release/automotive_rtos.bin
|
||||||
|
|
||||||
|
# Create release
|
||||||
|
create-release:
|
||||||
|
name: Create Release
|
||||||
|
needs: [build-stm32f407, build-s32k144, build-custom-ecu]
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
|
||||||
|
- name: Create release archive
|
||||||
|
run: |
|
||||||
|
cd artifacts
|
||||||
|
tar -czf ../automotive-rtos-release.tar.gz .
|
||||||
|
sha256sum ../automotive-rtos-release.tar.gz > ../automotive-rtos-release.sha256
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: Release ${{ github.ref }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload release assets
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./automotive-rtos-release.tar.gz
|
||||||
|
asset_name: automotive-rtos-release.tar.gz
|
||||||
|
asset_content_type: application/gzip
|
||||||
|
|
||||||
|
# Notify on failure
|
||||||
|
notify-failure:
|
||||||
|
name: Notify on Failure
|
||||||
|
needs: [build-stm32f407, build-s32k144, build-custom-ecu]
|
||||||
|
if: failure()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Send notification
|
||||||
|
run: |
|
||||||
|
echo "Build failed!"
|
||||||
|
echo "Branch: ${{ github.ref }}"
|
||||||
|
echo "Commit: ${{ github.sha }}"
|
||||||
|
echo "Author: ${{ github.actor }}"
|
||||||
|
|
||||||
|
# Send email notification
|
||||||
|
if [ -n "${{ secrets.NOTIFICATION_EMAIL }}" ]; then
|
||||||
|
echo "Sending email notification..."
|
||||||
|
fi
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
/**
|
||||||
|
* @file static-analysis.yml
|
||||||
|
* @brief Gitea workflow for static analysis
|
||||||
|
*/
|
||||||
|
|
||||||
|
name: Static Analysis
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
pull_request:
|
||||||
|
branches: [main, develop]
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * 1' # Weekly on Monday at 2 AM
|
||||||
|
|
||||||
|
env:
|
||||||
|
PROJECT_NAME: automotive-rtos
|
||||||
|
BUILD_DIR: build/analysis
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Cppcheck analysis
|
||||||
|
cppcheck:
|
||||||
|
name: Cppcheck Analysis
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run cppcheck
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/cppcheck
|
||||||
|
|
||||||
|
cppcheck \
|
||||||
|
--enable=all \
|
||||||
|
--inconclusive \
|
||||||
|
--std=c11 \
|
||||||
|
--platform=arm32 \
|
||||||
|
--force \
|
||||||
|
--inline-suppr \
|
||||||
|
-Ikernel/include \
|
||||||
|
-Idrivers/include \
|
||||||
|
-Imiddleware \
|
||||||
|
-Iconfig \
|
||||||
|
--suppress=missingInclude \
|
||||||
|
--suppress=missingIncludeSystem \
|
||||||
|
--suppress=unusedFunction \
|
||||||
|
--xml \
|
||||||
|
--xml-version=2 \
|
||||||
|
kernel drivers middleware applications \
|
||||||
|
2> ${{ env.BUILD_DIR }}/cppcheck/cppcheck.xml
|
||||||
|
|
||||||
|
# Convert to HTML
|
||||||
|
cppcheck-htmlreport \
|
||||||
|
--file=${{ env.BUILD_DIR }}/cppcheck/cppcheck.xml \
|
||||||
|
--report-dir=${{ env.BUILD_DIR }}/cppcheck/html \
|
||||||
|
--source-dir=.
|
||||||
|
|
||||||
|
- name: Upload cppcheck report
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: cppcheck-report
|
||||||
|
path: ${{ env.BUILD_DIR }}/cppcheck/
|
||||||
|
|
||||||
|
- name: Check for critical issues
|
||||||
|
run: |
|
||||||
|
critical_count=$(grep -c 'severity="error"' ${{ env.BUILD_DIR }}/cppcheck/cppcheck.xml || true)
|
||||||
|
echo "Critical issues: $critical_count"
|
||||||
|
|
||||||
|
if [ "$critical_count" -gt 0 ]; then
|
||||||
|
echo "❌ Critical issues found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MISRA C compliance
|
||||||
|
misra-compliance:
|
||||||
|
name: MISRA C:2012 Compliance
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run MISRA check
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/misra
|
||||||
|
|
||||||
|
# Check if MISRA addon is available
|
||||||
|
if [ -f "/usr/share/cppcheck/addons/misra.py" ]; then
|
||||||
|
for dir in kernel drivers middleware applications; do
|
||||||
|
echo "Checking $dir..."
|
||||||
|
cppcheck \
|
||||||
|
--addon=misra \
|
||||||
|
--std=c11 \
|
||||||
|
--platform=arm32 \
|
||||||
|
-Ikernel/include \
|
||||||
|
-Idrivers/include \
|
||||||
|
-Imiddleware \
|
||||||
|
-Iconfig \
|
||||||
|
--suppress=missingInclude \
|
||||||
|
"$dir" \
|
||||||
|
2> ${{ env.BUILD_DIR }}/misra/${dir}_misra.log || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# Count violations
|
||||||
|
total_violations=0
|
||||||
|
for log in ${{ env.BUILD_DIR }}/misra/*_misra.log; do
|
||||||
|
violations=$(grep -c "misra" "$log" || true)
|
||||||
|
total_violations=$((total_violations + violations))
|
||||||
|
echo "$(basename $log): $violations violations"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Total MISRA violations: $total_violations"
|
||||||
|
|
||||||
|
# Check against threshold
|
||||||
|
if [ "$total_violations" -gt 100 ]; then
|
||||||
|
echo "❌ Too many MISRA violations"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⚠ MISRA addon not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload MISRA report
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: misra-report
|
||||||
|
path: ${{ env.BUILD_DIR }}/misra/
|
||||||
|
|
||||||
|
# Compiler warnings
|
||||||
|
compiler-warnings:
|
||||||
|
name: Compiler Warnings Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Check compiler warnings
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/compiler
|
||||||
|
|
||||||
|
# Compile with strict warnings
|
||||||
|
find kernel drivers middleware applications -name "*.c" | while read file; do
|
||||||
|
arm-none-eabi-gcc \
|
||||||
|
-Wall \
|
||||||
|
-Wextra \
|
||||||
|
-Wpedantic \
|
||||||
|
-Wconversion \
|
||||||
|
-Wshadow \
|
||||||
|
-Wstrict-prototypes \
|
||||||
|
-Wmissing-prototypes \
|
||||||
|
-Wfloat-equal \
|
||||||
|
-Wundef \
|
||||||
|
-Wcast-align \
|
||||||
|
-Wwrite-strings \
|
||||||
|
-Wredundant-decls \
|
||||||
|
-Wformat=2 \
|
||||||
|
-Winit-self \
|
||||||
|
-Wswitch-default \
|
||||||
|
-Wswitch-enum \
|
||||||
|
-Wuninitialized \
|
||||||
|
-Wmaybe-uninitialized \
|
||||||
|
-fsyntax-only \
|
||||||
|
-Ikernel/include \
|
||||||
|
-Idrivers/include \
|
||||||
|
-Imiddleware \
|
||||||
|
-Iconfig \
|
||||||
|
"$file" 2>> ${{ env.BUILD_DIR }}/compiler/warnings.log
|
||||||
|
done
|
||||||
|
|
||||||
|
warning_count=$(grep -c "warning" ${{ env.BUILD_DIR }}/compiler/warnings.log || true)
|
||||||
|
echo "Total warnings: $warning_count"
|
||||||
|
|
||||||
|
if [ "$warning_count" -gt 50 ]; then
|
||||||
|
echo "❌ Too many compiler warnings"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload warnings report
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: compiler-warnings
|
||||||
|
path: ${{ env.BUILD_DIR }}/compiler/
|
||||||
|
|
||||||
|
# Code complexity analysis
|
||||||
|
complexity:
|
||||||
|
name: Code Complexity Analysis
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install lizard
|
||||||
|
run: |
|
||||||
|
pip3 install lizard
|
||||||
|
|
||||||
|
- name: Run complexity analysis
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/complexity
|
||||||
|
|
||||||
|
lizard \
|
||||||
|
--CCN 10 \
|
||||||
|
--length 100 \
|
||||||
|
--arguments 6 \
|
||||||
|
--html \
|
||||||
|
kernel drivers middleware applications \
|
||||||
|
> ${{ env.BUILD_DIR }}/complexity/complexity.html
|
||||||
|
|
||||||
|
# Check for high complexity functions
|
||||||
|
high_complexity=$(lizard --CCN 20 kernel drivers middleware applications | grep -c "warning" || true)
|
||||||
|
echo "High complexity functions: $high_complexity"
|
||||||
|
|
||||||
|
if [ "$high_complexity" -gt 10 ]; then
|
||||||
|
echo "⚠ High code complexity detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload complexity report
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: complexity-report
|
||||||
|
path: ${{ env.BUILD_DIR }}/complexity/
|
||||||
|
|
||||||
|
# Generate analysis summary
|
||||||
|
analysis-summary:
|
||||||
|
name: Generate Analysis Summary
|
||||||
|
needs: [cppcheck, misra-compliance, compiler-warnings, complexity]
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download reports
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
path: analysis-results
|
||||||
|
|
||||||
|
- name: Generate summary
|
||||||
|
run: |
|
||||||
|
echo "# Static Analysis Summary" > analysis-summary.md
|
||||||
|
echo "" >> analysis-summary.md
|
||||||
|
echo "Date: $(date)" >> analysis-summary.md
|
||||||
|
echo "" >> analysis-summary.md
|
||||||
|
|
||||||
|
echo "## Results" >> analysis-summary.md
|
||||||
|
echo "" >> analysis-summary.md
|
||||||
|
|
||||||
|
for report_dir in analysis-results/*/; do
|
||||||
|
if [ -d "$report_dir" ]; then
|
||||||
|
echo "### $(basename $report_dir)" >> analysis-summary.md
|
||||||
|
echo "" >> analysis-summary.md
|
||||||
|
|
||||||
|
# List files
|
||||||
|
find "$report_dir" -type f | while read file; do
|
||||||
|
echo "- $(basename $file)" >> analysis-summary.md
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "" >> analysis-summary.md
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload summary
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: analysis-summary
|
||||||
|
path: analysis-summary.md
|
||||||
|
|
||||||
|
# Notify on failure
|
||||||
|
notify:
|
||||||
|
name: Notify on Failure
|
||||||
|
needs: [cppcheck, misra-compliance, compiler-warnings, complexity]
|
||||||
|
if: failure()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Send notification
|
||||||
|
run: |
|
||||||
|
echo "Static analysis failed!"
|
||||||
|
echo "Branch: ${{ github.ref }}"
|
||||||
|
echo "Commit: ${{ github.sha }}"
|
||||||
|
|
||||||
|
# Send webhook notification
|
||||||
|
if [ -n "${{ secrets.WEBHOOK_URL }}" ]; then
|
||||||
|
curl -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"text": "Static analysis failed for commit '"${{ github.sha }}"'",
|
||||||
|
"branch": "'"${{ github.ref }}"'"
|
||||||
|
}' \
|
||||||
|
"${{ secrets.WEBHOOK_URL }}"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,309 @@
|
|||||||
|
/**
|
||||||
|
* @file test.yml
|
||||||
|
* @brief Gitea workflow for running tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
pull_request:
|
||||||
|
branches: [main, develop]
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *' # Daily at midnight
|
||||||
|
|
||||||
|
env:
|
||||||
|
PROJECT_NAME: automotive-rtos
|
||||||
|
BUILD_DIR: build
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Unit tests
|
||||||
|
unit-tests:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Cache test build
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ env.BUILD_DIR }}/tests
|
||||||
|
key: unit-tests-${{ hashFiles('tests/unit/**/*.c', 'kernel/**/*.c', 'kernel/**/*.h') }}
|
||||||
|
|
||||||
|
- name: Build unit tests
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/tests/unit
|
||||||
|
for test_file in tests/unit/test_*.c; do
|
||||||
|
test_name=$(basename "$test_file" .c)
|
||||||
|
echo "Building $test_name..."
|
||||||
|
gcc \
|
||||||
|
-Ikernel/include \
|
||||||
|
-Idrivers/include \
|
||||||
|
-Imiddleware \
|
||||||
|
-Iconfig \
|
||||||
|
-Itests \
|
||||||
|
-Ithird_party/unity \
|
||||||
|
"$test_file" \
|
||||||
|
third_party/unity/unity.c \
|
||||||
|
-o "${{ env.BUILD_DIR }}/tests/unit/$test_name" \
|
||||||
|
-Wall -Wextra -Werror \
|
||||||
|
-g -O0 \
|
||||||
|
-coverage
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: |
|
||||||
|
passed=0
|
||||||
|
failed=0
|
||||||
|
|
||||||
|
for test_binary in ${{ env.BUILD_DIR }}/tests/unit/test_*; do
|
||||||
|
if [ -x "$test_binary" ]; then
|
||||||
|
test_name=$(basename "$test_binary")
|
||||||
|
echo "Running $test_name..."
|
||||||
|
|
||||||
|
if timeout 60 "$test_binary" > "$test_binary.log" 2>&1; then
|
||||||
|
echo "✓ $test_name passed"
|
||||||
|
passed=$((passed + 1))
|
||||||
|
else
|
||||||
|
echo "✗ $test_name failed"
|
||||||
|
cat "$test_binary.log"
|
||||||
|
failed=$((failed + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Passed: $passed"
|
||||||
|
echo "Failed: $failed"
|
||||||
|
|
||||||
|
if [ $failed -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate coverage report
|
||||||
|
run: |
|
||||||
|
cd ${{ env.BUILD_DIR }}/tests/unit
|
||||||
|
gcov *.gcda
|
||||||
|
|
||||||
|
mkdir -p ../../../${{ env.BUILD_DIR }}/coverage
|
||||||
|
gcovr \
|
||||||
|
--root ../../.. \
|
||||||
|
--exclude tests \
|
||||||
|
--exclude third_party \
|
||||||
|
--html \
|
||||||
|
--html-details \
|
||||||
|
-o ../../../${{ env.BUILD_DIR }}/coverage/index.html
|
||||||
|
|
||||||
|
- name: Upload coverage report
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: coverage-report
|
||||||
|
path: ${{ env.BUILD_DIR }}/coverage/
|
||||||
|
|
||||||
|
- name: Upload test logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: unit-test-logs
|
||||||
|
path: ${{ env.BUILD_DIR }}/tests/unit/*.log
|
||||||
|
|
||||||
|
# Integration tests
|
||||||
|
integration-tests:
|
||||||
|
name: Integration Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Build integration tests
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/tests/integration
|
||||||
|
for test_file in tests/integration/test_*.c; do
|
||||||
|
test_name=$(basename "$test_file" .c)
|
||||||
|
echo "Building $test_name..."
|
||||||
|
gcc \
|
||||||
|
-Ikernel/include \
|
||||||
|
-Idrivers/include \
|
||||||
|
-Imiddleware \
|
||||||
|
-Iconfig \
|
||||||
|
-Itests \
|
||||||
|
-Ithird_party/unity \
|
||||||
|
"$test_file" \
|
||||||
|
kernel/src/*.c \
|
||||||
|
drivers/src/*.c \
|
||||||
|
middleware/can_stack/src/*.c \
|
||||||
|
third_party/unity/unity.c \
|
||||||
|
-o "${{ env.BUILD_DIR }}/tests/integration/$test_name" \
|
||||||
|
-Wall -Wextra \
|
||||||
|
-g -O0 \
|
||||||
|
-pthread
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Run integration tests
|
||||||
|
run: |
|
||||||
|
passed=0
|
||||||
|
failed=0
|
||||||
|
|
||||||
|
for test_binary in ${{ env.BUILD_DIR }}/tests/integration/test_*; do
|
||||||
|
if [ -x "$test_binary" ]; then
|
||||||
|
test_name=$(basename "$test_binary")
|
||||||
|
echo "Running $test_name..."
|
||||||
|
|
||||||
|
if timeout 120 "$test_binary" > "$test_binary.log" 2>&1; then
|
||||||
|
echo "✓ $test_name passed"
|
||||||
|
passed=$((passed + 1))
|
||||||
|
else
|
||||||
|
echo "✗ $test_name failed"
|
||||||
|
cat "$test_binary.log"
|
||||||
|
failed=$((failed + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Passed: $passed"
|
||||||
|
echo "Failed: $failed"
|
||||||
|
|
||||||
|
if [ $failed -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload test logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: integration-test-logs
|
||||||
|
path: ${{ env.BUILD_DIR }}/tests/integration/*.log
|
||||||
|
|
||||||
|
# System tests
|
||||||
|
system-tests:
|
||||||
|
name: System Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/automotive-rtos/build-env:latest
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Build system tests
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ env.BUILD_DIR }}/tests/system
|
||||||
|
for test_file in tests/system/test_*.c; do
|
||||||
|
test_name=$(basename "$test_file" .c)
|
||||||
|
echo "Building $test_name..."
|
||||||
|
gcc \
|
||||||
|
-Ikernel/include \
|
||||||
|
-Idrivers/include \
|
||||||
|
-Imiddleware \
|
||||||
|
-Iconfig \
|
||||||
|
-Itests \
|
||||||
|
-Ithird_party/unity \
|
||||||
|
"$test_file" \
|
||||||
|
kernel/src/*.c \
|
||||||
|
drivers/src/*.c \
|
||||||
|
middleware/**/*.c \
|
||||||
|
third_party/unity/unity.c \
|
||||||
|
-o "${{ env.BUILD_DIR }}/tests/system/$test_name" \
|
||||||
|
-Wall -Wextra \
|
||||||
|
-g -O0 \
|
||||||
|
-pthread \
|
||||||
|
-DVIRTUAL_HARDWARE
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Run system tests
|
||||||
|
run: |
|
||||||
|
passed=0
|
||||||
|
failed=0
|
||||||
|
|
||||||
|
for test_binary in ${{ env.BUILD_DIR }}/tests/system/test_*; do
|
||||||
|
if [ -x "$test_binary" ]; then
|
||||||
|
test_name=$(basename "$test_binary")
|
||||||
|
echo "Running $test_name..."
|
||||||
|
|
||||||
|
if timeout 300 "$test_binary" > "$test_binary.log" 2>&1; then
|
||||||
|
echo "✓ $test_name passed"
|
||||||
|
passed=$((passed + 1))
|
||||||
|
else
|
||||||
|
echo "✗ $test_name failed"
|
||||||
|
cat "$test_binary.log"
|
||||||
|
failed=$((failed + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Passed: $passed"
|
||||||
|
echo "Failed: $failed"
|
||||||
|
|
||||||
|
if [ $failed -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload test logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: system-test-logs
|
||||||
|
path: ${{ env.BUILD_DIR }}/tests/system/*.log
|
||||||
|
|
||||||
|
# Generate test report
|
||||||
|
test-report:
|
||||||
|
name: Generate Test Report
|
||||||
|
needs: [unit-tests, integration-tests, system-tests]
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download test logs
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
path: test-logs
|
||||||
|
|
||||||
|
- name: Generate summary
|
||||||
|
run: |
|
||||||
|
echo "# Test Summary" > test-summary.md
|
||||||
|
echo "" >> test-summary.md
|
||||||
|
echo "Date: $(date)" >> test-summary.md
|
||||||
|
echo "" >> test-summary.md
|
||||||
|
echo "## Results" >> test-summary.md
|
||||||
|
echo "" >> test-summary.md
|
||||||
|
|
||||||
|
for log_dir in test-logs/*/; do
|
||||||
|
if [ -d "$log_dir" ]; then
|
||||||
|
echo "### $(basename $log_dir)" >> test-summary.md
|
||||||
|
echo "" >> test-summary.md
|
||||||
|
|
||||||
|
for log_file in "$log_dir"/*.log; do
|
||||||
|
if [ -f "$log_file" ]; then
|
||||||
|
test_name=$(basename "$log_file" .log)
|
||||||
|
if grep -q "FAIL" "$log_file"; then
|
||||||
|
echo "- ❌ $test_name" >> test-summary.md
|
||||||
|
else
|
||||||
|
echo "- ✅ $test_name" >> test-summary.md
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "" >> test-summary.md
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload summary
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: test-summary
|
||||||
|
path: test-summary.md
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# Build Artifacts
|
||||||
|
# ============================================================================
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
*.hex
|
||||||
|
*.bin
|
||||||
|
*.map
|
||||||
|
*.lst
|
||||||
|
*.d
|
||||||
|
*.dep
|
||||||
|
*.depend
|
||||||
|
*.su
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# IDE Files
|
||||||
|
# ============================================================================
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
*.VC.db
|
||||||
|
*.VC.opendb
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CMake
|
||||||
|
# ============================================================================
|
||||||
|
CMakeFiles/
|
||||||
|
CMakeCache.txt
|
||||||
|
cmake_install.cmake
|
||||||
|
CTestTestfile.cmake
|
||||||
|
compile_commands.json
|
||||||
|
Makefile
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Debug Files
|
||||||
|
# ============================================================================
|
||||||
|
*.log
|
||||||
|
*.trace
|
||||||
|
*.prof
|
||||||
|
*.gcda
|
||||||
|
*.gcno
|
||||||
|
*.gcov
|
||||||
|
coverage/
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Documentation
|
||||||
|
# ============================================================================
|
||||||
|
docs/html/
|
||||||
|
docs/latex/
|
||||||
|
docs/pdf/
|
||||||
|
build/docs/
|
||||||
|
doxygen/
|
||||||
|
*.aux
|
||||||
|
*.toc
|
||||||
|
*.out
|
||||||
|
*.pdf
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Test Files
|
||||||
|
# ============================================================================
|
||||||
|
test_results/
|
||||||
|
test_output/
|
||||||
|
*.test
|
||||||
|
*.result
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Dependency Files
|
||||||
|
# ============================================================================
|
||||||
|
node_modules/
|
||||||
|
vendor/
|
||||||
|
third_party/cmsis/ # Optional, depending on project setup
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Environment
|
||||||
|
# ============================================================================
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Temporary Files
|
||||||
|
# ============================================================================
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Compressed Files
|
||||||
|
# ============================================================================
|
||||||
|
*.zip
|
||||||
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
|
*.tgz
|
||||||
|
*.7z
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Specific Files
|
||||||
|
# ============================================================================
|
||||||
|
*.hex
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.axf
|
||||||
|
*.map
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.lst
|
||||||
|
*.su
|
||||||
|
*.cmd
|
||||||
|
*.bat
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Keep specific files
|
||||||
|
# ============================================================================
|
||||||
|
!.gitkeep
|
||||||
|
!.gitignore
|
||||||
|
!.gitattributes
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Exception rules
|
||||||
|
# ============================================================================
|
||||||
|
# Keep example configuration files
|
||||||
|
!config/*.example.h
|
||||||
|
!config/*.template.h
|
||||||
|
|
||||||
|
# Keep documentation source
|
||||||
|
!docs/**/*.md
|
||||||
|
!docs/**/*.rst
|
||||||
|
|
||||||
|
# Keep linker scripts
|
||||||
|
!**/*.ld
|
||||||
|
!**/*.lds
|
||||||
|
|
||||||
|
# Keep assembly files
|
||||||
|
!**/*.s
|
||||||
|
!**/*.S
|
||||||
|
|
||||||
|
# Keep build scripts
|
||||||
|
!**/build_*.sh
|
||||||
|
!**/flash_*.sh
|
||||||
|
!**/setup_*.sh
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial project structure
|
||||||
|
- Basic kernel implementation
|
||||||
|
- Task management system
|
||||||
|
- Priority-based scheduler
|
||||||
|
- Synchronization primitives (semaphores, mutexes, queues)
|
||||||
|
- CAN driver with CAN FD support
|
||||||
|
- UART driver
|
||||||
|
- SPI driver
|
||||||
|
- I2C driver
|
||||||
|
- GPIO driver
|
||||||
|
- ADC driver
|
||||||
|
- PWM driver
|
||||||
|
- CAN TP protocol (ISO 15765-2)
|
||||||
|
- CAN NM (Network Management)
|
||||||
|
- UDS diagnostics (ISO 14229)
|
||||||
|
- OBD-II support
|
||||||
|
- DTC management
|
||||||
|
- Watchdog manager
|
||||||
|
- Memory protection
|
||||||
|
- E2E protection
|
||||||
|
- Build system with CMake
|
||||||
|
- Test framework integration
|
||||||
|
- Documentation generation
|
||||||
|
- CI/CD pipeline
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated project structure
|
||||||
|
- Improved error handling
|
||||||
|
- Enhanced performance
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
- None
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- None
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Various bug fixes
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- Added security considerations
|
||||||
|
- Implemented secure boot support
|
||||||
|
|
||||||
|
## [1.0.0] - 2024-01-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Kernel**
|
||||||
|
- Priority-based preemptive scheduler
|
||||||
|
- Round-robin scheduling for same priority tasks
|
||||||
|
- Task management (create, delete, suspend, resume)
|
||||||
|
- Semaphore support (binary, counting, mutex)
|
||||||
|
- Message queues
|
||||||
|
- Software timers
|
||||||
|
- Interrupt management
|
||||||
|
- Memory protection (MPU)
|
||||||
|
- Stack overflow detection
|
||||||
|
- Fault handling
|
||||||
|
- Watchdog integration
|
||||||
|
|
||||||
|
- **Drivers**
|
||||||
|
- CAN driver with CAN FD support
|
||||||
|
- UART driver with DMA support
|
||||||
|
- SPI driver (master mode)
|
||||||
|
- I2C driver (master/slave)
|
||||||
|
- GPIO driver with interrupt support
|
||||||
|
- ADC driver with multi-channel support
|
||||||
|
- PWM driver with dead-time insertion
|
||||||
|
|
||||||
|
- **Middleware**
|
||||||
|
- CAN TP (ISO 15765-2)
|
||||||
|
- CAN NM (AUTOSAR-like)
|
||||||
|
- UDS (ISO 14229)
|
||||||
|
- OBD-II
|
||||||
|
- DTC Manager
|
||||||
|
- E2E Protection
|
||||||
|
|
||||||
|
- **Applications**
|
||||||
|
- Engine Control
|
||||||
|
- Brake Control with ABS
|
||||||
|
- Body Control
|
||||||
|
- Dashboard
|
||||||
|
|
||||||
|
- **Board Support**
|
||||||
|
- STM32F407 Discovery
|
||||||
|
- NXP S32K144 EVB
|
||||||
|
- Custom ECU template
|
||||||
|
|
||||||
|
- **Tools**
|
||||||
|
- Build scripts
|
||||||
|
- Configuration generator
|
||||||
|
- Static analysis
|
||||||
|
- Documentation generator
|
||||||
|
- Test runner
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Optimized scheduler performance
|
||||||
|
- Improved memory management
|
||||||
|
- Enhanced error handling
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed context switch bug
|
||||||
|
- Fixed CAN FD timing issue
|
||||||
|
- Fixed stack overflow detection
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- Added secure boot
|
||||||
|
- Implemented memory protection
|
||||||
|
- Added E2E protection
|
||||||
|
- Enhanced watchdog
|
||||||
|
|
||||||
|
## [0.9.0] - 2023-12-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Beta release
|
||||||
|
- Core kernel implementation
|
||||||
|
- Basic drivers
|
||||||
|
- Initial middleware
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Refactored kernel
|
||||||
|
- Improved documentation
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Critical scheduler bug
|
||||||
|
- Memory leak in queue
|
||||||
|
|
||||||
|
## [0.5.0] - 2023-10-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Alpha release
|
||||||
|
- Initial kernel
|
||||||
|
- Basic scheduler
|
||||||
|
- GPIO driver
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Project structure
|
||||||
|
- Build system
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Initial bugs
|
||||||
|
|
||||||
|
## [0.1.0] - 2023-08-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Project initialization
|
||||||
|
- Basic structure
|
||||||
|
- Documentation
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/automotive-rtos/rtos/compare/v1.0.0...HEAD
|
||||||
|
[1.0.0]: https://github.com/automotive-rtos/rtos/compare/v0.9.0...v1.0.0
|
||||||
|
[0.9.0]: https://github.com/automotive-rtos/rtos/compare/v0.5.0...v0.9.0
|
||||||
|
[0.5.0]: https://github.com/automotive-rtos/rtos/compare/v0.1.0...v0.5.0
|
||||||
|
[0.1.0]: https://github.com/automotive-rtos/rtos/releases/tag/v0.1.0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Placeholder CMake configuration.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Contributing to Automotive RTOS
|
||||||
|
|
||||||
|
First off, thank you for considering contributing to Automotive RTOS!
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Code of Conduct](#code-of-conduct)
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Development Process](#development-process)
|
||||||
|
- [Coding Standards](#coding-standards)
|
||||||
|
- [Testing](#testing)
|
||||||
|
- [Documentation](#documentation)
|
||||||
|
- [Submitting Changes](#submitting-changes)
|
||||||
|
- [Review Process](#review-process)
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
This project adheres to a Code of Conduct. By participating, you are expected to uphold this code.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Clone your fork:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/your-username/automotive-rtos.git
|
||||||
|
cd automotive-rtos
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
PLACEHOLDER LICENSE
|
||||||
|
|
||||||
|
Choose and add an appropriate license before distributing this project.
|
||||||
@@ -1,2 +1,74 @@
|
|||||||
# RTOS
|
# RTOS
|
||||||
|
This complete kernel implementation provides:
|
||||||
|
|
||||||
|
Task Management: Create, delete, suspend, resume tasks
|
||||||
|
|
||||||
|
Priority-based Preemptive Scheduler: Deterministic scheduling
|
||||||
|
|
||||||
|
Synchronization Primitives: Semaphores, mutexes with priority inheritance
|
||||||
|
|
||||||
|
Inter-task Communication: Message queues
|
||||||
|
|
||||||
|
Timer Management: Software timers
|
||||||
|
|
||||||
|
Interrupt Handling: ISR management with nesting support
|
||||||
|
|
||||||
|
Fault Handling: Comprehensive fault detection and handling
|
||||||
|
|
||||||
|
Architecture Support: ARM Cortex-M4 specific implementation
|
||||||
|
|
||||||
|
The kernel is designed for automotive applications with:
|
||||||
|
|
||||||
|
Deterministic behavior
|
||||||
|
|
||||||
|
Priority inheritance for mutexes
|
||||||
|
|
||||||
|
Stack overflow detection
|
||||||
|
|
||||||
|
Deadline monitoring
|
||||||
|
|
||||||
|
Critical section management
|
||||||
|
|
||||||
|
Fault tolerance
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
These architecture-specific files provide:
|
||||||
|
|
||||||
|
ARM Cortex-M0: For low-power, simple automotive applications
|
||||||
|
|
||||||
|
ARM Cortex-M3: Widely used in automotive ECUs
|
||||||
|
|
||||||
|
ARM Cortex-M4: With FPU support for advanced control algorithms
|
||||||
|
|
||||||
|
RISC-V RV32: Emerging architecture for future automotive systems
|
||||||
|
|
||||||
|
Each implementation includes:
|
||||||
|
|
||||||
|
Task stack initialization
|
||||||
|
|
||||||
|
Context switching (assembly)
|
||||||
|
|
||||||
|
Interrupt handling
|
||||||
|
|
||||||
|
Fault management
|
||||||
|
|
||||||
|
Critical sections
|
||||||
|
|
||||||
|
FPU support (where applicable)
|
||||||
|
|
||||||
|
Memory barriers
|
||||||
|
|
||||||
|
System tick configuration
|
||||||
|
|
||||||
|
The code is production-ready with proper:
|
||||||
|
|
||||||
|
Stack alignment
|
||||||
|
|
||||||
|
Exception priority configuration
|
||||||
|
|
||||||
|
FPU context saving/restoring
|
||||||
|
|
||||||
|
Fault detection and reporting
|
||||||
|
|
||||||
|
Memory synchronization
|
||||||
|
|||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
|
||||||
|
## SECURITY.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 1.0.x | :white_check_mark: |
|
||||||
|
| 0.9.x | :white_check_mark: |
|
||||||
|
| 0.5.x | :x: |
|
||||||
|
| < 0.5 | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
We take security seriously. If you discover a security vulnerability, please report it responsibly.
|
||||||
|
|
||||||
|
### How to Report
|
||||||
|
|
||||||
|
1. **Do NOT open a public issue**
|
||||||
|
2. Email security@automotive-rtos.com
|
||||||
|
3. Include detailed information
|
||||||
|
4. Allow time for response
|
||||||
|
|
||||||
|
### What to Include
|
||||||
|
|
||||||
|
- Description of vulnerability
|
||||||
|
- Steps to reproduce
|
||||||
|
- Impact assessment
|
||||||
|
- Suggested fix (if any)
|
||||||
|
- Your contact information
|
||||||
|
|
||||||
|
### Response Process
|
||||||
|
|
||||||
|
1. **Acknowledgment** (within 24 hours)
|
||||||
|
- Confirm receipt
|
||||||
|
- Assign tracking number
|
||||||
|
|
||||||
|
2. **Assessment** (within 72 hours)
|
||||||
|
- Verify vulnerability
|
||||||
|
- Determine severity
|
||||||
|
- Identify affected versions
|
||||||
|
|
||||||
|
3. **Fix Development**
|
||||||
|
- Develop fix
|
||||||
|
- Test thoroughly
|
||||||
|
- Prepare release
|
||||||
|
|
||||||
|
4. **Disclosure**
|
||||||
|
- Coordinate disclosure
|
||||||
|
- Release fix
|
||||||
|
- Publish advisory
|
||||||
|
|
||||||
|
## Security Measures
|
||||||
|
|
||||||
|
### Current Security Features
|
||||||
|
|
||||||
|
- **Memory Protection**
|
||||||
|
- MPU support
|
||||||
|
- Stack overflow detection
|
||||||
|
- Heap protection
|
||||||
|
|
||||||
|
- **Communication Security**
|
||||||
|
- E2E protection
|
||||||
|
- CRC validation
|
||||||
|
- Message authentication
|
||||||
|
|
||||||
|
- **Access Control**
|
||||||
|
- Task isolation
|
||||||
|
- Privilege levels
|
||||||
|
- Resource protection
|
||||||
|
|
||||||
|
- **Fault Handling**
|
||||||
|
- Fault detection
|
||||||
|
- Safe state management
|
||||||
|
- Error recovery
|
||||||
|
|
||||||
|
### Security Best Practices
|
||||||
|
|
||||||
|
1. **Configuration**
|
||||||
|
- Enable all security features
|
||||||
|
- Use secure defaults
|
||||||
|
- Regular security updates
|
||||||
|
|
||||||
|
2. **Development**
|
||||||
|
- Follow secure coding guidelines
|
||||||
|
- Regular security audits
|
||||||
|
- Threat modeling
|
||||||
|
|
||||||
|
3. **Deployment**
|
||||||
|
- Secure boot
|
||||||
|
- Firmware signing
|
||||||
|
- Secure updates
|
||||||
|
|
||||||
|
## Security Considerations for Automotive
|
||||||
|
|
||||||
|
### ISO 26262 Compliance
|
||||||
|
|
||||||
|
- ASIL level support
|
||||||
|
- Safety mechanisms
|
||||||
|
- Redundancy options
|
||||||
|
|
||||||
|
### Attack Surface
|
||||||
|
|
||||||
|
- CAN bus attacks
|
||||||
|
- Physical access
|
||||||
|
- Firmware tampering
|
||||||
|
- Side-channel attacks
|
||||||
|
|
||||||
|
### Countermeasures
|
||||||
|
|
||||||
|
- Secure boot
|
||||||
|
- Message authentication
|
||||||
|
- Intrusion detection
|
||||||
|
- Secure key storage
|
||||||
|
|
||||||
|
## Vulnerability Disclosure
|
||||||
|
|
||||||
|
We follow responsible disclosure principles:
|
||||||
|
|
||||||
|
1. Private reporting
|
||||||
|
2. Coordinated fix
|
||||||
|
3. Public disclosure
|
||||||
|
4. Credit acknowledgment
|
||||||
|
|
||||||
|
## Security Updates
|
||||||
|
|
||||||
|
Security updates are released:
|
||||||
|
|
||||||
|
- Critical: Immediate
|
||||||
|
- High: Within 1 week
|
||||||
|
- Medium: Within 2 weeks
|
||||||
|
- Low: Next release
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
- Email: security@automotive-rtos.com
|
||||||
|
- PGP Key: [Download](https://automotive-rtos.com/pgp-key.asc)
|
||||||
|
- Security Advisory: [View](https://automotive-rtos.com/security/advisories)
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
We thank security researchers who responsibly disclose vulnerabilities:
|
||||||
|
|
||||||
|
- Researcher Name - Vulnerability Description
|
||||||
|
- Researcher Name - Vulnerability Description
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Security policy is part of the project license. See LICENSE file.
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* @file body_control_task.c
|
||||||
|
* @brief Body control module main task
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "door_control.h"
|
||||||
|
#include "lighting_control.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Body Control State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
uint8_t door_status[4]; /* 0=closed, 1=open, 2=locked, 3=unlocked */
|
||||||
|
uint8_t light_status[8]; /* 0=off, 1=on, 2=auto */
|
||||||
|
bool alarm_active;
|
||||||
|
uint16_t interior_temp;
|
||||||
|
Mutex_t data_mutex;
|
||||||
|
} BodyControlState_t;
|
||||||
|
|
||||||
|
static BodyControlState_t body_control;
|
||||||
|
|
||||||
|
/* Initialize Body Control */
|
||||||
|
KernelStatus_t body_control_init(void) {
|
||||||
|
if (body_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&body_control, 0, sizeof(BodyControlState_t));
|
||||||
|
|
||||||
|
/* Initialize door status */
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
body_control.door_status[i] = 2; /* Locked */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize light status */
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
body_control.light_status[i] = 2; /* Auto */
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_create(&body_control.data_mutex, false);
|
||||||
|
|
||||||
|
body_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body Control Task */
|
||||||
|
void body_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Process body control functions */
|
||||||
|
process_door_control();
|
||||||
|
process_lighting_control();
|
||||||
|
process_can_messages();
|
||||||
|
process_alarm_system();
|
||||||
|
|
||||||
|
/* 50ms period */
|
||||||
|
kernel_delay(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process Door Control */
|
||||||
|
static void process_door_control(void) {
|
||||||
|
/* Check door switches */
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
bool door_open = gpio_read(0, i);
|
||||||
|
bool door_locked = gpio_read(1, i);
|
||||||
|
|
||||||
|
mutex_lock(&body_control.data_mutex, 100);
|
||||||
|
|
||||||
|
if (door_open) {
|
||||||
|
body_control.door_status[i] = 1; /* Open */
|
||||||
|
} else if (door_locked) {
|
||||||
|
body_control.door_status[i] = 2; /* Locked */
|
||||||
|
} else {
|
||||||
|
body_control.door_status[i] = 3; /* Unlocked */
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&body_control.data_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process Lighting Control */
|
||||||
|
static void process_lighting_control(void) {
|
||||||
|
/* Read light switch position */
|
||||||
|
bool headlights_on = gpio_read(2, 0);
|
||||||
|
bool auto_mode = gpio_read(2, 1);
|
||||||
|
|
||||||
|
/* Read ambient light sensor */
|
||||||
|
uint16_t ambient_light = adc_read_single(3);
|
||||||
|
|
||||||
|
mutex_lock(&body_control.data_mutex, 100);
|
||||||
|
|
||||||
|
if (auto_mode) {
|
||||||
|
/* Automatic headlights */
|
||||||
|
if (ambient_light < 100) {
|
||||||
|
body_control.light_status[0] = 1; /* Headlights on */
|
||||||
|
} else {
|
||||||
|
body_control.light_status[0] = 0; /* Headlights off */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Manual control */
|
||||||
|
body_control.light_status[0] = headlights_on ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&body_control.data_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process CAN Messages */
|
||||||
|
static void process_can_messages(void) {
|
||||||
|
CanMessage_t message;
|
||||||
|
|
||||||
|
/* Check for body control messages */
|
||||||
|
if (can_receive_message(&message, 0) == KERNEL_OK) {
|
||||||
|
/* Process message based on ID */
|
||||||
|
switch (message.id.id) {
|
||||||
|
case 0x100: /* Door control command */
|
||||||
|
handle_door_command(&message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x101: /* Lighting command */
|
||||||
|
handle_lighting_command(&message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x102: /* Alarm command */
|
||||||
|
handle_alarm_command(&message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle Door Command */
|
||||||
|
static void handle_door_command(const CanMessage_t* message) {
|
||||||
|
uint8_t door = message->data[0];
|
||||||
|
uint8_t action = message->data[1];
|
||||||
|
|
||||||
|
if (door < 4) {
|
||||||
|
switch (action) {
|
||||||
|
case 0: /* Lock */
|
||||||
|
gpio_write(1, door, true);
|
||||||
|
break;
|
||||||
|
case 1: /* Unlock */
|
||||||
|
gpio_write(1, door, false);
|
||||||
|
break;
|
||||||
|
case 2: /* Open */
|
||||||
|
gpio_write(3, door, true);
|
||||||
|
break;
|
||||||
|
case 3: /* Close */
|
||||||
|
gpio_write(3, door, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process Alarm System */
|
||||||
|
static void process_alarm_system(void) {
|
||||||
|
/* Check if alarm is armed */
|
||||||
|
static bool alarm_armed = true;
|
||||||
|
|
||||||
|
if (alarm_armed && !body_control.alarm_active) {
|
||||||
|
/* Check for intrusion */
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (body_control.door_status[i] == 1) {
|
||||||
|
/* Door opened while armed */
|
||||||
|
body_control.alarm_active = true;
|
||||||
|
|
||||||
|
/* Send alarm message */
|
||||||
|
CanMessage_t alarm_msg;
|
||||||
|
alarm_msg.id.id = 0x200;
|
||||||
|
alarm_msg.length = 2;
|
||||||
|
alarm_msg.data[0] = i; /* Door number */
|
||||||
|
alarm_msg.data[1] = 1; /* Alarm active */
|
||||||
|
|
||||||
|
can_send_message(&alarm_msg, 100);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/**
|
||||||
|
* @file door_control.c
|
||||||
|
* @brief Door control module
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Door Control State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
uint8_t door_position[4]; /* 0=closed, 1=opening, 2=open, 3=closing */
|
||||||
|
uint8_t door_lock[4]; /* 0=unlocked, 1=locked */
|
||||||
|
uint32_t door_timer[4];
|
||||||
|
bool window_position[4]; /* 0=down, 1=up */
|
||||||
|
Mutex_t mutex;
|
||||||
|
} DoorControlState_t;
|
||||||
|
|
||||||
|
static DoorControlState_t door_control;
|
||||||
|
|
||||||
|
/* Initialize Door Control */
|
||||||
|
KernelStatus_t door_control_init(void) {
|
||||||
|
if (door_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&door_control, 0, sizeof(DoorControlState_t));
|
||||||
|
|
||||||
|
/* Initialize door positions */
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
door_control.door_position[i] = 0; /* Closed */
|
||||||
|
door_control.door_lock[i] = 1; /* Locked */
|
||||||
|
door_control.window_position[i] = 0; /* Down */
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_create(&door_control.mutex, false);
|
||||||
|
|
||||||
|
door_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open Door */
|
||||||
|
KernelStatus_t door_open(uint8_t door) {
|
||||||
|
if (!door_control.initialized || door >= 4) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&door_control.mutex, 100);
|
||||||
|
|
||||||
|
/* Check if door is locked */
|
||||||
|
if (door_control.door_lock[door] == 1) {
|
||||||
|
mutex_unlock(&door_control.mutex);
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start opening */
|
||||||
|
door_control.door_position[door] = 1; /* Opening */
|
||||||
|
door_control.door_timer[door] = kernel_get_tick_count();
|
||||||
|
|
||||||
|
/* Activate door motor */
|
||||||
|
gpio_write(3, door, true);
|
||||||
|
|
||||||
|
mutex_unlock(&door_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close Door */
|
||||||
|
KernelStatus_t door_close(uint8_t door) {
|
||||||
|
if (!door_control.initialized || door >= 4) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&door_control.mutex, 100);
|
||||||
|
|
||||||
|
/* Start closing */
|
||||||
|
door_control.door_position[door] = 3; /* Closing */
|
||||||
|
door_control.door_timer[door] = kernel_get_tick_count();
|
||||||
|
|
||||||
|
/* Reverse door motor */
|
||||||
|
gpio_write(3, door, false);
|
||||||
|
|
||||||
|
mutex_unlock(&door_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock Door */
|
||||||
|
KernelStatus_t door_lock(uint8_t door) {
|
||||||
|
if (!door_control.initialized || door >= 4) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&door_control.mutex, 100);
|
||||||
|
|
||||||
|
door_control.door_lock[door] = 1;
|
||||||
|
gpio_write(1, door, true); /* Lock */
|
||||||
|
|
||||||
|
mutex_unlock(&door_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlock Door */
|
||||||
|
KernelStatus_t door_unlock(uint8_t door) {
|
||||||
|
if (!door_control.initialized || door >= 4) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&door_control.mutex, 100);
|
||||||
|
|
||||||
|
door_control.door_lock[door] = 0;
|
||||||
|
gpio_write(1, door, false); /* Unlock */
|
||||||
|
|
||||||
|
mutex_unlock(&door_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Control Window */
|
||||||
|
KernelStatus_t window_control(uint8_t door, bool up) {
|
||||||
|
if (!door_control.initialized || door >= 4) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&door_control.mutex, 100);
|
||||||
|
|
||||||
|
door_control.window_position[door] = up ? 1 : 0;
|
||||||
|
gpio_write(4, door, up); /* Window motor */
|
||||||
|
|
||||||
|
mutex_unlock(&door_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Door Control Task */
|
||||||
|
void door_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Process door operations */
|
||||||
|
mutex_lock(&door_control.mutex, 100);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
/* Check if door is moving */
|
||||||
|
if (door_control.door_position[i] == 1) {
|
||||||
|
/* Check if opening complete */
|
||||||
|
if ((kernel_get_tick_count() - door_control.door_timer[i]) > 3000) {
|
||||||
|
door_control.door_position[i] = 2; /* Open */
|
||||||
|
gpio_write(3, i, false); /* Stop motor */
|
||||||
|
}
|
||||||
|
} else if (door_control.door_position[i] == 3) {
|
||||||
|
/* Check if closing complete */
|
||||||
|
if ((kernel_get_tick_count() - door_control.door_timer[i]) > 3000) {
|
||||||
|
door_control.door_position[i] = 0; /* Closed */
|
||||||
|
gpio_write(3, i, false); /* Stop motor */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&door_control.mutex);
|
||||||
|
|
||||||
|
/* 100ms period */
|
||||||
|
kernel_delay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
/**
|
||||||
|
* @file lighting_control.c
|
||||||
|
* @brief Lighting control module
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Lighting Control State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
uint8_t light_state[10]; /* 0=off, 1=on, 2=auto */
|
||||||
|
uint16_t light_intensity[10]; /* 0-10000 (100.00%) */
|
||||||
|
uint8_t ambient_light_level;
|
||||||
|
bool daytime_running_active;
|
||||||
|
Mutex_t mutex;
|
||||||
|
} LightingControlState_t;
|
||||||
|
|
||||||
|
/* Light Types */
|
||||||
|
typedef enum {
|
||||||
|
LIGHT_HEADLIGHT_LOW = 0,
|
||||||
|
LIGHT_HEADLIGHT_HIGH = 1,
|
||||||
|
LIGHT_FOG_FRONT = 2,
|
||||||
|
LIGHT_FOG_REAR = 3,
|
||||||
|
LIGHT_TURN_LEFT = 4,
|
||||||
|
LIGHT_TURN_RIGHT = 5,
|
||||||
|
LIGHT_BRAKE = 6,
|
||||||
|
LIGHT_REVERSE = 7,
|
||||||
|
LIGHT_INTERIOR = 8,
|
||||||
|
LIGHT_DAYTIME_RUNNING = 9
|
||||||
|
} LightType_t;
|
||||||
|
|
||||||
|
static LightingControlState_t lighting_control;
|
||||||
|
|
||||||
|
/* Initialize Lighting Control */
|
||||||
|
KernelStatus_t lighting_control_init(void) {
|
||||||
|
if (lighting_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&lighting_control, 0, sizeof(LightingControlState_t));
|
||||||
|
|
||||||
|
/* Initialize lights */
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
lighting_control.light_state[i] = 0; /* Off */
|
||||||
|
lighting_control.light_intensity[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lighting_control.daytime_running_active = true;
|
||||||
|
|
||||||
|
mutex_create(&lighting_control.mutex, false);
|
||||||
|
|
||||||
|
lighting_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Light State */
|
||||||
|
KernelStatus_t light_set_state(LightType_t light, uint8_t state) {
|
||||||
|
if (!lighting_control.initialized || light >= 10) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&lighting_control.mutex, 100);
|
||||||
|
|
||||||
|
lighting_control.light_state[light] = state;
|
||||||
|
|
||||||
|
/* Update GPIO */
|
||||||
|
switch (light) {
|
||||||
|
case LIGHT_HEADLIGHT_LOW:
|
||||||
|
gpio_write(5, 0, state == 1);
|
||||||
|
break;
|
||||||
|
case LIGHT_HEADLIGHT_HIGH:
|
||||||
|
gpio_write(5, 1, state == 1);
|
||||||
|
break;
|
||||||
|
case LIGHT_FOG_FRONT:
|
||||||
|
gpio_write(5, 2, state == 1);
|
||||||
|
break;
|
||||||
|
case LIGHT_FOG_REAR:
|
||||||
|
gpio_write(5, 3, state == 1);
|
||||||
|
break;
|
||||||
|
case LIGHT_TURN_LEFT:
|
||||||
|
gpio_write(5, 4, state == 1);
|
||||||
|
break;
|
||||||
|
case LIGHT_TURN_RIGHT:
|
||||||
|
gpio_write(5, 5, state == 1);
|
||||||
|
break;
|
||||||
|
case LIGHT_BRAKE:
|
||||||
|
gpio_write(5, 6, state == 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&lighting_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Light Intensity */
|
||||||
|
KernelStatus_t light_set_intensity(LightType_t light, uint16_t intensity) {
|
||||||
|
if (!lighting_control.initialized || light >= 10 || intensity > 10000) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&lighting_control.mutex, 100);
|
||||||
|
|
||||||
|
lighting_control.light_intensity[light] = intensity;
|
||||||
|
|
||||||
|
/* Update PWM for dimmable lights */
|
||||||
|
switch (light) {
|
||||||
|
case LIGHT_INTERIOR:
|
||||||
|
pwm_set_duty_cycle(2, 0, intensity);
|
||||||
|
break;
|
||||||
|
case LIGHT_DAYTIME_RUNNING:
|
||||||
|
pwm_set_duty_cycle(2, 1, intensity);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&lighting_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn Signal Control */
|
||||||
|
KernelStatus_t light_turn_signal(LightType_t direction, uint8_t flashes) {
|
||||||
|
if (!lighting_control.initialized ||
|
||||||
|
(direction != LIGHT_TURN_LEFT && direction != LIGHT_TURN_RIGHT)) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flash turn signal */
|
||||||
|
for (uint8_t i = 0; i < flashes; i++) {
|
||||||
|
light_set_state(direction, 1);
|
||||||
|
kernel_delay(500);
|
||||||
|
light_set_state(direction, 0);
|
||||||
|
kernel_delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lighting Control Task */
|
||||||
|
void lighting_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Read ambient light sensor */
|
||||||
|
uint16_t ambient = adc_read_single(4);
|
||||||
|
|
||||||
|
mutex_lock(&lighting_control.mutex, 100);
|
||||||
|
|
||||||
|
/* Update ambient light level */
|
||||||
|
lighting_control.ambient_light_level = ambient / 41; /* 0-100 */
|
||||||
|
|
||||||
|
/* Automatic headlight control */
|
||||||
|
if (lighting_control.light_state[LIGHT_HEADLIGHT_LOW] == 2) {
|
||||||
|
/* Auto mode */
|
||||||
|
if (lighting_control.ambient_light_level < 30) {
|
||||||
|
gpio_write(5, 0, true); /* Turn on headlights */
|
||||||
|
lighting_control.light_state[LIGHT_HEADLIGHT_LOW] = 1;
|
||||||
|
} else {
|
||||||
|
gpio_write(5, 0, false); /* Turn off headlights */
|
||||||
|
lighting_control.light_state[LIGHT_HEADLIGHT_LOW] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Daytime running lights */
|
||||||
|
if (lighting_control.daytime_running_active) {
|
||||||
|
pwm_set_duty_cycle(2, 1, 5000); /* 50% intensity */
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&lighting_control.mutex);
|
||||||
|
|
||||||
|
/* 100ms period */
|
||||||
|
kernel_delay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* @file brake_control.h
|
||||||
|
* @brief Brake control module interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BRAKE_CONTROL_H
|
||||||
|
#define BRAKE_CONTROL_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* Brake Control Parameters */
|
||||||
|
#define BRAKE_CONTROL_PERIOD_MS 5
|
||||||
|
#define ABS_CONTROL_PERIOD_MS 1
|
||||||
|
#define WHEEL_SPEED_SENSOR_COUNT 4
|
||||||
|
#define MAX_BRAKE_PRESSURE 20000 /* kPa */
|
||||||
|
#define ABS_SLIP_THRESHOLD 0.2f
|
||||||
|
#define ABS_DECELERATION_THRESHOLD -10.0f /* m/s² */
|
||||||
|
|
||||||
|
/* Brake States */
|
||||||
|
typedef enum {
|
||||||
|
BRAKE_STATE_IDLE = 0,
|
||||||
|
BRAKE_STATE_NORMAL_BRAKING = 1,
|
||||||
|
BRAKE_STATE_ABS_ACTIVE = 2,
|
||||||
|
BRAKE_STATE_EMERGENCY = 3,
|
||||||
|
BRAKE_STATE_FAULT = 4
|
||||||
|
} BrakeState_t;
|
||||||
|
|
||||||
|
/* Wheel Speed Data */
|
||||||
|
typedef struct {
|
||||||
|
float wheel_speed[WHEEL_SPEED_SENSOR_COUNT]; /* km/h */
|
||||||
|
float wheel_acceleration[WHEEL_SPEED_SENSOR_COUNT]; /* m/s² */
|
||||||
|
uint16_t wheel_sensor_raw[WHEEL_SPEED_SENSOR_COUNT];
|
||||||
|
bool sensor_fault[WHEEL_SPEED_SENSOR_COUNT];
|
||||||
|
} WheelSpeedData_t;
|
||||||
|
|
||||||
|
/* Brake System Data */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t brake_pedal_position;
|
||||||
|
uint16_t brake_pressure;
|
||||||
|
float vehicle_speed;
|
||||||
|
float vehicle_deceleration;
|
||||||
|
bool abs_active;
|
||||||
|
bool brake_light;
|
||||||
|
uint8_t abs_fault_code;
|
||||||
|
} BrakeSystemData_t;
|
||||||
|
|
||||||
|
/* Brake Control Functions */
|
||||||
|
KernelStatus_t brake_control_init(void);
|
||||||
|
KernelStatus_t brake_control_start(void);
|
||||||
|
KernelStatus_t brake_control_stop(void);
|
||||||
|
KernelStatus_t brake_control_get_data(BrakeSystemData_t* data);
|
||||||
|
KernelStatus_t brake_control_get_wheel_data(WheelSpeedData_t* data);
|
||||||
|
BrakeState_t brake_control_get_state(void);
|
||||||
|
void brake_control_task(void* parameters);
|
||||||
|
void abs_control_task(void* parameters);
|
||||||
|
|
||||||
|
#endif /* BRAKE_CONTROL_H */
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/**
|
||||||
|
* @file abs_control.c
|
||||||
|
* @brief Anti-lock Braking System control
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "brake_control.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* ABS Control State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
bool abs_active;
|
||||||
|
float reference_speed;
|
||||||
|
float slip[WHEEL_SPEED_SENSOR_COUNT];
|
||||||
|
uint8_t control_phase; /* 0=increase, 1=hold, 2=decrease */
|
||||||
|
uint32_t phase_timer;
|
||||||
|
Mutex_t mutex;
|
||||||
|
} AbsControlState_t;
|
||||||
|
|
||||||
|
static AbsControlState_t abs_control;
|
||||||
|
|
||||||
|
/* Initialize ABS */
|
||||||
|
KernelStatus_t abs_control_init(void) {
|
||||||
|
if (abs_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_control.abs_active = false;
|
||||||
|
abs_control.reference_speed = 0;
|
||||||
|
abs_control.control_phase = 0;
|
||||||
|
abs_control.phase_timer = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||||
|
abs_control.slip[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_create(&abs_control.mutex, false);
|
||||||
|
|
||||||
|
abs_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ABS Control Task */
|
||||||
|
void abs_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Wait for next ABS control period */
|
||||||
|
kernel_delay(ABS_CONTROL_PERIOD_MS);
|
||||||
|
|
||||||
|
WheelSpeedData_t wheels;
|
||||||
|
brake_control_get_wheel_data(&wheels);
|
||||||
|
|
||||||
|
/* Check if ABS should be active */
|
||||||
|
if (brake_control_get_state() == BRAKE_STATE_NORMAL_BRAKING &&
|
||||||
|
check_abs_activation(&wheels)) {
|
||||||
|
|
||||||
|
abs_control.abs_active = true;
|
||||||
|
control_abs(&wheels);
|
||||||
|
} else {
|
||||||
|
abs_control.abs_active = false;
|
||||||
|
abs_control.control_phase = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check ABS Activation Conditions */
|
||||||
|
static bool check_abs_activation(const WheelSpeedData_t* wheels) {
|
||||||
|
/* Calculate reference speed (maximum wheel speed) */
|
||||||
|
float max_speed = 0;
|
||||||
|
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||||
|
if (wheels->wheel_speed[i] > max_speed) {
|
||||||
|
max_speed = wheels->wheel_speed[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_control.reference_speed = max_speed;
|
||||||
|
|
||||||
|
/* Calculate slip for each wheel */
|
||||||
|
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||||
|
if (max_speed > 5.0f) { /* Only calculate above 5 km/h */
|
||||||
|
abs_control.slip[i] = (max_speed - wheels->wheel_speed[i]) / max_speed;
|
||||||
|
} else {
|
||||||
|
abs_control.slip[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if slip exceeds threshold */
|
||||||
|
if (abs_control.slip[i] > ABS_SLIP_THRESHOLD) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for excessive deceleration */
|
||||||
|
if (wheels->wheel_acceleration[i] < ABS_DECELERATION_THRESHOLD) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ABS Control Algorithm */
|
||||||
|
static void control_abs(const WheelSpeedData_t* wheels) {
|
||||||
|
mutex_lock(&abs_control.mutex, 100);
|
||||||
|
|
||||||
|
/* Simple ABS control algorithm */
|
||||||
|
switch (abs_control.control_phase) {
|
||||||
|
case 0: /* Increase pressure */
|
||||||
|
if (abs_control.slip[0] > ABS_SLIP_THRESHOLD) {
|
||||||
|
abs_control.control_phase = 2; /* Switch to decrease */
|
||||||
|
abs_control.phase_timer = kernel_get_tick_count();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /* Hold pressure */
|
||||||
|
if ((kernel_get_tick_count() - abs_control.phase_timer) > 10) {
|
||||||
|
abs_control.control_phase = 0; /* Switch to increase */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /* Decrease pressure */
|
||||||
|
if (abs_control.slip[0] < ABS_SLIP_THRESHOLD * 0.5f) {
|
||||||
|
abs_control.control_phase = 1; /* Switch to hold */
|
||||||
|
abs_control.phase_timer = kernel_get_tick_count();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&abs_control.mutex);
|
||||||
|
|
||||||
|
/* Control brake pressure valves */
|
||||||
|
control_pressure_valves();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Control Pressure Valves */
|
||||||
|
static void control_pressure_valves(void) {
|
||||||
|
switch (abs_control.control_phase) {
|
||||||
|
case 0: /* Increase pressure */
|
||||||
|
gpio_write(0, 0, false); /* Inlet valve open */
|
||||||
|
gpio_write(0, 1, false); /* Outlet valve closed */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /* Hold pressure */
|
||||||
|
gpio_write(0, 0, true); /* Inlet valve closed */
|
||||||
|
gpio_write(0, 1, false); /* Outlet valve closed */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /* Decrease pressure */
|
||||||
|
gpio_write(0, 0, true); /* Inlet valve closed */
|
||||||
|
gpio_write(0, 1, true); /* Outlet valve open */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get ABS Status */
|
||||||
|
bool abs_control_is_active(void) {
|
||||||
|
return abs_control.abs_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Slip Values */
|
||||||
|
KernelStatus_t abs_control_get_slip(float* slip, uint8_t* count) {
|
||||||
|
if (slip == NULL || count == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&abs_control.mutex, 100);
|
||||||
|
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||||
|
slip[i] = abs_control.slip[i];
|
||||||
|
}
|
||||||
|
*count = WHEEL_SPEED_SENSOR_COUNT;
|
||||||
|
mutex_unlock(&abs_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/**
|
||||||
|
* @file brake_task.c
|
||||||
|
* @brief Main brake control task
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "brake_control.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "dtc_manager.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* Brake Control State */
|
||||||
|
typedef struct {
|
||||||
|
BrakeState_t state;
|
||||||
|
BrakeSystemData_t data;
|
||||||
|
WheelSpeedData_t wheels;
|
||||||
|
uint16_t target_pressure;
|
||||||
|
uint16_t actual_pressure;
|
||||||
|
Mutex_t data_mutex;
|
||||||
|
bool initialized;
|
||||||
|
} BrakeControlState_t;
|
||||||
|
|
||||||
|
static BrakeControlState_t brake_control;
|
||||||
|
|
||||||
|
/* Initialize Brake Control */
|
||||||
|
KernelStatus_t brake_control_init(void) {
|
||||||
|
if (brake_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&brake_control, 0, sizeof(BrakeControlState_t));
|
||||||
|
brake_control.state = BRAKE_STATE_IDLE;
|
||||||
|
|
||||||
|
mutex_create(&brake_control.data_mutex, false);
|
||||||
|
|
||||||
|
/* Initialize ADC for brake sensors */
|
||||||
|
adc_init(1, &(AdcConfig_t){
|
||||||
|
.resolution = ADC_RESOLUTION_12BIT,
|
||||||
|
.mode = ADC_MODE_CONTINUOUS,
|
||||||
|
.channel_count = 2,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0}, /* Brake pedal position */
|
||||||
|
{.channel = 1} /* Brake pressure */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Initialize PWM for brake pressure control */
|
||||||
|
pwm_init(1, &(PwmConfig_t){
|
||||||
|
.frequency_hz = 2000,
|
||||||
|
.channel_count = 1,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .duty_cycle = 0} /* Brake pressure valve */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
brake_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Brake Control Task */
|
||||||
|
void brake_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Wait for next brake control period */
|
||||||
|
kernel_delay(BRAKE_CONTROL_PERIOD_MS);
|
||||||
|
|
||||||
|
/* Read brake sensors */
|
||||||
|
read_brake_sensors();
|
||||||
|
|
||||||
|
/* Process brake logic */
|
||||||
|
process_brake_logic();
|
||||||
|
|
||||||
|
/* Check for faults */
|
||||||
|
check_brake_faults();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read Brake Sensors */
|
||||||
|
static void read_brake_sensors(void) {
|
||||||
|
uint16_t brake_pedal = 0;
|
||||||
|
uint16_t brake_pressure = 0;
|
||||||
|
|
||||||
|
adc_read_channel(1, 0, &brake_pedal, 10);
|
||||||
|
adc_read_channel(1, 1, &brake_pressure, 10);
|
||||||
|
|
||||||
|
mutex_lock(&brake_control.data_mutex, 100);
|
||||||
|
brake_control.data.brake_pedal_position = brake_pedal;
|
||||||
|
brake_control.data.brake_pressure = brake_pressure;
|
||||||
|
mutex_unlock(&brake_control.data_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process Brake Logic */
|
||||||
|
static void process_brake_logic(void) {
|
||||||
|
mutex_lock(&brake_control.data_mutex, 100);
|
||||||
|
|
||||||
|
/* Check brake pedal position */
|
||||||
|
if (brake_control.data.brake_pedal_position > 100) {
|
||||||
|
brake_control.state = BRAKE_STATE_NORMAL_BRAKING;
|
||||||
|
brake_control.data.brake_light = true;
|
||||||
|
|
||||||
|
/* Calculate target pressure based on pedal position */
|
||||||
|
brake_control.target_pressure =
|
||||||
|
(brake_control.data.brake_pedal_position - 100) * 200;
|
||||||
|
|
||||||
|
/* Limit pressure */
|
||||||
|
if (brake_control.target_pressure > MAX_BRAKE_PRESSURE) {
|
||||||
|
brake_control.target_pressure = MAX_BRAKE_PRESSURE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
brake_control.state = BRAKE_STATE_IDLE;
|
||||||
|
brake_control.data.brake_light = false;
|
||||||
|
brake_control.target_pressure = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for emergency braking */
|
||||||
|
if (brake_control.data.brake_pedal_position > 900 &&
|
||||||
|
brake_control.data.vehicle_deceleration < -8.0f) {
|
||||||
|
brake_control.state = BRAKE_STATE_EMERGENCY;
|
||||||
|
|
||||||
|
/* Maximum braking force */
|
||||||
|
brake_control.target_pressure = MAX_BRAKE_PRESSURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&brake_control.data_mutex);
|
||||||
|
|
||||||
|
/* Update brake pressure control */
|
||||||
|
update_brake_pressure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update Brake Pressure */
|
||||||
|
static void update_brake_pressure(void) {
|
||||||
|
/* Simple PID pressure control */
|
||||||
|
int32_t pressure_error = brake_control.target_pressure -
|
||||||
|
brake_control.actual_pressure;
|
||||||
|
|
||||||
|
/* Calculate valve duty cycle */
|
||||||
|
uint16_t valve_duty = 0;
|
||||||
|
|
||||||
|
if (pressure_error > 100) {
|
||||||
|
valve_duty = 10000; /* Full open */
|
||||||
|
} else if (pressure_error > 0) {
|
||||||
|
valve_duty = (uint16_t)((pressure_error * 10000) / 100);
|
||||||
|
} else {
|
||||||
|
valve_duty = 0; /* Closed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update PWM */
|
||||||
|
pwm_set_duty_cycle(1, 0, valve_duty);
|
||||||
|
|
||||||
|
/* Update actual pressure */
|
||||||
|
brake_control.actual_pressure += pressure_error / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check Brake Faults */
|
||||||
|
static void check_brake_faults(void) {
|
||||||
|
/* Check wheel speed sensors */
|
||||||
|
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||||
|
if (brake_control.wheels.sensor_fault[i]) {
|
||||||
|
brake_control.state = BRAKE_STATE_FAULT;
|
||||||
|
brake_control.data.abs_fault_code = i + 1;
|
||||||
|
|
||||||
|
/* Add DTC */
|
||||||
|
DtcCode_t dtc_code = {
|
||||||
|
.high_byte = 0x01, /* Chassis */
|
||||||
|
.middle_byte = 0x00,
|
||||||
|
.low_byte = i + 1
|
||||||
|
};
|
||||||
|
dtc_manager_add_dtc(&dtc_code, 4); /* High severity */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
/**
|
||||||
|
* @file display_task.c
|
||||||
|
* @brief Dashboard display task
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "engine_control.h"
|
||||||
|
#include "brake_control.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Display State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
uint16_t display_buffer[1024]; /* Display frame buffer */
|
||||||
|
uint8_t current_screen;
|
||||||
|
bool backlight_on;
|
||||||
|
uint8_t backlight_intensity;
|
||||||
|
Mutex_t mutex;
|
||||||
|
} DisplayState_t;
|
||||||
|
|
||||||
|
static DisplayState_t display;
|
||||||
|
|
||||||
|
/* Dashboard Data */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t speed;
|
||||||
|
uint16_t rpm;
|
||||||
|
int16_t coolant_temp;
|
||||||
|
uint16_t fuel_level;
|
||||||
|
uint16_t odometer;
|
||||||
|
uint16_t trip_meter;
|
||||||
|
bool turn_left;
|
||||||
|
bool turn_right;
|
||||||
|
bool high_beam;
|
||||||
|
bool check_engine;
|
||||||
|
bool abs_warning;
|
||||||
|
bool oil_pressure_warning;
|
||||||
|
bool battery_warning;
|
||||||
|
} DashboardData_t;
|
||||||
|
|
||||||
|
static DashboardData_t dashboard_data;
|
||||||
|
|
||||||
|
/* Initialize Display */
|
||||||
|
KernelStatus_t display_init(void) {
|
||||||
|
if (display.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&display, 0, sizeof(DisplayState_t));
|
||||||
|
display.current_screen = 0;
|
||||||
|
display.backlight_on = true;
|
||||||
|
display.backlight_intensity = 100;
|
||||||
|
|
||||||
|
mutex_create(&display.mutex, false);
|
||||||
|
|
||||||
|
/* Initialize SPI for display */
|
||||||
|
spi_init(0, &(SpiConfig_t){
|
||||||
|
.mode = SPI_MODE_0,
|
||||||
|
.clock_speed = SPI_CLOCK_8MHZ,
|
||||||
|
.data_order = SPI_DATA_ORDER_MSB_FIRST,
|
||||||
|
.data_size = 8,
|
||||||
|
.use_dma = true,
|
||||||
|
.enable_hardware_cs = false,
|
||||||
|
.cs_polarity = SPI_CS_ACTIVE_LOW,
|
||||||
|
.cs_port = 0,
|
||||||
|
.cs_pin = 15
|
||||||
|
});
|
||||||
|
|
||||||
|
display.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dashboard Display Task */
|
||||||
|
void display_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Update dashboard data */
|
||||||
|
update_dashboard_data();
|
||||||
|
|
||||||
|
/* Render display */
|
||||||
|
render_display();
|
||||||
|
|
||||||
|
/* Update display */
|
||||||
|
update_display_hardware();
|
||||||
|
|
||||||
|
/* 50ms refresh rate */
|
||||||
|
kernel_delay(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update Dashboard Data */
|
||||||
|
static void update_dashboard_data(void) {
|
||||||
|
/* Get data from CAN bus */
|
||||||
|
CanMessage_t message;
|
||||||
|
|
||||||
|
while (can_receive_message(&message, 0) == KERNEL_OK) {
|
||||||
|
switch (message.id.id) {
|
||||||
|
case 0x300: /* Engine data */
|
||||||
|
dashboard_data.rpm = (message.data[0] << 8) | message.data[1];
|
||||||
|
dashboard_data.coolant_temp = (int16_t)((message.data[2] << 8) |
|
||||||
|
message.data[3]);
|
||||||
|
dashboard_data.check_engine = message.data[4] & 0x01;
|
||||||
|
dashboard_data.oil_pressure_warning = message.data[4] & 0x02;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x301: /* Vehicle speed */
|
||||||
|
dashboard_data.speed = (message.data[0] << 8) | message.data[1];
|
||||||
|
dashboard_data.odometer = (message.data[2] << 16) |
|
||||||
|
(message.data[3] << 8) |
|
||||||
|
message.data[4];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x302: /* Fuel level */
|
||||||
|
dashboard_data.fuel_level = message.data[0];
|
||||||
|
dashboard_data.battery_warning = message.data[1] & 0x01;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x303: /* Turn signals */
|
||||||
|
dashboard_data.turn_left = message.data[0] & 0x01;
|
||||||
|
dashboard_data.turn_right = message.data[0] & 0x02;
|
||||||
|
dashboard_data.high_beam = message.data[0] & 0x04;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x304: /* ABS status */
|
||||||
|
dashboard_data.abs_warning = message.data[0] & 0x01;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Render Display */
|
||||||
|
static void render_display(void) {
|
||||||
|
mutex_lock(&display.mutex, 100);
|
||||||
|
|
||||||
|
/* Clear display buffer */
|
||||||
|
memset(display.display_buffer, 0, sizeof(display.display_buffer));
|
||||||
|
|
||||||
|
/* Draw speedometer */
|
||||||
|
draw_speedometer();
|
||||||
|
|
||||||
|
/* Draw tachometer */
|
||||||
|
draw_tachometer();
|
||||||
|
|
||||||
|
/* Draw fuel gauge */
|
||||||
|
draw_fuel_gauge();
|
||||||
|
|
||||||
|
/* Draw temperature gauge */
|
||||||
|
draw_temperature_gauge();
|
||||||
|
|
||||||
|
/* Draw warning indicators */
|
||||||
|
draw_warning_indicators();
|
||||||
|
|
||||||
|
/* Draw odometer */
|
||||||
|
draw_odometer();
|
||||||
|
|
||||||
|
mutex_unlock(&display.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw Speedometer */
|
||||||
|
static void draw_speedometer(void) {
|
||||||
|
/* Draw circular gauge */
|
||||||
|
int center_x = 100;
|
||||||
|
int center_y = 100;
|
||||||
|
int radius = 80;
|
||||||
|
|
||||||
|
/* Draw arc */
|
||||||
|
for (int angle = 0; angle < 270; angle++) {
|
||||||
|
int x = center_x + (int)(radius * cos(angle * M_PI / 180));
|
||||||
|
int y = center_y + (int)(radius * sin(angle * M_PI / 180));
|
||||||
|
|
||||||
|
if (x >= 0 && x < 240 && y >= 0 && y < 320) {
|
||||||
|
display.display_buffer[y * 240 + x] = 0xFFFF; /* White */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw speed needle */
|
||||||
|
float speed_angle = (dashboard_data.speed * 270.0f) / 240.0f; /* 240 km/h max */
|
||||||
|
int needle_x = center_x + (int)((radius - 10) * cos(speed_angle * M_PI / 180));
|
||||||
|
int needle_y = center_y + (int)((radius - 10) * sin(speed_angle * M_PI / 180));
|
||||||
|
|
||||||
|
/* Draw line from center to needle tip */
|
||||||
|
draw_line(center_x, center_y, needle_x, needle_y, 0xF800); /* Red */
|
||||||
|
|
||||||
|
/* Draw speed text */
|
||||||
|
char speed_text[10];
|
||||||
|
snprintf(speed_text, sizeof(speed_text), "%d km/h", dashboard_data.speed);
|
||||||
|
draw_text(60, 200, speed_text, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw Tachometer */
|
||||||
|
static void draw_tachometer(void) {
|
||||||
|
int center_x = 300;
|
||||||
|
int center_y = 100;
|
||||||
|
int radius = 60;
|
||||||
|
|
||||||
|
/* Draw arc */
|
||||||
|
for (int angle = 0; angle < 270; angle++) {
|
||||||
|
int x = center_x + (int)(radius * cos(angle * M_PI / 180));
|
||||||
|
int y = center_y + (int)(radius * sin(angle * M_PI / 180));
|
||||||
|
|
||||||
|
if (x >= 0 && x < 480 && y >= 0 && y < 320) {
|
||||||
|
display.display_buffer[y * 480 + x] = 0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw RPM needle */
|
||||||
|
float rpm_angle = (dashboard_data.rpm * 270.0f) / 8000.0f; /* 8000 RPM max */
|
||||||
|
int needle_x = center_x + (int)((radius - 10) * cos(rpm_angle * M_PI / 180));
|
||||||
|
int needle_y = center_y + (int)((radius - 10) * sin(rpm_angle * M_PI / 180));
|
||||||
|
|
||||||
|
draw_line(center_x, center_y, needle_x, needle_y, 0x07E0); /* Green */
|
||||||
|
|
||||||
|
/* Draw RPM text */
|
||||||
|
char rpm_text[10];
|
||||||
|
snprintf(rpm_text, sizeof(rpm_text), "%d RPM", dashboard_data.rpm);
|
||||||
|
draw_text(260, 200, rpm_text, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw Warning Indicators */
|
||||||
|
static void draw_warning_indicators(void) {
|
||||||
|
/* Check engine light */
|
||||||
|
if (dashboard_data.check_engine) {
|
||||||
|
draw_text(20, 280, "CHECK ENGINE", 0xF800); /* Red */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ABS warning */
|
||||||
|
if (dashboard_data.abs_warning) {
|
||||||
|
draw_text(20, 300, "ABS", 0xF800);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Oil pressure warning */
|
||||||
|
if (dashboard_data.oil_pressure_warning) {
|
||||||
|
draw_text(100, 300, "OIL", 0xF800);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Battery warning */
|
||||||
|
if (dashboard_data.battery_warning) {
|
||||||
|
draw_text(150, 300, "BAT", 0xF800);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn signals */
|
||||||
|
if (dashboard_data.turn_left) {
|
||||||
|
draw_text(400, 280, "<--", 0x07E0); /* Green */
|
||||||
|
}
|
||||||
|
if (dashboard_data.turn_right) {
|
||||||
|
draw_text(440, 280, "-->", 0x07E0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* High beam */
|
||||||
|
if (dashboard_data.high_beam) {
|
||||||
|
draw_text(400, 300, "HIGH", 0x001F); /* Blue */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw Line */
|
||||||
|
static void draw_line(int x1, int y1, int x2, int y2, uint16_t color) {
|
||||||
|
int dx = abs(x2 - x1);
|
||||||
|
int dy = abs(y2 - y1);
|
||||||
|
int sx = (x1 < x2) ? 1 : -1;
|
||||||
|
int sy = (y1 < y2) ? 1 : -1;
|
||||||
|
int err = dx - dy;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (x1 >= 0 && x1 < 480 && y1 >= 0 && y1 < 320) {
|
||||||
|
display.display_buffer[y1 * 480 + x1] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1 == x2 && y1 == y2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2 = 2 * err;
|
||||||
|
if (e2 > -dy) {
|
||||||
|
err -= dy;
|
||||||
|
x1 += sx;
|
||||||
|
}
|
||||||
|
if (e2 < dx) {
|
||||||
|
err += dx;
|
||||||
|
y1 += sy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw Text */
|
||||||
|
static void draw_text(int x, int y, const char* text, uint16_t color) {
|
||||||
|
/* Simple 8x8 font rendering */
|
||||||
|
while (*text) {
|
||||||
|
char c = *text++;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
if (font_bitmap[(uint8_t)c][i] & (1 << j)) {
|
||||||
|
int px = x + j;
|
||||||
|
int py = y + i;
|
||||||
|
|
||||||
|
if (px >= 0 && px < 480 && py >= 0 && py < 320) {
|
||||||
|
display.display_buffer[py * 480 + px] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update Display Hardware */
|
||||||
|
static void update_display_hardware(void) {
|
||||||
|
mutex_lock(&display.mutex, 100);
|
||||||
|
|
||||||
|
/* Send frame buffer to display via SPI */
|
||||||
|
spi_write(0, (uint8_t*)display.display_buffer, sizeof(display.display_buffer), 100);
|
||||||
|
|
||||||
|
mutex_unlock(&display.mutex);
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/**
|
||||||
|
* @file gauge_control.c
|
||||||
|
* @brief Analog gauge control for dashboard
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* Gauge Control State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
uint16_t speed_gauge_position;
|
||||||
|
uint16_t rpm_gauge_position;
|
||||||
|
uint16_t fuel_gauge_position;
|
||||||
|
uint16_t temp_gauge_position;
|
||||||
|
uint16_t target_positions[4];
|
||||||
|
Mutex_t mutex;
|
||||||
|
} GaugeControlState_t;
|
||||||
|
|
||||||
|
static GaugeControlState_t gauge_control;
|
||||||
|
|
||||||
|
/* Initialize Gauge Control */
|
||||||
|
KernelStatus_t gauge_control_init(void) {
|
||||||
|
if (gauge_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&gauge_control, 0, sizeof(GaugeControlState_t));
|
||||||
|
|
||||||
|
/* Initialize PWM for gauges */
|
||||||
|
pwm_init(3, &(PwmConfig_t){
|
||||||
|
.frequency_hz = 100, /* 100 Hz for smooth gauge movement */
|
||||||
|
.channel_count = 4,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .duty_cycle = 0}, /* Speedometer */
|
||||||
|
{.channel = 1, .duty_cycle = 0}, /* Tachometer */
|
||||||
|
{.channel = 2, .duty_cycle = 0}, /* Fuel gauge */
|
||||||
|
{.channel = 3, .duty_cycle = 0} /* Temperature gauge */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mutex_create(&gauge_control.mutex, false);
|
||||||
|
|
||||||
|
gauge_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gauge Control Task */
|
||||||
|
void gauge_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Read CAN messages for gauge data */
|
||||||
|
CanMessage_t message;
|
||||||
|
|
||||||
|
while (can_receive_message(&message, 0) == KERNEL_OK) {
|
||||||
|
process_gauge_message(&message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth gauge movement */
|
||||||
|
smooth_gauge_movement();
|
||||||
|
|
||||||
|
/* 10ms update rate */
|
||||||
|
kernel_delay(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process Gauge Message */
|
||||||
|
static void process_gauge_message(const CanMessage_t* message) {
|
||||||
|
mutex_lock(&gauge_control.mutex, 100);
|
||||||
|
|
||||||
|
switch (message->id.id) {
|
||||||
|
case 0x300: /* Engine data */
|
||||||
|
/* RPM: 0-8000 RPM maps to 0-10000 duty cycle */
|
||||||
|
gauge_control.target_positions[1] =
|
||||||
|
((message->data[0] << 8) | message->data[1]) * 10000 / 8000;
|
||||||
|
|
||||||
|
/* Temperature: -40 to 120°C maps to 0-10000 */
|
||||||
|
int16_t temp = (message->data[2] << 8) | message->data[3];
|
||||||
|
gauge_control.target_positions[3] =
|
||||||
|
(temp + 40) * 10000 / 160;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x301: /* Vehicle speed */
|
||||||
|
/* Speed: 0-240 km/h maps to 0-10000 */
|
||||||
|
gauge_control.target_positions[0] =
|
||||||
|
((message->data[0] << 8) | message->data[1]) * 10000 / 240;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x302: /* Fuel level */
|
||||||
|
/* Fuel: 0-100% maps to 0-10000 */
|
||||||
|
gauge_control.target_positions[2] = message->data[0] * 100;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&gauge_control.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth Gauge Movement */
|
||||||
|
static void smooth_gauge_movement(void) {
|
||||||
|
mutex_lock(&gauge_control.mutex, 100);
|
||||||
|
|
||||||
|
/* Smooth movement for each gauge */
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
uint16_t current = 0;
|
||||||
|
uint16_t target = gauge_control.target_positions[i];
|
||||||
|
|
||||||
|
/* Get current position */
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
current = gauge_control.speed_gauge_position;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
current = gauge_control.rpm_gauge_position;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
current = gauge_control.fuel_gauge_position;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
current = gauge_control.temp_gauge_position;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate new position with smoothing */
|
||||||
|
int32_t delta = target - current;
|
||||||
|
uint16_t new_position = current + (delta / 10); /* 10% movement per update */
|
||||||
|
|
||||||
|
/* Update position */
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
gauge_control.speed_gauge_position = new_position;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gauge_control.rpm_gauge_position = new_position;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gauge_control.fuel_gauge_position = new_position;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
gauge_control.temp_gauge_position = new_position;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update PWM output */
|
||||||
|
pwm_set_duty_cycle(3, i, new_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&gauge_control.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calibrate Gauges */
|
||||||
|
KernelStatus_t gauge_calibrate(uint8_t gauge, uint16_t min_position,
|
||||||
|
uint16_t max_position) {
|
||||||
|
if (!gauge_control.initialized || gauge >= 4) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&gauge_control.mutex, 100);
|
||||||
|
|
||||||
|
/* Set gauge to minimum position */
|
||||||
|
pwm_set_duty_cycle(3, gauge, min_position);
|
||||||
|
kernel_delay(1000); /* Wait 1 second */
|
||||||
|
|
||||||
|
/* Set gauge to maximum position */
|
||||||
|
pwm_set_duty_cycle(3, gauge, max_position);
|
||||||
|
kernel_delay(1000); /* Wait 1 second */
|
||||||
|
|
||||||
|
/* Return to zero */
|
||||||
|
pwm_set_duty_cycle(3, gauge, min_position);
|
||||||
|
|
||||||
|
mutex_unlock(&gauge_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Self-Test Gauges */
|
||||||
|
KernelStatus_t gauge_self_test(void) {
|
||||||
|
if (!gauge_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform gauge sweep */
|
||||||
|
for (uint16_t position = 0; position <= 10000; position += 100) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
pwm_set_duty_cycle(3, i, position);
|
||||||
|
}
|
||||||
|
kernel_delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return to zero */
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
pwm_set_duty_cycle(3, i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @file engine_control.h
|
||||||
|
* @brief Engine control module interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ENGINE_CONTROL_H
|
||||||
|
#define ENGINE_CONTROL_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "engine_parameters.h"
|
||||||
|
|
||||||
|
/* Engine Control Functions */
|
||||||
|
KernelStatus_t engine_control_init(void);
|
||||||
|
KernelStatus_t engine_control_start(void);
|
||||||
|
KernelStatus_t engine_control_stop(void);
|
||||||
|
KernelStatus_t engine_control_get_sensor_data(EngineSensorData_t* data);
|
||||||
|
KernelStatus_t engine_control_get_actuator_data(EngineActuatorData_t* data);
|
||||||
|
KernelStatus_t engine_control_set_actuator_data(const EngineActuatorData_t* data);
|
||||||
|
EngineState_t engine_control_get_state(void);
|
||||||
|
KernelStatus_t engine_control_get_faults(EngineFaultCode_t* faults,
|
||||||
|
uint8_t* count);
|
||||||
|
KernelStatus_t engine_control_clear_faults(void);
|
||||||
|
void engine_control_task(void* parameters);
|
||||||
|
void fuel_injection_task(void* parameters);
|
||||||
|
void ignition_control_task(void* parameters);
|
||||||
|
void sensor_reading_task(void* parameters);
|
||||||
|
|
||||||
|
#endif /* ENGINE_CONTROL_H */
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* @file engine_parameters.h
|
||||||
|
* @brief Engine control parameters and calibration data
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ENGINE_PARAMETERS_H
|
||||||
|
#define ENGINE_PARAMETERS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Engine Operating Parameters */
|
||||||
|
#define ENGINE_MAX_RPM 6500
|
||||||
|
#define ENGINE_IDLE_RPM 800
|
||||||
|
#define ENGINE_REDLINE_RPM 6000
|
||||||
|
#define ENGINE_MAX_TORQUE_RPM 4000
|
||||||
|
#define ENGINE_MAX_POWER_RPM 5500
|
||||||
|
|
||||||
|
/* Temperature Limits */
|
||||||
|
#define ENGINE_MAX_COOLANT_TEMP 120 /* °C */
|
||||||
|
#define ENGINE_MIN_COOLANT_TEMP -40 /* °C */
|
||||||
|
#define ENGINE_OPTIMAL_TEMP 90 /* °C */
|
||||||
|
#define ENGINE_MAX_OIL_TEMP 150 /* °C */
|
||||||
|
#define ENGINE_MAX_INTAKE_TEMP 80 /* °C */
|
||||||
|
|
||||||
|
/* Pressure Limits */
|
||||||
|
#define ENGINE_MAX_MANIFOLD_PRESSURE 250 /* kPa */
|
||||||
|
#define ENGINE_MIN_OIL_PRESSURE 100 /* kPa */
|
||||||
|
#define ENGINE_MAX_FUEL_PRESSURE 500 /* kPa */
|
||||||
|
|
||||||
|
/* Fuel System Parameters */
|
||||||
|
#define FUEL_STOICHIOMETRIC_RATIO 14.7f
|
||||||
|
#define FUEL_MAX_INJECTION_TIME 20.0f /* ms */
|
||||||
|
#define FUEL_MIN_INJECTION_TIME 0.5f /* ms */
|
||||||
|
#define FUEL_INJECTOR_FLOW_RATE 250.0f /* cc/min */
|
||||||
|
|
||||||
|
/* Ignition Parameters */
|
||||||
|
#define IGNITION_MAX_ADVANCE 45.0f /* degrees BTDC */
|
||||||
|
#define IGNITION_MIN_ADVANCE -10.0f /* degrees ATDC */
|
||||||
|
#define IGNITION_BASE_ADVANCE 10.0f /* degrees BTDC */
|
||||||
|
#define IGNITION_DWELL_TIME 3.5f /* ms */
|
||||||
|
|
||||||
|
/* Control Loop Parameters */
|
||||||
|
#define ENGINE_CONTROL_PERIOD_MS 1 /* 1ms control loop */
|
||||||
|
#define FUEL_CONTROL_PERIOD_MS 10 /* 10ms fuel update */
|
||||||
|
#define IGNITION_CONTROL_PERIOD_MS 5 /* 5ms ignition update */
|
||||||
|
#define SENSOR_READ_PERIOD_MS 2 /* 2ms sensor reading */
|
||||||
|
|
||||||
|
/* PID Controller Gains */
|
||||||
|
typedef struct {
|
||||||
|
float kp;
|
||||||
|
float ki;
|
||||||
|
float kd;
|
||||||
|
float integral_limit;
|
||||||
|
float output_limit;
|
||||||
|
} PidGains_t;
|
||||||
|
|
||||||
|
/* Fuel Control PID */
|
||||||
|
static const PidGains_t fuel_pid_gains = {
|
||||||
|
.kp = 0.5f,
|
||||||
|
.ki = 0.1f,
|
||||||
|
.kd = 0.05f,
|
||||||
|
.integral_limit = 100.0f,
|
||||||
|
.output_limit = 100.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Idle Control PID */
|
||||||
|
static const PidGains_t idle_pid_gains = {
|
||||||
|
.kp = 0.8f,
|
||||||
|
.ki = 0.2f,
|
||||||
|
.kd = 0.1f,
|
||||||
|
.integral_limit = 50.0f,
|
||||||
|
.output_limit = 100.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Boost Control PID */
|
||||||
|
static const PidGains_t boost_pid_gains = {
|
||||||
|
.kp = 0.3f,
|
||||||
|
.ki = 0.05f,
|
||||||
|
.kd = 0.02f,
|
||||||
|
.integral_limit = 200.0f,
|
||||||
|
.output_limit = 250.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Engine State Enumeration */
|
||||||
|
typedef enum {
|
||||||
|
ENGINE_STATE_OFF = 0,
|
||||||
|
ENGINE_STATE_CRANKING = 1,
|
||||||
|
ENGINE_STATE_RUNNING = 2,
|
||||||
|
ENGINE_STATE_IDLE = 3,
|
||||||
|
ENGINE_STATE_ACCELERATING = 4,
|
||||||
|
ENGINE_STATE_DECELERATING = 5,
|
||||||
|
ENGINE_STATE_FAULT = 6,
|
||||||
|
ENGINE_STATE_LIMP_HOME = 7
|
||||||
|
} EngineState_t;
|
||||||
|
|
||||||
|
/* Engine Sensor Data */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t rpm;
|
||||||
|
uint16_t vehicle_speed;
|
||||||
|
int16_t coolant_temp;
|
||||||
|
int16_t intake_air_temp;
|
||||||
|
int16_t oil_temp;
|
||||||
|
uint16_t manifold_pressure;
|
||||||
|
uint16_t oil_pressure;
|
||||||
|
uint16_t fuel_pressure;
|
||||||
|
uint16_t throttle_position;
|
||||||
|
uint16_t accelerator_pedal;
|
||||||
|
float mass_air_flow;
|
||||||
|
float lambda;
|
||||||
|
float battery_voltage;
|
||||||
|
} EngineSensorData_t;
|
||||||
|
|
||||||
|
/* Engine Actuator Data */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t injector_pulse_width;
|
||||||
|
float ignition_advance;
|
||||||
|
uint16_t idle_air_control;
|
||||||
|
uint16_t boost_control;
|
||||||
|
uint16_t fuel_pump_duty;
|
||||||
|
uint16_t cooling_fan_duty;
|
||||||
|
} EngineActuatorData_t;
|
||||||
|
|
||||||
|
/* Engine Fault Codes */
|
||||||
|
typedef enum {
|
||||||
|
ENGINE_FAULT_NONE = 0,
|
||||||
|
ENGINE_FAULT_COOLANT_TEMP_SENSOR = 1,
|
||||||
|
ENGINE_FAULT_INTAKE_TEMP_SENSOR = 2,
|
||||||
|
ENGINE_FAULT_MANIFOLD_PRESSURE_SENSOR = 3,
|
||||||
|
ENGINE_FAULT_MAF_SENSOR = 4,
|
||||||
|
ENGINE_FAULT_OXYGEN_SENSOR = 5,
|
||||||
|
ENGINE_FAULT_KNOCK_SENSOR = 6,
|
||||||
|
ENGINE_FAULT_CRANKSHAFT_SENSOR = 7,
|
||||||
|
ENGINE_FAULT_CAMSHAFT_SENSOR = 8,
|
||||||
|
ENGINE_FAULT_INJECTOR_1 = 9,
|
||||||
|
ENGINE_FAULT_INJECTOR_2 = 10,
|
||||||
|
ENGINE_FAULT_INJECTOR_3 = 11,
|
||||||
|
ENGINE_FAULT_INJECTOR_4 = 12,
|
||||||
|
ENGINE_FAULT_IGNITION_COIL = 13,
|
||||||
|
ENGINE_FAULT_FUEL_PUMP = 14,
|
||||||
|
ENGINE_FAULT_OVERHEAT = 15,
|
||||||
|
ENGINE_FAULT_LOW_OIL_PRESSURE = 16
|
||||||
|
} EngineFaultCode_t;
|
||||||
|
|
||||||
|
/* Lookup Tables */
|
||||||
|
typedef struct {
|
||||||
|
const uint16_t* rpm_points;
|
||||||
|
const uint16_t* load_points;
|
||||||
|
const uint16_t* values;
|
||||||
|
uint8_t rpm_count;
|
||||||
|
uint8_t load_count;
|
||||||
|
} LookupTable2D_t;
|
||||||
|
|
||||||
|
/* Fuel Map (injection time in microseconds) */
|
||||||
|
static const uint16_t fuel_map_rpm[] = {0, 500, 1000, 2000, 3000, 4000, 5000, 6000};
|
||||||
|
static const uint16_t fuel_map_load[] = {0, 20, 40, 60, 80, 100};
|
||||||
|
static const uint16_t fuel_map_values[][8] = {
|
||||||
|
{0, 1000, 800, 700, 650, 600, 550, 500},
|
||||||
|
{1000, 1500, 1300, 1200, 1100, 1000, 950, 900},
|
||||||
|
{2000, 2500, 2200, 2000, 1800, 1700, 1600, 1500},
|
||||||
|
{3000, 3500, 3200, 3000, 2800, 2600, 2400, 2200},
|
||||||
|
{4000, 4500, 4200, 4000, 3800, 3600, 3400, 3200},
|
||||||
|
{5000, 5500, 5200, 5000, 4800, 4600, 4400, 4200}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Ignition Advance Map (degrees BTDC) */
|
||||||
|
static const uint16_t ignition_map_rpm[] = {0, 500, 1000, 2000, 3000, 4000, 5000, 6000};
|
||||||
|
static const uint16_t ignition_map_load[] = {0, 20, 40, 60, 80, 100};
|
||||||
|
static const int16_t ignition_map_values[][8] = {
|
||||||
|
{10, 12, 15, 18, 20, 22, 25, 28},
|
||||||
|
{10, 12, 15, 18, 20, 22, 25, 28},
|
||||||
|
{8, 10, 13, 16, 18, 20, 23, 26},
|
||||||
|
{6, 8, 11, 14, 16, 18, 21, 24},
|
||||||
|
{4, 6, 9, 12, 14, 16, 19, 22},
|
||||||
|
{2, 4, 7, 10, 12, 14, 17, 20}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ENGINE_PARAMETERS_H */
|
||||||
@@ -0,0 +1,366 @@
|
|||||||
|
/**
|
||||||
|
* @file engine_control_task.c
|
||||||
|
* @brief Main engine control task
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "engine_control.h"
|
||||||
|
#include "fuel_injection.h"
|
||||||
|
#include "ignition_control.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "dtc_manager.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* Engine Control State */
|
||||||
|
typedef struct {
|
||||||
|
EngineState_t state;
|
||||||
|
EngineSensorData_t sensors;
|
||||||
|
EngineActuatorData_t actuators;
|
||||||
|
EngineFaultCode_t faults[20];
|
||||||
|
uint8_t fault_count;
|
||||||
|
uint32_t engine_run_time;
|
||||||
|
Mutex_t data_mutex;
|
||||||
|
bool initialized;
|
||||||
|
} EngineControlState_t;
|
||||||
|
|
||||||
|
static EngineControlState_t engine_control;
|
||||||
|
|
||||||
|
/* PID Controller Structure */
|
||||||
|
typedef struct {
|
||||||
|
PidGains_t gains;
|
||||||
|
float integral;
|
||||||
|
float previous_error;
|
||||||
|
float output;
|
||||||
|
} PidController_t;
|
||||||
|
|
||||||
|
static PidController_t fuel_pid;
|
||||||
|
static PidController_t idle_pid;
|
||||||
|
static PidController_t boost_pid;
|
||||||
|
|
||||||
|
/* Initialize Engine Control */
|
||||||
|
KernelStatus_t engine_control_init(void) {
|
||||||
|
if (engine_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
memset(&engine_control, 0, sizeof(EngineControlState_t));
|
||||||
|
engine_control.state = ENGINE_STATE_OFF;
|
||||||
|
engine_control.fault_count = 0;
|
||||||
|
|
||||||
|
/* Initialize PID controllers */
|
||||||
|
fuel_pid.gains = fuel_pid_gains;
|
||||||
|
fuel_pid.integral = 0;
|
||||||
|
fuel_pid.previous_error = 0;
|
||||||
|
fuel_pid.output = 0;
|
||||||
|
|
||||||
|
idle_pid.gains = idle_pid_gains;
|
||||||
|
idle_pid.integral = 0;
|
||||||
|
idle_pid.previous_error = 0;
|
||||||
|
idle_pid.output = 0;
|
||||||
|
|
||||||
|
boost_pid.gains = boost_pid_gains;
|
||||||
|
boost_pid.integral = 0;
|
||||||
|
boost_pid.previous_error = 0;
|
||||||
|
boost_pid.output = 0;
|
||||||
|
|
||||||
|
/* Create mutex */
|
||||||
|
mutex_create(&engine_control.data_mutex, false);
|
||||||
|
|
||||||
|
/* Initialize sensors */
|
||||||
|
adc_init(0, &(AdcConfig_t){
|
||||||
|
.resolution = ADC_RESOLUTION_12BIT,
|
||||||
|
.mode = ADC_MODE_SCAN,
|
||||||
|
.trigger_source = ADC_TRIGGER_TIMER,
|
||||||
|
.reference = ADC_REFERENCE_VDD,
|
||||||
|
.channel_count = 8,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Coolant temp */
|
||||||
|
{.channel = 1, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Intake temp */
|
||||||
|
{.channel = 2, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Manifold pressure */
|
||||||
|
{.channel = 3, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Throttle position */
|
||||||
|
{.channel = 4, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Accelerator pedal */
|
||||||
|
{.channel = 5, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Oil pressure */
|
||||||
|
{.channel = 6, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Fuel pressure */
|
||||||
|
{.channel = 7, .sampling_time = ADC_SAMPLING_28_5_CYCLES} /* Battery voltage */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Initialize actuators */
|
||||||
|
pwm_init(0, &(PwmConfig_t){
|
||||||
|
.frequency_hz = 1000,
|
||||||
|
.alignment = PWM_ALIGNMENT_EDGE,
|
||||||
|
.channel_count = 4,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .duty_cycle = 0}, /* Injector 1 */
|
||||||
|
{.channel = 1, .duty_cycle = 0}, /* Injector 2 */
|
||||||
|
{.channel = 2, .duty_cycle = 0}, /* Idle air control */
|
||||||
|
{.channel = 3, .duty_cycle = 0} /* Boost control */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
engine_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start Engine */
|
||||||
|
KernelStatus_t engine_control_start(void) {
|
||||||
|
if (!engine_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_control.state = ENGINE_STATE_CRANKING;
|
||||||
|
|
||||||
|
/* Start PWM outputs */
|
||||||
|
pwm_start(0);
|
||||||
|
|
||||||
|
/* Start ADC conversions */
|
||||||
|
adc_start_conversion(0);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop Engine */
|
||||||
|
KernelStatus_t engine_control_stop(void) {
|
||||||
|
if (!engine_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop actuators */
|
||||||
|
pwm_stop(0);
|
||||||
|
|
||||||
|
/* Stop ADC */
|
||||||
|
adc_stop_conversion(0);
|
||||||
|
|
||||||
|
engine_control.state = ENGINE_STATE_OFF;
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Engine Control Task */
|
||||||
|
void engine_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
TickType_t last_wake_time = kernel_get_tick_count();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Wait for next control period */
|
||||||
|
kernel_delay(ENGINE_CONTROL_PERIOD_MS);
|
||||||
|
|
||||||
|
switch (engine_control.state) {
|
||||||
|
case ENGINE_STATE_CRANKING:
|
||||||
|
/* Check RPM for engine start */
|
||||||
|
if (engine_control.sensors.rpm > 400) {
|
||||||
|
engine_control.state = ENGINE_STATE_RUNNING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENGINE_STATE_RUNNING:
|
||||||
|
/* Check for idle condition */
|
||||||
|
if (engine_control.sensors.rpm < ENGINE_IDLE_RPM + 50 &&
|
||||||
|
engine_control.sensors.accelerator_pedal < 5) {
|
||||||
|
engine_control.state = ENGINE_STATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for acceleration */
|
||||||
|
if (engine_control.sensors.accelerator_pedal > 80) {
|
||||||
|
engine_control.state = ENGINE_STATE_ACCELERATING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENGINE_STATE_IDLE:
|
||||||
|
/* Idle speed control */
|
||||||
|
idle_pid_control();
|
||||||
|
|
||||||
|
/* Check if leaving idle */
|
||||||
|
if (engine_control.sensors.accelerator_pedal > 5) {
|
||||||
|
engine_control.state = ENGINE_STATE_RUNNING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENGINE_STATE_ACCELERATING:
|
||||||
|
/* Acceleration enrichment */
|
||||||
|
engine_control.actuators.injector_pulse_width *= 1.2f;
|
||||||
|
|
||||||
|
/* Check if still accelerating */
|
||||||
|
if (engine_control.sensors.accelerator_pedal < 80) {
|
||||||
|
engine_control.state = ENGINE_STATE_RUNNING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENGINE_STATE_DECELERATING:
|
||||||
|
/* Deceleration fuel cutoff */
|
||||||
|
if (engine_control.sensors.rpm > 1500 &&
|
||||||
|
engine_control.sensors.accelerator_pedal < 2) {
|
||||||
|
engine_control.actuators.injector_pulse_width = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if still decelerating */
|
||||||
|
if (engine_control.sensors.accelerator_pedal > 2) {
|
||||||
|
engine_control.state = ENGINE_STATE_RUNNING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENGINE_STATE_FAULT:
|
||||||
|
/* Handle faults */
|
||||||
|
handle_engine_faults();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENGINE_STATE_LIMP_HOME:
|
||||||
|
/* Limp home mode */
|
||||||
|
engine_control.actuators.injector_pulse_width =
|
||||||
|
fuel_map_values[0][3]; /* Fixed fuel */
|
||||||
|
engine_control.actuators.ignition_advance =
|
||||||
|
ignition_map_values[0][3]; /* Fixed timing */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update engine run time */
|
||||||
|
engine_control.engine_run_time += ENGINE_CONTROL_PERIOD_MS;
|
||||||
|
|
||||||
|
/* Check for faults */
|
||||||
|
check_engine_faults();
|
||||||
|
|
||||||
|
last_wake_time = kernel_get_tick_count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Idle Speed Control */
|
||||||
|
static void idle_pid_control(void) {
|
||||||
|
int32_t rpm_error = ENGINE_IDLE_RPM - engine_control.sensors.rpm;
|
||||||
|
|
||||||
|
/* PID calculation */
|
||||||
|
idle_pid.integral += rpm_error * (IDLE_CONTROL_PERIOD_MS / 1000.0f);
|
||||||
|
|
||||||
|
/* Limit integral */
|
||||||
|
if (idle_pid.integral > idle_pid.gains.integral_limit) {
|
||||||
|
idle_pid.integral = idle_pid.gains.integral_limit;
|
||||||
|
} else if (idle_pid.integral < -idle_pid.gains.integral_limit) {
|
||||||
|
idle_pid.integral = -idle_pid.gains.integral_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
float derivative = (rpm_error - idle_pid.previous_error) /
|
||||||
|
(IDLE_CONTROL_PERIOD_MS / 1000.0f);
|
||||||
|
|
||||||
|
idle_pid.output = idle_pid.gains.kp * rpm_error +
|
||||||
|
idle_pid.gains.ki * idle_pid.integral +
|
||||||
|
idle_pid.gains.kd * derivative;
|
||||||
|
|
||||||
|
/* Limit output */
|
||||||
|
if (idle_pid.output > idle_pid.gains.output_limit) {
|
||||||
|
idle_pid.output = idle_pid.gains.output_limit;
|
||||||
|
} else if (idle_pid.output < 0) {
|
||||||
|
idle_pid.output = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update actuator */
|
||||||
|
engine_control.actuators.idle_air_control = (uint16_t)idle_pid.output;
|
||||||
|
|
||||||
|
idle_pid.previous_error = rpm_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check Engine Faults */
|
||||||
|
static void check_engine_faults(void) {
|
||||||
|
/* Check coolant temperature */
|
||||||
|
if (engine_control.sensors.coolant_temp > ENGINE_MAX_COOLANT_TEMP) {
|
||||||
|
add_engine_fault(ENGINE_FAULT_OVERHEAT);
|
||||||
|
engine_control.state = ENGINE_STATE_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check oil pressure */
|
||||||
|
if (engine_control.sensors.oil_pressure < ENGINE_MIN_OIL_PRESSURE &&
|
||||||
|
engine_control.sensors.rpm > 1000) {
|
||||||
|
add_engine_fault(ENGINE_FAULT_LOW_OIL_PRESSURE);
|
||||||
|
engine_control.state = ENGINE_STATE_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check battery voltage */
|
||||||
|
if (engine_control.sensors.battery_voltage < 9.0f ||
|
||||||
|
engine_control.sensors.battery_voltage > 16.0f) {
|
||||||
|
add_engine_fault(ENGINE_FAULT_NONE); // Just log the condition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add Engine Fault */
|
||||||
|
static void add_engine_fault(EngineFaultCode_t fault) {
|
||||||
|
if (engine_control.fault_count < 20) {
|
||||||
|
/* Check if fault already exists */
|
||||||
|
for (uint8_t i = 0; i < engine_control.fault_count; i++) {
|
||||||
|
if (engine_control.faults[i] == fault) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_control.faults[engine_control.fault_count] = fault;
|
||||||
|
engine_control.fault_count++;
|
||||||
|
|
||||||
|
/* Add DTC */
|
||||||
|
DtcCode_t dtc_code = {
|
||||||
|
.high_byte = 0x00, /* Powertrain */
|
||||||
|
.middle_byte = 0x00,
|
||||||
|
.low_byte = fault
|
||||||
|
};
|
||||||
|
dtc_manager_add_dtc(&dtc_code, 3); /* Medium severity */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle Engine Faults */
|
||||||
|
static void handle_engine_faults(void) {
|
||||||
|
for (uint8_t i = 0; i < engine_control.fault_count; i++) {
|
||||||
|
switch (engine_control.faults[i]) {
|
||||||
|
case ENGINE_FAULT_OVERHEAT:
|
||||||
|
/* Enable cooling fan */
|
||||||
|
engine_control.actuators.cooling_fan_duty = 100;
|
||||||
|
|
||||||
|
/* Reduce power */
|
||||||
|
engine_control.actuators.injector_pulse_width *= 0.5f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENGINE_FAULT_LOW_OIL_PRESSURE:
|
||||||
|
/* Immediate engine shutdown */
|
||||||
|
engine_control_stop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Enter limp home mode */
|
||||||
|
engine_control.state = ENGINE_STATE_LIMP_HOME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Engine State */
|
||||||
|
EngineState_t engine_control_get_state(void) {
|
||||||
|
return engine_control.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Sensor Data */
|
||||||
|
KernelStatus_t engine_control_get_sensor_data(EngineSensorData_t* data) {
|
||||||
|
if (data == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&engine_control.data_mutex, 100);
|
||||||
|
memcpy(data, &engine_control.sensors, sizeof(EngineSensorData_t));
|
||||||
|
mutex_unlock(&engine_control.data_mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Actuator Data */
|
||||||
|
KernelStatus_t engine_control_get_actuator_data(EngineActuatorData_t* data) {
|
||||||
|
if (data == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&engine_control.data_mutex, 100);
|
||||||
|
memcpy(data, &engine_control.actuators, sizeof(EngineActuatorData_t));
|
||||||
|
mutex_unlock(&engine_control.data_mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* @file fuel_injection.c
|
||||||
|
* @brief Fuel injection control
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "engine_control.h"
|
||||||
|
#include "fuel_injection.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* Fuel Injection State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
float fuel_pressure;
|
||||||
|
float injection_time;
|
||||||
|
uint16_t injector_duty;
|
||||||
|
uint8_t injection_mode; /* 0=sequential, 1=batch, 2=simultaneous */
|
||||||
|
Mutex_t mutex;
|
||||||
|
} FuelInjectionState_t;
|
||||||
|
|
||||||
|
static FuelInjectionState_t fuel_injection;
|
||||||
|
|
||||||
|
/* Initialize Fuel Injection */
|
||||||
|
KernelStatus_t fuel_injection_init(void) {
|
||||||
|
if (fuel_injection.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
fuel_injection.fuel_pressure = 0;
|
||||||
|
fuel_injection.injection_time = 0;
|
||||||
|
fuel_injection.injector_duty = 0;
|
||||||
|
fuel_injection.injection_mode = 0; /* Sequential */
|
||||||
|
|
||||||
|
mutex_create(&fuel_injection.mutex, false);
|
||||||
|
|
||||||
|
fuel_injection.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fuel Injection Control Task */
|
||||||
|
void fuel_injection_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Wait for next fuel update period */
|
||||||
|
kernel_delay(FUEL_CONTROL_PERIOD_MS);
|
||||||
|
|
||||||
|
EngineSensorData_t sensors;
|
||||||
|
engine_control_get_sensor_data(&sensors);
|
||||||
|
|
||||||
|
/* Calculate required fuel */
|
||||||
|
float required_fuel = calculate_fuel_requirement(&sensors);
|
||||||
|
|
||||||
|
/* Apply corrections */
|
||||||
|
required_fuel = apply_fuel_corrections(required_fuel, &sensors);
|
||||||
|
|
||||||
|
/* Convert to injector pulse width */
|
||||||
|
uint16_t pulse_width = (uint16_t)(required_fuel * 1000); /* Convert to microseconds */
|
||||||
|
|
||||||
|
/* Limit pulse width */
|
||||||
|
if (pulse_width > (uint16_t)(FUEL_MAX_INJECTION_TIME * 1000)) {
|
||||||
|
pulse_width = (uint16_t)(FUEL_MAX_INJECTION_TIME * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update injector PWM */
|
||||||
|
mutex_lock(&fuel_injection.mutex, 100);
|
||||||
|
fuel_injection.injection_time = required_fuel;
|
||||||
|
fuel_injection.injector_duty = calculate_injector_duty(pulse_width,
|
||||||
|
sensors.rpm);
|
||||||
|
mutex_unlock(&fuel_injection.mutex);
|
||||||
|
|
||||||
|
/* Update PWM output */
|
||||||
|
pwm_set_duty_cycle(0, 0, fuel_injection.injector_duty); /* Injector 1 */
|
||||||
|
pwm_set_duty_cycle(0, 1, fuel_injection.injector_duty); /* Injector 2 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate Fuel Requirement */
|
||||||
|
static float calculate_fuel_requirement(const EngineSensorData_t* sensors) {
|
||||||
|
/* Basic fuel calculation using speed-density method */
|
||||||
|
float air_mass = 0;
|
||||||
|
|
||||||
|
if (sensors->mass_air_flow > 0) {
|
||||||
|
/* Use MAF sensor if available */
|
||||||
|
air_mass = sensors->mass_air_flow / (sensors->rpm / 2);
|
||||||
|
} else {
|
||||||
|
/* Speed-density calculation */
|
||||||
|
float air_density = 1.225f; /* kg/m³ at sea level */
|
||||||
|
float engine_displacement = 2.0f; /* 2.0L engine */
|
||||||
|
float volumetric_efficiency = 0.85f;
|
||||||
|
|
||||||
|
air_mass = air_density * engine_displacement *
|
||||||
|
volumetric_efficiency * sensors->manifold_pressure / 101.3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate fuel mass for stoichiometric mixture */
|
||||||
|
float fuel_mass = air_mass / FUEL_STOICHIOMETRIC_RATIO;
|
||||||
|
|
||||||
|
/* Convert to injection time */
|
||||||
|
float injection_time = (fuel_mass * 1000000) / FUEL_INJECTOR_FLOW_RATE;
|
||||||
|
|
||||||
|
return injection_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply Fuel Corrections */
|
||||||
|
static float apply_fuel_corrections(float base_fuel, const EngineSensorData_t* sensors) {
|
||||||
|
float corrected_fuel = base_fuel;
|
||||||
|
|
||||||
|
/* Lambda correction */
|
||||||
|
if (sensors->lambda > 0) {
|
||||||
|
float lambda_error = 1.0f - sensors->lambda;
|
||||||
|
corrected_fuel *= (1.0f + lambda_error * 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Coolant temperature correction */
|
||||||
|
if (sensors->coolant_temp < 70) {
|
||||||
|
float enrichment = (70 - sensors->coolant_temp) * 0.01f;
|
||||||
|
corrected_fuel *= (1.0f + enrichment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Acceleration enrichment */
|
||||||
|
if (sensors->accelerator_pedal > 80) {
|
||||||
|
corrected_fuel *= 1.2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Battery voltage correction */
|
||||||
|
if (sensors->battery_voltage < 12.0f) {
|
||||||
|
corrected_fuel *= (12.0f / sensors->battery_voltage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return corrected_fuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate Injector Duty Cycle */
|
||||||
|
static uint16_t calculate_injector_duty(uint16_t pulse_width_us, uint16_t rpm) {
|
||||||
|
/* Calculate period in microseconds */
|
||||||
|
uint32_t period_us = (60000000UL) / rpm; /* 2 revolutions per cycle */
|
||||||
|
|
||||||
|
/* Calculate duty cycle */
|
||||||
|
uint32_t duty = (pulse_width_us * PWM_MAX_DUTY_CYCLE) / period_us;
|
||||||
|
|
||||||
|
if (duty > PWM_MAX_DUTY_CYCLE) {
|
||||||
|
duty = PWM_MAX_DUTY_CYCLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint16_t)duty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Fuel Injection Data */
|
||||||
|
KernelStatus_t fuel_injection_get_data(float* pressure, float* injection_time,
|
||||||
|
uint16_t* duty_cycle) {
|
||||||
|
if (pressure == NULL || injection_time == NULL || duty_cycle == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&fuel_injection.mutex, 100);
|
||||||
|
*pressure = fuel_injection.fuel_pressure;
|
||||||
|
*injection_time = fuel_injection.injection_time;
|
||||||
|
*duty_cycle = fuel_injection.injector_duty;
|
||||||
|
mutex_unlock(&fuel_injection.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* @file ignition_control.c
|
||||||
|
* @brief Ignition timing control
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "engine_control.h"
|
||||||
|
#include "ignition_control.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* Ignition Control State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
float ignition_advance;
|
||||||
|
float dwell_time;
|
||||||
|
uint8_t ignition_mode;
|
||||||
|
bool knock_detected;
|
||||||
|
uint32_t knock_count;
|
||||||
|
Mutex_t mutex;
|
||||||
|
} IgnitionControlState_t;
|
||||||
|
|
||||||
|
static IgnitionControlState_t ignition_control;
|
||||||
|
|
||||||
|
/* Initialize Ignition Control */
|
||||||
|
KernelStatus_t ignition_control_init(void) {
|
||||||
|
if (ignition_control.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ignition_control.ignition_advance = IGNITION_BASE_ADVANCE;
|
||||||
|
ignition_control.dwell_time = IGNITION_DWELL_TIME;
|
||||||
|
ignition_control.ignition_mode = 0;
|
||||||
|
ignition_control.knock_detected = false;
|
||||||
|
ignition_control.knock_count = 0;
|
||||||
|
|
||||||
|
mutex_create(&ignition_control.mutex, false);
|
||||||
|
|
||||||
|
ignition_control.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignition Control Task */
|
||||||
|
void ignition_control_task(void* parameters) {
|
||||||
|
(void)parameters;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Wait for next ignition update period */
|
||||||
|
kernel_delay(IGNITION_CONTROL_PERIOD_MS);
|
||||||
|
|
||||||
|
EngineSensorData_t sensors;
|
||||||
|
engine_control_get_sensor_data(&sensors);
|
||||||
|
|
||||||
|
/* Calculate ignition advance */
|
||||||
|
float advance = calculate_ignition_advance(&sensors);
|
||||||
|
|
||||||
|
/* Apply knock correction */
|
||||||
|
if (ignition_control.knock_detected) {
|
||||||
|
advance -= 5.0f; /* Retard timing */
|
||||||
|
ignition_control.knock_detected = false;
|
||||||
|
ignition_control.knock_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit advance */
|
||||||
|
if (advance > IGNITION_MAX_ADVANCE) {
|
||||||
|
advance = IGNITION_MAX_ADVANCE;
|
||||||
|
} else if (advance < IGNITION_MIN_ADVANCE) {
|
||||||
|
advance = IGNITION_MIN_ADVANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update ignition control */
|
||||||
|
mutex_lock(&ignition_control.mutex, 100);
|
||||||
|
ignition_control.ignition_advance = advance;
|
||||||
|
mutex_unlock(&ignition_control.mutex);
|
||||||
|
|
||||||
|
/* Update actuator */
|
||||||
|
EngineActuatorData_t actuators;
|
||||||
|
engine_control_get_actuator_data(&actuators);
|
||||||
|
actuators.ignition_advance = advance;
|
||||||
|
engine_control_set_actuator_data(&actuators);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate Ignition Advance */
|
||||||
|
static float calculate_ignition_advance(const EngineSensorData_t* sensors) {
|
||||||
|
float advance = IGNITION_BASE_ADVANCE;
|
||||||
|
|
||||||
|
/* RPM correction */
|
||||||
|
if (sensors->rpm < 1000) {
|
||||||
|
advance += 5.0f; /* More advance at low RPM */
|
||||||
|
} else if (sensors->rpm > 5000) {
|
||||||
|
advance -= 3.0f; /* Less advance at high RPM */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load correction */
|
||||||
|
if (sensors->manifold_pressure < 40) {
|
||||||
|
advance += 2.0f; /* More advance at light load */
|
||||||
|
} else if (sensors->manifold_pressure > 80) {
|
||||||
|
advance -= 4.0f; /* Less advance at high load */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Temperature correction */
|
||||||
|
if (sensors->coolant_temp < 0) {
|
||||||
|
advance += 3.0f; /* More advance when cold */
|
||||||
|
} else if (sensors->coolant_temp > 100) {
|
||||||
|
advance -= 5.0f; /* Less advance when hot */
|
||||||
|
}
|
||||||
|
|
||||||
|
return advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Detect Knock */
|
||||||
|
void ignition_control_detect_knock(void) {
|
||||||
|
/* Read knock sensor */
|
||||||
|
uint16_t knock_signal = adc_read_single(7);
|
||||||
|
|
||||||
|
/* Check for knock */
|
||||||
|
if (knock_signal > 200) {
|
||||||
|
ignition_control.knock_detected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Ignition Data */
|
||||||
|
KernelStatus_t ignition_control_get_data(float* advance, float* dwell) {
|
||||||
|
if (advance == NULL || dwell == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&ignition_control.mutex, 100);
|
||||||
|
*advance = ignition_control.ignition_advance;
|
||||||
|
*dwell = ignition_control.dwell_time;
|
||||||
|
mutex_unlock(&ignition_control.mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* @file board.h
|
||||||
|
* @brief Custom ECU board definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOARD_H
|
||||||
|
#define BOARD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Identification
|
||||||
|
* ============================================================================ */
|
||||||
|
#define BOARD_NAME "CustomECU-v1.0"
|
||||||
|
#define BOARD_MANUFACTURER "Automotive RTOS Team"
|
||||||
|
#define BOARD_VERSION "1.0"
|
||||||
|
#define BOARD_MCU "Custom"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Clock Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
#define SYSTEM_CLOCK 100000000U /* 100 MHz */
|
||||||
|
#define PERIPHERAL_CLOCK 50000000U /* 50 MHz */
|
||||||
|
#define TIMER_CLOCK 10000000U /* 10 MHz */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* LED Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define LED_COUNT 4
|
||||||
|
#define LED_STATUS 0
|
||||||
|
#define LED_ERROR 1
|
||||||
|
#define LED_CAN_TX 2
|
||||||
|
#define LED_CAN_RX 3
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Communication Interfaces
|
||||||
|
* ============================================================================ */
|
||||||
|
#define CAN_INTERFACE_COUNT 2
|
||||||
|
#define UART_INTERFACE_COUNT 2
|
||||||
|
#define SPI_INTERFACE_COUNT 1
|
||||||
|
#define I2C_INTERFACE_COUNT 1
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_init(void);
|
||||||
|
void board_clock_init(void);
|
||||||
|
void board_gpio_init(void);
|
||||||
|
void board_uart_init(void);
|
||||||
|
void board_can_init(void);
|
||||||
|
void board_spi_init(void);
|
||||||
|
void board_i2c_init(void);
|
||||||
|
void board_adc_init(void);
|
||||||
|
void board_pwm_init(void);
|
||||||
|
void board_watchdog_init(void);
|
||||||
|
void board_watchdog_service(void);
|
||||||
|
void board_led_on(uint8_t led);
|
||||||
|
void board_led_off(uint8_t led);
|
||||||
|
void board_led_toggle(uint8_t led);
|
||||||
|
bool board_button_read(uint8_t button);
|
||||||
|
void board_delay_ms(uint32_t ms);
|
||||||
|
void board_delay_us(uint32_t us);
|
||||||
|
float board_get_temperature(void);
|
||||||
|
float board_get_voltage(void);
|
||||||
|
|
||||||
|
#endif /* BOARD_H */
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
/**
|
||||||
|
* @file board_init.c
|
||||||
|
* @brief Custom ECU board initialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "board.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "uart_driver.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "i2c_driver.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static bool board_initialized = false;
|
||||||
|
|
||||||
|
/* Board-specific configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t can_baudrate;
|
||||||
|
uint32_t uart_baudrate;
|
||||||
|
uint8_t node_id;
|
||||||
|
bool can_fd_enabled;
|
||||||
|
} BoardConfig_t;
|
||||||
|
|
||||||
|
static const BoardConfig_t board_config = {
|
||||||
|
.can_baudrate = 500000,
|
||||||
|
.uart_baudrate = 115200,
|
||||||
|
.node_id = 0x01,
|
||||||
|
.can_fd_enabled = true
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_init(void) {
|
||||||
|
if (board_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize clock */
|
||||||
|
board_clock_init();
|
||||||
|
|
||||||
|
/* Initialize GPIO */
|
||||||
|
board_gpio_init();
|
||||||
|
|
||||||
|
/* Initialize UART */
|
||||||
|
board_uart_init();
|
||||||
|
|
||||||
|
/* Initialize CAN */
|
||||||
|
board_can_init();
|
||||||
|
|
||||||
|
/* Initialize SPI */
|
||||||
|
board_spi_init();
|
||||||
|
|
||||||
|
/* Initialize I2C */
|
||||||
|
board_i2c_init();
|
||||||
|
|
||||||
|
/* Initialize ADC */
|
||||||
|
board_adc_init();
|
||||||
|
|
||||||
|
/* Initialize PWM */
|
||||||
|
board_pwm_init();
|
||||||
|
|
||||||
|
/* Initialize watchdog */
|
||||||
|
board_watchdog_init();
|
||||||
|
|
||||||
|
board_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Clock Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_clock_init(void) {
|
||||||
|
/* Custom clock initialization */
|
||||||
|
/* This would be implemented for specific hardware */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* GPIO Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_gpio_init(void) {
|
||||||
|
/* Configure status LEDs */
|
||||||
|
GpioPinConfig_t led_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.output_type = GPIO_OUTPUT_PUSH_PULL,
|
||||||
|
.pull = GPIO_PULL_NONE,
|
||||||
|
.speed = GPIO_SPEED_HIGH
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Configure each LED */
|
||||||
|
for (int i = 0; i < LED_COUNT; i++) {
|
||||||
|
led_config.port = 0;
|
||||||
|
led_config.pin = i;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn off all LEDs */
|
||||||
|
for (int i = 0; i < LED_COUNT; i++) {
|
||||||
|
board_led_off(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* UART Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_uart_init(void) {
|
||||||
|
/* Configure debug UART */
|
||||||
|
UartConfig_t uart_config = {
|
||||||
|
.baudrate = board_config.uart_baudrate,
|
||||||
|
.data_bits = UART_DATA_BITS_8,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.parity = UART_PARITY_NONE,
|
||||||
|
.flow_control = UART_FLOW_CONTROL_NONE,
|
||||||
|
.enable_rx = true,
|
||||||
|
.enable_tx = true,
|
||||||
|
.use_dma = false
|
||||||
|
};
|
||||||
|
|
||||||
|
uart_init(0, &uart_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_can_init(void) {
|
||||||
|
/* Configure main CAN interface */
|
||||||
|
CanConfig_t can_config = {
|
||||||
|
.nominal_baudrate = board_config.can_baudrate,
|
||||||
|
.data_baudrate = board_config.can_baudrate,
|
||||||
|
.frame_type = CAN_FRAME_CLASSIC,
|
||||||
|
.enable_fd = board_config.can_fd_enabled,
|
||||||
|
.enable_automatic_retransmission = true,
|
||||||
|
.filter_count = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
can_init(0, &can_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SPI Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_spi_init(void) {
|
||||||
|
/* Configure SPI for external devices */
|
||||||
|
SpiConfig_t spi_config = {
|
||||||
|
.mode = SPI_MODE_0,
|
||||||
|
.clock_speed = SPI_CLOCK_8MHZ,
|
||||||
|
.data_order = SPI_DATA_ORDER_MSB_FIRST,
|
||||||
|
.data_size = 8,
|
||||||
|
.use_dma = true,
|
||||||
|
.enable_hardware_cs = false,
|
||||||
|
.cs_polarity = SPI_CS_ACTIVE_LOW,
|
||||||
|
.cs_port = 0,
|
||||||
|
.cs_pin = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
spi_init(0, &spi_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* I2C Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_i2c_init(void) {
|
||||||
|
/* Configure I2C for sensors */
|
||||||
|
I2cConfig_t i2c_config = {
|
||||||
|
.speed = I2C_SPEED_FAST,
|
||||||
|
.addressing_mode = I2C_ADDRESSING_7BIT,
|
||||||
|
.own_address = 0x50,
|
||||||
|
.enable_general_call = false,
|
||||||
|
.enable_clock_stretching = true,
|
||||||
|
.use_dma = false
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_init(0, &i2c_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ADC Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_adc_init(void) {
|
||||||
|
/* Configure ADC for analog inputs */
|
||||||
|
AdcConfig_t adc_config = {
|
||||||
|
.resolution = ADC_RESOLUTION_12BIT,
|
||||||
|
.mode = ADC_MODE_SCAN,
|
||||||
|
.trigger_source = ADC_TRIGGER_TIMER,
|
||||||
|
.reference = ADC_REFERENCE_VDD,
|
||||||
|
.enable_dma = true,
|
||||||
|
.conversion_frequency = 1000,
|
||||||
|
.channel_count = 8,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 1, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 2, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 3, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 4, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 5, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 6, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 7, .sampling_time = ADC_SAMPLING_28_5_CYCLES}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
adc_init(0, &adc_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* PWM Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_pwm_init(void) {
|
||||||
|
/* Configure PWM outputs */
|
||||||
|
PwmConfig_t pwm_config = {
|
||||||
|
.frequency_hz = 1000,
|
||||||
|
.alignment = PWM_ALIGNMENT_EDGE,
|
||||||
|
.period_ticks = 1000,
|
||||||
|
.prescaler = 100,
|
||||||
|
.channel_count = 4,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .duty_cycle = 0},
|
||||||
|
{.channel = 1, .duty_cycle = 0},
|
||||||
|
{.channel = 2, .duty_cycle = 0},
|
||||||
|
{.channel = 3, .duty_cycle = 0}
|
||||||
|
},
|
||||||
|
.enable_fault_protection = true,
|
||||||
|
.fault_action = PWM_FAULT_DISABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
pwm_init(0, &pwm_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Watchdog Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_watchdog_init(void) {
|
||||||
|
/* Custom watchdog initialization */
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_watchdog_service(void) {
|
||||||
|
/* Custom watchdog service */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* LED Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_led_on(uint8_t led) {
|
||||||
|
if (led < LED_COUNT) {
|
||||||
|
gpio_write(0, led, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_led_off(uint8_t led) {
|
||||||
|
if (led < LED_COUNT) {
|
||||||
|
gpio_write(0, led, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_led_toggle(uint8_t led) {
|
||||||
|
if (led < LED_COUNT) {
|
||||||
|
gpio_toggle(0, led);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Button Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
bool board_button_read(uint8_t button) {
|
||||||
|
/* Custom button implementation */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Delay Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_delay_ms(uint32_t ms) {
|
||||||
|
kernel_delay(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_delay_us(uint32_t us) {
|
||||||
|
/* Custom microsecond delay */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Sensor Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
float board_get_temperature(void) {
|
||||||
|
/* Custom temperature sensor reading */
|
||||||
|
return 25.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float board_get_voltage(void) {
|
||||||
|
/* Custom voltage reading */
|
||||||
|
return 3.3f;
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/**
|
||||||
|
* @file linker_script.ld
|
||||||
|
* @brief Linker script for Custom ECU
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 512K
|
||||||
|
EEPROM (rw) : ORIGIN = 0x08080000, LENGTH = 16K
|
||||||
|
}
|
||||||
|
|
||||||
|
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
_min_stack_size = 0x800;
|
||||||
|
_min_heap_size = 0x400;
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.isr_vector :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(.isr_vector))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text)
|
||||||
|
*(.text*)
|
||||||
|
KEEP(*(.init))
|
||||||
|
KEEP(*(.fini))
|
||||||
|
. = ALIGN(4);
|
||||||
|
_etext = .;
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.rodata :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.ARM :
|
||||||
|
{
|
||||||
|
__exidx_start = .;
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
__exidx_end = .;
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__preinit_array_start = .);
|
||||||
|
KEEP(*(.preinit_array*))
|
||||||
|
PROVIDE_HIDDEN(__preinit_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__init_array_start = .);
|
||||||
|
KEEP(*(SORT(.init_array.*)))
|
||||||
|
KEEP(*(.init_array*))
|
||||||
|
PROVIDE_HIDDEN(__init_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__fini_array_start = .);
|
||||||
|
KEEP(*(SORT(.fini_array.*)))
|
||||||
|
KEEP(*(.fini_array*))
|
||||||
|
PROVIDE_HIDDEN(__fini_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data)
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_edata = .;
|
||||||
|
} >RAM AT> FLASH
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sbss = .;
|
||||||
|
__bss_start__ = _sbss;
|
||||||
|
*(.bss)
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
.eeprom :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.eeprom)
|
||||||
|
*(.eeprom*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >EEPROM
|
||||||
|
|
||||||
|
._user_heap_stack :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE(end = .);
|
||||||
|
PROVIDE(_end = .);
|
||||||
|
. = . + _min_heap_size;
|
||||||
|
. = . + _min_stack_size;
|
||||||
|
. = ALIGN(8);
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
libc.a(*)
|
||||||
|
libm.a(*)
|
||||||
|
libgcc.a(*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* @file board.h
|
||||||
|
* @brief NXP S32K144 EVB board definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOARD_H
|
||||||
|
#define BOARD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "S32K144.h"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Identification
|
||||||
|
* ============================================================================ */
|
||||||
|
#define BOARD_NAME "S32K144EVB-Q100"
|
||||||
|
#define BOARD_MANUFACTURER "NXP Semiconductors"
|
||||||
|
#define BOARD_VERSION "1.0"
|
||||||
|
#define BOARD_MCU "S32K144"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Clock Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
#define SOSC_FREQUENCY 8000000U /* System oscillator */
|
||||||
|
#define SPLL_FREQUENCY 160000000U /* System PLL */
|
||||||
|
#define FIRC_FREQUENCY 48000000U /* Fast IRC */
|
||||||
|
#define SIRC_FREQUENCY 8000000U /* Slow IRC */
|
||||||
|
|
||||||
|
#define SYSTEM_CLOCK 160000000U /* 160 MHz */
|
||||||
|
#define BUS_CLOCK 40000000U /* 40 MHz */
|
||||||
|
#define FLASH_CLOCK 20000000U /* 20 MHz */
|
||||||
|
|
||||||
|
/* PLL Configuration */
|
||||||
|
#define PLL_MULT 20
|
||||||
|
#define PLL_DIV 1
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* LED Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define LED_COUNT 3
|
||||||
|
|
||||||
|
#define LED_RED_PORT PTC
|
||||||
|
#define LED_RED_PIN 0
|
||||||
|
#define LED_RED_INDEX 0
|
||||||
|
|
||||||
|
#define LED_GREEN_PORT PTC
|
||||||
|
#define LED_GREEN_PIN 1
|
||||||
|
#define LED_GREEN_INDEX 1
|
||||||
|
|
||||||
|
#define LED_BLUE_PORT PTC
|
||||||
|
#define LED_BLUE_PIN 2
|
||||||
|
#define LED_BLUE_INDEX 2
|
||||||
|
|
||||||
|
/* LED Active Level */
|
||||||
|
#define LED_ACTIVE_LEVEL 0 /* Active low */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Button Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define BUTTON_COUNT 2
|
||||||
|
|
||||||
|
#define BUTTON1_PORT PTC
|
||||||
|
#define BUTTON1_PIN 3
|
||||||
|
#define BUTTON1_INDEX 0
|
||||||
|
|
||||||
|
#define BUTTON2_PORT PTC
|
||||||
|
#define BUTTON2_PIN 4
|
||||||
|
#define BUTTON2_INDEX 1
|
||||||
|
|
||||||
|
/* Button Active Level */
|
||||||
|
#define BUTTON_ACTIVE_LEVEL 0 /* Active low */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* UART Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define UART0_INSTANCE 0
|
||||||
|
#define UART0_BAUDRATE 115200
|
||||||
|
#define UART0_TX_PORT PTA
|
||||||
|
#define UART0_TX_PIN 2
|
||||||
|
#define UART0_TX_MUX 2
|
||||||
|
#define UART0_RX_PORT PTA
|
||||||
|
#define UART0_RX_PIN 3
|
||||||
|
#define UART0_RX_MUX 2
|
||||||
|
|
||||||
|
#define UART1_INSTANCE 1
|
||||||
|
#define UART1_BAUDRATE 115200
|
||||||
|
#define UART1_TX_PORT PTC
|
||||||
|
#define UART1_TX_PIN 7
|
||||||
|
#define UART1_TX_MUX 2
|
||||||
|
#define UART1_RX_PORT PTC
|
||||||
|
#define UART1_RX_PIN 6
|
||||||
|
#define UART1_RX_MUX 2
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define CAN0_INSTANCE 0
|
||||||
|
#define CAN0_BAUDRATE 500000
|
||||||
|
#define CAN0_TX_PORT PTE
|
||||||
|
#define CAN0_TX_PIN 5
|
||||||
|
#define CAN0_TX_MUX 5
|
||||||
|
#define CAN0_RX_PORT PTE
|
||||||
|
#define CAN0_RX_PIN 4
|
||||||
|
#define CAN0_RX_MUX 5
|
||||||
|
|
||||||
|
#define CAN1_INSTANCE 1
|
||||||
|
#define CAN1_BAUDRATE 500000
|
||||||
|
#define CAN1_TX_PORT PTC
|
||||||
|
#define CAN1_TX_PIN 17
|
||||||
|
#define CAN1_TX_MUX 2
|
||||||
|
#define CAN1_RX_PORT PTC
|
||||||
|
#define CAN1_RX_PIN 16
|
||||||
|
#define CAN1_RX_MUX 2
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SPI Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define SPI0_INSTANCE 0
|
||||||
|
#define SPI0_SCK_PORT PTA
|
||||||
|
#define SPI0_SCK_PIN 6
|
||||||
|
#define SPI0_SCK_MUX 2
|
||||||
|
#define SPI0_MISO_PORT PTA
|
||||||
|
#define SPI0_MISO_PIN 7
|
||||||
|
#define SPI0_MISO_MUX 2
|
||||||
|
#define SPI0_MOSI_PORT PTA
|
||||||
|
#define SPI0_MOSI_PIN 8
|
||||||
|
#define SPI0_MOSI_MUX 2
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* I2C Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define I2C0_INSTANCE 0
|
||||||
|
#define I2C0_SCL_PORT PTA
|
||||||
|
#define I2C0_SCL_PIN 9
|
||||||
|
#define I2C0_SCL_MUX 3
|
||||||
|
#define I2C0_SDA_PORT PTA
|
||||||
|
#define I2C0_SDA_PIN 10
|
||||||
|
#define I2C0_SDA_MUX 3
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ADC Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define ADC0_INSTANCE 0
|
||||||
|
#define ADC0_CHANNEL_COUNT 8
|
||||||
|
#define ADC0_CH0_PORT PTA
|
||||||
|
#define ADC0_CH0_PIN 0
|
||||||
|
#define ADC0_CH0_MUX 0
|
||||||
|
#define ADC0_CH1_PORT PTA
|
||||||
|
#define ADC0_CH1_PIN 1
|
||||||
|
#define ADC0_CH1_MUX 0
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* PWM Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define PWM_INSTANCE 0
|
||||||
|
#define PWM_TIMER FTM0
|
||||||
|
#define PWM_TIMER_CHANNEL_COUNT 4
|
||||||
|
#define PWM_TIMER_FREQUENCY 10000
|
||||||
|
#define PWM_TIMER_PRESCALER 4
|
||||||
|
|
||||||
|
#define PWM_CH0_PORT PTC
|
||||||
|
#define PWM_CH0_PIN 1
|
||||||
|
#define PWM_CH0_MUX 4
|
||||||
|
#define PWM_CH1_PORT PTC
|
||||||
|
#define PWM_CH1_PIN 2
|
||||||
|
#define PWM_CH1_MUX 4
|
||||||
|
#define PWM_CH2_PORT PTC
|
||||||
|
#define PWM_CH2_PIN 3
|
||||||
|
#define PWM_CH2_MUX 4
|
||||||
|
#define PWM_CH3_PORT PTC
|
||||||
|
#define PWM_CH3_PIN 4
|
||||||
|
#define PWM_CH3_MUX 4
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Watchdog Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
#define WATCHDOG_TIMEOUT_MS 100
|
||||||
|
#define WATCHDOG_WINDOW_MS 50
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_init(void);
|
||||||
|
void board_clock_init(void);
|
||||||
|
void board_gpio_init(void);
|
||||||
|
void board_uart_init(void);
|
||||||
|
void board_can_init(void);
|
||||||
|
void board_spi_init(void);
|
||||||
|
void board_i2c_init(void);
|
||||||
|
void board_adc_init(void);
|
||||||
|
void board_pwm_init(void);
|
||||||
|
void board_watchdog_init(void);
|
||||||
|
void board_watchdog_service(void);
|
||||||
|
void board_led_on(uint8_t led);
|
||||||
|
void board_led_off(uint8_t led);
|
||||||
|
void board_led_toggle(uint8_t led);
|
||||||
|
bool board_button_read(uint8_t button);
|
||||||
|
void board_delay_ms(uint32_t ms);
|
||||||
|
void board_delay_us(uint32_t us);
|
||||||
|
float board_get_temperature(void);
|
||||||
|
float board_get_voltage(void);
|
||||||
|
|
||||||
|
#endif /* BOARD_H */
|
||||||
@@ -0,0 +1,371 @@
|
|||||||
|
/**
|
||||||
|
* @file board_init.c
|
||||||
|
* @brief NXP S32K144 EVB board initialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "board.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "uart_driver.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "i2c_driver.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static bool board_initialized = false;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_init(void) {
|
||||||
|
if (board_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize clock */
|
||||||
|
board_clock_init();
|
||||||
|
|
||||||
|
/* Initialize GPIO */
|
||||||
|
board_gpio_init();
|
||||||
|
|
||||||
|
/* Initialize UART */
|
||||||
|
board_uart_init();
|
||||||
|
|
||||||
|
/* Initialize CAN */
|
||||||
|
board_can_init();
|
||||||
|
|
||||||
|
/* Initialize SPI */
|
||||||
|
board_spi_init();
|
||||||
|
|
||||||
|
/* Initialize I2C */
|
||||||
|
board_i2c_init();
|
||||||
|
|
||||||
|
/* Initialize ADC */
|
||||||
|
board_adc_init();
|
||||||
|
|
||||||
|
/* Initialize PWM */
|
||||||
|
board_pwm_init();
|
||||||
|
|
||||||
|
/* Initialize watchdog */
|
||||||
|
board_watchdog_init();
|
||||||
|
|
||||||
|
board_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Clock Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_clock_init(void) {
|
||||||
|
/* Configure system PLL */
|
||||||
|
SCG->SPLLCSR = 0; /* Reset SPLL */
|
||||||
|
SCG->SPLLDIV = SCG_SPLLDIV_SPLLDIV1(1) | SCG_SPLLDIV_SPLLDIV2(1);
|
||||||
|
SCG->SPLLCFG = SCG_SPLLCFG_MULT(20); /* 8MHz * 20 = 160MHz */
|
||||||
|
|
||||||
|
/* Enable SPLL */
|
||||||
|
SCG->SPLLCSR |= SCG_SPLLCSR_SPLLEN_MASK;
|
||||||
|
|
||||||
|
/* Wait for PLL lock */
|
||||||
|
while (!(SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK));
|
||||||
|
|
||||||
|
/* Switch to SPLL */
|
||||||
|
SCG->RCCR = SCG_RCCR_DIVCORE(1) |
|
||||||
|
SCG_RCCR_DIVBUS(4) | /* 160/4 = 40MHz */
|
||||||
|
SCG_RCCR_DIVSLOW(8) | /* 160/8 = 20MHz */
|
||||||
|
SCG_RCCR_SCS(6); /* SPLL */
|
||||||
|
|
||||||
|
/* Wait for clock switch */
|
||||||
|
while ((SCG->CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS(6));
|
||||||
|
|
||||||
|
/* Update SystemCoreClock */
|
||||||
|
SystemCoreClock = SYSTEM_CLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* GPIO Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_gpio_init(void) {
|
||||||
|
/* Enable GPIO clocks */
|
||||||
|
PCC->PCCn[PCC_PORTA_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||||
|
PCC->PCCn[PCC_PORTB_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||||
|
PCC->PCCn[PCC_PORTC_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||||
|
PCC->PCCn[PCC_PORTD_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||||
|
PCC->PCCn[PCC_PORTE_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||||
|
|
||||||
|
/* Configure LEDs */
|
||||||
|
GpioPinConfig_t led_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.output_type = GPIO_OUTPUT_PUSH_PULL,
|
||||||
|
.pull = GPIO_PULL_NONE,
|
||||||
|
.speed = GPIO_SPEED_HIGH
|
||||||
|
};
|
||||||
|
|
||||||
|
led_config.port = 2; /* PTC */
|
||||||
|
led_config.pin = 0;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
|
||||||
|
led_config.pin = 1;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
|
||||||
|
led_config.pin = 2;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
|
||||||
|
/* Configure buttons */
|
||||||
|
GpioPinConfig_t button_config = {
|
||||||
|
.port = 2, /* PTC */
|
||||||
|
.pin = 3,
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.pull = GPIO_PULL_UP
|
||||||
|
};
|
||||||
|
gpio_init(&button_config);
|
||||||
|
|
||||||
|
button_config.pin = 4;
|
||||||
|
gpio_init(&button_config);
|
||||||
|
|
||||||
|
/* Turn off all LEDs */
|
||||||
|
board_led_off(0);
|
||||||
|
board_led_off(1);
|
||||||
|
board_led_off(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* UART Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_uart_init(void) {
|
||||||
|
/* Configure UART0 */
|
||||||
|
UartConfig_t uart_config = {
|
||||||
|
.baudrate = UART0_BAUDRATE,
|
||||||
|
.data_bits = UART_DATA_BITS_8,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.parity = UART_PARITY_NONE,
|
||||||
|
.flow_control = UART_FLOW_CONTROL_NONE,
|
||||||
|
.enable_rx = true,
|
||||||
|
.enable_tx = true,
|
||||||
|
.use_dma = false
|
||||||
|
};
|
||||||
|
|
||||||
|
uart_init(UART0_INSTANCE, &uart_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_can_init(void) {
|
||||||
|
/* Configure CAN0 */
|
||||||
|
CanConfig_t can_config = {
|
||||||
|
.nominal_baudrate = CAN0_BAUDRATE,
|
||||||
|
.data_baudrate = CAN0_BAUDRATE,
|
||||||
|
.frame_type = CAN_FRAME_CLASSIC,
|
||||||
|
.enable_fd = true, /* S32K144 supports CAN FD */
|
||||||
|
.enable_automatic_retransmission = true,
|
||||||
|
.filter_count = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
can_init(CAN0_INSTANCE, &can_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SPI Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_spi_init(void) {
|
||||||
|
/* Configure SPI0 */
|
||||||
|
SpiConfig_t spi_config = {
|
||||||
|
.mode = SPI_MODE_0,
|
||||||
|
.clock_speed = SPI_CLOCK_8MHZ,
|
||||||
|
.data_order = SPI_DATA_ORDER_MSB_FIRST,
|
||||||
|
.data_size = 8,
|
||||||
|
.use_dma = false,
|
||||||
|
.enable_hardware_cs = false,
|
||||||
|
.cs_polarity = SPI_CS_ACTIVE_LOW,
|
||||||
|
.cs_port = 1,
|
||||||
|
.cs_pin = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
spi_init(SPI0_INSTANCE, &spi_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* I2C Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_i2c_init(void) {
|
||||||
|
/* Configure I2C0 */
|
||||||
|
I2cConfig_t i2c_config = {
|
||||||
|
.speed = I2C_SPEED_FAST,
|
||||||
|
.addressing_mode = I2C_ADDRESSING_7BIT,
|
||||||
|
.own_address = 0x50,
|
||||||
|
.enable_general_call = false,
|
||||||
|
.enable_clock_stretching = true,
|
||||||
|
.use_dma = false
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_init(I2C0_INSTANCE, &i2c_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ADC Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_adc_init(void) {
|
||||||
|
/* Configure ADC0 */
|
||||||
|
AdcConfig_t adc_config = {
|
||||||
|
.resolution = ADC_RESOLUTION_12BIT,
|
||||||
|
.mode = ADC_MODE_SCAN,
|
||||||
|
.trigger_source = ADC_TRIGGER_SOFTWARE,
|
||||||
|
.reference = ADC_REFERENCE_VDD,
|
||||||
|
.enable_dma = false,
|
||||||
|
.conversion_frequency = 1000,
|
||||||
|
.channel_count = 8,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .sampling_time = ADC_SAMPLING_13_5_CYCLES},
|
||||||
|
{.channel = 1, .sampling_time = ADC_SAMPLING_13_5_CYCLES},
|
||||||
|
{.channel = 2, .sampling_time = ADC_SAMPLING_13_5_CYCLES},
|
||||||
|
{.channel = 3, .sampling_time = ADC_SAMPLING_13_5_CYCLES},
|
||||||
|
{.channel = 4, .sampling_time = ADC_SAMPLING_13_5_CYCLES},
|
||||||
|
{.channel = 5, .sampling_time = ADC_SAMPLING_13_5_CYCLES},
|
||||||
|
{.channel = 6, .sampling_time = ADC_SAMPLING_13_5_CYCLES},
|
||||||
|
{.channel = 7, .sampling_time = ADC_SAMPLING_13_5_CYCLES}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
adc_init(ADC0_INSTANCE, &adc_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* PWM Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_pwm_init(void) {
|
||||||
|
/* Configure PWM */
|
||||||
|
PwmConfig_t pwm_config = {
|
||||||
|
.frequency_hz = PWM_TIMER_FREQUENCY,
|
||||||
|
.alignment = PWM_ALIGNMENT_EDGE,
|
||||||
|
.period_ticks = 10000,
|
||||||
|
.prescaler = PWM_TIMER_PRESCALER,
|
||||||
|
.channel_count = 4,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .duty_cycle = 0},
|
||||||
|
{.channel = 1, .duty_cycle = 0},
|
||||||
|
{.channel = 2, .duty_cycle = 0},
|
||||||
|
{.channel = 3, .duty_cycle = 0}
|
||||||
|
},
|
||||||
|
.enable_fault_protection = true,
|
||||||
|
.fault_action = PWM_FAULT_DISABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
pwm_init(PWM_INSTANCE, &pwm_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Watchdog Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_watchdog_init(void) {
|
||||||
|
/* Configure WDOG */
|
||||||
|
WDOG->CNT = 0x1000; /* Timeout value */
|
||||||
|
WDOG->TOVAL = 0x1000;
|
||||||
|
WDOG->CS = WDOG_CS_EN_MASK |
|
||||||
|
WDOG_CS_CLK(1) |
|
||||||
|
WDOG_CS_WIN_MASK |
|
||||||
|
WDOG_CS_UPDATE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_watchdog_service(void) {
|
||||||
|
/* Service WDOG */
|
||||||
|
WDOG->CNT = 0xB480;
|
||||||
|
WDOG->CNT = 0x4B80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* LED Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_led_on(uint8_t led) {
|
||||||
|
switch (led) {
|
||||||
|
case 0:
|
||||||
|
gpio_write(2, 0, false); /* Active low */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gpio_write(2, 1, false);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gpio_write(2, 2, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_led_off(uint8_t led) {
|
||||||
|
switch (led) {
|
||||||
|
case 0:
|
||||||
|
gpio_write(2, 0, true);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gpio_write(2, 1, true);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gpio_write(2, 2, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_led_toggle(uint8_t led) {
|
||||||
|
switch (led) {
|
||||||
|
case 0:
|
||||||
|
gpio_toggle(2, 0);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gpio_toggle(2, 1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gpio_toggle(2, 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Button Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
bool board_button_read(uint8_t button) {
|
||||||
|
switch (button) {
|
||||||
|
case 0:
|
||||||
|
return !gpio_read(2, 3); /* Active low */
|
||||||
|
case 1:
|
||||||
|
return !gpio_read(2, 4);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Delay Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_delay_ms(uint32_t ms) {
|
||||||
|
kernel_delay(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_delay_us(uint32_t us) {
|
||||||
|
uint32_t count = us * (SystemCoreClock / 1000000) / 5;
|
||||||
|
while (count--) {
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Sensor Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
float board_get_temperature(void) {
|
||||||
|
/* Read internal temperature sensor */
|
||||||
|
uint16_t adc_value = 0;
|
||||||
|
adc_read_channel(ADC0_INSTANCE, 26, &adc_value, 10); /* Internal temp sensor */
|
||||||
|
|
||||||
|
/* Convert to temperature (S32K144 specific formula) */
|
||||||
|
float voltage = adc_convert_to_voltage(adc_value, ADC_RESOLUTION_12BIT, 3.3f);
|
||||||
|
float temperature = 25.0f - ((voltage - 0.716f) / 0.00162f);
|
||||||
|
|
||||||
|
return temperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
float board_get_voltage(void) {
|
||||||
|
/* Read bandgap reference */
|
||||||
|
uint16_t adc_value = 0;
|
||||||
|
adc_read_channel(ADC0_INSTANCE, 27, &adc_value, 10); /* Bandgap */
|
||||||
|
|
||||||
|
float voltage = adc_convert_to_voltage(adc_value, ADC_RESOLUTION_12BIT, 3.3f);
|
||||||
|
return voltage;
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* @file linker_script.ld
|
||||||
|
* @brief Linker script for NXP S32K144
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K
|
||||||
|
SRAM_L (rwx) : ORIGIN = 0x1FFF8000, LENGTH = 32K
|
||||||
|
SRAM_U (rwx) : ORIGIN = 0x20000000, LENGTH = 28K
|
||||||
|
}
|
||||||
|
|
||||||
|
_estack = ORIGIN(SRAM_U) + LENGTH(SRAM_U);
|
||||||
|
_min_stack_size = 0x400;
|
||||||
|
_min_heap_size = 0x200;
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* Interrupt Vector Table */
|
||||||
|
.isr_vector :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(.isr_vector))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Program Code */
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text)
|
||||||
|
*(.text*)
|
||||||
|
*(.glue_7)
|
||||||
|
*(.glue_7t)
|
||||||
|
*(.eh_frame)
|
||||||
|
|
||||||
|
KEEP(*(.init))
|
||||||
|
KEEP(*(.fini))
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_etext = .;
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Constant Data */
|
||||||
|
.rodata :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* ARM Extensions */
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.ARM :
|
||||||
|
{
|
||||||
|
__exidx_start = .;
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
__exidx_end = .;
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Initialization Arrays */
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__preinit_array_start = .);
|
||||||
|
KEEP(*(.preinit_array*))
|
||||||
|
PROVIDE_HIDDEN(__preinit_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__init_array_start = .);
|
||||||
|
KEEP(*(SORT(.init_array.*)))
|
||||||
|
KEEP(*(.init_array*))
|
||||||
|
PROVIDE_HIDDEN(__init_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__fini_array_start = .);
|
||||||
|
KEEP(*(SORT(.fini_array.*)))
|
||||||
|
KEEP(*(.fini_array*))
|
||||||
|
PROVIDE_HIDDEN(__fini_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Data Sections */
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data)
|
||||||
|
*(.data*)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_edata = .;
|
||||||
|
} >SRAM_L AT> FLASH
|
||||||
|
|
||||||
|
/* BSS Section */
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sbss = .;
|
||||||
|
__bss_start__ = _sbss;
|
||||||
|
*(.bss)
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} >SRAM_L
|
||||||
|
|
||||||
|
/* Heap and Stack */
|
||||||
|
._user_heap_stack :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE(end = .);
|
||||||
|
PROVIDE(_end = .);
|
||||||
|
. = . + _min_heap_size;
|
||||||
|
. = . + _min_stack_size;
|
||||||
|
. = ALIGN(8);
|
||||||
|
} >SRAM_U
|
||||||
|
|
||||||
|
/* Discard */
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
libc.a(*)
|
||||||
|
libm.a(*)
|
||||||
|
libgcc.a(*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
/**
|
||||||
|
* @file board.h
|
||||||
|
* @brief STM32F407 Discovery board definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOARD_H
|
||||||
|
#define BOARD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "stm32f4xx.h"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Identification
|
||||||
|
* ============================================================================ */
|
||||||
|
#define BOARD_NAME "STM32F407G-DISC1"
|
||||||
|
#define BOARD_MANUFACTURER "STMicroelectronics"
|
||||||
|
#define BOARD_VERSION "1.0"
|
||||||
|
#define BOARD_MCU "STM32F407VG"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Clock Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
#define HSE_VALUE 8000000U /* External crystal */
|
||||||
|
#define HSI_VALUE 16000000U /* Internal oscillator */
|
||||||
|
#define LSE_VALUE 32768U /* Low speed external */
|
||||||
|
#define LSI_VALUE 32000U /* Low speed internal */
|
||||||
|
|
||||||
|
#define SYSTEM_CLOCK 168000000U /* 168 MHz */
|
||||||
|
#define AHB_CLOCK 168000000U
|
||||||
|
#define APB1_CLOCK 42000000U
|
||||||
|
#define APB2_CLOCK 84000000U
|
||||||
|
#define USB_CLOCK 48000000U
|
||||||
|
|
||||||
|
/* PLL Configuration */
|
||||||
|
#define PLL_M 8
|
||||||
|
#define PLL_N 336
|
||||||
|
#define PLL_P 2
|
||||||
|
#define PLL_Q 7
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* LED Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define LED_COUNT 4
|
||||||
|
|
||||||
|
#define LED1_PORT GPIOD
|
||||||
|
#define LED1_PIN GPIO_PIN_12
|
||||||
|
#define LED1_CLOCK __HAL_RCC_GPIOD_CLK_ENABLE()
|
||||||
|
|
||||||
|
#define LED2_PORT GPIOD
|
||||||
|
#define LED2_PIN GPIO_PIN_13
|
||||||
|
#define LED2_CLOCK __HAL_RCC_GPIOD_CLK_ENABLE()
|
||||||
|
|
||||||
|
#define LED3_PORT GPIOD
|
||||||
|
#define LED3_PIN GPIO_PIN_14
|
||||||
|
#define LED3_CLOCK __HAL_RCC_GPIOD_CLK_ENABLE()
|
||||||
|
|
||||||
|
#define LED4_PORT GPIOD
|
||||||
|
#define LED4_PIN GPIO_PIN_15
|
||||||
|
#define LED4_CLOCK __HAL_RCC_GPIOD_CLK_ENABLE()
|
||||||
|
|
||||||
|
/* LED Active Level */
|
||||||
|
#define LED_ACTIVE_LEVEL GPIO_PIN_SET
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Button Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define BUTTON_COUNT 1
|
||||||
|
|
||||||
|
#define BUTTON1_PORT GPIOA
|
||||||
|
#define BUTTON1_PIN GPIO_PIN_0
|
||||||
|
#define BUTTON1_CLOCK __HAL_RCC_GPIOA_CLK_ENABLE()
|
||||||
|
#define BUTTON1_EXTI_IRQ EXTI0_IRQn
|
||||||
|
|
||||||
|
/* Button Active Level */
|
||||||
|
#define BUTTON_ACTIVE_LEVEL GPIO_PIN_SET
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* UART Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define UART1_INSTANCE 0
|
||||||
|
#define UART1_BAUDRATE 115200
|
||||||
|
#define UART1_WORDLENGTH UART_WORDLENGTH_8B
|
||||||
|
#define UART1_STOPBITS UART_STOPBITS_1
|
||||||
|
#define UART1_PARITY UART_PARITY_NONE
|
||||||
|
#define UART1_HWFLOWCTL UART_HWCONTROL_NONE
|
||||||
|
|
||||||
|
#define UART1_TX_PORT GPIOA
|
||||||
|
#define UART1_TX_PIN GPIO_PIN_9
|
||||||
|
#define UART1_TX_AF GPIO_AF7_USART1
|
||||||
|
#define UART1_RX_PORT GPIOA
|
||||||
|
#define UART1_RX_PIN GPIO_PIN_10
|
||||||
|
#define UART1_RX_AF GPIO_AF7_USART1
|
||||||
|
#define UART1_CLOCK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE()
|
||||||
|
#define UART1_GPIO_CLOCK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
|
||||||
|
|
||||||
|
#define UART2_INSTANCE 1
|
||||||
|
#define UART2_BAUDRATE 115200
|
||||||
|
#define UART2_TX_PORT GPIOA
|
||||||
|
#define UART2_TX_PIN GPIO_PIN_2
|
||||||
|
#define UART2_TX_AF GPIO_AF7_USART2
|
||||||
|
#define UART2_RX_PORT GPIOA
|
||||||
|
#define UART2_RX_PIN GPIO_PIN_3
|
||||||
|
#define UART2_RX_AF GPIO_AF7_USART2
|
||||||
|
#define UART2_CLOCK_ENABLE() __HAL_RCC_USART2_CLK_ENABLE()
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define CAN1_INSTANCE 0
|
||||||
|
#define CAN1_BAUDRATE 500000
|
||||||
|
#define CAN1_TX_PORT GPIOB
|
||||||
|
#define CAN1_TX_PIN GPIO_PIN_9
|
||||||
|
#define CAN1_TX_AF GPIO_AF9_CAN1
|
||||||
|
#define CAN1_RX_PORT GPIOB
|
||||||
|
#define CAN1_RX_PIN GPIO_PIN_8
|
||||||
|
#define CAN1_RX_AF GPIO_AF9_CAN1
|
||||||
|
#define CAN1_CLOCK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE()
|
||||||
|
#define CAN1_GPIO_CLOCK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
|
||||||
|
|
||||||
|
#define CAN2_INSTANCE 1
|
||||||
|
#define CAN2_BAUDRATE 500000
|
||||||
|
#define CAN2_TX_PORT GPIOB
|
||||||
|
#define CAN2_TX_PIN GPIO_PIN_13
|
||||||
|
#define CAN2_TX_AF GPIO_AF9_CAN2
|
||||||
|
#define CAN2_RX_PORT GPIOB
|
||||||
|
#define CAN2_RX_PIN GPIO_PIN_12
|
||||||
|
#define CAN2_RX_AF GPIO_AF9_CAN2
|
||||||
|
#define CAN2_CLOCK_ENABLE() __HAL_RCC_CAN2_CLK_ENABLE()
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SPI Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define SPI1_INSTANCE 0
|
||||||
|
#define SPI1_SCK_PORT GPIOA
|
||||||
|
#define SPI1_SCK_PIN GPIO_PIN_5
|
||||||
|
#define SPI1_SCK_AF GPIO_AF5_SPI1
|
||||||
|
#define SPI1_MISO_PORT GPIOA
|
||||||
|
#define SPI1_MISO_PIN GPIO_PIN_6
|
||||||
|
#define SPI1_MISO_AF GPIO_AF5_SPI1
|
||||||
|
#define SPI1_MOSI_PORT GPIOA
|
||||||
|
#define SPI1_MOSI_PIN GPIO_PIN_7
|
||||||
|
#define SPI1_MOSI_AF GPIO_AF5_SPI1
|
||||||
|
#define SPI1_CLOCK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* I2C Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define I2C1_INSTANCE 0
|
||||||
|
#define I2C1_SCL_PORT GPIOB
|
||||||
|
#define I2C1_SCL_PIN GPIO_PIN_6
|
||||||
|
#define I2C1_SCL_AF GPIO_AF4_I2C1
|
||||||
|
#define I2C1_SDA_PORT GPIOB
|
||||||
|
#define I2C1_SDA_PIN GPIO_PIN_7
|
||||||
|
#define I2C1_SDA_AF GPIO_AF4_I2C1
|
||||||
|
#define I2C1_CLOCK_ENABLE() __HAL_RCC_I2C1_CLK_ENABLE()
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ADC Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define ADC1_INSTANCE 0
|
||||||
|
#define ADC1_CHANNEL_COUNT 6
|
||||||
|
#define ADC1_CH0_PORT GPIOA
|
||||||
|
#define ADC1_CH0_PIN GPIO_PIN_0
|
||||||
|
#define ADC1_CH1_PORT GPIOA
|
||||||
|
#define ADC1_CH1_PIN GPIO_PIN_1
|
||||||
|
#define ADC1_CH2_PORT GPIOA
|
||||||
|
#define ADC1_CH2_PIN GPIO_PIN_2
|
||||||
|
#define ADC1_CH3_PORT GPIOA
|
||||||
|
#define ADC1_CH3_PIN GPIO_PIN_3
|
||||||
|
#define ADC1_CLOCK_ENABLE() __HAL_RCC_ADC1_CLK_ENABLE()
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* PWM/Timer Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
#define PWM_TIMER_INSTANCE 0
|
||||||
|
#define PWM_TIMER TIM1
|
||||||
|
#define PWM_TIMER_CLOCK_ENABLE() __HAL_RCC_TIM1_CLK_ENABLE()
|
||||||
|
#define PWM_TIMER_CHANNEL_COUNT 4
|
||||||
|
#define PWM_TIMER_PRESCALER 168 /* 1 MHz */
|
||||||
|
#define PWM_TIMER_PERIOD 1000 /* 1 kHz */
|
||||||
|
#define PWM_TIMER_IRQ TIM1_UP_TIM10_IRQn
|
||||||
|
|
||||||
|
#define PWM_CH1_PORT GPIOA
|
||||||
|
#define PWM_CH1_PIN GPIO_PIN_8
|
||||||
|
#define PWM_CH1_AF GPIO_AF1_TIM1
|
||||||
|
#define PWM_CH2_PORT GPIOA
|
||||||
|
#define PWM_CH2_PIN GPIO_PIN_9
|
||||||
|
#define PWM_CH2_AF GPIO_AF1_TIM1
|
||||||
|
#define PWM_CH3_PORT GPIOA
|
||||||
|
#define PWM_CH3_PIN GPIO_PIN_10
|
||||||
|
#define PWM_CH3_AF GPIO_AF1_TIM1
|
||||||
|
#define PWM_CH4_PORT GPIOA
|
||||||
|
#define PWM_CH4_PIN GPIO_PIN_11
|
||||||
|
#define PWM_CH4_AF GPIO_AF1_TIM1
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Watchdog Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
#define WATCHDOG_INSTANCE 0
|
||||||
|
#define WATCHDOG_TIMEOUT_MS 100
|
||||||
|
#define WATCHDOG_WINDOW_MS 50
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_init(void);
|
||||||
|
void board_clock_init(void);
|
||||||
|
void board_gpio_init(void);
|
||||||
|
void board_uart_init(void);
|
||||||
|
void board_can_init(void);
|
||||||
|
void board_spi_init(void);
|
||||||
|
void board_i2c_init(void);
|
||||||
|
void board_adc_init(void);
|
||||||
|
void board_pwm_init(void);
|
||||||
|
void board_watchdog_init(void);
|
||||||
|
void board_watchdog_service(void);
|
||||||
|
void board_led_on(uint8_t led);
|
||||||
|
void board_led_off(uint8_t led);
|
||||||
|
void board_led_toggle(uint8_t led);
|
||||||
|
bool board_button_read(uint8_t button);
|
||||||
|
void board_delay_ms(uint32_t ms);
|
||||||
|
void board_delay_us(uint32_t us);
|
||||||
|
float board_get_temperature(void);
|
||||||
|
float board_get_voltage(void);
|
||||||
|
|
||||||
|
#endif /* BOARD_H */
|
||||||
@@ -0,0 +1,388 @@
|
|||||||
|
/**
|
||||||
|
* @file board_init.c
|
||||||
|
* @brief STM32F407 Discovery board initialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "board.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "uart_driver.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "i2c_driver.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Board state */
|
||||||
|
static bool board_initialized = false;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_init(void) {
|
||||||
|
if (board_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize clock */
|
||||||
|
board_clock_init();
|
||||||
|
|
||||||
|
/* Initialize GPIO */
|
||||||
|
board_gpio_init();
|
||||||
|
|
||||||
|
/* Initialize UART */
|
||||||
|
board_uart_init();
|
||||||
|
|
||||||
|
/* Initialize CAN */
|
||||||
|
board_can_init();
|
||||||
|
|
||||||
|
/* Initialize SPI */
|
||||||
|
board_spi_init();
|
||||||
|
|
||||||
|
/* Initialize I2C */
|
||||||
|
board_i2c_init();
|
||||||
|
|
||||||
|
/* Initialize ADC */
|
||||||
|
board_adc_init();
|
||||||
|
|
||||||
|
/* Initialize PWM */
|
||||||
|
board_pwm_init();
|
||||||
|
|
||||||
|
/* Initialize watchdog */
|
||||||
|
board_watchdog_init();
|
||||||
|
|
||||||
|
board_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Clock Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_clock_init(void) {
|
||||||
|
/* Reset RCC */
|
||||||
|
RCC->CR |= RCC_CR_HSION;
|
||||||
|
while (!(RCC->CR & RCC_CR_HSIRDY));
|
||||||
|
|
||||||
|
/* Configure PLL */
|
||||||
|
RCC->PLLCFGR = (PLL_M << RCC_PLLCFGR_PLLM_Pos) |
|
||||||
|
(PLL_N << RCC_PLLCFGR_PLLN_Pos) |
|
||||||
|
(((PLL_P >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos) |
|
||||||
|
(PLL_Q << RCC_PLLCFGR_PLLQ_Pos);
|
||||||
|
|
||||||
|
/* Enable PLL */
|
||||||
|
RCC->CR |= RCC_CR_PLLON;
|
||||||
|
while (!(RCC->CR & RCC_CR_PLLRDY));
|
||||||
|
|
||||||
|
/* Configure flash latency */
|
||||||
|
FLASH->ACR = FLASH_ACR_LATENCY_5WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
|
||||||
|
|
||||||
|
/* Configure AHB, APB1, APB2 prescalers */
|
||||||
|
RCC->CFGR = RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV4 | RCC_CFGR_PPRE2_DIV2;
|
||||||
|
|
||||||
|
/* Switch to PLL */
|
||||||
|
RCC->CFGR |= RCC_CFGR_SW_PLL;
|
||||||
|
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
|
||||||
|
|
||||||
|
/* Update SystemCoreClock variable */
|
||||||
|
SystemCoreClockUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* GPIO Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_gpio_init(void) {
|
||||||
|
/* Enable GPIO clocks */
|
||||||
|
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPIOE_CLK_ENABLE();
|
||||||
|
|
||||||
|
/* Configure LEDs */
|
||||||
|
GpioPinConfig_t led_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.output_type = GPIO_OUTPUT_PUSH_PULL,
|
||||||
|
.pull = GPIO_PULL_NONE,
|
||||||
|
.speed = GPIO_SPEED_HIGH
|
||||||
|
};
|
||||||
|
|
||||||
|
led_config.port = 3; /* GPIOD */
|
||||||
|
led_config.pin = 12;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
|
||||||
|
led_config.pin = 13;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
|
||||||
|
led_config.pin = 14;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
|
||||||
|
led_config.pin = 15;
|
||||||
|
gpio_init(&led_config);
|
||||||
|
|
||||||
|
/* Configure button */
|
||||||
|
GpioPinConfig_t button_config = {
|
||||||
|
.port = 0, /* GPIOA */
|
||||||
|
.pin = 0,
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.pull = GPIO_PULL_DOWN
|
||||||
|
};
|
||||||
|
gpio_init(&button_config);
|
||||||
|
|
||||||
|
/* Turn off all LEDs */
|
||||||
|
board_led_off(0);
|
||||||
|
board_led_off(1);
|
||||||
|
board_led_off(2);
|
||||||
|
board_led_off(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* UART Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_uart_init(void) {
|
||||||
|
/* Configure UART1 */
|
||||||
|
UartConfig_t uart1_config = {
|
||||||
|
.baudrate = UART1_BAUDRATE,
|
||||||
|
.data_bits = UART_DATA_BITS_8,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.parity = UART_PARITY_NONE,
|
||||||
|
.flow_control = UART_FLOW_CONTROL_NONE,
|
||||||
|
.enable_rx = true,
|
||||||
|
.enable_tx = true,
|
||||||
|
.use_dma = false
|
||||||
|
};
|
||||||
|
|
||||||
|
uart_init(UART1_INSTANCE, &uart1_config);
|
||||||
|
|
||||||
|
/* Configure UART2 */
|
||||||
|
UartConfig_t uart2_config = uart1_config;
|
||||||
|
uart_init(UART2_INSTANCE, &uart2_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_can_init(void) {
|
||||||
|
/* Configure CAN1 */
|
||||||
|
CanConfig_t can1_config = {
|
||||||
|
.nominal_baudrate = CAN1_BAUDRATE,
|
||||||
|
.data_baudrate = CAN1_BAUDRATE,
|
||||||
|
.frame_type = CAN_FRAME_CLASSIC,
|
||||||
|
.enable_fd = false,
|
||||||
|
.enable_automatic_retransmission = true,
|
||||||
|
.filter_count = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
can_init(&can1_config);
|
||||||
|
|
||||||
|
/* Configure CAN2 */
|
||||||
|
CanConfig_t can2_config = can1_config;
|
||||||
|
can_init(&can2_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SPI Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_spi_init(void) {
|
||||||
|
/* Configure SPI1 */
|
||||||
|
SpiConfig_t spi1_config = {
|
||||||
|
.mode = SPI_MODE_0,
|
||||||
|
.clock_speed = SPI_CLOCK_8MHZ,
|
||||||
|
.data_order = SPI_DATA_ORDER_MSB_FIRST,
|
||||||
|
.data_size = 8,
|
||||||
|
.use_dma = false,
|
||||||
|
.enable_hardware_cs = false,
|
||||||
|
.cs_polarity = SPI_CS_ACTIVE_LOW,
|
||||||
|
.cs_port = 0,
|
||||||
|
.cs_pin = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
spi_init(SPI1_INSTANCE, &spi1_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* I2C Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_i2c_init(void) {
|
||||||
|
/* Configure I2C1 */
|
||||||
|
I2cConfig_t i2c1_config = {
|
||||||
|
.speed = I2C_SPEED_FAST,
|
||||||
|
.addressing_mode = I2C_ADDRESSING_7BIT,
|
||||||
|
.own_address = 0x50,
|
||||||
|
.enable_general_call = false,
|
||||||
|
.enable_clock_stretching = true,
|
||||||
|
.use_dma = false
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_init(I2C1_INSTANCE, &i2c1_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ADC Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_adc_init(void) {
|
||||||
|
/* Configure ADC1 */
|
||||||
|
AdcConfig_t adc1_config = {
|
||||||
|
.resolution = ADC_RESOLUTION_12BIT,
|
||||||
|
.mode = ADC_MODE_SCAN,
|
||||||
|
.trigger_source = ADC_TRIGGER_TIMER,
|
||||||
|
.reference = ADC_REFERENCE_VDD,
|
||||||
|
.enable_dma = false,
|
||||||
|
.conversion_frequency = 1000,
|
||||||
|
.channel_count = 6,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 1, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 2, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 3, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 4, .sampling_time = ADC_SAMPLING_28_5_CYCLES},
|
||||||
|
{.channel = 5, .sampling_time = ADC_SAMPLING_28_5_CYCLES}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
adc_init(ADC1_INSTANCE, &adc1_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* PWM Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_pwm_init(void) {
|
||||||
|
/* Configure PWM timer */
|
||||||
|
PwmConfig_t pwm_config = {
|
||||||
|
.frequency_hz = 1000,
|
||||||
|
.alignment = PWM_ALIGNMENT_EDGE,
|
||||||
|
.period_ticks = 1000,
|
||||||
|
.prescaler = 168,
|
||||||
|
.channel_count = 4,
|
||||||
|
.channels = {
|
||||||
|
{.channel = 0, .duty_cycle = 0, .polarity = PWM_POLARITY_ACTIVE_HIGH},
|
||||||
|
{.channel = 1, .duty_cycle = 0, .polarity = PWM_POLARITY_ACTIVE_HIGH},
|
||||||
|
{.channel = 2, .duty_cycle = 0, .polarity = PWM_POLARITY_ACTIVE_HIGH},
|
||||||
|
{.channel = 3, .duty_cycle = 0, .polarity = PWM_POLARITY_ACTIVE_HIGH}
|
||||||
|
},
|
||||||
|
.enable_fault_protection = true,
|
||||||
|
.fault_action = PWM_FAULT_DISABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
pwm_init(PWM_TIMER_INSTANCE, &pwm_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Watchdog Initialization
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_watchdog_init(void) {
|
||||||
|
/* Enable IWDG */
|
||||||
|
IWDG->KR = 0x5555; /* Enable write access */
|
||||||
|
IWDG->PR = 0x06; /* Prescaler: 256 */
|
||||||
|
IWDG->RLR = 0x0FFF; /* Reload value */
|
||||||
|
IWDG->KR = 0xCCCC; /* Start watchdog */
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_watchdog_service(void) {
|
||||||
|
/* Service IWDG */
|
||||||
|
IWDG->KR = 0xAAAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* LED Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_led_on(uint8_t led) {
|
||||||
|
switch (led) {
|
||||||
|
case 0:
|
||||||
|
gpio_write(3, 12, true);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gpio_write(3, 13, true);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gpio_write(3, 14, true);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
gpio_write(3, 15, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_led_off(uint8_t led) {
|
||||||
|
switch (led) {
|
||||||
|
case 0:
|
||||||
|
gpio_write(3, 12, false);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gpio_write(3, 13, false);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gpio_write(3, 14, false);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
gpio_write(3, 15, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_led_toggle(uint8_t led) {
|
||||||
|
switch (led) {
|
||||||
|
case 0:
|
||||||
|
gpio_toggle(3, 12);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gpio_toggle(3, 13);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gpio_toggle(3, 14);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
gpio_toggle(3, 15);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Button Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
bool board_button_read(uint8_t button) {
|
||||||
|
if (button == 0) {
|
||||||
|
return gpio_read(0, 0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Delay Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
void board_delay_ms(uint32_t ms) {
|
||||||
|
kernel_delay(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_delay_us(uint32_t us) {
|
||||||
|
/* Simple busy-wait delay */
|
||||||
|
uint32_t count = us * (SystemCoreClock / 1000000) / 5;
|
||||||
|
while (count--) {
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Sensor Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
float board_get_temperature(void) {
|
||||||
|
/* Read internal temperature sensor */
|
||||||
|
uint16_t adc_value = 0;
|
||||||
|
adc_read_channel(ADC1_INSTANCE, 16, &adc_value, 10);
|
||||||
|
|
||||||
|
/* Convert to temperature */
|
||||||
|
float voltage = adc_convert_to_voltage(adc_value, ADC_RESOLUTION_12BIT, 3.3f);
|
||||||
|
float temperature = ((voltage - 0.76f) / 0.0025f) + 25.0f;
|
||||||
|
|
||||||
|
return temperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
float board_get_voltage(void) {
|
||||||
|
/* Read VREFINT */
|
||||||
|
uint16_t adc_value = 0;
|
||||||
|
adc_read_channel(ADC1_INSTANCE, 17, &adc_value, 10);
|
||||||
|
|
||||||
|
/* Convert to voltage */
|
||||||
|
float voltage = adc_convert_to_voltage(adc_value, ADC_RESOLUTION_12BIT, 3.3f);
|
||||||
|
|
||||||
|
return voltage;
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
* @file linker_script.ld
|
||||||
|
* @brief Linker script for STM32F407VG
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Entry Point */
|
||||||
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
|
/* Memory Map */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
|
||||||
|
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stack Configuration */
|
||||||
|
_estack = ORIGIN(RAM) + LENGTH(RAM); /* End of RAM */
|
||||||
|
_min_stack_size = 0x400; /* 1KB minimum stack */
|
||||||
|
_min_heap_size = 0x200; /* 512B minimum heap */
|
||||||
|
|
||||||
|
/* Sections */
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* Interrupt Vector Table */
|
||||||
|
.isr_vector :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(.isr_vector))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Program Code */
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text)
|
||||||
|
*(.text*)
|
||||||
|
*(.glue_7)
|
||||||
|
*(.glue_7t)
|
||||||
|
*(.eh_frame)
|
||||||
|
|
||||||
|
KEEP(*(.init))
|
||||||
|
KEEP(*(.fini))
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_etext = .;
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Constant Data */
|
||||||
|
.rodata :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* ARM Extensions */
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.ARM :
|
||||||
|
{
|
||||||
|
__exidx_start = .;
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
__exidx_end = .;
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Pre-initialization Array */
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__preinit_array_start = .);
|
||||||
|
KEEP(*(.preinit_array*))
|
||||||
|
PROVIDE_HIDDEN(__preinit_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Initialization Array */
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__init_array_start = .);
|
||||||
|
KEEP(*(SORT(.init_array.*)))
|
||||||
|
KEEP(*(.init_array*))
|
||||||
|
PROVIDE_HIDDEN(__init_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Finalization Array */
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN(__fini_array_start = .);
|
||||||
|
KEEP(*(SORT(.fini_array.*)))
|
||||||
|
KEEP(*(.fini_array*))
|
||||||
|
PROVIDE_HIDDEN(__fini_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Used by startup to initialize data */
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
/* Initialized Data */
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data)
|
||||||
|
*(.data*)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_edata = .;
|
||||||
|
} >RAM AT> FLASH
|
||||||
|
|
||||||
|
/* Uninitialized Data */
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sbss = .;
|
||||||
|
__bss_start__ = _sbss;
|
||||||
|
*(.bss)
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
/* CCMRAM Section */
|
||||||
|
.ccmram :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sccmram = .;
|
||||||
|
*(.ccmram)
|
||||||
|
*(.ccmram*)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_eccmram = .;
|
||||||
|
} >CCMRAM
|
||||||
|
|
||||||
|
/* Heap Section */
|
||||||
|
._user_heap_stack :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE(end = .);
|
||||||
|
PROVIDE(_end = .);
|
||||||
|
. = . + _min_heap_size;
|
||||||
|
. = . + _min_stack_size;
|
||||||
|
. = ALIGN(8);
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
/* Remove information from standard libraries */
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
libc.a(*)
|
||||||
|
libm.a(*)
|
||||||
|
libgcc.a(*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||||
|
}
|
||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Project root
|
||||||
|
PROJECT_ROOT="$(pwd)"
|
||||||
|
|
||||||
|
# Toolchain paths (adjust based on your system)
|
||||||
|
TOOLCHAIN_BIN="/opt/homebrew/bin"
|
||||||
|
NEWLIB_INCLUDE="/opt/homebrew/opt/arm-none-eabi-newlib/arm-none-eabi/include"
|
||||||
|
GCC_INCLUDE="/opt/homebrew/Cellar/arm-none-eabi-gcc/16.2.0/lib/gcc/arm-none-eabi/16.2.0/include"
|
||||||
|
|
||||||
|
# Project include paths
|
||||||
|
INCLUDE_PATHS=(
|
||||||
|
"-I${PROJECT_ROOT}/kernel/include"
|
||||||
|
"-I${PROJECT_ROOT}/drivers/include"
|
||||||
|
"-I${PROJECT_ROOT}/middleware"
|
||||||
|
"-I${PROJECT_ROOT}/middleware/can_stack/include"
|
||||||
|
"-I${PROJECT_ROOT}/middleware/diagnostics/include"
|
||||||
|
"-I${PROJECT_ROOT}/config"
|
||||||
|
"-I${NEWLIB_INCLUDE}"
|
||||||
|
"-I${GCC_INCLUDE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test compilation
|
||||||
|
echo "Testing compilation..."
|
||||||
|
arm-none-eabi-gcc \
|
||||||
|
-mcpu=cortex-m4 \
|
||||||
|
-mthumb \
|
||||||
|
-O0 \
|
||||||
|
-g \
|
||||||
|
-Wall \
|
||||||
|
-Wextra \
|
||||||
|
"${INCLUDE_PATHS[@]}" \
|
||||||
|
-c kernel/src/kernel_init.c \
|
||||||
|
-o test.o
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✓ Compilation successful!"
|
||||||
|
rm -f test.o
|
||||||
|
else
|
||||||
|
echo "✗ Compilation failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
/**
|
||||||
|
* @file board_config.h
|
||||||
|
* @brief Board-specific configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOARD_CONFIG_H
|
||||||
|
#define BOARD_CONFIG_H
|
||||||
|
|
||||||
|
#include "mcu_config.h"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Selection
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#define BOARD_STM32F407_DISCOVERY 1
|
||||||
|
#define BOARD_NXP_S32K144_EVB 0
|
||||||
|
#define BOARD_INFINEON_TC397_EVB 0
|
||||||
|
|
||||||
|
#if (BOARD_STM32F407_DISCOVERY + BOARD_NXP_S32K144_EVB + BOARD_INFINEON_TC397_EVB) != 1
|
||||||
|
#error "Exactly one board must be selected"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Pin Mapping
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#if BOARD_STM32F407_DISCOVERY
|
||||||
|
/* STM32F407 Discovery board pin mapping */
|
||||||
|
|
||||||
|
/* LED pins */
|
||||||
|
#define LED1_PORT 0 /* GPIOA */
|
||||||
|
#define LED1_PIN 0
|
||||||
|
#define LED2_PORT 0
|
||||||
|
#define LED2_PIN 1
|
||||||
|
#define LED3_PORT 0
|
||||||
|
#define LED3_PIN 2
|
||||||
|
#define LED4_PORT 0
|
||||||
|
#define LED4_PIN 3
|
||||||
|
|
||||||
|
/* Button pins */
|
||||||
|
#define BUTTON1_PORT 0 /* GPIOA */
|
||||||
|
#define BUTTON1_PIN 0
|
||||||
|
|
||||||
|
/* UART pins */
|
||||||
|
#define UART1_TX_PORT 0 /* GPIOA */
|
||||||
|
#define UART1_TX_PIN 9
|
||||||
|
#define UART1_RX_PORT 0
|
||||||
|
#define UART1_RX_PIN 10
|
||||||
|
|
||||||
|
/* CAN pins */
|
||||||
|
#define CAN1_TX_PORT 1 /* GPIOB */
|
||||||
|
#define CAN1_TX_PIN 9
|
||||||
|
#define CAN1_RX_PORT 1
|
||||||
|
#define CAN1_RX_PIN 8
|
||||||
|
|
||||||
|
/* SPI pins */
|
||||||
|
#define SPI1_SCK_PORT 0 /* GPIOA */
|
||||||
|
#define SPI1_SCK_PIN 5
|
||||||
|
#define SPI1_MISO_PORT 0
|
||||||
|
#define SPI1_MISO_PIN 6
|
||||||
|
#define SPI1_MOSI_PORT 0
|
||||||
|
#define SPI1_MOSI_PIN 7
|
||||||
|
|
||||||
|
/* I2C pins */
|
||||||
|
#define I2C1_SCL_PORT 1 /* GPIOB */
|
||||||
|
#define I2C1_SCL_PIN 6
|
||||||
|
#define I2C1_SDA_PORT 1
|
||||||
|
#define I2C1_SDA_PIN 7
|
||||||
|
|
||||||
|
/* ADC pins */
|
||||||
|
#define ADC1_CH0_PORT 0 /* GPIOA */
|
||||||
|
#define ADC1_CH0_PIN 0
|
||||||
|
#define ADC1_CH1_PORT 0
|
||||||
|
#define ADC1_CH1_PIN 1
|
||||||
|
|
||||||
|
/* PWM pins */
|
||||||
|
#define PWM1_CH1_PORT 0 /* GPIOA */
|
||||||
|
#define PWM1_CH1_PIN 8
|
||||||
|
#define PWM1_CH2_PORT 0
|
||||||
|
#define PWM1_CH2_PIN 9
|
||||||
|
|
||||||
|
#elif BOARD_NXP_S32K144_EVB
|
||||||
|
/* NXP S32K144 EVB pin mapping */
|
||||||
|
|
||||||
|
/* LED pins */
|
||||||
|
#define LED_RED_PORT 2 /* GPIOC */
|
||||||
|
#define LED_RED_PIN 0
|
||||||
|
#define LED_GREEN_PORT 2
|
||||||
|
#define LED_GREEN_PIN 1
|
||||||
|
#define LED_BLUE_PORT 2
|
||||||
|
#define LED_BLUE_PIN 2
|
||||||
|
|
||||||
|
/* Button pins */
|
||||||
|
#define BUTTON1_PORT 2 /* GPIOC */
|
||||||
|
#define BUTTON1_PIN 3
|
||||||
|
#define BUTTON2_PORT 2
|
||||||
|
#define BUTTON2_PIN 4
|
||||||
|
|
||||||
|
/* UART pins */
|
||||||
|
#define UART0_TX_PORT 0 /* GPIOA */
|
||||||
|
#define UART0_TX_PIN 2
|
||||||
|
#define UART0_RX_PORT 0
|
||||||
|
#define UART0_RX_PIN 3
|
||||||
|
|
||||||
|
/* CAN pins */
|
||||||
|
#define CAN0_TX_PORT 4 /* GPIOE */
|
||||||
|
#define CAN0_TX_PIN 5
|
||||||
|
#define CAN0_RX_PORT 4
|
||||||
|
#define CAN0_RX_PIN 4
|
||||||
|
|
||||||
|
/* SPI pins */
|
||||||
|
#define SPI0_SCK_PORT 0 /* GPIOA */
|
||||||
|
#define SPI0_SCK_PIN 6
|
||||||
|
#define SPI0_MISO_PORT 0
|
||||||
|
#define SPI0_MISO_PIN 7
|
||||||
|
#define SPI0_MOSI_PORT 0
|
||||||
|
#define SPI0_MOSI_PIN 8
|
||||||
|
|
||||||
|
#elif BOARD_INFINEON_TC397_EVB
|
||||||
|
/* Infineon TC397 EVB pin mapping */
|
||||||
|
|
||||||
|
/* LED pins */
|
||||||
|
#define LED1_PORT 0 /* P00 */
|
||||||
|
#define LED1_PIN 0
|
||||||
|
#define LED2_PORT 0
|
||||||
|
#define LED2_PIN 1
|
||||||
|
|
||||||
|
/* Button pins */
|
||||||
|
#define BUTTON1_PORT 0 /* P00 */
|
||||||
|
#define BUTTON1_PIN 2
|
||||||
|
|
||||||
|
/* UART pins */
|
||||||
|
#define UART0_TX_PORT 0 /* P00 */
|
||||||
|
#define UART0_TX_PIN 3
|
||||||
|
#define UART0_RX_PORT 0
|
||||||
|
#define UART0_RX_PIN 4
|
||||||
|
|
||||||
|
/* CAN pins */
|
||||||
|
#define CAN0_TX_PORT 0 /* P00 */
|
||||||
|
#define CAN0_TX_PIN 5
|
||||||
|
#define CAN0_RX_PORT 0
|
||||||
|
#define CAN0_RX_PIN 6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board Features
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Debug interface */
|
||||||
|
#define DEBUG_INTERFACE_SWD 1
|
||||||
|
#define DEBUG_INTERFACE_JTAG 0
|
||||||
|
|
||||||
|
/* External crystal frequency */
|
||||||
|
#define EXTERNAL_CRYSTAL_HZ 8000000U
|
||||||
|
|
||||||
|
/* Board voltage */
|
||||||
|
#define BOARD_VOLTAGE 3.3f /* Volts */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* External Components
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* External memory */
|
||||||
|
#define EXT_FLASH_ENABLED 0
|
||||||
|
#define EXT_RAM_ENABLED 0
|
||||||
|
|
||||||
|
/* External sensors */
|
||||||
|
#define TEMP_SENSOR_ENABLED 1
|
||||||
|
#define ACCELEROMETER_ENABLED 1
|
||||||
|
#define GYROSCOPE_ENABLED 0
|
||||||
|
|
||||||
|
/* Display */
|
||||||
|
#define LCD_DISPLAY_ENABLED 1
|
||||||
|
#define LCD_WIDTH 240
|
||||||
|
#define LCD_HEIGHT 320
|
||||||
|
#define LCD_COLOR_DEPTH 16
|
||||||
|
|
||||||
|
/* CAN transceiver */
|
||||||
|
#define CAN_TRANSCEIVER_ENABLED 1
|
||||||
|
#define CAN_TRANSCEIVER_TYPE 0 /* 0=TJA1050, 1=MCP2551 */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Board-Specific Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Board initialization function */
|
||||||
|
void board_init(void);
|
||||||
|
|
||||||
|
/* Board-specific GPIO initialization */
|
||||||
|
void board_gpio_init(void);
|
||||||
|
|
||||||
|
/* Board-specific clock initialization */
|
||||||
|
void board_clock_init(void);
|
||||||
|
|
||||||
|
/* Board-specific peripheral initialization */
|
||||||
|
void board_peripheral_init(void);
|
||||||
|
|
||||||
|
/* Board-specific CAN initialization */
|
||||||
|
void board_can_init(void);
|
||||||
|
|
||||||
|
/* Board-specific UART initialization */
|
||||||
|
void board_uart_init(void);
|
||||||
|
|
||||||
|
/* Board-specific SPI initialization */
|
||||||
|
void board_spi_init(void);
|
||||||
|
|
||||||
|
/* Board-specific I2C initialization */
|
||||||
|
void board_i2c_init(void);
|
||||||
|
|
||||||
|
/* Board-specific ADC initialization */
|
||||||
|
void board_adc_init(void);
|
||||||
|
|
||||||
|
/* Board-specific PWM initialization */
|
||||||
|
void board_pwm_init(void);
|
||||||
|
|
||||||
|
/* Board LED control */
|
||||||
|
void board_led_on(uint8_t led);
|
||||||
|
void board_led_off(uint8_t led);
|
||||||
|
void board_led_toggle(uint8_t led);
|
||||||
|
|
||||||
|
/* Board button reading */
|
||||||
|
bool board_button_read(uint8_t button);
|
||||||
|
|
||||||
|
/* Board-specific delay */
|
||||||
|
void board_delay_ms(uint32_t ms);
|
||||||
|
void board_delay_us(uint32_t us);
|
||||||
|
|
||||||
|
/* Board-specific watchdog */
|
||||||
|
void board_watchdog_init(void);
|
||||||
|
void board_watchdog_service(void);
|
||||||
|
|
||||||
|
/* Board temperature reading */
|
||||||
|
float board_get_temperature(void);
|
||||||
|
|
||||||
|
/* Board voltage reading */
|
||||||
|
float board_get_voltage(void);
|
||||||
|
|
||||||
|
#endif /* BOARD_CONFIG_H */
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* @file can_config.h
|
||||||
|
* @brief CAN bus configuration for automotive applications
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAN_CONFIG_H
|
||||||
|
#define CAN_CONFIG_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Hardware Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Number of CAN controllers */
|
||||||
|
#define CAN_CONTROLLER_COUNT 2
|
||||||
|
|
||||||
|
/* CAN controller base addresses */
|
||||||
|
#define CAN0_BASE_ADDRESS 0x40006400U
|
||||||
|
#define CAN1_BASE_ADDRESS 0x40006800U
|
||||||
|
|
||||||
|
/* CAN controller interrupts */
|
||||||
|
#define CAN0_IRQ_NUMBER 19
|
||||||
|
#define CAN1_IRQ_NUMBER 20
|
||||||
|
|
||||||
|
/* CAN controller priorities */
|
||||||
|
#define CAN0_IRQ_PRIORITY 5
|
||||||
|
#define CAN1_IRQ_PRIORITY 5
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Baudrate Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* CAN baudrates */
|
||||||
|
#define CAN_BAUDRATE_125K 125000
|
||||||
|
#define CAN_BAUDRATE_250K 250000
|
||||||
|
#define CAN_BAUDRATE_500K 500000
|
||||||
|
#define CAN_BAUDRATE_1M 1000000
|
||||||
|
|
||||||
|
/* CAN FD baudrates */
|
||||||
|
#define CAN_FD_BAUDRATE_2M 2000000
|
||||||
|
#define CAN_FD_BAUDRATE_5M 5000000
|
||||||
|
#define CAN_FD_BAUDRATE_8M 8000000
|
||||||
|
|
||||||
|
/* Default CAN baudrate */
|
||||||
|
#define CAN_DEFAULT_BAUDRATE CAN_BAUDRATE_500K
|
||||||
|
|
||||||
|
/* CAN FD data baudrate */
|
||||||
|
#define CAN_FD_DATA_BAUDRATE CAN_FD_BAUDRATE_2M
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Frame Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable CAN FD */
|
||||||
|
#define CAN_FD_ENABLED 1
|
||||||
|
|
||||||
|
/* Maximum CAN frame length */
|
||||||
|
#define CAN_MAX_FRAME_LENGTH 64
|
||||||
|
|
||||||
|
/* Enable automatic retransmission */
|
||||||
|
#define CAN_AUTO_RETRANSMIT 1
|
||||||
|
|
||||||
|
/* Maximum retransmission attempts */
|
||||||
|
#define CAN_MAX_RETRANSMIT 3
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Message IDs
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* OBD-II IDs */
|
||||||
|
#define CAN_ID_OBD_REQUEST 0x7DF
|
||||||
|
#define CAN_ID_OBD_RESPONSE 0x7E8
|
||||||
|
#define CAN_ID_OBD_RESPONSE_2 0x7E9
|
||||||
|
|
||||||
|
/* Diagnostic IDs */
|
||||||
|
#define CAN_ID_DIAG_REQUEST 0x700
|
||||||
|
#define CAN_ID_DIAG_RESPONSE 0x708
|
||||||
|
|
||||||
|
/* Network management IDs */
|
||||||
|
#define CAN_ID_NM_BASE 0x400
|
||||||
|
#define CAN_ID_NM_END 0x4FF
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Application Message IDs
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Engine control messages */
|
||||||
|
#define CAN_ID_ENGINE_DATA 0x100
|
||||||
|
#define CAN_ID_ENGINE_CONTROL 0x101
|
||||||
|
#define CAN_ID_ENGINE_STATUS 0x102
|
||||||
|
#define CAN_ID_ENGINE_FAULT 0x103
|
||||||
|
|
||||||
|
/* Transmission messages */
|
||||||
|
#define CAN_ID_TRANSMISSION_DATA 0x110
|
||||||
|
#define CAN_ID_TRANSMISSION_CONTROL 0x111
|
||||||
|
#define CAN_ID_GEAR_POSITION 0x112
|
||||||
|
|
||||||
|
/* Brake control messages */
|
||||||
|
#define CAN_ID_BRAKE_DATA 0x120
|
||||||
|
#define CAN_ID_BRAKE_CONTROL 0x121
|
||||||
|
#define CAN_ID_ABS_DATA 0x122
|
||||||
|
#define CAN_ID_WHEEL_SPEED 0x123
|
||||||
|
|
||||||
|
/* Steering messages */
|
||||||
|
#define CAN_ID_STEERING_ANGLE 0x130
|
||||||
|
#define CAN_ID_STEERING_TORQUE 0x131
|
||||||
|
#define CAN_ID_STEERING_CONTROL 0x132
|
||||||
|
|
||||||
|
/* Body control messages */
|
||||||
|
#define CAN_ID_BODY_CONTROL 0x140
|
||||||
|
#define CAN_ID_DOOR_STATUS 0x141
|
||||||
|
#define CAN_ID_LIGHT_STATUS 0x142
|
||||||
|
#define CAN_ID_WINDOW_CONTROL 0x143
|
||||||
|
|
||||||
|
/* Dashboard messages */
|
||||||
|
#define CAN_ID_SPEED 0x150
|
||||||
|
#define CAN_ID_RPM 0x151
|
||||||
|
#define CAN_ID_FUEL_LEVEL 0x152
|
||||||
|
#define CAN_ID_TEMPERATURE 0x153
|
||||||
|
|
||||||
|
/* Safety messages */
|
||||||
|
#define CAN_ID_AIRBAG_STATUS 0x160
|
||||||
|
#define CAN_ID_SEATBELT_STATUS 0x161
|
||||||
|
#define CAN_ID_CRASH_DETECTION 0x162
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Filter Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t filter_id;
|
||||||
|
uint32_t filter_mask;
|
||||||
|
bool is_extended;
|
||||||
|
uint8_t controller;
|
||||||
|
uint8_t fifo;
|
||||||
|
} CanFilterConfig_t;
|
||||||
|
|
||||||
|
/* Default CAN filters */
|
||||||
|
static const CanFilterConfig_t can_filters[] = {
|
||||||
|
/* Engine messages */
|
||||||
|
{.filter_id = 0x100, .filter_mask = 0x1F0, .is_extended = false, .controller = 0, .fifo = 0},
|
||||||
|
/* Transmission messages */
|
||||||
|
{.filter_id = 0x110, .filter_mask = 0x1F0, .is_extended = false, .controller = 0, .fifo = 0},
|
||||||
|
/* Brake messages */
|
||||||
|
{.filter_id = 0x120, .filter_mask = 0x1F0, .is_extended = false, .controller = 0, .fifo = 0},
|
||||||
|
/* Steering messages */
|
||||||
|
{.filter_id = 0x130, .filter_mask = 0x1F0, .is_extended = false, .controller = 0, .fifo = 0},
|
||||||
|
/* Body control messages */
|
||||||
|
{.filter_id = 0x140, .filter_mask = 0x1F0, .is_extended = false, .controller = 0, .fifo = 1},
|
||||||
|
/* Dashboard messages */
|
||||||
|
{.filter_id = 0x150, .filter_mask = 0x1F0, .is_extended = false, .controller = 0, .fifo = 1},
|
||||||
|
/* Safety messages */
|
||||||
|
{.filter_id = 0x160, .filter_mask = 0x1F0, .is_extended = false, .controller = 0, .fifo = 0},
|
||||||
|
/* OBD-II messages */
|
||||||
|
{.filter_id = 0x7DF, .filter_mask = 0x7FF, .is_extended = false, .controller = 1, .fifo = 0},
|
||||||
|
/* Diagnostic messages */
|
||||||
|
{.filter_id = 0x700, .filter_mask = 0x7F0, .is_extended = false, .controller = 1, .fifo = 1},
|
||||||
|
/* Network management */
|
||||||
|
{.filter_id = 0x400, .filter_mask = 0x700, .is_extended = false, .controller = 0, .fifo = 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CAN_FILTER_COUNT (sizeof(can_filters) / sizeof(CanFilterConfig_t))
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN TP Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* CAN TP timeouts */
|
||||||
|
#define CAN_TP_N_AS_TIMEOUT 1000 /* ms */
|
||||||
|
#define CAN_TP_N_BS_TIMEOUT 1000 /* ms */
|
||||||
|
#define CAN_TP_N_CR_TIMEOUT 1000 /* ms */
|
||||||
|
|
||||||
|
/* CAN TP parameters */
|
||||||
|
#define CAN_TP_STMIN_DEFAULT 10 /* ms */
|
||||||
|
#define CAN_TP_BS_DEFAULT 8 /* frames */
|
||||||
|
#define CAN_TP_MAX_PAYLOAD 4096 /* bytes */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN NM Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* CAN NM parameters */
|
||||||
|
#define CAN_NM_TIMEOUT 2000 /* ms */
|
||||||
|
#define CAN_NM_REPEAT_TIME 500 /* ms */
|
||||||
|
#define CAN_NM_WAIT_BUS_SLEEP 2000 /* ms */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* CAN Statistics
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable CAN statistics collection */
|
||||||
|
#define CAN_STATS_ENABLED 1
|
||||||
|
|
||||||
|
/* Statistics buffer size */
|
||||||
|
#define CAN_STATS_BUFFER_SIZE 100
|
||||||
|
|
||||||
|
#endif /* CAN_CONFIG_H */
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
/**
|
||||||
|
* @file kernel_config.h
|
||||||
|
* @brief Kernel configuration for Automotive RTOS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KERNEL_CONFIG_H
|
||||||
|
#define KERNEL_CONFIG_H
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Kernel Version Information
|
||||||
|
* ============================================================================ */
|
||||||
|
#define KERNEL_VERSION_MAJOR 1
|
||||||
|
#define KERNEL_VERSION_MINOR 0
|
||||||
|
#define KERNEL_VERSION_PATCH 0
|
||||||
|
#define KERNEL_VERSION_STRING "1.0.0"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Basic Kernel Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Maximum number of tasks */
|
||||||
|
#define MAX_TASKS 32
|
||||||
|
|
||||||
|
/* Maximum priority levels (0 = highest, 15 = lowest) */
|
||||||
|
#define MAX_PRIORITY_LEVELS 16
|
||||||
|
|
||||||
|
/* Idle task priority (lowest) */
|
||||||
|
#define IDLE_TASK_PRIORITY (MAX_PRIORITY_LEVELS - 1)
|
||||||
|
|
||||||
|
/* Maximum task name length */
|
||||||
|
#define MAX_TASK_NAME_LENGTH 16
|
||||||
|
|
||||||
|
/* System tick rate in Hz */
|
||||||
|
#define TICK_RATE_HZ 1000
|
||||||
|
|
||||||
|
/* Tick period in milliseconds */
|
||||||
|
#define TICK_PERIOD_MS (1000 / TICK_RATE_HZ)
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Scheduler Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Scheduler type */
|
||||||
|
#define SCHEDULER_TYPE SCHEDULER_PRIORITY_PREEMPTIVE
|
||||||
|
|
||||||
|
/* Enable round-robin scheduling for same priority tasks */
|
||||||
|
#define ENABLE_ROUND_ROBIN 1
|
||||||
|
|
||||||
|
/* Round-robin time slice in ticks */
|
||||||
|
#define ROUND_ROBIN_TIME_SLICE 10
|
||||||
|
|
||||||
|
/* Enable deadline monitoring */
|
||||||
|
#define ENABLE_DEADLINE_MONITORING 1
|
||||||
|
|
||||||
|
/* Maximum allowed deadline misses before fault */
|
||||||
|
#define MAX_DEADLINE_MISSES 10
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Synchronization Primitives
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Maximum number of semaphores */
|
||||||
|
#define MAX_SEMAPHORES 32
|
||||||
|
|
||||||
|
/* Maximum number of mutexes */
|
||||||
|
#define MAX_MUTEXES 16
|
||||||
|
|
||||||
|
/* Maximum number of message queues */
|
||||||
|
#define MAX_QUEUES 16
|
||||||
|
|
||||||
|
/* Maximum queue size */
|
||||||
|
#define MAX_QUEUE_SIZE 256
|
||||||
|
|
||||||
|
/* Maximum number of event groups */
|
||||||
|
#define MAX_EVENT_GROUPS 8
|
||||||
|
|
||||||
|
/* Maximum number of software timers */
|
||||||
|
#define MAX_TIMERS 16
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Memory Management
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable memory protection */
|
||||||
|
#define ENABLE_MEMORY_PROTECTION 1
|
||||||
|
|
||||||
|
/* Enable stack overflow detection */
|
||||||
|
#define ENABLE_STACK_CHECK 1
|
||||||
|
|
||||||
|
/* Stack fill pattern for overflow detection */
|
||||||
|
#define STACK_FILL_PATTERN 0xA5A5A5A5
|
||||||
|
|
||||||
|
/* Default task stack size */
|
||||||
|
#define DEFAULT_TASK_STACK_SIZE 1024
|
||||||
|
|
||||||
|
/* Minimum task stack size */
|
||||||
|
#define MIN_TASK_STACK_SIZE 256
|
||||||
|
|
||||||
|
/* Idle task stack size */
|
||||||
|
#define IDLE_TASK_STACK_SIZE 512
|
||||||
|
|
||||||
|
/* ISR stack size */
|
||||||
|
#define ISR_STACK_SIZE 2048
|
||||||
|
|
||||||
|
/* Heap size for dynamic allocation */
|
||||||
|
#define HEAP_SIZE 65536
|
||||||
|
|
||||||
|
/* Enable heap usage tracking */
|
||||||
|
#define ENABLE_HEAP_TRACKING 1
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Interrupt Management
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Maximum number of ISR handlers */
|
||||||
|
#define MAX_ISR_HANDLERS 128
|
||||||
|
|
||||||
|
/* Maximum interrupt nesting level */
|
||||||
|
#define MAX_INTERRUPT_NESTING 8
|
||||||
|
|
||||||
|
/* Kernel interrupt priority (highest) */
|
||||||
|
#define KERNEL_INTERRUPT_PRIORITY 0
|
||||||
|
|
||||||
|
/* System call interrupt priority */
|
||||||
|
#define SYSCALL_INTERRUPT_PRIORITY 1
|
||||||
|
|
||||||
|
/* PendSV interrupt priority (lowest) */
|
||||||
|
#define PENDSV_INTERRUPT_PRIORITY (MAX_PRIORITY_LEVELS - 1)
|
||||||
|
|
||||||
|
/* SysTick interrupt priority */
|
||||||
|
#define SYSTICK_INTERRUPT_PRIORITY (MAX_PRIORITY_LEVELS - 1)
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Fault Handling
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable fault handlers */
|
||||||
|
#define ENABLE_FAULT_HANDLERS 1
|
||||||
|
|
||||||
|
/* Enable hard fault handler */
|
||||||
|
#define ENABLE_HARD_FAULT_HANDLER 1
|
||||||
|
|
||||||
|
/* Enable bus fault handler */
|
||||||
|
#define ENABLE_BUS_FAULT_HANDLER 1
|
||||||
|
|
||||||
|
/* Enable usage fault handler */
|
||||||
|
#define ENABLE_USAGE_FAULT_HANDLER 1
|
||||||
|
|
||||||
|
/* Enable memory management fault handler */
|
||||||
|
#define ENABLE_MEM_FAULT_HANDLER 1
|
||||||
|
|
||||||
|
/* Maximum number of fault records */
|
||||||
|
#define MAX_FAULT_RECORDS 16
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Debug and Trace
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable debug output */
|
||||||
|
#define ENABLE_DEBUG_OUTPUT 1
|
||||||
|
|
||||||
|
/* Debug level (0=off, 1=error, 2=warning, 3=info, 4=debug) */
|
||||||
|
#define DEBUG_LEVEL 3
|
||||||
|
|
||||||
|
/* Enable trace functionality */
|
||||||
|
#define ENABLE_TRACE 0
|
||||||
|
|
||||||
|
/* Trace buffer size */
|
||||||
|
#define TRACE_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
/* Enable assertions */
|
||||||
|
#define ENABLE_ASSERT 1
|
||||||
|
|
||||||
|
/* Enable runtime statistics */
|
||||||
|
#define ENABLE_RUNTIME_STATS 1
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Power Management
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable power management */
|
||||||
|
#define ENABLE_POWER_MANAGEMENT 1
|
||||||
|
|
||||||
|
/* Enable sleep modes */
|
||||||
|
#define ENABLE_SLEEP_MODE 1
|
||||||
|
|
||||||
|
/* Enable deep sleep modes */
|
||||||
|
#define ENABLE_DEEP_SLEEP 0
|
||||||
|
|
||||||
|
/* Idle task uses WFI */
|
||||||
|
#define IDLE_TASK_USES_WFI 1
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Safety Features
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable watchdog integration */
|
||||||
|
#define ENABLE_WATCHDOG 1
|
||||||
|
|
||||||
|
/* Watchdog timeout in milliseconds */
|
||||||
|
#define WATCHDOG_TIMEOUT_MS 100
|
||||||
|
|
||||||
|
/* Enable task supervision */
|
||||||
|
#define ENABLE_TASK_SUPERVISION 1
|
||||||
|
|
||||||
|
/* Enable E2E protection */
|
||||||
|
#define ENABLE_E2E_PROTECTION 1
|
||||||
|
|
||||||
|
/* Enable memory ECC */
|
||||||
|
#define ENABLE_MEMORY_ECC 1
|
||||||
|
|
||||||
|
/* Enable clock monitoring */
|
||||||
|
#define ENABLE_CLOCK_MONITORING 1
|
||||||
|
|
||||||
|
/* Enable temperature monitoring */
|
||||||
|
#define ENABLE_TEMP_MONITORING 1
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Communication
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable CAN support */
|
||||||
|
#define ENABLE_CAN 1
|
||||||
|
|
||||||
|
/* Enable LIN support */
|
||||||
|
#define ENABLE_LIN 0
|
||||||
|
|
||||||
|
/* Enable FlexRay support */
|
||||||
|
#define ENABLE_FLEXRAY 0
|
||||||
|
|
||||||
|
/* Enable Ethernet support */
|
||||||
|
#define ENABLE_ETHERNET 0
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Diagnostics
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable UDS support */
|
||||||
|
#define ENABLE_UDS 1
|
||||||
|
|
||||||
|
/* Enable OBD-II support */
|
||||||
|
#define ENABLE_OBD_II 1
|
||||||
|
|
||||||
|
/* Enable DTC management */
|
||||||
|
#define ENABLE_DTC_MANAGEMENT 1
|
||||||
|
|
||||||
|
/* Maximum number of DTCs */
|
||||||
|
#define MAX_DTC_COUNT 100
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Configuration Validation
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#if MAX_TASKS > 255
|
||||||
|
#error "MAX_TASKS must be less than 256"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MAX_PRIORITY_LEVELS > 32
|
||||||
|
#error "MAX_PRIORITY_LEVELS must be less than 33"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TICK_RATE_HZ < 100 || TICK_RATE_HZ > 10000
|
||||||
|
#error "TICK_RATE_HZ must be between 100 and 10000"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* KERNEL_CONFIG_H */
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
/**
|
||||||
|
* @file mcu_config.h
|
||||||
|
* @brief MCU-specific configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MCU_CONFIG_H
|
||||||
|
#define MCU_CONFIG_H
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* MCU Selection
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Select target MCU */
|
||||||
|
#define MCU_STM32F407VG 1
|
||||||
|
#define MCU_NXP_S32K144 0
|
||||||
|
#define MCU_INFINEON_TC397 0
|
||||||
|
|
||||||
|
#if (MCU_STM32F407VG + MCU_NXP_S32K144 + MCU_INFINEON_TC397) != 1
|
||||||
|
#error "Exactly one MCU must be selected"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Clock Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#if MCU_STM32F407VG
|
||||||
|
/* STM32F407VG clock configuration */
|
||||||
|
#define HSE_VALUE 8000000U /* External crystal */
|
||||||
|
#define HSI_VALUE 16000000U /* Internal oscillator */
|
||||||
|
#define LSE_VALUE 32768U /* Low speed external */
|
||||||
|
#define LSI_VALUE 32000U /* Low speed internal */
|
||||||
|
|
||||||
|
#define SYSTEM_CLOCK 168000000U /* 168 MHz */
|
||||||
|
#define AHB_CLOCK 168000000U /* 168 MHz */
|
||||||
|
#define APB1_CLOCK 42000000U /* 42 MHz */
|
||||||
|
#define APB2_CLOCK 84000000U /* 84 MHz */
|
||||||
|
|
||||||
|
/* PLL configuration */
|
||||||
|
#define PLL_M 8
|
||||||
|
#define PLL_N 336
|
||||||
|
#define PLL_P 2
|
||||||
|
#define PLL_Q 7
|
||||||
|
|
||||||
|
#elif MCU_NXP_S32K144
|
||||||
|
/* NXP S32K144 clock configuration */
|
||||||
|
#define SOSC_FREQUENCY 8000000U /* System oscillator */
|
||||||
|
#define SPLL_FREQUENCY 160000000U /* System PLL */
|
||||||
|
#define FIRC_FREQUENCY 48000000U /* Fast IRC */
|
||||||
|
#define SIRC_FREQUENCY 8000000U /* Slow IRC */
|
||||||
|
|
||||||
|
#define SYSTEM_CLOCK 160000000U /* 160 MHz */
|
||||||
|
#define BUS_CLOCK 40000000U /* 40 MHz */
|
||||||
|
#define FLASH_CLOCK 20000000U /* 20 MHz */
|
||||||
|
|
||||||
|
/* PLL configuration */
|
||||||
|
#define PLL_MULT 20
|
||||||
|
#define PLL_DIV 1
|
||||||
|
|
||||||
|
#elif MCU_INFINEON_TC397
|
||||||
|
/* Infineon TC397 clock configuration */
|
||||||
|
#define F_OSC 20000000U /* External oscillator */
|
||||||
|
#define F_PLL 300000000U /* PLL frequency */
|
||||||
|
|
||||||
|
#define SYSTEM_CLOCK 300000000U /* 300 MHz */
|
||||||
|
#define PERIPHERAL_CLOCK 150000000U /* 150 MHz */
|
||||||
|
#define STM_CLOCK 100000000U /* 100 MHz */
|
||||||
|
|
||||||
|
/* PLL configuration */
|
||||||
|
#define PLL_N 30
|
||||||
|
#define PLL_P 2
|
||||||
|
#define PLL_K2 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Peripheral Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#if MCU_STM32F407VG
|
||||||
|
/* STM32F407VG peripherals */
|
||||||
|
#define GPIO_PORT_COUNT 6
|
||||||
|
#define UART_COUNT 6
|
||||||
|
#define SPI_COUNT 3
|
||||||
|
#define I2C_COUNT 3
|
||||||
|
#define CAN_COUNT 2
|
||||||
|
#define ADC_COUNT 3
|
||||||
|
#define TIMER_COUNT 14
|
||||||
|
#define DMA_STREAM_COUNT 16
|
||||||
|
|
||||||
|
/* GPIO ports */
|
||||||
|
#define GPIOA_ENABLE 1
|
||||||
|
#define GPIOB_ENABLE 1
|
||||||
|
#define GPIOC_ENABLE 1
|
||||||
|
#define GPIOD_ENABLE 1
|
||||||
|
#define GPIOE_ENABLE 1
|
||||||
|
#define GPIOF_ENABLE 0
|
||||||
|
#define GPIOG_ENABLE 0
|
||||||
|
|
||||||
|
/* UART configuration */
|
||||||
|
#define UART1_ENABLE 1
|
||||||
|
#define UART2_ENABLE 1
|
||||||
|
#define UART3_ENABLE 0
|
||||||
|
#define UART4_ENABLE 0
|
||||||
|
#define UART5_ENABLE 0
|
||||||
|
#define UART6_ENABLE 0
|
||||||
|
|
||||||
|
/* SPI configuration */
|
||||||
|
#define SPI1_ENABLE 1
|
||||||
|
#define SPI2_ENABLE 1
|
||||||
|
#define SPI3_ENABLE 0
|
||||||
|
|
||||||
|
/* I2C configuration */
|
||||||
|
#define I2C1_ENABLE 1
|
||||||
|
#define I2C2_ENABLE 0
|
||||||
|
#define I2C3_ENABLE 0
|
||||||
|
|
||||||
|
/* CAN configuration */
|
||||||
|
#define CAN1_ENABLE 1
|
||||||
|
#define CAN2_ENABLE 1
|
||||||
|
|
||||||
|
/* ADC configuration */
|
||||||
|
#define ADC1_ENABLE 1
|
||||||
|
#define ADC2_ENABLE 0
|
||||||
|
#define ADC3_ENABLE 0
|
||||||
|
|
||||||
|
#elif MCU_NXP_S32K144
|
||||||
|
/* NXP S32K144 peripherals */
|
||||||
|
#define GPIO_PORT_COUNT 5
|
||||||
|
#define UART_COUNT 3
|
||||||
|
#define SPI_COUNT 3
|
||||||
|
#define I2C_COUNT 2
|
||||||
|
#define CAN_COUNT 3
|
||||||
|
#define ADC_COUNT 2
|
||||||
|
#define TIMER_COUNT 5
|
||||||
|
|
||||||
|
/* GPIO ports */
|
||||||
|
#define GPIOA_ENABLE 1
|
||||||
|
#define GPIOB_ENABLE 1
|
||||||
|
#define GPIOC_ENABLE 1
|
||||||
|
#define GPIOD_ENABLE 1
|
||||||
|
#define GPIOE_ENABLE 1
|
||||||
|
|
||||||
|
/* UART configuration */
|
||||||
|
#define UART0_ENABLE 1
|
||||||
|
#define UART1_ENABLE 1
|
||||||
|
#define UART2_ENABLE 0
|
||||||
|
|
||||||
|
/* SPI configuration */
|
||||||
|
#define SPI0_ENABLE 1
|
||||||
|
#define SPI1_ENABLE 1
|
||||||
|
#define SPI2_ENABLE 0
|
||||||
|
|
||||||
|
/* I2C configuration */
|
||||||
|
#define I2C0_ENABLE 1
|
||||||
|
#define I2C1_ENABLE 0
|
||||||
|
|
||||||
|
/* CAN configuration */
|
||||||
|
#define CAN0_ENABLE 1
|
||||||
|
#define CAN1_ENABLE 1
|
||||||
|
#define CAN2_ENABLE 0
|
||||||
|
|
||||||
|
/* ADC configuration */
|
||||||
|
#define ADC0_ENABLE 1
|
||||||
|
#define ADC1_ENABLE 1
|
||||||
|
|
||||||
|
#elif MCU_INFINEON_TC397
|
||||||
|
/* Infineon TC397 peripherals */
|
||||||
|
#define GPIO_PORT_COUNT 16
|
||||||
|
#define UART_COUNT 4
|
||||||
|
#define SPI_COUNT 4
|
||||||
|
#define I2C_COUNT 2
|
||||||
|
#define CAN_COUNT 4
|
||||||
|
#define ADC_COUNT 8
|
||||||
|
#define TIMER_COUNT 20
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Interrupt Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* NVIC priority grouping */
|
||||||
|
#define NVIC_PRIORITY_GROUPING 4 /* 4 bits preemption, 0 bits sub-priority */
|
||||||
|
|
||||||
|
/* Maximum interrupt priority */
|
||||||
|
#define MAX_INTERRUPT_PRIORITY 15
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Power Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Voltage monitoring */
|
||||||
|
#define ENABLE_VOLTAGE_MONITORING 1
|
||||||
|
#define VOLTAGE_MONITOR_THRESHOLD 2.7f /* Volts */
|
||||||
|
|
||||||
|
/* Temperature monitoring */
|
||||||
|
#define ENABLE_TEMP_MONITORING 1
|
||||||
|
#define TEMP_MONITOR_THRESHOLD 125 /* °C */
|
||||||
|
|
||||||
|
/* Brown-out reset */
|
||||||
|
#define ENABLE_BROWN_OUT_RESET 1
|
||||||
|
#define BROWN_OUT_THRESHOLD 2.7f /* Volts */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Safety Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Watchdog */
|
||||||
|
#define WATCHDOG_ENABLED 1
|
||||||
|
#define WATCHDOG_TIMEOUT 100 /* ms */
|
||||||
|
#define WATCHDOG_WINDOW 50 /* ms */
|
||||||
|
|
||||||
|
/* Clock security system */
|
||||||
|
#define ENABLE_CLOCK_SECURITY 1
|
||||||
|
|
||||||
|
/* Memory protection */
|
||||||
|
#define ENABLE_MPU 1
|
||||||
|
|
||||||
|
/* ECC */
|
||||||
|
#define ENABLE_ECC 1
|
||||||
|
|
||||||
|
#endif /* MCU_CONFIG_H */
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
/**
|
||||||
|
* @file memory_config.h
|
||||||
|
* @brief Memory configuration for automotive RTOS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MEMORY_CONFIG_H
|
||||||
|
#define MEMORY_CONFIG_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Memory Map Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Flash memory configuration */
|
||||||
|
#define FLASH_BASE_ADDRESS 0x08000000U
|
||||||
|
#define FLASH_SIZE 0x00100000U /* 1 MB */
|
||||||
|
#define FLASH_PAGE_SIZE 0x00002000U /* 8 KB */
|
||||||
|
#define FLASH_SECTOR_SIZE 0x00010000U /* 64 KB */
|
||||||
|
|
||||||
|
/* RAM memory configuration */
|
||||||
|
#define RAM_BASE_ADDRESS 0x20000000U
|
||||||
|
#define RAM_SIZE 0x00020000U /* 128 KB */
|
||||||
|
|
||||||
|
/* CCM RAM (Core Coupled Memory) */
|
||||||
|
#define CCM_RAM_BASE_ADDRESS 0x10000000U
|
||||||
|
#define CCM_RAM_SIZE 0x00010000U /* 64 KB */
|
||||||
|
|
||||||
|
/* External RAM configuration */
|
||||||
|
#define EXT_RAM_BASE_ADDRESS 0x60000000U
|
||||||
|
#define EXT_RAM_SIZE 0x00800000U /* 8 MB */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Memory Regions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Kernel memory region */
|
||||||
|
#define KERNEL_MEMORY_START FLASH_BASE_ADDRESS
|
||||||
|
#define KERNEL_MEMORY_SIZE 0x00040000U /* 256 KB */
|
||||||
|
|
||||||
|
/* Application memory region */
|
||||||
|
#define APP_MEMORY_START (KERNEL_MEMORY_START + KERNEL_MEMORY_SIZE)
|
||||||
|
#define APP_MEMORY_SIZE 0x00080000U /* 512 KB */
|
||||||
|
|
||||||
|
/* Calibration data region */
|
||||||
|
#define CALIBRATION_MEMORY_START (APP_MEMORY_START + APP_MEMORY_SIZE)
|
||||||
|
#define CALIBRATION_MEMORY_SIZE 0x00020000U /* 128 KB */
|
||||||
|
|
||||||
|
/* Bootloader region */
|
||||||
|
#define BOOTLOADER_MEMORY_START FLASH_BASE_ADDRESS
|
||||||
|
#define BOOTLOADER_MEMORY_SIZE 0x00010000U /* 64 KB */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Stack Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Main stack (MSP) */
|
||||||
|
#define MAIN_STACK_SIZE 0x00001000U /* 4 KB */
|
||||||
|
|
||||||
|
/* Process stack (PSP) */
|
||||||
|
#define PROCESS_STACK_SIZE 0x00000800U /* 2 KB */
|
||||||
|
|
||||||
|
/* ISR stack */
|
||||||
|
#define ISR_STACK_SIZE 0x00000800U /* 2 KB */
|
||||||
|
|
||||||
|
/* Exception stack */
|
||||||
|
#define EXCEPTION_STACK_SIZE 0x00000400U /* 1 KB */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Heap Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Heap region */
|
||||||
|
#define HEAP_START (RAM_BASE_ADDRESS + 0x00010000U)
|
||||||
|
#define HEAP_SIZE 0x00010000U /* 64 KB */
|
||||||
|
|
||||||
|
/* Heap alignment */
|
||||||
|
#define HEAP_ALIGNMENT 8
|
||||||
|
|
||||||
|
/* Minimum heap block size */
|
||||||
|
#define HEAP_MIN_BLOCK_SIZE 16
|
||||||
|
|
||||||
|
/* Enable heap poisoning */
|
||||||
|
#define ENABLE_HEAP_POISONING 1
|
||||||
|
|
||||||
|
/* Heap poison value */
|
||||||
|
#define HEAP_POISON_VALUE 0xDEADBEEF
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Memory Protection Units (MPU)
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Number of MPU regions */
|
||||||
|
#define MPU_REGION_COUNT 8
|
||||||
|
|
||||||
|
/* MPU region definitions */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t base_address;
|
||||||
|
uint32_t size;
|
||||||
|
uint8_t permissions;
|
||||||
|
bool executable;
|
||||||
|
bool cacheable;
|
||||||
|
bool bufferable;
|
||||||
|
} MpuRegionConfig_t;
|
||||||
|
|
||||||
|
/* Default MPU configuration */
|
||||||
|
static const MpuRegionConfig_t mpu_regions[] = {
|
||||||
|
/* Kernel code region (read-only, executable) */
|
||||||
|
{
|
||||||
|
.base_address = KERNEL_MEMORY_START,
|
||||||
|
.size = KERNEL_MEMORY_SIZE,
|
||||||
|
.permissions = 0x05, /* Read-only */
|
||||||
|
.executable = true,
|
||||||
|
.cacheable = true,
|
||||||
|
.bufferable = false
|
||||||
|
},
|
||||||
|
/* Application code region (read-only, executable) */
|
||||||
|
{
|
||||||
|
.base_address = APP_MEMORY_START,
|
||||||
|
.size = APP_MEMORY_SIZE,
|
||||||
|
.permissions = 0x05, /* Read-only */
|
||||||
|
.executable = true,
|
||||||
|
.cacheable = true,
|
||||||
|
.bufferable = false
|
||||||
|
},
|
||||||
|
/* RAM region (read-write, non-executable) */
|
||||||
|
{
|
||||||
|
.base_address = RAM_BASE_ADDRESS,
|
||||||
|
.size = RAM_SIZE,
|
||||||
|
.permissions = 0x03, /* Read-write */
|
||||||
|
.executable = false,
|
||||||
|
.cacheable = true,
|
||||||
|
.bufferable = true
|
||||||
|
},
|
||||||
|
/* Peripheral region (read-write, non-executable) */
|
||||||
|
{
|
||||||
|
.base_address = 0x40000000U,
|
||||||
|
.size = 0x10000000U, /* 256 MB */
|
||||||
|
.permissions = 0x03, /* Read-write */
|
||||||
|
.executable = false,
|
||||||
|
.cacheable = false,
|
||||||
|
.bufferable = true
|
||||||
|
},
|
||||||
|
/* External memory region */
|
||||||
|
{
|
||||||
|
.base_address = 0x60000000U,
|
||||||
|
.size = 0x10000000U, /* 256 MB */
|
||||||
|
.permissions = 0x03, /* Read-write */
|
||||||
|
.executable = false,
|
||||||
|
.cacheable = true,
|
||||||
|
.bufferable = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MPU_REGION_COUNT_CONFIGURED (sizeof(mpu_regions) / sizeof(MpuRegionConfig_t))
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Memory Pools
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Memory pool configurations */
|
||||||
|
#define MEMORY_POOL_COUNT 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t block_size;
|
||||||
|
uint32_t block_count;
|
||||||
|
uint32_t total_size;
|
||||||
|
} MemoryPoolConfig_t;
|
||||||
|
|
||||||
|
static const MemoryPoolConfig_t memory_pools[MEMORY_POOL_COUNT] = {
|
||||||
|
{.block_size = 32, .block_count = 64, .total_size = 2048},
|
||||||
|
{.block_size = 64, .block_count = 64, .total_size = 4096},
|
||||||
|
{.block_size = 128, .block_count = 32, .total_size = 4096},
|
||||||
|
{.block_size = 256, .block_count = 16, .total_size = 4096}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* DMA Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* DMA channels */
|
||||||
|
#define DMA_CHANNEL_COUNT 8
|
||||||
|
|
||||||
|
/* DMA buffer sizes */
|
||||||
|
#define DMA_BUFFER_SIZE_CAN 512
|
||||||
|
#define DMA_BUFFER_SIZE_UART 256
|
||||||
|
#define DMA_BUFFER_SIZE_SPI 1024
|
||||||
|
#define DMA_BUFFER_SIZE_ADC 512
|
||||||
|
|
||||||
|
/* DMA priorities */
|
||||||
|
#define DMA_PRIORITY_LOW 0
|
||||||
|
#define DMA_PRIORITY_MEDIUM 1
|
||||||
|
#define DMA_PRIORITY_HIGH 2
|
||||||
|
#define DMA_PRIORITY_VERY_HIGH 3
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ECC Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable ECC */
|
||||||
|
#define ENABLE_ECC 1
|
||||||
|
|
||||||
|
/* ECC protected regions */
|
||||||
|
#define ECC_FLASH_ENABLED 1
|
||||||
|
#define ECC_RAM_ENABLED 1
|
||||||
|
|
||||||
|
/* ECC error handling */
|
||||||
|
#define ECC_SINGLE_ERROR_CORRECT 1
|
||||||
|
#define ECC_DOUBLE_ERROR_DETECT 1
|
||||||
|
|
||||||
|
#endif /* MEMORY_CONFIG_H */
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/**
|
||||||
|
* @file task_config.h
|
||||||
|
* @brief Task configuration for automotive applications
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TASK_CONFIG_H
|
||||||
|
#define TASK_CONFIG_H
|
||||||
|
|
||||||
|
#include "kernel_config.h"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Task Priority Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Critical safety tasks (highest priority) */
|
||||||
|
#define TASK_PRIORITY_SAFETY_CRITICAL 0
|
||||||
|
#define TASK_PRIORITY_ENGINE_CONTROL 1
|
||||||
|
#define TASK_PRIORITY_BRAKE_CONTROL 1
|
||||||
|
#define TASK_PRIORITY_ABS_CONTROL 2
|
||||||
|
#define TASK_PRIORITY_AIRBAG_CONTROL 2
|
||||||
|
|
||||||
|
/* Real-time control tasks */
|
||||||
|
#define TASK_PRIORITY_FUEL_INJECTION 3
|
||||||
|
#define TASK_PRIORITY_IGNITION_CONTROL 3
|
||||||
|
#define TASK_PRIORITY_TRANSMISSION 4
|
||||||
|
#define TASK_PRIORITY_STEERING_CONTROL 4
|
||||||
|
|
||||||
|
/* Communication tasks */
|
||||||
|
#define TASK_PRIORITY_CAN_COMM 5
|
||||||
|
#define TASK_PRIORITY_LIN_COMM 6
|
||||||
|
#define TASK_PRIORITY_DIAGNOSTICS 6
|
||||||
|
|
||||||
|
/* Sensor processing tasks */
|
||||||
|
#define TASK_PRIORITY_SENSOR_READING 7
|
||||||
|
#define TASK_PRIORITY_SENSOR_FUSION 7
|
||||||
|
#define TASK_PRIORITY_DATA_LOGGING 8
|
||||||
|
|
||||||
|
/* Body control tasks */
|
||||||
|
#define TASK_PRIORITY_BODY_CONTROL 8
|
||||||
|
#define TASK_PRIORITY_DOOR_CONTROL 9
|
||||||
|
#define TASK_PRIORITY_LIGHTING_CONTROL 9
|
||||||
|
|
||||||
|
/* User interface tasks */
|
||||||
|
#define TASK_PRIORITY_DISPLAY 10
|
||||||
|
#define TASK_PRIORITY_GAUGE_CONTROL 10
|
||||||
|
#define TASK_PRIORITY_INFOTAINMENT 11
|
||||||
|
|
||||||
|
/* Background tasks (lowest priority) */
|
||||||
|
#define TASK_PRIORITY_DIAGNOSTIC_MONITOR 12
|
||||||
|
#define TASK_PRIORITY_MAINTENANCE 13
|
||||||
|
#define TASK_PRIORITY_IDLE IDLE_TASK_PRIORITY
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Task Period Definitions (in milliseconds)
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Safety-critical periods */
|
||||||
|
#define TASK_PERIOD_AIRBAG_CONTROL 1
|
||||||
|
#define TASK_PERIOD_ABS_CONTROL 1
|
||||||
|
#define TASK_PERIOD_ENGINE_CONTROL 1
|
||||||
|
|
||||||
|
/* Control loop periods */
|
||||||
|
#define TASK_PERIOD_FUEL_INJECTION 5
|
||||||
|
#define TASK_PERIOD_IGNITION_CONTROL 5
|
||||||
|
#define TASK_PERIOD_BRAKE_CONTROL 5
|
||||||
|
#define TASK_PERIOD_STEERING_CONTROL 5
|
||||||
|
|
||||||
|
/* Communication periods */
|
||||||
|
#define TASK_PERIOD_CAN_COMM 10
|
||||||
|
#define TASK_PERIOD_LIN_COMM 20
|
||||||
|
#define TASK_PERIOD_DIAGNOSTICS 50
|
||||||
|
|
||||||
|
/* Sensor periods */
|
||||||
|
#define TASK_PERIOD_SENSOR_READING 2
|
||||||
|
#define TASK_PERIOD_SENSOR_FUSION 10
|
||||||
|
#define TASK_PERIOD_DATA_LOGGING 100
|
||||||
|
|
||||||
|
/* Body control periods */
|
||||||
|
#define TASK_PERIOD_BODY_CONTROL 50
|
||||||
|
#define TASK_PERIOD_DOOR_CONTROL 100
|
||||||
|
#define TASK_PERIOD_LIGHTING_CONTROL 100
|
||||||
|
|
||||||
|
/* Display periods */
|
||||||
|
#define TASK_PERIOD_DISPLAY 50
|
||||||
|
#define TASK_PERIOD_GAUGE_CONTROL 10
|
||||||
|
#define TASK_PERIOD_INFOTAINMENT 100
|
||||||
|
|
||||||
|
/* Background periods */
|
||||||
|
#define TASK_PERIOD_DIAGNOSTIC_MONITOR 500
|
||||||
|
#define TASK_PERIOD_MAINTENANCE 1000
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Task Stack Sizes (in bytes)
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Safety-critical task stacks */
|
||||||
|
#define TASK_STACK_AIRBAG_CONTROL 2048
|
||||||
|
#define TASK_STACK_ABS_CONTROL 2048
|
||||||
|
#define TASK_STACK_ENGINE_CONTROL 2048
|
||||||
|
|
||||||
|
/* Control task stacks */
|
||||||
|
#define TASK_STACK_FUEL_INJECTION 1024
|
||||||
|
#define TASK_STACK_IGNITION_CONTROL 1024
|
||||||
|
#define TASK_STACK_BRAKE_CONTROL 1024
|
||||||
|
#define TASK_STACK_STEERING_CONTROL 1024
|
||||||
|
|
||||||
|
/* Communication task stacks */
|
||||||
|
#define TASK_STACK_CAN_COMM 2048
|
||||||
|
#define TASK_STACK_LIN_COMM 1024
|
||||||
|
#define TASK_STACK_DIAGNOSTICS 2048
|
||||||
|
|
||||||
|
/* Sensor task stacks */
|
||||||
|
#define TASK_STACK_SENSOR_READING 1024
|
||||||
|
#define TASK_STACK_SENSOR_FUSION 2048
|
||||||
|
#define TASK_STACK_DATA_LOGGING 4096
|
||||||
|
|
||||||
|
/* Body control task stacks */
|
||||||
|
#define TASK_STACK_BODY_CONTROL 1024
|
||||||
|
#define TASK_STACK_DOOR_CONTROL 1024
|
||||||
|
#define TASK_STACK_LIGHTING_CONTROL 1024
|
||||||
|
|
||||||
|
/* Display task stacks */
|
||||||
|
#define TASK_STACK_DISPLAY 4096
|
||||||
|
#define TASK_STACK_GAUGE_CONTROL 1024
|
||||||
|
#define TASK_STACK_INFOTAINMENT 8192
|
||||||
|
|
||||||
|
/* Background task stacks */
|
||||||
|
#define TASK_STACK_DIAGNOSTIC_MONITOR 2048
|
||||||
|
#define TASK_STACK_MAINTENANCE 1024
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Task Configuration Table
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
uint8_t priority;
|
||||||
|
uint16_t period_ms;
|
||||||
|
uint32_t stack_size;
|
||||||
|
uint32_t deadline_ms;
|
||||||
|
bool is_periodic;
|
||||||
|
bool is_critical;
|
||||||
|
} TaskConfigEntry_t;
|
||||||
|
|
||||||
|
/* Task configuration table */
|
||||||
|
static const TaskConfigEntry_t task_config_table[] = {
|
||||||
|
/* Name Priority Period Stack Deadline Periodic Critical */
|
||||||
|
{"Airbag Control", 0, 1, 2048, 2, true, true},
|
||||||
|
{"ABS Control", 2, 1, 2048, 2, true, true},
|
||||||
|
{"Engine Control", 1, 1, 2048, 2, true, true},
|
||||||
|
{"Brake Control", 1, 5, 1024, 10, true, true},
|
||||||
|
{"Fuel Injection", 3, 5, 1024, 10, true, false},
|
||||||
|
{"Ignition Control", 3, 5, 1024, 10, true, false},
|
||||||
|
{"Steering Control", 4, 5, 1024, 10, true, false},
|
||||||
|
{"Transmission Control", 4, 10, 2048, 20, true, false},
|
||||||
|
{"CAN Communication", 5, 10, 2048, 20, true, false},
|
||||||
|
{"LIN Communication", 6, 20, 1024, 40, true, false},
|
||||||
|
{"Diagnostics", 6, 50, 2048, 100, true, false},
|
||||||
|
{"Sensor Reading", 7, 2, 1024, 4, true, false},
|
||||||
|
{"Sensor Fusion", 7, 10, 2048, 20, true, false},
|
||||||
|
{"Data Logging", 8, 100, 4096, 200, true, false},
|
||||||
|
{"Body Control", 8, 50, 1024, 100, true, false},
|
||||||
|
{"Door Control", 9, 100, 1024, 200, true, false},
|
||||||
|
{"Lighting Control", 9, 100, 1024, 200, true, false},
|
||||||
|
{"Display", 10, 50, 4096, 100, true, false},
|
||||||
|
{"Gauge Control", 10, 10, 1024, 20, true, false},
|
||||||
|
{"Infotainment", 11, 100, 8192, 200, true, false},
|
||||||
|
{"Diagnostic Monitor", 12, 500, 2048, 1000, true, false},
|
||||||
|
{"Maintenance", 13, 1000, 1024, 2000, true, false},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Number of tasks in configuration table */
|
||||||
|
#define TASK_CONFIG_COUNT (sizeof(task_config_table) / sizeof(TaskConfigEntry_t))
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Task Supervision Configuration
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/* Enable task supervision */
|
||||||
|
#define TASK_SUPERVISION_ENABLED 1
|
||||||
|
|
||||||
|
/* Default task timeout in milliseconds */
|
||||||
|
#define TASK_DEFAULT_TIMEOUT_MS 1000
|
||||||
|
|
||||||
|
/* Maximum alive counter before reset */
|
||||||
|
#define TASK_MAX_ALIVE_COUNT 5
|
||||||
|
|
||||||
|
/* Task supervision check period */
|
||||||
|
#define TASK_SUPERVISION_PERIOD_MS 100
|
||||||
|
|
||||||
|
#endif /* TASK_CONFIG_H */
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
automotive_rtos/
|
||||||
|
│
|
||||||
|
├── README.md
|
||||||
|
├── LICENSE
|
||||||
|
├── Makefile
|
||||||
|
├── CMakeLists.txt
|
||||||
|
│
|
||||||
|
├── docs/
|
||||||
|
│ ├── architecture/
|
||||||
|
│ │ ├── system_architecture.md
|
||||||
|
│ │ ├── task_model.md
|
||||||
|
│ │ └── memory_map.md
|
||||||
|
│ ├── api/
|
||||||
|
│ │ ├── kernel_api.md
|
||||||
|
│ │ └── driver_api.md
|
||||||
|
│ ├── safety/
|
||||||
|
│ │ ├── safety_manual.md
|
||||||
|
│ │ ├── hazard_analysis.md
|
||||||
|
│ │ └── iso26262_compliance.md
|
||||||
|
│ └── user_guide/
|
||||||
|
│ ├── getting_started.md
|
||||||
|
│ └── configuration_guide.md
|
||||||
|
│
|
||||||
|
├── kernel/
|
||||||
|
│ ├── include/
|
||||||
|
│ │ ├── kernel.h
|
||||||
|
│ │ ├── task.h
|
||||||
|
│ │ ├── scheduler.h
|
||||||
|
│ │ ├── semaphore.h
|
||||||
|
│ │ ├── mutex.h
|
||||||
|
│ │ ├── queue.h
|
||||||
|
│ │ ├── timer.h
|
||||||
|
│ │ └── isr.h
|
||||||
|
│ │
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── kernel_init.c
|
||||||
|
│ │ ├── scheduler.c
|
||||||
|
│ │ ├── task.c
|
||||||
|
│ │ ├── semaphore.c
|
||||||
|
│ │ ├── mutex.c
|
||||||
|
│ │ ├── queue.c
|
||||||
|
│ │ ├── timer.c
|
||||||
|
│ │ └── fault_handler.c
|
||||||
|
│ │
|
||||||
|
│ └── arch/
|
||||||
|
│ ├── arm/
|
||||||
|
│ │ ├── cortex-m0/
|
||||||
|
│ │ │ ├── port.c
|
||||||
|
│ │ │ ├── port_asm.s
|
||||||
|
│ │ │ └── portmacro.h
|
||||||
|
│ │ ├── cortex-m3/
|
||||||
|
│ │ │ ├── port.c
|
||||||
|
│ │ │ ├── port_asm.s
|
||||||
|
│ │ │ └── portmacro.h
|
||||||
|
│ │ └── cortex-m4/
|
||||||
|
│ │ ├── port.c
|
||||||
|
│ │ ├── port_asm.s
|
||||||
|
│ │ └── portmacro.h
|
||||||
|
│ │
|
||||||
|
│ └── riscv/
|
||||||
|
│ └── rv32/
|
||||||
|
│ ├── port.c
|
||||||
|
│ └── portmacro.h
|
||||||
|
│
|
||||||
|
├── drivers/
|
||||||
|
│ ├── include/
|
||||||
|
│ │ ├── can_driver.h
|
||||||
|
│ │ ├── uart_driver.h
|
||||||
|
│ │ ├── spi_driver.h
|
||||||
|
│ │ ├── i2c_driver.h
|
||||||
|
│ │ ├── gpio_driver.h
|
||||||
|
│ │ ├── adc_driver.h
|
||||||
|
│ │ └── pwm_driver.h
|
||||||
|
│ │
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── can_driver.c
|
||||||
|
│ │ ├── uart_driver.c
|
||||||
|
│ │ ├── spi_driver.c
|
||||||
|
│ │ ├── i2c_driver.c
|
||||||
|
│ │ ├── gpio_driver.c
|
||||||
|
│ │ ├── adc_driver.c
|
||||||
|
│ │ └── pwm_driver.c
|
||||||
|
│ │
|
||||||
|
│ └── mcu_specific/
|
||||||
|
│ ├── stm32f4/
|
||||||
|
│ │ ├── stm32f4xx_hal.c
|
||||||
|
│ │ └── stm32f4xx_config.h
|
||||||
|
│ ├── nxp_s32k/
|
||||||
|
│ │ ├── s32k14x_hal.c
|
||||||
|
│ │ └── s32k14x_config.h
|
||||||
|
│ └── infineon_tricore/
|
||||||
|
│ ├── tc3xx_hal.c
|
||||||
|
│ └── tc3xx_config.h
|
||||||
|
│
|
||||||
|
├── middleware/
|
||||||
|
│ ├── can_stack/
|
||||||
|
│ │ ├── include/
|
||||||
|
│ │ │ ├── can_tp.h
|
||||||
|
│ │ │ ├── can_nm.h
|
||||||
|
│ │ │ └── uds.h
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── can_tp.c
|
||||||
|
│ │ ├── can_nm.c
|
||||||
|
│ │ └── uds.c
|
||||||
|
│ │
|
||||||
|
│ ├── diagnostics/
|
||||||
|
│ │ ├── include/
|
||||||
|
│ │ │ ├── dtc_manager.h
|
||||||
|
│ │ │ └── obd_ii.h
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── dtc_manager.c
|
||||||
|
│ │ └── obd_ii.c
|
||||||
|
│ │
|
||||||
|
│ └── safety/
|
||||||
|
│ ├── watchdog_manager.c
|
||||||
|
│ ├── memory_protection.c
|
||||||
|
│ └── e2e_protection.c
|
||||||
|
│
|
||||||
|
├── applications/
|
||||||
|
│ ├── engine_control/
|
||||||
|
│ │ ├── include/
|
||||||
|
│ │ │ ├── engine_control.h
|
||||||
|
│ │ │ └── engine_parameters.h
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── engine_control_task.c
|
||||||
|
│ │ ├── fuel_injection.c
|
||||||
|
│ │ └── ignition_control.c
|
||||||
|
│ │
|
||||||
|
│ ├── brake_control/
|
||||||
|
│ │ ├── include/
|
||||||
|
│ │ │ └── brake_control.h
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── brake_task.c
|
||||||
|
│ │ └── abs_control.c
|
||||||
|
│ │
|
||||||
|
│ ├── body_control/
|
||||||
|
│ │ ├── include/
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── body_control_task.c
|
||||||
|
│ │ ├── door_control.c
|
||||||
|
│ │ └── lighting_control.c
|
||||||
|
│ │
|
||||||
|
│ └── dashboard/
|
||||||
|
│ ├── include/
|
||||||
|
│ └── src/
|
||||||
|
│ ├── display_task.c
|
||||||
|
│ └── gauge_control.c
|
||||||
|
│
|
||||||
|
├── config/
|
||||||
|
│ ├── kernel_config.h
|
||||||
|
│ ├── task_config.h
|
||||||
|
│ ├── memory_config.h
|
||||||
|
│ ├── can_config.h
|
||||||
|
│ ├── mcu_config.h
|
||||||
|
│ └── board_config.h
|
||||||
|
│
|
||||||
|
├── board/
|
||||||
|
│ ├── stm32f407_discovery/
|
||||||
|
│ │ ├── board_init.c
|
||||||
|
│ │ ├── board.h
|
||||||
|
│ │ └── linker_script.ld
|
||||||
|
│ │
|
||||||
|
│ ├── nxp_s32k144_evb/
|
||||||
|
│ │ ├── board_init.c
|
||||||
|
│ │ ├── board.h
|
||||||
|
│ │ └── linker_script.ld
|
||||||
|
│ │
|
||||||
|
│ └── custom_ecu/
|
||||||
|
│ ├── board_init.c
|
||||||
|
│ ├── board.h
|
||||||
|
│ └── linker_script.ld
|
||||||
|
│
|
||||||
|
├── tests/
|
||||||
|
│ ├── unit/
|
||||||
|
│ │ ├── test_scheduler.c
|
||||||
|
│ │ ├── test_task.c
|
||||||
|
│ │ ├── test_semaphore.c
|
||||||
|
│ │ ├── test_mutex.c
|
||||||
|
│ │ └── test_queue.c
|
||||||
|
│ │
|
||||||
|
│ ├── integration/
|
||||||
|
│ │ ├── test_can_communication.c
|
||||||
|
│ │ ├── test_timing.c
|
||||||
|
│ │ └── test_fault_handling.c
|
||||||
|
│ │
|
||||||
|
│ ├── system/
|
||||||
|
│ │ ├── test_full_system.c
|
||||||
|
│ │ └── test_stress.c
|
||||||
|
│ │
|
||||||
|
│ └── hardware/
|
||||||
|
│ ├── test_gpio.c
|
||||||
|
│ ├── test_adc.c
|
||||||
|
│ └── test_pwm.c
|
||||||
|
│
|
||||||
|
├── tools/
|
||||||
|
│ ├── build_scripts/
|
||||||
|
│ │ ├── build_all.sh
|
||||||
|
│ │ ├── build_target.sh
|
||||||
|
│ │ └── flash_target.sh
|
||||||
|
│ │
|
||||||
|
│ ├── analysis/
|
||||||
|
│ │ ├── stack_usage_analyzer.py
|
||||||
|
│ │ ├── timing_analyzer.py
|
||||||
|
│ │ └── misra_checker.sh
|
||||||
|
│ │
|
||||||
|
│ ├── debugging/
|
||||||
|
│ │ ├── gdb_init.gdb
|
||||||
|
│ │ └── trace_analyzer.py
|
||||||
|
│ │
|
||||||
|
│ └── configuration/
|
||||||
|
│ ├── config_generator.py
|
||||||
|
│ └── task_priority_calculator.py
|
||||||
|
│
|
||||||
|
├── third_party/
|
||||||
|
│ ├── unity/ # Unit testing framework
|
||||||
|
│ ├── cmock/ # Mock objects for testing
|
||||||
|
│ └── cmsis/ # ARM CMSIS headers
|
||||||
|
│
|
||||||
|
├── scripts/
|
||||||
|
│ ├── setup_environment.sh
|
||||||
|
│ ├── generate_docs.sh
|
||||||
|
│ ├── run_tests.sh
|
||||||
|
│ └── static_analysis.sh
|
||||||
|
│
|
||||||
|
├── .github/
|
||||||
|
│ ├── workflows/
|
||||||
|
│ │ ├── build.yml
|
||||||
|
│ │ ├── test.yml
|
||||||
|
│ │ └── static-analysis.yml
|
||||||
|
│ └── ISSUE_TEMPLATE/
|
||||||
|
│ ├── bug_report.md
|
||||||
|
│ └── feature_request.md
|
||||||
|
│
|
||||||
|
├── .vscode/
|
||||||
|
│ ├── launch.json
|
||||||
|
│ ├── tasks.json
|
||||||
|
│ └── settings.json
|
||||||
|
│
|
||||||
|
├── .gitignore
|
||||||
|
├── .gitattributes
|
||||||
|
├── CHANGELOG.md
|
||||||
|
├── CONTRIBUTING.md
|
||||||
|
└── SECURITY.md
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# driver api
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# kernel api
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# memory map
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# system architecture
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# task model
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# hazard analysis
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# iso26262 compliance
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# safety manual
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# configuration guide
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# getting started
|
||||||
|
|
||||||
|
Placeholder documentation.
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* @file adc_driver.h
|
||||||
|
* @brief ADC driver interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADC_DRIVER_H
|
||||||
|
#define ADC_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* ADC Configuration Constants */
|
||||||
|
#define ADC_MAX_INSTANCES 3
|
||||||
|
#define ADC_MAX_CHANNELS 16
|
||||||
|
#define ADC_MAX_SEQUENCES 8
|
||||||
|
|
||||||
|
/* ADC Resolution */
|
||||||
|
typedef enum {
|
||||||
|
ADC_RESOLUTION_6BIT = 6,
|
||||||
|
ADC_RESOLUTION_8BIT = 8,
|
||||||
|
ADC_RESOLUTION_10BIT = 10,
|
||||||
|
ADC_RESOLUTION_12BIT = 12,
|
||||||
|
ADC_RESOLUTION_14BIT = 14,
|
||||||
|
ADC_RESOLUTION_16BIT = 16
|
||||||
|
} AdcResolution_t;
|
||||||
|
|
||||||
|
/* ADC Conversion Modes */
|
||||||
|
typedef enum {
|
||||||
|
ADC_MODE_SINGLE = 0,
|
||||||
|
ADC_MODE_CONTINUOUS = 1,
|
||||||
|
ADC_MODE_SCAN = 2,
|
||||||
|
ADC_MODE_DISCONTINUOUS = 3
|
||||||
|
} AdcConversionMode_t;
|
||||||
|
|
||||||
|
/* ADC Trigger Sources */
|
||||||
|
typedef enum {
|
||||||
|
ADC_TRIGGER_SOFTWARE = 0,
|
||||||
|
ADC_TRIGGER_TIMER = 1,
|
||||||
|
ADC_TRIGGER_EXTERNAL = 2
|
||||||
|
} AdcTriggerSource_t;
|
||||||
|
|
||||||
|
/* ADC Reference Voltage */
|
||||||
|
typedef enum {
|
||||||
|
ADC_REFERENCE_INTERNAL = 0,
|
||||||
|
ADC_REFERENCE_EXTERNAL = 1,
|
||||||
|
ADC_REFERENCE_VDD = 2
|
||||||
|
} AdcReference_t;
|
||||||
|
|
||||||
|
/* ADC Sampling Time */
|
||||||
|
typedef enum {
|
||||||
|
ADC_SAMPLING_1_5_CYCLES = 0,
|
||||||
|
ADC_SAMPLING_7_5_CYCLES = 1,
|
||||||
|
ADC_SAMPLING_13_5_CYCLES = 2,
|
||||||
|
ADC_SAMPLING_28_5_CYCLES = 3,
|
||||||
|
ADC_SAMPLING_41_5_CYCLES = 4,
|
||||||
|
ADC_SAMPLING_55_5_CYCLES = 5,
|
||||||
|
ADC_SAMPLING_71_5_CYCLES = 6,
|
||||||
|
ADC_SAMPLING_239_5_CYCLES = 7
|
||||||
|
} AdcSamplingTime_t;
|
||||||
|
|
||||||
|
/* ADC Callbacks */
|
||||||
|
typedef void (*AdcConversionCompleteCallback_t)(uint16_t* values, uint8_t count);
|
||||||
|
|
||||||
|
/* ADC Channel Configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t channel;
|
||||||
|
AdcSamplingTime_t sampling_time;
|
||||||
|
bool enable_watchdog;
|
||||||
|
uint16_t watchdog_high_threshold;
|
||||||
|
uint16_t watchdog_low_threshold;
|
||||||
|
} AdcChannelConfig_t;
|
||||||
|
|
||||||
|
/* ADC Configuration */
|
||||||
|
typedef struct {
|
||||||
|
AdcResolution_t resolution;
|
||||||
|
AdcConversionMode_t mode;
|
||||||
|
AdcTriggerSource_t trigger_source;
|
||||||
|
AdcReference_t reference;
|
||||||
|
bool enable_dma;
|
||||||
|
uint32_t conversion_frequency;
|
||||||
|
AdcChannelConfig_t channels[ADC_MAX_CHANNELS];
|
||||||
|
uint8_t channel_count;
|
||||||
|
AdcConversionCompleteCallback_t conversion_complete_callback;
|
||||||
|
} AdcConfig_t;
|
||||||
|
|
||||||
|
/* ADC Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t conversions_completed;
|
||||||
|
uint32_t conversions_failed;
|
||||||
|
uint32_t watchdog_events;
|
||||||
|
uint32_t overrun_errors;
|
||||||
|
uint32_t dma_transfers;
|
||||||
|
uint32_t average_conversion_time_us;
|
||||||
|
} AdcStatistics_t;
|
||||||
|
|
||||||
|
/* ADC Driver Interface */
|
||||||
|
KernelStatus_t adc_init(uint8_t instance, AdcConfig_t* config);
|
||||||
|
KernelStatus_t adc_deinit(uint8_t instance);
|
||||||
|
KernelStatus_t adc_start_conversion(uint8_t instance);
|
||||||
|
KernelStatus_t adc_stop_conversion(uint8_t instance);
|
||||||
|
KernelStatus_t adc_read_channel(uint8_t instance, uint8_t channel, uint16_t* value,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t adc_read_channels(uint8_t instance, uint16_t* values, uint8_t count,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t adc_start_dma(uint8_t instance, uint16_t* buffer, uint16_t length);
|
||||||
|
KernelStatus_t adc_stop_dma(uint8_t instance);
|
||||||
|
KernelStatus_t adc_calibrate(uint8_t instance);
|
||||||
|
KernelStatus_t adc_get_statistics(uint8_t instance, AdcStatistics_t* stats);
|
||||||
|
float adc_convert_to_voltage(uint16_t adc_value, AdcResolution_t resolution,
|
||||||
|
float reference_voltage);
|
||||||
|
void adc_process_interrupt(uint8_t instance);
|
||||||
|
|
||||||
|
#endif /* ADC_DRIVER_H */
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/**
|
||||||
|
* @file can_driver.h
|
||||||
|
* @brief CAN (Controller Area Network) driver interface
|
||||||
|
* @note Supports classical CAN and CAN FD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAN_DRIVER_H
|
||||||
|
#define CAN_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* CAN Configuration Constants */
|
||||||
|
#define CAN_MAX_MESSAGE_LENGTH 8 /* Classical CAN */
|
||||||
|
#define CAN_FD_MAX_MESSAGE_LENGTH 64 /* CAN FD */
|
||||||
|
#define CAN_MAX_FILTERS 32
|
||||||
|
#define CAN_MAX_TX_MAILBOXES 3
|
||||||
|
#define CAN_MAX_RX_FIFO_DEPTH 16
|
||||||
|
|
||||||
|
/* CAN Baudrates */
|
||||||
|
typedef enum {
|
||||||
|
CAN_BAUD_125K = 125000,
|
||||||
|
CAN_BAUD_250K = 250000,
|
||||||
|
CAN_BAUD_500K = 500000,
|
||||||
|
CAN_BAUD_1M = 1000000,
|
||||||
|
CAN_FD_BAUD_2M = 2000000,
|
||||||
|
CAN_FD_BAUD_5M = 5000000
|
||||||
|
} CanBaudrate_t;
|
||||||
|
|
||||||
|
/* CAN Message Types */
|
||||||
|
typedef enum {
|
||||||
|
CAN_FRAME_CLASSIC = 0,
|
||||||
|
CAN_FRAME_FD = 1,
|
||||||
|
CAN_FRAME_FD_BRS = 2 /* FD with Bit Rate Switch */
|
||||||
|
} CanFrameType_t;
|
||||||
|
|
||||||
|
/* CAN Frame Format */
|
||||||
|
typedef enum {
|
||||||
|
CAN_FORMAT_STANDARD = 0, /* 11-bit identifier */
|
||||||
|
CAN_FORMAT_EXTENDED = 1 /* 29-bit identifier */
|
||||||
|
} CanFrameFormat_t;
|
||||||
|
|
||||||
|
/* CAN Message ID */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t id : 29;
|
||||||
|
CanFrameFormat_t format : 1;
|
||||||
|
bool is_remote : 1;
|
||||||
|
bool is_extended : 1;
|
||||||
|
} CanMessageId_t;
|
||||||
|
|
||||||
|
/* CAN Message Structure */
|
||||||
|
typedef struct {
|
||||||
|
CanMessageId_t id;
|
||||||
|
CanFrameType_t type;
|
||||||
|
uint8_t data[CAN_FD_MAX_MESSAGE_LENGTH];
|
||||||
|
uint8_t length;
|
||||||
|
uint32_t timestamp;
|
||||||
|
bool is_rx;
|
||||||
|
} CanMessage_t;
|
||||||
|
|
||||||
|
/* CAN Filter Configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t filter_id;
|
||||||
|
uint32_t filter_mask;
|
||||||
|
CanFrameFormat_t format;
|
||||||
|
bool enable;
|
||||||
|
} CanFilterConfig_t;
|
||||||
|
|
||||||
|
/* CAN Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t tx_messages;
|
||||||
|
uint32_t rx_messages;
|
||||||
|
uint32_t tx_errors;
|
||||||
|
uint32_t rx_errors;
|
||||||
|
uint32_t bus_off_count;
|
||||||
|
uint32_t error_warning_count;
|
||||||
|
uint32_t error_passive_count;
|
||||||
|
uint32_t tx_overflow;
|
||||||
|
uint32_t rx_overflow;
|
||||||
|
} CanStatistics_t;
|
||||||
|
|
||||||
|
/* CAN Callbacks */
|
||||||
|
typedef void (*CanRxCallback_t)(const CanMessage_t* message);
|
||||||
|
typedef void (*CanTxCallback_t)(uint32_t mailbox, bool success);
|
||||||
|
typedef void (*CanErrorCallback_t)(uint32_t error_code);
|
||||||
|
|
||||||
|
/* CAN Configuration Structure */
|
||||||
|
typedef struct {
|
||||||
|
CanBaudrate_t nominal_baudrate;
|
||||||
|
CanBaudrate_t data_baudrate; /* For CAN FD */
|
||||||
|
CanFrameType_t frame_type;
|
||||||
|
bool enable_fd;
|
||||||
|
bool enable_automatic_retransmission;
|
||||||
|
CanFilterConfig_t filters[CAN_MAX_FILTERS];
|
||||||
|
uint8_t filter_count;
|
||||||
|
CanRxCallback_t rx_callback;
|
||||||
|
CanTxCallback_t tx_callback;
|
||||||
|
CanErrorCallback_t error_callback;
|
||||||
|
} CanConfig_t;
|
||||||
|
|
||||||
|
/* CAN Driver Interface */
|
||||||
|
KernelStatus_t can_init(CanConfig_t* config);
|
||||||
|
KernelStatus_t can_deinit(void);
|
||||||
|
KernelStatus_t can_send_message(const CanMessage_t* message, uint32_t timeout_ms);
|
||||||
|
KernelStatus_t can_receive_message(CanMessage_t* message, uint32_t timeout_ms);
|
||||||
|
KernelStatus_t can_configure_filter(const CanFilterConfig_t* filter);
|
||||||
|
KernelStatus_t can_set_baudrate(CanBaudrate_t baudrate);
|
||||||
|
KernelStatus_t can_get_statistics(CanStatistics_t* stats);
|
||||||
|
KernelStatus_t can_clear_statistics(void);
|
||||||
|
bool can_is_bus_off(void);
|
||||||
|
KernelStatus_t can_recover_bus_off(void);
|
||||||
|
void can_process_interrupt(void);
|
||||||
|
|
||||||
|
/* CAN FD Specific Functions */
|
||||||
|
KernelStatus_t can_fd_set_data_baudrate(CanBaudrate_t baudrate);
|
||||||
|
KernelStatus_t can_fd_set_transmit_delay(uint16_t delay_us);
|
||||||
|
bool can_fd_is_enabled(void);
|
||||||
|
|
||||||
|
#endif /* CAN_DRIVER_H */
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* @file gpio_driver.h
|
||||||
|
* @brief GPIO driver interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GPIO_DRIVER_H
|
||||||
|
#define GPIO_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* GPIO Configuration Constants */
|
||||||
|
#define GPIO_MAX_PORTS 8
|
||||||
|
#define GPIO_MAX_PINS_PER_PORT 16
|
||||||
|
#define GPIO_MAX_INTERRUPTS 32
|
||||||
|
|
||||||
|
/* GPIO Modes */
|
||||||
|
typedef enum {
|
||||||
|
GPIO_MODE_INPUT = 0,
|
||||||
|
GPIO_MODE_OUTPUT = 1,
|
||||||
|
GPIO_MODE_ALTERNATE = 2,
|
||||||
|
GPIO_MODE_ANALOG = 3
|
||||||
|
} GpioMode_t;
|
||||||
|
|
||||||
|
/* GPIO Output Types */
|
||||||
|
typedef enum {
|
||||||
|
GPIO_OUTPUT_PUSH_PULL = 0,
|
||||||
|
GPIO_OUTPUT_OPEN_DRAIN = 1
|
||||||
|
} GpioOutputType_t;
|
||||||
|
|
||||||
|
/* GPIO Pull Configuration */
|
||||||
|
typedef enum {
|
||||||
|
GPIO_PULL_NONE = 0,
|
||||||
|
GPIO_PULL_UP = 1,
|
||||||
|
GPIO_PULL_DOWN = 2
|
||||||
|
} GpioPull_t;
|
||||||
|
|
||||||
|
/* GPIO Speed */
|
||||||
|
typedef enum {
|
||||||
|
GPIO_SPEED_LOW = 0,
|
||||||
|
GPIO_SPEED_MEDIUM = 1,
|
||||||
|
GPIO_SPEED_HIGH = 2,
|
||||||
|
GPIO_SPEED_VERY_HIGH = 3
|
||||||
|
} GpioSpeed_t;
|
||||||
|
|
||||||
|
/* GPIO Alternate Functions */
|
||||||
|
typedef enum {
|
||||||
|
GPIO_AF0 = 0,
|
||||||
|
GPIO_AF1 = 1,
|
||||||
|
GPIO_AF2 = 2,
|
||||||
|
GPIO_AF3 = 3,
|
||||||
|
GPIO_AF4 = 4,
|
||||||
|
GPIO_AF5 = 5,
|
||||||
|
GPIO_AF6 = 6,
|
||||||
|
GPIO_AF7 = 7,
|
||||||
|
GPIO_AF8 = 8,
|
||||||
|
GPIO_AF9 = 9,
|
||||||
|
GPIO_AF10 = 10,
|
||||||
|
GPIO_AF11 = 11,
|
||||||
|
GPIO_AF12 = 12,
|
||||||
|
GPIO_AF13 = 13,
|
||||||
|
GPIO_AF14 = 14,
|
||||||
|
GPIO_AF15 = 15
|
||||||
|
} GpioAlternateFunction_t;
|
||||||
|
|
||||||
|
/* GPIO Interrupt Triggers */
|
||||||
|
typedef enum {
|
||||||
|
GPIO_INTERRUPT_NONE = 0,
|
||||||
|
GPIO_INTERRUPT_RISING = 1,
|
||||||
|
GPIO_INTERRUPT_FALLING = 2,
|
||||||
|
GPIO_INTERRUPT_BOTH = 3
|
||||||
|
} GpioInterruptTrigger_t;
|
||||||
|
|
||||||
|
/* GPIO Pin Configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t port;
|
||||||
|
uint8_t pin;
|
||||||
|
GpioMode_t mode;
|
||||||
|
GpioOutputType_t output_type;
|
||||||
|
GpioPull_t pull;
|
||||||
|
GpioSpeed_t speed;
|
||||||
|
GpioAlternateFunction_t alternate_function;
|
||||||
|
} GpioPinConfig_t;
|
||||||
|
|
||||||
|
/* GPIO Interrupt Configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t port;
|
||||||
|
uint8_t pin;
|
||||||
|
GpioInterruptTrigger_t trigger;
|
||||||
|
void (*callback)(uint8_t port, uint8_t pin);
|
||||||
|
} GpioInterruptConfig_t;
|
||||||
|
|
||||||
|
/* GPIO Driver Interface */
|
||||||
|
KernelStatus_t gpio_init(const GpioPinConfig_t* config);
|
||||||
|
KernelStatus_t gpio_deinit(uint8_t port, uint8_t pin);
|
||||||
|
KernelStatus_t gpio_set_mode(uint8_t port, uint8_t pin, GpioMode_t mode);
|
||||||
|
KernelStatus_t gpio_set_output_type(uint8_t port, uint8_t pin, GpioOutputType_t type);
|
||||||
|
KernelStatus_t gpio_set_pull(uint8_t port, uint8_t pin, GpioPull_t pull);
|
||||||
|
KernelStatus_t gpio_set_speed(uint8_t port, uint8_t pin, GpioSpeed_t speed);
|
||||||
|
KernelStatus_t gpio_set_alternate_function(uint8_t port, uint8_t pin,
|
||||||
|
GpioAlternateFunction_t af);
|
||||||
|
KernelStatus_t gpio_write(uint8_t port, uint8_t pin, bool value);
|
||||||
|
KernelStatus_t gpio_write_port(uint8_t port, uint16_t value);
|
||||||
|
bool gpio_read(uint8_t port, uint8_t pin);
|
||||||
|
uint16_t gpio_read_port(uint8_t port);
|
||||||
|
KernelStatus_t gpio_toggle(uint8_t port, uint8_t pin);
|
||||||
|
KernelStatus_t gpio_configure_interrupt(const GpioInterruptConfig_t* config);
|
||||||
|
KernelStatus_t gpio_enable_interrupt(uint8_t port, uint8_t pin);
|
||||||
|
KernelStatus_t gpio_disable_interrupt(uint8_t port, uint8_t pin);
|
||||||
|
void gpio_process_interrupt(uint8_t port);
|
||||||
|
|
||||||
|
#endif /* GPIO_DRIVER_H */
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* @file i2c_driver.h
|
||||||
|
* @brief I2C driver interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I2C_DRIVER_H
|
||||||
|
#define I2C_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* I2C Configuration Constants */
|
||||||
|
#define I2C_MAX_INSTANCES 4
|
||||||
|
#define I2C_MAX_BUFFER_SIZE 1024
|
||||||
|
#define I2C_MAX_TRANSFER_SIZE 255
|
||||||
|
|
||||||
|
/* I2C Speeds */
|
||||||
|
typedef enum {
|
||||||
|
I2C_SPEED_STANDARD = 100000, /* 100 kHz */
|
||||||
|
I2C_SPEED_FAST = 400000, /* 400 kHz */
|
||||||
|
I2C_SPEED_FAST_PLUS = 1000000, /* 1 MHz */
|
||||||
|
I2C_SPEED_HIGH = 3400000 /* 3.4 MHz */
|
||||||
|
} I2cSpeed_t;
|
||||||
|
|
||||||
|
/* I2C Addressing Mode */
|
||||||
|
typedef enum {
|
||||||
|
I2C_ADDRESSING_7BIT = 0,
|
||||||
|
I2C_ADDRESSING_10BIT = 1
|
||||||
|
} I2cAddressingMode_t;
|
||||||
|
|
||||||
|
/* I2C Transfer Direction */
|
||||||
|
typedef enum {
|
||||||
|
I2C_DIRECTION_WRITE = 0,
|
||||||
|
I2C_DIRECTION_READ = 1
|
||||||
|
} I2cDirection_t;
|
||||||
|
|
||||||
|
/* I2C Transfer Status */
|
||||||
|
typedef enum {
|
||||||
|
I2C_TRANSFER_COMPLETE = 0,
|
||||||
|
I2C_TRANSFER_ERROR = 1,
|
||||||
|
I2C_TRANSFER_NACK = 2,
|
||||||
|
I2C_TRANSFER_TIMEOUT = 3,
|
||||||
|
I2C_TRANSFER_ARBITRATION_LOST = 4,
|
||||||
|
I2C_TRANSFER_BUS_ERROR = 5
|
||||||
|
} I2cTransferStatus_t;
|
||||||
|
|
||||||
|
/* I2C Callbacks */
|
||||||
|
typedef void (*I2cTransferCompleteCallback_t)(I2cTransferStatus_t status, void* user_data);
|
||||||
|
|
||||||
|
/* I2C Configuration */
|
||||||
|
typedef struct {
|
||||||
|
I2cSpeed_t speed;
|
||||||
|
I2cAddressingMode_t addressing_mode;
|
||||||
|
uint16_t own_address;
|
||||||
|
bool enable_general_call;
|
||||||
|
bool enable_clock_stretching;
|
||||||
|
bool use_dma;
|
||||||
|
I2cTransferCompleteCallback_t transfer_complete_callback;
|
||||||
|
} I2cConfig_t;
|
||||||
|
|
||||||
|
/* I2C Message */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t slave_address;
|
||||||
|
I2cDirection_t direction;
|
||||||
|
uint8_t* data;
|
||||||
|
uint16_t length;
|
||||||
|
bool generate_stop;
|
||||||
|
bool generate_restart;
|
||||||
|
} I2cMessage_t;
|
||||||
|
|
||||||
|
/* I2C Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t transfers_completed;
|
||||||
|
uint32_t transfers_failed;
|
||||||
|
uint32_t nack_errors;
|
||||||
|
uint32_t arbitration_lost;
|
||||||
|
uint32_t bus_errors;
|
||||||
|
uint32_t timeout_errors;
|
||||||
|
uint32_t bytes_transferred;
|
||||||
|
} I2cStatistics_t;
|
||||||
|
|
||||||
|
/* I2C Driver Interface */
|
||||||
|
KernelStatus_t i2c_init(uint8_t instance, I2cConfig_t* config);
|
||||||
|
KernelStatus_t i2c_deinit(uint8_t instance);
|
||||||
|
KernelStatus_t i2c_transfer(uint8_t instance, const I2cMessage_t* message,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t i2c_transfer_async(uint8_t instance, const I2cMessage_t* message);
|
||||||
|
KernelStatus_t i2c_write(uint8_t instance, uint16_t slave_address,
|
||||||
|
const uint8_t* data, uint16_t length, uint32_t timeout_ms);
|
||||||
|
KernelStatus_t i2c_read(uint8_t instance, uint16_t slave_address,
|
||||||
|
uint8_t* data, uint16_t length, uint32_t timeout_ms);
|
||||||
|
KernelStatus_t i2c_write_read(uint8_t instance, uint16_t slave_address,
|
||||||
|
const uint8_t* tx_data, uint16_t tx_length,
|
||||||
|
uint8_t* rx_data, uint16_t rx_length,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t i2c_get_statistics(uint8_t instance, I2cStatistics_t* stats);
|
||||||
|
bool i2c_is_device_ready(uint8_t instance, uint16_t slave_address);
|
||||||
|
void i2c_process_interrupt(uint8_t instance);
|
||||||
|
|
||||||
|
#endif /* I2C_DRIVER_H */
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* @file pwm_driver.h
|
||||||
|
* @brief PWM driver interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PWM_DRIVER_H
|
||||||
|
#define PWM_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* PWM Configuration Constants */
|
||||||
|
#define PWM_MAX_INSTANCES 8
|
||||||
|
#define PWM_MAX_CHANNELS 4
|
||||||
|
#define PWM_MAX_DUTY_CYCLE 10000 /* 100.00% in 0.01% steps */
|
||||||
|
|
||||||
|
/* PWM Alignment Modes */
|
||||||
|
typedef enum {
|
||||||
|
PWM_ALIGNMENT_EDGE = 0,
|
||||||
|
PWM_ALIGNMENT_CENTER = 1
|
||||||
|
} PwmAlignment_t;
|
||||||
|
|
||||||
|
/* PWM Polarity */
|
||||||
|
typedef enum {
|
||||||
|
PWM_POLARITY_ACTIVE_HIGH = 0,
|
||||||
|
PWM_POLARITY_ACTIVE_LOW = 1
|
||||||
|
} PwmPolarity_t;
|
||||||
|
|
||||||
|
/* PWM Modes */
|
||||||
|
typedef enum {
|
||||||
|
PWM_MODE_NORMAL = 0,
|
||||||
|
PWM_MODE_COMPLEMENTARY = 1,
|
||||||
|
PWM_MODE_COMBINED = 2
|
||||||
|
} PwmMode_t;
|
||||||
|
|
||||||
|
/* PWM Dead Time */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t rising_edge_delay_ns;
|
||||||
|
uint16_t falling_edge_delay_ns;
|
||||||
|
} PwmDeadTime_t;
|
||||||
|
|
||||||
|
/* PWM Fault Actions */
|
||||||
|
typedef enum {
|
||||||
|
PWM_FAULT_DISABLE = 0,
|
||||||
|
PWM_FAULT_ENABLE = 1,
|
||||||
|
PWM_FAULT_HIGH_Z = 2
|
||||||
|
} PwmFaultAction_t;
|
||||||
|
|
||||||
|
/* PWM Callbacks */
|
||||||
|
typedef void (*PwmPeriodElapsedCallback_t)(uint8_t instance);
|
||||||
|
typedef void (*PwmFaultCallback_t)(uint8_t instance, uint32_t fault_flags);
|
||||||
|
|
||||||
|
/* PWM Channel Configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t channel;
|
||||||
|
uint32_t duty_cycle; /* 0 to PWM_MAX_DUTY_CYCLE */
|
||||||
|
PwmPolarity_t polarity;
|
||||||
|
PwmMode_t mode;
|
||||||
|
PwmDeadTime_t dead_time;
|
||||||
|
} PwmChannelConfig_t;
|
||||||
|
|
||||||
|
/* PWM Configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t frequency_hz;
|
||||||
|
PwmAlignment_t alignment;
|
||||||
|
uint32_t period_ticks;
|
||||||
|
uint8_t prescaler;
|
||||||
|
PwmChannelConfig_t channels[PWM_MAX_CHANNELS];
|
||||||
|
uint8_t channel_count;
|
||||||
|
bool enable_fault_protection;
|
||||||
|
PwmFaultAction_t fault_action;
|
||||||
|
PwmPeriodElapsedCallback_t period_elapsed_callback;
|
||||||
|
PwmFaultCallback_t fault_callback;
|
||||||
|
} PwmConfig_t;
|
||||||
|
|
||||||
|
/* PWM Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t period_elapsed_count;
|
||||||
|
uint32_t fault_events;
|
||||||
|
uint32_t duty_cycle_updates;
|
||||||
|
uint32_t overcurrent_events;
|
||||||
|
uint32_t overvoltage_events;
|
||||||
|
} PwmStatistics_t;
|
||||||
|
|
||||||
|
/* PWM Driver Interface */
|
||||||
|
KernelStatus_t pwm_init(uint8_t instance, PwmConfig_t* config);
|
||||||
|
KernelStatus_t pwm_deinit(uint8_t instance);
|
||||||
|
KernelStatus_t pwm_start(uint8_t instance);
|
||||||
|
KernelStatus_t pwm_stop(uint8_t instance);
|
||||||
|
KernelStatus_t pwm_set_duty_cycle(uint8_t instance, uint8_t channel, uint32_t duty_cycle);
|
||||||
|
KernelStatus_t pwm_set_frequency(uint8_t instance, uint32_t frequency_hz);
|
||||||
|
KernelStatus_t pwm_set_period(uint8_t instance, uint32_t period_ticks);
|
||||||
|
KernelStatus_t pwm_set_dead_time(uint8_t instance, uint8_t channel,
|
||||||
|
const PwmDeadTime_t* dead_time);
|
||||||
|
KernelStatus_t pwm_enable_channel(uint8_t instance, uint8_t channel);
|
||||||
|
KernelStatus_t pwm_disable_channel(uint8_t instance, uint8_t channel);
|
||||||
|
KernelStatus_t pwm_configure_fault(uint8_t instance, PwmFaultAction_t action);
|
||||||
|
KernelStatus_t pwm_clear_fault(uint8_t instance);
|
||||||
|
KernelStatus_t pwm_get_statistics(uint8_t instance, PwmStatistics_t* stats);
|
||||||
|
uint32_t pwm_get_duty_cycle(uint8_t instance, uint8_t channel);
|
||||||
|
uint32_t pwm_get_frequency(uint8_t instance);
|
||||||
|
void pwm_process_interrupt(uint8_t instance);
|
||||||
|
|
||||||
|
#endif /* PWM_DRIVER_H */
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* @file spi_driver.h
|
||||||
|
* @brief SPI driver interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPI_DRIVER_H
|
||||||
|
#define SPI_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* SPI Configuration Constants */
|
||||||
|
#define SPI_MAX_INSTANCES 4
|
||||||
|
#define SPI_MAX_BUFFER_SIZE 4096
|
||||||
|
#define SPI_MAX_TRANSFER_SIZE 65535
|
||||||
|
|
||||||
|
/* SPI Modes */
|
||||||
|
typedef enum {
|
||||||
|
SPI_MODE_0 = 0, /* CPOL=0, CPHA=0 */
|
||||||
|
SPI_MODE_1 = 1, /* CPOL=0, CPHA=1 */
|
||||||
|
SPI_MODE_2 = 2, /* CPOL=1, CPHA=0 */
|
||||||
|
SPI_MODE_3 = 3 /* CPOL=1, CPHA=1 */
|
||||||
|
} SpiMode_t;
|
||||||
|
|
||||||
|
/* SPI Clock Speeds */
|
||||||
|
typedef enum {
|
||||||
|
SPI_CLOCK_1MHZ = 1000000,
|
||||||
|
SPI_CLOCK_2MHZ = 2000000,
|
||||||
|
SPI_CLOCK_4MHZ = 4000000,
|
||||||
|
SPI_CLOCK_8MHZ = 8000000,
|
||||||
|
SPI_CLOCK_16MHZ = 16000000,
|
||||||
|
SPI_CLOCK_32MHZ = 32000000
|
||||||
|
} SpiClockSpeed_t;
|
||||||
|
|
||||||
|
/* SPI Data Order */
|
||||||
|
typedef enum {
|
||||||
|
SPI_DATA_ORDER_MSB_FIRST = 0,
|
||||||
|
SPI_DATA_ORDER_LSB_FIRST = 1
|
||||||
|
} SpiDataOrder_t;
|
||||||
|
|
||||||
|
/* SPI Chip Select */
|
||||||
|
typedef enum {
|
||||||
|
SPI_CS_ACTIVE_LOW = 0,
|
||||||
|
SPI_CS_ACTIVE_HIGH = 1
|
||||||
|
} SpiChipSelectPolarity_t;
|
||||||
|
|
||||||
|
/* SPI Transfer Status */
|
||||||
|
typedef enum {
|
||||||
|
SPI_TRANSFER_COMPLETE = 0,
|
||||||
|
SPI_TRANSFER_ERROR = 1,
|
||||||
|
SPI_TRANSFER_TIMEOUT = 2,
|
||||||
|
SPI_TRANSFER_DMA_COMPLETE = 3
|
||||||
|
} SpiTransferStatus_t;
|
||||||
|
|
||||||
|
/* SPI Callbacks */
|
||||||
|
typedef void (*SpiTransferCompleteCallback_t)(SpiTransferStatus_t status, void* user_data);
|
||||||
|
|
||||||
|
/* SPI Configuration */
|
||||||
|
typedef struct {
|
||||||
|
SpiMode_t mode;
|
||||||
|
SpiClockSpeed_t clock_speed;
|
||||||
|
SpiDataOrder_t data_order;
|
||||||
|
uint8_t data_size; /* 8 or 16 bits */
|
||||||
|
bool use_dma;
|
||||||
|
bool enable_hardware_cs;
|
||||||
|
SpiChipSelectPolarity_t cs_polarity;
|
||||||
|
uint8_t cs_port;
|
||||||
|
uint8_t cs_pin;
|
||||||
|
SpiTransferCompleteCallback_t transfer_complete_callback;
|
||||||
|
} SpiConfig_t;
|
||||||
|
|
||||||
|
/* SPI Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t transfers_completed;
|
||||||
|
uint32_t transfers_failed;
|
||||||
|
uint32_t bytes_transferred;
|
||||||
|
uint32_t dma_transfers;
|
||||||
|
uint32_t overrun_errors;
|
||||||
|
uint32_t underrun_errors;
|
||||||
|
uint32_t timeout_errors;
|
||||||
|
} SpiStatistics_t;
|
||||||
|
|
||||||
|
/* SPI Transaction */
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t* tx_data;
|
||||||
|
uint8_t* rx_data;
|
||||||
|
uint16_t length;
|
||||||
|
bool keep_cs_active;
|
||||||
|
void* user_data;
|
||||||
|
} SpiTransaction_t;
|
||||||
|
|
||||||
|
/* SPI Driver Interface */
|
||||||
|
KernelStatus_t spi_init(uint8_t instance, SpiConfig_t* config);
|
||||||
|
KernelStatus_t spi_deinit(uint8_t instance);
|
||||||
|
KernelStatus_t spi_transfer(uint8_t instance, const SpiTransaction_t* transaction,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t spi_transfer_async(uint8_t instance, const SpiTransaction_t* transaction);
|
||||||
|
KernelStatus_t spi_read(uint8_t instance, uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t spi_write(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t spi_read_write(uint8_t instance, const uint8_t* tx_data,
|
||||||
|
uint8_t* rx_data, uint16_t length, uint32_t timeout_ms);
|
||||||
|
KernelStatus_t spi_get_statistics(uint8_t instance, SpiStatistics_t* stats);
|
||||||
|
void spi_set_chip_select(uint8_t instance, bool active);
|
||||||
|
void spi_process_interrupt(uint8_t instance);
|
||||||
|
|
||||||
|
#endif /* SPI_DRIVER_H */
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* @file uart_driver.h
|
||||||
|
* @brief UART driver interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UART_DRIVER_H
|
||||||
|
#define UART_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* UART Configuration Constants */
|
||||||
|
#define UART_MAX_INSTANCES 6
|
||||||
|
#define UART_MAX_BUFFER_SIZE 2048
|
||||||
|
#define UART_DEFAULT_BAUDRATE 115200
|
||||||
|
|
||||||
|
/* UART Baudrates */
|
||||||
|
typedef enum {
|
||||||
|
UART_BAUD_9600 = 9600,
|
||||||
|
UART_BAUD_19200 = 19200,
|
||||||
|
UART_BAUD_38400 = 38400,
|
||||||
|
UART_BAUD_57600 = 57600,
|
||||||
|
UART_BAUD_115200 = 115200,
|
||||||
|
UART_BAUD_230400 = 230400,
|
||||||
|
UART_BAUD_460800 = 460800,
|
||||||
|
UART_BAUD_921600 = 921600
|
||||||
|
} UartBaudrate_t;
|
||||||
|
|
||||||
|
/* UART Data Bits */
|
||||||
|
typedef enum {
|
||||||
|
UART_DATA_BITS_5 = 5,
|
||||||
|
UART_DATA_BITS_6 = 6,
|
||||||
|
UART_DATA_BITS_7 = 7,
|
||||||
|
UART_DATA_BITS_8 = 8,
|
||||||
|
UART_DATA_BITS_9 = 9
|
||||||
|
} UartDataBits_t;
|
||||||
|
|
||||||
|
/* UART Stop Bits */
|
||||||
|
typedef enum {
|
||||||
|
UART_STOP_BITS_1 = 0,
|
||||||
|
UART_STOP_BITS_1_5 = 1,
|
||||||
|
UART_STOP_BITS_2 = 2
|
||||||
|
} UartStopBits_t;
|
||||||
|
|
||||||
|
/* UART Parity */
|
||||||
|
typedef enum {
|
||||||
|
UART_PARITY_NONE = 0,
|
||||||
|
UART_PARITY_EVEN = 1,
|
||||||
|
UART_PARITY_ODD = 2
|
||||||
|
} UartParity_t;
|
||||||
|
|
||||||
|
/* UART Flow Control */
|
||||||
|
typedef enum {
|
||||||
|
UART_FLOW_CONTROL_NONE = 0,
|
||||||
|
UART_FLOW_CONTROL_RTS_CTS = 1,
|
||||||
|
UART_FLOW_CONTROL_XON_XOFF = 2
|
||||||
|
} UartFlowControl_t;
|
||||||
|
|
||||||
|
/* UART Callbacks */
|
||||||
|
typedef void (*UartRxCallback_t)(uint8_t* data, uint16_t length);
|
||||||
|
typedef void (*UartTxCallback_t)(void);
|
||||||
|
typedef void (*UartErrorCallback_t)(uint32_t error);
|
||||||
|
|
||||||
|
/* UART Configuration */
|
||||||
|
typedef struct {
|
||||||
|
UartBaudrate_t baudrate;
|
||||||
|
UartDataBits_t data_bits;
|
||||||
|
UartStopBits_t stop_bits;
|
||||||
|
UartParity_t parity;
|
||||||
|
UartFlowControl_t flow_control;
|
||||||
|
bool enable_rx;
|
||||||
|
bool enable_tx;
|
||||||
|
bool use_dma;
|
||||||
|
UartRxCallback_t rx_callback;
|
||||||
|
UartTxCallback_t tx_callback;
|
||||||
|
UartErrorCallback_t error_callback;
|
||||||
|
} UartConfig_t;
|
||||||
|
|
||||||
|
/* UART Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t tx_bytes;
|
||||||
|
uint32_t rx_bytes;
|
||||||
|
uint32_t tx_errors;
|
||||||
|
uint32_t rx_errors;
|
||||||
|
uint32_t parity_errors;
|
||||||
|
uint32_t framing_errors;
|
||||||
|
uint32_t overrun_errors;
|
||||||
|
uint32_t dma_transfers;
|
||||||
|
} UartStatistics_t;
|
||||||
|
|
||||||
|
/* UART Driver Interface */
|
||||||
|
KernelStatus_t uart_init(uint8_t instance, UartConfig_t* config);
|
||||||
|
KernelStatus_t uart_deinit(uint8_t instance);
|
||||||
|
KernelStatus_t uart_send(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t uart_receive(uint8_t instance, uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
KernelStatus_t uart_send_async(uint8_t instance, const uint8_t* data, uint16_t length);
|
||||||
|
KernelStatus_t uart_receive_async(uint8_t instance, uint8_t* data, uint16_t length);
|
||||||
|
KernelStatus_t uart_flush(uint8_t instance);
|
||||||
|
KernelStatus_t uart_get_statistics(uint8_t instance, UartStatistics_t* stats);
|
||||||
|
uint16_t uart_get_rx_count(uint8_t instance);
|
||||||
|
uint16_t uart_get_tx_count(uint8_t instance);
|
||||||
|
void uart_process_interrupt(uint8_t instance);
|
||||||
|
|
||||||
|
#endif /* UART_DRIVER_H */
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* @file tc3xx_config.h
|
||||||
|
* @brief Infineon TriCore TC3xx specific configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TC3XX_CONFIG_H
|
||||||
|
#define TC3XX_CONFIG_H
|
||||||
|
|
||||||
|
/* MCU Specific Definitions */
|
||||||
|
#define TC397
|
||||||
|
#define CPU_FREQUENCY 300000000U /* 300 MHz */
|
||||||
|
#define PERIPHERAL_FREQUENCY 150000000U /* 150 MHz */
|
||||||
|
#define STM_FREQUENCY 100000000U /* 100 MHz */
|
||||||
|
|
||||||
|
/* Peripheral Base Addresses */
|
||||||
|
#define CAN0_BASE 0xF0200000U
|
||||||
|
#define CAN1_BASE 0xF0210000U
|
||||||
|
#define CAN2_BASE 0xF0220000U
|
||||||
|
#define CAN3_BASE 0xF0230000U
|
||||||
|
|
||||||
|
#define ASCLIN0_BASE 0xF0000000U
|
||||||
|
#define ASCLIN1_BASE 0xF0000100U
|
||||||
|
#define ASCLIN2_BASE 0xF0000200U
|
||||||
|
#define ASCLIN3_BASE 0xF0000300U
|
||||||
|
|
||||||
|
#define QSPI0_BASE 0xF0001000U
|
||||||
|
#define QSPI1_BASE 0xF0001100U
|
||||||
|
#define QSPI2_BASE 0xF0001200U
|
||||||
|
#define QSPI3_BASE 0xF0001300U
|
||||||
|
|
||||||
|
#define I2C0_BASE 0xF0002000U
|
||||||
|
#define I2C1_BASE 0xF0002100U
|
||||||
|
|
||||||
|
#define VADC0_BASE 0xF0020000U
|
||||||
|
#define VADC1_BASE 0xF0020100U
|
||||||
|
|
||||||
|
#define GTM_BASE 0xF0100000U
|
||||||
|
#define GPT12_BASE 0xF0003000U
|
||||||
|
|
||||||
|
/* Interrupt Priorities */
|
||||||
|
#define CAN0_IRQ_PRIORITY 5
|
||||||
|
#define CAN1_IRQ_PRIORITY 5
|
||||||
|
#define ASCLIN0_IRQ_PRIORITY 6
|
||||||
|
#define QSPI0_IRQ_PRIORITY 7
|
||||||
|
#define I2C0_IRQ_PRIORITY 7
|
||||||
|
#define VADC0_IRQ_PRIORITY 8
|
||||||
|
#define GTM_IRQ_PRIORITY 8
|
||||||
|
|
||||||
|
/* DMA Configuration */
|
||||||
|
#define DMA_CHANNEL_COUNT 128
|
||||||
|
#define DMA_PRIORITY_HIGH 0
|
||||||
|
#define DMA_PRIORITY_MEDIUM 1
|
||||||
|
#define DMA_PRIORITY_LOW 2
|
||||||
|
|
||||||
|
/* Safety Features */
|
||||||
|
#define ENABLE_SAFETY_WATCHDOG 1
|
||||||
|
#define WATCHDOG_TIMEOUT_MS 100
|
||||||
|
#define ENABLE_ECC 1
|
||||||
|
#define ENABLE_MEMORY_PROTECTION 1
|
||||||
|
|
||||||
|
/* Memory Configuration */
|
||||||
|
#define FLASH_SIZE 0x800000U /* 8 MB */
|
||||||
|
#define RAM_SIZE 0x280000U /* 2.5 MB */
|
||||||
|
#define DSPR_SIZE 0x20000U /* 128 KB per CPU */
|
||||||
|
|
||||||
|
/* CAN FD Configuration */
|
||||||
|
#define CAN_FD_ENABLED 1
|
||||||
|
#define CAN_FD_MAX_PAYLOAD 64
|
||||||
|
#define CAN_TX_FIFO_SIZE 32
|
||||||
|
#define CAN_RX_FIFO_SIZE 64
|
||||||
|
|
||||||
|
#endif /* TC3XX_CONFIG_H */
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* @file tc3xx_hal.c
|
||||||
|
* @brief Infineon TriCore TC3xx Hardware Abstraction Layer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tc3xx_config.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "uart_driver.h"
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "i2c_driver.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "Ifx_Types.h"
|
||||||
|
#include "IfxCan_Can.h"
|
||||||
|
#include "IfxAsclin_Asc.h"
|
||||||
|
#include "IfxQspi_SpiMaster.h"
|
||||||
|
#include "IfxI2c_I2c.h"
|
||||||
|
#include "IfxPort.h"
|
||||||
|
#include "IfxVadc_Adc.h"
|
||||||
|
#include "IfxGtm_Tom_PwmHl.h"
|
||||||
|
|
||||||
|
/* CAN HAL Implementation */
|
||||||
|
int hal_can_init(uint32_t baudrate, uint8_t frame_type, bool enable_fd) {
|
||||||
|
/* Create CAN module configuration */
|
||||||
|
IfxCan_Can_Config canConfig;
|
||||||
|
IfxCan_Can_initModuleConfig(&canConfig, &MODULE_CAN0);
|
||||||
|
|
||||||
|
/* Configure node */
|
||||||
|
canConfig.nodeConfig[0].baudRate.baudrate = baudrate;
|
||||||
|
canConfig.nodeConfig[0].frame.type = IfxCan_FrameType_receive;
|
||||||
|
|
||||||
|
if (enable_fd) {
|
||||||
|
canConfig.nodeConfig[0].frame.mode = IfxCan_FrameMode_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize CAN module */
|
||||||
|
IfxCan_Can_initModule(&g_canDriver, &canConfig);
|
||||||
|
|
||||||
|
/* Configure TX FIFO */
|
||||||
|
IfxCan_Can_initTxFifo(&g_canDriver, &g_canTxFifo);
|
||||||
|
|
||||||
|
/* Configure RX FIFO */
|
||||||
|
IfxCan_Can_initRxFifo(&g_canDriver, &g_canRxFifo);
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
IfxCan_Can_enableInterrupt(&g_canDriver, IfxCan_Interrupt_messageStoredToDedicatedRxFifo);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hal_can_send_message(const CanMessage_t* message, uint32_t* mailbox) {
|
||||||
|
/* Create TX message */
|
||||||
|
IfxCan_Can_Message txMessage;
|
||||||
|
|
||||||
|
/* Set message ID */
|
||||||
|
txMessage.messageId = message->id.id;
|
||||||
|
txMessage.extendedFrame = message->id.is_extended;
|
||||||
|
|
||||||
|
/* Set data */
|
||||||
|
txMessage.dataLengthCode = message->length;
|
||||||
|
for (int i = 0; i < message->length; i++) {
|
||||||
|
txMessage.data[i] = message->data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send message */
|
||||||
|
if (IfxCan_Can_sendMessage(&g_canDriver, &txMessage, &txMessage.messageId)
|
||||||
|
!= IfxCan_Status_ok) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*mailbox = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hal_can_receive_message(CanMessage_t* message) {
|
||||||
|
/* Create RX message */
|
||||||
|
IfxCan_Can_Message rxMessage;
|
||||||
|
|
||||||
|
/* Receive message */
|
||||||
|
if (IfxCan_Can_readMessage(&g_canDriver, &rxMessage, &rxMessage.messageId)
|
||||||
|
!= IfxCan_Status_ok) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy message data */
|
||||||
|
message->id.id = rxMessage.messageId;
|
||||||
|
message->id.is_extended = rxMessage.extendedFrame;
|
||||||
|
message->length = rxMessage.dataLengthCode;
|
||||||
|
|
||||||
|
for (int i = 0; i < message->length; i++) {
|
||||||
|
message->data[i] = rxMessage.data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GPIO HAL Implementation */
|
||||||
|
void hal_gpio_init(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||||
|
/* Configure port pin */
|
||||||
|
IfxPort_setPinMode(&MODULE_P00, pin, IfxPort_Mode_outputPushPullGeneral);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hal_gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||||
|
if (value) {
|
||||||
|
IfxPort_setPinHigh(&MODULE_P00, pin);
|
||||||
|
} else {
|
||||||
|
IfxPort_setPinLow(&MODULE_P00, pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hal_gpio_read(uint8_t port, uint8_t pin) {
|
||||||
|
return IfxPort_getPinState(&MODULE_P00, pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UART HAL Implementation */
|
||||||
|
int hal_uart_init(uint8_t instance, UartConfig_t* config) {
|
||||||
|
/* Create ASCLIN configuration */
|
||||||
|
IfxAsclin_Asc_Config ascConfig;
|
||||||
|
IfxAsclin_Asc_initModuleConfig(&ascConfig, &MODULE_ASCLIN0);
|
||||||
|
|
||||||
|
/* Configure baudrate */
|
||||||
|
ascConfig.baudrate.baudrate = config->baudrate;
|
||||||
|
|
||||||
|
/* Configure pins */
|
||||||
|
ascConfig.pins = &g_ascPins;
|
||||||
|
|
||||||
|
/* Initialize module */
|
||||||
|
IfxAsclin_Asc_initModule(&g_ascDriver, &ascConfig);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* @file s32k14x_config.h
|
||||||
|
* @brief NXP S32K14x specific configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef S32K14X_CONFIG_H
|
||||||
|
#define S32K14X_CONFIG_H
|
||||||
|
|
||||||
|
/* MCU Specific Definitions */
|
||||||
|
#define S32K144
|
||||||
|
#define CPU_FREQUENCY 160000000U /* 160 MHz */
|
||||||
|
#define BUS_FREQUENCY 40000000U /* 40 MHz */
|
||||||
|
#define SLOW_FREQUENCY 10000000U /* 10 MHz */
|
||||||
|
|
||||||
|
/* Peripheral Base Addresses */
|
||||||
|
#define GPIOA_BASE 0x400FF000U
|
||||||
|
#define GPIOB_BASE 0x400FF040U
|
||||||
|
#define GPIOC_BASE 0x400FF080U
|
||||||
|
#define GPIOD_BASE 0x400FF0C0U
|
||||||
|
#define GPIOE_BASE 0x400FF100U
|
||||||
|
|
||||||
|
#define LPUART0_BASE 0x4006A000U
|
||||||
|
#define LPUART1_BASE 0x4006B000U
|
||||||
|
#define LPUART2_BASE 0x4006C000U
|
||||||
|
|
||||||
|
#define LPSPI0_BASE 0x4002C000U
|
||||||
|
#define LPSPI1_BASE 0x4002D000U
|
||||||
|
#define LPSPI2_BASE 0x4002E000U
|
||||||
|
|
||||||
|
#define LPI2C0_BASE 0x40066000U
|
||||||
|
#define LPI2C1_BASE 0x40067000U
|
||||||
|
|
||||||
|
#define ADC0_BASE 0x4003B000U
|
||||||
|
#define ADC1_BASE 0x4003C000U
|
||||||
|
|
||||||
|
#define FTM0_BASE 0x40038000U
|
||||||
|
#define FTM1_BASE 0x40039000U
|
||||||
|
#define FTM2_BASE 0x4003A000U
|
||||||
|
#define FTM3_BASE 0x40026000U
|
||||||
|
#define FTM4_BASE 0x40027000U
|
||||||
|
|
||||||
|
#define FLEXCAN0_BASE 0x40024000U
|
||||||
|
#define FLEXCAN1_BASE 0x40025000U
|
||||||
|
#define FLEXCAN2_BASE 0x4002B000U
|
||||||
|
|
||||||
|
/* Clock Configuration */
|
||||||
|
#define SOSC_FREQUENCY 8000000U /* System oscillator */
|
||||||
|
#define SPLL_FREQUENCY 160000000U /* System PLL */
|
||||||
|
#define FIRC_FREQUENCY 48000000U /* Fast IRC */
|
||||||
|
#define SIRC_FREQUENCY 8000000U /* Slow IRC */
|
||||||
|
|
||||||
|
/* Peripheral Clock Configuration */
|
||||||
|
#define FLEXCAN0_CLOCK 40000000U /* 40 MHz */
|
||||||
|
#define FLEXCAN1_CLOCK 40000000U
|
||||||
|
#define LPUART0_CLOCK 40000000U
|
||||||
|
#define LPSPI0_CLOCK 40000000U
|
||||||
|
#define LPI2C0_CLOCK 40000000U
|
||||||
|
#define ADC0_CLOCK 40000000U
|
||||||
|
#define FTM0_CLOCK 40000000U
|
||||||
|
|
||||||
|
/* NVIC Priority Configuration */
|
||||||
|
#define FLEXCAN0_IRQ_PRIORITY 5
|
||||||
|
#define FLEXCAN1_IRQ_PRIORITY 5
|
||||||
|
#define LPUART0_IRQ_PRIORITY 6
|
||||||
|
#define LPUART1_IRQ_PRIORITY 6
|
||||||
|
#define LPSPI0_IRQ_PRIORITY 7
|
||||||
|
#define LPI2C0_IRQ_PRIORITY 7
|
||||||
|
#define ADC0_IRQ_PRIORITY 8
|
||||||
|
#define FTM0_IRQ_PRIORITY 8
|
||||||
|
|
||||||
|
/* DMA Configuration */
|
||||||
|
#define DMA_CHANNEL_COUNT 16
|
||||||
|
#define DMA_MUX_CHANNEL_COUNT 16
|
||||||
|
|
||||||
|
/* Safety Features */
|
||||||
|
#define ENABLE_CLOCK_MONITORING 1
|
||||||
|
#define ENABLE_MEMORY_PROTECTION 1
|
||||||
|
#define WATCHDOG_TIMEOUT_MS 100
|
||||||
|
|
||||||
|
/* Memory Configuration */
|
||||||
|
#define FLASH_SIZE 0x100000U /* 1 MB */
|
||||||
|
#define RAM_SIZE 0x20000U /* 128 KB */
|
||||||
|
#define EEPROM_SIZE 0x1000U /* 4 KB */
|
||||||
|
|
||||||
|
/* CAN FD Configuration */
|
||||||
|
#define CAN_FD_ENABLED 1
|
||||||
|
#define CAN_FD_MAX_PAYLOAD 64
|
||||||
|
|
||||||
|
#endif /* S32K14X_CONFIG_H */
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
/**
|
||||||
|
* @file s32k14x_hal.c
|
||||||
|
* @brief NXP S32K14x Hardware Abstraction Layer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "s32k14x_config.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "uart_driver.h"
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "i2c_driver.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "S32K144.h"
|
||||||
|
|
||||||
|
/* CAN HAL Implementation */
|
||||||
|
int hal_can_init(uint32_t baudrate, uint8_t frame_type, bool enable_fd) {
|
||||||
|
/* Enable FLEXCAN0 clock */
|
||||||
|
PCC->PCCn[PCC_FlexCAN0_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||||
|
|
||||||
|
/* Configure CAN pins */
|
||||||
|
// PTE4 - CAN0_RX, PTE5 - CAN0_TX
|
||||||
|
PCC->PCCn[PCC_PORTE_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||||
|
PORTE->PCR[4] = PORT_PCR_MUX(5); /* CAN0_RX */
|
||||||
|
PORTE->PCR[5] = PORT_PCR_MUX(5); /* CAN0_TX */
|
||||||
|
|
||||||
|
/* Reset FLEXCAN */
|
||||||
|
CAN0->MCR |= CAN_MCR_SOFTRST_MASK;
|
||||||
|
while (CAN0->MCR & CAN_MCR_SOFTRST_MASK);
|
||||||
|
|
||||||
|
/* Configure for CAN FD if enabled */
|
||||||
|
if (enable_fd) {
|
||||||
|
CAN0->MCR |= CAN_MCR_FDEN_MASK; /* Enable FD */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set baudrate */
|
||||||
|
uint32_t prescaler = BUS_FREQUENCY / (baudrate * 10); /* 10 time quanta */
|
||||||
|
CAN0->CTRL1 = CAN_CTRL1_PRESDIV(prescaler - 1) |
|
||||||
|
CAN_CTRL1_PSEG1(3) |
|
||||||
|
CAN_CTRL1_PSEG2(2) |
|
||||||
|
CAN_CTRL1_PROPSEG(4);
|
||||||
|
|
||||||
|
/* Configure message buffers */
|
||||||
|
CAN0->RXMGMASK = 0x1FFFFFFF; /* Accept all IDs */
|
||||||
|
CAN0->RX14MASK = 0x1FFFFFFF;
|
||||||
|
CAN0->RX15MASK = 0x1FFFFFFF;
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
CAN0->IMASK1 |= CAN_IMASK1_BUF31TO0M_MASK;
|
||||||
|
CAN0->MCR |= CAN_MCR_IRMQ_MASK; /* Individual RX masking */
|
||||||
|
|
||||||
|
/* Normal mode */
|
||||||
|
CAN0->MCR &= ~CAN_MCR_HALT_MASK;
|
||||||
|
while (CAN0->MCR & CAN_MCR_FRZACK_MASK);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hal_can_send_message(const CanMessage_t* message, uint32_t* mailbox) {
|
||||||
|
/* Find free message buffer */
|
||||||
|
*mailbox = 0;
|
||||||
|
while (*mailbox < 32) {
|
||||||
|
if ((CAN0->IFLAG1 & (1 << *mailbox)) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*mailbox)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*mailbox >= 32) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure message buffer */
|
||||||
|
CAN0->RAMn[*mailbox * 4 + 1] = (message->id.id << 18) |
|
||||||
|
(message->id.is_extended ? 1 << 29 : 0) |
|
||||||
|
(message->length << 16);
|
||||||
|
|
||||||
|
/* Copy data */
|
||||||
|
for (int i = 0; i < message->length; i += 4) {
|
||||||
|
uint32_t data = 0;
|
||||||
|
for (int j = 0; j < 4 && (i + j) < message->length; j++) {
|
||||||
|
data |= (message->data[i + j] << (j * 8));
|
||||||
|
}
|
||||||
|
CAN0->RAMn[*mailbox * 4 + 2 + (i / 4)] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable transmission */
|
||||||
|
CAN0->RAMn[*mailbox * 4] = CAN_WORD0_IDE_MASK |
|
||||||
|
CAN_WORD0_SRR_MASK |
|
||||||
|
CAN_WORD0_ESI_MASK |
|
||||||
|
CAN_WORD0_CODE(0xC); /* TX data */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hal_can_receive_message(CanMessage_t* message) {
|
||||||
|
/* Check for received messages */
|
||||||
|
uint32_t iflag = CAN0->IFLAG1;
|
||||||
|
if (iflag == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find received message buffer */
|
||||||
|
uint32_t mailbox = 0;
|
||||||
|
while (mailbox < 32) {
|
||||||
|
if (iflag & (1 << mailbox)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mailbox++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mailbox >= 32) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read message */
|
||||||
|
uint32_t word0 = CAN0->RAMn[mailbox * 4];
|
||||||
|
uint32_t word1 = CAN0->RAMn[mailbox * 4 + 1];
|
||||||
|
|
||||||
|
/* Check if RX buffer */
|
||||||
|
if ((word0 & CAN_WORD0_CODE_MASK) != CAN_WORD0_CODE(0x4)) {
|
||||||
|
CAN0->IFLAG1 = (1 << mailbox); /* Clear flag */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get ID */
|
||||||
|
message->id.is_extended = (word0 & CAN_WORD0_IDE_MASK) != 0;
|
||||||
|
if (message->id.is_extended) {
|
||||||
|
message->id.id = (word0 & CAN_WORD0_ID_MASK) >> 0;
|
||||||
|
} else {
|
||||||
|
message->id.id = (word0 & CAN_WORD0_ID_MASK) >> 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get data length */
|
||||||
|
message->length = (word1 & CAN_WORD1_DLC_MASK) >> 16;
|
||||||
|
|
||||||
|
/* Get data */
|
||||||
|
for (int i = 0; i < message->length; i += 4) {
|
||||||
|
uint32_t data = CAN0->RAMn[mailbox * 4 + 2 + (i / 4)];
|
||||||
|
for (int j = 0; j < 4 && (i + j) < message->length; j++) {
|
||||||
|
message->data[i + j] = (data >> (j * 8)) & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear flag */
|
||||||
|
CAN0->IFLAG1 = (1 << mailbox);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GPIO HAL Implementation */
|
||||||
|
void hal_gpio_init(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||||
|
GPIO_Type* gpio_port = get_gpio_port(port);
|
||||||
|
PORT_Type* port_config = get_port_config(port);
|
||||||
|
|
||||||
|
if (gpio_port == NULL || port_config == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable clock */
|
||||||
|
PCC->PCCn[PCC_PORTA_INDEX + port] |= PCC_PCCn_CGC_MASK;
|
||||||
|
PCC->PCCn[PCC_GPIOA_INDEX + port] |= PCC_PCCn_CGC_MASK;
|
||||||
|
|
||||||
|
/* Configure pin mux */
|
||||||
|
switch (mode) {
|
||||||
|
case GPIO_MODE_INPUT:
|
||||||
|
port_config->PCR[pin] = PORT_PCR_MUX(1);
|
||||||
|
gpio_port->PDDR &= ~(1 << pin);
|
||||||
|
break;
|
||||||
|
case GPIO_MODE_OUTPUT:
|
||||||
|
port_config->PCR[pin] = PORT_PCR_MUX(1);
|
||||||
|
gpio_port->PDDR |= (1 << pin);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hal_gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||||
|
GPIO_Type* gpio_port = get_gpio_port(port);
|
||||||
|
|
||||||
|
if (gpio_port == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
gpio_port->PSOR = (1 << pin);
|
||||||
|
} else {
|
||||||
|
gpio_port->PCOR = (1 << pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hal_gpio_read(uint8_t port, uint8_t pin) {
|
||||||
|
GPIO_Type* gpio_port = get_gpio_port(port);
|
||||||
|
|
||||||
|
if (gpio_port == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (gpio_port->PDIR & (1 << pin)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper functions */
|
||||||
|
static GPIO_Type* get_gpio_port(uint8_t port) {
|
||||||
|
switch (port) {
|
||||||
|
case 0: return PTA;
|
||||||
|
case 1: return PTB;
|
||||||
|
case 2: return PTC;
|
||||||
|
case 3: return PTD;
|
||||||
|
case 4: return PTE;
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PORT_Type* get_port_config(uint8_t port) {
|
||||||
|
switch (port) {
|
||||||
|
case 0: return PORTA;
|
||||||
|
case 1: return PORTB;
|
||||||
|
case 2: return PORTC;
|
||||||
|
case 3: return PORTD;
|
||||||
|
case 4: return PORTE;
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* @file stm32f4xx_config.h
|
||||||
|
* @brief STM32F4 specific configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STM32F4XX_CONFIG_H
|
||||||
|
#define STM32F4XX_CONFIG_H
|
||||||
|
|
||||||
|
/* MCU Specific Definitions */
|
||||||
|
#define STM32F407xx
|
||||||
|
#define HSE_VALUE 8000000U /* External crystal */
|
||||||
|
#define HSI_VALUE 16000000U /* Internal oscillator */
|
||||||
|
#define LSE_VALUE 32768U /* Low speed external */
|
||||||
|
#define LSI_VALUE 32000U /* Low speed internal */
|
||||||
|
|
||||||
|
/* Clock Configuration */
|
||||||
|
#define SYSTEM_CLOCK 168000000U /* 168 MHz */
|
||||||
|
#define AHB_CLOCK 168000000U
|
||||||
|
#define APB1_CLOCK 42000000U /* 42 MHz */
|
||||||
|
#define APB2_CLOCK 84000000U /* 84 MHz */
|
||||||
|
|
||||||
|
/* Peripheral Base Addresses */
|
||||||
|
#define GPIOA_BASE 0x40020000U
|
||||||
|
#define GPIOB_BASE 0x40020400U
|
||||||
|
#define GPIOC_BASE 0x40020800U
|
||||||
|
#define GPIOD_BASE 0x40020C00U
|
||||||
|
#define GPIOE_BASE 0x40021000U
|
||||||
|
#define GPIOF_BASE 0x40021400U
|
||||||
|
#define GPIOG_BASE 0x40021800U
|
||||||
|
#define GPIOH_BASE 0x40021C00U
|
||||||
|
|
||||||
|
#define USART1_BASE 0x40011000U
|
||||||
|
#define USART2_BASE 0x40004400U
|
||||||
|
#define USART3_BASE 0x40004800U
|
||||||
|
#define UART4_BASE 0x40004C00U
|
||||||
|
#define UART5_BASE 0x40005000U
|
||||||
|
#define USART6_BASE 0x40011400U
|
||||||
|
|
||||||
|
#define SPI1_BASE 0x40013000U
|
||||||
|
#define SPI2_BASE 0x40003800U
|
||||||
|
#define SPI3_BASE 0x40003C00U
|
||||||
|
|
||||||
|
#define I2C1_BASE 0x40005400U
|
||||||
|
#define I2C2_BASE 0x40005800U
|
||||||
|
#define I2C3_BASE 0x40005C00U
|
||||||
|
|
||||||
|
#define ADC1_BASE 0x40012000U
|
||||||
|
#define ADC2_BASE 0x40012100U
|
||||||
|
#define ADC3_BASE 0x40012200U
|
||||||
|
|
||||||
|
#define TIM1_BASE 0x40010000U
|
||||||
|
#define TIM2_BASE 0x40000000U
|
||||||
|
#define TIM3_BASE 0x40000400U
|
||||||
|
#define TIM4_BASE 0x40000800U
|
||||||
|
#define TIM5_BASE 0x40000C00U
|
||||||
|
#define TIM8_BASE 0x40010400U
|
||||||
|
#define TIM9_BASE 0x40014000U
|
||||||
|
#define TIM10_BASE 0x40014400U
|
||||||
|
#define TIM11_BASE 0x40014800U
|
||||||
|
#define TIM12_BASE 0x40001800U
|
||||||
|
#define TIM13_BASE 0x40001C00U
|
||||||
|
#define TIM14_BASE 0x40002000U
|
||||||
|
|
||||||
|
#define CAN1_BASE 0x40006400U
|
||||||
|
#define CAN2_BASE 0x40006800U
|
||||||
|
|
||||||
|
/* NVIC Priority Configuration */
|
||||||
|
#define CAN1_IRQ_PRIORITY 5
|
||||||
|
#define CAN2_IRQ_PRIORITY 5
|
||||||
|
#define USART1_IRQ_PRIORITY 6
|
||||||
|
#define USART2_IRQ_PRIORITY 6
|
||||||
|
#define USART3_IRQ_PRIORITY 6
|
||||||
|
#define SPI1_IRQ_PRIORITY 7
|
||||||
|
#define SPI2_IRQ_PRIORITY 7
|
||||||
|
#define I2C1_IRQ_PRIORITY 7
|
||||||
|
#define I2C2_IRQ_PRIORITY 7
|
||||||
|
#define ADC_IRQ_PRIORITY 8
|
||||||
|
#define TIM_IRQ_PRIORITY 8
|
||||||
|
|
||||||
|
/* DMA Configuration */
|
||||||
|
#define DMA1_STREAM0_CHANNEL 0
|
||||||
|
#define DMA1_STREAM1_CHANNEL 1
|
||||||
|
#define DMA1_STREAM2_CHANNEL 2
|
||||||
|
#define DMA1_STREAM3_CHANNEL 3
|
||||||
|
#define DMA1_STREAM4_CHANNEL 4
|
||||||
|
#define DMA1_STREAM5_CHANNEL 5
|
||||||
|
#define DMA1_STREAM6_CHANNEL 6
|
||||||
|
#define DMA1_STREAM7_CHANNEL 7
|
||||||
|
|
||||||
|
#define DMA2_STREAM0_CHANNEL 0
|
||||||
|
#define DMA2_STREAM1_CHANNEL 1
|
||||||
|
#define DMA2_STREAM2_CHANNEL 2
|
||||||
|
#define DMA2_STREAM3_CHANNEL 3
|
||||||
|
#define DMA2_STREAM4_CHANNEL 4
|
||||||
|
#define DMA2_STREAM5_CHANNEL 5
|
||||||
|
#define DMA2_STREAM6_CHANNEL 6
|
||||||
|
#define DMA2_STREAM7_CHANNEL 7
|
||||||
|
|
||||||
|
/* GPIO Alternate Function Mapping */
|
||||||
|
#define GPIO_AF_UART1_TX 7
|
||||||
|
#define GPIO_AF_UART1_RX 7
|
||||||
|
#define GPIO_AF_UART2_TX 7
|
||||||
|
#define GPIO_AF_UART2_RX 7
|
||||||
|
#define GPIO_AF_SPI1_SCK 5
|
||||||
|
#define GPIO_AF_SPI1_MOSI 5
|
||||||
|
#define GPIO_AF_SPI1_MISO 5
|
||||||
|
#define GPIO_AF_I2C1_SCL 4
|
||||||
|
#define GPIO_AF_I2C1_SDA 4
|
||||||
|
#define GPIO_AF_CAN1_TX 9
|
||||||
|
#define GPIO_AF_CAN1_RX 9
|
||||||
|
#define GPIO_AF_TIM1_CH1 1
|
||||||
|
#define GPIO_AF_TIM1_CH2 1
|
||||||
|
#define GPIO_AF_TIM1_CH3 1
|
||||||
|
#define GPIO_AF_TIM1_CH4 1
|
||||||
|
|
||||||
|
/* Memory Configuration */
|
||||||
|
#define FLASH_SIZE 0x100000U /* 1 MB */
|
||||||
|
#define RAM_SIZE 0x20000U /* 128 KB */
|
||||||
|
#define CCM_RAM_SIZE 0x10000U /* 64 KB */
|
||||||
|
|
||||||
|
/* Safety Features */
|
||||||
|
#define ENABLE_CLOCK_SECURITY_SYSTEM 1
|
||||||
|
#define ENABLE_BROWN_OUT_RESET 1
|
||||||
|
#define BROWNOUT_THRESHOLD 0x08 /* 2.7V */
|
||||||
|
|
||||||
|
#endif /* STM32F4XX_CONFIG_H */
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
/**
|
||||||
|
* @file stm32f4xx_hal.c
|
||||||
|
* @brief STM32F4 Hardware Abstraction Layer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stm32f4xx_config.h"
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "uart_driver.h"
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "i2c_driver.h"
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "stm32f4xx.h"
|
||||||
|
|
||||||
|
/* CAN HAL Implementation */
|
||||||
|
int hal_can_init(uint32_t baudrate, uint8_t frame_type, bool enable_fd) {
|
||||||
|
/* Enable CAN clock */
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
|
||||||
|
|
||||||
|
/* Configure CAN GPIO */
|
||||||
|
// PB8 - CAN1_RX, PB9 - CAN1_TX
|
||||||
|
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
|
||||||
|
|
||||||
|
GPIOB->MODER |= (GPIO_MODER_MODER8_1 | GPIO_MODER_MODER9_1);
|
||||||
|
GPIOB->OTYPER &= ~(GPIO_OTYPER_OT_8 | GPIO_OTYPER_OT_9);
|
||||||
|
GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR8 | GPIO_OSPEEDER_OSPEEDR9);
|
||||||
|
GPIOB->AFR[1] |= (9 << 0) | (9 << 4); /* AF9 for CAN */
|
||||||
|
|
||||||
|
/* Reset CAN */
|
||||||
|
CAN1->MCR |= CAN_MCR_RESET;
|
||||||
|
CAN1->MCR &= ~CAN_MCR_RESET;
|
||||||
|
|
||||||
|
/* Exit sleep mode */
|
||||||
|
CAN1->MCR &= ~CAN_MCR_SLEEP;
|
||||||
|
|
||||||
|
/* Set baudrate */
|
||||||
|
uint32_t prescaler = 0;
|
||||||
|
uint32_t time_quantum = 0;
|
||||||
|
|
||||||
|
switch (baudrate) {
|
||||||
|
case 125000:
|
||||||
|
prescaler = 21;
|
||||||
|
time_quantum = 16;
|
||||||
|
break;
|
||||||
|
case 250000:
|
||||||
|
prescaler = 11;
|
||||||
|
time_quantum = 15;
|
||||||
|
break;
|
||||||
|
case 500000:
|
||||||
|
prescaler = 5;
|
||||||
|
time_quantum = 16;
|
||||||
|
break;
|
||||||
|
case 1000000:
|
||||||
|
prescaler = 3;
|
||||||
|
time_quantum = 14;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAN1->BTR = ((prescaler - 1) << 20) |
|
||||||
|
((time_quantum - 1) << 16) |
|
||||||
|
(3 << 20) | /* SJW = 4 */
|
||||||
|
(7 << 16); /* BS1 = 8 */
|
||||||
|
|
||||||
|
/* Configure filters */
|
||||||
|
CAN1->FMR |= CAN_FMR_FINIT;
|
||||||
|
CAN1->FM1R &= ~CAN_FM1R_FBM0; /* Mask mode for filter 0 */
|
||||||
|
CAN1->FS1R |= CAN_FS1R_FSC0; /* 32-bit scale */
|
||||||
|
CAN1->FFA1R &= ~CAN_FFA1R_FFA0; /* FIFO 0 */
|
||||||
|
CAN1->FMR &= ~CAN_FMR_FINIT;
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
CAN1->IER |= CAN_IER_FMPIE0 | /* FIFO 0 message pending */
|
||||||
|
CAN_IER_TMEIE | /* Transmit mailbox empty */
|
||||||
|
CAN_IER_BOFIE | /* Bus-off */
|
||||||
|
CAN_IER_ERRIE; /* Error */
|
||||||
|
|
||||||
|
/* Normal mode */
|
||||||
|
CAN1->MCR &= ~CAN_MCR_SLEEP;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hal_can_send_message(const CanMessage_t* message, uint32_t* mailbox) {
|
||||||
|
/* Check for free mailbox */
|
||||||
|
if ((CAN1->TSR & CAN_TSR_TME0) != 0) {
|
||||||
|
*mailbox = 0;
|
||||||
|
} else if ((CAN1->TSR & CAN_TSR_TME1) != 0) {
|
||||||
|
*mailbox = 1;
|
||||||
|
} else if ((CAN1->TSR & CAN_TSR_TME2) != 0) {
|
||||||
|
*mailbox = 2;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure mailbox */
|
||||||
|
CAN_TxMailBox_TypeDef* tx_mailbox = &CAN1->sTxMailBox[*mailbox];
|
||||||
|
|
||||||
|
/* Set ID */
|
||||||
|
if (message->id.is_extended) {
|
||||||
|
tx_mailbox->TIR = (message->id.id << 3) | CAN_TI0R_IDE;
|
||||||
|
} else {
|
||||||
|
tx_mailbox->TIR = (message->id.id << 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set data length and data */
|
||||||
|
tx_mailbox->TDTR = message->length;
|
||||||
|
|
||||||
|
/* Copy data */
|
||||||
|
uint32_t data[2] = {0, 0};
|
||||||
|
for (int i = 0; i < message->length; i++) {
|
||||||
|
if (i < 4) {
|
||||||
|
data[0] |= (message->data[i] << (i * 8));
|
||||||
|
} else {
|
||||||
|
data[1] |= (message->data[i] << ((i - 4) * 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx_mailbox->TDLR = data[0];
|
||||||
|
tx_mailbox->TDHR = data[1];
|
||||||
|
|
||||||
|
/* Request transmission */
|
||||||
|
tx_mailbox->TIR |= CAN_TI0R_TXRQ;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hal_can_receive_message(CanMessage_t* message) {
|
||||||
|
/* Check if message available */
|
||||||
|
if ((CAN1->RF0R & CAN_RF0R_FMP0) == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get message */
|
||||||
|
CAN_FIFOMailBox_TypeDef* rx_mailbox = &CAN1->sFIFOMailBox[0];
|
||||||
|
|
||||||
|
/* Get ID */
|
||||||
|
if (rx_mailbox->RIR & CAN_RI0R_IDE) {
|
||||||
|
message->id.is_extended = true;
|
||||||
|
message->id.id = (rx_mailbox->RIR >> 3) & 0x1FFFFFFF;
|
||||||
|
} else {
|
||||||
|
message->id.is_extended = false;
|
||||||
|
message->id.id = (rx_mailbox->RIR >> 21) & 0x7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get data length */
|
||||||
|
message->length = rx_mailbox->RDTR & CAN_RDT0R_DLC;
|
||||||
|
|
||||||
|
/* Get data */
|
||||||
|
uint32_t data_low = rx_mailbox->RDLR;
|
||||||
|
uint32_t data_high = rx_mailbox->RDHR;
|
||||||
|
|
||||||
|
for (int i = 0; i < message->length; i++) {
|
||||||
|
if (i < 4) {
|
||||||
|
message->data[i] = (data_low >> (i * 8)) & 0xFF;
|
||||||
|
} else {
|
||||||
|
message->data[i] = (data_high >> ((i - 4) * 8)) & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release FIFO */
|
||||||
|
CAN1->RF0R |= CAN_RF0R_RFOM0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GPIO HAL Implementation */
|
||||||
|
void hal_gpio_init(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||||
|
GPIO_TypeDef* gpio_port = get_gpio_port(port);
|
||||||
|
|
||||||
|
if (gpio_port == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable GPIO clock */
|
||||||
|
RCC->AHB1ENR |= (1 << port);
|
||||||
|
|
||||||
|
/* Configure mode */
|
||||||
|
uint32_t moder_value = 0;
|
||||||
|
switch (mode) {
|
||||||
|
case GPIO_MODE_INPUT:
|
||||||
|
moder_value = 0x00;
|
||||||
|
break;
|
||||||
|
case GPIO_MODE_OUTPUT:
|
||||||
|
moder_value = 0x01;
|
||||||
|
break;
|
||||||
|
case GPIO_MODE_ALTERNATE:
|
||||||
|
moder_value = 0x02;
|
||||||
|
break;
|
||||||
|
case GPIO_MODE_ANALOG:
|
||||||
|
moder_value = 0x03;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_port->MODER &= ~(0x03 << (pin * 2));
|
||||||
|
gpio_port->MODER |= (moder_value << (pin * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hal_gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||||
|
GPIO_TypeDef* gpio_port = get_gpio_port(port);
|
||||||
|
|
||||||
|
if (gpio_port == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
gpio_port->BSRR = (1 << pin);
|
||||||
|
} else {
|
||||||
|
gpio_port->BSRR = (1 << (pin + 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hal_gpio_read(uint8_t port, uint8_t pin) {
|
||||||
|
GPIO_TypeDef* gpio_port = get_gpio_port(port);
|
||||||
|
|
||||||
|
if (gpio_port == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (gpio_port->IDR & (1 << pin)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UART HAL Implementation */
|
||||||
|
int hal_uart_init(uint8_t instance, UartConfig_t* config) {
|
||||||
|
USART_TypeDef* uart = get_uart_instance(instance);
|
||||||
|
|
||||||
|
if (uart == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable clock */
|
||||||
|
if (instance == 0) {
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
|
||||||
|
} else {
|
||||||
|
RCC->APB1ENR |= (RCC_APB1ENR_USART2EN << (instance - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure baudrate */
|
||||||
|
uint32_t clock = (instance == 0 || instance == 5) ? APB2_CLOCK : APB1_CLOCK;
|
||||||
|
uart->BRR = clock / config->baudrate;
|
||||||
|
|
||||||
|
/* Configure control registers */
|
||||||
|
uart->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
|
||||||
|
|
||||||
|
if (config->data_bits == UART_DATA_BITS_9) {
|
||||||
|
uart->CR1 |= USART_CR1_M;
|
||||||
|
}
|
||||||
|
|
||||||
|
uart->CR2 = 0;
|
||||||
|
if (config->stop_bits == UART_STOP_BITS_2) {
|
||||||
|
uart->CR2 |= USART_CR2_STOP_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
if (config->enable_rx) {
|
||||||
|
uart->CR1 |= USART_CR1_RXNEIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper functions */
|
||||||
|
static GPIO_TypeDef* get_gpio_port(uint8_t port) {
|
||||||
|
switch (port) {
|
||||||
|
case 0: return GPIOA;
|
||||||
|
case 1: return GPIOB;
|
||||||
|
case 2: return GPIOC;
|
||||||
|
case 3: return GPIOD;
|
||||||
|
case 4: return GPIOE;
|
||||||
|
case 5: return GPIOF;
|
||||||
|
case 6: return GPIOG;
|
||||||
|
case 7: return GPIOH;
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static USART_TypeDef* get_uart_instance(uint8_t instance) {
|
||||||
|
switch (instance) {
|
||||||
|
case 0: return USART1;
|
||||||
|
case 1: return USART2;
|
||||||
|
case 2: return USART3;
|
||||||
|
case 3: return UART4;
|
||||||
|
case 4: return UART5;
|
||||||
|
case 5: return USART6;
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
/**
|
||||||
|
* @file adc_driver.c
|
||||||
|
* @brief ADC driver implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "adc_driver.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ADC Driver State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
AdcConfig_t config;
|
||||||
|
AdcStatistics_t statistics;
|
||||||
|
uint16_t conversion_buffer[ADC_MAX_CHANNELS];
|
||||||
|
uint8_t current_channel;
|
||||||
|
bool conversion_active;
|
||||||
|
bool dma_active;
|
||||||
|
uint16_t* dma_buffer;
|
||||||
|
uint16_t dma_length;
|
||||||
|
uint16_t dma_index;
|
||||||
|
Semaphore_t conversion_semaphore;
|
||||||
|
Mutex_t conversion_mutex;
|
||||||
|
} AdcDriverState_t;
|
||||||
|
|
||||||
|
static AdcDriverState_t adc_drivers[ADC_MAX_INSTANCES];
|
||||||
|
|
||||||
|
/* Initialize ADC Driver */
|
||||||
|
KernelStatus_t adc_init(uint8_t instance, AdcConfig_t* config) {
|
||||||
|
if (instance >= ADC_MAX_INSTANCES || config == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||||
|
|
||||||
|
if (driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy configuration */
|
||||||
|
memcpy(&driver->config, config, sizeof(AdcConfig_t));
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
memset(&driver->statistics, 0, sizeof(AdcStatistics_t));
|
||||||
|
driver->current_channel = 0;
|
||||||
|
driver->conversion_active = false;
|
||||||
|
driver->dma_active = false;
|
||||||
|
driver->dma_buffer = NULL;
|
||||||
|
driver->dma_length = 0;
|
||||||
|
driver->dma_index = 0;
|
||||||
|
|
||||||
|
/* Create synchronization primitives */
|
||||||
|
semaphore_create(&driver->conversion_semaphore, SEMAPHORE_BINARY, 0, 1);
|
||||||
|
mutex_create(&driver->conversion_mutex, false);
|
||||||
|
|
||||||
|
/* Initialize ADC hardware */
|
||||||
|
if (hal_adc_init(instance, config) != 0) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
hal_adc_enable_interrupts(instance);
|
||||||
|
|
||||||
|
driver->initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start ADC Conversion */
|
||||||
|
KernelStatus_t adc_start_conversion(uint8_t instance) {
|
||||||
|
if (instance >= ADC_MAX_INSTANCES) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver->conversion_active) {
|
||||||
|
return KERNEL_RESOURCE_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset conversion state */
|
||||||
|
driver->current_channel = 0;
|
||||||
|
driver->conversion_active = true;
|
||||||
|
|
||||||
|
/* Start conversion */
|
||||||
|
hal_adc_start_conversion(instance);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop ADC Conversion */
|
||||||
|
KernelStatus_t adc_stop_conversion(uint8_t instance) {
|
||||||
|
if (instance >= ADC_MAX_INSTANCES) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_adc_stop_conversion(instance);
|
||||||
|
driver->conversion_active = false;
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read ADC Channel */
|
||||||
|
KernelStatus_t adc_read_channel(uint8_t instance, uint8_t channel, uint16_t* value,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
if (instance >= ADC_MAX_INSTANCES || value == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock conversion mutex */
|
||||||
|
if (mutex_lock(&driver->conversion_mutex, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure single channel conversion */
|
||||||
|
hal_adc_configure_channel(instance, channel);
|
||||||
|
|
||||||
|
/* Start conversion */
|
||||||
|
hal_adc_start_single_conversion(instance, channel);
|
||||||
|
|
||||||
|
/* Wait for conversion complete */
|
||||||
|
if (semaphore_take(&driver->conversion_semaphore, timeout_ms) != KERNEL_OK) {
|
||||||
|
mutex_unlock(&driver->conversion_mutex);
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read value */
|
||||||
|
*value = hal_adc_read_value(instance, channel);
|
||||||
|
|
||||||
|
/* Update statistics */
|
||||||
|
driver->statistics.conversions_completed++;
|
||||||
|
|
||||||
|
/* Unlock */
|
||||||
|
mutex_unlock(&driver->conversion_mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read Multiple ADC Channels */
|
||||||
|
KernelStatus_t adc_read_channels(uint8_t instance, uint16_t* values, uint8_t count,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
if (instance >= ADC_MAX_INSTANCES || values == NULL || count == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start conversion */
|
||||||
|
if (adc_start_conversion(instance) != KERNEL_OK) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for all channels */
|
||||||
|
if (semaphore_take(&driver->conversion_semaphore, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy values */
|
||||||
|
memcpy(values, driver->conversion_buffer, count * sizeof(uint16_t));
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start DMA Conversion */
|
||||||
|
KernelStatus_t adc_start_dma(uint8_t instance, uint16_t* buffer, uint16_t length) {
|
||||||
|
if (instance >= ADC_MAX_INSTANCES || buffer == NULL || length == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver->dma_active) {
|
||||||
|
return KERNEL_RESOURCE_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure DMA */
|
||||||
|
driver->dma_buffer = buffer;
|
||||||
|
driver->dma_length = length;
|
||||||
|
driver->dma_index = 0;
|
||||||
|
driver->dma_active = true;
|
||||||
|
|
||||||
|
/* Start DMA conversion */
|
||||||
|
hal_adc_start_dma(instance, buffer, length);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ADC Interrupt Handler */
|
||||||
|
void adc_process_interrupt(uint8_t instance) {
|
||||||
|
if (instance >= ADC_MAX_INSTANCES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t interrupt_status = hal_adc_get_interrupt_status(instance);
|
||||||
|
|
||||||
|
/* Handle conversion complete */
|
||||||
|
if (interrupt_status & ADC_INTERRUPT_CONVERSION_COMPLETE) {
|
||||||
|
if (driver->conversion_active) {
|
||||||
|
/* Store converted value */
|
||||||
|
uint16_t value = hal_adc_read_current_value(instance);
|
||||||
|
|
||||||
|
if (driver->current_channel < ADC_MAX_CHANNELS) {
|
||||||
|
driver->conversion_buffer[driver->current_channel] = value;
|
||||||
|
driver->current_channel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if all channels converted */
|
||||||
|
if (driver->current_channel >= driver->config.channel_count) {
|
||||||
|
driver->conversion_active = false;
|
||||||
|
|
||||||
|
/* Call callback */
|
||||||
|
if (driver->config.conversion_complete_callback != NULL) {
|
||||||
|
driver->config.conversion_complete_callback(
|
||||||
|
driver->conversion_buffer,
|
||||||
|
driver->config.channel_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal conversion complete */
|
||||||
|
semaphore_give(&driver->conversion_semaphore);
|
||||||
|
} else {
|
||||||
|
/* Start next channel conversion */
|
||||||
|
hal_adc_start_channel_conversion(instance,
|
||||||
|
driver->config.channels[driver->current_channel].channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle DMA complete */
|
||||||
|
if (interrupt_status & ADC_INTERRUPT_DMA_COMPLETE) {
|
||||||
|
driver->dma_active = false;
|
||||||
|
driver->statistics.dma_transfers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle watchdog */
|
||||||
|
if (interrupt_status & ADC_INTERRUPT_WATCHDOG) {
|
||||||
|
driver->statistics.watchdog_events++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle errors */
|
||||||
|
if (interrupt_status & ADC_INTERRUPT_ERROR) {
|
||||||
|
driver->statistics.conversions_failed++;
|
||||||
|
driver->statistics.overrun_errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear interrupt flags */
|
||||||
|
hal_adc_clear_interrupts(instance, interrupt_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert ADC Value to Voltage */
|
||||||
|
float adc_convert_to_voltage(uint16_t adc_value, AdcResolution_t resolution,
|
||||||
|
float reference_voltage) {
|
||||||
|
uint32_t max_value = (1 << resolution) - 1;
|
||||||
|
return ((float)adc_value / (float)max_value) * reference_voltage;
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
/**
|
||||||
|
* @file can_driver.c
|
||||||
|
* @brief CAN driver implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "can_driver.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* CAN Driver State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
CanConfig_t config;
|
||||||
|
CanStatistics_t statistics;
|
||||||
|
CanMessage_t rx_fifo[CAN_MAX_RX_FIFO_DEPTH];
|
||||||
|
uint8_t rx_head;
|
||||||
|
uint8_t rx_tail;
|
||||||
|
uint8_t rx_count;
|
||||||
|
Semaphore_t rx_semaphore;
|
||||||
|
Semaphore_t tx_semaphore;
|
||||||
|
Mutex_t tx_mutex;
|
||||||
|
bool bus_off;
|
||||||
|
} CanDriverState_t;
|
||||||
|
|
||||||
|
static CanDriverState_t can_driver;
|
||||||
|
|
||||||
|
/* Initialize CAN Driver */
|
||||||
|
KernelStatus_t can_init(CanConfig_t* config) {
|
||||||
|
if (config == NULL || can_driver.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy configuration */
|
||||||
|
memcpy(&can_driver.config, config, sizeof(CanConfig_t));
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
memset(&can_driver.statistics, 0, sizeof(CanStatistics_t));
|
||||||
|
can_driver.rx_head = 0;
|
||||||
|
can_driver.rx_tail = 0;
|
||||||
|
can_driver.rx_count = 0;
|
||||||
|
can_driver.bus_off = false;
|
||||||
|
|
||||||
|
/* Create synchronization primitives */
|
||||||
|
semaphore_create(&can_driver.rx_semaphore, SEMAPHORE_COUNTING, 0,
|
||||||
|
CAN_MAX_RX_FIFO_DEPTH);
|
||||||
|
semaphore_create(&can_driver.tx_semaphore, SEMAPHORE_COUNTING,
|
||||||
|
CAN_MAX_TX_MAILBOXES, CAN_MAX_TX_MAILBOXES);
|
||||||
|
mutex_create(&can_driver.tx_mutex, false);
|
||||||
|
|
||||||
|
/* Configure CAN hardware */
|
||||||
|
if (hal_can_init(&config->nominal_baudrate, config->frame_type,
|
||||||
|
config->enable_fd) != 0) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure filters */
|
||||||
|
for (uint8_t i = 0; i < config->filter_count; i++) {
|
||||||
|
if (config->filters[i].enable) {
|
||||||
|
hal_can_configure_filter(&config->filters[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable CAN interrupts */
|
||||||
|
hal_can_enable_interrupts();
|
||||||
|
|
||||||
|
can_driver.initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send CAN Message */
|
||||||
|
KernelStatus_t can_send_message(const CanMessage_t* message, uint32_t timeout_ms) {
|
||||||
|
if (!can_driver.initialized || message == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_driver.bus_off) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take TX semaphore with timeout */
|
||||||
|
if (semaphore_take(&can_driver.tx_semaphore, timeout_ms) != KERNEL_OK) {
|
||||||
|
can_driver.statistics.tx_overflow++;
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock TX mutex */
|
||||||
|
if (mutex_lock(&can_driver.tx_mutex, timeout_ms) != KERNEL_OK) {
|
||||||
|
semaphore_give(&can_driver.tx_semaphore);
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send message via hardware */
|
||||||
|
uint32_t mailbox;
|
||||||
|
if (hal_can_send_message(message, &mailbox) != 0) {
|
||||||
|
mutex_unlock(&can_driver.tx_mutex);
|
||||||
|
semaphore_give(&can_driver.tx_semaphore);
|
||||||
|
can_driver.statistics.tx_errors++;
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update statistics */
|
||||||
|
can_driver.statistics.tx_messages++;
|
||||||
|
|
||||||
|
/* Unlock TX mutex */
|
||||||
|
mutex_unlock(&can_driver.tx_mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive CAN Message */
|
||||||
|
KernelStatus_t can_receive_message(CanMessage_t* message, uint32_t timeout_ms) {
|
||||||
|
if (!can_driver.initialized || message == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for message */
|
||||||
|
if (semaphore_take(&can_driver.rx_semaphore, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get message from FIFO */
|
||||||
|
critical_section_enter();
|
||||||
|
|
||||||
|
memcpy(message, &can_driver.rx_fifo[can_driver.rx_head], sizeof(CanMessage_t));
|
||||||
|
can_driver.rx_head = (can_driver.rx_head + 1) % CAN_MAX_RX_FIFO_DEPTH;
|
||||||
|
can_driver.rx_count--;
|
||||||
|
|
||||||
|
critical_section_exit();
|
||||||
|
|
||||||
|
can_driver.statistics.rx_messages++;
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CAN Interrupt Handler */
|
||||||
|
void can_process_interrupt(void) {
|
||||||
|
uint32_t interrupt_status = hal_can_get_interrupt_status();
|
||||||
|
|
||||||
|
/* Check for received messages */
|
||||||
|
if (interrupt_status & CAN_INTERRUPT_RX) {
|
||||||
|
CanMessage_t message;
|
||||||
|
|
||||||
|
while (hal_can_receive_message(&message) == 0) {
|
||||||
|
/* Add to RX FIFO */
|
||||||
|
critical_section_enter();
|
||||||
|
|
||||||
|
if (can_driver.rx_count < CAN_MAX_RX_FIFO_DEPTH) {
|
||||||
|
memcpy(&can_driver.rx_fifo[can_driver.rx_tail], &message,
|
||||||
|
sizeof(CanMessage_t));
|
||||||
|
can_driver.rx_tail = (can_driver.rx_tail + 1) % CAN_MAX_RX_FIFO_DEPTH;
|
||||||
|
can_driver.rx_count++;
|
||||||
|
|
||||||
|
/* Signal message available */
|
||||||
|
semaphore_give(&can_driver.rx_semaphore);
|
||||||
|
|
||||||
|
/* Call callback if registered */
|
||||||
|
if (can_driver.config.rx_callback != NULL) {
|
||||||
|
can_driver.config.rx_callback(&message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
can_driver.statistics.rx_overflow++;
|
||||||
|
}
|
||||||
|
|
||||||
|
critical_section_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for transmit complete */
|
||||||
|
if (interrupt_status & CAN_INTERRUPT_TX) {
|
||||||
|
uint32_t mailbox = hal_can_get_tx_mailbox();
|
||||||
|
|
||||||
|
/* Release TX semaphore */
|
||||||
|
semaphore_give(&can_driver.tx_semaphore);
|
||||||
|
|
||||||
|
/* Call callback if registered */
|
||||||
|
if (can_driver.config.tx_callback != NULL) {
|
||||||
|
can_driver.config.tx_callback(mailbox, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for errors */
|
||||||
|
if (interrupt_status & CAN_INTERRUPT_ERROR) {
|
||||||
|
uint32_t error_code = hal_can_get_error_status();
|
||||||
|
|
||||||
|
can_driver.statistics.rx_errors++;
|
||||||
|
can_driver.statistics.tx_errors++;
|
||||||
|
|
||||||
|
/* Check for bus-off */
|
||||||
|
if (error_code & CAN_ERROR_BUS_OFF) {
|
||||||
|
can_driver.bus_off = true;
|
||||||
|
can_driver.statistics.bus_off_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call error callback */
|
||||||
|
if (can_driver.config.error_callback != NULL) {
|
||||||
|
can_driver.config.error_callback(error_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recover from Bus-Off */
|
||||||
|
KernelStatus_t can_recover_bus_off(void) {
|
||||||
|
if (!can_driver.initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset CAN controller */
|
||||||
|
hal_can_reset();
|
||||||
|
|
||||||
|
/* Reconfigure */
|
||||||
|
hal_can_init(&can_driver.config.nominal_baudrate,
|
||||||
|
can_driver.config.frame_type, can_driver.config.enable_fd);
|
||||||
|
|
||||||
|
/* Clear bus-off flag */
|
||||||
|
can_driver.bus_off = false;
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get CAN Statistics */
|
||||||
|
KernelStatus_t can_get_statistics(CanStatistics_t* stats) {
|
||||||
|
if (stats == NULL) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(stats, &can_driver.statistics, sizeof(CanStatistics_t));
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* @file gpio_driver.c
|
||||||
|
* @brief GPIO driver implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gpio_driver.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* GPIO Driver State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
GpioPinConfig_t pins[GPIO_MAX_PORTS][GPIO_MAX_PINS_PER_PORT];
|
||||||
|
GpioInterruptConfig_t interrupts[GPIO_MAX_INTERRUPTS];
|
||||||
|
uint8_t interrupt_count;
|
||||||
|
Mutex_t mutex;
|
||||||
|
} GpioDriverState_t;
|
||||||
|
|
||||||
|
static GpioDriverState_t gpio_driver;
|
||||||
|
|
||||||
|
/* Initialize GPIO Driver */
|
||||||
|
KernelStatus_t gpio_init(const GpioPinConfig_t* config) {
|
||||||
|
if (config == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gpio_driver.initialized) {
|
||||||
|
memset(&gpio_driver, 0, sizeof(GpioDriverState_t));
|
||||||
|
mutex_create(&gpio_driver.mutex, false);
|
||||||
|
gpio_driver.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate parameters */
|
||||||
|
if (config->port >= GPIO_MAX_PORTS || config->pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store pin configuration */
|
||||||
|
gpio_driver.pins[config->port][config->pin] = *config;
|
||||||
|
|
||||||
|
/* Configure GPIO hardware */
|
||||||
|
hal_gpio_init(config->port, config->pin, config->mode);
|
||||||
|
|
||||||
|
/* Configure output type */
|
||||||
|
if (config->mode == GPIO_MODE_OUTPUT || config->mode == GPIO_MODE_ALTERNATE) {
|
||||||
|
hal_gpio_set_output_type(config->port, config->pin, config->output_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure pull */
|
||||||
|
hal_gpio_set_pull(config->port, config->pin, config->pull);
|
||||||
|
|
||||||
|
/* Configure speed */
|
||||||
|
hal_gpio_set_speed(config->port, config->pin, config->speed);
|
||||||
|
|
||||||
|
/* Configure alternate function */
|
||||||
|
if (config->mode == GPIO_MODE_ALTERNATE) {
|
||||||
|
hal_gpio_set_alternate_function(config->port, config->pin,
|
||||||
|
config->alternate_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set GPIO Mode */
|
||||||
|
KernelStatus_t gpio_set_mode(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||||
|
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_driver.pins[port][pin].mode = mode;
|
||||||
|
hal_gpio_init(port, pin, mode);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write GPIO Pin */
|
||||||
|
KernelStatus_t gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||||
|
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpio_driver.pins[port][pin].mode != GPIO_MODE_OUTPUT) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_gpio_write(port, pin, value);
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write GPIO Port */
|
||||||
|
KernelStatus_t gpio_write_port(uint8_t port, uint16_t value) {
|
||||||
|
if (port >= GPIO_MAX_PORTS) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_gpio_write_port(port, value);
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read GPIO Pin */
|
||||||
|
bool gpio_read(uint8_t port, uint8_t pin) {
|
||||||
|
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hal_gpio_read(port, pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read GPIO Port */
|
||||||
|
uint16_t gpio_read_port(uint8_t port) {
|
||||||
|
if (port >= GPIO_MAX_PORTS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hal_gpio_read_port(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle GPIO Pin */
|
||||||
|
KernelStatus_t gpio_toggle(uint8_t port, uint8_t pin) {
|
||||||
|
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_gpio_toggle(port, pin);
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure GPIO Interrupt */
|
||||||
|
KernelStatus_t gpio_configure_interrupt(const GpioInterruptConfig_t* config) {
|
||||||
|
if (config == NULL || config->callback == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->port >= GPIO_MAX_PORTS || config->pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpio_driver.interrupt_count >= GPIO_MAX_INTERRUPTS) {
|
||||||
|
return KERNEL_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store interrupt configuration */
|
||||||
|
gpio_driver.interrupts[gpio_driver.interrupt_count] = *config;
|
||||||
|
gpio_driver.interrupt_count++;
|
||||||
|
|
||||||
|
/* Configure hardware interrupt */
|
||||||
|
hal_gpio_configure_interrupt(config->port, config->pin, config->trigger);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GPIO Interrupt Handler */
|
||||||
|
void gpio_process_interrupt(uint8_t port) {
|
||||||
|
if (port >= GPIO_MAX_PORTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get interrupt status */
|
||||||
|
uint16_t interrupt_status = hal_gpio_get_interrupt_status(port);
|
||||||
|
|
||||||
|
/* Process each interrupt */
|
||||||
|
for (uint8_t pin = 0; pin < GPIO_MAX_PINS_PER_PORT; pin++) {
|
||||||
|
if (interrupt_status & (1 << pin)) {
|
||||||
|
/* Find matching interrupt configuration */
|
||||||
|
for (uint8_t i = 0; i < gpio_driver.interrupt_count; i++) {
|
||||||
|
if (gpio_driver.interrupts[i].port == port &&
|
||||||
|
gpio_driver.interrupts[i].pin == pin) {
|
||||||
|
/* Call callback */
|
||||||
|
gpio_driver.interrupts[i].callback(port, pin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear interrupt flag */
|
||||||
|
hal_gpio_clear_interrupt(port, pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
/**
|
||||||
|
* @file i2c_driver.c
|
||||||
|
* @brief I2C driver implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "i2c_driver.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* I2C Driver State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
I2cConfig_t config;
|
||||||
|
I2cStatistics_t statistics;
|
||||||
|
I2cMessage_t current_message;
|
||||||
|
uint16_t transfer_index;
|
||||||
|
bool transfer_active;
|
||||||
|
Semaphore_t transfer_semaphore;
|
||||||
|
Mutex_t transfer_mutex;
|
||||||
|
void* user_data;
|
||||||
|
} I2cDriverState_t;
|
||||||
|
|
||||||
|
static I2cDriverState_t i2c_drivers[I2C_MAX_INSTANCES];
|
||||||
|
|
||||||
|
/* Initialize I2C Driver */
|
||||||
|
KernelStatus_t i2c_init(uint8_t instance, I2cConfig_t* config) {
|
||||||
|
if (instance >= I2C_MAX_INSTANCES || config == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||||
|
|
||||||
|
if (driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy configuration */
|
||||||
|
memcpy(&driver->config, config, sizeof(I2cConfig_t));
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
memset(&driver->statistics, 0, sizeof(I2cStatistics_t));
|
||||||
|
driver->transfer_index = 0;
|
||||||
|
driver->transfer_active = false;
|
||||||
|
|
||||||
|
/* Create synchronization primitives */
|
||||||
|
semaphore_create(&driver->transfer_semaphore, SEMAPHORE_BINARY, 1, 1);
|
||||||
|
mutex_create(&driver->transfer_mutex, false);
|
||||||
|
|
||||||
|
/* Initialize I2C hardware */
|
||||||
|
if (hal_i2c_init(instance, config) != 0) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
hal_i2c_enable_interrupts(instance);
|
||||||
|
|
||||||
|
driver->initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2C Transfer */
|
||||||
|
KernelStatus_t i2c_transfer(uint8_t instance, const I2cMessage_t* message,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
if (instance >= I2C_MAX_INSTANCES || message == NULL ||
|
||||||
|
message->data == NULL || message->length == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver->transfer_active) {
|
||||||
|
return KERNEL_RESOURCE_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take transfer semaphore */
|
||||||
|
if (semaphore_take(&driver->transfer_semaphore, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock transfer mutex */
|
||||||
|
if (mutex_lock(&driver->transfer_mutex, timeout_ms) != KERNEL_OK) {
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up transfer */
|
||||||
|
memcpy(&driver->current_message, message, sizeof(I2cMessage_t));
|
||||||
|
driver->transfer_index = 0;
|
||||||
|
driver->transfer_active = true;
|
||||||
|
|
||||||
|
/* Start transfer */
|
||||||
|
if (message->direction == I2C_DIRECTION_WRITE) {
|
||||||
|
hal_i2c_start_write(instance, message->slave_address);
|
||||||
|
} else {
|
||||||
|
hal_i2c_start_read(instance, message->slave_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for completion */
|
||||||
|
if (timeout_ms > 0) {
|
||||||
|
TickType_t start_tick = kernel_get_tick_count();
|
||||||
|
|
||||||
|
while (driver->transfer_active) {
|
||||||
|
if ((kernel_get_tick_count() - start_tick) > timeout_ms) {
|
||||||
|
/* Timeout - abort transfer */
|
||||||
|
hal_i2c_abort_transfer(instance);
|
||||||
|
driver->transfer_active = false;
|
||||||
|
|
||||||
|
mutex_unlock(&driver->transfer_mutex);
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
driver->statistics.timeout_errors++;
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
kernel_delay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update statistics */
|
||||||
|
driver->statistics.transfers_completed++;
|
||||||
|
driver->statistics.bytes_transferred += message->length;
|
||||||
|
|
||||||
|
/* Unlock */
|
||||||
|
mutex_unlock(&driver->transfer_mutex);
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2C Write */
|
||||||
|
KernelStatus_t i2c_write(uint8_t instance, uint16_t slave_address,
|
||||||
|
const uint8_t* data, uint16_t length, uint32_t timeout_ms) {
|
||||||
|
I2cMessage_t message = {
|
||||||
|
.slave_address = slave_address,
|
||||||
|
.direction = I2C_DIRECTION_WRITE,
|
||||||
|
.data = (uint8_t*)data,
|
||||||
|
.length = length,
|
||||||
|
.generate_stop = true,
|
||||||
|
.generate_restart = false
|
||||||
|
};
|
||||||
|
|
||||||
|
return i2c_transfer(instance, &message, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2C Read */
|
||||||
|
KernelStatus_t i2c_read(uint8_t instance, uint16_t slave_address,
|
||||||
|
uint8_t* data, uint16_t length, uint32_t timeout_ms) {
|
||||||
|
I2cMessage_t message = {
|
||||||
|
.slave_address = slave_address,
|
||||||
|
.direction = I2C_DIRECTION_READ,
|
||||||
|
.data = data,
|
||||||
|
.length = length,
|
||||||
|
.generate_stop = true,
|
||||||
|
.generate_restart = false
|
||||||
|
};
|
||||||
|
|
||||||
|
return i2c_transfer(instance, &message, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2C Write/Read */
|
||||||
|
KernelStatus_t i2c_write_read(uint8_t instance, uint16_t slave_address,
|
||||||
|
const uint8_t* tx_data, uint16_t tx_length,
|
||||||
|
uint8_t* rx_data, uint16_t rx_length,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
/* First write */
|
||||||
|
I2cMessage_t write_message = {
|
||||||
|
.slave_address = slave_address,
|
||||||
|
.direction = I2C_DIRECTION_WRITE,
|
||||||
|
.data = (uint8_t*)tx_data,
|
||||||
|
.length = tx_length,
|
||||||
|
.generate_stop = false,
|
||||||
|
.generate_restart = true
|
||||||
|
};
|
||||||
|
|
||||||
|
KernelStatus_t status = i2c_transfer(instance, &write_message, timeout_ms);
|
||||||
|
if (status != KERNEL_OK) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then read */
|
||||||
|
I2cMessage_t read_message = {
|
||||||
|
.slave_address = slave_address,
|
||||||
|
.direction = I2C_DIRECTION_READ,
|
||||||
|
.data = rx_data,
|
||||||
|
.length = rx_length,
|
||||||
|
.generate_stop = true,
|
||||||
|
.generate_restart = false
|
||||||
|
};
|
||||||
|
|
||||||
|
return i2c_transfer(instance, &read_message, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if Device is Ready */
|
||||||
|
bool i2c_is_device_ready(uint8_t instance, uint16_t slave_address) {
|
||||||
|
if (instance >= I2C_MAX_INSTANCES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hal_i2c_check_device(instance, slave_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2C Interrupt Handler */
|
||||||
|
void i2c_process_interrupt(uint8_t instance) {
|
||||||
|
if (instance >= I2C_MAX_INSTANCES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized || !driver->transfer_active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t interrupt_status = hal_i2c_get_interrupt_status(instance);
|
||||||
|
|
||||||
|
/* Handle address sent */
|
||||||
|
if (interrupt_status & I2C_INTERRUPT_ADDRESS_SENT) {
|
||||||
|
if (driver->current_message.direction == I2C_DIRECTION_READ) {
|
||||||
|
hal_i2c_prepare_receive(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle data transfer */
|
||||||
|
if (interrupt_status & I2C_INTERRUPT_DATA) {
|
||||||
|
if (driver->transfer_index < driver->current_message.length) {
|
||||||
|
if (driver->current_message.direction == I2C_DIRECTION_WRITE) {
|
||||||
|
/* Transmit next byte */
|
||||||
|
uint8_t byte = driver->current_message.data[driver->transfer_index];
|
||||||
|
hal_i2c_transmit_byte(instance, byte);
|
||||||
|
} else {
|
||||||
|
/* Receive next byte */
|
||||||
|
driver->current_message.data[driver->transfer_index] =
|
||||||
|
hal_i2c_receive_byte(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->transfer_index++;
|
||||||
|
|
||||||
|
/* Check if last byte */
|
||||||
|
if (driver->transfer_index == driver->current_message.length) {
|
||||||
|
if (driver->current_message.generate_stop) {
|
||||||
|
hal_i2c_generate_stop(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle NACK */
|
||||||
|
if (interrupt_status & I2C_INTERRUPT_NACK) {
|
||||||
|
driver->transfer_active = false;
|
||||||
|
driver->statistics.nack_errors++;
|
||||||
|
|
||||||
|
if (driver->config.transfer_complete_callback != NULL) {
|
||||||
|
driver->config.transfer_complete_callback(I2C_TRANSFER_NACK,
|
||||||
|
driver->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle transfer complete */
|
||||||
|
if (interrupt_status & I2C_INTERRUPT_COMPLETE) {
|
||||||
|
driver->transfer_active = false;
|
||||||
|
|
||||||
|
if (driver->config.transfer_complete_callback != NULL) {
|
||||||
|
driver->config.transfer_complete_callback(I2C_TRANSFER_COMPLETE,
|
||||||
|
driver->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle errors */
|
||||||
|
if (interrupt_status & I2C_INTERRUPT_ERROR) {
|
||||||
|
driver->transfer_active = false;
|
||||||
|
driver->statistics.transfers_failed++;
|
||||||
|
|
||||||
|
uint32_t error = hal_i2c_get_error(instance);
|
||||||
|
|
||||||
|
if (error & I2C_ERROR_ARBITRATION_LOST) {
|
||||||
|
driver->statistics.arbitration_lost++;
|
||||||
|
}
|
||||||
|
if (error & I2C_ERROR_BUS) {
|
||||||
|
driver->statistics.bus_errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver->config.transfer_complete_callback != NULL) {
|
||||||
|
driver->config.transfer_complete_callback(I2C_TRANSFER_ERROR,
|
||||||
|
driver->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
/**
|
||||||
|
* @file pwm_driver.c
|
||||||
|
* @brief PWM driver implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pwm_driver.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* PWM Driver State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
PwmConfig_t config;
|
||||||
|
PwmStatistics_t statistics;
|
||||||
|
uint32_t current_duty_cycles[PWM_MAX_CHANNELS];
|
||||||
|
bool channel_enabled[PWM_MAX_CHANNELS];
|
||||||
|
uint32_t fault_flags;
|
||||||
|
Mutex_t mutex;
|
||||||
|
} PwmDriverState_t;
|
||||||
|
|
||||||
|
static PwmDriverState_t pwm_drivers[PWM_MAX_INSTANCES];
|
||||||
|
|
||||||
|
/* Initialize PWM Driver */
|
||||||
|
KernelStatus_t pwm_init(uint8_t instance, PwmConfig_t* config) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || config == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy configuration */
|
||||||
|
memcpy(&driver->config, config, sizeof(PwmConfig_t));
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
memset(&driver->statistics, 0, sizeof(PwmStatistics_t));
|
||||||
|
driver->fault_flags = 0;
|
||||||
|
|
||||||
|
/* Initialize channel states */
|
||||||
|
for (uint8_t i = 0; i < PWM_MAX_CHANNELS; i++) {
|
||||||
|
driver->current_duty_cycles[i] = 0;
|
||||||
|
driver->channel_enabled[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create mutex */
|
||||||
|
mutex_create(&driver->mutex, false);
|
||||||
|
|
||||||
|
/* Initialize PWM hardware */
|
||||||
|
if (hal_pwm_init(instance, config) != 0) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure channels */
|
||||||
|
for (uint8_t i = 0; i < config->channel_count; i++) {
|
||||||
|
driver->current_duty_cycles[config->channels[i].channel] =
|
||||||
|
config->channels[i].duty_cycle;
|
||||||
|
driver->channel_enabled[config->channels[i].channel] = true;
|
||||||
|
|
||||||
|
hal_pwm_configure_channel(instance, &config->channels[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure fault protection */
|
||||||
|
if (config->enable_fault_protection) {
|
||||||
|
hal_pwm_configure_fault(instance, config->fault_action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
hal_pwm_enable_interrupts(instance);
|
||||||
|
|
||||||
|
driver->initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start PWM */
|
||||||
|
KernelStatus_t pwm_start(uint8_t instance) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_pwm_start(instance);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop PWM */
|
||||||
|
KernelStatus_t pwm_stop(uint8_t instance) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_pwm_stop(instance);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set PWM Duty Cycle */
|
||||||
|
KernelStatus_t pwm_set_duty_cycle(uint8_t instance, uint8_t channel,
|
||||||
|
uint32_t duty_cycle) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duty_cycle > PWM_MAX_DUTY_CYCLE) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock mutex */
|
||||||
|
if (mutex_lock(&driver->mutex, 100) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update duty cycle */
|
||||||
|
driver->current_duty_cycles[channel] = duty_cycle;
|
||||||
|
driver->statistics.duty_cycle_updates++;
|
||||||
|
|
||||||
|
/* Update hardware */
|
||||||
|
hal_pwm_set_duty_cycle(instance, channel, duty_cycle);
|
||||||
|
|
||||||
|
/* Unlock mutex */
|
||||||
|
mutex_unlock(&driver->mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set PWM Frequency */
|
||||||
|
KernelStatus_t pwm_set_frequency(uint8_t instance, uint32_t frequency_hz) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || frequency_hz == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock mutex */
|
||||||
|
if (mutex_lock(&driver->mutex, 100) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update frequency */
|
||||||
|
driver->config.frequency_hz = frequency_hz;
|
||||||
|
|
||||||
|
/* Update hardware */
|
||||||
|
hal_pwm_set_frequency(instance, frequency_hz);
|
||||||
|
|
||||||
|
/* Unlock mutex */
|
||||||
|
mutex_unlock(&driver->mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set PWM Dead Time */
|
||||||
|
KernelStatus_t pwm_set_dead_time(uint8_t instance, uint8_t channel,
|
||||||
|
const PwmDeadTime_t* dead_time) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS ||
|
||||||
|
dead_time == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock mutex */
|
||||||
|
if (mutex_lock(&driver->mutex, 100) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update dead time */
|
||||||
|
driver->config.channels[channel].dead_time = *dead_time;
|
||||||
|
|
||||||
|
/* Update hardware */
|
||||||
|
hal_pwm_set_dead_time(instance, channel, dead_time);
|
||||||
|
|
||||||
|
/* Unlock mutex */
|
||||||
|
mutex_unlock(&driver->mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable PWM Channel */
|
||||||
|
KernelStatus_t pwm_enable_channel(uint8_t instance, uint8_t channel) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->channel_enabled[channel] = true;
|
||||||
|
hal_pwm_enable_channel(instance, channel);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable PWM Channel */
|
||||||
|
KernelStatus_t pwm_disable_channel(uint8_t instance, uint8_t channel) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->channel_enabled[channel] = false;
|
||||||
|
hal_pwm_disable_channel(instance, channel);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear PWM Fault */
|
||||||
|
KernelStatus_t pwm_clear_fault(uint8_t instance) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->fault_flags = 0;
|
||||||
|
hal_pwm_clear_fault(instance);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PWM Interrupt Handler */
|
||||||
|
void pwm_process_interrupt(uint8_t instance) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t interrupt_status = hal_pwm_get_interrupt_status(instance);
|
||||||
|
|
||||||
|
/* Handle period elapsed */
|
||||||
|
if (interrupt_status & PWM_INTERRUPT_PERIOD_ELAPSED) {
|
||||||
|
driver->statistics.period_elapsed_count++;
|
||||||
|
|
||||||
|
/* Call callback */
|
||||||
|
if (driver->config.period_elapsed_callback != NULL) {
|
||||||
|
driver->config.period_elapsed_callback(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle fault */
|
||||||
|
if (interrupt_status & PWM_INTERRUPT_FAULT) {
|
||||||
|
driver->statistics.fault_events++;
|
||||||
|
driver->fault_flags = hal_pwm_get_fault_flags(instance);
|
||||||
|
|
||||||
|
/* Apply fault action */
|
||||||
|
hal_pwm_apply_fault_action(instance, driver->config.fault_action);
|
||||||
|
|
||||||
|
/* Call callback */
|
||||||
|
if (driver->config.fault_callback != NULL) {
|
||||||
|
driver->config.fault_callback(instance, driver->fault_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear interrupt flags */
|
||||||
|
hal_pwm_clear_interrupts(instance, interrupt_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PWM Statistics */
|
||||||
|
KernelStatus_t pwm_get_statistics(uint8_t instance, PwmStatistics_t* stats) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || stats == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(stats, &pwm_drivers[instance].statistics, sizeof(PwmStatistics_t));
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PWM Duty Cycle */
|
||||||
|
uint32_t pwm_get_duty_cycle(uint8_t instance, uint8_t channel) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pwm_drivers[instance].current_duty_cycles[channel];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PWM Frequency */
|
||||||
|
uint32_t pwm_get_frequency(uint8_t instance) {
|
||||||
|
if (instance >= PWM_MAX_INSTANCES) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pwm_drivers[instance].config.frequency_hz;
|
||||||
|
}
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|
/**
|
||||||
|
* @file spi_driver.c
|
||||||
|
* @brief SPI driver implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spi_driver.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* SPI Driver State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
SpiConfig_t config;
|
||||||
|
SpiStatistics_t statistics;
|
||||||
|
uint8_t tx_buffer[SPI_MAX_BUFFER_SIZE];
|
||||||
|
uint8_t rx_buffer[SPI_MAX_BUFFER_SIZE];
|
||||||
|
uint16_t transfer_length;
|
||||||
|
uint16_t transfer_index;
|
||||||
|
bool transfer_active;
|
||||||
|
bool use_dma;
|
||||||
|
Semaphore_t transfer_semaphore;
|
||||||
|
Mutex_t transfer_mutex;
|
||||||
|
void* user_data;
|
||||||
|
} SpiDriverState_t;
|
||||||
|
|
||||||
|
static SpiDriverState_t spi_drivers[SPI_MAX_INSTANCES];
|
||||||
|
|
||||||
|
/* Initialize SPI Driver */
|
||||||
|
KernelStatus_t spi_init(uint8_t instance, SpiConfig_t* config) {
|
||||||
|
if (instance >= SPI_MAX_INSTANCES || config == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||||
|
|
||||||
|
if (driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy configuration */
|
||||||
|
memcpy(&driver->config, config, sizeof(SpiConfig_t));
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
memset(&driver->statistics, 0, sizeof(SpiStatistics_t));
|
||||||
|
driver->transfer_length = 0;
|
||||||
|
driver->transfer_index = 0;
|
||||||
|
driver->transfer_active = false;
|
||||||
|
driver->use_dma = config->use_dma;
|
||||||
|
|
||||||
|
/* Create synchronization primitives */
|
||||||
|
semaphore_create(&driver->transfer_semaphore, SEMAPHORE_BINARY, 1, 1);
|
||||||
|
mutex_create(&driver->transfer_mutex, false);
|
||||||
|
|
||||||
|
/* Initialize SPI hardware */
|
||||||
|
if (hal_spi_init(instance, config) != 0) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts if not using DMA */
|
||||||
|
if (!driver->use_dma) {
|
||||||
|
hal_spi_enable_interrupts(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SPI Transfer */
|
||||||
|
KernelStatus_t spi_transfer(uint8_t instance, const SpiTransaction_t* transaction,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
if (instance >= SPI_MAX_INSTANCES || transaction == NULL ||
|
||||||
|
transaction->length == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if transfer is already active */
|
||||||
|
if (driver->transfer_active) {
|
||||||
|
return KERNEL_RESOURCE_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take transfer semaphore */
|
||||||
|
if (semaphore_take(&driver->transfer_semaphore, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock transfer mutex */
|
||||||
|
if (mutex_lock(&driver->transfer_mutex, timeout_ms) != KERNEL_OK) {
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up transfer */
|
||||||
|
driver->transfer_length = transaction->length;
|
||||||
|
driver->transfer_index = 0;
|
||||||
|
driver->transfer_active = true;
|
||||||
|
driver->user_data = transaction->user_data;
|
||||||
|
|
||||||
|
/* Copy TX data if provided */
|
||||||
|
if (transaction->tx_data != NULL) {
|
||||||
|
memcpy(driver->tx_buffer, transaction->tx_data, transaction->length);
|
||||||
|
} else {
|
||||||
|
memset(driver->tx_buffer, 0xFF, transaction->length); /* Dummy data */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear RX buffer */
|
||||||
|
memset(driver->rx_buffer, 0, transaction->length);
|
||||||
|
|
||||||
|
/* Select chip if hardware CS is not used */
|
||||||
|
if (!driver->config.enable_hardware_cs) {
|
||||||
|
spi_set_chip_select(instance, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start transfer */
|
||||||
|
if (driver->use_dma) {
|
||||||
|
hal_spi_start_dma_transfer(instance, driver->tx_buffer, driver->rx_buffer,
|
||||||
|
transaction->length);
|
||||||
|
} else {
|
||||||
|
hal_spi_start_transfer(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for completion */
|
||||||
|
if (timeout_ms > 0) {
|
||||||
|
TickType_t start_tick = kernel_get_tick_count();
|
||||||
|
|
||||||
|
while (driver->transfer_active) {
|
||||||
|
if ((kernel_get_tick_count() - start_tick) > timeout_ms) {
|
||||||
|
/* Timeout - abort transfer */
|
||||||
|
hal_spi_abort_transfer(instance);
|
||||||
|
driver->transfer_active = false;
|
||||||
|
|
||||||
|
if (!driver->config.enable_hardware_cs) {
|
||||||
|
spi_set_chip_select(instance, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&driver->transfer_mutex);
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
driver->statistics.timeout_errors++;
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
kernel_delay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy RX data if requested */
|
||||||
|
if (transaction->rx_data != NULL) {
|
||||||
|
memcpy(transaction->rx_data, driver->rx_buffer, transaction->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deselect chip */
|
||||||
|
if (!driver->config.enable_hardware_cs) {
|
||||||
|
spi_set_chip_select(instance, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update statistics */
|
||||||
|
driver->statistics.transfers_completed++;
|
||||||
|
driver->statistics.bytes_transferred += transaction->length;
|
||||||
|
|
||||||
|
/* Unlock */
|
||||||
|
mutex_unlock(&driver->transfer_mutex);
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SPI Transfer Asynchronously */
|
||||||
|
KernelStatus_t spi_transfer_async(uint8_t instance, const SpiTransaction_t* transaction) {
|
||||||
|
if (instance >= SPI_MAX_INSTANCES || transaction == NULL ||
|
||||||
|
transaction->length == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver->transfer_active) {
|
||||||
|
return KERNEL_RESOURCE_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up transfer */
|
||||||
|
driver->transfer_length = transaction->length;
|
||||||
|
driver->transfer_index = 0;
|
||||||
|
driver->transfer_active = true;
|
||||||
|
driver->user_data = transaction->user_data;
|
||||||
|
|
||||||
|
/* Copy TX data */
|
||||||
|
if (transaction->tx_data != NULL) {
|
||||||
|
memcpy(driver->tx_buffer, transaction->tx_data, transaction->length);
|
||||||
|
} else {
|
||||||
|
memset(driver->tx_buffer, 0xFF, transaction->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select chip */
|
||||||
|
if (!driver->config.enable_hardware_cs) {
|
||||||
|
spi_set_chip_select(instance, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start transfer */
|
||||||
|
if (driver->use_dma) {
|
||||||
|
hal_spi_start_dma_transfer(instance, driver->tx_buffer, driver->rx_buffer,
|
||||||
|
transaction->length);
|
||||||
|
} else {
|
||||||
|
hal_spi_start_transfer(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SPI Read */
|
||||||
|
KernelStatus_t spi_read(uint8_t instance, uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
SpiTransaction_t transaction = {
|
||||||
|
.tx_data = NULL,
|
||||||
|
.rx_data = data,
|
||||||
|
.length = length,
|
||||||
|
.keep_cs_active = false,
|
||||||
|
.user_data = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
return spi_transfer(instance, &transaction, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SPI Write */
|
||||||
|
KernelStatus_t spi_write(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
SpiTransaction_t transaction = {
|
||||||
|
.tx_data = data,
|
||||||
|
.rx_data = NULL,
|
||||||
|
.length = length,
|
||||||
|
.keep_cs_active = false,
|
||||||
|
.user_data = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
return spi_transfer(instance, &transaction, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SPI Read/Write */
|
||||||
|
KernelStatus_t spi_read_write(uint8_t instance, const uint8_t* tx_data,
|
||||||
|
uint8_t* rx_data, uint16_t length, uint32_t timeout_ms) {
|
||||||
|
SpiTransaction_t transaction = {
|
||||||
|
.tx_data = tx_data,
|
||||||
|
.rx_data = rx_data,
|
||||||
|
.length = length,
|
||||||
|
.keep_cs_active = false,
|
||||||
|
.user_data = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
return spi_transfer(instance, &transaction, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SPI Interrupt Handler */
|
||||||
|
void spi_process_interrupt(uint8_t instance) {
|
||||||
|
if (instance >= SPI_MAX_INSTANCES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized || !driver->transfer_active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if more data to transfer */
|
||||||
|
if (driver->transfer_index < driver->transfer_length) {
|
||||||
|
/* Transmit next byte */
|
||||||
|
uint8_t tx_byte = driver->tx_buffer[driver->transfer_index];
|
||||||
|
uint8_t rx_byte = hal_spi_transfer_byte(instance, tx_byte);
|
||||||
|
|
||||||
|
/* Store received byte */
|
||||||
|
if (driver->transfer_index < SPI_MAX_BUFFER_SIZE) {
|
||||||
|
driver->rx_buffer[driver->transfer_index] = rx_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->transfer_index++;
|
||||||
|
} else {
|
||||||
|
/* Transfer complete */
|
||||||
|
driver->transfer_active = false;
|
||||||
|
|
||||||
|
/* Deselect chip */
|
||||||
|
if (!driver->config.enable_hardware_cs) {
|
||||||
|
spi_set_chip_select(instance, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update statistics */
|
||||||
|
driver->statistics.transfers_completed++;
|
||||||
|
driver->statistics.bytes_transferred += driver->transfer_length;
|
||||||
|
|
||||||
|
/* Call callback */
|
||||||
|
if (driver->config.transfer_complete_callback != NULL) {
|
||||||
|
driver->config.transfer_complete_callback(SPI_TRANSFER_COMPLETE,
|
||||||
|
driver->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal completion */
|
||||||
|
semaphore_give(&driver->transfer_semaphore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Chip Select */
|
||||||
|
void spi_set_chip_select(uint8_t instance, bool active) {
|
||||||
|
if (instance >= SPI_MAX_INSTANCES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||||
|
|
||||||
|
bool cs_level = active ?
|
||||||
|
(driver->config.cs_polarity == SPI_CS_ACTIVE_LOW ? false : true) :
|
||||||
|
(driver->config.cs_polarity == SPI_CS_ACTIVE_LOW ? true : false);
|
||||||
|
|
||||||
|
gpio_write(driver->config.cs_port, driver->config.cs_pin, cs_level);
|
||||||
|
}
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
/**
|
||||||
|
* @file uart_driver.c
|
||||||
|
* @brief UART driver implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uart_driver.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* UART Driver State */
|
||||||
|
typedef struct {
|
||||||
|
bool initialized;
|
||||||
|
UartConfig_t config;
|
||||||
|
UartStatistics_t statistics;
|
||||||
|
uint8_t tx_buffer[UART_MAX_BUFFER_SIZE];
|
||||||
|
uint8_t rx_buffer[UART_MAX_BUFFER_SIZE];
|
||||||
|
uint16_t tx_head;
|
||||||
|
uint16_t tx_tail;
|
||||||
|
uint16_t rx_head;
|
||||||
|
uint16_t rx_tail;
|
||||||
|
uint16_t tx_count;
|
||||||
|
uint16_t rx_count;
|
||||||
|
Semaphore_t tx_semaphore;
|
||||||
|
Semaphore_t rx_semaphore;
|
||||||
|
Mutex_t tx_mutex;
|
||||||
|
Mutex_t rx_mutex;
|
||||||
|
} UartDriverState_t;
|
||||||
|
|
||||||
|
static UartDriverState_t uart_drivers[UART_MAX_INSTANCES];
|
||||||
|
|
||||||
|
/* Initialize UART Driver */
|
||||||
|
KernelStatus_t uart_init(uint8_t instance, UartConfig_t* config) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES || config == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
UartDriverState_t* driver = &uart_drivers[instance];
|
||||||
|
|
||||||
|
if (driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy configuration */
|
||||||
|
memcpy(&driver->config, config, sizeof(UartConfig_t));
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
memset(&driver->statistics, 0, sizeof(UartStatistics_t));
|
||||||
|
driver->tx_head = 0;
|
||||||
|
driver->tx_tail = 0;
|
||||||
|
driver->rx_head = 0;
|
||||||
|
driver->rx_tail = 0;
|
||||||
|
driver->tx_count = 0;
|
||||||
|
driver->rx_count = 0;
|
||||||
|
|
||||||
|
/* Create synchronization primitives */
|
||||||
|
semaphore_create(&driver->tx_semaphore, SEMAPHORE_COUNTING,
|
||||||
|
UART_MAX_BUFFER_SIZE, UART_MAX_BUFFER_SIZE);
|
||||||
|
semaphore_create(&driver->rx_semaphore, SEMAPHORE_COUNTING, 0,
|
||||||
|
UART_MAX_BUFFER_SIZE);
|
||||||
|
mutex_create(&driver->tx_mutex, false);
|
||||||
|
mutex_create(&driver->rx_mutex, false);
|
||||||
|
|
||||||
|
/* Initialize UART hardware */
|
||||||
|
if (hal_uart_init(instance, config) != 0) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
hal_uart_enable_interrupts(instance, true, true);
|
||||||
|
|
||||||
|
driver->initialized = true;
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send Data via UART */
|
||||||
|
KernelStatus_t uart_send(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES || data == NULL || length == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
UartDriverState_t* driver = &uart_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock TX mutex */
|
||||||
|
if (mutex_lock(&driver->tx_mutex, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if async transfer is in progress */
|
||||||
|
if (driver->tx_count > 0) {
|
||||||
|
mutex_unlock(&driver->tx_mutex);
|
||||||
|
return KERNEL_RESOURCE_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data to TX buffer */
|
||||||
|
uint16_t copy_length = (length < UART_MAX_BUFFER_SIZE) ? length : UART_MAX_BUFFER_SIZE;
|
||||||
|
|
||||||
|
critical_section_enter();
|
||||||
|
memcpy(driver->tx_buffer, data, copy_length);
|
||||||
|
driver->tx_head = 0;
|
||||||
|
driver->tx_tail = copy_length;
|
||||||
|
driver->tx_count = copy_length;
|
||||||
|
critical_section_exit();
|
||||||
|
|
||||||
|
/* Start transmission */
|
||||||
|
hal_uart_start_tx(instance);
|
||||||
|
|
||||||
|
/* Wait for completion if synchronous */
|
||||||
|
if (timeout_ms > 0) {
|
||||||
|
TickType_t start_tick = kernel_get_tick_count();
|
||||||
|
|
||||||
|
while (driver->tx_count > 0) {
|
||||||
|
if ((kernel_get_tick_count() - start_tick) > timeout_ms) {
|
||||||
|
mutex_unlock(&driver->tx_mutex);
|
||||||
|
driver->statistics.tx_errors++;
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
kernel_delay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update statistics */
|
||||||
|
driver->statistics.tx_bytes += copy_length;
|
||||||
|
|
||||||
|
/* Unlock TX mutex */
|
||||||
|
mutex_unlock(&driver->tx_mutex);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive Data via UART */
|
||||||
|
KernelStatus_t uart_receive(uint8_t instance, uint8_t* data, uint16_t length,
|
||||||
|
uint32_t timeout_ms) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES || data == NULL || length == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
UartDriverState_t* driver = &uart_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for data */
|
||||||
|
if (semaphore_take(&driver->rx_semaphore, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock RX mutex */
|
||||||
|
if (mutex_lock(&driver->rx_mutex, timeout_ms) != KERNEL_OK) {
|
||||||
|
return KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data from RX buffer */
|
||||||
|
critical_section_enter();
|
||||||
|
|
||||||
|
uint16_t copy_length = 0;
|
||||||
|
while (copy_length < length && driver->rx_count > 0) {
|
||||||
|
data[copy_length] = driver->rx_buffer[driver->rx_head];
|
||||||
|
driver->rx_head = (driver->rx_head + 1) % UART_MAX_BUFFER_SIZE;
|
||||||
|
driver->rx_count--;
|
||||||
|
copy_length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
critical_section_exit();
|
||||||
|
|
||||||
|
/* Update statistics */
|
||||||
|
driver->statistics.rx_bytes += copy_length;
|
||||||
|
|
||||||
|
/* Unlock RX mutex */
|
||||||
|
mutex_unlock(&driver->rx_mutex);
|
||||||
|
|
||||||
|
return (copy_length == length) ? KERNEL_OK : KERNEL_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send Data Asynchronously */
|
||||||
|
KernelStatus_t uart_send_async(uint8_t instance, const uint8_t* data, uint16_t length) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES || data == NULL || length == 0) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
UartDriverState_t* driver = &uart_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return KERNEL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver->tx_count > 0) {
|
||||||
|
return KERNEL_RESOURCE_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data to TX buffer */
|
||||||
|
uint16_t copy_length = (length < UART_MAX_BUFFER_SIZE) ? length : UART_MAX_BUFFER_SIZE;
|
||||||
|
|
||||||
|
critical_section_enter();
|
||||||
|
memcpy(driver->tx_buffer, data, copy_length);
|
||||||
|
driver->tx_head = 0;
|
||||||
|
driver->tx_tail = copy_length;
|
||||||
|
driver->tx_count = copy_length;
|
||||||
|
critical_section_exit();
|
||||||
|
|
||||||
|
/* Start transmission */
|
||||||
|
hal_uart_start_tx(instance);
|
||||||
|
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UART Interrupt Handler */
|
||||||
|
void uart_process_interrupt(uint8_t instance) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UartDriverState_t* driver = &uart_drivers[instance];
|
||||||
|
|
||||||
|
if (!driver->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t interrupt_status = hal_uart_get_interrupt_status(instance);
|
||||||
|
|
||||||
|
/* Handle TX complete */
|
||||||
|
if (interrupt_status & UART_INTERRUPT_TX_COMPLETE) {
|
||||||
|
if (driver->tx_count > 0) {
|
||||||
|
/* Get next byte to transmit */
|
||||||
|
uint8_t byte = driver->tx_buffer[driver->tx_head];
|
||||||
|
driver->tx_head = (driver->tx_head + 1) % UART_MAX_BUFFER_SIZE;
|
||||||
|
driver->tx_count--;
|
||||||
|
|
||||||
|
/* Transmit byte */
|
||||||
|
hal_uart_transmit_byte(instance, byte);
|
||||||
|
|
||||||
|
if (driver->tx_count == 0) {
|
||||||
|
/* All data transmitted */
|
||||||
|
if (driver->config.tx_callback != NULL) {
|
||||||
|
driver->config.tx_callback();
|
||||||
|
}
|
||||||
|
hal_uart_disable_tx_interrupt(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle RX ready */
|
||||||
|
if (interrupt_status & UART_INTERRUPT_RX_READY) {
|
||||||
|
while (hal_uart_is_rx_ready(instance)) {
|
||||||
|
uint8_t byte = hal_uart_receive_byte(instance);
|
||||||
|
|
||||||
|
/* Add to RX buffer */
|
||||||
|
critical_section_enter();
|
||||||
|
|
||||||
|
if (driver->rx_count < UART_MAX_BUFFER_SIZE) {
|
||||||
|
driver->rx_buffer[driver->rx_tail] = byte;
|
||||||
|
driver->rx_tail = (driver->rx_tail + 1) % UART_MAX_BUFFER_SIZE;
|
||||||
|
driver->rx_count++;
|
||||||
|
|
||||||
|
/* Signal data available */
|
||||||
|
semaphore_give(&driver->rx_semaphore);
|
||||||
|
|
||||||
|
/* Call callback if registered */
|
||||||
|
if (driver->config.rx_callback != NULL) {
|
||||||
|
driver->config.rx_callback(&byte, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
driver->statistics.rx_errors++;
|
||||||
|
driver->statistics.overrun_errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
critical_section_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle errors */
|
||||||
|
if (interrupt_status & UART_INTERRUPT_ERROR) {
|
||||||
|
uint32_t error = hal_uart_get_error_status(instance);
|
||||||
|
|
||||||
|
if (error & UART_ERROR_PARITY) {
|
||||||
|
driver->statistics.parity_errors++;
|
||||||
|
}
|
||||||
|
if (error & UART_ERROR_FRAMING) {
|
||||||
|
driver->statistics.framing_errors++;
|
||||||
|
}
|
||||||
|
if (error & UART_ERROR_OVERRUN) {
|
||||||
|
driver->statistics.overrun_errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver->config.error_callback != NULL) {
|
||||||
|
driver->config.error_callback(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear error flags */
|
||||||
|
hal_uart_clear_errors(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get UART Statistics */
|
||||||
|
KernelStatus_t uart_get_statistics(uint8_t instance, UartStatistics_t* stats) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES || stats == NULL) {
|
||||||
|
return KERNEL_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(stats, &uart_drivers[instance].statistics, sizeof(UartStatistics_t));
|
||||||
|
return KERNEL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get RX Count */
|
||||||
|
uint16_t uart_get_rx_count(uint8_t instance) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uart_drivers[instance].rx_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get TX Count */
|
||||||
|
uint16_t uart_get_tx_count(uint8_t instance) {
|
||||||
|
if (instance >= UART_MAX_INSTANCES) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uart_drivers[instance].tx_count;
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
/**
|
||||||
|
* @file port.c
|
||||||
|
* @brief ARM Cortex-M0 architecture specific port
|
||||||
|
* @note Cortex-M0 is used in low-power automotive applications
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "portmacro.h"
|
||||||
|
|
||||||
|
/* Global variables for context switching */
|
||||||
|
uint32_t* current_task_sp = NULL;
|
||||||
|
uint32_t* next_task_sp = NULL;
|
||||||
|
|
||||||
|
/* Initialize Architecture Port */
|
||||||
|
void port_init(void) {
|
||||||
|
/* Configure SysTick for 1ms interrupts */
|
||||||
|
SysTick->LOAD = (SystemCoreClock / 1000) - 1;
|
||||||
|
SysTick->VAL = 0;
|
||||||
|
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
|
||||||
|
SysTick_CTRL_TICKINT_Msk |
|
||||||
|
SysTick_CTRL_ENABLE_Msk;
|
||||||
|
|
||||||
|
/* Set lowest priority for SysTick and PendSV */
|
||||||
|
NVIC_SetPriority(SysTick_IRQn, 3);
|
||||||
|
NVIC_SetPriority(PendSV_IRQn, 3);
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
void port_start_first_task(void) {
|
||||||
|
TaskHandle_t first_task = scheduler_get_current_task();
|
||||||
|
|
||||||
|
if (first_task == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set PSP to task stack pointer */
|
||||||
|
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||||
|
|
||||||
|
/* Switch to using PSP */
|
||||||
|
__set_CONTROL(0x02);
|
||||||
|
__ISB();
|
||||||
|
|
||||||
|
/* Restore context and start task */
|
||||||
|
__asm volatile (
|
||||||
|
"POP {R4-R7}\n"
|
||||||
|
"MOV R8, R4\n"
|
||||||
|
"MOV R9, R5\n"
|
||||||
|
"MOV R10, R6\n"
|
||||||
|
"MOV R11, R7\n"
|
||||||
|
"POP {R4-R7}\n"
|
||||||
|
"POP {R0-R3}\n"
|
||||||
|
"POP {R12}\n"
|
||||||
|
"POP {LR}\n"
|
||||||
|
"POP {PC}\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context Switch Trigger */
|
||||||
|
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||||
|
current_task_sp = current_context;
|
||||||
|
next_task_sp = next_context;
|
||||||
|
|
||||||
|
/* Trigger PendSV */
|
||||||
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Yield from ISR */
|
||||||
|
void port_yield_from_isr(void) {
|
||||||
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter Critical Section */
|
||||||
|
void port_disable_interrupts(void) {
|
||||||
|
__disable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit Critical Section */
|
||||||
|
void port_enable_interrupts(void) {
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Current Exception Number */
|
||||||
|
uint32_t port_get_current_exception(void) {
|
||||||
|
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize Task Stack */
|
||||||
|
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top) {
|
||||||
|
uint32_t* stack_ptr = stack_top;
|
||||||
|
|
||||||
|
/* Align stack to 8 bytes */
|
||||||
|
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||||
|
|
||||||
|
/* Initial stack frame for Cortex-M0 */
|
||||||
|
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||||
|
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||||
|
*(--stack_ptr) = 0xFFFFFFFD; /* LR (return to thread mode) */
|
||||||
|
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||||
|
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||||
|
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||||
|
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||||
|
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||||
|
|
||||||
|
/* Additional registers */
|
||||||
|
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||||
|
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||||
|
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||||
|
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||||
|
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||||
|
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||||
|
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||||
|
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||||
|
|
||||||
|
return stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SysTick Handler */
|
||||||
|
void SysTick_Handler(void) {
|
||||||
|
/* Increment tick count */
|
||||||
|
extern void kernel_tick_handler(void);
|
||||||
|
kernel_tick_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PendSV Handler */
|
||||||
|
void PendSV_Handler(void) {
|
||||||
|
/* Save current context */
|
||||||
|
__asm volatile (
|
||||||
|
"MRS R0, PSP\n"
|
||||||
|
"SUBS R0, R0, #32\n"
|
||||||
|
"STMIA R0!, {R4-R7}\n"
|
||||||
|
"MOV R4, R8\n"
|
||||||
|
"MOV R5, R9\n"
|
||||||
|
"MOV R6, R10\n"
|
||||||
|
"MOV R7, R11\n"
|
||||||
|
"STMIA R0!, {R4-R7}\n"
|
||||||
|
"SUBS R0, R0, #32\n"
|
||||||
|
"LDR R1, =current_task_sp\n"
|
||||||
|
"STR R0, [R1]\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Load next context */
|
||||||
|
__asm volatile (
|
||||||
|
"LDR R0, =next_task_sp\n"
|
||||||
|
"LDR R1, [R0]\n"
|
||||||
|
"LDMIA R1!, {R4-R7}\n"
|
||||||
|
"MOV R8, R4\n"
|
||||||
|
"MOV R9, R5\n"
|
||||||
|
"MOV R10, R6\n"
|
||||||
|
"MOV R11, R7\n"
|
||||||
|
"LDMIA R1!, {R4-R7}\n"
|
||||||
|
"MSR PSP, R1\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
void SVC_Handler(void) {
|
||||||
|
/* Handle system calls */
|
||||||
|
__asm volatile (
|
||||||
|
"TST LR, #4\n"
|
||||||
|
"ITE EQ\n"
|
||||||
|
"MRSEQ R0, MSP\n"
|
||||||
|
"MRSNE R0, PSP\n"
|
||||||
|
"LDR R0, [R0, #24]\n"
|
||||||
|
"LDRB R0, [R0, #-2]\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hard Fault Handler */
|
||||||
|
void HardFault_Handler(void) {
|
||||||
|
/* Save fault information */
|
||||||
|
uint32_t fault_address;
|
||||||
|
uint32_t fault_status;
|
||||||
|
|
||||||
|
__asm volatile (
|
||||||
|
"MRS %0, BFAR\n"
|
||||||
|
"MRS %1, BFSR\n"
|
||||||
|
: "=r" (fault_address), "=r" (fault_status)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(1, fault_address, fault_status);
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* @file port_asm.s
|
||||||
|
* @brief ARM Cortex-M0 assembly routines
|
||||||
|
*/
|
||||||
|
|
||||||
|
.syntax unified
|
||||||
|
.thumb
|
||||||
|
.arch armv6-m
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
/* Global symbols */
|
||||||
|
.global current_task_sp
|
||||||
|
.global next_task_sp
|
||||||
|
.global port_context_switch
|
||||||
|
.global port_start_first_task
|
||||||
|
.global port_initialize_task_stack
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
.section .bss
|
||||||
|
.align 2
|
||||||
|
current_task_sp: .word 0
|
||||||
|
next_task_sp: .word 0
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.thumb_func
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
port_start_first_task:
|
||||||
|
/* Load task stack pointer */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
|
||||||
|
/* Set PSP */
|
||||||
|
msr PSP, r1
|
||||||
|
|
||||||
|
/* Switch to PSP */
|
||||||
|
movs r0, #2
|
||||||
|
msr CONTROL, r0
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
pop {r4-r7}
|
||||||
|
mov r8, r4
|
||||||
|
mov r9, r5
|
||||||
|
mov r10, r6
|
||||||
|
mov r11, r7
|
||||||
|
pop {r4-r7}
|
||||||
|
pop {r0-r3}
|
||||||
|
pop {r12}
|
||||||
|
pop {lr}
|
||||||
|
pop {pc}
|
||||||
|
|
||||||
|
/* PendSV Handler */
|
||||||
|
PendSV_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
subs r0, r0, #32
|
||||||
|
stmia r0!, {r4-r7}
|
||||||
|
mov r4, r8
|
||||||
|
mov r5, r9
|
||||||
|
mov r6, r10
|
||||||
|
mov r7, r11
|
||||||
|
stmia r0!, {r4-r7}
|
||||||
|
subs r0, r0, #32
|
||||||
|
ldr r1, =current_task_sp
|
||||||
|
str r0, [r1]
|
||||||
|
|
||||||
|
/* Load next context */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
ldmia r1!, {r4-r7}
|
||||||
|
mov r8, r4
|
||||||
|
mov r9, r5
|
||||||
|
mov r10, r6
|
||||||
|
mov r11, r7
|
||||||
|
ldmia r1!, {r4-r7}
|
||||||
|
msr PSP, r1
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
SVC_Handler:
|
||||||
|
tst lr, #4
|
||||||
|
ite eq
|
||||||
|
mrseq r0, MSP
|
||||||
|
mrsne r0, PSP
|
||||||
|
ldr r0, [r0, #24]
|
||||||
|
ldrb r0, [r0, #-2]
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Hard Fault Handler */
|
||||||
|
HardFault_Handler:
|
||||||
|
/* Save registers */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r7}
|
||||||
|
mov r4, r8
|
||||||
|
mov r5, r9
|
||||||
|
mov r6, r10
|
||||||
|
mov r7, r11
|
||||||
|
stmdb r0!, {r4-r7}
|
||||||
|
|
||||||
|
/* Get fault information */
|
||||||
|
mrs r0, BFAR
|
||||||
|
mrs r1, BFSR
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r2, #1
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
.end
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* @file portmacro.h
|
||||||
|
* @brief ARM Cortex-M0 specific macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORTMACRO_H
|
||||||
|
#define PORTMACRO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Data Types */
|
||||||
|
typedef uint32_t port_stack_type_t;
|
||||||
|
typedef uint32_t port_base_type_t;
|
||||||
|
|
||||||
|
/* Architecture Constants */
|
||||||
|
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||||
|
#define PORT_BYTE_ALIGNMENT 8
|
||||||
|
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 3
|
||||||
|
|
||||||
|
/* Critical Section Macros */
|
||||||
|
#define portENTER_CRITICAL() __disable_irq()
|
||||||
|
#define portEXIT_CRITICAL() __enable_irq()
|
||||||
|
|
||||||
|
/* Interrupt Control */
|
||||||
|
#define portENABLE_INTERRUPTS() __enable_irq()
|
||||||
|
#define portDISABLE_INTERRUPTS() __disable_irq()
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
|
||||||
|
/* Memory Barriers */
|
||||||
|
#define portMEMORY_BARRIER() __asm volatile("DMB")
|
||||||
|
#define portSYNC_BARRIER() __asm volatile("DSB")
|
||||||
|
#define portINSTRUCTION_BARRIER() __asm volatile("ISB")
|
||||||
|
|
||||||
|
/* NOP */
|
||||||
|
#define portNOP() __asm volatile("NOP")
|
||||||
|
|
||||||
|
#endif /* PORTMACRO_H */
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
/**
|
||||||
|
* @file port.c
|
||||||
|
* @brief ARM Cortex-M3 architecture specific port
|
||||||
|
* @note Cortex-M3 is widely used in automotive ECUs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "portmacro.h"
|
||||||
|
|
||||||
|
/* Context storage */
|
||||||
|
uint32_t* current_task_sp = NULL;
|
||||||
|
uint32_t* next_task_sp = NULL;
|
||||||
|
|
||||||
|
/* Exception priorities */
|
||||||
|
#define SYSTICK_PRIORITY 0xFF
|
||||||
|
#define PENDSV_PRIORITY 0xFF
|
||||||
|
#define SVC_PRIORITY 0x00
|
||||||
|
|
||||||
|
/* Initialize Architecture Port */
|
||||||
|
void port_init(void) {
|
||||||
|
/* Set priority grouping - 4 bits preemption, 0 bits sub-priority */
|
||||||
|
NVIC_SetPriorityGrouping(4);
|
||||||
|
|
||||||
|
/* Configure SysTick */
|
||||||
|
SysTick->LOAD = (SystemCoreClock / 1000) - 1;
|
||||||
|
SysTick->VAL = 0;
|
||||||
|
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
|
||||||
|
SysTick_CTRL_TICKINT_Msk |
|
||||||
|
SysTick_CTRL_ENABLE_Msk;
|
||||||
|
|
||||||
|
/* Set exception priorities */
|
||||||
|
NVIC_SetPriority(SysTick_IRQn, SYSTICK_PRIORITY);
|
||||||
|
NVIC_SetPriority(PendSV_IRQn, PENDSV_PRIORITY);
|
||||||
|
NVIC_SetPriority(SVCall_IRQn, SVC_PRIORITY);
|
||||||
|
|
||||||
|
/* Enable faults */
|
||||||
|
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk |
|
||||||
|
SCB_SHCSR_BUSFAULTENA_Msk |
|
||||||
|
SCB_SHCSR_USGFAULTENA_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
void port_start_first_task(void) {
|
||||||
|
TaskHandle_t first_task = scheduler_get_current_task();
|
||||||
|
|
||||||
|
if (first_task == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set PSP */
|
||||||
|
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||||
|
|
||||||
|
/* Switch to PSP */
|
||||||
|
__set_CONTROL(0x02);
|
||||||
|
__ISB();
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
__asm volatile (
|
||||||
|
"LDMIA R0!, {R4-R11}\n"
|
||||||
|
"MSR PSP, R0\n"
|
||||||
|
"MOV LR, #0xFFFFFFFD\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||||
|
current_task_sp = current_context;
|
||||||
|
next_task_sp = next_context;
|
||||||
|
|
||||||
|
/* Trigger PendSV */
|
||||||
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
|
||||||
|
/* Data synchronization barrier */
|
||||||
|
__DSB();
|
||||||
|
__ISB();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Yield */
|
||||||
|
void port_yield(void) {
|
||||||
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Yield from ISR */
|
||||||
|
void port_yield_from_isr(void) {
|
||||||
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter Critical Section */
|
||||||
|
void port_disable_interrupts(void) {
|
||||||
|
__disable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit Critical Section */
|
||||||
|
void port_enable_interrupts(void) {
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Current Exception Number */
|
||||||
|
uint32_t port_get_current_exception(void) {
|
||||||
|
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize Task Stack */
|
||||||
|
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top) {
|
||||||
|
uint32_t* stack_ptr = stack_top;
|
||||||
|
|
||||||
|
/* 8-byte alignment */
|
||||||
|
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||||
|
|
||||||
|
/* Exception frame */
|
||||||
|
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||||
|
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||||
|
*(--stack_ptr) = 0xFFFFFFFD; /* LR */
|
||||||
|
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||||
|
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||||
|
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||||
|
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||||
|
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||||
|
|
||||||
|
/* Additional context */
|
||||||
|
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||||
|
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||||
|
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||||
|
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||||
|
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||||
|
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||||
|
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||||
|
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||||
|
|
||||||
|
return stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SysTick Handler */
|
||||||
|
void SysTick_Handler(void) {
|
||||||
|
extern void kernel_tick_handler(void);
|
||||||
|
kernel_tick_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PendSV Handler */
|
||||||
|
__attribute__((naked)) void PendSV_Handler(void) {
|
||||||
|
__asm volatile (
|
||||||
|
"MRS R0, PSP\n"
|
||||||
|
"STMDB R0!, {R4-R11}\n"
|
||||||
|
"LDR R1, =current_task_sp\n"
|
||||||
|
"STR R0, [R1]\n"
|
||||||
|
"LDR R0, =next_task_sp\n"
|
||||||
|
"LDR R1, [R0]\n"
|
||||||
|
"LDMIA R1!, {R4-R11}\n"
|
||||||
|
"MSR PSP, R1\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
__attribute__((naked)) void SVC_Handler(void) {
|
||||||
|
__asm volatile (
|
||||||
|
"TST LR, #4\n"
|
||||||
|
"ITE EQ\n"
|
||||||
|
"MRSEQ R0, MSP\n"
|
||||||
|
"MRSNE R0, PSP\n"
|
||||||
|
"LDR R0, [R0, #24]\n"
|
||||||
|
"LDRB R0, [R0, #-2]\n"
|
||||||
|
"PUSH {LR}\n"
|
||||||
|
"BL svc_handler\n"
|
||||||
|
"POP {LR}\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Memory Management Fault Handler */
|
||||||
|
void MemManage_Handler(void) {
|
||||||
|
uint32_t fault_address;
|
||||||
|
uint32_t fault_status;
|
||||||
|
|
||||||
|
fault_address = SCB->MMFAR;
|
||||||
|
fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(2, fault_address, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bus Fault Handler */
|
||||||
|
void BusFault_Handler(void) {
|
||||||
|
uint32_t fault_address;
|
||||||
|
uint32_t fault_status;
|
||||||
|
|
||||||
|
fault_address = SCB->BFAR;
|
||||||
|
fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(3, fault_address, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Usage Fault Handler */
|
||||||
|
void UsageFault_Handler(void) {
|
||||||
|
uint32_t fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(4, 0, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hard Fault Handler */
|
||||||
|
void HardFault_Handler(void) {
|
||||||
|
uint32_t fault_address = 0;
|
||||||
|
uint32_t fault_status = SCB->HFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(1, fault_address, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* @file port_asm.s
|
||||||
|
* @brief ARM Cortex-M3 assembly routines
|
||||||
|
*/
|
||||||
|
|
||||||
|
.syntax unified
|
||||||
|
.thumb
|
||||||
|
.arch armv7-m
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
/* Global symbols */
|
||||||
|
.global current_task_sp
|
||||||
|
.global next_task_sp
|
||||||
|
.global port_context_switch
|
||||||
|
.global port_start_first_task
|
||||||
|
.global port_initialize_task_stack
|
||||||
|
.global port_disable_interrupts
|
||||||
|
.global port_enable_interrupts
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
.section .bss
|
||||||
|
.align 2
|
||||||
|
current_task_sp: .word 0
|
||||||
|
next_task_sp: .word 0
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.thumb_func
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
port_start_first_task:
|
||||||
|
/* Load task stack pointer */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
|
||||||
|
/* Set PSP */
|
||||||
|
msr PSP, r1
|
||||||
|
|
||||||
|
/* Switch to PSP */
|
||||||
|
mov r0, #2
|
||||||
|
msr CONTROL, r0
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
ldmia r1!, {r4-r11}
|
||||||
|
msr PSP, r1
|
||||||
|
mov lr, #0xFFFFFFFD
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* PendSV Handler */
|
||||||
|
PendSV_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
ldr r1, =current_task_sp
|
||||||
|
str r0, [r1]
|
||||||
|
|
||||||
|
/* Load next context */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
ldmia r1!, {r4-r11}
|
||||||
|
msr PSP, r1
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
SVC_Handler:
|
||||||
|
tst lr, #4
|
||||||
|
ite eq
|
||||||
|
mrseq r0, MSP
|
||||||
|
mrsne r0, PSP
|
||||||
|
ldr r0, [r0, #24]
|
||||||
|
ldrb r0, [r0, #-2]
|
||||||
|
push {lr}
|
||||||
|
bl svc_handler
|
||||||
|
pop {lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Disable Interrupts */
|
||||||
|
port_disable_interrupts:
|
||||||
|
cpsid i
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Enable Interrupts */
|
||||||
|
port_enable_interrupts:
|
||||||
|
cpsie i
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Memory Management Fault */
|
||||||
|
MemManage_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault information */
|
||||||
|
ldr r1, =0xE000ED34 /* MMFAR */
|
||||||
|
ldr r1, [r1]
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #2
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Bus Fault Handler */
|
||||||
|
BusFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault information */
|
||||||
|
ldr r1, =0xE000ED38 /* BFAR */
|
||||||
|
ldr r1, [r1]
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #3
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Usage Fault Handler */
|
||||||
|
UsageFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault status */
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #4
|
||||||
|
movs r1, #0
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Hard Fault Handler */
|
||||||
|
HardFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault status */
|
||||||
|
ldr r2, =0xE000ED2C /* HFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #1
|
||||||
|
movs r1, #0
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
.end
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @file portmacro.h
|
||||||
|
* @brief ARM Cortex-M3 specific macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORTMACRO_H
|
||||||
|
#define PORTMACRO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Data Types */
|
||||||
|
typedef uint32_t port_stack_type_t;
|
||||||
|
typedef uint32_t port_base_type_t;
|
||||||
|
|
||||||
|
/* Architecture Constants */
|
||||||
|
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||||
|
#define PORT_BYTE_ALIGNMENT 8
|
||||||
|
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 0x50
|
||||||
|
|
||||||
|
/* Critical Section Macros */
|
||||||
|
#define portENTER_CRITICAL() __asm volatile("CPSID I" ::: "memory")
|
||||||
|
#define portEXIT_CRITICAL() __asm volatile("CPSIE I" ::: "memory")
|
||||||
|
|
||||||
|
/* Interrupt Control */
|
||||||
|
#define portENABLE_INTERRUPTS() __asm volatile("CPSIE I" ::: "memory")
|
||||||
|
#define portDISABLE_INTERRUPTS() __asm volatile("CPSID I" ::: "memory")
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
|
||||||
|
/* Memory Barriers */
|
||||||
|
#define portMEMORY_BARRIER() __asm volatile("DMB" ::: "memory")
|
||||||
|
#define portSYNC_BARRIER() __asm volatile("DSB" ::: "memory")
|
||||||
|
#define portINSTRUCTION_BARRIER() __asm volatile("ISB" ::: "memory")
|
||||||
|
|
||||||
|
/* NOP */
|
||||||
|
#define portNOP() __asm volatile("NOP")
|
||||||
|
|
||||||
|
/* Endian Definition */
|
||||||
|
#define portBYTE_ORDER LITTLE_ENDIAN
|
||||||
|
|
||||||
|
#endif /* PORTMACRO_H */
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
/**
|
||||||
|
* @file port.c
|
||||||
|
* @brief ARM Cortex-M4 architecture specific port with FPU support
|
||||||
|
* @note Cortex-M4 with FPU is common in modern automotive MCUs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "portmacro.h"
|
||||||
|
|
||||||
|
/* Context storage */
|
||||||
|
uint32_t* current_task_sp = NULL;
|
||||||
|
uint32_t* next_task_sp = NULL;
|
||||||
|
|
||||||
|
/* FPU context storage */
|
||||||
|
uint32_t current_fpu_context[32];
|
||||||
|
uint32_t next_fpu_context[32];
|
||||||
|
|
||||||
|
/* Exception priorities */
|
||||||
|
#define SYSTICK_PRIORITY 0xFF
|
||||||
|
#define PENDSV_PRIORITY 0xFF
|
||||||
|
#define SVC_PRIORITY 0x00
|
||||||
|
|
||||||
|
/* Initialize Architecture Port */
|
||||||
|
void port_init(void) {
|
||||||
|
/* Set priority grouping - 4 bits preemption, 0 bits sub-priority */
|
||||||
|
NVIC_SetPriorityGrouping(4);
|
||||||
|
|
||||||
|
/* Enable FPU */
|
||||||
|
SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2));
|
||||||
|
|
||||||
|
/* Configure SysTick */
|
||||||
|
SysTick->LOAD = (SystemCoreClock / 1000) - 1;
|
||||||
|
SysTick->VAL = 0;
|
||||||
|
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
|
||||||
|
SysTick_CTRL_TICKINT_Msk |
|
||||||
|
SysTick_CTRL_ENABLE_Msk;
|
||||||
|
|
||||||
|
/* Set exception priorities */
|
||||||
|
NVIC_SetPriority(SysTick_IRQn, SYSTICK_PRIORITY);
|
||||||
|
NVIC_SetPriority(PendSV_IRQn, PENDSV_PRIORITY);
|
||||||
|
NVIC_SetPriority(SVCall_IRQn, SVC_PRIORITY);
|
||||||
|
|
||||||
|
/* Enable faults */
|
||||||
|
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk |
|
||||||
|
SCB_SHCSR_BUSFAULTENA_Msk |
|
||||||
|
SCB_SHCSR_USGFAULTENA_Msk;
|
||||||
|
|
||||||
|
/* Enable divide by zero trap */
|
||||||
|
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
|
||||||
|
|
||||||
|
/* Enable unaligned access trap */
|
||||||
|
SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
void port_start_first_task(void) {
|
||||||
|
TaskHandle_t first_task = scheduler_get_current_task();
|
||||||
|
|
||||||
|
if (first_task == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore FPU context if task uses FPU */
|
||||||
|
if (first_task->context[31] & 0x10) {
|
||||||
|
/* FPU was used - restore FPU registers */
|
||||||
|
__asm volatile (
|
||||||
|
"VLDM R0!, {S16-S31}\n"
|
||||||
|
"VLDM R0!, {S0-S15}\n"
|
||||||
|
:
|
||||||
|
: "r" (first_task->context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set PSP */
|
||||||
|
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||||
|
|
||||||
|
/* Switch to PSP */
|
||||||
|
__set_CONTROL(0x02);
|
||||||
|
__ISB();
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
__asm volatile (
|
||||||
|
"LDMIA R0!, {R4-R11}\n"
|
||||||
|
"MSR PSP, R0\n"
|
||||||
|
"MOV LR, #0xFFFFFFFD\n"
|
||||||
|
"BX LR\n"
|
||||||
|
:
|
||||||
|
: "r" (first_task->stack_pointer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||||
|
current_task_sp = current_context;
|
||||||
|
next_task_sp = next_context;
|
||||||
|
|
||||||
|
/* Trigger PendSV */
|
||||||
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
|
||||||
|
/* Data synchronization barrier */
|
||||||
|
__DSB();
|
||||||
|
__ISB();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize Task Stack with FPU support */
|
||||||
|
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top) {
|
||||||
|
uint32_t* stack_ptr = stack_top;
|
||||||
|
|
||||||
|
/* 8-byte alignment */
|
||||||
|
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||||
|
|
||||||
|
/* Exception frame with FPU */
|
||||||
|
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||||
|
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||||
|
*(--stack_ptr) = 0xFFFFFFFD; /* LR */
|
||||||
|
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||||
|
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||||
|
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||||
|
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||||
|
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||||
|
|
||||||
|
/* Additional context */
|
||||||
|
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||||
|
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||||
|
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||||
|
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||||
|
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||||
|
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||||
|
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||||
|
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||||
|
|
||||||
|
/* FPU context (S0-S31) */
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
*(--stack_ptr) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FPSCR */
|
||||||
|
*(--stack_ptr) = 0;
|
||||||
|
|
||||||
|
return stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SysTick Handler */
|
||||||
|
void SysTick_Handler(void) {
|
||||||
|
extern void kernel_tick_handler(void);
|
||||||
|
kernel_tick_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PendSV Handler with FPU context saving */
|
||||||
|
__attribute__((naked)) void PendSV_Handler(void) {
|
||||||
|
__asm volatile (
|
||||||
|
/* Check if FPU was used */
|
||||||
|
"TST LR, #0x10\n"
|
||||||
|
"IT EQ\n"
|
||||||
|
"BEQ 1f\n"
|
||||||
|
|
||||||
|
/* Save FPU context */
|
||||||
|
"MRS R0, CONTROL\n"
|
||||||
|
"TST R0, #0x04\n"
|
||||||
|
"IT EQ\n"
|
||||||
|
"BEQ 1f\n"
|
||||||
|
|
||||||
|
/* Save FPU registers */
|
||||||
|
"VSTMDB R0!, {S16-S31}\n"
|
||||||
|
"VSTMDB R0!, {S0-S15}\n"
|
||||||
|
|
||||||
|
"1:\n"
|
||||||
|
/* Save core context */
|
||||||
|
"MRS R0, PSP\n"
|
||||||
|
"STMDB R0!, {R4-R11}\n"
|
||||||
|
"LDR R1, =current_task_sp\n"
|
||||||
|
"STR R0, [R1]\n"
|
||||||
|
|
||||||
|
/* Load next context */
|
||||||
|
"LDR R0, =next_task_sp\n"
|
||||||
|
"LDR R1, [R0]\n"
|
||||||
|
"LDMIA R1!, {R4-R11}\n"
|
||||||
|
"MSR PSP, R1\n"
|
||||||
|
|
||||||
|
/* Restore FPU context if needed */
|
||||||
|
"TST LR, #0x10\n"
|
||||||
|
"IT EQ\n"
|
||||||
|
"BEQ 2f\n"
|
||||||
|
|
||||||
|
/* Restore FPU registers */
|
||||||
|
"VLDMIA R0!, {S0-S15}\n"
|
||||||
|
"VLDMIA R0!, {S16-S31}\n"
|
||||||
|
|
||||||
|
"2:\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
__attribute__((naked)) void SVC_Handler(void) {
|
||||||
|
__asm volatile (
|
||||||
|
"TST LR, #4\n"
|
||||||
|
"ITE EQ\n"
|
||||||
|
"MRSEQ R0, MSP\n"
|
||||||
|
"MRSNE R0, PSP\n"
|
||||||
|
"LDR R0, [R0, #24]\n"
|
||||||
|
"LDRB R0, [R0, #-2]\n"
|
||||||
|
"PUSH {LR}\n"
|
||||||
|
"BL svc_handler\n"
|
||||||
|
"POP {LR}\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fault Handlers */
|
||||||
|
void MemManage_Handler(void) {
|
||||||
|
uint32_t fault_address = SCB->MMFAR;
|
||||||
|
uint32_t fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(2, fault_address, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusFault_Handler(void) {
|
||||||
|
uint32_t fault_address = SCB->BFAR;
|
||||||
|
uint32_t fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(3, fault_address, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsageFault_Handler(void) {
|
||||||
|
uint32_t fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(4, 0, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardFault_Handler(void) {
|
||||||
|
uint32_t fault_status = SCB->HFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(1, 0, fault_status);
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
/**
|
||||||
|
* @file port_asm.s
|
||||||
|
* @brief ARM Cortex-M4 assembly routines with FPU support
|
||||||
|
*/
|
||||||
|
|
||||||
|
.syntax unified
|
||||||
|
.thumb
|
||||||
|
.arch armv7e-m
|
||||||
|
.fpu fpv4-sp-d16
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
/* Global symbols */
|
||||||
|
.global current_task_sp
|
||||||
|
.global next_task_sp
|
||||||
|
.global port_context_switch
|
||||||
|
.global port_start_first_task
|
||||||
|
.global port_initialize_task_stack
|
||||||
|
.global port_disable_interrupts
|
||||||
|
.global port_enable_interrupts
|
||||||
|
.global port_enable_fpu
|
||||||
|
.global port_disable_fpu
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
.section .bss
|
||||||
|
.align 3
|
||||||
|
current_task_sp: .word 0
|
||||||
|
next_task_sp: .word 0
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.thumb_func
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
port_start_first_task:
|
||||||
|
/* Load task stack pointer */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
|
||||||
|
/* Check if FPU context needs restoring */
|
||||||
|
tst lr, #0x10
|
||||||
|
beq 1f
|
||||||
|
|
||||||
|
/* Restore FPU registers */
|
||||||
|
add r1, r1, #64
|
||||||
|
vldmia r1!, {s16-s31}
|
||||||
|
vldmia r1!, {s0-s15}
|
||||||
|
sub r1, r1, #128
|
||||||
|
|
||||||
|
1:
|
||||||
|
/* Set PSP */
|
||||||
|
msr PSP, r1
|
||||||
|
|
||||||
|
/* Switch to PSP */
|
||||||
|
mov r0, #2
|
||||||
|
msr CONTROL, r0
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Restore core context */
|
||||||
|
ldmia r1!, {r4-r11}
|
||||||
|
msr PSP, r1
|
||||||
|
mov lr, #0xFFFFFFFD
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* PendSV Handler */
|
||||||
|
PendSV_Handler:
|
||||||
|
/* Check if using PSP */
|
||||||
|
mrs r0, CONTROL
|
||||||
|
tst r0, #2
|
||||||
|
beq 1f
|
||||||
|
|
||||||
|
/* Check if FPU was used */
|
||||||
|
tst lr, #0x10
|
||||||
|
beq 1f
|
||||||
|
|
||||||
|
/* Save FPU context */
|
||||||
|
mrs r0, PSP
|
||||||
|
add r0, r0, #64
|
||||||
|
vstmdb r0!, {s16-s31}
|
||||||
|
vstmdb r0!, {s0-s15}
|
||||||
|
|
||||||
|
1:
|
||||||
|
/* Save core context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
ldr r1, =current_task_sp
|
||||||
|
str r0, [r1]
|
||||||
|
|
||||||
|
/* Load next context */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
ldmia r1!, {r4-r11}
|
||||||
|
msr PSP, r1
|
||||||
|
|
||||||
|
/* Restore FPU context if needed */
|
||||||
|
tst lr, #0x10
|
||||||
|
beq 2f
|
||||||
|
|
||||||
|
/* Restore FPU registers */
|
||||||
|
add r1, r1, #64
|
||||||
|
vldmia r1!, {s0-s15}
|
||||||
|
vldmia r1!, {s16-s31}
|
||||||
|
|
||||||
|
2:
|
||||||
|
/* Return from exception */
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
SVC_Handler:
|
||||||
|
tst lr, #4
|
||||||
|
ite eq
|
||||||
|
mrseq r0, MSP
|
||||||
|
mrsne r0, PSP
|
||||||
|
ldr r0, [r0, #24]
|
||||||
|
ldrb r0, [r0, #-2]
|
||||||
|
push {lr}
|
||||||
|
bl svc_handler
|
||||||
|
pop {lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Enable FPU */
|
||||||
|
port_enable_fpu:
|
||||||
|
/* Enable CP10 and CP11 */
|
||||||
|
ldr r0, =0xE000ED88 /* CPACR */
|
||||||
|
ldr r1, [r0]
|
||||||
|
orr r1, r1, #(0xF << 20)
|
||||||
|
str r1, [r0]
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Disable FPU */
|
||||||
|
port_disable_fpu:
|
||||||
|
/* Disable CP10 and CP11 */
|
||||||
|
ldr r0, =0xE000ED88 /* CPACR */
|
||||||
|
ldr r1, [r0]
|
||||||
|
bic r1, r1, #(0xF << 20)
|
||||||
|
str r1, [r0]
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Disable Interrupts */
|
||||||
|
port_disable_interrupts:
|
||||||
|
cpsid i
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Enable Interrupts */
|
||||||
|
port_enable_interrupts:
|
||||||
|
cpsie i
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Memory Management Fault */
|
||||||
|
MemManage_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault information */
|
||||||
|
ldr r1, =0xE000ED34 /* MMFAR */
|
||||||
|
ldr r1, [r1]
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #2
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Bus Fault Handler */
|
||||||
|
BusFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault information */
|
||||||
|
ldr r1, =0xE000ED38 /* BFAR */
|
||||||
|
ldr r1, [r1]
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #3
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Usage Fault Handler */
|
||||||
|
UsageFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault status */
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #4
|
||||||
|
movs r1, #0
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Hard Fault Handler */
|
||||||
|
HardFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault status */
|
||||||
|
ldr r2, =0xE000ED2C /* HFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #1
|
||||||
|
movs r1, #0
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
b .
|
||||||
|
|
||||||
|
.end
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* @file portmacro.h
|
||||||
|
* @brief ARM Cortex-M4 specific macros with FPU support
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORTMACRO_H
|
||||||
|
#define PORTMACRO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "stm32f4xx.h" /* Adjust for specific MCU */
|
||||||
|
|
||||||
|
/* Data Types */
|
||||||
|
typedef uint32_t port_stack_type_t;
|
||||||
|
typedef uint32_t port_base_type_t;
|
||||||
|
|
||||||
|
/* Architecture Constants */
|
||||||
|
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||||
|
#define PORT_BYTE_ALIGNMENT 8
|
||||||
|
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 0x50
|
||||||
|
|
||||||
|
/* Critical Section Macros */
|
||||||
|
#define portENTER_CRITICAL() __asm volatile("CPSID I" ::: "memory")
|
||||||
|
#define portEXIT_CRITICAL() __asm volatile("CPSIE I" ::: "memory")
|
||||||
|
|
||||||
|
/* Interrupt Control */
|
||||||
|
#define portENABLE_INTERRUPTS() __asm volatile("CPSIE I" ::: "memory")
|
||||||
|
#define portDISABLE_INTERRUPTS() __asm volatile("CPSID I" ::: "memory")
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
|
||||||
|
/* Memory Barriers */
|
||||||
|
#define portMEMORY_BARRIER() __asm volatile("DMB" ::: "memory")
|
||||||
|
#define portSYNC_BARRIER() __asm volatile("DSB" ::: "memory")
|
||||||
|
#define portINSTRUCTION_BARRIER() __asm volatile("ISB" ::: "memory")
|
||||||
|
|
||||||
|
/* NOP */
|
||||||
|
#define portNOP() __asm volatile("NOP")
|
||||||
|
|
||||||
|
/* FPU Control */
|
||||||
|
#define portENABLE_FPU() \
|
||||||
|
do { \
|
||||||
|
SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2)); \
|
||||||
|
__DSB(); \
|
||||||
|
__ISB(); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define portDISABLE_FPU() \
|
||||||
|
do { \
|
||||||
|
SCB->CPACR &= ~((3UL << 10*2) | (3UL << 11*2)); \
|
||||||
|
__DSB(); \
|
||||||
|
__ISB(); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* Endian Definition */
|
||||||
|
#define portBYTE_ORDER LITTLE_ENDIAN
|
||||||
|
|
||||||
|
/* Optimization */
|
||||||
|
#define portFORCE_INLINE __attribute__((always_inline)) inline
|
||||||
|
|
||||||
|
/* Task Utilities */
|
||||||
|
#define portTASK_RETURN_ADDRESS (0xFFFFFFFD)
|
||||||
|
|
||||||
|
#endif /* PORTMACRO_H */
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* @file port_common.h
|
||||||
|
* @brief Common port interface definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORT_COMMON_H
|
||||||
|
#define PORT_COMMON_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* Common port function declarations */
|
||||||
|
void port_common_init(void);
|
||||||
|
void port_common_context_switch(TaskHandle_t current, TaskHandle_t next);
|
||||||
|
uint32_t* port_common_init_stack(TaskFunction_t function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top,
|
||||||
|
uint32_t stack_size);
|
||||||
|
void port_common_fault_handler(uint32_t fault_type, uint32_t fault_address,
|
||||||
|
uint32_t fault_status);
|
||||||
|
void port_enter_safe_state(void);
|
||||||
|
void port_set_safe_outputs(void);
|
||||||
|
void port_log_fault(void* fault_info);
|
||||||
|
|
||||||
|
/* Architecture-specific functions that must be implemented */
|
||||||
|
void port_init(void);
|
||||||
|
void port_start_first_task(void);
|
||||||
|
void port_context_switch(uint32_t* current_context, uint32_t* next_context);
|
||||||
|
uint32_t* port_initialize_task_stack(TaskFunction_t function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top);
|
||||||
|
void port_save_context(uint32_t* context);
|
||||||
|
void port_restore_context(uint32_t* context);
|
||||||
|
void port_disable_interrupts(void);
|
||||||
|
void port_enable_interrupts(void);
|
||||||
|
uint32_t port_get_current_exception(void);
|
||||||
|
void port_service_watchdog(void);
|
||||||
|
void port_yield(void);
|
||||||
|
void port_yield_from_isr(void);
|
||||||
|
|
||||||
|
#endif /* PORT_COMMON_H */
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
/**
|
||||||
|
* @file port.c
|
||||||
|
* @brief Infineon TriCore TC3xx architecture specific port
|
||||||
|
* @note TriCore is widely used in automotive powertrain and safety applications
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "portmacro.h"
|
||||||
|
#include "Ifx_Types.h"
|
||||||
|
#include "IfxCpu.h"
|
||||||
|
#include "IfxStm.h"
|
||||||
|
#include "IfxScuWdt.h"
|
||||||
|
|
||||||
|
/* Context storage */
|
||||||
|
uint32_t* current_task_sp = NULL;
|
||||||
|
uint32_t* next_task_sp = NULL;
|
||||||
|
|
||||||
|
/* STM configuration for system tick */
|
||||||
|
static IfxStm_CompareConfig stmCompareConfig;
|
||||||
|
static volatile uint32_t stmTickCount = 0;
|
||||||
|
|
||||||
|
/* CSA (Context Save Area) management */
|
||||||
|
#define MAX_CSA_AREAS 64
|
||||||
|
static uint32_t csa_areas[MAX_CSA_AREAS][16] __attribute__((aligned(64)));
|
||||||
|
static uint32_t csa_index = 0;
|
||||||
|
|
||||||
|
/* Interrupt priorities */
|
||||||
|
#define SYSTICK_PRIORITY 255
|
||||||
|
#define PENDSV_PRIORITY 255
|
||||||
|
#define SVC_PRIORITY 0
|
||||||
|
|
||||||
|
/* Initialize Architecture Port */
|
||||||
|
void port_init(void) {
|
||||||
|
/* Initialize CPU configuration */
|
||||||
|
IfxCpu_init();
|
||||||
|
|
||||||
|
/* Disable global interrupts during initialization */
|
||||||
|
IfxCpu_disableInterrupts();
|
||||||
|
|
||||||
|
/* Initialize CSA areas */
|
||||||
|
for (uint32_t i = 0; i < MAX_CSA_AREAS; i++) {
|
||||||
|
/* Initialize PCXI register for each CSA */
|
||||||
|
uint32_t pcxi_value = ((uint32_t)&csa_areas[i][0]) & 0xFFFFF000;
|
||||||
|
pcxi_value |= (i << 16); /* Set previous context pointer */
|
||||||
|
csa_areas[i][0] = pcxi_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure STM for 1ms tick */
|
||||||
|
IfxStm_initCompareConfig(&stmCompareConfig);
|
||||||
|
stmCompareConfig.triggerPriority = SYSTICK_PRIORITY;
|
||||||
|
stmCompareConfig.typeOfService = IfxSrc_Tos_cpu0;
|
||||||
|
stmCompareConfig.ticks = IfxStm_getFrequency(&MODULE_STM0) / 1000;
|
||||||
|
|
||||||
|
/* Initialize STM compare */
|
||||||
|
IfxStm_initCompare(&MODULE_STM0, &stmCompareConfig);
|
||||||
|
|
||||||
|
/* Enable safety watchdog */
|
||||||
|
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
|
||||||
|
IfxScuWdt_clearCpuEndinit(IfxScuWdt_getCpuWatchdogPassword());
|
||||||
|
IfxScuWdt_enableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword(),
|
||||||
|
100, 200);
|
||||||
|
IfxScuWdt_setCpuEndinit(IfxScuWdt_getCpuWatchdogPassword());
|
||||||
|
|
||||||
|
/* Enable global interrupts */
|
||||||
|
IfxCpu_enableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
void port_start_first_task(void) {
|
||||||
|
TaskHandle_t first_task = scheduler_get_current_task();
|
||||||
|
|
||||||
|
if (first_task == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get task context from CSA */
|
||||||
|
uint32_t* context = (uint32_t*)first_task->context;
|
||||||
|
uint32_t pcxi = context[0];
|
||||||
|
uint32_t pc = context[1];
|
||||||
|
uint32_t sp = context[2];
|
||||||
|
|
||||||
|
/* Set up context for first task execution */
|
||||||
|
__asm volatile (
|
||||||
|
"mov.a %%sp, %0\n" /* Set stack pointer */
|
||||||
|
"mov %%a11, %1\n" /* Set return address */
|
||||||
|
"mtsv %%pcxi, %2\n" /* Set context */
|
||||||
|
"rslcx\n" /* Restore lower context */
|
||||||
|
"rfe\n" /* Return from exception */
|
||||||
|
:
|
||||||
|
: "r" (sp), "r" (pc), "r" (pcxi)
|
||||||
|
: "a11", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||||
|
current_task_sp = current_context;
|
||||||
|
next_task_sp = next_context;
|
||||||
|
|
||||||
|
/* Trigger software interrupt for context switch */
|
||||||
|
__asm volatile (
|
||||||
|
"syscall 0\n" /* System call for context switch */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize Task Stack */
|
||||||
|
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top) {
|
||||||
|
uint32_t* stack_ptr = stack_top;
|
||||||
|
|
||||||
|
/* Align to 64 bytes */
|
||||||
|
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x3F);
|
||||||
|
|
||||||
|
/* Allocate CSA for task */
|
||||||
|
if (csa_index >= MAX_CSA_AREAS) {
|
||||||
|
return NULL; /* Out of CSA areas */
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* csa = csa_areas[csa_index];
|
||||||
|
csa_index++;
|
||||||
|
|
||||||
|
/* Initialize CSA */
|
||||||
|
csa[0] = 0; /* PCXI will be set during first context switch */
|
||||||
|
csa[1] = (uint32_t)task_function; /* PC */
|
||||||
|
csa[2] = (uint32_t)stack_ptr; /* SP */
|
||||||
|
csa[3] = (uint32_t)parameters; /* A4 (first argument) */
|
||||||
|
|
||||||
|
/* Initialize upper context registers */
|
||||||
|
csa[4] = 0; /* A5 */
|
||||||
|
csa[5] = 0; /* A6 */
|
||||||
|
csa[6] = 0; /* A7 */
|
||||||
|
csa[7] = 0; /* A8 */
|
||||||
|
csa[8] = 0; /* A9 */
|
||||||
|
csa[9] = 0; /* A10 */
|
||||||
|
csa[10] = 0; /* A11 */
|
||||||
|
csa[11] = 0; /* A12 */
|
||||||
|
csa[12] = 0; /* A13 */
|
||||||
|
csa[13] = 0; /* A14 */
|
||||||
|
csa[14] = 0; /* A15 */
|
||||||
|
csa[15] = 0; /* D8 */
|
||||||
|
|
||||||
|
/* Store CSA pointer in task context */
|
||||||
|
TaskHandle_t task = scheduler_get_current_task();
|
||||||
|
if (task != NULL) {
|
||||||
|
task->context[0] = (uint32_t)csa;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* System Timer Interrupt Handler */
|
||||||
|
IFX_INTERRUPT(stm_compare_match_isr, 0, SYSTICK_PRIORITY) {
|
||||||
|
/* Clear interrupt flag */
|
||||||
|
IfxStm_clearCompareFlag(&MODULE_STM0, stmCompareConfig.comparator);
|
||||||
|
|
||||||
|
/* Update compare value for next interrupt */
|
||||||
|
IfxStm_increaseCompare(&MODULE_STM0, stmCompareConfig.comparator,
|
||||||
|
IfxStm_getFrequency(&MODULE_STM0) / 1000);
|
||||||
|
|
||||||
|
/* Call kernel tick handler */
|
||||||
|
extern void kernel_tick_handler(void);
|
||||||
|
kernel_tick_handler();
|
||||||
|
|
||||||
|
/* Refresh watchdog */
|
||||||
|
IfxScuWdt_clearSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Software Interrupt Handler for Context Switch */
|
||||||
|
IFX_INTERRUPT(software_context_switch_isr, 0, PENDSV_PRIORITY) {
|
||||||
|
__asm volatile (
|
||||||
|
/* Save current context */
|
||||||
|
"svlcx\n" /* Save lower context */
|
||||||
|
"mov %%d15, %%a11\n" /* Save return address */
|
||||||
|
|
||||||
|
/* Store current stack pointer */
|
||||||
|
"mov.a %%a15, %%sp\n"
|
||||||
|
"mov.aa %0, %%a15\n"
|
||||||
|
|
||||||
|
/* Load next stack pointer */
|
||||||
|
"mov.aa %%a15, %1\n"
|
||||||
|
"mov.a %%sp, %%a15\n"
|
||||||
|
|
||||||
|
/* Restore lower context */
|
||||||
|
"rslcx\n"
|
||||||
|
"mov %%a11, %%d15\n" /* Restore return address */
|
||||||
|
"rfe\n" /* Return from exception */
|
||||||
|
:
|
||||||
|
: "m" (current_task_sp), "m" (next_task_sp)
|
||||||
|
: "a15", "d15", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* System Call Handler */
|
||||||
|
IFX_INTERRUPT(syscall_handler, 0, SVC_PRIORITY) {
|
||||||
|
/* Get system call number */
|
||||||
|
uint32_t syscall_number;
|
||||||
|
__asm volatile (
|
||||||
|
"mov %0, %%d15\n" /* Get syscall number from D15 */
|
||||||
|
: "=r" (syscall_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Handle system calls */
|
||||||
|
switch (syscall_number) {
|
||||||
|
case 0: /* Context switch */
|
||||||
|
software_context_switch_isr();
|
||||||
|
break;
|
||||||
|
case 1: /* Yield */
|
||||||
|
scheduler_yield();
|
||||||
|
break;
|
||||||
|
case 2: /* Task exit */
|
||||||
|
task_delete(scheduler_get_current_task());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trap Handlers */
|
||||||
|
IFX_INTERRUPT(trap_handler, 0, 0) {
|
||||||
|
/* Get trap information */
|
||||||
|
uint32_t trap_class;
|
||||||
|
uint32_t trap_id;
|
||||||
|
|
||||||
|
__asm volatile (
|
||||||
|
"mov %0, %%d15\n" /* Trap class */
|
||||||
|
"mov %1, %%d14\n" /* Trap ID */
|
||||||
|
: "=r" (trap_class), "=r" (trap_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(trap_class, trap_id, 0);
|
||||||
|
|
||||||
|
/* Enter safe state */
|
||||||
|
while(1) {
|
||||||
|
/* Safe state - wait for watchdog */
|
||||||
|
IfxScuWdt_clearSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter Critical Section */
|
||||||
|
void port_disable_interrupts(void) {
|
||||||
|
IfxCpu_disableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit Critical Section */
|
||||||
|
void port_enable_interrupts(void) {
|
||||||
|
IfxCpu_enableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Current Exception Number */
|
||||||
|
uint32_t port_get_current_exception(void) {
|
||||||
|
uint32_t icr;
|
||||||
|
__asm volatile (
|
||||||
|
"mfcr %0, $ICR\n" /* Get Interrupt Control Register */
|
||||||
|
: "=r" (icr)
|
||||||
|
);
|
||||||
|
return (icr >> 16) & 0xFF; /* Extract current CPU priority */
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* @file port_asm.s
|
||||||
|
* @brief Infineon TriCore TC3xx assembly routines
|
||||||
|
*/
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
/* Global symbols */
|
||||||
|
.global current_task_sp
|
||||||
|
.global next_task_sp
|
||||||
|
.global port_context_switch
|
||||||
|
.global port_start_first_task
|
||||||
|
.global port_disable_interrupts
|
||||||
|
.global port_enable_interrupts
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
.section .bss
|
||||||
|
.align 2
|
||||||
|
current_task_sp: .word 0
|
||||||
|
next_task_sp: .word 0
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
port_start_first_task:
|
||||||
|
/* Load task context */
|
||||||
|
mov.a a15, [next_task_sp]
|
||||||
|
ld.a a14, [a15] /* Load CSA pointer */
|
||||||
|
|
||||||
|
/* Set up context */
|
||||||
|
mtsv pcxi, a14 /* Set previous context */
|
||||||
|
ld.a sp, [a14+8] /* Load stack pointer */
|
||||||
|
ld.a a11, [a14+4] /* Load return address */
|
||||||
|
|
||||||
|
/* Restore context and start */
|
||||||
|
rslcx /* Restore lower context */
|
||||||
|
rfe /* Return from exception */
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
port_context_switch:
|
||||||
|
/* Save current context */
|
||||||
|
svlcx /* Save lower context */
|
||||||
|
|
||||||
|
/* Store current stack pointer */
|
||||||
|
mov.a a15, sp
|
||||||
|
mov.aa [current_task_sp], a15
|
||||||
|
|
||||||
|
/* Load next stack pointer */
|
||||||
|
mov.aa a15, [next_task_sp]
|
||||||
|
mov.a sp, a15
|
||||||
|
|
||||||
|
/* Restore next context */
|
||||||
|
rslcx /* Restore lower context */
|
||||||
|
rfe /* Return from exception */
|
||||||
|
|
||||||
|
/* Disable Interrupts */
|
||||||
|
port_disable_interrupts:
|
||||||
|
disable /* Disable interrupts */
|
||||||
|
ret
|
||||||
|
|
||||||
|
/* Enable Interrupts */
|
||||||
|
port_enable_interrupts:
|
||||||
|
enable /* Enable interrupts */
|
||||||
|
ret
|
||||||
|
|
||||||
|
/* System Timer Interrupt */
|
||||||
|
stm_compare_match_isr:
|
||||||
|
/* Save context */
|
||||||
|
svlcx
|
||||||
|
|
||||||
|
/* Clear interrupt */
|
||||||
|
movh.a a15, 0xF000 /* STM base address */
|
||||||
|
lea a15, [a15]0x0010 /* STM interrupt clear register */
|
||||||
|
st.w [a15], 0x1 /* Clear compare match flag */
|
||||||
|
|
||||||
|
/* Update compare value */
|
||||||
|
movh.a a15, 0xF000
|
||||||
|
lea a15, [a15]0x0020 /* STM compare register */
|
||||||
|
ld.w d15, [a15]
|
||||||
|
movh.a a14, 0xF000
|
||||||
|
lea a14, [a14]0x0024 /* STM compare update register */
|
||||||
|
add d15, d15, 1000 /* Add 1ms */
|
||||||
|
st.w [a14], d15
|
||||||
|
|
||||||
|
/* Call kernel tick handler */
|
||||||
|
call kernel_tick_handler
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
rslcx
|
||||||
|
rfe
|
||||||
|
|
||||||
|
/* Trap Handler */
|
||||||
|
trap_handler:
|
||||||
|
/* Save trap information */
|
||||||
|
mov d15, d15 /* Trap class */
|
||||||
|
mov d14, d14 /* Trap ID */
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
call fault_handler_process
|
||||||
|
|
||||||
|
/* Enter safe state */
|
||||||
|
j .
|
||||||
|
|
||||||
|
.end
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* @file portmacro.h
|
||||||
|
* @brief Infineon TriCore TC3xx specific macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORTMACRO_H
|
||||||
|
#define PORTMACRO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "Ifx_Types.h"
|
||||||
|
#include "IfxCpu.h"
|
||||||
|
|
||||||
|
/* Data Types */
|
||||||
|
typedef uint32_t port_stack_type_t;
|
||||||
|
typedef uint32_t port_base_type_t;
|
||||||
|
|
||||||
|
/* Architecture Constants */
|
||||||
|
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||||
|
#define PORT_BYTE_ALIGNMENT 64
|
||||||
|
|
||||||
|
/* Critical Section Macros */
|
||||||
|
#define portENTER_CRITICAL() IfxCpu_disableInterrupts()
|
||||||
|
#define portEXIT_CRITICAL() IfxCpu_enableInterrupts()
|
||||||
|
|
||||||
|
/* Interrupt Control */
|
||||||
|
#define portENABLE_INTERRUPTS() IfxCpu_enableInterrupts()
|
||||||
|
#define portDISABLE_INTERRUPTS() IfxCpu_disableInterrupts()
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
#define portYIELD() __asm volatile("syscall 0")
|
||||||
|
#define portYIELD_FROM_ISR() __asm volatile("syscall 0")
|
||||||
|
|
||||||
|
/* Memory Barriers */
|
||||||
|
#define portMEMORY_BARRIER() __asm volatile("dsync")
|
||||||
|
#define portSYNC_BARRIER() __asm volatile("dsync")
|
||||||
|
#define portINSTRUCTION_BARRIER() __asm volatile("isync")
|
||||||
|
|
||||||
|
/* NOP */
|
||||||
|
#define portNOP() __asm volatile("nop")
|
||||||
|
|
||||||
|
/* CSA Management */
|
||||||
|
#define portALLOCATE_CSA() __port_allocate_csa()
|
||||||
|
#define portFREE_CSA(csa) __port_free_csa(csa)
|
||||||
|
|
||||||
|
/* Safety Features */
|
||||||
|
#define portENABLE_SAFETY_WATCHDOG(timeout) \
|
||||||
|
IfxScuWdt_enableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword(), timeout, timeout*2)
|
||||||
|
|
||||||
|
#define portSERVICE_WATCHDOG() \
|
||||||
|
IfxScuWdt_clearSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword())
|
||||||
|
|
||||||
|
#endif /* PORTMACRO_H */
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
/**
|
||||||
|
* @file port.c
|
||||||
|
* @brief NXP S32K architecture specific port
|
||||||
|
* @note S32K is designed for automotive body and safety applications
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "portmacro.h"
|
||||||
|
#include "S32K144.h"
|
||||||
|
#include "interrupt_manager.h"
|
||||||
|
|
||||||
|
/* Context storage */
|
||||||
|
uint32_t* current_task_sp = NULL;
|
||||||
|
uint32_t* next_task_sp = NULL;
|
||||||
|
|
||||||
|
/* LPIT configuration */
|
||||||
|
#define LPIT_CHANNEL 0
|
||||||
|
#define LPIT_TICK_PERIOD 1000 /* 1ms at 1MHz */
|
||||||
|
|
||||||
|
/* Interrupt priorities */
|
||||||
|
#define SYSTICK_PRIORITY 15
|
||||||
|
#define PENDSV_PRIORITY 15
|
||||||
|
#define SVC_PRIORITY 0
|
||||||
|
|
||||||
|
/* Initialize Architecture Port */
|
||||||
|
void port_init(void) {
|
||||||
|
/* Disable global interrupts */
|
||||||
|
__disable_irq();
|
||||||
|
|
||||||
|
/* Configure system clock */
|
||||||
|
/* Assuming 8MHz external crystal, PLL to 160MHz */
|
||||||
|
SCG->SPLLCSR = SCG_SPLLCSR_SPLLEN_MASK;
|
||||||
|
SCG->SPLLDIV = SCG_SPLLDIV_SPLLDIV1(1) | SCG_SPLLDIV_SPLLDIV2(1);
|
||||||
|
SCG->SPLLCFG = SCG_SPLLCFG_MULT(20); /* 8MHz * 20 = 160MHz */
|
||||||
|
|
||||||
|
/* Wait for PLL lock */
|
||||||
|
while(!(SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK));
|
||||||
|
|
||||||
|
/* Switch to PLL */
|
||||||
|
SCG->RCCR = SCG_RCCR_DIVCORE(1) | SCG_RCCR_DIVBUS(2) |
|
||||||
|
SCG_RCCR_DIVSLOW(4) | SCG_RCCR_SCS(6);
|
||||||
|
|
||||||
|
/* Enable clock to LPIT */
|
||||||
|
PCC->PCCn[PCC_LPIT0_INDEX] = PCC_PCCn_PCS(6) | PCC_PCCn_CGC_MASK;
|
||||||
|
|
||||||
|
/* Configure LPIT for system tick */
|
||||||
|
LPIT0->MCR = LPIT_MCR_M_CEN_MASK; /* Enable module */
|
||||||
|
|
||||||
|
/* Configure channel 0 */
|
||||||
|
LPIT0->TMR[LPIT_CHANNEL].TVAL = LPIT_TICK_PERIOD;
|
||||||
|
LPIT0->TMR[LPIT_CHANNEL].TCTRL =
|
||||||
|
LPIT_TMR_TCTRL_T_EN_MASK | /* Enable timer */
|
||||||
|
LPIT_TMR_TCTRL_MODE_MASK; /* 32-bit counter mode */
|
||||||
|
|
||||||
|
/* Enable LPIT interrupt */
|
||||||
|
LPIT0->MIER |= (1 << LPIT_CHANNEL);
|
||||||
|
|
||||||
|
/* Set interrupt priority */
|
||||||
|
NVIC_SetPriority(LPIT0_IRQn, SYSTICK_PRIORITY);
|
||||||
|
NVIC_EnableIRQ(LPIT0_IRQn);
|
||||||
|
|
||||||
|
/* Configure watchdog */
|
||||||
|
WDOG->CNT = 0x1000; /* 4s timeout */
|
||||||
|
WDOG->TOVAL = 0x1000;
|
||||||
|
WDOG->CS = WDOG_CS_EN_MASK | WDOG_CS_CLK(1) |
|
||||||
|
WDOG_CS_WIN_MASK | WDOG_CS_UPDATE_MASK;
|
||||||
|
|
||||||
|
/* Enable faults */
|
||||||
|
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk |
|
||||||
|
SCB_SHCSR_BUSFAULTENA_Msk |
|
||||||
|
SCB_SHCSR_USGFAULTENA_Msk;
|
||||||
|
|
||||||
|
/* Enable global interrupts */
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
void port_start_first_task(void) {
|
||||||
|
TaskHandle_t first_task = scheduler_get_current_task();
|
||||||
|
|
||||||
|
if (first_task == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set PSP */
|
||||||
|
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||||
|
|
||||||
|
/* Switch to PSP */
|
||||||
|
__set_CONTROL(0x02);
|
||||||
|
__ISB();
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
__asm volatile (
|
||||||
|
"LDMIA R0!, {R4-R11}\n"
|
||||||
|
"MSR PSP, R0\n"
|
||||||
|
"MOV LR, #0xFFFFFFFD\n"
|
||||||
|
"BX LR\n"
|
||||||
|
:
|
||||||
|
: "r" (first_task->stack_pointer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||||
|
current_task_sp = current_context;
|
||||||
|
next_task_sp = next_context;
|
||||||
|
|
||||||
|
/* Trigger PendSV */
|
||||||
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
|
||||||
|
/* Data synchronization barrier */
|
||||||
|
__DSB();
|
||||||
|
__ISB();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize Task Stack */
|
||||||
|
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top) {
|
||||||
|
uint32_t* stack_ptr = stack_top;
|
||||||
|
|
||||||
|
/* 8-byte alignment */
|
||||||
|
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||||
|
|
||||||
|
/* Exception frame */
|
||||||
|
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||||
|
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||||
|
*(--stack_ptr) = 0xFFFFFFFD; /* LR */
|
||||||
|
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||||
|
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||||
|
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||||
|
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||||
|
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||||
|
|
||||||
|
/* Additional context */
|
||||||
|
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||||
|
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||||
|
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||||
|
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||||
|
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||||
|
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||||
|
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||||
|
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||||
|
|
||||||
|
return stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LPIT Interrupt Handler */
|
||||||
|
void LPIT0_IRQHandler(void) {
|
||||||
|
/* Clear interrupt flag */
|
||||||
|
LPIT0->MSR |= (1 << LPIT_CHANNEL);
|
||||||
|
|
||||||
|
/* Call kernel tick handler */
|
||||||
|
extern void kernel_tick_handler(void);
|
||||||
|
kernel_tick_handler();
|
||||||
|
|
||||||
|
/* Service watchdog */
|
||||||
|
WDOG->CNT = 0xB480; /* Refresh sequence */
|
||||||
|
WDOG->CNT = 0x4B80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PendSV Handler */
|
||||||
|
__attribute__((naked)) void PendSV_Handler(void) {
|
||||||
|
__asm volatile (
|
||||||
|
"MRS R0, PSP\n"
|
||||||
|
"STMDB R0!, {R4-R11}\n"
|
||||||
|
"LDR R1, =current_task_sp\n"
|
||||||
|
"STR R0, [R1]\n"
|
||||||
|
"LDR R0, =next_task_sp\n"
|
||||||
|
"LDR R1, [R0]\n"
|
||||||
|
"LDMIA R1!, {R4-R11}\n"
|
||||||
|
"MSR PSP, R1\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
__attribute__((naked)) void SVC_Handler(void) {
|
||||||
|
__asm volatile (
|
||||||
|
"TST LR, #4\n"
|
||||||
|
"ITE EQ\n"
|
||||||
|
"MRSEQ R0, MSP\n"
|
||||||
|
"MRSNE R0, PSP\n"
|
||||||
|
"LDR R0, [R0, #24]\n"
|
||||||
|
"LDRB R0, [R0, #-2]\n"
|
||||||
|
"PUSH {LR}\n"
|
||||||
|
"BL svc_handler\n"
|
||||||
|
"POP {LR}\n"
|
||||||
|
"BX LR\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fault Handlers */
|
||||||
|
void MemManage_Handler(void) {
|
||||||
|
uint32_t fault_address = SCB->MMFAR;
|
||||||
|
uint32_t fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(2, fault_address, fault_status);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
/* Safe state */
|
||||||
|
WDOG->CNT = 0xB480;
|
||||||
|
WDOG->CNT = 0x4B80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusFault_Handler(void) {
|
||||||
|
uint32_t fault_address = SCB->BFAR;
|
||||||
|
uint32_t fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(3, fault_address, fault_status);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
WDOG->CNT = 0xB480;
|
||||||
|
WDOG->CNT = 0x4B80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsageFault_Handler(void) {
|
||||||
|
uint32_t fault_status = SCB->CFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(4, 0, fault_status);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
WDOG->CNT = 0xB480;
|
||||||
|
WDOG->CNT = 0x4B80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardFault_Handler(void) {
|
||||||
|
uint32_t fault_status = SCB->HFSR;
|
||||||
|
|
||||||
|
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||||
|
fault_handler_process(1, 0, fault_status);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
WDOG->CNT = 0xB480;
|
||||||
|
WDOG->CNT = 0x4B80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter Critical Section */
|
||||||
|
void port_disable_interrupts(void) {
|
||||||
|
__disable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit Critical Section */
|
||||||
|
void port_enable_interrupts(void) {
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Current Exception Number */
|
||||||
|
uint32_t port_get_current_exception(void) {
|
||||||
|
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* @file port_asm.s
|
||||||
|
* @brief NXP S32K assembly routines
|
||||||
|
*/
|
||||||
|
|
||||||
|
.syntax unified
|
||||||
|
.thumb
|
||||||
|
.arch armv7e-m
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
/* Global symbols */
|
||||||
|
.global current_task_sp
|
||||||
|
.global next_task_sp
|
||||||
|
.global port_context_switch
|
||||||
|
.global port_start_first_task
|
||||||
|
.global port_disable_interrupts
|
||||||
|
.global port_enable_interrupts
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
.section .bss
|
||||||
|
.align 3
|
||||||
|
current_task_sp: .word 0
|
||||||
|
next_task_sp: .word 0
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.thumb_func
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
port_start_first_task:
|
||||||
|
/* Load task stack pointer */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
|
||||||
|
/* Set PSP */
|
||||||
|
msr PSP, r1
|
||||||
|
|
||||||
|
/* Switch to PSP */
|
||||||
|
mov r0, #2
|
||||||
|
msr CONTROL, r0
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
ldmia r1!, {r4-r11}
|
||||||
|
msr PSP, r1
|
||||||
|
mov lr, #0xFFFFFFFD
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* PendSV Handler */
|
||||||
|
PendSV_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
ldr r1, =current_task_sp
|
||||||
|
str r0, [r1]
|
||||||
|
|
||||||
|
/* Load next context */
|
||||||
|
ldr r0, =next_task_sp
|
||||||
|
ldr r1, [r0]
|
||||||
|
ldmia r1!, {r4-r11}
|
||||||
|
msr PSP, r1
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* SVC Handler */
|
||||||
|
SVC_Handler:
|
||||||
|
tst lr, #4
|
||||||
|
ite eq
|
||||||
|
mrseq r0, MSP
|
||||||
|
mrsne r0, PSP
|
||||||
|
ldr r0, [r0, #24]
|
||||||
|
ldrb r0, [r0, #-2]
|
||||||
|
push {lr}
|
||||||
|
bl svc_handler
|
||||||
|
pop {lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Disable Interrupts */
|
||||||
|
port_disable_interrupts:
|
||||||
|
cpsid i
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Enable Interrupts */
|
||||||
|
port_enable_interrupts:
|
||||||
|
cpsie i
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* LPIT Interrupt Handler */
|
||||||
|
LPIT0_IRQHandler:
|
||||||
|
/* Save context */
|
||||||
|
push {r4-r11, lr}
|
||||||
|
|
||||||
|
/* Clear interrupt flag */
|
||||||
|
ldr r0, =0x40037008 /* LPIT0->MSR */
|
||||||
|
movs r1, #1
|
||||||
|
str r1, [r0]
|
||||||
|
|
||||||
|
/* Call kernel tick handler */
|
||||||
|
bl kernel_tick_handler
|
||||||
|
|
||||||
|
/* Service watchdog */
|
||||||
|
ldr r0, =0x40052000 /* WDOG->CNT */
|
||||||
|
ldr r1, =0xB480
|
||||||
|
str r1, [r0]
|
||||||
|
ldr r1, =0x4B80
|
||||||
|
str r1, [r0]
|
||||||
|
|
||||||
|
/* Restore context */
|
||||||
|
pop {r4-r11, lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
/* Hard Fault Handler */
|
||||||
|
HardFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault status */
|
||||||
|
ldr r2, =0xE000ED2C /* HFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #1
|
||||||
|
movs r1, #0
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Safe state */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Memory Management Fault */
|
||||||
|
MemManage_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault information */
|
||||||
|
ldr r1, =0xE000ED34 /* MMFAR */
|
||||||
|
ldr r1, [r1]
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #2
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Safe state */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Bus Fault Handler */
|
||||||
|
BusFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault information */
|
||||||
|
ldr r1, =0xE000ED38 /* BFAR */
|
||||||
|
ldr r1, [r1]
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #3
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Safe state */
|
||||||
|
b .
|
||||||
|
|
||||||
|
/* Usage Fault Handler */
|
||||||
|
UsageFault_Handler:
|
||||||
|
/* Save context */
|
||||||
|
mrs r0, PSP
|
||||||
|
stmdb r0!, {r4-r11}
|
||||||
|
|
||||||
|
/* Get fault status */
|
||||||
|
ldr r2, =0xE000ED28 /* CFSR */
|
||||||
|
ldr r2, [r2]
|
||||||
|
|
||||||
|
/* Call fault handler */
|
||||||
|
movs r0, #4
|
||||||
|
movs r1, #0
|
||||||
|
bl fault_handler_process
|
||||||
|
|
||||||
|
/* Safe state */
|
||||||
|
b .
|
||||||
|
|
||||||
|
.end
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* @file portmacro.h
|
||||||
|
* @brief NXP S32K specific macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORTMACRO_H
|
||||||
|
#define PORTMACRO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "S32K144.h"
|
||||||
|
|
||||||
|
/* Data Types */
|
||||||
|
typedef uint32_t port_stack_type_t;
|
||||||
|
typedef uint32_t port_base_type_t;
|
||||||
|
|
||||||
|
/* Architecture Constants */
|
||||||
|
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||||
|
#define PORT_BYTE_ALIGNMENT 8
|
||||||
|
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 0x50
|
||||||
|
|
||||||
|
/* Critical Section Macros */
|
||||||
|
#define portENTER_CRITICAL() __asm volatile("CPSID I" ::: "memory")
|
||||||
|
#define portEXIT_CRITICAL() __asm volatile("CPSIE I" ::: "memory")
|
||||||
|
|
||||||
|
/* Interrupt Control */
|
||||||
|
#define portENABLE_INTERRUPTS() __asm volatile("CPSIE I" ::: "memory")
|
||||||
|
#define portDISABLE_INTERRUPTS() __asm volatile("CPSID I" ::: "memory")
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||||
|
|
||||||
|
/* Memory Barriers */
|
||||||
|
#define portMEMORY_BARRIER() __asm volatile("DMB" ::: "memory")
|
||||||
|
#define portSYNC_BARRIER() __asm volatile("DSB" ::: "memory")
|
||||||
|
#define portINSTRUCTION_BARRIER() __asm volatile("ISB" ::: "memory")
|
||||||
|
|
||||||
|
/* NOP */
|
||||||
|
#define portNOP() __asm volatile("NOP")
|
||||||
|
|
||||||
|
/* Watchdog Control */
|
||||||
|
#define portSERVICE_WATCHDOG() \
|
||||||
|
do { \
|
||||||
|
WDOG->CNT = 0xB480; \
|
||||||
|
WDOG->CNT = 0x4B80; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* Low Power Modes */
|
||||||
|
#define portENTER_SLEEP() __asm volatile("WFI")
|
||||||
|
#define portENTER_DEEP_SLEEP() __asm volatile("WFE")
|
||||||
|
|
||||||
|
/* Endian Definition */
|
||||||
|
#define portBYTE_ORDER LITTLE_ENDIAN
|
||||||
|
|
||||||
|
#endif /* PORTMACRO_H */
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
/**
|
||||||
|
* @file port.c
|
||||||
|
* @brief RISC-V RV32 architecture specific port
|
||||||
|
* @note RISC-V is emerging in automotive applications
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "portmacro.h"
|
||||||
|
|
||||||
|
/* Context storage */
|
||||||
|
uint32_t* current_task_sp = NULL;
|
||||||
|
uint32_t* next_task_sp = NULL;
|
||||||
|
|
||||||
|
/* Machine Timer registers */
|
||||||
|
#define MTIME_ADDR 0x0200BFF8
|
||||||
|
#define MTIMECMP_ADDR 0x02004000
|
||||||
|
|
||||||
|
/* Initialize Architecture Port */
|
||||||
|
void port_init(void) {
|
||||||
|
/* Configure machine timer for 1ms interrupts */
|
||||||
|
volatile uint32_t* mtime = (uint32_t*)MTIME_ADDR;
|
||||||
|
volatile uint32_t* mtimecmp = (uint32_t*)MTIMECMP_ADDR;
|
||||||
|
|
||||||
|
uint32_t current_time = *mtime;
|
||||||
|
*mtimecmp = current_time + (SystemCoreClock / 1000);
|
||||||
|
|
||||||
|
/* Enable machine timer interrupt */
|
||||||
|
__asm volatile (
|
||||||
|
"li t0, 0x80\n" /* Machine Timer Interrupt Enable */
|
||||||
|
"csrs mie, t0\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Enable global interrupts */
|
||||||
|
__asm volatile (
|
||||||
|
"csrsi mstatus, 0x8\n" /* Machine Interrupt Enable */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start First Task */
|
||||||
|
void port_start_first_task(void) {
|
||||||
|
TaskHandle_t first_task = scheduler_get_current_task();
|
||||||
|
|
||||||
|
if (first_task == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load task stack pointer */
|
||||||
|
__asm volatile (
|
||||||
|
"mv sp, %0\n"
|
||||||
|
"ret\n"
|
||||||
|
:
|
||||||
|
: "r" (first_task->stack_pointer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||||
|
current_task_sp = current_context;
|
||||||
|
next_task_sp = next_context;
|
||||||
|
|
||||||
|
/* Trigger software interrupt */
|
||||||
|
__asm volatile (
|
||||||
|
"li t0, 0x8\n" /* Machine Software Interrupt */
|
||||||
|
"csrs mip, t0\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize Task Stack */
|
||||||
|
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||||
|
void* parameters,
|
||||||
|
uint32_t* stack_top) {
|
||||||
|
uint32_t* stack_ptr = stack_top;
|
||||||
|
|
||||||
|
/* Align to 16 bytes */
|
||||||
|
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0xF);
|
||||||
|
|
||||||
|
/* Initial stack frame */
|
||||||
|
*(--stack_ptr) = (uint32_t)parameters; /* a0 */
|
||||||
|
*(--stack_ptr) = 0; /* a1 */
|
||||||
|
*(--stack_ptr) = 0; /* a2 */
|
||||||
|
*(--stack_ptr) = 0; /* a3 */
|
||||||
|
*(--stack_ptr) = 0; /* a4 */
|
||||||
|
*(--stack_ptr) = 0; /* a5 */
|
||||||
|
*(--stack_ptr) = 0; /* a6 */
|
||||||
|
*(--stack_ptr) = 0; /* a7 */
|
||||||
|
|
||||||
|
*(--stack_ptr) = (uint32_t)task_function; /* ra */
|
||||||
|
*(--stack_ptr) = 0; /* t0 */
|
||||||
|
*(--stack_ptr) = 0; /* t1 */
|
||||||
|
*(--stack_ptr) = 0; /* t2 */
|
||||||
|
*(--stack_ptr) = 0; /* t3 */
|
||||||
|
*(--stack_ptr) = 0; /* t4 */
|
||||||
|
*(--stack_ptr) = 0; /* t5 */
|
||||||
|
*(--stack_ptr) = 0; /* t6 */
|
||||||
|
|
||||||
|
*(--stack_ptr) = 0; /* s0 */
|
||||||
|
*(--stack_ptr) = 0; /* s1 */
|
||||||
|
*(--stack_ptr) = 0; /* s2 */
|
||||||
|
*(--stack_ptr) = 0; /* s3 */
|
||||||
|
*(--stack_ptr) = 0; /* s4 */
|
||||||
|
*(--stack_ptr) = 0; /* s5 */
|
||||||
|
*(--stack_ptr) = 0; /* s6 */
|
||||||
|
*(--stack_ptr) = 0; /* s7 */
|
||||||
|
*(--stack_ptr) = 0; /* s8 */
|
||||||
|
*(--stack_ptr) = 0; /* s9 */
|
||||||
|
*(--stack_ptr) = 0; /* s10 */
|
||||||
|
*(--stack_ptr) = 0; /* s11 */
|
||||||
|
|
||||||
|
*(--stack_ptr) = 0; /* gp */
|
||||||
|
*(--stack_ptr) = 0; /* tp */
|
||||||
|
|
||||||
|
/* mstatus */
|
||||||
|
*(--stack_ptr) = 0x00001880; /* MPP = Machine mode */
|
||||||
|
|
||||||
|
return stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Machine Timer Interrupt Handler */
|
||||||
|
void machine_timer_interrupt_handler(void) {
|
||||||
|
/* Clear interrupt */
|
||||||
|
volatile uint32_t* mtimecmp = (uint32_t*)MTIMECMP_ADDR;
|
||||||
|
volatile uint32_t* mtime = (uint32_t*)MTIME_ADDR;
|
||||||
|
*mtimecmp = *mtime + (SystemCoreClock / 1000);
|
||||||
|
|
||||||
|
/* Call kernel tick handler */
|
||||||
|
extern void kernel_tick_handler(void);
|
||||||
|
kernel_tick_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Machine Software Interrupt Handler */
|
||||||
|
void machine_software_interrupt_handler(void) {
|
||||||
|
/* Clear interrupt */
|
||||||
|
__asm volatile (
|
||||||
|
"li t0, 0x8\n"
|
||||||
|
"csrc mip, t0\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Perform context switch */
|
||||||
|
__asm volatile (
|
||||||
|
/* Save current context */
|
||||||
|
"addi sp, sp, -128\n"
|
||||||
|
"sw ra, 0(sp)\n"
|
||||||
|
"sw t0, 4(sp)\n"
|
||||||
|
"sw t1, 8(sp)\n"
|
||||||
|
"sw t2, 12(sp)\n"
|
||||||
|
"sw s0, 16(sp)\n"
|
||||||
|
"sw s1, 20(sp)\n"
|
||||||
|
"sw a0, 24(sp)\n"
|
||||||
|
"sw a1, 28(sp)\n"
|
||||||
|
"sw a2, 32(sp)\n"
|
||||||
|
"sw a3, 36(sp)\n"
|
||||||
|
"sw a4, 40(sp)\n"
|
||||||
|
"sw a5, 44(sp)\n"
|
||||||
|
"sw a6, 48(sp)\n"
|
||||||
|
"sw a7, 52(sp)\n"
|
||||||
|
"sw s2, 56(sp)\n"
|
||||||
|
"sw s3, 60(sp)\n"
|
||||||
|
"sw s4, 64(sp)\n"
|
||||||
|
"sw s5, 68(sp)\n"
|
||||||
|
"sw s6, 72(sp)\n"
|
||||||
|
"sw s7, 76(sp)\n"
|
||||||
|
"sw s8, 80(sp)\n"
|
||||||
|
"sw s9, 84(sp)\n"
|
||||||
|
"sw s10, 88(sp)\n"
|
||||||
|
"sw s11, 92(sp)\n"
|
||||||
|
"sw t3, 96(sp)\n"
|
||||||
|
"sw t4, 100(sp)\n"
|
||||||
|
"sw t5, 104(sp)\n"
|
||||||
|
"sw t6, 108(sp)\n"
|
||||||
|
"csrr t0, mstatus\n"
|
||||||
|
"sw t0, 112(sp)\n"
|
||||||
|
"csrr t0, mepc\n"
|
||||||
|
"sw t0, 116(sp)\n"
|
||||||
|
|
||||||
|
/* Save current stack pointer */
|
||||||
|
"la t0, current_task_sp\n"
|
||||||
|
"sw sp, 0(t0)\n"
|
||||||
|
|
||||||
|
/* Load next stack pointer */
|
||||||
|
"la t0, next_task_sp\n"
|
||||||
|
"lw sp, 0(t0)\n"
|
||||||
|
|
||||||
|
/* Restore next context */
|
||||||
|
"lw ra, 0(sp)\n"
|
||||||
|
"lw t0, 4(sp)\n"
|
||||||
|
"lw t1, 8(sp)\n"
|
||||||
|
"lw t2, 12(sp)\n"
|
||||||
|
"lw s0, 16(sp)\n"
|
||||||
|
"lw s1, 20(sp)\n"
|
||||||
|
"lw a0, 24(sp)\n"
|
||||||
|
"lw a1, 28(sp)\n"
|
||||||
|
"lw a2, 32(sp)\n"
|
||||||
|
"lw a3, 36(sp)\n"
|
||||||
|
"lw a4, 40(sp)\n"
|
||||||
|
"lw a5, 44(sp)\n"
|
||||||
|
"lw a6, 48(sp)\n"
|
||||||
|
"lw a7, 52(sp)\n"
|
||||||
|
"lw s2, 56(sp)\n"
|
||||||
|
"lw s3, 60(sp)\n"
|
||||||
|
"lw s4, 64(sp)\n"
|
||||||
|
"lw s5, 68(sp)\n"
|
||||||
|
"lw s6, 72(sp)\n"
|
||||||
|
"lw s7, 76(sp)\n"
|
||||||
|
"lw s8, 80(sp)\n"
|
||||||
|
"lw s9, 84(sp)\n"
|
||||||
|
"lw s10, 88(sp)\n"
|
||||||
|
"lw s11, 92(sp)\n"
|
||||||
|
"lw t3, 96(sp)\n"
|
||||||
|
"lw t4, 100(sp)\n"
|
||||||
|
"lw t5, 104(sp)\n"
|
||||||
|
"lw t6, 108(sp)\n"
|
||||||
|
"lw t0, 112(sp)\n"
|
||||||
|
"csrw mstatus, t0\n"
|
||||||
|
"lw t0, 116(sp)\n"
|
||||||
|
"csrw mepc, t0\n"
|
||||||
|
"addi sp, sp, 128\n"
|
||||||
|
|
||||||
|
/* Return from interrupt */
|
||||||
|
"mret\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter Critical Section */
|
||||||
|
void port_disable_interrupts(void) {
|
||||||
|
__asm volatile (
|
||||||
|
"csrci mstatus, 0x8\n" /* Clear Machine Interrupt Enable */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit Critical Section */
|
||||||
|
void port_enable_interrupts(void) {
|
||||||
|
__asm volatile (
|
||||||
|
"csrsi mstatus, 0x8\n" /* Set Machine Interrupt Enable */
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @file portmacro.h
|
||||||
|
* @brief RISC-V RV32 specific macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORTMACRO_H
|
||||||
|
#define PORTMACRO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Data Types */
|
||||||
|
typedef uint32_t port_stack_type_t;
|
||||||
|
typedef uint32_t port_base_type_t;
|
||||||
|
|
||||||
|
/* Architecture Constants */
|
||||||
|
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||||
|
#define PORT_BYTE_ALIGNMENT 16
|
||||||
|
|
||||||
|
/* Critical Section Macros */
|
||||||
|
#define portENTER_CRITICAL() __asm volatile("csrci mstatus, 0x8")
|
||||||
|
#define portEXIT_CRITICAL() __asm volatile("csrsi mstatus, 0x8")
|
||||||
|
|
||||||
|
/* Interrupt Control */
|
||||||
|
#define portENABLE_INTERRUPTS() __asm volatile("csrsi mstatus, 0x8")
|
||||||
|
#define portDISABLE_INTERRUPTS() __asm volatile("csrci mstatus, 0x8")
|
||||||
|
|
||||||
|
/* Context Switch */
|
||||||
|
#define portYIELD() __asm volatile("li t0, 0x8\ncsrs mip, t0")
|
||||||
|
#define portYIELD_FROM_ISR() __asm volatile("li t0, 0x8\ncsrs mip, t0")
|
||||||
|
|
||||||
|
/* Memory Barriers */
|
||||||
|
#define portMEMORY_BARRIER() __asm volatile("fence")
|
||||||
|
#define portSYNC_BARRIER() __asm volatile("fence")
|
||||||
|
#define portINSTRUCTION_BARRIER() __asm volatile("fence.i")
|
||||||
|
|
||||||
|
/* NOP */
|
||||||
|
#define portNOP() __asm volatile("nop")
|
||||||
|
|
||||||
|
#endif /* PORTMACRO_H */
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* @file isr.h
|
||||||
|
* @brief Interrupt Service Routine management
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ISR_H
|
||||||
|
#define ISR_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* ISR Types */
|
||||||
|
typedef void (*ISRHandler_t)(void);
|
||||||
|
|
||||||
|
/* ISR Configuration */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t irq_number;
|
||||||
|
ISRHandler_t handler;
|
||||||
|
uint8_t priority;
|
||||||
|
} ISRConfig_t;
|
||||||
|
|
||||||
|
/* ISR Functions */
|
||||||
|
KernelStatus_t isr_register(uint32_t irq_number, ISRHandler_t handler,
|
||||||
|
uint8_t priority);
|
||||||
|
KernelStatus_t isr_unregister(uint32_t irq_number);
|
||||||
|
KernelStatus_t isr_enable(uint32_t irq_number);
|
||||||
|
KernelStatus_t isr_disable(uint32_t irq_number);
|
||||||
|
KernelStatus_t isr_set_priority(uint32_t irq_number, uint8_t priority);
|
||||||
|
void isr_enter(void);
|
||||||
|
void isr_exit(void);
|
||||||
|
bool isr_is_in_context(void);
|
||||||
|
|
||||||
|
/* Critical Section Management */
|
||||||
|
void critical_section_enter(void);
|
||||||
|
void critical_section_exit(void);
|
||||||
|
|
||||||
|
#endif /* ISR_H */
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* @file kernel.h
|
||||||
|
* @brief Core kernel definitions and types for Automotive RTOS
|
||||||
|
* @author Automotive RTOS Team
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KERNEL_H
|
||||||
|
#define KERNEL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* Kernel Version */
|
||||||
|
#define KERNEL_VERSION_MAJOR 1
|
||||||
|
#define KERNEL_VERSION_MINOR 0
|
||||||
|
#define KERNEL_VERSION_PATCH 0
|
||||||
|
|
||||||
|
/* Configuration Constants */
|
||||||
|
#define MAX_TASKS 32
|
||||||
|
#define MAX_PRIORITY_LEVELS 16
|
||||||
|
#define MAX_TASK_NAME_LENGTH 16
|
||||||
|
#define IDLE_TASK_PRIORITY (MAX_PRIORITY_LEVELS - 1)
|
||||||
|
|
||||||
|
/* Task States */
|
||||||
|
typedef enum {
|
||||||
|
TASK_SUSPENDED = 0,
|
||||||
|
TASK_READY = 1,
|
||||||
|
TASK_RUNNING = 2,
|
||||||
|
TASK_BLOCKED = 3,
|
||||||
|
TASK_TERMINATED = 4
|
||||||
|
} TaskState_t;
|
||||||
|
|
||||||
|
/* Task Priority Type */
|
||||||
|
typedef uint8_t TaskPriority_t;
|
||||||
|
|
||||||
|
/* Task Handle */
|
||||||
|
typedef struct TaskControlBlock* TaskHandle_t;
|
||||||
|
|
||||||
|
/* Task Function */
|
||||||
|
typedef void (*TaskFunction_t)(void* parameters);
|
||||||
|
|
||||||
|
/* Time Types */
|
||||||
|
typedef uint32_t TickType_t;
|
||||||
|
typedef uint32_t TimeOut_t;
|
||||||
|
|
||||||
|
/* Error Codes */
|
||||||
|
typedef enum {
|
||||||
|
KERNEL_OK = 0,
|
||||||
|
KERNEL_ERROR = -1,
|
||||||
|
KERNEL_INVALID_PARAMETER = -2,
|
||||||
|
KERNEL_OUT_OF_MEMORY = -3,
|
||||||
|
KERNEL_TASK_NOT_FOUND = -4,
|
||||||
|
KERNEL_PRIORITY_INVALID = -5,
|
||||||
|
KERNEL_TIMEOUT = -6,
|
||||||
|
KERNEL_RESOURCE_BUSY = -7
|
||||||
|
} KernelStatus_t;
|
||||||
|
|
||||||
|
/* Task Creation Parameters */
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
TaskFunction_t function;
|
||||||
|
void* parameters;
|
||||||
|
uint32_t stack_size;
|
||||||
|
TaskPriority_t priority;
|
||||||
|
TickType_t period_ticks; /* 0 for aperiodic tasks */
|
||||||
|
} TaskConfig_t;
|
||||||
|
|
||||||
|
/* Task Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t execution_count;
|
||||||
|
TickType_t last_execution_time;
|
||||||
|
TickType_t max_execution_time;
|
||||||
|
TickType_t total_execution_time;
|
||||||
|
uint32_t stack_high_water_mark;
|
||||||
|
uint32_t deadline_misses;
|
||||||
|
} TaskStatistics_t;
|
||||||
|
|
||||||
|
/* Kernel Core Functions */
|
||||||
|
KernelStatus_t kernel_init(void);
|
||||||
|
KernelStatus_t kernel_start(void);
|
||||||
|
void kernel_stop(void);
|
||||||
|
TickType_t kernel_get_tick_count(void);
|
||||||
|
KernelStatus_t kernel_delay(TickType_t ticks);
|
||||||
|
|
||||||
|
/* Task Management Functions */
|
||||||
|
TaskHandle_t task_create(const TaskConfig_t* config);
|
||||||
|
KernelStatus_t task_delete(TaskHandle_t task);
|
||||||
|
KernelStatus_t task_suspend(TaskHandle_t task);
|
||||||
|
KernelStatus_t task_resume(TaskHandle_t task);
|
||||||
|
KernelStatus_t task_set_priority(TaskHandle_t task, TaskPriority_t new_priority);
|
||||||
|
TaskPriority_t task_get_priority(TaskHandle_t task);
|
||||||
|
TaskState_t task_get_state(TaskHandle_t task);
|
||||||
|
KernelStatus_t task_get_statistics(TaskHandle_t task, TaskStatistics_t* stats);
|
||||||
|
|
||||||
|
/* Scheduler Control */
|
||||||
|
void scheduler_yield(void);
|
||||||
|
void scheduler_lock(void);
|
||||||
|
void scheduler_unlock(void);
|
||||||
|
TaskHandle_t scheduler_get_current_task(void);
|
||||||
|
|
||||||
|
#endif /* KERNEL_H */
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @file mutex.h
|
||||||
|
* @brief Mutex with priority inheritance
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MUTEX_H
|
||||||
|
#define MUTEX_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* Mutex Control Block */
|
||||||
|
typedef struct {
|
||||||
|
TaskHandle_t owner;
|
||||||
|
TaskPriority_t original_priority;
|
||||||
|
uint32_t lock_count;
|
||||||
|
TaskHandle_t* waiting_tasks;
|
||||||
|
uint32_t waiting_count;
|
||||||
|
bool recursive;
|
||||||
|
} Mutex_t;
|
||||||
|
|
||||||
|
/* Mutex Functions */
|
||||||
|
KernelStatus_t mutex_create(Mutex_t* mutex, bool recursive);
|
||||||
|
KernelStatus_t mutex_lock(Mutex_t* mutex, TimeOut_t timeout);
|
||||||
|
KernelStatus_t mutex_unlock(Mutex_t* mutex);
|
||||||
|
KernelStatus_t mutex_delete(Mutex_t* mutex);
|
||||||
|
TaskHandle_t mutex_get_owner(Mutex_t* mutex);
|
||||||
|
|
||||||
|
#endif /* MUTEX_H */
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* @file queue.h
|
||||||
|
* @brief Message queue for inter-task communication
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QUEUE_H
|
||||||
|
#define QUEUE_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* Queue Control Block */
|
||||||
|
typedef struct {
|
||||||
|
void* buffer;
|
||||||
|
uint32_t item_size;
|
||||||
|
uint32_t max_items;
|
||||||
|
uint32_t current_items;
|
||||||
|
uint32_t head;
|
||||||
|
uint32_t tail;
|
||||||
|
TaskHandle_t* waiting_senders;
|
||||||
|
TaskHandle_t* waiting_receivers;
|
||||||
|
uint32_t waiting_sender_count;
|
||||||
|
uint32_t waiting_receiver_count;
|
||||||
|
} Queue_t;
|
||||||
|
|
||||||
|
/* Queue Functions */
|
||||||
|
KernelStatus_t queue_create(Queue_t* queue, void* buffer, uint32_t item_size,
|
||||||
|
uint32_t max_items);
|
||||||
|
KernelStatus_t queue_send(Queue_t* queue, const void* item, TimeOut_t timeout);
|
||||||
|
KernelStatus_t queue_receive(Queue_t* queue, void* item, TimeOut_t timeout);
|
||||||
|
KernelStatus_t queue_send_from_isr(Queue_t* queue, const void* item);
|
||||||
|
KernelStatus_t queue_receive_from_isr(Queue_t* queue, void* item);
|
||||||
|
uint32_t queue_get_count(Queue_t* queue);
|
||||||
|
KernelStatus_t queue_delete(Queue_t* queue);
|
||||||
|
|
||||||
|
#endif /* QUEUE_H */
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @file scheduler.h
|
||||||
|
* @brief Priority-based preemptive scheduler
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SCHEDULER_H
|
||||||
|
#define SCHEDULER_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
/* Scheduler Types */
|
||||||
|
typedef enum {
|
||||||
|
SCHEDULER_PRIORITY_PREEMPTIVE = 0,
|
||||||
|
SCHEDULER_ROUND_ROBIN = 1,
|
||||||
|
SCHEDULER_EDF = 2, /* Earliest Deadline First */
|
||||||
|
SCHEDULER_RATE_MONOTONIC = 3
|
||||||
|
} SchedulerType_t;
|
||||||
|
|
||||||
|
/* Scheduler Configuration */
|
||||||
|
typedef struct {
|
||||||
|
SchedulerType_t type;
|
||||||
|
TickType_t time_slice_ticks; /* For round-robin */
|
||||||
|
bool enable_deadline_monitoring;
|
||||||
|
} SchedulerConfig_t;
|
||||||
|
|
||||||
|
/* Scheduler Interface */
|
||||||
|
void scheduler_init(const SchedulerConfig_t* config);
|
||||||
|
void scheduler_start(void);
|
||||||
|
void scheduler_tick(void);
|
||||||
|
void scheduler_add_task(TaskHandle_t task);
|
||||||
|
void scheduler_remove_task(TaskHandle_t task);
|
||||||
|
void scheduler_update_task_state(TaskHandle_t task, TaskState_t new_state);
|
||||||
|
TaskHandle_t scheduler_select_next_task(void);
|
||||||
|
void scheduler_context_switch(TaskHandle_t next_task);
|
||||||
|
|
||||||
|
/* Scheduler Statistics */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t context_switches;
|
||||||
|
uint32_t preemptions;
|
||||||
|
TickType_t max_scheduling_latency;
|
||||||
|
TickType_t total_idle_time;
|
||||||
|
} SchedulerStatistics_t;
|
||||||
|
|
||||||
|
void scheduler_get_statistics(SchedulerStatistics_t* stats);
|
||||||
|
|
||||||
|
#endif /* SCHEDULER_H */
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* @file semaphore.h
|
||||||
|
* @brief Semaphore synchronization primitive
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SEMAPHORE_H
|
||||||
|
#define SEMAPHORE_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* Semaphore Types */
|
||||||
|
typedef enum {
|
||||||
|
SEMAPHORE_BINARY = 0,
|
||||||
|
SEMAPHORE_COUNTING = 1,
|
||||||
|
SEMAPHORE_MUTEX = 2
|
||||||
|
} SemaphoreType_t;
|
||||||
|
|
||||||
|
/* Semaphore Control Block */
|
||||||
|
typedef struct {
|
||||||
|
SemaphoreType_t type;
|
||||||
|
uint32_t count;
|
||||||
|
uint32_t max_count;
|
||||||
|
TaskHandle_t owner; /* For mutex */
|
||||||
|
uint8_t priority_ceiling;
|
||||||
|
TaskHandle_t* waiting_tasks;
|
||||||
|
uint32_t waiting_count;
|
||||||
|
} Semaphore_t;
|
||||||
|
|
||||||
|
/* Semaphore Functions */
|
||||||
|
KernelStatus_t semaphore_create(Semaphore_t* sem, SemaphoreType_t type,
|
||||||
|
uint32_t initial_count, uint32_t max_count);
|
||||||
|
KernelStatus_t semaphore_take(Semaphore_t* sem, TimeOut_t timeout);
|
||||||
|
KernelStatus_t semaphore_give(Semaphore_t* sem);
|
||||||
|
KernelStatus_t semaphore_delete(Semaphore_t* sem);
|
||||||
|
uint32_t semaphore_get_count(Semaphore_t* sem);
|
||||||
|
|
||||||
|
#endif /* SEMAPHORE_H */
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* @file task.h
|
||||||
|
* @brief Task management interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TASK_H
|
||||||
|
#define TASK_H
|
||||||
|
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/* Internal Task Control Block */
|
||||||
|
struct TaskControlBlock {
|
||||||
|
/* Task Identification */
|
||||||
|
char name[MAX_TASK_NAME_LENGTH];
|
||||||
|
TaskHandle_t self;
|
||||||
|
uint32_t task_id;
|
||||||
|
|
||||||
|
/* Task Function */
|
||||||
|
TaskFunction_t function;
|
||||||
|
void* parameters;
|
||||||
|
|
||||||
|
/* Stack Management */
|
||||||
|
uint32_t* stack_pointer;
|
||||||
|
uint32_t* stack_base;
|
||||||
|
uint32_t stack_size;
|
||||||
|
uint32_t stack_high_water_mark;
|
||||||
|
|
||||||
|
/* Scheduling Information */
|
||||||
|
TaskPriority_t priority;
|
||||||
|
TaskState_t state;
|
||||||
|
TickType_t period_ticks;
|
||||||
|
TickType_t last_wake_time;
|
||||||
|
TickType_t deadline_ticks;
|
||||||
|
|
||||||
|
/* Blocking Information */
|
||||||
|
TickType_t block_timeout;
|
||||||
|
void* blocked_on;
|
||||||
|
|
||||||
|
/* Statistics */
|
||||||
|
TaskStatistics_t statistics;
|
||||||
|
|
||||||
|
/* List Management */
|
||||||
|
struct TaskControlBlock* next;
|
||||||
|
struct TaskControlBlock* prev;
|
||||||
|
|
||||||
|
/* Architecture Specific */
|
||||||
|
uint32_t context[32]; /* CPU register context */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Internal Task Management Functions */
|
||||||
|
void task_init(void);
|
||||||
|
void task_switch_context(TaskHandle_t next_task);
|
||||||
|
TaskHandle_t task_get_idle_task(void);
|
||||||
|
void task_update_statistics(void);
|
||||||
|
void task_check_stack_overflow(void);
|
||||||
|
bool task_is_ready(TaskHandle_t task);
|
||||||
|
|
||||||
|
#endif /* TASK_H */
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user