This commit is contained in:
orin
2026-07-30 08:45:38 +08:00
commit 25b24ac4cd
1333 changed files with 170092 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
# T2G seat-controller firmware
`Seat_Controller_demo/` is the repository-owned IAR interface project for the
physical seat bench. It combines the verified heater, fan and four-motor demos
and is the only firmware accepted by the hardware executor.
The Infineon/T2G vendor library is intentionally not duplicated here. The IAR
project keeps its original relative `common/` and `tviibh8m/` references;
`tools/build_seat_firmware.ps1` stages the repository project beside junctions
to `COGNITIVE_AGENT_T2G_SDK_ROOT`, builds it, and copies only the resulting
`Seat_Controller_demo.hex` and `Seat_Controller_demo.out` back into the
repository.
```powershell
powershell -ExecutionPolicy Bypass -File .\tools\build_seat_firmware.ps1 -Profile combined
```
The build script never opens a debug probe or flashes a target. Physical
programming is performed only by an authorized `execution_mode=hardware`
request when `COGNITIVE_AGENT_SEAT_AUTO_FLASH_ENABLED=true` and the CAN
identity handshake does not match the repository firmware.
Automatic programming uses `IAR/Seat_Controller_program.mac`, not the
interactive-debug `Seat_Controller_demo.mac`. The automatic-programming macro
forces motor, fan and heater outputs low before download, clears the retained
debug-session marker, and uses a bounded Download-and-Debug handoff with
`--leave_target_running`. `--download_only` is intentionally not used because
it leaves the CM0+ stopped after programming.
Firmware identity protocol:
- request: `0x304`, `53 45 41 54 01 00 00 00`
- expected response: `0x305`, `53 45 41 54 01 01 00 3F`
The response identifies protocol 1, firmware 1.1.0 and the combined capability
mask. It is a non-actuating readiness probe and is checked before any seat
control frame is sent.
+99
View File
@@ -0,0 +1,99 @@
/***************************************************************************//**
* \file main_cm0plus.c
*
* CYT4BFBCJE rev_d CM0+ entry point for the four-motor seat movement ECU.
*******************************************************************************/
#include "cy_project.h"
#include "cy_device_headers.h"
#include "bsp_seat_move_can.h"
#include "motor_control.h"
#include "safety_manager.h"
#include "seat_move_application.h"
#include "seat_move_config.h"
#include "bsp_seat_backrest_heater.h"
#include "bsp_seat_fan.h"
#include "seat_controller_thermal.h"
volatile uint32_t g_seatMoveResetReason = 0u;
volatile uint32_t g_seatMoveInitResult = MOTOR_API_FAILURE;
static void SeatMove_HoldSafeOff(void)
{
for(;;)
{
Motor_ForceSafeOff();
SeatControllerThermal_ForceSafeOff();
SafetyManager_KickWatchdog();
Cy_SysTick_DelayInUs(SEAT_MOVE_TASK_PERIOD_US);
}
}
int main(void)
{
bool startupInhibited;
(void)BspSeatBackrestHeater_ForceOff();
(void)BspSeatFan_ForceOff();
SystemInit();
/* DRVOFF is asserted and IN1..IN4 are GPIO-low before any motor clock,
* SPI transaction or CAN command can enable a bridge. */
Motor_ForceSafeOff();
if(!BspSeatBackrestHeater_ForceOff() || !BspSeatFan_ForceOff())
{
SeatMove_HoldSafeOff();
}
g_seatMoveResetReason = Cy_SysReset_GetResetReason();
Cy_SysReset_ClearAllResetReasons();
SafetyManager_Init(g_seatMoveResetReason);
startupInhibited = SafetyManager_IsInhibited();
__enable_irq();
g_seatMoveInitResult = Motor_Init();
if(g_seatMoveInitResult != MOTOR_API_SUCCESS)
{
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
}
/* A retained reset/debug fault must remain fail-safe, but it must not
* suppress CAN diagnostics. Motor_Init() establishes the timer and SPI
* state needed by status reporting, so re-apply the inhibit afterwards. */
if(startupInhibited)
{
Motor_LatchSafetyInhibitFromIsr(0u);
Motor_ForceSafeOff();
}
Motor_SetWatchdogServiceEnabled(true);
if(!SeatControllerThermal_Init())
{
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
}
if(!SeatMoveApplication_Init())
{
/* When CAN itself could not initialize there is no diagnostic path to
* preserve. Other startup failures remain visible through identity
* and inhibited status frames while every actuator stays safe-off. */
if(g_bspSeatMoveCanInitStatus != (uint32_t)CY_CANFD_SUCCESS)
{
SeatMove_HoldSafeOff();
}
}
for(;;)
{
SeatMoveApplication_Service();
SeatControllerThermal_Service1ms();
if(SafetyManager_IsInhibited())
{
Motor_ForceSafeOff();
SeatControllerThermal_ForceSafeOff();
}
SafetyManager_KickWatchdog();
Cy_SysTick_DelayInUs(SEAT_MOVE_TASK_PERIOD_US);
}
}
/* [] END OF FILE */
+173
View File
@@ -0,0 +1,173 @@
#include "safety_manager.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "motor_control.h"
#include "seat_motor_manager.h"
#include "seat_move_config.h"
#if defined(CoreDebug) && defined(CoreDebug_DHCSR_C_DEBUGEN_Msk)
#define SAFETY_DEBUG_DETACH_SUPPORTED (1u)
#else
#define SAFETY_DEBUG_DETACH_SUPPORTED (0u)
#endif
static volatile safety_fault_t latchedFault = SAFETY_FAULT_NONE;
static bool watchdogReset = false;
static bool watchdogInitialized = false;
#if (SAFETY_DEBUG_DETACH_SUPPORTED == 1u)
static bool debuggerWasAttached = false;
#endif
static bool SafetyManager_DebuggerAttached(void)
{
#if (SAFETY_DEBUG_DETACH_SUPPORTED == 1u)
return ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) != 0ul);
#else
return false;
#endif
}
static uint32_t SafetyManager_ReadDebugMarker(void)
{
return BACKUP->unBREG[SEAT_MOVE_DEBUG_SESSION_BREG_INDEX].u32Register;
}
static bool SafetyManager_WriteDebugMarker(uint32_t marker)
{
BACKUP->unBREG[SEAT_MOVE_DEBUG_SESSION_BREG_INDEX].u32Register = marker;
__DSB();
return (SafetyManager_ReadDebugMarker() == marker);
}
void SafetyManager_Init(uint32_t resetReason)
{
const uint32_t watchdogMask = CY_SYSRESET_WDT |
CY_SYSRESET_MCWDT0 |
CY_SYSRESET_MCWDT1 |
CY_SYSRESET_MCWDT2 |
CY_SYSRESET_MCWDT3;
bool debuggerAttached = SafetyManager_DebuggerAttached();
uint32_t marker = SafetyManager_ReadDebugMarker();
bool markerOk = true;
latchedFault = SAFETY_FAULT_NONE;
watchdogInitialized = false;
watchdogReset = ((resetReason & watchdogMask) != 0ul);
if((resetReason & CY_SYSRESET_PORVDDD) != 0ul)
{
markerOk = SafetyManager_WriteDebugMarker(0u);
marker = SafetyManager_ReadDebugMarker();
}
#if (SAFETY_DEBUG_DETACH_SUPPORTED == 1u)
debuggerWasAttached = debuggerAttached;
#endif
if(!markerOk || (marker == SEAT_MOVE_DEBUG_EXIT_MAGIC) ||
((marker == SEAT_MOVE_DEBUG_ACTIVE_MAGIC) && !debuggerAttached))
{
latchedFault = SAFETY_FAULT_DEBUG_DETACH;
}
else if(watchdogReset)
{
latchedFault = SAFETY_FAULT_BOOT_RECOVERY;
}
else if(debuggerAttached &&
!SafetyManager_WriteDebugMarker(SEAT_MOVE_DEBUG_ACTIVE_MAGIC))
{
latchedFault = SAFETY_FAULT_DEBUG_DETACH;
}
Cy_WDT_Init();
Cy_WDT_Unlock();
Cy_WDT_SetUpperLimit(SEAT_MOVE_WDT_TIMEOUT_TICKS);
Cy_WDT_SetUpperAction(CY_WDT_LOW_UPP_ACTION_RESET);
Cy_WDT_SetWarnAction(CY_WDT_WARN_ACTION_NONE);
Cy_WDT_SetDebugRun(CY_WDT_ENABLE);
Cy_WDT_Lock();
Cy_WDT_Enable();
watchdogInitialized = true;
SafetyManager_KickWatchdog();
}
void SafetyManager_KickWatchdog(void)
{
if(false != watchdogInitialized)
{
Cy_WDT_ClearWatchdog();
}
}
bool SafetyManager_IsWatchdogInitialized(void)
{
return watchdogInitialized;
}
void SafetyManager_LatchFault(safety_fault_t fault)
{
if((fault != SAFETY_FAULT_NONE) &&
(latchedFault == SAFETY_FAULT_NONE))
{
latchedFault = fault;
Motor_LatchSafetyInhibitFromIsr(0u);
Motor_StopAll();
SeatMotorManager_StopAll(0ul);
}
}
void SafetyManager_Service(void)
{
bool debuggerAttached = SafetyManager_DebuggerAttached();
uint32_t marker = SafetyManager_ReadDebugMarker();
if(marker == SEAT_MOVE_DEBUG_EXIT_MAGIC)
{
SafetyManager_LatchFault(SAFETY_FAULT_DEBUG_DETACH);
}
if(Motor_IsDriverFaultLatched())
{
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
}
else
{
/* No newly observed driver fault. */
}
#if (SAFETY_DEBUG_DETACH_SUPPORTED == 1u)
if(debuggerAttached)
{
if((marker != SEAT_MOVE_DEBUG_ACTIVE_MAGIC) &&
!SafetyManager_WriteDebugMarker(SEAT_MOVE_DEBUG_ACTIVE_MAGIC))
{
SafetyManager_LatchFault(SAFETY_FAULT_DEBUG_DETACH);
}
debuggerWasAttached = true;
}
else if(debuggerWasAttached)
{
SafetyManager_LatchFault(SAFETY_FAULT_DEBUG_DETACH);
debuggerWasAttached = false;
}
#else
(void)debuggerAttached;
#endif
SafetyManager_KickWatchdog();
}
bool SafetyManager_IsInhibited(void)
{
return (latchedFault != SAFETY_FAULT_NONE);
}
safety_fault_t SafetyManager_GetFault(void)
{
return latchedFault;
}
bool SafetyManager_WasWatchdogReset(void)
{
return watchdogReset;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef SAFETY_MANAGER_H
#define SAFETY_MANAGER_H
#include <stdbool.h>
#include <stdint.h>
typedef enum
{
SAFETY_FAULT_NONE = 0,
SAFETY_FAULT_DRIVER = 1,
SAFETY_FAULT_CAN_BUS_OFF = 2,
SAFETY_FAULT_CONFIG_STORE = 3,
SAFETY_FAULT_BOOT_RECOVERY = 4,
SAFETY_FAULT_DEBUG_DETACH = 5,
SAFETY_FAULT_CAN_INIT = 6
} safety_fault_t;
void SafetyManager_Init(uint32_t resetReason);
void SafetyManager_Service(void);
void SafetyManager_LatchFault(safety_fault_t fault);
void SafetyManager_KickWatchdog(void);
bool SafetyManager_IsWatchdogInitialized(void);
bool SafetyManager_IsInhibited(void);
safety_fault_t SafetyManager_GetFault(void);
bool SafetyManager_WasWatchdogReset(void);
#endif /* SAFETY_MANAGER_H */
@@ -0,0 +1,258 @@
#include "seat_backrest_heater.h"
#include "bsp_seat_backrest_heater.h"
#include "bsp_seat_backrest_heater_adc.h"
#include "seat_backrest_heater_config.h"
volatile seat_backrest_heater_level_t g_seatBackrestHeaterRequestedLevel =
(seat_backrest_heater_level_t)SEAT_BACKREST_HEATER_DEFAULT_LEVEL;
volatile seat_backrest_heater_level_t g_seatBackrestHeaterAppliedLevel =
SEAT_BACKREST_HEATER_LEVEL_OFF;
volatile seat_backrest_heater_state_t g_seatBackrestHeaterState =
SEAT_BACKREST_HEATER_STATE_UNINITIALIZED;
volatile uint32_t g_seatBackrestHeaterDutyPercent = 0u;
volatile uint32_t g_seatBackrestHeaterActiveTimeMs = 0u;
volatile uint32_t g_seatBackrestHeaterLoopCount = 0u;
volatile uint32_t g_seatBackrestHeaterTimeoutCount = 0u;
volatile uint32_t g_seatBackrestHeaterAdcErrorCount = 0u;
volatile uint16_t g_seatBackrestHeaterSenseRaw = 0u;
volatile uint32_t g_seatBackrestHeaterEstimatedCurrentMa = 0u;
static bool s_faultLatched = false;
static uint32_t s_senseSampleElapsedMs = 0u;
static uint32_t s_senseConsecutiveErrors = 0u;
static bool SeatBackrestHeater_ApplyLevel(
seat_backrest_heater_level_t level);
static uint32_t SeatBackrestHeater_LevelToDuty(
seat_backrest_heater_level_t level);
static seat_backrest_heater_state_t SeatBackrestHeater_LevelToState(
seat_backrest_heater_level_t level);
static void SeatBackrestHeater_LatchFault(
seat_backrest_heater_state_t state);
bool SeatBackrestHeater_Init(void)
{
BspSeatBackrestHeater_FailSafeOff();
if(!BspSeatBackrestHeater_InitPwm())
{
SeatBackrestHeater_LatchFault(
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF);
return false;
}
if(!BspSeatBackrestHeaterAdc_Init())
{
SeatBackrestHeater_LatchFault(
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF);
return false;
}
s_faultLatched = false;
s_senseSampleElapsedMs = 0u;
s_senseConsecutiveErrors = 0u;
g_seatBackrestHeaterRequestedLevel =
(seat_backrest_heater_level_t)SEAT_BACKREST_HEATER_DEFAULT_LEVEL;
g_seatBackrestHeaterAppliedLevel = SEAT_BACKREST_HEATER_LEVEL_OFF;
g_seatBackrestHeaterState = SEAT_BACKREST_HEATER_STATE_OFF;
g_seatBackrestHeaterDutyPercent = 0u;
g_seatBackrestHeaterActiveTimeMs = 0u;
return true;
}
void SeatBackrestHeater_Task1ms(void)
{
g_seatBackrestHeaterLoopCount++;
if((uint32_t)g_seatBackrestHeaterRequestedLevel >
(uint32_t)SEAT_BACKREST_HEATER_LEVEL_HIGH)
{
SeatBackrestHeater_LatchFault(
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF);
return;
}
if(s_faultLatched)
{
BspSeatBackrestHeater_FailSafeOff();
return;
}
if(g_seatBackrestHeaterRequestedLevel !=
g_seatBackrestHeaterAppliedLevel)
{
if(!SeatBackrestHeater_ApplyLevel(
g_seatBackrestHeaterRequestedLevel))
{
SeatBackrestHeater_LatchFault(
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF);
return;
}
}
if(g_seatBackrestHeaterAppliedLevel !=
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
g_seatBackrestHeaterActiveTimeMs++;
if(g_seatBackrestHeaterActiveTimeMs >=
SEAT_BACKREST_HEATER_MAX_ACTIVE_TIME_MS)
{
g_seatBackrestHeaterTimeoutCount++;
g_seatBackrestHeaterRequestedLevel =
SEAT_BACKREST_HEATER_LEVEL_OFF;
SeatBackrestHeater_LatchFault(
SEAT_BACKREST_HEATER_STATE_TIMEOUT_SAFE_OFF);
return;
}
}
s_senseSampleElapsedMs++;
if(s_senseSampleElapsedMs >= SEAT_BACKREST_HEATER_SENSE_SAMPLE_MS)
{
s_senseSampleElapsedMs = 0u;
if(BspSeatBackrestHeaterAdc_Sample())
{
s_senseConsecutiveErrors = 0u;
g_seatBackrestHeaterSenseRaw =
BspSeatBackrestHeaterAdc_GetRaw();
g_seatBackrestHeaterEstimatedCurrentMa =
BspSeatBackrestHeaterAdc_GetEstimatedCurrentMilliamp();
}
else
{
g_seatBackrestHeaterAdcErrorCount++;
s_senseConsecutiveErrors++;
if(s_senseConsecutiveErrors >=
SEAT_BACKREST_HEATER_SENSE_MAX_ERRORS)
{
SeatBackrestHeater_LatchFault(
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF);
}
}
}
}
bool SeatBackrestHeater_SetLevel(seat_backrest_heater_level_t level)
{
if((uint32_t)level > (uint32_t)SEAT_BACKREST_HEATER_LEVEL_HIGH)
{
return false;
}
if(s_faultLatched && (level != SEAT_BACKREST_HEATER_LEVEL_OFF))
{
return false;
}
g_seatBackrestHeaterRequestedLevel = level;
return true;
}
bool SeatBackrestHeater_ClearFault(void)
{
if(g_seatBackrestHeaterRequestedLevel !=
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
return false;
}
BspSeatBackrestHeater_FailSafeOff();
s_faultLatched = false;
g_seatBackrestHeaterAppliedLevel = SEAT_BACKREST_HEATER_LEVEL_OFF;
g_seatBackrestHeaterState = SEAT_BACKREST_HEATER_STATE_OFF;
g_seatBackrestHeaterDutyPercent = 0u;
g_seatBackrestHeaterActiveTimeMs = 0u;
s_senseConsecutiveErrors = 0u;
return true;
}
void SeatBackrestHeater_EmergencyOff(void)
{
g_seatBackrestHeaterRequestedLevel = SEAT_BACKREST_HEATER_LEVEL_OFF;
SeatBackrestHeater_LatchFault(
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF);
}
static bool SeatBackrestHeater_ApplyLevel(
seat_backrest_heater_level_t level)
{
uint32_t dutyPercent;
if(level == SEAT_BACKREST_HEATER_LEVEL_OFF)
{
BspSeatBackrestHeater_FailSafeOff();
g_seatBackrestHeaterAppliedLevel = level;
g_seatBackrestHeaterState = SEAT_BACKREST_HEATER_STATE_OFF;
g_seatBackrestHeaterDutyPercent = 0u;
g_seatBackrestHeaterActiveTimeMs = 0u;
return true;
}
dutyPercent = SeatBackrestHeater_LevelToDuty(level);
if(dutyPercent == 0u)
{
return false;
}
if(BspSeatBackrestHeater_IsRunning())
{
if(!BspSeatBackrestHeater_SetDutyPercent(dutyPercent))
{
return false;
}
}
else if(!BspSeatBackrestHeater_Start(dutyPercent))
{
return false;
}
if(g_seatBackrestHeaterAppliedLevel ==
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
g_seatBackrestHeaterActiveTimeMs = 0u;
}
g_seatBackrestHeaterAppliedLevel = level;
g_seatBackrestHeaterState = SeatBackrestHeater_LevelToState(level);
g_seatBackrestHeaterDutyPercent = dutyPercent;
return true;
}
static uint32_t SeatBackrestHeater_LevelToDuty(
seat_backrest_heater_level_t level)
{
switch(level)
{
case SEAT_BACKREST_HEATER_LEVEL_LOW:
return SEAT_BACKREST_HEATER_LOW_DUTY_PERCENT;
case SEAT_BACKREST_HEATER_LEVEL_MEDIUM:
return SEAT_BACKREST_HEATER_MEDIUM_DUTY_PERCENT;
case SEAT_BACKREST_HEATER_LEVEL_HIGH:
return SEAT_BACKREST_HEATER_HIGH_DUTY_PERCENT;
default:
return 0u;
}
}
static seat_backrest_heater_state_t SeatBackrestHeater_LevelToState(
seat_backrest_heater_level_t level)
{
switch(level)
{
case SEAT_BACKREST_HEATER_LEVEL_LOW:
return SEAT_BACKREST_HEATER_STATE_LOW;
case SEAT_BACKREST_HEATER_LEVEL_MEDIUM:
return SEAT_BACKREST_HEATER_STATE_MEDIUM;
case SEAT_BACKREST_HEATER_LEVEL_HIGH:
return SEAT_BACKREST_HEATER_STATE_HIGH;
default:
return SEAT_BACKREST_HEATER_STATE_OFF;
}
}
static void SeatBackrestHeater_LatchFault(
seat_backrest_heater_state_t state)
{
BspSeatBackrestHeater_FailSafeOff();
s_faultLatched = true;
g_seatBackrestHeaterAppliedLevel = SEAT_BACKREST_HEATER_LEVEL_OFF;
g_seatBackrestHeaterDutyPercent = 0u;
g_seatBackrestHeaterState = state;
}
@@ -0,0 +1,44 @@
#ifndef SEAT_BACKREST_HEATER_H
#define SEAT_BACKREST_HEATER_H
#include <stdbool.h>
#include <stdint.h>
typedef enum
{
SEAT_BACKREST_HEATER_LEVEL_OFF = 0u,
SEAT_BACKREST_HEATER_LEVEL_LOW = 1u,
SEAT_BACKREST_HEATER_LEVEL_MEDIUM = 2u,
SEAT_BACKREST_HEATER_LEVEL_HIGH = 3u,
} seat_backrest_heater_level_t;
typedef enum
{
SEAT_BACKREST_HEATER_STATE_UNINITIALIZED = 0u,
SEAT_BACKREST_HEATER_STATE_OFF,
SEAT_BACKREST_HEATER_STATE_LOW,
SEAT_BACKREST_HEATER_STATE_MEDIUM,
SEAT_BACKREST_HEATER_STATE_HIGH,
SEAT_BACKREST_HEATER_STATE_TIMEOUT_SAFE_OFF,
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF = 0xE0u,
} seat_backrest_heater_state_t;
bool SeatBackrestHeater_Init(void);
void SeatBackrestHeater_Task1ms(void);
bool SeatBackrestHeater_SetLevel(seat_backrest_heater_level_t level);
bool SeatBackrestHeater_ClearFault(void);
void SeatBackrestHeater_EmergencyOff(void);
/* IAR Watch/debug integration. Set requested level to 0, 1, 2, or 3. */
extern volatile seat_backrest_heater_level_t g_seatBackrestHeaterRequestedLevel;
extern volatile seat_backrest_heater_level_t g_seatBackrestHeaterAppliedLevel;
extern volatile seat_backrest_heater_state_t g_seatBackrestHeaterState;
extern volatile uint32_t g_seatBackrestHeaterDutyPercent;
extern volatile uint32_t g_seatBackrestHeaterActiveTimeMs;
extern volatile uint32_t g_seatBackrestHeaterLoopCount;
extern volatile uint32_t g_seatBackrestHeaterTimeoutCount;
extern volatile uint32_t g_seatBackrestHeaterAdcErrorCount;
extern volatile uint16_t g_seatBackrestHeaterSenseRaw;
extern volatile uint32_t g_seatBackrestHeaterEstimatedCurrentMa;
#endif /* SEAT_BACKREST_HEATER_H */
@@ -0,0 +1,426 @@
#include "seat_controller_thermal.h"
#include "bsp_seat_backrest_heater.h"
#include "bsp_seat_fan.h"
#include "safety_manager.h"
#include "seat_backrest_heater.h"
#include "seat_backrest_heater_config.h"
#include "seat_fan_config.h"
#define SEAT_CONTROLLER_THERMAL_CONTROL_ID (0x29Eu)
#define SEAT_CONTROLLER_THERMAL_CONFIG_ID (0x2AAu)
#define SEAT_CONTROLLER_THERMAL_MODE_ID (0x3CEu)
#define SEAT_CONTROLLER_THERMAL_SWITCH_ID (0x42Au)
#define SEAT_CONTROLLER_THERMAL_FEEDBACK_ID (0x4FAu)
#define SEAT_CONTROLLER_THERMAL_FEEDBACK_MS (100u)
volatile uint8_t g_seatControllerFanFeedbackRaw = 1u;
volatile uint8_t g_seatControllerHeaterFeedbackRaw = 1u;
volatile uint32_t g_seatControllerThermalRxCount = 0u;
volatile uint32_t g_seatControllerThermalInvalidCount = 0u;
volatile uint32_t g_seatControllerThermalFeedbackTxCount = 0u;
volatile uint32_t g_seatControllerThermalFeedbackTxFailureCount = 0u;
static bool s_fanRunning = false;
static bool s_heaterModeValid = false;
static bool s_feedbackPending = true;
static uint8_t s_fanLastVehicleGear = SEAT_FAN_DEFAULT_GEAR;
static uint8_t s_fanTargetDutyPercent = 0u;
static uint8_t s_fanConfiguredMaxGear = 3u;
static uint8_t s_heaterLastVehicleGear =
SEAT_BACKREST_HEATER_DEFAULT_CAN_GEAR;
static uint8_t s_heaterConfiguredMaxLevel = 3u;
static uint32_t s_fanStartupRemainingMs = 0u;
static uint32_t s_feedbackElapsedMs = 0u;
static void SeatControllerThermal_HandleControl29e(
const bsp_seat_move_can_frame_t *frame);
static void SeatControllerThermal_HandleConfig2aa(
const bsp_seat_move_can_frame_t *frame);
static void SeatControllerThermal_HandleMode3ce(
const bsp_seat_move_can_frame_t *frame);
static void SeatControllerThermal_HandleSwitch42a(
const bsp_seat_move_can_frame_t *frame);
static bool SeatControllerThermal_RequestFan(uint8_t vehicleGear);
static bool SeatControllerThermal_RequestHeater(uint8_t vehicleGear);
static void SeatControllerThermal_StopFan(uint8_t feedbackRaw);
static void SeatControllerThermal_StopHeater(uint8_t feedbackRaw);
static uint8_t SeatControllerThermal_GearToDuty(uint8_t physicalGear);
static void SeatControllerThermal_LatchOutputFault(void);
static void SeatControllerThermal_ServiceFeedback(void);
bool SeatControllerThermal_Init(void)
{
if(!SeatBackrestHeater_Init())
{
SeatControllerThermal_ForceSafeOff();
return false;
}
if(!BspSeatFan_InitPwm())
{
SeatControllerThermal_ForceSafeOff();
return false;
}
SeatControllerThermal_StopFan(1u);
SeatControllerThermal_StopHeater(1u);
s_feedbackElapsedMs = SEAT_CONTROLLER_THERMAL_FEEDBACK_MS;
s_feedbackPending = true;
return true;
}
void SeatControllerThermal_HandleFrame(const bsp_seat_move_can_frame_t *frame)
{
if(frame == NULL)
{
g_seatControllerThermalInvalidCount++;
return;
}
g_seatControllerThermalRxCount++;
switch(frame->identifier)
{
case SEAT_CONTROLLER_THERMAL_CONTROL_ID:
SeatControllerThermal_HandleControl29e(frame);
break;
case SEAT_CONTROLLER_THERMAL_CONFIG_ID:
SeatControllerThermal_HandleConfig2aa(frame);
break;
case SEAT_CONTROLLER_THERMAL_MODE_ID:
SeatControllerThermal_HandleMode3ce(frame);
break;
case SEAT_CONTROLLER_THERMAL_SWITCH_ID:
SeatControllerThermal_HandleSwitch42a(frame);
break;
default:
g_seatControllerThermalInvalidCount++;
break;
}
}
void SeatControllerThermal_Service1ms(void)
{
SeatBackrestHeater_Task1ms();
if((g_seatBackrestHeaterState ==
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF) ||
(g_seatBackrestHeaterState ==
SEAT_BACKREST_HEATER_STATE_TIMEOUT_SAFE_OFF) ||
(g_seatBackrestHeaterState ==
SEAT_BACKREST_HEATER_STATE_UNINITIALIZED))
{
g_seatControllerHeaterFeedbackRaw = 0u;
s_feedbackPending = true;
}
else if(g_seatBackrestHeaterAppliedLevel ==
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
if(g_seatControllerHeaterFeedbackRaw != 0u)
{
g_seatControllerHeaterFeedbackRaw = 1u;
}
}
else
{
g_seatControllerHeaterFeedbackRaw =
(uint8_t)g_seatBackrestHeaterAppliedLevel + 1u;
}
if(s_fanRunning && (s_fanStartupRemainingMs > 0u))
{
s_fanStartupRemainingMs--;
if((s_fanStartupRemainingMs == 0u) &&
!BspSeatFan_SetDutyPercent(s_fanTargetDutyPercent))
{
SeatControllerThermal_LatchOutputFault();
}
}
SeatControllerThermal_ServiceFeedback();
}
void SeatControllerThermal_ForceSafeOff(void)
{
SeatBackrestHeater_EmergencyOff();
BspSeatBackrestHeater_FailSafeOff();
BspSeatFan_FailSafeOff();
s_fanRunning = false;
s_fanStartupRemainingMs = 0u;
g_seatControllerFanFeedbackRaw = 0u;
g_seatControllerHeaterFeedbackRaw = 0u;
s_feedbackPending = true;
}
static void SeatControllerThermal_HandleControl29e(
const bsp_seat_move_can_frame_t *frame)
{
uint8_t fanOn;
uint8_t fanMode;
uint8_t fanGear;
uint8_t heaterOn;
uint8_t heaterGear;
if(frame->dlc < 3u)
{
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopFan(0u);
SeatControllerThermal_StopHeater(0u);
return;
}
fanOn = (uint8_t)(frame->data0 & 0x03u);
fanMode = (uint8_t)((frame->data0 >> 2u) & 0x03u);
heaterOn = (uint8_t)((frame->data0 >> 4u) & 0x03u);
fanGear = (uint8_t)((frame->data0 >> 16u) & 0x0Fu);
heaterGear = (uint8_t)((frame->data0 >> 20u) & 0x0Fu);
if(fanOn == 2u)
{
SeatControllerThermal_StopFan(1u);
}
else if((fanOn == 1u) &&
((fanMode == 1u) || (fanMode == 2u)) &&
(fanGear >= 1u) && (fanGear <= 5u))
{
if(!SeatControllerThermal_RequestFan(fanGear))
{
SeatControllerThermal_LatchOutputFault();
}
}
else
{
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopFan(0u);
}
if(heaterOn == 2u)
{
SeatControllerThermal_StopHeater(1u);
}
else if((heaterOn == 1u) &&
(heaterGear >= 1u) && (heaterGear <= 5u) &&
s_heaterModeValid)
{
if(!SeatControllerThermal_RequestHeater(heaterGear))
{
SeatControllerThermal_LatchOutputFault();
}
}
else
{
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopHeater(0u);
}
}
static void SeatControllerThermal_HandleConfig2aa(
const bsp_seat_move_can_frame_t *frame)
{
uint8_t subId;
uint8_t fanConfig;
uint8_t heaterConfig;
if(frame->dlc < 3u)
{
g_seatControllerThermalInvalidCount++;
return;
}
subId = (uint8_t)(frame->data0 & 0xFFu);
fanConfig = (uint8_t)((frame->data0 >> 16u) & 0x07u);
heaterConfig = (uint8_t)((frame->data0 >> 19u) & 0x07u);
if((subId != 1u) || (fanConfig > 4u) || (heaterConfig > 4u))
{
g_seatControllerThermalInvalidCount++;
return;
}
s_fanConfiguredMaxGear = (fanConfig == 1u) ? 1u :
((fanConfig == 2u) ? 2u : 3u);
s_heaterConfiguredMaxLevel = (heaterConfig == 1u) ? 1u :
((heaterConfig == 2u) ? 2u : 3u);
if(s_fanRunning &&
!SeatControllerThermal_RequestFan(s_fanLastVehicleGear))
{
SeatControllerThermal_LatchOutputFault();
}
if(g_seatBackrestHeaterAppliedLevel !=
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
if(!SeatControllerThermal_RequestHeater(s_heaterLastVehicleGear))
{
SeatControllerThermal_LatchOutputFault();
}
}
}
static void SeatControllerThermal_HandleMode3ce(
const bsp_seat_move_can_frame_t *frame)
{
uint8_t mode;
if(frame->dlc < 1u)
{
s_heaterModeValid = false;
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopHeater(0u);
return;
}
mode = (uint8_t)((frame->data0 >> 4u) & 0x03u);
s_heaterModeValid = (mode == 1u) || (mode == 2u);
if(!s_heaterModeValid)
{
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopHeater(0u);
}
}
static void SeatControllerThermal_HandleSwitch42a(
const bsp_seat_move_can_frame_t *frame)
{
uint8_t fanSwitch;
uint8_t heaterSwitch;
if(frame->dlc < 1u)
{
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopFan(0u);
SeatControllerThermal_StopHeater(0u);
return;
}
fanSwitch = (uint8_t)(frame->data0 & 0x03u);
heaterSwitch = (uint8_t)((frame->data0 >> 2u) & 0x03u);
if(fanSwitch == 1u)
{
if(!SeatControllerThermal_RequestFan(s_fanLastVehicleGear))
{
SeatControllerThermal_LatchOutputFault();
}
}
else if(fanSwitch == 2u)
{
SeatControllerThermal_StopFan(1u);
}
if(heaterSwitch == 1u)
{
if(!SeatControllerThermal_RequestHeater(s_heaterLastVehicleGear))
{
SeatControllerThermal_LatchOutputFault();
}
}
else if(heaterSwitch == 2u)
{
SeatControllerThermal_StopHeater(1u);
}
}
static bool SeatControllerThermal_RequestFan(uint8_t vehicleGear)
{
uint8_t physicalGear = (vehicleGear >= 3u) ? 3u : vehicleGear;
if((vehicleGear == 0u) || (vehicleGear > 5u))
{
return false;
}
if(physicalGear > s_fanConfiguredMaxGear)
{
physicalGear = s_fanConfiguredMaxGear;
}
s_fanLastVehicleGear = vehicleGear;
s_fanTargetDutyPercent = SeatControllerThermal_GearToDuty(physicalGear);
g_seatControllerFanFeedbackRaw = physicalGear + 1u;
s_feedbackPending = true;
if(!s_fanRunning)
{
if(!BspSeatFan_Start(SEAT_FAN_STARTUP_DUTY_PERCENT))
{
return false;
}
s_fanRunning = true;
s_fanStartupRemainingMs = SEAT_FAN_STARTUP_TIME_MS;
}
else if(s_fanStartupRemainingMs == 0u)
{
return BspSeatFan_SetDutyPercent(s_fanTargetDutyPercent);
}
return true;
}
static bool SeatControllerThermal_RequestHeater(uint8_t vehicleGear)
{
uint8_t physicalLevel = (vehicleGear >= 3u) ? 3u : vehicleGear;
if((vehicleGear == 0u) || (vehicleGear > 5u) || !s_heaterModeValid)
{
return false;
}
if(physicalLevel > s_heaterConfiguredMaxLevel)
{
physicalLevel = s_heaterConfiguredMaxLevel;
}
s_heaterLastVehicleGear = vehicleGear;
if(!SeatBackrestHeater_SetLevel(
(seat_backrest_heater_level_t)physicalLevel))
{
return false;
}
g_seatControllerHeaterFeedbackRaw = physicalLevel + 1u;
s_feedbackPending = true;
return true;
}
static void SeatControllerThermal_StopFan(uint8_t feedbackRaw)
{
BspSeatFan_FailSafeOff();
s_fanRunning = false;
s_fanStartupRemainingMs = 0u;
s_fanTargetDutyPercent = 0u;
g_seatControllerFanFeedbackRaw = (feedbackRaw <= 4u) ? feedbackRaw : 0u;
s_feedbackPending = true;
}
static void SeatControllerThermal_StopHeater(uint8_t feedbackRaw)
{
if(!SeatBackrestHeater_SetLevel(SEAT_BACKREST_HEATER_LEVEL_OFF))
{
BspSeatBackrestHeater_FailSafeOff();
}
g_seatControllerHeaterFeedbackRaw =
(feedbackRaw <= 4u) ? feedbackRaw : 0u;
s_feedbackPending = true;
}
static uint8_t SeatControllerThermal_GearToDuty(uint8_t physicalGear)
{
if(physicalGear == 1u)
{
return SEAT_FAN_GEAR1_DUTY_PERCENT;
}
if(physicalGear == 2u)
{
return SEAT_FAN_GEAR2_DUTY_PERCENT;
}
return SEAT_FAN_GEAR3_DUTY_PERCENT;
}
static void SeatControllerThermal_LatchOutputFault(void)
{
SeatControllerThermal_ForceSafeOff();
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
}
static void SeatControllerThermal_ServiceFeedback(void)
{
uint32_t data0;
s_feedbackElapsedMs++;
if(!s_feedbackPending &&
(s_feedbackElapsedMs < SEAT_CONTROLLER_THERMAL_FEEDBACK_MS))
{
return;
}
data0 = ((uint32_t)g_seatControllerFanFeedbackRaw & 0x07u) |
(((uint32_t)g_seatControllerHeaterFeedbackRaw & 0x07u) << 3u);
if(BspSeatMoveCan_Send(SEAT_CONTROLLER_THERMAL_FEEDBACK_ID, data0, 0u))
{
g_seatControllerThermalFeedbackTxCount++;
s_feedbackPending = false;
s_feedbackElapsedMs = 0u;
}
else
{
g_seatControllerThermalFeedbackTxFailureCount++;
}
}
@@ -0,0 +1,21 @@
#ifndef SEAT_CONTROLLER_THERMAL_H
#define SEAT_CONTROLLER_THERMAL_H
#include <stdbool.h>
#include <stdint.h>
#include "bsp_seat_move_can.h"
bool SeatControllerThermal_Init(void);
void SeatControllerThermal_HandleFrame(const bsp_seat_move_can_frame_t *frame);
void SeatControllerThermal_Service1ms(void);
void SeatControllerThermal_ForceSafeOff(void);
extern volatile uint8_t g_seatControllerFanFeedbackRaw;
extern volatile uint8_t g_seatControllerHeaterFeedbackRaw;
extern volatile uint32_t g_seatControllerThermalRxCount;
extern volatile uint32_t g_seatControllerThermalInvalidCount;
extern volatile uint32_t g_seatControllerThermalFeedbackTxCount;
extern volatile uint32_t g_seatControllerThermalFeedbackTxFailureCount;
#endif /* SEAT_CONTROLLER_THERMAL_H */
+919
View File
@@ -0,0 +1,919 @@
#include "seat_motor_manager.h"
#include <stddef.h>
#include <string.h>
#define SEAT_MOTOR_RETRY_DELAY_MS (2000ul)
#define SEAT_MOTOR_SPEED_CAL_WARMUP_MS (500ul)
#define SEAT_MOTOR_SPEED_CAL_MEASURE_MS (1000ul)
#define SEAT_MOTOR_SPEED_CAL_DUTY_PERCENT (95u)
typedef char seat_motor_manager_requires_four_motors[
(MOTOR_MODE_COUNT == 4) ? 1 : -1];
typedef struct
{
seat_motor_task_state_t state;
seat_motor_task_state_t stateAfterStart;
seat_motor_result_t result;
uint32_t commandSequence;
uint32_t targetValueX100;
uint32_t durationMs;
uint32_t deadlineMs;
uint32_t retryAtMs;
uint32_t speedValuePerSecondX100;
uint32_t timedRawStartEdges;
uint32_t calibrationRawStart;
uint32_t calibrationStartMs;
uint32_t measuredMaxSpeedPpsX100;
motor_direction_t direction;
uint8_t dutyPercent;
uint8_t retryCount;
bool dynamicSpeed;
bool active;
} seat_motor_task_t;
static seat_motor_task_t tasks[MOTOR_MODE_COUNT];
static bool SeatMotor_ModeValid(motor_mode_t mode)
{
return ((uint32_t)mode < (uint32_t)MOTOR_MODE_COUNT);
}
static uint32_t SeatMotor_MaxValueX100(motor_mode_t mode)
{
if(mode == MOTOR_MODE_0_LINEAR)
{
return MOTOR_MODE0_MAX_POSITION_MM_X100;
}
if(mode == MOTOR_MODE_1_ANGLE)
{
return MOTOR_MODE1_MAX_ANGLE_DEG_X100;
}
if(mode == MOTOR_MODE_2_ANGLE)
{
return MOTOR_MODE2_MAX_ANGLE_DEG_X100;
}
if(mode == MOTOR_MODE_3_LINEAR_TRACK)
{
return MOTOR_MODE3_MAX_POSITION_MM_X100;
}
return 0ul;
}
static bool SeatMotor_TimeReached(uint32_t now, uint32_t deadline)
{
return ((int32_t)(now - deadline) >= 0);
}
static uint32_t SeatMotor_AbsDelta(uint32_t lhs, uint32_t rhs)
{
return (lhs >= rhs) ? (lhs - rhs) : (rhs - lhs);
}
static bool SeatMotor_DutyValid(uint8_t dutyPercent)
{
return (dutyPercent >= MOTOR_SPEED_MIN_DUTY_PERCENT) &&
(dutyPercent <= MOTOR_SPEED_MAX_DUTY_PERCENT);
}
static bool SeatMotor_TaskIsPosition(const seat_motor_task_t *task)
{
return (task->state == SEAT_MOTOR_TASK_POSITION) ||
((task->state == SEAT_MOTOR_TASK_WAIT_START) &&
(task->stateAfterStart == SEAT_MOTOR_TASK_POSITION));
}
static bool SeatMotor_TaskIsTimed(const seat_motor_task_t *task)
{
return (task->state == SEAT_MOTOR_TASK_TIMED) ||
((task->state == SEAT_MOTOR_TASK_WAIT_START) &&
(task->stateAfterStart == SEAT_MOTOR_TASK_TIMED));
}
static void SeatMotor_SetFailed(motor_mode_t mode,
seat_motor_result_t result)
{
seat_motor_task_t *task = &tasks[(uint32_t)mode];
(void)Motor_StopMode(mode);
task->state = SEAT_MOTOR_TASK_FAILED;
task->result = result;
task->active = false;
}
static void SeatMotor_SetComplete(motor_mode_t mode)
{
seat_motor_task_t *task = &tasks[(uint32_t)mode];
task->state = SEAT_MOTOR_TASK_COMPLETE;
task->result = SEAT_MOTOR_RESULT_OK;
task->active = false;
}
static void SeatMotor_SetStopped(motor_mode_t mode)
{
seat_motor_task_t *task = &tasks[(uint32_t)mode];
task->state = SEAT_MOTOR_TASK_IDLE;
task->result = SEAT_MOTOR_RESULT_STOPPED;
task->active = false;
}
static bool SeatMotor_PrepareNewTask(motor_mode_t mode,
uint32_t commandSequence)
{
seat_motor_task_t *task;
if((false == SeatMotor_ModeValid(mode)) ||
Motor_IsDriverFaultLatched() ||
(commandSequence > UINT16_MAX))
{
return false;
}
if((MOTOR_API_SUCCESS != Motor_StopMode(mode)) ||
(MOTOR_API_SUCCESS != Motor_AcknowledgeCommand(
mode, (uint16_t)commandSequence)))
{
return false;
}
task = &tasks[(uint32_t)mode];
(void)memset(task, 0, sizeof(*task));
task->commandSequence = commandSequence;
task->result = SEAT_MOTOR_RESULT_RUNNING;
task->active = true;
return true;
}
static bool SeatMotor_StartPositionTask(motor_mode_t mode,
uint32_t targetValueX100,
uint8_t dutyPercent,
uint32_t speedValuePerSecondX100,
uint32_t durationLimitMs,
uint32_t commandSequence,
bool dynamicSpeed,
bool preserveRetry,
seat_motor_task_state_t runningState)
{
seat_motor_task_t savedTask;
seat_motor_task_t *task;
motor_position_info_t info;
uint32_t travelValueX100;
uint32_t planDurationMs;
uint8_t retryCount;
if((false == SeatMotor_ModeValid(mode)) ||
((runningState != SEAT_MOTOR_TASK_POSITION) &&
(runningState != SEAT_MOTOR_TASK_TIMED)) ||
(targetValueX100 > SeatMotor_MaxValueX100(mode)) ||
(MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)) ||
(false == info.positionValid))
{
return false;
}
task = &tasks[(uint32_t)mode];
retryCount = task->retryCount;
if((false == dynamicSpeed) && (false == SeatMotor_DutyValid(dutyPercent)))
{
return false;
}
travelValueX100 = SeatMotor_AbsDelta(targetValueX100, info.valueX100);
if((false != dynamicSpeed) &&
((0ul == speedValuePerSecondX100) || (0ul == travelValueX100)))
{
if(0ul == travelValueX100)
{
if(false == preserveRetry)
{
if(false == SeatMotor_PrepareNewTask(mode, commandSequence))
{
return false;
}
}
SeatMotor_SetComplete(mode);
return true;
}
return false;
}
savedTask = *task;
if((false == preserveRetry) &&
(false == SeatMotor_PrepareNewTask(mode, commandSequence)))
{
return false;
}
task = &tasks[(uint32_t)mode];
if(false != preserveRetry)
{
*task = savedTask;
task->result = SEAT_MOTOR_RESULT_RUNNING;
task->active = true;
retryCount = 1u;
}
if(false != dynamicSpeed)
{
planDurationMs = (durationLimitMs != 0ul) ? durationLimitMs :
(uint32_t)((((uint64_t)travelValueX100 * 1000ull) +
speedValuePerSecondX100 - 1ull) /
speedValuePerSecondX100);
if((0ul == planDurationMs) ||
(MOTOR_API_SUCCESS != Motor_SetSpeedPlan(mode,
planDurationMs,
travelValueX100)))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
}
else if((MOTOR_API_SUCCESS != Motor_SetFixedDutyPercent(mode,
dutyPercent)))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
task->stateAfterStart = runningState;
task->state = (durationLimitMs != 0ul) ?
SEAT_MOTOR_TASK_WAIT_START : runningState;
task->targetValueX100 = targetValueX100;
task->durationMs = durationLimitMs;
task->deadlineMs = 0ul;
task->speedValuePerSecondX100 = speedValuePerSecondX100;
task->dutyPercent = dutyPercent;
task->dynamicSpeed = dynamicSpeed;
task->retryCount = retryCount;
task->direction = (targetValueX100 >= info.valueX100) ?
MOTOR_DIRECTION_TOWARD_FAR :
MOTOR_DIRECTION_TOWARD_NEAR;
if(MOTOR_API_SUCCESS != Motor_StartMoveToValue(mode, targetValueX100))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
return true;
}
void SeatMotorManager_Init(void)
{
(void)memset(tasks, 0, sizeof(tasks));
}
void SeatMotorManager_Stop(motor_mode_t mode, uint32_t commandSequence)
{
seat_motor_task_t *task;
if(false == SeatMotor_ModeValid(mode))
{
return;
}
(void)Motor_StopMode(mode);
task = &tasks[(uint32_t)mode];
(void)memset(task, 0, sizeof(*task));
task->commandSequence = commandSequence;
task->state = SEAT_MOTOR_TASK_IDLE;
task->result = SEAT_MOTOR_RESULT_STOPPED;
}
void SeatMotorManager_StopAll(uint32_t commandSequence)
{
uint32_t index;
for(index = 0ul; index < (uint32_t)MOTOR_MODE_COUNT; index++)
{
SeatMotorManager_Stop((motor_mode_t)index, commandSequence);
}
}
bool SeatMotorManager_IsAnyActive(void)
{
uint32_t index;
for(index = 0ul; index < (uint32_t)MOTOR_MODE_COUNT; index++)
{
if(false != tasks[index].active)
{
return true;
}
}
return false;
}
static void SeatMotor_ServiceSpeedCalibration(motor_mode_t mode,
const motor_position_info_t *info,
uint32_t now)
{
seat_motor_task_t *task = &tasks[(uint32_t)mode];
motor_persistent_state_t rawState;
uint32_t elapsedMs;
uint32_t measuredPpsX100;
if(task->state == SEAT_MOTOR_TASK_SPEED_CAL_SETTLE)
{
if(false == Motor_IsCommandCurrent(
mode, (uint16_t)task->commandSequence))
{
SeatMotor_SetStopped(mode);
return;
}
if(Motor_IsMoving(mode))
{
return;
}
if((0ul == task->measuredMaxSpeedPpsX100) ||
(MOTOR_API_SUCCESS != Motor_SetMaximumSpeedPpsX100(
mode,
task->measuredMaxSpeedPpsX100)))
{
if(false == Motor_IsCommandCurrent(
mode, (uint16_t)task->commandSequence))
{
SeatMotor_SetStopped(mode);
}
else
{
SeatMotor_SetFailed(
mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
}
return;
}
SeatMotor_SetComplete(mode);
return;
}
if((info->lastEvent == MOTOR_EVENT_STALL) ||
(info->lastEvent == MOTOR_EVENT_NEAR_ENDPOINT) ||
(info->lastEvent == MOTOR_EVENT_FAR_ENDPOINT) ||
(info->lastEvent == MOTOR_EVENT_TIMEOUT) ||
(info->lastEvent == MOTOR_EVENT_TARGET_REACHED))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return;
}
if(task->state == SEAT_MOTOR_TASK_WAIT_START)
{
return;
}
if(task->state == SEAT_MOTOR_TASK_SPEED_CAL_WARMUP)
{
if(SeatMotor_TimeReached(now,
task->calibrationStartMs +
SEAT_MOTOR_SPEED_CAL_WARMUP_MS))
{
if(MOTOR_API_SUCCESS != Motor_GetPersistentState(mode, &rawState))
{
SeatMotor_SetFailed(mode,
SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return;
}
task->calibrationRawStart = rawState.rawRisingEdges;
task->calibrationStartMs = now;
task->state = SEAT_MOTOR_TASK_SPEED_CAL_MEASURE;
}
return;
}
if(SeatMotor_TimeReached(now,
task->calibrationStartMs +
SEAT_MOTOR_SPEED_CAL_MEASURE_MS))
{
if(MOTOR_API_SUCCESS != Motor_GetPersistentState(mode, &rawState))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return;
}
elapsedMs = now - task->calibrationStartMs;
measuredPpsX100 = (uint32_t)
(((uint64_t)(rawState.rawRisingEdges -
task->calibrationRawStart) * 100000ull) /
elapsedMs);
if((0ul == measuredPpsX100) ||
(MOTOR_API_SUCCESS != Motor_StopMode(mode)))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return;
}
task->measuredMaxSpeedPpsX100 = measuredPpsX100;
task->state = SEAT_MOTOR_TASK_SPEED_CAL_SETTLE;
}
}
void SeatMotorManager_Service(void)
{
uint32_t index;
uint32_t now;
Motor_ServiceAll();
now = Motor_GetTimeMs();
for(index = 0ul; index < (uint32_t)MOTOR_MODE_COUNT; index++)
{
motor_mode_t mode = (motor_mode_t)index;
seat_motor_task_t *task = &tasks[index];
motor_position_info_t info;
bool endpointEvent;
if((false == task->active) ||
(MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)))
{
continue;
}
if(Motor_IsDriverFaultLatched() ||
(info.lastEvent == MOTOR_EVENT_DRIVER_FAULT))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_DRIVER_FAULT);
continue;
}
if(false == Motor_IsCommandCurrent(
mode, (uint16_t)task->commandSequence))
{
SeatMotor_SetStopped(mode);
continue;
}
if((info.lastEvent == MOTOR_EVENT_ABORTED) &&
(task->state != SEAT_MOTOR_TASK_SPEED_CAL_SETTLE))
{
SeatMotor_SetStopped(mode);
continue;
}
if((task->state == SEAT_MOTOR_TASK_WAIT_START) &&
(info.direction != MOTOR_DIRECTION_STOPPED))
{
task->state = task->stateAfterStart;
if(task->state == SEAT_MOTOR_TASK_SPEED_CAL_WARMUP)
{
task->calibrationStartMs = now;
}
else if(0ul != task->durationMs)
{
task->deadlineMs = now + task->durationMs;
}
else
{
task->deadlineMs = 0ul;
}
}
if((task->state == SEAT_MOTOR_TASK_SPEED_CAL_WARMUP) ||
(task->state == SEAT_MOTOR_TASK_SPEED_CAL_MEASURE) ||
(task->state == SEAT_MOTOR_TASK_SPEED_CAL_SETTLE) ||
((task->state == SEAT_MOTOR_TASK_WAIT_START) &&
(task->stateAfterStart ==
SEAT_MOTOR_TASK_SPEED_CAL_WARMUP)))
{
SeatMotor_ServiceSpeedCalibration(mode, &info, now);
continue;
}
if(task->state == SEAT_MOTOR_TASK_RETRY_DELAY)
{
if(SeatMotor_TimeReached(now, task->retryAtMs))
{
if(false == SeatMotor_StartPositionTask(
mode,
task->targetValueX100,
task->dutyPercent,
task->speedValuePerSecondX100,
task->durationMs,
task->commandSequence,
task->dynamicSpeed,
true,
task->stateAfterStart))
{
if(false == Motor_IsCommandCurrent(
mode,
(uint16_t)task->commandSequence))
{
SeatMotor_SetStopped(mode);
}
else
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_STALL);
}
}
}
continue;
}
endpointEvent = (info.lastEvent == MOTOR_EVENT_NEAR_ENDPOINT) ||
(info.lastEvent == MOTOR_EVENT_FAR_ENDPOINT);
if((info.lastEvent == MOTOR_EVENT_STALL) || endpointEvent)
{
if(SeatMotor_TaskIsPosition(task) &&
(0u == task->retryCount))
{
if(MOTOR_API_SUCCESS != Motor_StopMode(mode))
{
SeatMotor_SetFailed(
mode,
Motor_IsDriverFaultLatched() ?
SEAT_MOTOR_RESULT_DRIVER_FAULT :
SEAT_MOTOR_RESULT_INVALID);
continue;
}
task->state = SEAT_MOTOR_TASK_RETRY_DELAY;
task->deadlineMs = 0ul;
task->retryAtMs = now + SEAT_MOTOR_RETRY_DELAY_MS;
task->retryCount = 1u;
}
else if(endpointEvent && SeatMotor_TaskIsTimed(task))
{
SeatMotor_SetComplete(mode);
}
else
{
SeatMotor_SetFailed(
mode,
endpointEvent ? SEAT_MOTOR_RESULT_ENDPOINT :
SEAT_MOTOR_RESULT_STALL);
}
continue;
}
if(info.lastEvent == MOTOR_EVENT_TARGET_REACHED)
{
SeatMotor_SetComplete(mode);
continue;
}
if(info.lastEvent == MOTOR_EVENT_TIMEOUT)
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_TIMEOUT);
continue;
}
if((0ul != task->deadlineMs) &&
SeatMotor_TimeReached(now, task->deadlineMs))
{
if(MOTOR_API_SUCCESS != Motor_StopMode(mode))
{
SeatMotor_SetFailed(
mode,
Motor_IsDriverFaultLatched() ?
SEAT_MOTOR_RESULT_DRIVER_FAULT :
SEAT_MOTOR_RESULT_INVALID);
continue;
}
if(task->state == SEAT_MOTOR_TASK_TIMED)
{
motor_persistent_state_t rawState;
/* A duration expiring is not proof that the requested axis
* actually moved. Require at least one Hall edge so an open
* circuit, unpowered bridge or disconnected motor is reported
* as STALL instead of a misleading OK completion. */
if(MOTOR_API_SUCCESS !=
Motor_GetPersistentState(mode, &rawState))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
}
else if(rawState.rawRisingEdges ==
task->timedRawStartEdges)
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_STALL);
}
else
{
SeatMotor_SetComplete(mode);
}
}
else
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_TIMEOUT);
}
}
}
}
bool SeatMotor_MoveAbsoluteDuty(motor_mode_t mode,
uint32_t targetValueX100,
uint8_t dutyPercent,
uint32_t commandSequence)
{
return SeatMotor_StartPositionTask(mode,
targetValueX100,
dutyPercent,
0ul,
0ul,
commandSequence,
false,
false,
SEAT_MOTOR_TASK_POSITION);
}
bool SeatMotor_MoveRelativeDuty(motor_mode_t mode,
int32_t deltaValueX100,
uint8_t dutyPercent,
uint32_t commandSequence)
{
motor_position_info_t info;
int64_t target;
if((MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)) ||
(false == info.positionValid))
{
return false;
}
target = (int64_t)info.valueX100 + deltaValueX100;
if((target < 0ll) || (target > (int64_t)SeatMotor_MaxValueX100(mode)))
{
return false;
}
return SeatMotor_MoveAbsoluteDuty(mode,
(uint32_t)target,
dutyPercent,
commandSequence);
}
bool SeatMotor_MoveTimedDuty(motor_mode_t mode,
motor_direction_t direction,
uint32_t durationMs,
uint8_t dutyPercent,
uint32_t commandSequence)
{
seat_motor_task_t *task;
if((0ul == durationMs) || (false == SeatMotor_DutyValid(dutyPercent)) ||
((direction != MOTOR_DIRECTION_TOWARD_FAR) &&
(direction != MOTOR_DIRECTION_TOWARD_NEAR)) ||
(false == SeatMotor_PrepareNewTask(mode, commandSequence)))
{
return false;
}
if(MOTOR_API_SUCCESS != Motor_SetFixedDutyPercent(mode, dutyPercent))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
task = &tasks[(uint32_t)mode];
{
motor_persistent_state_t rawState;
if(MOTOR_API_SUCCESS != Motor_GetPersistentState(mode, &rawState))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
task->timedRawStartEdges = rawState.rawRisingEdges;
}
/* A timed manual movement is valid even before an absolute endpoint has
* been calibrated. It therefore uses the open-loop direction primitive
* and is bounded by duration, Hall stall/endpoint detection and the
* low-level absolute operation timeout. */
if(MOTOR_API_SUCCESS != Motor_StartOpenLoop(mode, direction))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
task->state = SEAT_MOTOR_TASK_WAIT_START;
task->stateAfterStart = SEAT_MOTOR_TASK_TIMED;
task->direction = direction;
task->durationMs = durationMs;
task->deadlineMs = 0ul;
task->dutyPercent = dutyPercent;
return true;
}
bool SeatMotor_MoveAbsoluteSpeed(motor_mode_t mode,
uint32_t targetValueX100,
uint32_t speedValuePerSecondX100,
uint32_t commandSequence)
{
return SeatMotor_StartPositionTask(mode,
targetValueX100,
0u,
speedValuePerSecondX100,
0ul,
commandSequence,
true,
false,
SEAT_MOTOR_TASK_POSITION);
}
bool SeatMotor_MoveRelativeSpeed(motor_mode_t mode,
int32_t deltaValueX100,
uint32_t speedValuePerSecondX100,
uint32_t commandSequence)
{
motor_position_info_t info;
int64_t target;
if((MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)) ||
(false == info.positionValid))
{
return false;
}
target = (int64_t)info.valueX100 + deltaValueX100;
if((target < 0ll) || (target > (int64_t)SeatMotor_MaxValueX100(mode)))
{
return false;
}
return SeatMotor_MoveAbsoluteSpeed(mode,
(uint32_t)target,
speedValuePerSecondX100,
commandSequence);
}
bool SeatMotor_MoveTimedSpeed(motor_mode_t mode,
motor_direction_t direction,
uint32_t durationMs,
uint32_t speedValuePerSecondX100,
uint32_t commandSequence)
{
seat_motor_task_t *task;
uint32_t travel;
if((0ul == durationMs) || (0ul == speedValuePerSecondX100) ||
((direction != MOTOR_DIRECTION_TOWARD_FAR) &&
(direction != MOTOR_DIRECTION_TOWARD_NEAR)) ||
(false == SeatMotor_PrepareNewTask(mode, commandSequence)))
{
return false;
}
travel = (uint32_t)((((uint64_t)speedValuePerSecondX100) *
durationMs) / 1000ull);
if((0ul == travel) || (travel > SeatMotor_MaxValueX100(mode)) ||
(MOTOR_API_SUCCESS != Motor_SetSpeedPlan(mode, durationMs, travel)))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
task = &tasks[(uint32_t)mode];
{
motor_persistent_state_t rawState;
if(MOTOR_API_SUCCESS != Motor_GetPersistentState(mode, &rawState))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
task->timedRawStartEdges = rawState.rawRisingEdges;
}
if(MOTOR_API_SUCCESS != Motor_StartOpenLoop(mode, direction))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_INVALID);
return false;
}
task->state = SEAT_MOTOR_TASK_WAIT_START;
task->stateAfterStart = SEAT_MOTOR_TASK_TIMED;
task->direction = direction;
task->durationMs = durationMs;
task->deadlineMs = 0ul;
task->speedValuePerSecondX100 = speedValuePerSecondX100;
task->dynamicSpeed = true;
return true;
}
bool SeatMotor_MoveAbsoluteInTime(motor_mode_t mode,
uint32_t targetValueX100,
uint32_t durationMs,
uint32_t commandSequence)
{
motor_position_info_t info;
uint32_t travel;
uint32_t speed;
if((0ul == durationMs) ||
(MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)) ||
(false == info.positionValid))
{
return false;
}
travel = SeatMotor_AbsDelta(targetValueX100, info.valueX100);
if(0ul == travel)
{
return SeatMotor_MoveAbsoluteDuty(mode,
targetValueX100,
MOTOR_SPEED_MIN_DUTY_PERCENT,
commandSequence);
}
speed = (uint32_t)((((uint64_t)travel) * 1000ull) / durationMs);
return SeatMotor_StartPositionTask(mode,
targetValueX100,
0u,
speed,
durationMs,
commandSequence,
true,
false,
SEAT_MOTOR_TASK_POSITION);
}
bool SeatMotor_CalibrateRange(motor_mode_t mode, uint32_t commandSequence)
{
motor_calibration_result_t result;
if(false == SeatMotor_ModeValid(mode))
{
return false;
}
if((false == SeatMotor_PrepareNewTask(mode, commandSequence)) ||
(MOTOR_API_SUCCESS != Motor_SetFixedDutyPercent(
mode, MOTOR_SPEED_MIN_DUTY_PERCENT)) ||
(MOTOR_API_SUCCESS != Motor_Calibrate(mode,
MOTOR_CALIBRATE_FIND_BOTH,
&result)) ||
(false == result.nearEndpointFound) ||
(false == result.farEndpointFound))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return false;
}
SeatMotor_SetComplete(mode);
return true;
}
bool SeatMotor_StartMaximumSpeedCalibration(motor_mode_t mode,
uint32_t commandSequence)
{
motor_position_info_t info;
seat_motor_task_t *task;
motor_direction_t direction;
uint32_t target;
if((false == SeatMotor_ModeValid(mode)) ||
(MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)) ||
(false == info.positionValid) ||
(false == SeatMotor_PrepareNewTask(mode, commandSequence)))
{
return false;
}
direction = (info.percentX100 <= 5000ul) ?
MOTOR_DIRECTION_TOWARD_FAR : MOTOR_DIRECTION_TOWARD_NEAR;
target = (direction == MOTOR_DIRECTION_TOWARD_FAR) ?
SeatMotor_MaxValueX100(mode) : 0ul;
if((MOTOR_API_SUCCESS != Motor_SetFixedDutyPercent(
mode, SEAT_MOTOR_SPEED_CAL_DUTY_PERCENT)) ||
(MOTOR_API_SUCCESS != Motor_StartMoveToValue(mode, target)))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return false;
}
task = &tasks[(uint32_t)mode];
task->state = SEAT_MOTOR_TASK_WAIT_START;
task->stateAfterStart = SEAT_MOTOR_TASK_SPEED_CAL_WARMUP;
task->direction = direction;
task->dutyPercent = SEAT_MOTOR_SPEED_CAL_DUTY_PERCENT;
task->calibrationStartMs = 0ul;
return true;
}
bool SeatMotor_RecoverPosition(motor_mode_t mode, uint32_t savedHallCount)
{
motor_calibration_result_t calibration;
motor_persistent_state_t state;
uint32_t targetValueX100;
if((MOTOR_API_SUCCESS != Motor_Calibrate(mode,
MOTOR_CALIBRATE_FIND_NEAR,
&calibration)) ||
(false == calibration.nearEndpointFound) ||
(MOTOR_API_SUCCESS != Motor_GetPersistentState(mode, &state)))
{
return false;
}
if(savedHallCount > state.maxHallCount)
{
savedHallCount = state.maxHallCount;
}
targetValueX100 = (uint32_t)
(((uint64_t)savedHallCount * SeatMotor_MaxValueX100(mode) +
(state.maxHallCount / 2ul)) / state.maxHallCount);
if(0ul == targetValueX100)
{
return true;
}
if((MOTOR_API_SUCCESS != Motor_SetFixedDutyPercent(
mode, MOTOR_SPEED_MIN_DUTY_PERCENT)) ||
(MOTOR_API_SUCCESS != Motor_MoveToValue(mode, targetValueX100)))
{
return false;
}
return true;
}
bool SeatMotorManager_GetStatus(motor_mode_t mode,
seat_motor_status_t *status)
{
motor_position_info_t position;
motor_speed_info_t speed;
motor_persistent_state_t persistent;
const seat_motor_task_t *task;
if((NULL == status) || (false == SeatMotor_ModeValid(mode)) ||
(MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &position)) ||
(MOTOR_API_SUCCESS != Motor_GetSpeedInfo(mode, &speed)) ||
(MOTOR_API_SUCCESS != Motor_GetPersistentState(mode, &persistent)))
{
return false;
}
task = &tasks[(uint32_t)mode];
status->taskState = task->state;
status->result = task->result;
status->lastMotorEvent = position.lastEvent;
status->direction = position.direction;
status->commandSequence = task->commandSequence;
status->valueX100 = position.valueX100;
status->hallCount = position.hallCount;
status->maxHallCount = position.maxHallCount;
status->maxSpeedPpsX100 = persistent.maxSpeedPulsesPerSecondX100;
status->dutyPercent = speed.currentDutyPercent;
status->active = task->active;
status->retried = (task->retryCount != 0u);
status->positionValid = position.positionValid;
return true;
}
+104
View File
@@ -0,0 +1,104 @@
#ifndef SEAT_MOTOR_MANAGER_H
#define SEAT_MOTOR_MANAGER_H
#include <stdbool.h>
#include <stdint.h>
#include "motor_control.h"
typedef enum
{
SEAT_MOTOR_TASK_IDLE = 0,
SEAT_MOTOR_TASK_WAIT_START,
SEAT_MOTOR_TASK_POSITION,
SEAT_MOTOR_TASK_TIMED,
SEAT_MOTOR_TASK_RETRY_DELAY,
SEAT_MOTOR_TASK_SPEED_CAL_WARMUP,
SEAT_MOTOR_TASK_SPEED_CAL_MEASURE,
SEAT_MOTOR_TASK_SPEED_CAL_SETTLE,
SEAT_MOTOR_TASK_COMPLETE,
SEAT_MOTOR_TASK_FAILED
} seat_motor_task_state_t;
typedef enum
{
SEAT_MOTOR_RESULT_NONE = 0,
SEAT_MOTOR_RESULT_RUNNING,
SEAT_MOTOR_RESULT_OK,
SEAT_MOTOR_RESULT_STOPPED,
SEAT_MOTOR_RESULT_STALL,
SEAT_MOTOR_RESULT_ENDPOINT,
SEAT_MOTOR_RESULT_TIMEOUT,
SEAT_MOTOR_RESULT_DRIVER_FAULT,
SEAT_MOTOR_RESULT_INVALID,
SEAT_MOTOR_RESULT_CALIBRATION_FAILED
} seat_motor_result_t;
typedef struct
{
seat_motor_task_state_t taskState;
seat_motor_result_t result;
motor_event_t lastMotorEvent;
motor_direction_t direction;
uint32_t commandSequence;
uint32_t valueX100;
uint32_t hallCount;
uint32_t maxHallCount;
uint32_t maxSpeedPpsX100;
uint8_t dutyPercent;
bool active;
bool retried;
bool positionValid;
} seat_motor_status_t;
void SeatMotorManager_Init(void);
void SeatMotorManager_Service(void);
void SeatMotorManager_Stop(motor_mode_t mode, uint32_t commandSequence);
void SeatMotorManager_StopAll(uint32_t commandSequence);
bool SeatMotorManager_IsAnyActive(void);
bool SeatMotorManager_GetStatus(motor_mode_t mode,
seat_motor_status_t *status);
/* Six standardized, non-blocking motion primitives. Physical values are x100. */
bool SeatMotor_MoveAbsoluteDuty(motor_mode_t mode,
uint32_t targetValueX100,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveRelativeDuty(motor_mode_t mode,
int32_t deltaValueX100,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveTimedDuty(motor_mode_t mode,
motor_direction_t direction,
uint32_t durationMs,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveAbsoluteSpeed(motor_mode_t mode,
uint32_t targetValueX100,
uint32_t speedValuePerSecondX100,
uint32_t commandSequence);
bool SeatMotor_MoveRelativeSpeed(motor_mode_t mode,
int32_t deltaValueX100,
uint32_t speedValuePerSecondX100,
uint32_t commandSequence);
bool SeatMotor_MoveTimedSpeed(motor_mode_t mode,
motor_direction_t direction,
uint32_t durationMs,
uint32_t speedValuePerSecondX100,
uint32_t commandSequence);
/* Position command with an explicit requested arrival time. */
bool SeatMotor_MoveAbsoluteInTime(motor_mode_t mode,
uint32_t targetValueX100,
uint32_t durationMs,
uint32_t commandSequence);
/* Range calibration is interruptible by Motor_RequestAbortAllFromIsr(). */
bool SeatMotor_CalibrateRange(motor_mode_t mode, uint32_t commandSequence);
bool SeatMotor_StartMaximumSpeedCalibration(motor_mode_t mode,
uint32_t commandSequence);
/* Optional endpoint-based position recovery primitive. */
bool SeatMotor_RecoverPosition(motor_mode_t mode, uint32_t savedHallCount);
#endif /* SEAT_MOTOR_MANAGER_H */
@@ -0,0 +1,926 @@
#include "seat_move_application.h"
#include <stddef.h>
#include <string.h>
#include "bsp_seat_move_can.h"
#include "motor_control.h"
#include "safety_manager.h"
#include "seat_motor_manager.h"
#include "seat_move_config.h"
#include "seat_move_store.h"
#include "seat_controller_thermal.h"
typedef enum
{
SEAT_MOVE_ACK_IDLE = 0u,
SEAT_MOVE_ACK_ACCEPTED = 1u,
SEAT_MOVE_ACK_REJECTED = 2u,
SEAT_MOVE_ACK_CONFLICT = 3u,
SEAT_MOVE_ACK_INHIBITED = 4u,
SEAT_MOVE_ACK_STORAGE_ERROR = 5u
} seat_move_ack_t;
typedef enum
{
SEAT_MOVE_REJECT_NONE = 0u,
SEAT_MOVE_REJECT_FORMAT = 1u,
SEAT_MOVE_REJECT_CONFLICT = 2u,
SEAT_MOVE_REJECT_RANGE = 3u,
SEAT_MOVE_REJECT_MISSING_FIELD = 4u,
SEAT_MOVE_REJECT_INHIBITED = 5u,
SEAT_MOVE_REJECT_EXECUTION = 6u,
SEAT_MOVE_REJECT_STORAGE = 7u
} seat_move_reject_t;
typedef enum
{
SEAT_MOVE_OPCODE_INVALID = 0x00u,
SEAT_MOVE_OPCODE_ABSOLUTE_DUTY = 0x01u,
SEAT_MOVE_OPCODE_RELATIVE_DUTY = 0x02u,
SEAT_MOVE_OPCODE_TIMED_DUTY = 0x03u,
SEAT_MOVE_OPCODE_ABSOLUTE_SPEED = 0x04u,
SEAT_MOVE_OPCODE_RELATIVE_SPEED = 0x05u,
SEAT_MOVE_OPCODE_TIMED_SPEED = 0x06u,
SEAT_MOVE_OPCODE_ABSOLUTE_IN_TIME = 0x07u,
SEAT_MOVE_OPCODE_STOP_MOTOR = 0x08u,
SEAT_MOVE_OPCODE_STOP_ALL = 0x09u,
SEAT_MOVE_OPCODE_CALIBRATE_RANGE = 0x0Au,
SEAT_MOVE_OPCODE_CALIBRATE_SPEED = 0x0Bu,
SEAT_MOVE_OPCODE_SAVE_SHUTDOWN = 0x0Cu
} seat_move_opcode_t;
typedef struct
{
uint8_t opcode;
uint8_t motor;
motor_direction_t direction;
uint16_t sequence;
uint16_t paramA;
uint16_t paramB;
} seat_move_command_t;
typedef struct
{
seat_move_command_t command;
bool valid;
} seat_move_pending_t;
volatile uint32_t g_seatMoveRxCommandCount = 0u;
volatile uint32_t g_seatMoveInvalidCommandCount = 0u;
volatile uint32_t g_seatMoveConflictCount = 0u;
volatile uint32_t g_seatMoveAcceptedCount = 0u;
volatile uint32_t g_seatMoveFeedbackTxCount = 0u;
volatile uint32_t g_seatMoveFeedbackTxFailureCount = 0u;
volatile uint8_t g_seatMoveAck = SEAT_MOVE_ACK_IDLE;
volatile uint8_t g_seatMoveRejectReason = SEAT_MOVE_REJECT_NONE;
volatile uint8_t g_seatMoveLastOpcode = SEAT_MOVE_OPCODE_INVALID;
volatile uint16_t g_seatMoveLastSequence = 0u;
volatile uint8_t g_seatMovePendingMask = 0u;
volatile uint32_t g_seatControllerIdentityRequestCount = 0u;
volatile uint32_t g_seatControllerIdentityResponseCount = 0u;
static seat_move_pending_t s_pending[SEAT_MOVE_MOTOR_COUNT];
static seat_motor_status_t s_lastStatus[SEAT_MOVE_MOTOR_COUNT];
static bool s_lastStatusValid[SEAT_MOVE_MOTOR_COUNT];
static bool s_lastSequenceValid = false;
static uint16_t s_sequenceReference = 0u;
static bool s_shutdownInhibited = false;
static bool s_systemStatusPending = true;
static uint8_t s_motorStatusPendingMask = SEAT_MOVE_ACTIVE_MOTOR_MASK;
static uint8_t s_diagnosticPendingMask = SEAT_MOVE_ACTIVE_MOTOR_MASK;
static uint8_t s_dispatchCursor = 0u;
static uint32_t s_nextStartMs = 0u;
static uint32_t s_nextSystemStatusMs = 0u;
static uint32_t s_nextMotorStatusMs = 0u;
static uint32_t s_nextDiagnosticMs = 0u;
static bool SeatMove_TimeReached(uint32_t now, uint32_t deadline);
static bool SeatMove_SequenceNewer(uint16_t candidate, uint16_t reference);
static uint32_t SeatMove_MaxValueX100(uint8_t motor);
static void SeatMove_SetResponse(seat_move_ack_t ack,
seat_move_reject_t reject,
const seat_move_command_t *command);
static bool SeatMove_ParseCommand(const bsp_seat_move_can_frame_t *frame,
seat_move_command_t *command);
static bool SeatMove_ParametersValid(const seat_move_command_t *command,
seat_move_reject_t *reject);
static bool SeatMove_MotorBusy(uint8_t motor);
static bool SeatMove_AnyPending(void);
static void SeatMove_HandleCommand(const bsp_seat_move_can_frame_t *frame);
static void SeatMove_HandleIdentityRequest(
const bsp_seat_move_can_frame_t *frame);
static void SeatMove_StopMotor(uint8_t motor, uint16_t sequence);
static void SeatMove_QueueCommand(const seat_move_command_t *command);
static bool SeatMove_DispatchCommand(const seat_move_command_t *command);
static void SeatMove_ServiceDispatcher(void);
static void SeatMove_ServiceStateChanges(void);
static bool SeatMove_StatusChanged(const seat_motor_status_t *lhs,
const seat_motor_status_t *rhs);
static uint8_t SeatMove_ActiveMask(void);
static uint8_t SeatMove_RetryMask(void);
static bool SeatMove_SendSystemStatus(void);
static bool SeatMove_SendMotorStatus(uint8_t motor);
static bool SeatMove_SendDiagnostic(uint8_t motor);
static void SeatMove_ServiceFeedback(void);
bool SeatMoveApplication_Init(void)
{
uint32_t now;
(void)memset(s_pending, 0, sizeof(s_pending));
(void)memset(s_lastStatus, 0, sizeof(s_lastStatus));
(void)memset(s_lastStatusValid, 0, sizeof(s_lastStatusValid));
SeatMotorManager_Init();
/* Bring up the diagnostic/identity channel before reading calibration.
* Corrupt Work Flash or an actuator fault must inhibit motion, but must
* not make a correctly programmed controller disappear from the bus. */
if(!BspSeatMoveCan_Init())
{
SafetyManager_LatchFault(SAFETY_FAULT_CAN_INIT);
return false;
}
SeatMoveStore_Init();
if(SeatMoveStore_IsValid() && SeatMoveStore_IsClean() &&
!SeatMoveStore_Restore())
{
SafetyManager_LatchFault(SAFETY_FAULT_CONFIG_STORE);
return false;
}
now = Motor_GetTimeMs();
s_nextStartMs = now;
s_nextSystemStatusMs = now;
s_nextMotorStatusMs = now;
s_nextDiagnosticMs = now;
s_systemStatusPending = true;
s_motorStatusPendingMask = SEAT_MOVE_ACTIVE_MOTOR_MASK;
s_diagnosticPendingMask = SEAT_MOVE_ACTIVE_MOTOR_MASK;
return true;
}
void SeatMoveApplication_Service(void)
{
bsp_seat_move_can_frame_t frame;
if(g_bspSeatMoveCanBusOff != 0u)
{
SafetyManager_LatchFault(SAFETY_FAULT_CAN_BUS_OFF);
}
SafetyManager_Service();
if(SafetyManager_IsInhibited())
{
uint8_t motor;
for(motor = 0u; motor < SEAT_MOVE_MOTOR_COUNT; motor++)
{
s_pending[motor].valid = false;
}
g_seatMovePendingMask = 0u;
s_systemStatusPending = true;
}
while(BspSeatMoveCan_Read(&frame))
{
if(frame.identifier == SEAT_MOVE_CAN_COMMAND_ID)
{
SeatMove_HandleCommand(&frame);
}
else if(frame.identifier == SEAT_CONTROLLER_IDENTITY_REQUEST_ID)
{
SeatMove_HandleIdentityRequest(&frame);
}
else
{
SeatControllerThermal_HandleFrame(&frame);
}
}
SeatMove_ServiceDispatcher();
SeatMotorManager_Service();
SeatMove_ServiceStateChanges();
SeatMove_ServiceFeedback();
}
static void SeatMove_HandleIdentityRequest(
const bsp_seat_move_can_frame_t *frame)
{
if((frame == NULL) || (frame->dlc != SEAT_MOVE_CAN_DLC) ||
(frame->data0 != SEAT_CONTROLLER_IDENTITY_MAGIC) ||
((frame->data1 & 0xFFu) != SEAT_CONTROLLER_IDENTITY_PROTOCOL))
{
return;
}
g_seatControllerIdentityRequestCount++;
if(BspSeatMoveCan_Send(SEAT_CONTROLLER_IDENTITY_RESPONSE_ID,
SEAT_CONTROLLER_IDENTITY_MAGIC,
SEAT_CONTROLLER_IDENTITY_RESPONSE_DATA1))
{
g_seatControllerIdentityResponseCount++;
}
}
void SeatMoveApplication_StopAll(uint16_t sequence)
{
uint8_t motor;
Motor_RequestAbortAllFromIsr(sequence);
Motor_ServiceAll();
for(motor = 0u; motor < SEAT_MOVE_MOTOR_COUNT; motor++)
{
(void)Motor_AcknowledgeCommand((motor_mode_t)motor, sequence);
SeatMotorManager_Stop((motor_mode_t)motor, sequence);
s_pending[motor].valid = false;
}
g_seatMovePendingMask = 0u;
s_motorStatusPendingMask = SEAT_MOVE_ACTIVE_MOTOR_MASK;
s_systemStatusPending = true;
}
static bool SeatMove_TimeReached(uint32_t now, uint32_t deadline)
{
return ((int32_t)(now - deadline) >= 0);
}
static bool SeatMove_SequenceNewer(uint16_t candidate, uint16_t reference)
{
return ((int16_t)(candidate - reference) > 0);
}
static uint32_t SeatMove_MaxValueX100(uint8_t motor)
{
static const uint32_t maximums[SEAT_MOVE_MOTOR_COUNT] =
{
MOTOR_MODE0_MAX_POSITION_MM_X100,
MOTOR_MODE1_MAX_ANGLE_DEG_X100,
MOTOR_MODE2_MAX_ANGLE_DEG_X100,
MOTOR_MODE3_MAX_POSITION_MM_X100,
};
return (motor < SEAT_MOVE_MOTOR_COUNT) ? maximums[motor] : 0u;
}
static void SeatMove_SetResponse(seat_move_ack_t ack,
seat_move_reject_t reject,
const seat_move_command_t *command)
{
g_seatMoveAck = (uint8_t)ack;
g_seatMoveRejectReason = (uint8_t)reject;
if(command != NULL)
{
g_seatMoveLastOpcode = command->opcode;
g_seatMoveLastSequence = command->sequence;
}
s_systemStatusPending = true;
}
static bool SeatMove_ParseCommand(const bsp_seat_move_can_frame_t *frame,
seat_move_command_t *command)
{
uint8_t control;
if((frame == NULL) || (command == NULL) ||
(frame->identifier != SEAT_MOVE_CAN_COMMAND_ID) ||
(frame->dlc != SEAT_MOVE_CAN_DLC))
{
return false;
}
command->opcode = (uint8_t)(frame->data0 & 0xFFu);
control = (uint8_t)((frame->data0 >> 8u) & 0xFFu);
command->motor = control & 0x03u;
command->direction = (motor_direction_t)((control >> 2u) & 0x03u);
command->sequence = (uint16_t)((frame->data0 >> 16u) & 0xFFFFu);
command->paramA = (uint16_t)(frame->data1 & 0xFFFFu);
command->paramB = (uint16_t)((frame->data1 >> 16u) & 0xFFFFu);
return ((((control >> 4u) & 0x03u) ==
SEAT_MOVE_CAN_PROTOCOL_VERSION) &&
((control & 0xC0u) == 0u) &&
(command->opcode >= SEAT_MOVE_OPCODE_ABSOLUTE_DUTY) &&
(command->opcode <= SEAT_MOVE_OPCODE_SAVE_SHUTDOWN));
}
static bool SeatMove_ParametersValid(const seat_move_command_t *command,
seat_move_reject_t *reject)
{
bool globalCommand;
bool timedCommand;
uint32_t maxValue;
if((command == NULL) || (reject == NULL))
{
return false;
}
*reject = SEAT_MOVE_REJECT_FORMAT;
globalCommand = (command->opcode == SEAT_MOVE_OPCODE_STOP_ALL) ||
(command->opcode == SEAT_MOVE_OPCODE_SAVE_SHUTDOWN);
if(globalCommand)
{
if((command->motor != 3u) ||
(command->direction != MOTOR_DIRECTION_STOPPED))
{
return false;
}
}
else if(command->motor >= SEAT_MOVE_MOTOR_COUNT)
{
return false;
}
timedCommand = (command->opcode == SEAT_MOVE_OPCODE_TIMED_DUTY) ||
(command->opcode == SEAT_MOVE_OPCODE_TIMED_SPEED);
if(timedCommand)
{
if((command->direction != MOTOR_DIRECTION_TOWARD_FAR) &&
(command->direction != MOTOR_DIRECTION_TOWARD_NEAR))
{
return false;
}
}
else if(command->direction != MOTOR_DIRECTION_STOPPED)
{
return false;
}
maxValue = SeatMove_MaxValueX100(command->motor);
switch((seat_move_opcode_t)command->opcode)
{
case SEAT_MOVE_OPCODE_ABSOLUTE_DUTY:
if(command->paramA > maxValue)
{
*reject = SEAT_MOVE_REJECT_RANGE;
return false;
}
/* fall through */
case SEAT_MOVE_OPCODE_RELATIVE_DUTY:
if(((command->paramB >> 8u) != 0u) ||
((command->paramB & 0xFFu) < MOTOR_SPEED_MIN_DUTY_PERCENT) ||
((command->paramB & 0xFFu) > MOTOR_SPEED_MAX_DUTY_PERCENT))
{
*reject = SEAT_MOVE_REJECT_RANGE;
return false;
}
break;
case SEAT_MOVE_OPCODE_TIMED_DUTY:
if((command->paramA == 0u) ||
(command->paramA > SEAT_MOVE_MAX_COMMAND_DURATION_MS) ||
((command->paramB >> 8u) != 0u) ||
((command->paramB & 0xFFu) < MOTOR_SPEED_MIN_DUTY_PERCENT) ||
((command->paramB & 0xFFu) > MOTOR_SPEED_MAX_DUTY_PERCENT))
{
*reject = SEAT_MOVE_REJECT_RANGE;
return false;
}
break;
case SEAT_MOVE_OPCODE_ABSOLUTE_SPEED:
if(command->paramA > maxValue)
{
*reject = SEAT_MOVE_REJECT_RANGE;
return false;
}
/* fall through */
case SEAT_MOVE_OPCODE_RELATIVE_SPEED:
if(command->paramB == 0u)
{
*reject = SEAT_MOVE_REJECT_RANGE;
return false;
}
break;
case SEAT_MOVE_OPCODE_TIMED_SPEED:
if((command->paramA == 0u) ||
(command->paramA > SEAT_MOVE_MAX_COMMAND_DURATION_MS) ||
(command->paramB == 0u))
{
*reject = SEAT_MOVE_REJECT_RANGE;
return false;
}
break;
case SEAT_MOVE_OPCODE_ABSOLUTE_IN_TIME:
if((command->paramA > maxValue) || (command->paramB == 0u) ||
(command->paramB > SEAT_MOVE_MAX_COMMAND_DURATION_MS))
{
*reject = SEAT_MOVE_REJECT_RANGE;
return false;
}
break;
case SEAT_MOVE_OPCODE_STOP_MOTOR:
case SEAT_MOVE_OPCODE_STOP_ALL:
case SEAT_MOVE_OPCODE_CALIBRATE_RANGE:
case SEAT_MOVE_OPCODE_CALIBRATE_SPEED:
case SEAT_MOVE_OPCODE_SAVE_SHUTDOWN:
if((command->paramA != 0u) || (command->paramB != 0u))
{
return false;
}
break;
default:
return false;
}
*reject = SEAT_MOVE_REJECT_NONE;
return true;
}
static bool SeatMove_MotorBusy(uint8_t motor)
{
seat_motor_status_t status;
if((motor >= SEAT_MOVE_MOTOR_COUNT) || s_pending[motor].valid)
{
return true;
}
return SeatMotorManager_GetStatus((motor_mode_t)motor, &status) &&
status.active;
}
static bool SeatMove_AnyPending(void)
{
return (g_seatMovePendingMask != 0u);
}
static void SeatMove_HandleCommand(const bsp_seat_move_can_frame_t *frame)
{
seat_move_command_t command = { 0 };
seat_move_reject_t reject = SEAT_MOVE_REJECT_FORMAT;
bool stopAllCandidate = (frame != NULL) &&
((uint8_t)(frame->data0 & 0xFFu) == SEAT_MOVE_OPCODE_STOP_ALL);
g_seatMoveRxCommandCount++;
if(!SeatMove_ParseCommand(frame, &command) ||
!SeatMove_ParametersValid(&command, &reject))
{
g_seatMoveInvalidCommandCount++;
SeatMove_SetResponse(SEAT_MOVE_ACK_REJECTED,
(reject == SEAT_MOVE_REJECT_NONE) ?
SEAT_MOVE_REJECT_FORMAT : reject,
(frame != NULL) ? &command : NULL);
return;
}
if(!stopAllCandidate && s_lastSequenceValid &&
!SeatMove_SequenceNewer(command.sequence, s_sequenceReference))
{
g_seatMoveInvalidCommandCount++;
SeatMove_SetResponse(SEAT_MOVE_ACK_REJECTED,
SEAT_MOVE_REJECT_FORMAT,
&command);
return;
}
s_lastSequenceValid = true;
s_sequenceReference = command.sequence;
g_seatMoveLastSequence = command.sequence;
g_seatMoveLastOpcode = command.opcode;
if(command.opcode == SEAT_MOVE_OPCODE_STOP_ALL)
{
SeatMoveApplication_StopAll(command.sequence);
SeatMove_SetResponse(SEAT_MOVE_ACK_ACCEPTED,
SEAT_MOVE_REJECT_NONE,
&command);
g_seatMoveAcceptedCount++;
return;
}
if(command.opcode == SEAT_MOVE_OPCODE_STOP_MOTOR)
{
SeatMove_StopMotor(command.motor, command.sequence);
SeatMove_SetResponse(SEAT_MOVE_ACK_ACCEPTED,
SEAT_MOVE_REJECT_NONE,
&command);
g_seatMoveAcceptedCount++;
return;
}
if(SafetyManager_IsInhibited() || s_shutdownInhibited)
{
SeatMove_SetResponse(SEAT_MOVE_ACK_INHIBITED,
SEAT_MOVE_REJECT_INHIBITED,
&command);
return;
}
if(command.opcode == SEAT_MOVE_OPCODE_SAVE_SHUTDOWN)
{
SeatMoveApplication_StopAll(command.sequence);
SafetyManager_KickWatchdog();
if(!SeatMoveStore_Save())
{
SeatMove_SetResponse(SEAT_MOVE_ACK_STORAGE_ERROR,
SEAT_MOVE_REJECT_STORAGE,
&command);
SafetyManager_LatchFault(SAFETY_FAULT_CONFIG_STORE);
}
else
{
s_shutdownInhibited = true;
SeatMove_SetResponse(SEAT_MOVE_ACK_ACCEPTED,
SEAT_MOVE_REJECT_NONE,
&command);
g_seatMoveAcceptedCount++;
}
SafetyManager_KickWatchdog();
return;
}
if((command.opcode == SEAT_MOVE_OPCODE_CALIBRATE_RANGE) &&
(SeatMotorManager_IsAnyActive() || SeatMove_AnyPending()))
{
g_seatMoveConflictCount++;
SeatMove_SetResponse(SEAT_MOVE_ACK_CONFLICT,
SEAT_MOVE_REJECT_CONFLICT,
&command);
return;
}
if(SeatMove_MotorBusy(command.motor))
{
g_seatMoveConflictCount++;
SeatMove_SetResponse(SEAT_MOVE_ACK_CONFLICT,
SEAT_MOVE_REJECT_CONFLICT,
&command);
return;
}
SeatMove_QueueCommand(&command);
SeatMove_SetResponse(SEAT_MOVE_ACK_ACCEPTED,
SEAT_MOVE_REJECT_NONE,
&command);
g_seatMoveAcceptedCount++;
}
static void SeatMove_StopMotor(uint8_t motor, uint16_t sequence)
{
if(motor >= SEAT_MOVE_MOTOR_COUNT)
{
return;
}
Motor_RequestAbortMaskFromIsr((uint8_t)(1u << motor), sequence);
Motor_ServiceAll();
(void)Motor_AcknowledgeCommand((motor_mode_t)motor, sequence);
SeatMotorManager_Stop((motor_mode_t)motor, sequence);
s_pending[motor].valid = false;
g_seatMovePendingMask &= (uint8_t)(~(uint8_t)(1u << motor));
s_motorStatusPendingMask |= (uint8_t)(1u << motor);
s_systemStatusPending = true;
}
static void SeatMove_QueueCommand(const seat_move_command_t *command)
{
uint8_t motor = command->motor;
s_pending[motor].command = *command;
s_pending[motor].valid = true;
g_seatMovePendingMask |= (uint8_t)(1u << motor);
s_motorStatusPendingMask |= (uint8_t)(1u << motor);
s_systemStatusPending = true;
}
static bool SeatMove_DispatchCommand(const seat_move_command_t *command)
{
motor_mode_t mode = (motor_mode_t)command->motor;
int32_t delta = (int16_t)command->paramA;
switch((seat_move_opcode_t)command->opcode)
{
case SEAT_MOVE_OPCODE_ABSOLUTE_DUTY:
return SeatMotor_MoveAbsoluteDuty(mode,
command->paramA,
(uint8_t)command->paramB,
command->sequence);
case SEAT_MOVE_OPCODE_RELATIVE_DUTY:
return SeatMotor_MoveRelativeDuty(mode,
delta,
(uint8_t)command->paramB,
command->sequence);
case SEAT_MOVE_OPCODE_TIMED_DUTY:
return SeatMotor_MoveTimedDuty(mode,
command->direction,
command->paramA,
(uint8_t)command->paramB,
command->sequence);
case SEAT_MOVE_OPCODE_ABSOLUTE_SPEED:
return SeatMotor_MoveAbsoluteSpeed(mode,
command->paramA,
command->paramB,
command->sequence);
case SEAT_MOVE_OPCODE_RELATIVE_SPEED:
return SeatMotor_MoveRelativeSpeed(mode,
delta,
command->paramB,
command->sequence);
case SEAT_MOVE_OPCODE_TIMED_SPEED:
return SeatMotor_MoveTimedSpeed(mode,
command->direction,
command->paramA,
command->paramB,
command->sequence);
case SEAT_MOVE_OPCODE_ABSOLUTE_IN_TIME:
return SeatMotor_MoveAbsoluteInTime(mode,
command->paramA,
command->paramB,
command->sequence);
case SEAT_MOVE_OPCODE_CALIBRATE_RANGE:
return SeatMotor_CalibrateRange(mode, command->sequence);
case SEAT_MOVE_OPCODE_CALIBRATE_SPEED:
return SeatMotor_StartMaximumSpeedCalibration(mode,
command->sequence);
default:
return false;
}
}
static void SeatMove_ServiceDispatcher(void)
{
uint32_t now = Motor_GetTimeMs();
uint8_t checked;
if(SafetyManager_IsInhibited() || s_shutdownInhibited ||
!SeatMove_TimeReached(now, s_nextStartMs))
{
return;
}
for(checked = 0u; checked < SEAT_MOVE_MOTOR_COUNT; checked++)
{
uint8_t motor = (uint8_t)
((s_dispatchCursor + checked) % SEAT_MOVE_MOTOR_COUNT);
if(s_pending[motor].valid)
{
seat_move_command_t command = s_pending[motor].command;
s_pending[motor].valid = false;
g_seatMovePendingMask &= (uint8_t)(~(uint8_t)(1u << motor));
s_dispatchCursor = (uint8_t)((motor + 1u) % SEAT_MOVE_MOTOR_COUNT);
s_nextStartMs = now + SEAT_MOVE_START_STAGGER_MS;
if(!SeatMove_DispatchCommand(&command))
{
SeatMove_SetResponse(SEAT_MOVE_ACK_REJECTED,
SEAT_MOVE_REJECT_EXECUTION,
&command);
}
s_motorStatusPendingMask |= (uint8_t)(1u << motor);
s_systemStatusPending = true;
break;
}
}
}
static void SeatMove_ServiceStateChanges(void)
{
uint8_t motor;
for(motor = 0u; motor < SEAT_MOVE_MOTOR_COUNT; motor++)
{
seat_motor_status_t status;
if(SeatMotorManager_GetStatus((motor_mode_t)motor, &status) &&
(!s_lastStatusValid[motor] ||
SeatMove_StatusChanged(&status, &s_lastStatus[motor])))
{
s_lastStatus[motor] = status;
s_lastStatusValid[motor] = true;
s_motorStatusPendingMask |= (uint8_t)(1u << motor);
s_diagnosticPendingMask |= (uint8_t)(1u << motor);
s_systemStatusPending = true;
}
}
}
static bool SeatMove_StatusChanged(const seat_motor_status_t *lhs,
const seat_motor_status_t *rhs)
{
return (lhs->taskState != rhs->taskState) ||
(lhs->result != rhs->result) ||
(lhs->lastMotorEvent != rhs->lastMotorEvent) ||
(lhs->direction != rhs->direction) ||
(lhs->commandSequence != rhs->commandSequence) ||
(lhs->valueX100 != rhs->valueX100) ||
(lhs->hallCount != rhs->hallCount) ||
(lhs->maxHallCount != rhs->maxHallCount) ||
(lhs->maxSpeedPpsX100 != rhs->maxSpeedPpsX100) ||
(lhs->dutyPercent != rhs->dutyPercent) ||
(lhs->active != rhs->active) ||
(lhs->retried != rhs->retried) ||
(lhs->positionValid != rhs->positionValid);
}
static uint8_t SeatMove_ActiveMask(void)
{
uint8_t motor;
uint8_t mask = g_seatMovePendingMask;
for(motor = 0u; motor < SEAT_MOVE_MOTOR_COUNT; motor++)
{
seat_motor_status_t status;
if(SeatMotorManager_GetStatus((motor_mode_t)motor, &status) &&
status.active)
{
mask |= (uint8_t)(1u << motor);
}
}
return mask;
}
static uint8_t SeatMove_RetryMask(void)
{
uint8_t motor;
uint8_t mask = 0u;
for(motor = 0u; motor < SEAT_MOVE_MOTOR_COUNT; motor++)
{
seat_motor_status_t status;
if(SeatMotorManager_GetStatus((motor_mode_t)motor, &status) &&
status.retried)
{
mask |= (uint8_t)(1u << motor);
}
}
return mask;
}
static bool SeatMove_SendSystemStatus(void)
{
uint32_t data0;
uint32_t data1;
uint8_t activeMask = SeatMove_ActiveMask();
uint8_t retryMask = SeatMove_RetryMask();
uint8_t byte0 = (uint8_t)((g_seatMoveAck & 0x07u) |
((g_seatMoveRejectReason & 0x07u) << 3u) |
(s_shutdownInhibited ? 0x40u : 0u) |
(SafetyManager_IsInhibited() ? 0x80u : 0u));
uint8_t byte1 = (uint8_t)((g_seatMoveLastOpcode & 0x0Fu) |
((activeMask & 0x0Fu) << 4u));
uint8_t byte4 = (uint8_t)((retryMask & 0x0Fu) |
(SeatMoveStore_IsClean() ? 0x10u : 0u) |
(SafetyManager_WasWatchdogReset() ? 0x20u : 0u) |
(SeatMoveStore_IsValid() ? 0x40u : 0u));
uint16_t configSequence = (uint16_t)SeatMoveStore_GetSequence();
data0 = (uint32_t)byte0 |
((uint32_t)byte1 << 8u) |
((uint32_t)g_seatMoveLastSequence << 16u);
data1 = (uint32_t)byte4 |
((uint32_t)SafetyManager_GetFault() << 8u) |
((uint32_t)configSequence << 16u);
return BspSeatMoveCan_Send(SEAT_MOVE_CAN_SYSTEM_STATUS_ID, data0, data1);
}
static bool SeatMove_SendMotorStatus(uint8_t motor)
{
seat_motor_status_t status;
seat_move_command_t *pendingCommand = &s_pending[motor].command;
uint8_t taskState;
uint8_t result;
uint8_t direction;
uint8_t flags;
uint8_t duty;
uint16_t sequence;
uint16_t value;
uint32_t data0;
uint32_t data1;
if(!SeatMotorManager_GetStatus((motor_mode_t)motor, &status))
{
return false;
}
taskState = (uint8_t)status.taskState;
result = (uint8_t)status.result;
direction = (uint8_t)status.direction;
duty = status.dutyPercent;
sequence = (uint16_t)status.commandSequence;
if(s_pending[motor].valid)
{
taskState = (uint8_t)SEAT_MOTOR_TASK_WAIT_START;
result = (uint8_t)SEAT_MOTOR_RESULT_RUNNING;
direction = (uint8_t)pendingCommand->direction;
sequence = pendingCommand->sequence;
if((pendingCommand->opcode == SEAT_MOVE_OPCODE_ABSOLUTE_DUTY) ||
(pendingCommand->opcode == SEAT_MOVE_OPCODE_RELATIVE_DUTY) ||
(pendingCommand->opcode == SEAT_MOVE_OPCODE_TIMED_DUTY))
{
duty = (uint8_t)pendingCommand->paramB;
}
}
flags = (uint8_t)((direction & 0x03u) |
((status.active || s_pending[motor].valid) ? 0x04u : 0u) |
(status.retried ? 0x08u : 0u) |
(status.positionValid ? 0x10u : 0u));
value = (status.valueX100 > UINT16_MAX) ?
UINT16_MAX : (uint16_t)status.valueX100;
data0 = (uint32_t)motor |
((uint32_t)((taskState & 0x0Fu) | ((result & 0x0Fu) << 4u)) << 8u) |
((uint32_t)flags << 16u) |
((uint32_t)(sequence & 0xFFu) << 24u);
data1 = (uint32_t)(sequence >> 8u) |
((uint32_t)value << 8u) |
((uint32_t)duty << 24u);
return BspSeatMoveCan_Send(SEAT_MOVE_CAN_MOTOR_STATUS_ID, data0, data1);
}
static bool SeatMove_SendDiagnostic(uint8_t motor)
{
seat_motor_status_t status;
uint16_t hallCount;
uint16_t maxHallCount;
uint16_t maxSpeed;
uint32_t data0;
uint32_t data1;
if(!SeatMotorManager_GetStatus((motor_mode_t)motor, &status))
{
return false;
}
hallCount = (status.hallCount > UINT16_MAX) ?
UINT16_MAX : (uint16_t)status.hallCount;
maxHallCount = (status.maxHallCount > UINT16_MAX) ?
UINT16_MAX : (uint16_t)status.maxHallCount;
maxSpeed = (status.maxSpeedPpsX100 > UINT16_MAX) ?
UINT16_MAX : (uint16_t)status.maxSpeedPpsX100;
data0 = (uint32_t)motor |
((uint32_t)((uint8_t)status.lastMotorEvent & 0x0Fu) << 8u) |
((uint32_t)hallCount << 16u);
data1 = (uint32_t)maxHallCount | ((uint32_t)maxSpeed << 16u);
return BspSeatMoveCan_Send(SEAT_MOVE_CAN_DIAGNOSTIC_ID, data0, data1);
}
static void SeatMove_ServiceFeedback(void)
{
uint32_t now = Motor_GetTimeMs();
uint8_t motor;
if(SeatMove_TimeReached(now, s_nextSystemStatusMs))
{
s_systemStatusPending = true;
s_nextSystemStatusMs = now + SEAT_MOVE_SYSTEM_STATUS_PERIOD_MS;
}
if(SeatMove_TimeReached(now, s_nextMotorStatusMs))
{
s_motorStatusPendingMask |= SEAT_MOVE_ACTIVE_MOTOR_MASK;
s_nextMotorStatusMs = now + SEAT_MOVE_MOTOR_STATUS_PERIOD_MS;
}
if(SeatMove_TimeReached(now, s_nextDiagnosticMs))
{
s_diagnosticPendingMask |= SEAT_MOVE_ACTIVE_MOTOR_MASK;
s_nextDiagnosticMs = now + SEAT_MOVE_DIAGNOSTIC_PERIOD_MS;
}
if(s_systemStatusPending)
{
if(SeatMove_SendSystemStatus())
{
s_systemStatusPending = false;
g_seatMoveFeedbackTxCount++;
/* Pace CAN feedback to one frame per 1 ms application service.
* A 500 ms boundary can make 1 system, 4 motor-status and
* 4 diagnostic frames pending at once. Sending all nine in one
* call overruns the eight dedicated TX buffers and delays the
* Motor3 diagnostic frame. */
return;
}
else
{
g_seatMoveFeedbackTxFailureCount++;
return;
}
}
for(motor = 0u; motor < SEAT_MOVE_MOTOR_COUNT; motor++)
{
uint8_t bit = (uint8_t)(1u << motor);
if((s_motorStatusPendingMask & bit) != 0u)
{
if(SeatMove_SendMotorStatus(motor))
{
s_motorStatusPendingMask &= (uint8_t)(~bit);
g_seatMoveFeedbackTxCount++;
return;
}
else
{
g_seatMoveFeedbackTxFailureCount++;
return;
}
}
}
for(motor = 0u; motor < SEAT_MOVE_MOTOR_COUNT; motor++)
{
uint8_t bit = (uint8_t)(1u << motor);
if((s_diagnosticPendingMask & bit) != 0u)
{
if(SeatMove_SendDiagnostic(motor))
{
s_diagnosticPendingMask &= (uint8_t)(~bit);
g_seatMoveFeedbackTxCount++;
return;
}
else
{
g_seatMoveFeedbackTxFailureCount++;
return;
}
}
}
}
@@ -0,0 +1,23 @@
#ifndef SEAT_MOVE_APPLICATION_H
#define SEAT_MOVE_APPLICATION_H
#include <stdbool.h>
#include <stdint.h>
bool SeatMoveApplication_Init(void);
void SeatMoveApplication_Service(void);
void SeatMoveApplication_StopAll(uint16_t sequence);
extern volatile uint32_t g_seatMoveRxCommandCount;
extern volatile uint32_t g_seatMoveInvalidCommandCount;
extern volatile uint32_t g_seatMoveConflictCount;
extern volatile uint32_t g_seatMoveAcceptedCount;
extern volatile uint32_t g_seatMoveFeedbackTxCount;
extern volatile uint32_t g_seatMoveFeedbackTxFailureCount;
extern volatile uint8_t g_seatMoveAck;
extern volatile uint8_t g_seatMoveRejectReason;
extern volatile uint8_t g_seatMoveLastOpcode;
extern volatile uint16_t g_seatMoveLastSequence;
extern volatile uint8_t g_seatMovePendingMask;
#endif /* SEAT_MOVE_APPLICATION_H */
+283
View File
@@ -0,0 +1,283 @@
#include "seat_move_store.h"
#include <stddef.h>
#include "cy_project.h"
#include "cy_device_headers.h"
#include "flash/cy_mw_flash.h"
#include "motor_control.h"
#include "seat_move_config.h"
#define SEAT_MOVE_STORE_SLOT_WORDS (CY_WORK_SES_SIZE_IN_WORD)
#define SEAT_MOVE_STORE_SLOT_COUNT (2u)
#define SEAT_MOVE_STORE_HEADER_WORDS (4u)
#define SEAT_MOVE_STORE_MOTOR_WORDS (5u)
#define SEAT_MOVE_STORE_CRC_WORD_INDEX \
(SEAT_MOVE_STORE_HEADER_WORDS + \
(SEAT_MOVE_MOTOR_COUNT * SEAT_MOVE_STORE_MOTOR_WORDS))
#define SEAT_MOVE_STORE_LEGACY_CRC_WORD_INDEX \
(SEAT_MOVE_STORE_HEADER_WORDS + \
(SEAT_MOVE_STORE_LEGACY_MOTOR_COUNT * SEAT_MOVE_STORE_MOTOR_WORDS))
typedef char seat_move_store_record_must_fit[
(SEAT_MOVE_STORE_CRC_WORD_INDEX < SEAT_MOVE_STORE_SLOT_WORDS) ? 1 : -1];
typedef enum
{
SEAT_MOVE_STORE_FORMAT_INVALID = 0,
SEAT_MOVE_STORE_FORMAT_LEGACY_THREE_MOTOR,
SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR
} seat_move_store_format_t;
static uint32_t s_record[SEAT_MOVE_STORE_SLOT_WORDS];
static uint32_t s_activeSlot = 0u;
static bool s_valid = false;
static bool s_clean = false;
static seat_move_store_format_t s_format = SEAT_MOVE_STORE_FORMAT_INVALID;
static uint32_t SeatMoveStore_Crc32(const uint32_t *words, uint32_t wordCount);
static uint32_t SeatMoveStore_SlotAddress(uint32_t slot);
static seat_move_store_format_t SeatMoveStore_ReadSlot(
uint32_t slot, uint32_t *destination);
static bool SeatMoveStore_SequenceNewer(uint32_t candidate, uint32_t reference);
void SeatMoveStore_Init(void)
{
uint32_t slot0[SEAT_MOVE_STORE_SLOT_WORDS];
uint32_t slot1[SEAT_MOVE_STORE_SLOT_WORDS];
seat_move_store_format_t slot0Format;
seat_move_store_format_t slot1Format;
bool slot0Valid;
bool slot1Valid;
uint32_t index;
Cy_FlashInit(false);
slot0Format = SeatMoveStore_ReadSlot(0u, slot0);
slot1Format = SeatMoveStore_ReadSlot(1u, slot1);
slot0Valid = (slot0Format != SEAT_MOVE_STORE_FORMAT_INVALID);
slot1Valid = (slot1Format != SEAT_MOVE_STORE_FORMAT_INVALID);
s_valid = slot0Valid || slot1Valid;
if(!s_valid)
{
s_clean = false;
s_activeSlot = 0u;
s_format = SEAT_MOVE_STORE_FORMAT_INVALID;
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
s_record[index] = 0xFFFFFFFFu;
}
return;
}
if(slot1Valid &&
(!slot0Valid || SeatMoveStore_SequenceNewer(slot1[2], slot0[2])))
{
s_activeSlot = 1u;
s_format = slot1Format;
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
s_record[index] = slot1[index];
}
}
else
{
s_activeSlot = 0u;
s_format = slot0Format;
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
s_record[index] = slot0[index];
}
}
s_clean = (s_record[3] == 1u);
}
bool SeatMoveStore_Restore(void)
{
uint32_t motorIndex;
uint32_t restoreMotorCount;
if(!s_valid || !s_clean)
{
return false;
}
/* Preserve the already calibrated motors 0..2 when upgrading an SMV2
* three-motor record. Motor3 remains at its estimated defaults with
* positionValid=false until it is physically connected and calibrated. */
restoreMotorCount =
(s_format == SEAT_MOVE_STORE_FORMAT_LEGACY_THREE_MOTOR) ?
SEAT_MOVE_STORE_LEGACY_MOTOR_COUNT : SEAT_MOVE_MOTOR_COUNT;
for(motorIndex = 0u; motorIndex < restoreMotorCount; motorIndex++)
{
uint32_t base = SEAT_MOVE_STORE_HEADER_WORDS +
(motorIndex * SEAT_MOVE_STORE_MOTOR_WORDS);
motor_persistent_state_t state;
state.positionHallCount = s_record[base];
state.maxHallCount = s_record[base + 1u];
state.maxSpeedPulsesPerSecondX100 = s_record[base + 2u];
state.rawRisingEdges = s_record[base + 3u];
state.positionValid = (s_record[base + 4u] == 1u);
if(MOTOR_API_SUCCESS != Motor_ApplyPersistentState(
(motor_mode_t)motorIndex, &state))
{
return false;
}
}
return true;
}
bool SeatMoveStore_Save(void)
{
uint32_t newRecord[SEAT_MOVE_STORE_SLOT_WORDS];
uint32_t motorIndex;
uint32_t wordIndex;
uint32_t targetSlot = s_valid ? (s_activeSlot ^ 1u) : 0u;
uint32_t address = SeatMoveStore_SlotAddress(targetSlot);
volatile const uint32_t *flashWords =
(volatile const uint32_t *)(uintptr_t)address;
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
newRecord[wordIndex] = 0xFFFFFFFFu;
}
newRecord[0] = SEAT_MOVE_STORE_MAGIC;
newRecord[1] = SEAT_MOVE_STORE_VERSION;
newRecord[2] = s_valid ? (s_record[2] + 1u) : 1u;
newRecord[3] = 1u;
for(motorIndex = 0u; motorIndex < SEAT_MOVE_MOTOR_COUNT; motorIndex++)
{
uint32_t base = SEAT_MOVE_STORE_HEADER_WORDS +
(motorIndex * SEAT_MOVE_STORE_MOTOR_WORDS);
motor_persistent_state_t state;
if(MOTOR_API_SUCCESS != Motor_GetPersistentState(
(motor_mode_t)motorIndex, &state))
{
return false;
}
newRecord[base] = state.positionHallCount;
newRecord[base + 1u] = state.maxHallCount;
newRecord[base + 2u] = state.maxSpeedPulsesPerSecondX100;
newRecord[base + 3u] = state.rawRisingEdges;
newRecord[base + 4u] = state.positionValid ? 1u : 0u;
}
newRecord[SEAT_MOVE_STORE_CRC_WORD_INDEX] =
SeatMoveStore_Crc32(newRecord, SEAT_MOVE_STORE_CRC_WORD_INDEX);
Cy_FlashSectorErase(address, CY_FLASH_DRIVER_BLOCKING);
if(!Cy_WorkFlashBlankCheck(address, CY_FLASH_DRIVER_BLOCKING))
{
return false;
}
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
Cy_FlashWriteWork(address + (wordIndex * sizeof(uint32_t)),
&newRecord[wordIndex],
CY_FLASH_DRIVER_BLOCKING);
}
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
if(flashWords[wordIndex] != newRecord[wordIndex])
{
return false;
}
s_record[wordIndex] = newRecord[wordIndex];
}
s_activeSlot = targetSlot;
s_valid = true;
s_clean = true;
s_format = SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR;
return true;
}
bool SeatMoveStore_IsValid(void)
{
return s_valid;
}
bool SeatMoveStore_IsClean(void)
{
return s_clean;
}
uint32_t SeatMoveStore_GetSequence(void)
{
return s_valid ? s_record[2] : 0u;
}
static uint32_t SeatMoveStore_Crc32(const uint32_t *words, uint32_t wordCount)
{
uint32_t crc = 0xFFFFFFFFu;
uint32_t wordIndex;
for(wordIndex = 0u; wordIndex < wordCount; wordIndex++)
{
uint32_t value = words[wordIndex];
uint32_t byteIndex;
for(byteIndex = 0u; byteIndex < sizeof(uint32_t); byteIndex++)
{
uint32_t bitIndex;
crc ^= (value & 0xFFu);
value >>= 8u;
for(bitIndex = 0u; bitIndex < 8u; bitIndex++)
{
crc = (crc >> 1u) ^
((crc & 1u) ? 0xEDB88320u : 0u);
}
}
}
return ~crc;
}
static uint32_t SeatMoveStore_SlotAddress(uint32_t slot)
{
return SEAT_MOVE_STORE_ADDRESS + (slot * CY_WORK_SES_SIZE_IN_BYTE);
}
static seat_move_store_format_t SeatMoveStore_ReadSlot(
uint32_t slot, uint32_t *destination)
{
volatile const uint32_t *source =
(volatile const uint32_t *)(uintptr_t)SeatMoveStore_SlotAddress(slot);
uint32_t index;
if(destination == NULL)
{
return SEAT_MOVE_STORE_FORMAT_INVALID;
}
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
destination[index] = source[index];
}
if(destination[3] > 1u)
{
return SEAT_MOVE_STORE_FORMAT_INVALID;
}
if((destination[0] == SEAT_MOVE_STORE_MAGIC) &&
(destination[1] == SEAT_MOVE_STORE_VERSION) &&
(destination[SEAT_MOVE_STORE_CRC_WORD_INDEX] ==
SeatMoveStore_Crc32(destination,
SEAT_MOVE_STORE_CRC_WORD_INDEX)))
{
return SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR;
}
if((destination[0] == SEAT_MOVE_STORE_LEGACY_MAGIC) &&
(destination[1] == SEAT_MOVE_STORE_LEGACY_VERSION) &&
(destination[SEAT_MOVE_STORE_LEGACY_CRC_WORD_INDEX] ==
SeatMoveStore_Crc32(destination,
SEAT_MOVE_STORE_LEGACY_CRC_WORD_INDEX)))
{
return SEAT_MOVE_STORE_FORMAT_LEGACY_THREE_MOTOR;
}
return SEAT_MOVE_STORE_FORMAT_INVALID;
}
static bool SeatMoveStore_SequenceNewer(uint32_t candidate, uint32_t reference)
{
return ((int32_t)(candidate - reference) > 0);
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef SEAT_MOVE_STORE_H
#define SEAT_MOVE_STORE_H
#include <stdbool.h>
#include <stdint.h>
void SeatMoveStore_Init(void);
bool SeatMoveStore_Restore(void);
bool SeatMoveStore_Save(void);
bool SeatMoveStore_IsValid(void);
bool SeatMoveStore_IsClean(void);
uint32_t SeatMoveStore_GetSequence(void);
#endif /* SEAT_MOVE_STORE_H */
@@ -0,0 +1,681 @@
#include "bsp_seat_backrest_heater.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "seat_backrest_heater_config.h"
static bool BspSeatBackrestHeater_ConfigClock(void);
static uint32_t BspSeatBackrestHeater_DutyToCompare(uint32_t dutyPercent);
static void BspSeatBackrestHeater_SetPinGpioLow(void);
static bool BspSeatBackrestHeaterCan_ConfigClock(void);
static bool BspSeatBackrestHeaterCan_ConfigPins(void);
static void BspSeatBackrestHeaterCan_IrqHandler(void);
static void BspSeatBackrestHeaterCan_RxCallback(
bool rxFifoMessage,
uint8_t rxBufferOrFifo,
cy_stc_canfd_msg_t *message);
static uint32_t s_heaterDutyPercent = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanInitStatus =
(uint32_t)CY_CANFD_SUCCESS;
volatile uint32_t g_bspSeatBackrestHeaterCanRxCount = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanRxQueueOverflowCount = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanTxCount = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanTxBusyCount = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanLastRxId = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanLastTxId = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanLastTxStatus =
(uint32_t)CY_CANFD_SUCCESS;
static volatile bsp_seat_backrest_heater_can_frame_t
s_canRxQueue[SEAT_BACKREST_HEATER_CAN_RX_QUEUE_DEPTH];
static volatile uint8_t s_canRxQueueHead = 0u;
static volatile uint8_t s_canRxQueueTail = 0u;
static const cy_stc_id_filter_t s_canStandardIdFilters[] =
{
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_BACKREST_HEATER_CAN_CONTROL_29E_ID,
SEAT_BACKREST_HEATER_CAN_CONTROL_29E_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_BACKREST_HEATER_CAN_CONFIG_2AA_ID,
SEAT_BACKREST_HEATER_CAN_CONFIG_2AA_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_BACKREST_HEATER_CAN_MODE_3CE_ID,
SEAT_BACKREST_HEATER_CAN_MODE_3CE_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_BACKREST_HEATER_CAN_SWITCH_42A_ID,
SEAT_BACKREST_HEATER_CAN_SWITCH_42A_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
};
static cy_stc_sysint_irq_t s_canIrqConfig =
{
.sysIntSrc = SEAT_BACKREST_HEATER_CAN_IRQN,
.intIdx = SEAT_BACKREST_HEATER_CAN_CPU_IRQ,
.isEnabled = true,
};
static cy_stc_canfd_config_t s_canConfig =
{
.txCallback = NULL,
.rxCallback = BspSeatBackrestHeaterCan_RxCallback,
.rxFifoWithTopCallback = NULL,
.statusCallback = NULL,
.errorCallback = NULL,
.canFDMode = false,
.bitrate =
{
/* 40 MHz / (10 * (1 + 5 + 2)) = 500 kbit/s. */
.prescaler = 10u - 1u,
.timeSegment1 = 5u - 1u,
.timeSegment2 = 2u - 1u,
.syncJumpWidth = 2u - 1u,
},
.fastBitrate =
{
.prescaler = 0u,
.timeSegment1 = 0u,
.timeSegment2 = 0u,
.syncJumpWidth = 0u,
},
.tdcConfig =
{
.tdcEnabled = false,
.tdcOffset = 0u,
.tdcFilterWindow = 0u,
},
.sidFilterConfig =
{
.numberOfSIDFilters =
sizeof(s_canStandardIdFilters) /
sizeof(s_canStandardIdFilters[0]),
.sidFilter = s_canStandardIdFilters,
},
.extidFilterConfig =
{
.numberOfEXTIDFilters = 0u,
.extidFilter = NULL,
.extIDANDMask = 0x1FFFFFFFu,
},
.globalFilterConfig =
{
.nonMatchingFramesStandard = CY_CANFD_REJECT_NON_MATCHING,
.nonMatchingFramesExtended = CY_CANFD_REJECT_NON_MATCHING,
.rejectRemoteFramesStandard = true,
.rejectRemoteFramesExtended = true,
},
.rxBufferDataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo1DataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo0DataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.txBufferDataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo0Config =
{
.mode = CY_CANFD_FIFO_MODE_BLOCKING,
.watermark = 0u,
.numberOfFifoElements =
SEAT_BACKREST_HEATER_CAN_RX_FIFO0_ELEMENTS,
.topPointerLogicEnabled = false,
},
.rxFifo1Config =
{
.mode = CY_CANFD_FIFO_MODE_BLOCKING,
.watermark = 0u,
.numberOfFifoElements = 0u,
.topPointerLogicEnabled = false,
},
.noOfRxBuffers = 0u,
.noOfTxBuffers = SEAT_BACKREST_HEATER_CAN_TX_BUFFER_COUNT,
};
static const cy_stc_tcpwm_pwm_config_t s_heaterPwmConfig =
{
.pwmMode = CY_TCPWM_PWM_MODE_PWM,
.clockPrescaler = CY_TCPWM_PRESCALER_DIVBY_1,
.debug_pause = false,
.deadTime = 0u,
.deadTimeComp = 0u,
.runMode = CY_TCPWM_PWM_CONTINUOUS,
.countDirection = CY_TCPWM_COUNTER_COUNT_UP,
.cc0MatchMode = CY_TCPWM_PWM_TR_CTRL2_CLEAR,
.overflowMode = CY_TCPWM_PWM_TR_CTRL2_SET,
.underflowMode = CY_TCPWM_PWM_TR_CTRL2_NO_CHANGE,
.cc1MatchMode = CY_TCPWM_PWM_TR_CTRL2_NO_CHANGE,
.period = SEAT_BACKREST_HEATER_PWM_PERIOD_TICKS - 1u,
.period_buff = 0u,
.enablePeriodSwap = false,
.enableLineSelSwap = false,
.compare0 = 0u,
.compare0_buff = 0u,
.enableCompare0Swap = false,
.compare1 = 0u,
.compare1_buff = 0u,
.enableCompare1Swap = false,
.interruptSources = CY_TCPWM_INT_NONE,
.invertPWMOut = 0u,
.invertPWMOutN = 0u,
.killMode = CY_TCPWM_PWM_STOP_ON_KILL,
.switchInputMode = CY_TCPWM_INPUT_LEVEL,
.switchInput = 0u,
.reloadInputMode = CY_TCPWM_INPUT_LEVEL,
.reloadInput = 0u,
.startInputMode = CY_TCPWM_INPUT_LEVEL,
.startInput = 0u,
.kill0InputMode = CY_TCPWM_INPUT_LEVEL,
.kill0Input = 0u,
.kill1InputMode = CY_TCPWM_INPUT_LEVEL,
.kill1Input = 0u,
.countInputMode = CY_TCPWM_INPUT_LEVEL,
.countInput = 1u,
.immediateKill = true,
.pwmOnDisable = CY_TCPWM_PWM_OUT_MODE_LOW,
.trigger0EventCfg = CY_TCPWM_COUNTER_OVERFLOW,
.trigger1EventCfg = CY_TCPWM_COUNTER_OVERFLOW,
};
bool BspSeatBackrestHeater_ForceOff(void)
{
cy_stc_gpio_pin_config_t pinConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_STRONG_IN_OFF,
.hsiom = SEAT_BACKREST_HEATER_PWM_GPIO_HSIOM,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = CY_GPIO_DRIVE_FULL,
};
cy_en_gpio_status_t status;
status = Cy_GPIO_Pin_Init(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
&pinConfig);
Cy_GPIO_Write(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
0u);
s_heaterDutyPercent = 0u;
return ((status == CY_GPIO_SUCCESS) &&
(Cy_GPIO_GetHSIOM(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN) ==
SEAT_BACKREST_HEATER_PWM_GPIO_HSIOM));
}
bool BspSeatBackrestHeater_InitPwm(void)
{
if(!BspSeatBackrestHeater_ConfigClock())
{
return false;
}
Cy_Tcpwm_Pwm_Disable(SEAT_BACKREST_HEATER_PWM_TCPWM);
if(Cy_Tcpwm_Pwm_Init(SEAT_BACKREST_HEATER_PWM_TCPWM,
&s_heaterPwmConfig) != CY_RET_SUCCESS)
{
return false;
}
return ((Cy_Tcpwm_Pwm_GetPeriod(SEAT_BACKREST_HEATER_PWM_TCPWM) ==
(SEAT_BACKREST_HEATER_PWM_PERIOD_TICKS - 1u)) &&
(Cy_Tcpwm_Pwm_GetCompare0(SEAT_BACKREST_HEATER_PWM_TCPWM) == 0u));
}
bool BspSeatBackrestHeater_Start(uint32_t dutyPercent)
{
if(!BspSeatBackrestHeater_SetDutyPercent(dutyPercent))
{
return false;
}
/* P16.7 remains GPIO low until the PWM counter is confirmed running. */
Cy_GPIO_Write(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
0u);
Cy_Tcpwm_Pwm_SetCounter(SEAT_BACKREST_HEATER_PWM_TCPWM,
SEAT_BACKREST_HEATER_PWM_PERIOD_TICKS - 1u);
Cy_Tcpwm_Pwm_Enable(SEAT_BACKREST_HEATER_PWM_TCPWM);
Cy_Tcpwm_TriggerStart(SEAT_BACKREST_HEATER_PWM_TCPWM);
Cy_SysTick_DelayInUs(2u);
if((Cy_Tcpwm_Pwm_GetStatus(SEAT_BACKREST_HEATER_PWM_TCPWM) &
TCPWM_GRP_CNT_STATUS_RUNNING_Msk) == 0u)
{
BspSeatBackrestHeater_FailSafeOff();
return false;
}
Cy_GPIO_SetDriveSel(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
CY_GPIO_DRIVE_FULL);
Cy_GPIO_SetDrivemode(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
CY_GPIO_DM_STRONG_IN_OFF);
Cy_GPIO_SetHSIOM(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
SEAT_BACKREST_HEATER_PWM_HSIOM);
if(Cy_GPIO_GetHSIOM(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN) !=
SEAT_BACKREST_HEATER_PWM_HSIOM)
{
BspSeatBackrestHeater_FailSafeOff();
return false;
}
return true;
}
bool BspSeatBackrestHeater_SetDutyPercent(uint32_t dutyPercent)
{
uint32_t compare;
/* A true OFF uses FailSafeOff, avoiding a possible zero-compare pulse. */
if((dutyPercent == 0u) || (dutyPercent > 100u))
{
return false;
}
compare = BspSeatBackrestHeater_DutyToCompare(dutyPercent);
Cy_Tcpwm_Pwm_SetCompare0(SEAT_BACKREST_HEATER_PWM_TCPWM, compare);
if(Cy_Tcpwm_Pwm_GetCompare0(SEAT_BACKREST_HEATER_PWM_TCPWM) != compare)
{
return false;
}
s_heaterDutyPercent = dutyPercent;
return true;
}
void BspSeatBackrestHeater_FailSafeOff(void)
{
Cy_Tcpwm_Pwm_Disable(SEAT_BACKREST_HEATER_PWM_TCPWM);
BspSeatBackrestHeater_SetPinGpioLow();
s_heaterDutyPercent = 0u;
}
bool BspSeatBackrestHeater_IsRunning(void)
{
return ((Cy_Tcpwm_Pwm_GetStatus(SEAT_BACKREST_HEATER_PWM_TCPWM) &
TCPWM_GRP_CNT_STATUS_RUNNING_Msk) != 0u);
}
uint32_t BspSeatBackrestHeater_GetDutyPercent(void)
{
return s_heaterDutyPercent;
}
bool BspSeatBackrestHeaterCan_Init(void)
{
cy_en_canfd_status_t canStatus;
if(!BspSeatBackrestHeaterCan_ConfigClock() ||
!BspSeatBackrestHeaterCan_ConfigPins())
{
g_bspSeatBackrestHeaterCanInitStatus =
(uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
if(Cy_SysInt_InitIRQ(&s_canIrqConfig) != CY_SYSINT_SUCCESS)
{
g_bspSeatBackrestHeaterCanInitStatus =
(uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
Cy_SysInt_SetSystemIrqVector(s_canIrqConfig.sysIntSrc,
BspSeatBackrestHeaterCan_IrqHandler);
NVIC_SetPriority(s_canIrqConfig.intIdx, 0u);
NVIC_ClearPendingIRQ(s_canIrqConfig.intIdx);
(void)Cy_CANFD_DeInit(SEAT_BACKREST_HEATER_CAN_TYPE);
canStatus = Cy_CANFD_Init(SEAT_BACKREST_HEATER_CAN_TYPE, &s_canConfig);
g_bspSeatBackrestHeaterCanInitStatus = (uint32_t)canStatus;
if(canStatus != CY_CANFD_SUCCESS)
{
return false;
}
NVIC_EnableIRQ(s_canIrqConfig.intIdx);
return true;
}
bool BspSeatBackrestHeaterCan_Read(
bsp_seat_backrest_heater_can_frame_t *frame)
{
uint32_t interruptState;
uint8_t tail;
if(frame == NULL)
{
return false;
}
interruptState = Cy_SysLib_EnterCriticalSection();
tail = s_canRxQueueTail;
if(tail == s_canRxQueueHead)
{
Cy_SysLib_ExitCriticalSection(interruptState);
return false;
}
frame->identifier = s_canRxQueue[tail].identifier;
frame->data0 = s_canRxQueue[tail].data0;
frame->data1 = s_canRxQueue[tail].data1;
frame->dlc = s_canRxQueue[tail].dlc;
tail++;
if(tail >= SEAT_BACKREST_HEATER_CAN_RX_QUEUE_DEPTH)
{
tail = 0u;
}
s_canRxQueueTail = tail;
Cy_SysLib_ExitCriticalSection(interruptState);
return true;
}
bool BspSeatBackrestHeaterCan_Send(uint32_t identifier,
uint32_t data0,
uint32_t data1)
{
cy_stc_canfd_msg_t message;
uint8_t bufferIndex;
uint8_t dataIndex;
if(identifier > 0x7FFu)
{
g_bspSeatBackrestHeaterCanLastTxStatus =
(uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
message.canFDFormat = false;
message.idConfig.extended = false;
message.idConfig.identifier = identifier;
message.dataConfig.dataLengthCode = SEAT_BACKREST_HEATER_CAN_DLC;
message.dataConfig.data[0] = data0;
message.dataConfig.data[1] = data1;
for(dataIndex = 2u;
dataIndex < CY_CANFD_MESSAGE_DATA_BUFFER_SIZE;
dataIndex++)
{
message.dataConfig.data[dataIndex] = 0u;
}
for(bufferIndex = 0u;
bufferIndex < SEAT_BACKREST_HEATER_CAN_TX_BUFFER_COUNT;
bufferIndex++)
{
cy_en_canfd_tx_buffer_status_t bufferStatus =
Cy_CANFD_GetTxBufferStatus(SEAT_BACKREST_HEATER_CAN_TYPE,
bufferIndex);
if((bufferStatus != CY_CANFD_TX_BUFFER_PENDING) &&
(bufferStatus != CY_CANFD_TX_BUFFER_CANCEL_REQUESTED))
{
cy_en_canfd_status_t sendStatus;
g_bspSeatBackrestHeaterCanLastTxId = identifier;
sendStatus = Cy_CANFD_UpdateAndTransmitMsgBuffer(
SEAT_BACKREST_HEATER_CAN_TYPE,
bufferIndex,
&message);
g_bspSeatBackrestHeaterCanLastTxStatus = (uint32_t)sendStatus;
if(sendStatus == CY_CANFD_SUCCESS)
{
g_bspSeatBackrestHeaterCanTxCount++;
return true;
}
return false;
}
}
g_bspSeatBackrestHeaterCanTxBusyCount++;
return false;
}
static bool BspSeatBackrestHeaterCan_ConfigClock(void)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup;
if(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS)
{
return false;
}
if((periClockHz < SEAT_BACKREST_HEATER_CAN_CLOCK_HZ) ||
((periClockHz % SEAT_BACKREST_HEATER_CAN_CLOCK_HZ) != 0u))
{
return false;
}
divider = periClockHz / SEAT_BACKREST_HEATER_CAN_CLOCK_HZ;
if((divider == 0u) || (divider > 256u))
{
return false;
}
clockGroup = Cy_SysClk_GetClockGroup(SEAT_BACKREST_HEATER_CAN_PCLK);
if(Cy_SysClk_PeriphDisableDivider(
clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_BACKREST_HEATER_CAN_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(
SEAT_BACKREST_HEATER_CAN_PCLK,
CY_SYSCLK_DIV_8_BIT,
SEAT_BACKREST_HEATER_CAN_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(
clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_BACKREST_HEATER_CAN_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
return (Cy_SysClk_PeriphEnableDivider(
clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_BACKREST_HEATER_CAN_DIVIDER_NUMBER) ==
CY_SYSCLK_SUCCESS);
}
static bool BspSeatBackrestHeaterCan_ConfigPins(void)
{
cy_stc_gpio_pin_config_t rxConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_HIGHZ,
.hsiom = SEAT_BACKREST_HEATER_CAN_RX_MUX,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_gpio_pin_config_t txConfig =
{
.outVal = 1u,
.driveMode = CY_GPIO_DM_STRONG,
.hsiom = SEAT_BACKREST_HEATER_CAN_TX_MUX,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_gpio_pin_config_t transceiverConfig =
{
.outVal = SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_ENABLE,
.driveMode = CY_GPIO_DM_STRONG_IN_OFF,
.hsiom = SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_GPIO_HSIOM,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = CY_GPIO_DRIVE_FULL,
};
if(Cy_GPIO_Pin_Init(SEAT_BACKREST_HEATER_CAN_RX_PORT,
SEAT_BACKREST_HEATER_CAN_RX_PIN,
&rxConfig) != CY_GPIO_SUCCESS)
{
return false;
}
if(Cy_GPIO_Pin_Init(SEAT_BACKREST_HEATER_CAN_TX_PORT,
SEAT_BACKREST_HEATER_CAN_TX_PIN,
&txConfig) != CY_GPIO_SUCCESS)
{
return false;
}
if(Cy_GPIO_Pin_Init(SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PORT,
SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PIN,
&transceiverConfig) != CY_GPIO_SUCCESS)
{
return false;
}
Cy_GPIO_Write(SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PORT,
SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PIN,
SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_ENABLE);
Cy_SysTick_DelayInUs(1000u);
return (Cy_GPIO_ReadOut(SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PORT,
SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PIN) ==
SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_ENABLE);
}
static void BspSeatBackrestHeaterCan_IrqHandler(void)
{
Cy_CANFD_IrqHandler(SEAT_BACKREST_HEATER_CAN_TYPE);
}
static void BspSeatBackrestHeaterCan_RxCallback(
bool rxFifoMessage,
uint8_t rxBufferOrFifo,
cy_stc_canfd_msg_t *message)
{
uint8_t head;
uint8_t nextHead;
(void)rxFifoMessage;
(void)rxBufferOrFifo;
if((message == NULL) || message->idConfig.extended || message->canFDFormat)
{
return;
}
g_bspSeatBackrestHeaterCanRxCount++;
g_bspSeatBackrestHeaterCanLastRxId = message->idConfig.identifier;
head = s_canRxQueueHead;
nextHead = head + 1u;
if(nextHead >= SEAT_BACKREST_HEATER_CAN_RX_QUEUE_DEPTH)
{
nextHead = 0u;
}
if(nextHead == s_canRxQueueTail)
{
g_bspSeatBackrestHeaterCanRxQueueOverflowCount++;
return;
}
s_canRxQueue[head].identifier = message->idConfig.identifier;
s_canRxQueue[head].dlc = message->dataConfig.dataLengthCode;
s_canRxQueue[head].data0 = message->dataConfig.data[0];
s_canRxQueue[head].data1 = message->dataConfig.data[1];
s_canRxQueueHead = nextHead;
}
static bool BspSeatBackrestHeater_ConfigClock(void)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup;
if(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS)
{
return false;
}
if((periClockHz < SEAT_BACKREST_HEATER_COUNTER_CLOCK_HZ) ||
((periClockHz % SEAT_BACKREST_HEATER_COUNTER_CLOCK_HZ) != 0u))
{
return false;
}
divider = periClockHz / SEAT_BACKREST_HEATER_COUNTER_CLOCK_HZ;
if((divider == 0u) || (divider > 65536u))
{
return false;
}
clockGroup = Cy_SysClk_GetClockGroup(SEAT_BACKREST_HEATER_PWM_PCLK);
if(Cy_SysClk_PeriphDisableDivider(clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_PWM_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(SEAT_BACKREST_HEATER_PWM_PCLK,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_PWM_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_PWM_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
return (Cy_SysClk_PeriphEnableDivider(clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_PWM_DIVIDER_NUMBER) ==
CY_SYSCLK_SUCCESS);
}
static uint32_t BspSeatBackrestHeater_DutyToCompare(uint32_t dutyPercent)
{
return (SEAT_BACKREST_HEATER_PWM_PERIOD_TICKS * dutyPercent) / 100u;
}
static void BspSeatBackrestHeater_SetPinGpioLow(void)
{
Cy_GPIO_Write(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
0u);
Cy_GPIO_SetHSIOM(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
SEAT_BACKREST_HEATER_PWM_GPIO_HSIOM);
Cy_GPIO_SetDriveSel(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
CY_GPIO_DRIVE_FULL);
Cy_GPIO_SetDrivemode(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
CY_GPIO_DM_STRONG_IN_OFF);
Cy_GPIO_Write(SEAT_BACKREST_HEATER_PWM_PORT,
SEAT_BACKREST_HEATER_PWM_PIN,
0u);
}
@@ -0,0 +1,55 @@
#ifndef BSP_SEAT_BACKREST_HEATER_H
#define BSP_SEAT_BACKREST_HEATER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct
{
uint32_t identifier;
uint32_t data0;
uint32_t data1;
uint8_t dlc;
} bsp_seat_backrest_heater_can_frame_t;
/* Force the BTS7010 IN path to GPIO low before PWM initialization. */
bool BspSeatBackrestHeater_ForceOff(void);
/* Configure TCPWM1 counter 65 while P16.7 remains a strong GPIO low. */
bool BspSeatBackrestHeater_InitPwm(void);
/* Start a non-zero PWM duty, then connect P16.7 to TCPWM line 65. */
bool BspSeatBackrestHeater_Start(uint32_t dutyPercent);
/* Change a running non-zero duty. Use FailSafeOff for a true 0% output. */
bool BspSeatBackrestHeater_SetDutyPercent(uint32_t dutyPercent);
/* Disable TCPWM and restore P16.7 to strong GPIO low. */
void BspSeatBackrestHeater_FailSafeOff(void);
bool BspSeatBackrestHeater_IsRunning(void);
uint32_t BspSeatBackrestHeater_GetDutyPercent(void);
/* Initialize the proven CAN4/TJA1044 board interface at 500 kbit/s. */
bool BspSeatBackrestHeaterCan_Init(void);
/* Pop one received standard Classic CAN frame from the interrupt-safe queue. */
bool BspSeatBackrestHeaterCan_Read(
bsp_seat_backrest_heater_can_frame_t *frame);
/* Send one 8-byte standard Classic CAN frame. */
bool BspSeatBackrestHeaterCan_Send(uint32_t identifier,
uint32_t data0,
uint32_t data1);
/* Watch-window diagnostics for CAN hardware integration. */
extern volatile uint32_t g_bspSeatBackrestHeaterCanInitStatus;
extern volatile uint32_t g_bspSeatBackrestHeaterCanRxCount;
extern volatile uint32_t g_bspSeatBackrestHeaterCanRxQueueOverflowCount;
extern volatile uint32_t g_bspSeatBackrestHeaterCanTxCount;
extern volatile uint32_t g_bspSeatBackrestHeaterCanTxBusyCount;
extern volatile uint32_t g_bspSeatBackrestHeaterCanLastRxId;
extern volatile uint32_t g_bspSeatBackrestHeaterCanLastTxId;
extern volatile uint32_t g_bspSeatBackrestHeaterCanLastTxStatus;
#endif /* BSP_SEAT_BACKREST_HEATER_H */
@@ -0,0 +1,259 @@
#include "bsp_seat_backrest_heater_adc.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "seat_backrest_heater_config.h"
#define HEATER_ADC_OPERATION_FREQ_MAX_HZ (13340000u)
#define HEATER_ADC_SAMPLE_TIME_MIN_NS (600ull)
#define HEATER_ADC_POLL_TIMEOUT (100000u)
#define HEATER_ADC_DIV_ROUND_UP(a, b) (((a) + (b) - 1u) / (b))
static bool BspSeatBackrestHeaterAdc_ConfigClock(uint32_t *actualClockHz);
static bool BspSeatBackrestHeaterAdc_WaitDone(void);
static void BspSeatBackrestHeaterAdc_ClearStatus(void);
static bool s_adcInitialized = false;
static uint16_t s_heaterSenseRaw = 0u;
bool BspSeatBackrestHeaterAdc_Init(void)
{
#if (SEAT_BACKREST_HEATER_SENSE_ENABLE == 1u)
uint32_t actualClockHz;
uint32_t samplingCycles;
cy_stc_gpio_pin_config_t pinConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_ANALOG,
.hsiom = SEAT_BACKREST_HEATER_SENSE_GPIO_HSIOM,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_adc_config_t adcConfig =
{
.preconditionTime = 0u,
.powerupTime = 0u,
.enableIdlePowerDown = false,
.msbStretchMode = CY_ADC_MSB_STRETCH_MODE_1CYCLE,
.enableHalfLsbConv = false,
.sarMuxEnable = true,
.adcEnable = true,
.sarIpEnable = true,
};
cy_stc_adc_channel_config_t channelConfig =
{
.triggerSelection = CY_ADC_TRIGGER_OFF,
.channelPriority = 0u,
.preenptionType = CY_ADC_PREEMPTION_FINISH_RESUME,
.isGroupEnd = true,
.doneLevel = CY_ADC_DONE_LEVEL_PULSE,
.pinAddress = SEAT_BACKREST_HEATER_SENSE_PIN_ADDRESS,
.portAddress = SEAT_BACKREST_HEATER_SENSE_PORT_ADDRESS,
.extMuxSelect = 0u,
.extMuxEnable = true,
.preconditionMode = CY_ADC_PRECONDITION_MODE_OFF,
.overlapDiagMode = CY_ADC_OVERLAP_DIAG_MODE_OFF,
.sampleTime = 0u,
.calibrationValueSelect = CY_ADC_CALIBRATION_VALUE_REGULAR,
.postProcessingMode = CY_ADC_POST_PROCESSING_MODE_NONE,
.resultAlignment = CY_ADC_RESULT_ALIGNMENT_RIGHT,
.signExtention = CY_ADC_SIGN_EXTENTION_UNSIGNED,
.averageCount = 0u,
.rightShift = 0u,
.rangeDetectionMode = CY_ADC_RANGE_DETECTION_MODE_INSIDE_RANGE,
.rangeDetectionLoThreshold = 0u,
.rangeDetectionHiThreshold = SEAT_BACKREST_HEATER_ADC_FULL_SCALE,
.mask.grpDone = true,
.mask.grpCancelled = false,
.mask.grpOverflow = false,
.mask.chRange = false,
.mask.chPulse = false,
.mask.chOverflow = false,
};
if(Cy_GPIO_Pin_Init(SEAT_BACKREST_HEATER_SENSE_PORT,
SEAT_BACKREST_HEATER_SENSE_PIN,
&pinConfig) != CY_GPIO_SUCCESS)
{
return false;
}
if(!BspSeatBackrestHeaterAdc_ConfigClock(&actualClockHz))
{
return false;
}
samplingCycles = (uint32_t)HEATER_ADC_DIV_ROUND_UP(
(HEATER_ADC_SAMPLE_TIME_MIN_NS * (uint64_t)actualClockHz),
1000000000ull);
if(samplingCycles == 0u)
{
samplingCycles = 1u;
}
channelConfig.sampleTime = (uint16_t)samplingCycles;
Cy_Adc_DeInit(SEAT_BACKREST_HEATER_SENSE_ADC);
if(Cy_Adc_Init(SEAT_BACKREST_HEATER_SENSE_ADC,
&adcConfig) != CY_ADC_SUCCESS)
{
return false;
}
if(Cy_Adc_Channel_Init(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL,
&channelConfig) != CY_ADC_SUCCESS)
{
return false;
}
Cy_Adc_Channel_Enable(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL);
s_adcInitialized = true;
return BspSeatBackrestHeaterAdc_Sample();
#else
s_adcInitialized = true;
return true;
#endif
}
bool BspSeatBackrestHeaterAdc_Sample(void)
{
#if (SEAT_BACKREST_HEATER_SENSE_ENABLE == 1u)
uint16_t raw = 0u;
cy_stc_adc_ch_status_t resultStatus = { false };
if(!s_adcInitialized)
{
return false;
}
BspSeatBackrestHeaterAdc_ClearStatus();
Cy_Adc_Channel_SoftwareTrigger(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL);
if(!BspSeatBackrestHeaterAdc_WaitDone())
{
return false;
}
if(Cy_Adc_Channel_GetResult(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL,
&raw,
&resultStatus) != CY_ADC_SUCCESS)
{
return false;
}
if(!resultStatus.valid)
{
return false;
}
s_heaterSenseRaw =
(uint16_t)(raw & SEAT_BACKREST_HEATER_ADC_FULL_SCALE);
BspSeatBackrestHeaterAdc_ClearStatus();
return true;
#else
return true;
#endif
}
uint16_t BspSeatBackrestHeaterAdc_GetRaw(void)
{
return s_heaterSenseRaw;
}
uint32_t BspSeatBackrestHeaterAdc_GetEstimatedCurrentMilliamp(void)
{
uint64_t numerator =
(uint64_t)s_heaterSenseRaw *
(uint64_t)SEAT_BACKREST_HEATER_ADC_REFERENCE_MV *
(uint64_t)SEAT_BACKREST_HEATER_SENSE_KILIS;
uint64_t denominator =
(uint64_t)SEAT_BACKREST_HEATER_ADC_FULL_SCALE *
(uint64_t)SEAT_BACKREST_HEATER_SENSE_RESISTOR_OHM;
return (uint32_t)(numerator / denominator);
}
static bool BspSeatBackrestHeaterAdc_ConfigClock(uint32_t *actualClockHz)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup =
Cy_SysClk_GetClockGroup(SEAT_BACKREST_HEATER_SENSE_PCLK);
if(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS)
{
return false;
}
divider = HEATER_ADC_DIV_ROUND_UP(
periClockHz, HEATER_ADC_OPERATION_FREQ_MAX_HZ);
if((divider == 0u) || (divider > 65536u))
{
return false;
}
if(Cy_SysClk_PeriphDisableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(
SEAT_BACKREST_HEATER_SENSE_PCLK,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphEnableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
*actualClockHz = periClockHz / divider;
return true;
}
static bool BspSeatBackrestHeaterAdc_WaitDone(void)
{
uint32_t timeout = HEATER_ADC_POLL_TIMEOUT;
cy_stc_adc_interrupt_source_t status = { false };
do
{
(void)Cy_Adc_Channel_GetInterruptStatus(
SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL, &status);
if(status.grpDone)
{
return true;
}
timeout--;
} while(timeout > 0u);
return false;
}
static void BspSeatBackrestHeaterAdc_ClearStatus(void)
{
cy_stc_adc_interrupt_source_t clearSource =
{
.grpDone = true,
.grpCancelled = true,
.grpOverflow = true,
.chRange = false,
.chPulse = false,
.chOverflow = true,
};
(void)Cy_Adc_Channel_ClearInterruptStatus(
SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL, &clearSource);
}
@@ -0,0 +1,12 @@
#ifndef BSP_SEAT_BACKREST_HEATER_ADC_H
#define BSP_SEAT_BACKREST_HEATER_ADC_H
#include <stdbool.h>
#include <stdint.h>
bool BspSeatBackrestHeaterAdc_Init(void);
bool BspSeatBackrestHeaterAdc_Sample(void);
uint16_t BspSeatBackrestHeaterAdc_GetRaw(void);
uint32_t BspSeatBackrestHeaterAdc_GetEstimatedCurrentMilliamp(void);
#endif /* BSP_SEAT_BACKREST_HEATER_ADC_H */
+653
View File
@@ -0,0 +1,653 @@
#include "bsp_seat_fan.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "seat_fan_config.h"
static bool BspSeatFan_ConfigClock(void);
static uint32_t BspSeatFan_DutyToCompare(uint32_t dutyPercent);
static void BspSeatFan_SetPinGpioLow(volatile stc_GPIO_PRT_t *port,
uint32_t pin,
en_hsiom_sel_t gpioHsiom);
static bool BspSeatCan_ConfigClock(void);
static bool BspSeatCan_ConfigPins(void);
static void BspSeatCan_IrqHandler(void);
static void BspSeatCan_RxCallback(bool rxFifoMessage,
uint8_t rxBufferOrFifo,
cy_stc_canfd_msg_t *message);
volatile uint32_t g_bspSeatCanInitStatus = (uint32_t)CY_CANFD_SUCCESS;
volatile uint32_t g_bspSeatCanRxCount = 0u;
volatile uint32_t g_bspSeatCanRxQueueOverflowCount = 0u;
volatile uint32_t g_bspSeatCanTxCount = 0u;
volatile uint32_t g_bspSeatCanTxBusyCount = 0u;
volatile uint32_t g_bspSeatCanLastRxId = 0u;
volatile uint32_t g_bspSeatCanLastTxId = 0u;
volatile uint32_t g_bspSeatCanLastTxStatus = (uint32_t)CY_CANFD_SUCCESS;
static volatile bsp_seat_can_frame_t s_canRxQueue[SEAT_FAN_CAN_RX_QUEUE_DEPTH];
static volatile uint8_t s_canRxQueueHead = 0u;
static volatile uint8_t s_canRxQueueTail = 0u;
static const cy_stc_id_filter_t s_canStandardIdFilters[] =
{
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_FAN_CAN_CONTROL_29E_ID,
SEAT_FAN_CAN_CONTROL_29E_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_FAN_CAN_CONFIG_2AA_ID,
SEAT_FAN_CAN_CONFIG_2AA_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_FAN_CAN_SWITCH_42A_ID,
SEAT_FAN_CAN_SWITCH_42A_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
};
static cy_stc_sysint_irq_t s_canIrqConfig =
{
.sysIntSrc = SEAT_FAN_CAN_IRQN,
.intIdx = SEAT_FAN_CAN_CPU_IRQ,
.isEnabled = true,
};
static cy_stc_canfd_config_t s_canConfig =
{
.txCallback = NULL,
.rxCallback = BspSeatCan_RxCallback,
.rxFifoWithTopCallback = NULL,
.statusCallback = NULL,
.errorCallback = NULL,
.canFDMode = false,
.bitrate =
{
/* 40 MHz / (10 * (1 + 5 + 2)) = 500 kbit/s. */
.prescaler = 10u - 1u,
.timeSegment1 = 5u - 1u,
.timeSegment2 = 2u - 1u,
.syncJumpWidth = 2u - 1u,
},
.fastBitrate =
{
.prescaler = 0u,
.timeSegment1 = 0u,
.timeSegment2 = 0u,
.syncJumpWidth = 0u,
},
.tdcConfig =
{
.tdcEnabled = false,
.tdcOffset = 0u,
.tdcFilterWindow = 0u,
},
.sidFilterConfig =
{
.numberOfSIDFilters =
sizeof(s_canStandardIdFilters) / sizeof(s_canStandardIdFilters[0]),
.sidFilter = s_canStandardIdFilters,
},
.extidFilterConfig =
{
.numberOfEXTIDFilters = 0u,
.extidFilter = NULL,
.extIDANDMask = 0x1FFFFFFFu,
},
.globalFilterConfig =
{
.nonMatchingFramesStandard = CY_CANFD_REJECT_NON_MATCHING,
.nonMatchingFramesExtended = CY_CANFD_REJECT_NON_MATCHING,
.rejectRemoteFramesStandard = true,
.rejectRemoteFramesExtended = true,
},
.rxBufferDataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo1DataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo0DataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.txBufferDataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo0Config =
{
.mode = CY_CANFD_FIFO_MODE_BLOCKING,
.watermark = 0u,
.numberOfFifoElements = SEAT_FAN_CAN_RX_FIFO0_ELEMENTS,
.topPointerLogicEnabled = false,
},
.rxFifo1Config =
{
.mode = CY_CANFD_FIFO_MODE_BLOCKING,
.watermark = 0u,
.numberOfFifoElements = 0u,
.topPointerLogicEnabled = false,
},
.noOfRxBuffers = 0u,
.noOfTxBuffers = SEAT_FAN_CAN_TX_BUFFER_COUNT,
};
static const cy_stc_tcpwm_pwm_config_t s_seatFanPwmConfig =
{
.pwmMode = CY_TCPWM_PWM_MODE_PWM,
.clockPrescaler = CY_TCPWM_PRESCALER_DIVBY_1,
.debug_pause = false,
.deadTime = 0u,
.deadTimeComp = 0u,
.runMode = CY_TCPWM_PWM_CONTINUOUS,
.countDirection = CY_TCPWM_COUNTER_COUNT_UP,
.cc0MatchMode = CY_TCPWM_PWM_TR_CTRL2_CLEAR,
.overflowMode = CY_TCPWM_PWM_TR_CTRL2_SET,
.underflowMode = CY_TCPWM_PWM_TR_CTRL2_NO_CHANGE,
.cc1MatchMode = CY_TCPWM_PWM_TR_CTRL2_NO_CHANGE,
.period = SEAT_FAN_PWM_PERIOD_TICKS - 1u,
.period_buff = 0u,
.enablePeriodSwap = false,
.enableLineSelSwap = false,
.compare0 = SEAT_FAN_PWM_PERIOD_TICKS,
.compare0_buff = 0u,
.enableCompare0Swap = false,
.compare1 = 0u,
.compare1_buff = 0u,
.enableCompare1Swap = false,
.interruptSources = CY_TCPWM_INT_NONE,
.invertPWMOut = 0u,
.invertPWMOutN = 0u,
.killMode = CY_TCPWM_PWM_STOP_ON_KILL,
.switchInputMode = CY_TCPWM_INPUT_LEVEL,
.switchInput = 0u,
.reloadInputMode = CY_TCPWM_INPUT_LEVEL,
.reloadInput = 0u,
.startInputMode = CY_TCPWM_INPUT_LEVEL,
.startInput = 0u,
.kill0InputMode = CY_TCPWM_INPUT_LEVEL,
.kill0Input = 0u,
.kill1InputMode = CY_TCPWM_INPUT_LEVEL,
.kill1Input = 0u,
.countInputMode = CY_TCPWM_INPUT_LEVEL,
.countInput = 1u,
.immediateKill = true,
.pwmOnDisable = CY_TCPWM_PWM_OUT_MODE_LOW,
.trigger0EventCfg = CY_TCPWM_COUNTER_OVERFLOW,
.trigger1EventCfg = CY_TCPWM_COUNTER_OVERFLOW,
};
bool BspSeatFan_ForceOff(void)
{
cy_stc_gpio_pin_config_t pinConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_STRONG_IN_OFF,
.hsiom = SEAT_FAN_PWM_GPIO_HSIOM,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = CY_GPIO_DRIVE_FULL,
};
cy_en_gpio_status_t pwmPinStatus;
cy_en_gpio_status_t unusedPinStatus;
pwmPinStatus = Cy_GPIO_Pin_Init(SEAT_FAN_PWM_PORT,
SEAT_FAN_PWM_PIN,
&pinConfig);
pinConfig.hsiom = SEAT_FAN_UNUSED_GPIO_HSIOM;
unusedPinStatus = Cy_GPIO_Pin_Init(SEAT_FAN_UNUSED_PORT,
SEAT_FAN_UNUSED_PIN,
&pinConfig);
/* Write the output latches again after configuration for an explicit safe state. */
Cy_GPIO_Write(SEAT_FAN_PWM_PORT, SEAT_FAN_PWM_PIN, 0u);
Cy_GPIO_Write(SEAT_FAN_UNUSED_PORT, SEAT_FAN_UNUSED_PIN, 0u);
return ((pwmPinStatus == CY_GPIO_SUCCESS) &&
(unusedPinStatus == CY_GPIO_SUCCESS));
}
bool BspSeatFan_InitPwm(void)
{
if(!BspSeatFan_ConfigClock())
{
return false;
}
Cy_Tcpwm_Pwm_Disable(SEAT_FAN_PWM_TCPWM);
if(Cy_Tcpwm_Pwm_Init(SEAT_FAN_PWM_TCPWM,
&s_seatFanPwmConfig) != CY_RET_SUCCESS)
{
return false;
}
return ((Cy_Tcpwm_Pwm_GetPeriod(SEAT_FAN_PWM_TCPWM) ==
(SEAT_FAN_PWM_PERIOD_TICKS - 1u)) &&
(Cy_Tcpwm_Pwm_GetCompare0(SEAT_FAN_PWM_TCPWM) ==
SEAT_FAN_PWM_PERIOD_TICKS));
}
bool BspSeatFan_Start(uint32_t dutyPercent)
{
if(!BspSeatFan_SetDutyPercent(dutyPercent))
{
return false;
}
/*
* P11.1 is still GPIO low here. Starting from PERIOD makes the first
* counter clock generate overflow immediately, which asserts LINE high
* for the 100% startup setting before the pin is switched to TCPWM.
*/
Cy_GPIO_Write(SEAT_FAN_PWM_PORT, SEAT_FAN_PWM_PIN, 0u);
Cy_Tcpwm_Pwm_SetCounter(SEAT_FAN_PWM_TCPWM,
SEAT_FAN_PWM_PERIOD_TICKS - 1u);
Cy_Tcpwm_Pwm_Enable(SEAT_FAN_PWM_TCPWM);
Cy_Tcpwm_TriggerStart(SEAT_FAN_PWM_TCPWM);
/* Keep the pin at GPIO low until the 2 MHz counter has started and overflowed. */
Cy_SysTick_DelayInUs(2u);
if((Cy_Tcpwm_Pwm_GetStatus(SEAT_FAN_PWM_TCPWM) &
TCPWM_GRP_CNT_STATUS_RUNNING_Msk) == 0u)
{
BspSeatFan_FailSafeOff();
return false;
}
Cy_GPIO_SetDriveSel(SEAT_FAN_PWM_PORT,
SEAT_FAN_PWM_PIN,
CY_GPIO_DRIVE_FULL);
Cy_GPIO_SetDrivemode(SEAT_FAN_PWM_PORT,
SEAT_FAN_PWM_PIN,
CY_GPIO_DM_STRONG_IN_OFF);
Cy_GPIO_SetHSIOM(SEAT_FAN_PWM_PORT,
SEAT_FAN_PWM_PIN,
SEAT_FAN_PWM_HSIOM);
if(Cy_GPIO_GetHSIOM(SEAT_FAN_PWM_PORT,
SEAT_FAN_PWM_PIN) != SEAT_FAN_PWM_HSIOM)
{
BspSeatFan_FailSafeOff();
return false;
}
return true;
}
bool BspSeatFan_SetDutyPercent(uint32_t dutyPercent)
{
uint32_t compare;
if(dutyPercent > 100u)
{
return false;
}
compare = BspSeatFan_DutyToCompare(dutyPercent);
Cy_Tcpwm_Pwm_SetCompare0(SEAT_FAN_PWM_TCPWM, compare);
return (Cy_Tcpwm_Pwm_GetCompare0(SEAT_FAN_PWM_TCPWM) == compare);
}
void BspSeatFan_FailSafeOff(void)
{
Cy_Tcpwm_Pwm_Disable(SEAT_FAN_PWM_TCPWM);
BspSeatFan_SetPinGpioLow(SEAT_FAN_PWM_PORT,
SEAT_FAN_PWM_PIN,
SEAT_FAN_PWM_GPIO_HSIOM);
BspSeatFan_SetPinGpioLow(SEAT_FAN_UNUSED_PORT,
SEAT_FAN_UNUSED_PIN,
SEAT_FAN_UNUSED_GPIO_HSIOM);
}
bool BspSeatCan_Init(void)
{
cy_en_canfd_status_t canStatus;
if(!BspSeatCan_ConfigClock() || !BspSeatCan_ConfigPins())
{
g_bspSeatCanInitStatus = (uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
if(Cy_SysInt_InitIRQ(&s_canIrqConfig) != CY_SYSINT_SUCCESS)
{
g_bspSeatCanInitStatus = (uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
Cy_SysInt_SetSystemIrqVector(s_canIrqConfig.sysIntSrc,
BspSeatCan_IrqHandler);
NVIC_SetPriority(s_canIrqConfig.intIdx, 0u);
NVIC_ClearPendingIRQ(s_canIrqConfig.intIdx);
(void)Cy_CANFD_DeInit(SEAT_FAN_CAN_TYPE);
canStatus = Cy_CANFD_Init(SEAT_FAN_CAN_TYPE, &s_canConfig);
g_bspSeatCanInitStatus = (uint32_t)canStatus;
if(canStatus != CY_CANFD_SUCCESS)
{
return false;
}
NVIC_EnableIRQ(s_canIrqConfig.intIdx);
return true;
}
bool BspSeatCan_Read(bsp_seat_can_frame_t *frame)
{
uint32_t interruptState;
uint8_t tail;
if(frame == NULL)
{
return false;
}
interruptState = Cy_SysLib_EnterCriticalSection();
tail = s_canRxQueueTail;
if(tail == s_canRxQueueHead)
{
Cy_SysLib_ExitCriticalSection(interruptState);
return false;
}
frame->identifier = s_canRxQueue[tail].identifier;
frame->data0 = s_canRxQueue[tail].data0;
frame->data1 = s_canRxQueue[tail].data1;
frame->dlc = s_canRxQueue[tail].dlc;
tail++;
if(tail >= SEAT_FAN_CAN_RX_QUEUE_DEPTH)
{
tail = 0u;
}
s_canRxQueueTail = tail;
Cy_SysLib_ExitCriticalSection(interruptState);
return true;
}
bool BspSeatCan_Send(uint32_t identifier, uint32_t data0, uint32_t data1)
{
cy_stc_canfd_msg_t message;
uint8_t bufferIndex;
uint8_t dataIndex;
if(identifier > 0x7FFu)
{
g_bspSeatCanLastTxStatus = (uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
message.canFDFormat = false;
message.idConfig.extended = false;
message.idConfig.identifier = identifier;
message.dataConfig.dataLengthCode = SEAT_FAN_CAN_DLC;
message.dataConfig.data[0] = data0;
message.dataConfig.data[1] = data1;
for(dataIndex = 2u;
dataIndex < CY_CANFD_MESSAGE_DATA_BUFFER_SIZE;
dataIndex++)
{
message.dataConfig.data[dataIndex] = 0u;
}
for(bufferIndex = 0u;
bufferIndex < SEAT_FAN_CAN_TX_BUFFER_COUNT;
bufferIndex++)
{
cy_en_canfd_tx_buffer_status_t bufferStatus =
Cy_CANFD_GetTxBufferStatus(SEAT_FAN_CAN_TYPE, bufferIndex);
if((bufferStatus != CY_CANFD_TX_BUFFER_PENDING) &&
(bufferStatus != CY_CANFD_TX_BUFFER_CANCEL_REQUESTED))
{
cy_en_canfd_status_t sendStatus;
g_bspSeatCanLastTxId = identifier;
sendStatus = Cy_CANFD_UpdateAndTransmitMsgBuffer(
SEAT_FAN_CAN_TYPE,
bufferIndex,
&message);
g_bspSeatCanLastTxStatus = (uint32_t)sendStatus;
if(sendStatus == CY_CANFD_SUCCESS)
{
g_bspSeatCanTxCount++;
return true;
}
return false;
}
}
g_bspSeatCanTxBusyCount++;
return false;
}
static bool BspSeatCan_ConfigClock(void)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup;
if(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS)
{
return false;
}
if((periClockHz < SEAT_FAN_CAN_CLOCK_HZ) ||
((periClockHz % SEAT_FAN_CAN_CLOCK_HZ) != 0u))
{
return false;
}
divider = periClockHz / SEAT_FAN_CAN_CLOCK_HZ;
if((divider == 0u) || (divider > 256u))
{
return false;
}
clockGroup = Cy_SysClk_GetClockGroup(SEAT_FAN_CAN_PCLK);
if(Cy_SysClk_PeriphDisableDivider(clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_FAN_CAN_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(SEAT_FAN_CAN_PCLK,
CY_SYSCLK_DIV_8_BIT,
SEAT_FAN_CAN_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_FAN_CAN_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
return (Cy_SysClk_PeriphEnableDivider(clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_FAN_CAN_DIVIDER_NUMBER) ==
CY_SYSCLK_SUCCESS);
}
static bool BspSeatCan_ConfigPins(void)
{
cy_stc_gpio_pin_config_t rxConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_HIGHZ,
.hsiom = SEAT_FAN_CAN_RX_MUX,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_gpio_pin_config_t txConfig =
{
.outVal = 1u,
.driveMode = CY_GPIO_DM_STRONG,
.hsiom = SEAT_FAN_CAN_TX_MUX,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_gpio_pin_config_t transceiverConfig =
{
.outVal = SEAT_FAN_CAN_TRANSCEIVER_ENABLE,
.driveMode = CY_GPIO_DM_STRONG_IN_OFF,
.hsiom = SEAT_FAN_CAN_TRANSCEIVER_GPIO_HSIOM,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = CY_GPIO_DRIVE_FULL,
};
if(Cy_GPIO_Pin_Init(SEAT_FAN_CAN_RX_PORT,
SEAT_FAN_CAN_RX_PIN,
&rxConfig) != CY_GPIO_SUCCESS)
{
return false;
}
if(Cy_GPIO_Pin_Init(SEAT_FAN_CAN_TX_PORT,
SEAT_FAN_CAN_TX_PIN,
&txConfig) != CY_GPIO_SUCCESS)
{
return false;
}
if(Cy_GPIO_Pin_Init(SEAT_FAN_CAN_TRANSCEIVER_PORT,
SEAT_FAN_CAN_TRANSCEIVER_PIN,
&transceiverConfig) != CY_GPIO_SUCCESS)
{
return false;
}
Cy_GPIO_Write(SEAT_FAN_CAN_TRANSCEIVER_PORT,
SEAT_FAN_CAN_TRANSCEIVER_PIN,
SEAT_FAN_CAN_TRANSCEIVER_ENABLE);
Cy_SysTick_DelayInUs(1000u);
return (Cy_GPIO_ReadOut(SEAT_FAN_CAN_TRANSCEIVER_PORT,
SEAT_FAN_CAN_TRANSCEIVER_PIN) ==
SEAT_FAN_CAN_TRANSCEIVER_ENABLE);
}
static void BspSeatCan_IrqHandler(void)
{
Cy_CANFD_IrqHandler(SEAT_FAN_CAN_TYPE);
}
static void BspSeatCan_RxCallback(bool rxFifoMessage,
uint8_t rxBufferOrFifo,
cy_stc_canfd_msg_t *message)
{
uint8_t head;
uint8_t nextHead;
(void)rxFifoMessage;
(void)rxBufferOrFifo;
if((message == NULL) || message->idConfig.extended || message->canFDFormat)
{
return;
}
g_bspSeatCanRxCount++;
g_bspSeatCanLastRxId = message->idConfig.identifier;
head = s_canRxQueueHead;
nextHead = head + 1u;
if(nextHead >= SEAT_FAN_CAN_RX_QUEUE_DEPTH)
{
nextHead = 0u;
}
if(nextHead == s_canRxQueueTail)
{
g_bspSeatCanRxQueueOverflowCount++;
return;
}
s_canRxQueue[head].identifier = message->idConfig.identifier;
s_canRxQueue[head].dlc = message->dataConfig.dataLengthCode;
s_canRxQueue[head].data0 = message->dataConfig.data[0];
s_canRxQueue[head].data1 = message->dataConfig.data[1];
s_canRxQueueHead = nextHead;
}
static bool BspSeatFan_ConfigClock(void)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup;
if(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS)
{
return false;
}
if((periClockHz < SEAT_FAN_COUNTER_CLOCK_HZ) ||
((periClockHz % SEAT_FAN_COUNTER_CLOCK_HZ) != 0u))
{
return false;
}
divider = periClockHz / SEAT_FAN_COUNTER_CLOCK_HZ;
if((divider == 0u) || (divider > 65536u))
{
return false;
}
clockGroup = Cy_SysClk_GetClockGroup(SEAT_FAN_PWM_PCLK);
if(Cy_SysClk_PeriphDisableDivider(clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_FAN_PWM_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(SEAT_FAN_PWM_PCLK,
CY_SYSCLK_DIV_16_BIT,
SEAT_FAN_PWM_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_FAN_PWM_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
return (Cy_SysClk_PeriphEnableDivider(clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_FAN_PWM_DIVIDER_NUMBER) ==
CY_SYSCLK_SUCCESS);
}
static uint32_t BspSeatFan_DutyToCompare(uint32_t dutyPercent)
{
return (SEAT_FAN_PWM_PERIOD_TICKS * dutyPercent) / 100u;
}
static void BspSeatFan_SetPinGpioLow(volatile stc_GPIO_PRT_t *port,
uint32_t pin,
en_hsiom_sel_t gpioHsiom)
{
/* Set the latch low before changing HSIOM to avoid a transient high. */
Cy_GPIO_Write(port, pin, 0u);
Cy_GPIO_SetHSIOM(port, pin, gpioHsiom);
Cy_GPIO_SetDriveSel(port, pin, CY_GPIO_DRIVE_FULL);
Cy_GPIO_SetDrivemode(port, pin, CY_GPIO_DM_STRONG_IN_OFF);
Cy_GPIO_Write(port, pin, 0u);
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef BSP_SEAT_FAN_H
#define BSP_SEAT_FAN_H
#include <stdbool.h>
#include <stdint.h>
typedef struct
{
uint32_t identifier;
uint32_t data0;
uint32_t data1;
uint8_t dlc;
} bsp_seat_can_frame_t;
/* Configure P11.1 and P11.2 as strong GPIO outputs at logic low. */
bool BspSeatFan_ForceOff(void);
/* Configure the peripheral clock and PWM0-60 while P11.1 remains GPIO low. */
bool BspSeatFan_InitPwm(void);
/* Start PWM and only then connect P11.1 to the TCPWM line output. */
bool BspSeatFan_Start(uint32_t dutyPercent);
/* Change the active PWM compare value. Valid range is 0 through 100 percent. */
bool BspSeatFan_SetDutyPercent(uint32_t dutyPercent);
/* Disable TCPWM and restore both known fan-control pins to GPIO low. */
void BspSeatFan_FailSafeOff(void);
/* Initialize the proven CAN4/TJA1044 board interface at 500 kbit/s. */
bool BspSeatCan_Init(void);
/* Pop one received standard Classic CAN frame from the interrupt-safe queue. */
bool BspSeatCan_Read(bsp_seat_can_frame_t *frame);
/* Send one 8-byte standard Classic CAN frame. */
bool BspSeatCan_Send(uint32_t identifier, uint32_t data0, uint32_t data1);
/* Watch-window diagnostics for hardware integration. */
extern volatile uint32_t g_bspSeatCanInitStatus;
extern volatile uint32_t g_bspSeatCanRxCount;
extern volatile uint32_t g_bspSeatCanRxQueueOverflowCount;
extern volatile uint32_t g_bspSeatCanTxCount;
extern volatile uint32_t g_bspSeatCanTxBusyCount;
extern volatile uint32_t g_bspSeatCanLastRxId;
extern volatile uint32_t g_bspSeatCanLastTxId;
extern volatile uint32_t g_bspSeatCanLastTxStatus;
#endif /* BSP_SEAT_FAN_H */
+410
View File
@@ -0,0 +1,410 @@
#include "bsp_seat_move_can.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "seat_move_config.h"
static bool BspSeatMoveCan_ConfigClock(void);
static bool BspSeatMoveCan_ConfigPins(void);
static void BspSeatMoveCan_IrqHandler(void);
static void BspSeatMoveCan_RxCallback(bool rxFifoMessage,
uint8_t rxBufferOrFifo,
cy_stc_canfd_msg_t *message);
static void BspSeatMoveCan_ErrorCallback(cy_en_canfd_bus_error_t error);
volatile uint32_t g_bspSeatMoveCanInitStatus = (uint32_t)CY_CANFD_SUCCESS;
volatile uint32_t g_bspSeatMoveCanRxCount = 0u;
volatile uint32_t g_bspSeatMoveCanRxQueueOverflowCount = 0u;
volatile uint32_t g_bspSeatMoveCanTxCount = 0u;
volatile uint32_t g_bspSeatMoveCanTxBusyCount = 0u;
volatile uint32_t g_bspSeatMoveCanLastRxId = 0u;
volatile uint32_t g_bspSeatMoveCanLastTxId = 0u;
volatile uint32_t g_bspSeatMoveCanLastTxStatus = (uint32_t)CY_CANFD_SUCCESS;
volatile uint32_t g_bspSeatMoveCanBusOff = 0u;
static volatile bsp_seat_move_can_frame_t
s_rxQueue[SEAT_MOVE_CAN_RX_QUEUE_DEPTH];
static volatile uint8_t s_rxQueueHead = 0u;
static volatile uint8_t s_rxQueueTail = 0u;
static const cy_stc_id_filter_t s_standardIdFilters[] =
{
CANFD_CONFIG_STD_ID_FILTER_RANGE(
0x29Eu,
0x29Eu,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
0x2AAu,
0x2AAu,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
0x3CEu,
0x3CEu,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
0x42Au,
0x42Au,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_MOVE_CAN_COMMAND_ID,
SEAT_MOVE_CAN_COMMAND_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_CONTROLLER_IDENTITY_REQUEST_ID,
SEAT_CONTROLLER_IDENTITY_REQUEST_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
};
static cy_stc_sysint_irq_t s_canIrqConfig =
{
.sysIntSrc = SEAT_MOVE_CAN_IRQN,
.intIdx = SEAT_MOVE_CAN_CPU_IRQ,
.isEnabled = true,
};
static cy_stc_canfd_config_t s_canConfig =
{
.txCallback = NULL,
.rxCallback = BspSeatMoveCan_RxCallback,
.rxFifoWithTopCallback = NULL,
.statusCallback = NULL,
.errorCallback = BspSeatMoveCan_ErrorCallback,
.canFDMode = false,
.bitrate =
{
/* 40 MHz / (10 * (1 + 5 + 2)) = 500 kbit/s. */
.prescaler = 10u - 1u,
.timeSegment1 = 5u - 1u,
.timeSegment2 = 2u - 1u,
.syncJumpWidth = 2u - 1u,
},
.fastBitrate =
{
.prescaler = 0u,
.timeSegment1 = 0u,
.timeSegment2 = 0u,
.syncJumpWidth = 0u,
},
.tdcConfig =
{
.tdcEnabled = false,
.tdcOffset = 0u,
.tdcFilterWindow = 0u,
},
.sidFilterConfig =
{
.numberOfSIDFilters =
sizeof(s_standardIdFilters) / sizeof(s_standardIdFilters[0]),
.sidFilter = s_standardIdFilters,
},
.extidFilterConfig =
{
.numberOfEXTIDFilters = 0u,
.extidFilter = NULL,
.extIDANDMask = 0x1FFFFFFFu,
},
.globalFilterConfig =
{
.nonMatchingFramesStandard = CY_CANFD_REJECT_NON_MATCHING,
.nonMatchingFramesExtended = CY_CANFD_REJECT_NON_MATCHING,
.rejectRemoteFramesStandard = true,
.rejectRemoteFramesExtended = true,
},
.rxBufferDataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo1DataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo0DataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.txBufferDataSize = CY_CANFD_BUFFER_DATA_SIZE_8,
.rxFifo0Config =
{
.mode = CY_CANFD_FIFO_MODE_BLOCKING,
.watermark = 0u,
.numberOfFifoElements = SEAT_MOVE_CAN_RX_FIFO0_ELEMENTS,
.topPointerLogicEnabled = false,
},
.rxFifo1Config =
{
.mode = CY_CANFD_FIFO_MODE_BLOCKING,
.watermark = 0u,
.numberOfFifoElements = 0u,
.topPointerLogicEnabled = false,
},
.noOfRxBuffers = 0u,
.noOfTxBuffers = SEAT_MOVE_CAN_TX_BUFFER_COUNT,
};
bool BspSeatMoveCan_Init(void)
{
cy_en_canfd_status_t canStatus;
s_rxQueueHead = 0u;
s_rxQueueTail = 0u;
g_bspSeatMoveCanBusOff = 0u;
if(!BspSeatMoveCan_ConfigClock() || !BspSeatMoveCan_ConfigPins())
{
g_bspSeatMoveCanInitStatus = (uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
if(Cy_SysInt_InitIRQ(&s_canIrqConfig) != CY_SYSINT_SUCCESS)
{
g_bspSeatMoveCanInitStatus = (uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
Cy_SysInt_SetSystemIrqVector(s_canIrqConfig.sysIntSrc,
BspSeatMoveCan_IrqHandler);
NVIC_SetPriority(s_canIrqConfig.intIdx, 0u);
NVIC_ClearPendingIRQ(s_canIrqConfig.intIdx);
(void)Cy_CANFD_DeInit(SEAT_MOVE_CAN_TYPE);
canStatus = Cy_CANFD_Init(SEAT_MOVE_CAN_TYPE, &s_canConfig);
g_bspSeatMoveCanInitStatus = (uint32_t)canStatus;
if(canStatus != CY_CANFD_SUCCESS)
{
return false;
}
NVIC_EnableIRQ(s_canIrqConfig.intIdx);
return true;
}
bool BspSeatMoveCan_Read(bsp_seat_move_can_frame_t *frame)
{
uint32_t interruptState;
uint8_t tail;
if(frame == NULL)
{
return false;
}
interruptState = Cy_SysLib_EnterCriticalSection();
tail = s_rxQueueTail;
if(tail == s_rxQueueHead)
{
Cy_SysLib_ExitCriticalSection(interruptState);
return false;
}
frame->identifier = s_rxQueue[tail].identifier;
frame->data0 = s_rxQueue[tail].data0;
frame->data1 = s_rxQueue[tail].data1;
frame->dlc = s_rxQueue[tail].dlc;
tail++;
if(tail >= SEAT_MOVE_CAN_RX_QUEUE_DEPTH)
{
tail = 0u;
}
s_rxQueueTail = tail;
Cy_SysLib_ExitCriticalSection(interruptState);
return true;
}
bool BspSeatMoveCan_Send(uint32_t identifier,
uint32_t data0,
uint32_t data1)
{
cy_stc_canfd_msg_t message = { 0 };
uint8_t bufferIndex;
if((identifier > 0x7FFu) || (g_bspSeatMoveCanBusOff != 0u))
{
g_bspSeatMoveCanLastTxStatus = (uint32_t)CY_CANFD_BAD_PARAM;
return false;
}
message.canFDFormat = false;
message.idConfig.extended = false;
message.idConfig.identifier = identifier;
message.dataConfig.dataLengthCode = SEAT_MOVE_CAN_DLC;
message.dataConfig.data[0] = data0;
message.dataConfig.data[1] = data1;
for(bufferIndex = 0u;
bufferIndex < SEAT_MOVE_CAN_TX_BUFFER_COUNT;
bufferIndex++)
{
cy_en_canfd_tx_buffer_status_t bufferStatus =
Cy_CANFD_GetTxBufferStatus(SEAT_MOVE_CAN_TYPE, bufferIndex);
if((bufferStatus != CY_CANFD_TX_BUFFER_PENDING) &&
(bufferStatus != CY_CANFD_TX_BUFFER_CANCEL_REQUESTED))
{
cy_en_canfd_status_t sendStatus;
g_bspSeatMoveCanLastTxId = identifier;
sendStatus = Cy_CANFD_UpdateAndTransmitMsgBuffer(
SEAT_MOVE_CAN_TYPE, bufferIndex, &message);
g_bspSeatMoveCanLastTxStatus = (uint32_t)sendStatus;
if(sendStatus == CY_CANFD_SUCCESS)
{
g_bspSeatMoveCanTxCount++;
return true;
}
return false;
}
}
g_bspSeatMoveCanTxBusyCount++;
return false;
}
static bool BspSeatMoveCan_ConfigClock(void)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup;
if(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS)
{
return false;
}
if((periClockHz < SEAT_MOVE_CAN_CLOCK_HZ) ||
((periClockHz % SEAT_MOVE_CAN_CLOCK_HZ) != 0u))
{
return false;
}
divider = periClockHz / SEAT_MOVE_CAN_CLOCK_HZ;
if((divider == 0u) || (divider > 256u))
{
return false;
}
clockGroup = Cy_SysClk_GetClockGroup(SEAT_MOVE_CAN_PCLK);
if(Cy_SysClk_PeriphDisableDivider(clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_MOVE_CAN_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(SEAT_MOVE_CAN_PCLK,
CY_SYSCLK_DIV_8_BIT,
SEAT_MOVE_CAN_DIVIDER_NUMBER) !=
CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_MOVE_CAN_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
return (Cy_SysClk_PeriphEnableDivider(clockGroup,
CY_SYSCLK_DIV_8_BIT,
SEAT_MOVE_CAN_DIVIDER_NUMBER) ==
CY_SYSCLK_SUCCESS);
}
static bool BspSeatMoveCan_ConfigPins(void)
{
cy_stc_gpio_pin_config_t rxConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_HIGHZ,
.hsiom = SEAT_MOVE_CAN_RX_MUX,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_gpio_pin_config_t txConfig =
{
.outVal = 1u,
.driveMode = CY_GPIO_DM_STRONG,
.hsiom = SEAT_MOVE_CAN_TX_MUX,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_gpio_pin_config_t enableConfig =
{
.outVal = SEAT_MOVE_CAN_TRANSCEIVER_ENABLE,
.driveMode = CY_GPIO_DM_STRONG_IN_OFF,
.hsiom = SEAT_MOVE_CAN_TRANSCEIVER_GPIO_HSIOM,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = CY_GPIO_DRIVE_FULL,
};
if(Cy_GPIO_Pin_Init(SEAT_MOVE_CAN_RX_PORT,
SEAT_MOVE_CAN_RX_PIN,
&rxConfig) != CY_GPIO_SUCCESS)
{
return false;
}
if(Cy_GPIO_Pin_Init(SEAT_MOVE_CAN_TX_PORT,
SEAT_MOVE_CAN_TX_PIN,
&txConfig) != CY_GPIO_SUCCESS)
{
return false;
}
if(Cy_GPIO_Pin_Init(SEAT_MOVE_CAN_TRANSCEIVER_PORT,
SEAT_MOVE_CAN_TRANSCEIVER_PIN,
&enableConfig) != CY_GPIO_SUCCESS)
{
return false;
}
Cy_GPIO_Write(SEAT_MOVE_CAN_TRANSCEIVER_PORT,
SEAT_MOVE_CAN_TRANSCEIVER_PIN,
SEAT_MOVE_CAN_TRANSCEIVER_ENABLE);
Cy_SysTick_DelayInUs(1000u);
return (Cy_GPIO_ReadOut(SEAT_MOVE_CAN_TRANSCEIVER_PORT,
SEAT_MOVE_CAN_TRANSCEIVER_PIN) ==
SEAT_MOVE_CAN_TRANSCEIVER_ENABLE);
}
static void BspSeatMoveCan_IrqHandler(void)
{
Cy_CANFD_IrqHandler(SEAT_MOVE_CAN_TYPE);
}
static void BspSeatMoveCan_RxCallback(bool rxFifoMessage,
uint8_t rxBufferOrFifo,
cy_stc_canfd_msg_t *message)
{
uint8_t head;
uint8_t nextHead;
(void)rxFifoMessage;
(void)rxBufferOrFifo;
if((message == NULL) || message->idConfig.extended || message->canFDFormat)
{
return;
}
g_bspSeatMoveCanRxCount++;
g_bspSeatMoveCanLastRxId = message->idConfig.identifier;
head = s_rxQueueHead;
nextHead = head + 1u;
if(nextHead >= SEAT_MOVE_CAN_RX_QUEUE_DEPTH)
{
nextHead = 0u;
}
if(nextHead == s_rxQueueTail)
{
g_bspSeatMoveCanRxQueueOverflowCount++;
return;
}
s_rxQueue[head].identifier = message->idConfig.identifier;
s_rxQueue[head].dlc = message->dataConfig.dataLengthCode;
s_rxQueue[head].data0 = message->dataConfig.data[0];
s_rxQueue[head].data1 = message->dataConfig.data[1];
s_rxQueueHead = nextHead;
}
static void BspSeatMoveCan_ErrorCallback(cy_en_canfd_bus_error_t error)
{
if(error == CY_CANFD_BUSOFF)
{
g_bspSeatMoveCanBusOff = 1u;
}
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef BSP_SEAT_MOVE_CAN_H
#define BSP_SEAT_MOVE_CAN_H
#include <stdbool.h>
#include <stdint.h>
typedef struct
{
uint32_t identifier;
uint32_t data0;
uint32_t data1;
uint8_t dlc;
} bsp_seat_move_can_frame_t;
bool BspSeatMoveCan_Init(void);
bool BspSeatMoveCan_Read(bsp_seat_move_can_frame_t *frame);
bool BspSeatMoveCan_Send(uint32_t identifier, uint32_t data0, uint32_t data1);
extern volatile uint32_t g_bspSeatMoveCanInitStatus;
extern volatile uint32_t g_bspSeatMoveCanRxCount;
extern volatile uint32_t g_bspSeatMoveCanRxQueueOverflowCount;
extern volatile uint32_t g_bspSeatMoveCanTxCount;
extern volatile uint32_t g_bspSeatMoveCanTxBusyCount;
extern volatile uint32_t g_bspSeatMoveCanLastRxId;
extern volatile uint32_t g_bspSeatMoveCanLastTxId;
extern volatile uint32_t g_bspSeatMoveCanLastTxStatus;
extern volatile uint32_t g_bspSeatMoveCanBusOff;
#endif /* BSP_SEAT_MOVE_CAN_H */
@@ -0,0 +1,157 @@
#ifndef SEAT_BACKREST_HEATER_CONFIG_H
#define SEAT_BACKREST_HEATER_CONFIG_H
#include "cy_project.h"
#include "cy_device_headers.h"
/*
* Schematic-confirmed backrest-heater path:
* P16.7 / KBJRD -> R126 (1 kOhm) -> BTS7010-1EPA IN -> backrest heater.
* The rev_d SDK maps P16.7 to TCPWM1 line 65.
*/
#define SEAT_BACKREST_HEATER_PWM_PORT GPIO_PRT16
#define SEAT_BACKREST_HEATER_PWM_PIN (7u)
#define SEAT_BACKREST_HEATER_PWM_GPIO_HSIOM P16_7_GPIO
#define SEAT_BACKREST_HEATER_PWM_HSIOM P16_7_TCPWM1_LINE65
#define SEAT_BACKREST_HEATER_PWM_TCPWM TCPWM1_GRP0_CNT65
#define SEAT_BACKREST_HEATER_PWM_PCLK PCLK_TCPWM1_CLOCKS65
#define SEAT_BACKREST_HEATER_PWM_DIVIDER_NUMBER (3u)
#define SEAT_BACKREST_HEATER_COUNTER_CLOCK_HZ (2000000u)
#define SEAT_BACKREST_HEATER_PWM_FREQUENCY_HZ (100u)
#define SEAT_BACKREST_HEATER_PWM_PERIOD_TICKS \
(SEAT_BACKREST_HEATER_COUNTER_CLOCK_HZ / \
SEAT_BACKREST_HEATER_PWM_FREQUENCY_HZ)
/* Three open-loop power levels. Tune only after thermal validation. */
#define SEAT_BACKREST_HEATER_LOW_DUTY_PERCENT (30u)
#define SEAT_BACKREST_HEATER_MEDIUM_DUTY_PERCENT (60u)
#define SEAT_BACKREST_HEATER_HIGH_DUTY_PERCENT (90u)
#define SEAT_BACKREST_HEATER_DEFAULT_LEVEL (0u)
#define SEAT_BACKREST_HEATER_TASK_PERIOD_US (1000u)
#define SEAT_BACKREST_HEATER_SENSE_SAMPLE_MS (10u)
#define SEAT_BACKREST_HEATER_SENSE_MAX_ERRORS (3u)
#define SEAT_BACKREST_HEATER_MAX_ACTIVE_TIME_MS (600000u)
/*
* Vehicle CAN4 wiring proven by D:\wcl\can and Seat_Fan_demo:
* P3.7 RX, P3.6 TX, P3.1 high enables the board TJA1044 circuit.
*/
#define SEAT_BACKREST_HEATER_CAN_TYPE CY_CANFD1_2_TYPE
#define SEAT_BACKREST_HEATER_CAN_RX_PORT GPIO_PRT3
#define SEAT_BACKREST_HEATER_CAN_RX_PIN (7u)
#define SEAT_BACKREST_HEATER_CAN_RX_MUX P3_7_CANFD1_TTCAN_RX2
#define SEAT_BACKREST_HEATER_CAN_TX_PORT GPIO_PRT3
#define SEAT_BACKREST_HEATER_CAN_TX_PIN (6u)
#define SEAT_BACKREST_HEATER_CAN_TX_MUX P3_6_CANFD1_TTCAN_TX2
#define SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PORT GPIO_PRT3
#define SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_PIN (1u)
#define SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_GPIO_HSIOM P3_1_GPIO
#define SEAT_BACKREST_HEATER_CAN_TRANSCEIVER_ENABLE (1u)
#define SEAT_BACKREST_HEATER_CAN_PCLK PCLK_CANFD1_CLOCK_CAN2
#define SEAT_BACKREST_HEATER_CAN_IRQN canfd_1_interrupts0_2_IRQn
#define SEAT_BACKREST_HEATER_CAN_CPU_IRQ CPUIntIdx2_IRQn
#define SEAT_BACKREST_HEATER_CAN_DIVIDER_NUMBER (0u)
#define SEAT_BACKREST_HEATER_CAN_CLOCK_HZ (40000000u)
#define SEAT_BACKREST_HEATER_CAN_TX_BUFFER_COUNT (4u)
#define SEAT_BACKREST_HEATER_CAN_RX_FIFO0_ELEMENTS (8u)
#define SEAT_BACKREST_HEATER_CAN_RX_QUEUE_DEPTH (8u)
/* Classic CAN matrix messages, 11-bit IDs, 8-byte payloads, Intel bit order. */
#define SEAT_BACKREST_HEATER_CAN_CONTROL_29E_ID (0x29Eu)
#define SEAT_BACKREST_HEATER_CAN_CONFIG_2AA_ID (0x2AAu)
#define SEAT_BACKREST_HEATER_CAN_MODE_3CE_ID (0x3CEu)
#define SEAT_BACKREST_HEATER_CAN_SWITCH_42A_ID (0x42Au)
#define SEAT_BACKREST_HEATER_CAN_FEEDBACK_4FA_ID (0x4FAu)
#define SEAT_BACKREST_HEATER_CAN_DLC (8u)
#define SEAT_BACKREST_HEATER_CAN_FEEDBACK_PERIOD_MS (100u)
#define SEAT_BACKREST_HEATER_CAN_ACCEPT_42A_COMMAND (1u)
#define SEAT_BACKREST_HEATER_DEFAULT_CAN_GEAR (2u)
/* Safety watchdog and retained C-SPY session latch, derived from Seat_Fan_demo. */
#define SEAT_BACKREST_HEATER_WDT_TIMEOUT_TICKS (3200u)
#define SEAT_BACKREST_HEATER_DEBUG_BREG_INDEX (3u)
#define SEAT_BACKREST_HEATER_DEBUG_ACTIVE_MAGIC (0x48444247u) /* "HDBG" */
#define SEAT_BACKREST_HEATER_DEBUG_EXIT_MAGIC (0x484F4646u) /* "HOFF" */
/*
* Schematic-confirmed diagnostic current-sense path:
* KBJRD-AD -> P12.2 / ADC[1]_6. The BTS7010 IS pin uses a 1.2 kOhm
* sense resistor and the schematic annotates K=5000. The calculated current
* is for bench visibility only; production thresholds require calibration.
*/
#define SEAT_BACKREST_HEATER_SENSE_ENABLE (1u)
#define SEAT_BACKREST_HEATER_SENSE_PORT GPIO_PRT12
#define SEAT_BACKREST_HEATER_SENSE_PIN (2u)
#define SEAT_BACKREST_HEATER_SENSE_GPIO_HSIOM P12_2_GPIO
#define SEAT_BACKREST_HEATER_SENSE_ADC PASS0_SAR1
#define SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL PASS0_SAR1_CH6
#define SEAT_BACKREST_HEATER_SENSE_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN6
#define SEAT_BACKREST_HEATER_SENSE_PORT_ADDRESS CY_ADC_PORT_ADDRESS_SARMUX0
#define SEAT_BACKREST_HEATER_SENSE_PCLK PCLK_PASS0_CLOCK_SAR1
#define SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER (4u)
#define SEAT_BACKREST_HEATER_SENSE_RESISTOR_OHM (1200u)
#define SEAT_BACKREST_HEATER_SENSE_KILIS (5000u)
#define SEAT_BACKREST_HEATER_ADC_REFERENCE_MV (3300u)
#define SEAT_BACKREST_HEATER_ADC_FULL_SCALE (4095u)
/* BTS7010 control facts confirmed on the power-driver schematic page. */
#define SEAT_BACKREST_HEATER_BTS7010_IN_ACTIVE_HIGH (1u)
#define SEAT_BACKREST_HEATER_BTS7010_DEN_MCU_CONTROLLED (0u)
#if !defined(CYT4BFBCJE) || !defined(CY_MCU_rev_d)
#error "Seat_Controller_demo requires CYT4BFBCJE rev_d headers."
#endif
#if ((SEAT_BACKREST_HEATER_COUNTER_CLOCK_HZ % \
SEAT_BACKREST_HEATER_PWM_FREQUENCY_HZ) != 0u)
#error "The heater counter clock must divide evenly by the PWM frequency."
#endif
#if ((SEAT_BACKREST_HEATER_PWM_PERIOD_TICKS == 0u) || \
(SEAT_BACKREST_HEATER_PWM_PERIOD_TICKS > 65535u))
#error "The heater PWM period does not fit TCPWM1 group 0 counter 65."
#endif
#if ((SEAT_BACKREST_HEATER_LOW_DUTY_PERCENT == 0u) || \
(SEAT_BACKREST_HEATER_LOW_DUTY_PERCENT >= \
SEAT_BACKREST_HEATER_MEDIUM_DUTY_PERCENT) || \
(SEAT_BACKREST_HEATER_MEDIUM_DUTY_PERCENT >= \
SEAT_BACKREST_HEATER_HIGH_DUTY_PERCENT) || \
(SEAT_BACKREST_HEATER_HIGH_DUTY_PERCENT > 100u))
#error "Heater duties must be ordered LOW < MEDIUM < HIGH <= 100."
#endif
#if (SEAT_BACKREST_HEATER_DEFAULT_LEVEL > 3u)
#error "Default heater level must be 0 (off) through 3 (high)."
#endif
#if ((SEAT_BACKREST_HEATER_CAN_CONTROL_29E_ID > 0x7FFu) || \
(SEAT_BACKREST_HEATER_CAN_CONFIG_2AA_ID > 0x7FFu) || \
(SEAT_BACKREST_HEATER_CAN_MODE_3CE_ID > 0x7FFu) || \
(SEAT_BACKREST_HEATER_CAN_SWITCH_42A_ID > 0x7FFu) || \
(SEAT_BACKREST_HEATER_CAN_FEEDBACK_4FA_ID > 0x7FFu))
#error "Seat heating CAN IDs must be 11-bit standard identifiers."
#endif
#if (SEAT_BACKREST_HEATER_CAN_RX_QUEUE_DEPTH < 5u)
#error "The heater CAN receive queue must hold at least five frames."
#endif
#if ((SEAT_BACKREST_HEATER_DEFAULT_CAN_GEAR == 0u) || \
(SEAT_BACKREST_HEATER_DEFAULT_CAN_GEAR > 3u))
#error "Default heater CAN gear must be in the range 1 through 3."
#endif
#if ((SEAT_BACKREST_HEATER_SENSE_SAMPLE_MS == 0u) || \
(SEAT_BACKREST_HEATER_SENSE_MAX_ERRORS == 0u) || \
(SEAT_BACKREST_HEATER_MAX_ACTIVE_TIME_MS == 0u))
#error "Heater sample period and maximum active time must be non-zero."
#endif
#if (SEAT_BACKREST_HEATER_DEBUG_BREG_INDEX >= SRSS_BACKUP_NUM_BREG)
#error "The selected heater backup register is not present on this device."
#endif
#endif /* SEAT_BACKREST_HEATER_CONFIG_H */
+136
View File
@@ -0,0 +1,136 @@
#ifndef SEAT_FAN_CONFIG_H
#define SEAT_FAN_CONFIG_H
#include "cy_project.h"
#include "cy_device_headers.h"
/*
* Backrest fan:
* P11.1 -> KBFSDY-PWM-IN -> BTS7030 IN0 -> backrest fan.
* The schematic name "PWM0-60" maps to TCPWM1 group 0 counter 60 in SDL.
*/
#define SEAT_FAN_PWM_PORT GPIO_PRT11
#define SEAT_FAN_PWM_PIN (1u)
#define SEAT_FAN_PWM_GPIO_HSIOM P11_1_GPIO
#define SEAT_FAN_PWM_HSIOM P11_1_TCPWM1_LINE60
#define SEAT_FAN_PWM_TCPWM TCPWM1_GRP0_CNT60
#define SEAT_FAN_PWM_PCLK PCLK_TCPWM1_CLOCKS60
/*
* Seat-cushion fan output is unused in this first version and must stay low:
* P11.2 -> ZDFSDY-PWM-IN.
*/
#define SEAT_FAN_UNUSED_PORT GPIO_PRT11
#define SEAT_FAN_UNUSED_PIN (2u)
#define SEAT_FAN_UNUSED_GPIO_HSIOM P11_2_GPIO
#define SEAT_FAN_PWM_DIVIDER_NUMBER (0u)
#define SEAT_FAN_COUNTER_CLOCK_HZ (2000000u)
#define SEAT_FAN_PWM_FREQUENCY_HZ (100u)
#define SEAT_FAN_PWM_PERIOD_TICKS \
(SEAT_FAN_COUNTER_CLOCK_HZ / SEAT_FAN_PWM_FREQUENCY_HZ)
#define SEAT_FAN_STARTUP_DUTY_PERCENT (100u)
#define SEAT_FAN_RUNNING_DUTY_PERCENT (70u)
#define SEAT_FAN_STARTUP_TIME_US (2000000u)
#define SEAT_FAN_DEBUG_POLL_TIME_US (1000u)
#define SEAT_FAN_WDT_TIMEOUT_TICKS (3200u)
/* CAN-controlled application timing and the three physical fan levels. */
#define SEAT_FAN_TASK_PERIOD_US (1000u)
#define SEAT_FAN_STARTUP_TIME_MS (SEAT_FAN_STARTUP_TIME_US / 1000u)
#define SEAT_FAN_GEAR1_DUTY_PERCENT (50u)
#define SEAT_FAN_GEAR2_DUTY_PERCENT (70u)
#define SEAT_FAN_GEAR3_DUTY_PERCENT (100u)
#define SEAT_FAN_DEFAULT_GEAR (2u)
/*
* Vehicle CAN4 wiring proven by D:\wcl\can:
* P3.7 RX, P3.6 TX, P3.1 high enables the board TJA1044 circuit.
*/
#define SEAT_FAN_CAN_TYPE CY_CANFD1_2_TYPE
#define SEAT_FAN_CAN_RX_PORT GPIO_PRT3
#define SEAT_FAN_CAN_RX_PIN (7u)
#define SEAT_FAN_CAN_RX_MUX P3_7_CANFD1_TTCAN_RX2
#define SEAT_FAN_CAN_TX_PORT GPIO_PRT3
#define SEAT_FAN_CAN_TX_PIN (6u)
#define SEAT_FAN_CAN_TX_MUX P3_6_CANFD1_TTCAN_TX2
#define SEAT_FAN_CAN_TRANSCEIVER_PORT GPIO_PRT3
#define SEAT_FAN_CAN_TRANSCEIVER_PIN (1u)
#define SEAT_FAN_CAN_TRANSCEIVER_GPIO_HSIOM P3_1_GPIO
#define SEAT_FAN_CAN_TRANSCEIVER_ENABLE (1u)
#define SEAT_FAN_CAN_PCLK PCLK_CANFD1_CLOCK_CAN2
#define SEAT_FAN_CAN_IRQN canfd_1_interrupts0_2_IRQn
#define SEAT_FAN_CAN_CPU_IRQ CPUIntIdx2_IRQn
#define SEAT_FAN_CAN_DIVIDER_NUMBER (0u)
#define SEAT_FAN_CAN_CLOCK_HZ (40000000u)
#define SEAT_FAN_CAN_TX_BUFFER_COUNT (4u)
#define SEAT_FAN_CAN_RX_FIFO0_ELEMENTS (8u)
#define SEAT_FAN_CAN_RX_QUEUE_DEPTH (8u)
/* Classic CAN matrix messages, 11-bit IDs, 8-byte payloads, Intel bit order. */
#define SEAT_FAN_CAN_CONTROL_29E_ID (0x29Eu)
#define SEAT_FAN_CAN_CONFIG_2AA_ID (0x2AAu)
#define SEAT_FAN_CAN_SWITCH_42A_ID (0x42Au)
#define SEAT_FAN_CAN_FEEDBACK_4FA_ID (0x4FAu)
#define SEAT_FAN_CAN_DLC (8u)
#define SEAT_FAN_CAN_FEEDBACK_PERIOD_MS (100u)
#define SEAT_FAN_CAN_ACCEPT_42A_COMMAND (1u)
/*
* BREG3 retains the debug-session state through debug/XRES/WDT resets.
* DEBUG_ACTIVE is written by firmware as soon as it observes C_DEBUGEN, so a
* debugger-induced reset cannot accidentally restart the fan even if the
* C-SPY exit hook was skipped. DEBUG_EXIT is written when detach is observed
* or by the C-SPY exit hook. A genuine POR releases either marker.
*/
#define SEAT_FAN_DEBUG_SESSION_BREG_INDEX (3u)
#define SEAT_FAN_DEBUG_ACTIVE_MAGIC (0x44424741u) /* ASCII "DBGA" */
#define SEAT_FAN_DEBUG_EXIT_MAGIC (0x5346414Eu) /* ASCII "SFAN" */
#if !defined(CYT4BFBCJE) || !defined(CY_MCU_rev_d)
#error "Seat_Controller_demo must be built for CYT4BFBCJE with the rev_d headers."
#endif
#if ((SEAT_FAN_COUNTER_CLOCK_HZ % SEAT_FAN_PWM_FREQUENCY_HZ) != 0u)
#error "The TCPWM counter clock must divide evenly by the requested PWM frequency."
#endif
#if ((SEAT_FAN_PWM_PERIOD_TICKS == 0u) || (SEAT_FAN_PWM_PERIOD_TICKS > 65535u))
#error "The PWM period does not fit TCPWM1 group 0 counter 60."
#endif
#if ((SEAT_FAN_STARTUP_TIME_US % SEAT_FAN_DEBUG_POLL_TIME_US) != 0u)
#error "The startup duration must be an integer number of debug polling periods."
#endif
#if ((SEAT_FAN_STARTUP_TIME_US % 1000u) != 0u)
#error "The fan startup time must be an integer number of milliseconds."
#endif
#if ((SEAT_FAN_GEAR1_DUTY_PERCENT > SEAT_FAN_GEAR2_DUTY_PERCENT) || \
(SEAT_FAN_GEAR2_DUTY_PERCENT > SEAT_FAN_GEAR3_DUTY_PERCENT) || \
(SEAT_FAN_GEAR3_DUTY_PERCENT > 100u))
#error "Fan gear duty percentages must be monotonic and no greater than 100."
#endif
#if ((SEAT_FAN_DEFAULT_GEAR == 0u) || (SEAT_FAN_DEFAULT_GEAR > 3u))
#error "The default physical fan gear must be in the range 1 through 3."
#endif
#if ((SEAT_FAN_CAN_CONTROL_29E_ID > 0x7FFu) || \
(SEAT_FAN_CAN_CONFIG_2AA_ID > 0x7FFu) || \
(SEAT_FAN_CAN_SWITCH_42A_ID > 0x7FFu) || \
(SEAT_FAN_CAN_FEEDBACK_4FA_ID > 0x7FFu))
#error "Seat ventilation CAN IDs must be 11-bit standard identifiers."
#endif
#if (SEAT_FAN_CAN_RX_QUEUE_DEPTH < 4u)
#error "The CAN receive queue must hold at least four frames."
#endif
#if (SEAT_FAN_DEBUG_SESSION_BREG_INDEX >= SRSS_BACKUP_NUM_BREG)
#error "The selected backup register is not present on this device."
#endif
#endif /* SEAT_FAN_CONFIG_H */
@@ -0,0 +1,95 @@
#ifndef SEAT_MOVE_CONFIG_H
#define SEAT_MOVE_CONFIG_H
#include "cy_project.h"
#include "cy_device_headers.h"
#define SEAT_MOVE_MOTOR_COUNT (4u)
#define SEAT_MOVE_ACTIVE_MOTOR_MASK (0x0Fu)
#define SEAT_MOVE_TASK_PERIOD_US (1000u)
#define SEAT_MOVE_START_STAGGER_MS (100u)
#define SEAT_MOVE_MAX_COMMAND_DURATION_MS (60000u)
#define SEAT_MOVE_COMMAND_QUEUE_DEPTH (8u)
/* CAN4 wiring verified by Seat_Fan_demo and D:\wcl\can. P3.1 is shared with
* the DRV8718 DVDD enable on this board; both users require it to remain high. */
#define SEAT_MOVE_CAN_TYPE CY_CANFD1_2_TYPE
#define SEAT_MOVE_CAN_RX_PORT GPIO_PRT3
#define SEAT_MOVE_CAN_RX_PIN (7u)
#define SEAT_MOVE_CAN_RX_MUX P3_7_CANFD1_TTCAN_RX2
#define SEAT_MOVE_CAN_TX_PORT GPIO_PRT3
#define SEAT_MOVE_CAN_TX_PIN (6u)
#define SEAT_MOVE_CAN_TX_MUX P3_6_CANFD1_TTCAN_TX2
#define SEAT_MOVE_CAN_TRANSCEIVER_PORT GPIO_PRT3
#define SEAT_MOVE_CAN_TRANSCEIVER_PIN (1u)
#define SEAT_MOVE_CAN_TRANSCEIVER_GPIO_HSIOM P3_1_GPIO
#define SEAT_MOVE_CAN_TRANSCEIVER_ENABLE (1u)
#define SEAT_MOVE_CAN_PCLK PCLK_CANFD1_CLOCK_CAN2
#define SEAT_MOVE_CAN_IRQN canfd_1_interrupts0_2_IRQn
#define SEAT_MOVE_CAN_CPU_IRQ CPUIntIdx2_IRQn
#define SEAT_MOVE_CAN_DIVIDER_NUMBER (0u)
#define SEAT_MOVE_CAN_CLOCK_HZ (40000000u)
#define SEAT_MOVE_CAN_TX_BUFFER_COUNT (8u)
#define SEAT_MOVE_CAN_RX_FIFO0_ELEMENTS (8u)
#define SEAT_MOVE_CAN_RX_QUEUE_DEPTH (16u)
#define SEAT_MOVE_CAN_DLC (8u)
#define SEAT_MOVE_CAN_PROTOCOL_VERSION (1u)
#define SEAT_MOVE_CAN_COMMAND_ID (0x300u)
#define SEAT_MOVE_CAN_SYSTEM_STATUS_ID (0x301u)
#define SEAT_MOVE_CAN_MOTOR_STATUS_ID (0x302u)
#define SEAT_MOVE_CAN_DIAGNOSTIC_ID (0x303u)
#define SEAT_CONTROLLER_IDENTITY_REQUEST_ID (0x304u)
#define SEAT_CONTROLLER_IDENTITY_RESPONSE_ID (0x305u)
#define SEAT_CONTROLLER_IDENTITY_MAGIC (0x54414553u) /* "SEAT" */
#define SEAT_CONTROLLER_IDENTITY_PROTOCOL (1u)
#define SEAT_CONTROLLER_FIRMWARE_VERSION_MAJOR (1u)
#define SEAT_CONTROLLER_FIRMWARE_VERSION_MINOR (1u)
#define SEAT_CONTROLLER_FIRMWARE_VERSION_PATCH (0u)
#define SEAT_CONTROLLER_CAPABILITY_MASK (0x3Fu)
#define SEAT_CONTROLLER_IDENTITY_RESPONSE_DATA1 \
((SEAT_CONTROLLER_CAPABILITY_MASK << 24u) | \
(SEAT_CONTROLLER_FIRMWARE_VERSION_PATCH << 16u) | \
(SEAT_CONTROLLER_FIRMWARE_VERSION_MINOR << 8u) | \
SEAT_CONTROLLER_FIRMWARE_VERSION_MAJOR)
#define SEAT_MOVE_SYSTEM_STATUS_PERIOD_MS (100u)
#define SEAT_MOVE_MOTOR_STATUS_PERIOD_MS (100u)
#define SEAT_MOVE_DIAGNOSTIC_PERIOD_MS (500u)
/* BREG3 records a C-SPY session across debug/XRES/WDT resets. A genuine POR
* clears the latch, so normal vehicle operation never requires a debugger. */
#define SEAT_MOVE_DEBUG_SESSION_BREG_INDEX (3u)
#define SEAT_MOVE_DEBUG_ACTIVE_MAGIC (0x4D4F5641u) /* "MOVA" */
#define SEAT_MOVE_DEBUG_EXIT_MAGIC (0x4D4F5645u) /* "MOVE" */
#define SEAT_MOVE_WDT_TIMEOUT_TICKS (3200u)
/* One dedicated 128-byte Work Flash small sector. */
#define SEAT_MOVE_STORE_ADDRESS (CY_WFLASH_SM_SBM_TOP)
#define SEAT_MOVE_STORE_MAGIC (0x534D5633u) /* "SMV3" */
#define SEAT_MOVE_STORE_VERSION (3u)
#define SEAT_MOVE_STORE_LEGACY_MAGIC (0x534D5632u) /* "SMV2" */
#define SEAT_MOVE_STORE_LEGACY_VERSION (2u)
#define SEAT_MOVE_STORE_LEGACY_MOTOR_COUNT (3u)
#if !defined(CYT4BFBCJE) || !defined(CY_MCU_rev_d)
#error "Seat_Controller_demo must be built for CYT4BFBCJE with rev_d headers."
#endif
#if (SEAT_MOVE_ACTIVE_MOTOR_MASK != 0x0Fu)
#error "All four vehicle seat-motor outputs must be enabled."
#endif
#if ((SEAT_MOVE_CAN_COMMAND_ID > 0x7FFu) || \
(SEAT_MOVE_CAN_SYSTEM_STATUS_ID > 0x7FFu) || \
(SEAT_MOVE_CAN_MOTOR_STATUS_ID > 0x7FFu) || \
(SEAT_MOVE_CAN_DIAGNOSTIC_ID > 0x7FFu) || \
(SEAT_CONTROLLER_IDENTITY_REQUEST_ID > 0x7FFu) || \
(SEAT_CONTROLLER_IDENTITY_RESPONSE_ID > 0x7FFu))
#error "Seat movement CAN identifiers must be 11-bit standard IDs."
#endif
#if (SEAT_MOVE_DEBUG_SESSION_BREG_INDEX >= SRSS_BACKUP_NUM_BREG)
#error "The selected debug-session backup register is not available."
#endif
#endif /* SEAT_MOVE_CONFIG_H */
@@ -0,0 +1,39 @@
# Seat_Controller_demo
`Seat_Controller_demo` 是 `CYT4BFBCJE` / `CY_MCU_rev_d` / Cortex-M0+ 的单板统一座椅控制器工程。它以已验证的 `Seat_Move_demo` 为安全和四电机骨架,并合入 `Seat_Backrest_Heater_demo` 与 `Seat_Fan_demo` 的 PWM/ADC 驱动和 CAN 控制语义。
## 单板资源
- 共享 CAN4:RX=P3.7、TX=P3.6、收发器/DVDD 使能=P3.1,高电平有效。
- 通风:P11.1 / TCPWM1-60;P11.2 保持 GPIO 低。
- 靠背加热:P16.7 / TCPWM1-65;电流诊断 P12.2 / SAR1 CH6。
- 四电机与 Hall:沿用 `Seat_Move_demo` 的 DRV8718、TCPWM1-12/13/14/19/20 和四路 Hall 资源。
- 资源分频:风扇=divider0、SCB4=divider1、电机=divider2、加热=divider3、ADC=divider4;各已分离。
## 共享 CAN 协议
一个 CAN4 实例和一个 IRQ/RX 队列负责全部报文:
- RX `0x29E/0x2AA/0x3CE/0x42A`:通风和加热。
- RX `0x300`:四电机命令。
- TX `0x4FA`:byte0 bits0..2 通风状态,bits3..5 加热状态,合成在同一帧中。
- TX `0x301/0x302/0x303`:移动 ACK、状态和诊断。
CAN 为 500 kbit/s、经典标准帧、11-bit ID、DLC 8。`seat_move_application.c` 只把 `0x300` 交给移动协议,其余已过滤热管理帧交给 `seat_controller_thermal.c`,避免多个模块争抢同一 CAN 外设。
## 安全行为
- 启动最早阶段把电机、P11.1/P11.2 和 P16.7 强制为 GPIO 低。
- 任何热/风输出初始化或运行故障都会关断热/风并锁存全局 driver fault;移动也进入 inhibit。
- 移动保留 `STOP_ALL`、sequence、堵转、端点、超时、Work Flash 和 WDT 机制。
- C-SPY `execUserExit()` 同时关断四电机 PWM、风扇 TCPWM60 和加热 TCPWM65,并验证物理输出 latch 为低。
- Motor3 仍未接线、未实标;固件可以解析协议,但上位机默认禁止发送该轴命令。
## 构建
```powershell
& 'D:\Program Files\IAR Systems\Embedded Workbench 9.2\common\bin\iarbuild.exe' `
'.\IAR\Seat_Controller_demo.ewp' -build rev_d -log all
```
输出:`IAR/rev_d/Exe/Seat_Controller_demo.hex`。构建不会烧录或启动执行器。首次烧录后的热、风、Motor0..2 组合回归仍必须在台架完成,未完成前不得标记为 HIL verified。
@@ -0,0 +1,96 @@
# Seat_Move_demo
本工程是 `CYT4BFBCJE`、`CY_MCU_rev_d`、Cortex-M0+ Flash 的独立 IAR EWARM 9.2 工程。目录骨架沿用 `Seat_Fan_demo`,四路 DRV8718S-Q1 有刷电机/Hall 控制源自 `D:\Project\project-code` 的 `Seat_FourMotor_Direct_Function_Test`。四路CAN协议见 `Docs/outputs/seat_move_four_motor_20260724/座椅四电机移动功能CAN矩阵表.xlsx`。
## 1. 电机与硬件资源
| CAN MotorSelector | 功能/量程 | PWM/桥臂 | Hall | 默认量程 |
| ---: | --- | --- | --- | ---: |
| 0 | 腿托伸缩,0~102.00 mm | P5.4 / IN2 / HB7-HB8 / TCPWM1-13 | P18.2 | 480 pulse,已标定 |
| 1 | 腿托上下,0~85.00° | P5.5 / IN3 / HB5-HB6 / TCPWM1-14 | P10.1 | 790 pulse,已标定 |
| 2 | 靠背前后,0~90.00° | P8.0 / IN4 / HB3-HB4 / TCPWM1-19 | P14.0 | 1590 pulse,已标定 |
| 3 | 座椅水平移动,0~240.00 mm(预估) | P1.0 / IN1 / HB1-HB2 / TCPWM1-12 | P12.5 | 960 pulse,待实标 |
DRV8718 公共资源:SCB4 SPI=P6.0/P6.1/P6.2,nSCS=P31.2,nSLEEP=P9.1,DRVOFF=P8.1,DVDD_EN=P3.1。P3.1 同时是开发板 CAN 收发器使能,两者均要求高电平。
电机3暂按原始第四路约4 pulse/mm估算为240.00 mm、960 pulse、86 pulse/s。未接电机3时不要发送其运动命令;初始化、故障、`STOP_ALL`和调试退出仍会把P1.0切回GPIO低并使HB1-HB2高阻。
## 2. 避免多电机干涉
- CAN IRQ 只把完整帧写入环形队列,不调用电机、SPI 或 Flash API。
- 主循环是 DRV8718 SPI 和共享桥臂影子寄存器的唯一所有者;SPI 另有不可重入保护。
- 每台电机使用独立任务、Hall 计数、方向、换向滑行、速度环、堵转窗口和超时状态。
- 同一电机存在活动或待启动命令时,新运动命令返回 `CONFLICT`,不会覆盖运行中的状态机。
- 不同电机允许并发,但连续收到的启动帧至少错开 100 ms,降低同时起动电流和共享驱动寄存器切换造成的相互影响。
- 换向先关闭 PWM/将输入切回 GPIO 低,再等待至少 100 ms 且 Hall 连续静默 100 ms,最后配置桥臂并启动新方向。
- 驱动器故障、CAN bus-off、配置存储错误、WDT 恢复或调试退出均触发全局锁存停止。
## 3. CAN 接口
CAN4:RX=P3.7,TX=P3.6,经典标准 CAN,500 kbit/s,11-bit ID,DLC=8,Intel/little-endian。
| ID | 方向 | 周期/触发 | 内容 |
| ---: | --- | --- | --- |
| `0x300` | RX | Event | 命令:Byte0 opcode;Byte1 Motor/Direction/Version;Byte2..3 sequence;Byte4..5 ParamA;Byte6..7 ParamB |
| `0x301` | TX | 100 ms + Event | 系统 ACK、拒绝原因、活动掩码、最近 sequence、存储状态和安全故障 |
| `0x302` | TX | 每台电机 100 ms + Event | Byte0 电机 Sub ID,任务/结果、方向、位置、占空比 |
| `0x303` | TX | 每台电机 500 ms + Event | Byte0 电机 Sub ID,底层事件、Hall、校准量程和最大速度 |
`0x300` Byte1:bits0..1 MotorSelector=`0..3`,bits2..3 Direction,bits4..5 ProtocolVersion=`1`,bits6..7=`0`。MotorSelector=`3`在电机类Opcode中表示电机3;在`STOP_ALL`/`SAVE_SHUTDOWN`中由Opcode定义全局语义,仍要求Selector=`3`。除`STOP_ALL`外,16-bit sequence必须按有符号环形比较递增;`STOP_ALL`总是立即生效并重同步sequence。
`0x301` Byte1 bits0..3为LastOpcode、bits4..7为四路ActiveMask;Byte4 bits0..3为四路RetryMask、bit4为RecordClean、bit5为WatchdogReset、bit6为RecordValid、bit7保留。
### ZCANPRO 安全测试帧
所有帧为标准帧、DLC 8。初次上板优先使用 500 ms、50% 的定时命令;它不要求绝对位置已经校准。
| 用途 | ID | Data bytes 0..7 |
| --- | ---: | --- |
| 全停并同步 sequence=1 | `0x300` | `09 13 01 00 00 00 00 00` |
| 电机0向远端 500 ms/50%,seq=2 | `0x300` | `03 14 02 00 F4 01 32 00` |
| 电机0向近端 500 ms/50%,seq=3 | `0x300` | `03 18 03 00 F4 01 32 00` |
| 电机1向远端 500 ms/50%,seq=4 | `0x300` | `03 15 04 00 F4 01 32 00` |
| 电机1向近端 500 ms/50%,seq=5 | `0x300` | `03 19 05 00 F4 01 32 00` |
| 电机2向远端 500 ms/50%,seq=6 | `0x300` | `03 16 06 00 F4 01 32 00` |
| 电机2向近端 500 ms/50%,seq=7 | `0x300` | `03 1A 07 00 F4 01 32 00` |
| 电机3向远端 500 ms/50%,seq=8(接线后) | `0x300` | `03 17 08 00 F4 01 32 00` |
| 电机3向近端 500 ms/50%,seq=9(接线后) | `0x300` | `03 1B 09 00 F4 01 32 00` |
| 停止电机3,seq=10 | `0x300` | `08 13 0A 00 00 00 00 00` |
| 全停,seq=11 | `0x300` | `09 13 0B 00 00 00 00 00` |
| 保存状态并受控关机,seq=12 | `0x300` | `0C 13 0C 00 00 00 00 00` |
电机3接线确认后,并发错峰测试可在很短间隔内依次发送四帧:
```text
03 14 14 00 E8 03 32 00 motor0 far, 1000 ms, seq=20
03 15 15 00 E8 03 32 00 motor1 far, 1000 ms, seq=21
03 16 16 00 E8 03 32 00 motor2 far, 1000 ms, seq=22
03 17 17 00 E8 03 32 00 motor3 far, 1000 ms, seq=23
```
预期`0x301` ActiveMask逐步变为`1→3→7→15`;四路物理起动相隔约100 ms。测试结束发送`09 13 18 00 00 00 00 00`全停。
## 4. 位置、校准与存储
单 Hall 只能按指令方向累计相对位置。首次使用绝对/相对位置命令前,需要完成端点校准,或恢复一份 `SAVE_SHUTDOWN` 保存的 clean 记录。定时占空比/定时速度命令可在绝对位置无效时使用,但仍受 Hall 堵转、端点和 60 s 参数上限保护。定时运动到期时必须至少检测到一个 Hall 边沿,否则任务返回 `FAILED/STALL`,不会把断线、未供电或桥臂未动作误报为 `OK`。
`SAVE_SHUTDOWN`先全停,再把四路Hall位置、量程和最大速度写入两个交替使用的128-byte Work Flash小扇区,记录包含版本、递增sequence和CRC32。固件能迁移旧SMV2三电机记录:保留电机0~2已存标定,电机3保持`positionValid=false`;首次保存后写为SMV3四电机记录。
量程校准会移动到机械端点且当前底层API为阻塞式,仅允许在四路全部停止时接收;常规车辆运行不应发送该维护命令。
## 5. 调试退出与正常产品启动
`IAR/Seat_Move_demo.mac` 的 `execUserExit()` 在 C-SPY 仍持有目标时直接执行:写入 BREG3 退出标记、置高 P8.1/DRVOFF、关闭 TCPWM1-12/13/14/19/20,并把 IN1..IN4 切回 GPIO 低。WDT 在 debug halt 时继续运行,补偿 C-SPY 跳过正常退出路径的情况。
调试结束后,开发板不断电也保持安全关闭;新的 Download and Debug 会由 `execUserSetup()` 清除旧标记。真正 POR 上电也会清除标记,因此量产/车辆运行不依赖调试器,冷启动后正常初始化并等待 CAN 命令。
## 6. 构建
```powershell
& 'D:\Program Files\IAR Systems\Embedded Workbench 9.2\common\bin\iarbuild.exe' `
'.\IAR\Seat_Move_demo.ewp' -build rev_d -log all
```
也可运行工程根目录 `build_rev_d.bat`。可烧录文件为 `IAR/rev_d/Exe/Seat_Move_demo.hex`。
三路版本历史结果见`Docs/TEST_REPORT_2026-07-23.md`;四路升级与电机0~2重新实测见`Docs/TEST_REPORT_2026-07-24.md`。
@@ -0,0 +1,52 @@
# Seat_Move_demo 实机验证记录(2026-07-23)
## 构建产物
- 工具链:IAR Embedded Workbench 9.2,EWARM 9.40.1。
- 配置:`CYT4BFBCJE`、`CY_MCU_rev_d`、Cortex-M0+、Flash、`rev_d`。
- 最终构建:0 errors,0 warnings。
- HEX:`IAR/rev_d/Exe/Seat_Move_demo.hex`,120588 bytes。
- HEX SHA-256:`C3BA6044C6D0DA22B8285483298C2C4D2BC670EC4F043C33700FD39022C18C58`。
## CAN实测
- ZCAN设备:USBCANFD-200U设备0通道0。
- CAN4:500 kbit/s,Classic CAN,11-bit标准帧,DLC=8。
- 周期反馈:`0x301`约100 ms;三路`0x302`各约100 ms;三路`0x303`各约500 ms。
- `STOP_ALL`无条件生效,三路`0x302`均进入STOPPED且`0x301`活动掩码清零。
- 三路有效运动命令突发到达后,`0x301`活动掩码达到`0b111`,三路`0x302`均进入TIMED/RUNNING;启动仍由应用层按100 ms错峰调度。
- 同电机冲突回归:先发送电机0远端5 s/50%、sequence=4,再在150 ms后发送电机0近端500 ms/50%、sequence=5。系统反馈`0x301 = 13 03 05 00 00 00 00 00`:ACK=`CONFLICT`、RejectReason=`CONFLICT`、LastOpcode=`TIMED_DUTY`、LastSequence=`5`,第二条命令没有覆盖第一条活动任务。
- 冲突测试后发送`STOP_ALL sequence=6`(`09 13 06 00 00 00 00 00`),系统反馈`0x301 = 01 09 06 00 00 00 00 00`:ACK=`ACCEPTED`、活动掩码=`0`;`0x302`反馈进入STOPPED。
- 电机2最终回归:500 ms、50%远端命令返回COMPLETE/OK,Hall从0增加到14,位置反馈为0.79度。
- 电机0和电机1在正反向短脉冲中都未观察到Hall边沿。最终固件已改为:定时运动到期但没有任何Hall边沿时返回FAILED/STALL,不再误报OK。电机0回归帧的`0x302` Byte1为`0x49`(FAILED + STALL)。
## C-SPY退出安全修正
首次退出实测发现宏自检报错:
`Seat_Move_demo ERROR: DRVOFF/IN4 safe levels not latched.`
根因是退出宏混淆了GPIO `OUT_CLR`和`OUT_SET`地址:原实现会错误地清除P8.1/DRVOFF,并置高IN1..IN4。已修正为:
- P8.1/DRVOFF通过`GPIO_PRT8_OUT_SET`置高。
- P1.0、P5.4、P5.5、P8.0通过各自`OUT_CLR`清零。
- 停止TCPWM1计数器12、13、14、19、20。
- 把所有PWM输入HSIOM切回GPIO并配置为强输出。
- 退出前验证BREG3退出标志、5个TCPWM使能位、3组HSIOM字段及P1/P5/P8输出锁存值。
最终退出日志:
`Seat_Move_demo: C-SPY exit asserted DRVOFF and forced all PWM inputs low.`
退出后ZCAN接收计数在1.5 s观察窗口内保持`120931`不变,发送计数保持`41`、错误帧计数保持`0`,说明正常CAN应用不再继续运行;开发板不断电时输出保持硬件安全关闭。
## 尚需硬件核查
电机0和电机1无Hall边沿,且DRV8718没有上报全局驱动故障。建议按以下顺序检查:
1. 示波器检查P5.4/IN2和P5.5/IN3在500 ms命令期间是否有50% PWM。
2. 检查HB7-HB8、HB5-HB6的桥臂输出和电机供电/连接器。
3. 手动转动机构并检查P18.2、P10.1是否有Hall边沿及电平幅值。
4. 确认电机0/1没有处于机械端点或机构卡滞。
在上述两路硬件原因明确前,不执行阻塞式`CALIBRATE_RANGE`,也不执行Work Flash `SAVE_SHUTDOWN`验证。
@@ -0,0 +1,234 @@
# Seat_Move_demo 四电机 CAN 与硬件测试报告
测试日期:2026-07-24
测试对象:CYT4BFBCJE rev_d / Cortex-M0+ Flash / DRV8718S-Q1
CAN:CAN4,11-bit 标准帧,500 kbit/s,DLC=8
实接电机:Motor0(腿托伸缩)、Motor1(腿托上下)、Motor2(靠背前后)
未接电机:Motor3(座椅水平移动)
## 1. 测试结论
- Motor0~2 的 FAR、NEAR、Hall 反馈和三路并发控制通过。
- 本轮可确认三台电机均实际产生 Hall 运动,且 FAR 计数增加、NEAR 计数减少;“腿托伸出/收回、抬起/落下、靠背前倾/后仰”的整椅机械语义仍需现场人员按机构动作做一次目视签字确认,软件和 CAN 数据无法代替该方向目检。
- 三路并发时 `ActiveMask=0x7`,Motor0~2 均产生独立 Hall 边沿,Motor3 Hall 始终为 0,没有被误驱动。
- Motor3 的 `0x302`、`0x303` 周期反馈已存在;预估参数为 960 pulse、86 pulse/s。
- 发现并修正反馈发送突发拥塞:原实现每个 500 ms 边界可能同一轮发送 9 帧,但 CAN 仅配置 8 个 TX buffer,实测 `g_seatMoveFeedbackTxFailureCount=950`。修正为每个 1 ms 应用周期最多发送一帧后,计数保持 0,Motor3 诊断帧稳定可见。
- 最终 IAR 全量构建:0 Error,0 Warning。
- 最终结束 C-SPY 后,宏日志确认 DRVOFF 拉高并将全部 PWM 输入强制为低;ZCAN 接收计数保持 `91664 -> 91664`,目标不再发送周期帧。
## 2. 报文判读
### 0x301 系统状态
- Byte0 bits0..2:ACK;bits3..5:RejectReason。
- Byte1 bits0..3:LastOpcode;bits4..7:四路 ActiveMask。
- Byte2..3:最后命令 Sequence,小端。
### 0x302 电机任务状态
- Byte0:Motor SubID 0~3。
- Byte1=`0x13`:TIMED + RUNNING。
- Byte1=`0x28`:COMPLETE + OK。
- Byte2:方向、重试和位置有效等标志。
- Byte3..4:Sequence,小端;Byte5..6:物理量 x100;Byte7:占空比。
### 0x303 电机诊断
- Byte0:Motor SubID。
- Byte1:最近底层事件。
- Byte2..3:当前 Hall 计数。
- Byte4..5:最大 Hall 计数。
- Byte6..7:最大速度 pulse/s x100。
## 3. 单电机实测(修正前功能基线)
### 3.1 STOP_ALL 同步
发送:`0x300 09 13 21 00 00 00 00 00`
实际接收:`0x301 01 09 21 00 00 00 00 00`
预期接收:ACK=ACCEPTED,LastOpcode=STOP_ALL,ActiveMask=0,Sequence=`0x0021`。
判定:通过。
### 3.2 Motor0 腿托伸缩
FAR 发送:`0x300 03 14 22 00 F4 01 32 00`(500 ms,50%)
实际接收:
- `0x301 01 03 22 00 00 00 00 00`
- `0x302 00 28 00 22 00 2A 01 32`
- `0x303 00 00 0E 00 E0 01 98 21`
预期接收:Motor0 `COMPLETE + OK`,Sequence=`0x0022`,Duty=50%,Hall 增加。
实际结果:Hall `0 -> 14`,物理量反馈 `0x012A=298`(2.98 mm)。
判定:通过。
NEAR 发送:`0x300 03 18 23 00 F4 01 32 00`(500 ms,50%)
实际接收:
- `0x301 01 03 23 00 00 00 00 00`
- `0x302 00 28 00 23 00 15 00 32`
- `0x303 00 00 01 00 E0 01 98 21`
预期接收:Motor0 `COMPLETE + OK`,Hall 向零点方向减少。
实际结果:Hall `14 -> 1`,物理量反馈 `0x0015=21`(0.21 mm)。
判定:通过。
### 3.3 Motor1 腿托上下
FAR 发送:`0x300 03 15 24 00 F4 01 32 00`(500 ms,50%)
实际接收:
- 运行中 `0x301 01 23 24 00 00 00 00 00`(ActiveMask=`0x2`)
- `0x303 01 00 09 00 16 03 D4 17`
- 完成后 `0x301 01 03 24 00 00 00 00 00`
预期接收:仅 Motor1 活动,Hall 增加,完成后 ActiveMask 清零。
实际结果:Hall `0 -> 9`;MaxHall=`0x0316=790`,MaxSpeed=`0x17D4=6100`。
判定:通过。
NEAR 发送:`0x300 03 19 25 00 F4 01 32 00`(500 ms,50%)
实际接收:
- `0x301 01 23 25 00 00 00 00 00`
- `0x302 01 13 06 25 00 0B 00 32`
- `0x303 01 00 01 00 16 03 D4 17`
- 完成后 `0x301 01 03 25 00 00 00 00 00`
预期接收:Motor1 `TIMED + RUNNING`,NEAR 方向,Hall 减少。
实际结果:Hall `9 -> 1`,未影响其他电机状态。
判定:通过。
### 3.4 Motor2 靠背前后
FAR 发送:`0x300 03 16 26 00 F4 01 32 00`(500 ms,50%)
实际接收:
- 运行中 `0x301 01 43 26 00 00 00 00 00`(ActiveMask=`0x4`)
- `0x303 02 00 0B 00 36 06 28 23`
- 完成后 `0x301 01 03 26 00 00 00 00 00`
- 完成后 `0x303 02 00 10 00 36 06 28 23`
预期接收:仅 Motor2 活动,Hall 增加,完成后 ActiveMask 清零。
实际结果:Hall `0 -> 16`;MaxHall=`0x0636=1590`,MaxSpeed=`0x2328=9000`。
判定:通过。
NEAR 发送:`0x300 03 1A 27 00 F4 01 32 00`(500 ms,50%)
实际接收:
- `0x301 01 43 27 00 00 00 00 00`
- `0x302 02 13 06 27 00 11 00 32`
- 运行中 `0x303 02 00 03 00 36 06 28 23`
- 完成后 `0x303 02 00 00 00 36 06 28 23`
预期接收:Motor2 `TIMED + RUNNING`,NEAR 方向,Hall 减少至接近零。
实际结果:Hall `16 -> 0`。
判定:通过。
## 4. 三路并发与干涉消除实测
首次并发发送:
```text
0x300 03 14 28 00 B8 0B 32 00 Motor0 FAR, 3000 ms, 50%
0x300 03 15 29 00 B8 0B 32 00 Motor1 FAR, 3000 ms, 50%
0x300 03 16 2A 00 B8 0B 32 00 Motor2 FAR, 3000 ms, 50%
```
实际接收:
- `0x301 01 73 2A 00 00 00 00 00`,ActiveMask=`0x7`。
- `0x302 02 13 05 2A 00 6C 00 32`,Motor2 独立为 `TIMED + RUNNING`。
- `0x303 02 00 13 00 36 06 28 23`,Motor2 Hall=19。
停止发送:`0x300 09 13 2B 00 00 00 00 00`
实际接收:`0x301 01 09 2B 00 00 00 00 00`。
IAR 实际 Hall:Motor0=`83`,Motor1=`64`,Motor2=`90`,Motor3=`0`。
预期:Motor0~2同时活动且分别产生 Hall;Motor3不得被误驱动;STOP_ALL 后 ActiveMask=0。
判定:通过。
## 5. 反馈调度问题与修正后复测
### 5.1 问题
原 `SeatMove_ServiceFeedback()` 在同一个 1 ms 服务周期内连续处理全部 pending:一个 500 ms 周期边界可能生成 1 个 `0x301`、4 个 `0x302`、4 个 `0x303`,共 9 帧;但 `SEAT_MOVE_CAN_TX_BUFFER_COUNT=8`。实测:
- `g_seatMoveFeedbackTxFailureCount=950`
- Motor3 `0x303` 在 ZCAN 分类显示中存在延后
- `s_diagnosticPendingMask` 最终能清零,说明旧实现会重试,但存在不必要的拥塞与时延抖动
### 5.2 修正
`SeatMove_ServiceFeedback()` 改为每个 1 ms 应用任务周期最多成功发送一帧;保留 pending 位并在后续周期继续发送。这样 100 ms 状态组在约 5 ms 内发完,500 ms诊断组在约 9 ms 内发完,仍满足矩阵周期要求,同时不会瞬间占满8个 TX buffer。
### 5.3 修正后周期反馈
实际接收:
- `0x301 00 00 00 00 00 00 00 00`
- `0x302 03 00 00 00 00 00 00 32`
- `0x303 03 00 00 00 C0 03 98 21`
预期:Motor3 SubID存在,Hall=0,MaxHall=`0x03C0=960`,MaxSpeed=`0x2198=8600`。
实际:完全一致,且 `g_seatMoveFeedbackTxFailureCount=0`、`s_diagnosticPendingMask=0`。
判定:通过。
### 5.4 参数边界检查
发送了 30% 占空比的三路命令(低于代码定义的最小50%),实际收到:
`0x301 1A 03 33 00 00 00 00 00`
解析:ACK=REJECTED,RejectReason=RANGE,ActiveMask=0。
预期:非法占空比不得启动任何电机。
判定:通过。
### 5.5 最终有效并发烟雾测试
发送:
```text
0x300 03 14 35 00 C4 09 32 00 Motor0 FAR, 2500 ms, 50%
0x300 03 15 36 00 C4 09 32 00 Motor1 FAR, 2500 ms, 50%
0x300 03 16 37 00 C4 09 32 00 Motor2 FAR, 2500 ms, 50%
```
实际接收:`0x301 01 73 37 00 00 00 00 00`,ActiveMask=`0x7`。
停止发送:`0x300 09 13 38 00 00 00 00 00`。
实际接收:
- `0x301 01 09 38 00 00 00 00 00`
- `0x302 03 30 00 38 00 00 00 32`
- `0x303 03 00 00 00 C0 03 98 21`
最终 IAR Hall:Motor0=`69`,Motor1=`52`,Motor2=`75`,Motor3=`0`。
最终反馈发送失败计数:`0`。
预期:三路独立运行、Motor3保持关闭、STOP_ALL 后四路停止。
判定:通过。
## 6. Motor3 后续手动测试
Motor3 未接硬件,本次没有发送任何 Motor3 运动命令。接线后建议先执行:
```text
0x300 09 13 40 00 00 00 00 00 STOP_ALL / 同步
0x300 03 17 41 00 F4 01 32 00 Motor3 FAR, 500 ms, 50%
0x300 03 1B 42 00 F4 01 32 00 Motor3 NEAR, 500 ms, 50%
0x300 09 13 43 00 00 00 00 00 STOP_ALL
```
需核对:
- 运行中 `0x301` ActiveMask=`0x8`。
- `0x302` Byte0=`03`,状态在 RUNNING 后转为 COMPLETE/OK。
- `0x303` Byte0=`03`,FAR Hall增加,NEAR Hall减少。
- 实际机械方向是否与 FAR/NEAR 定义一致。
- 960 pulse、240 mm、86 pulse/s 目前均为估算,必须根据实车端点和速度重新标定。
## 7. 构建与烧录产物
- HEX:`IAR/rev_d/Exe/Seat_Move_demo.hex`
- 文件大小:121136 bytes
- SHA-256:`DD372103E4C0A4A40C8AF613AE5712BDFE848EC067EECE762F02A825EF1E66D0`
- IAR:Errors=0,Warnings=0
- C-SPY退出日志:`Seat_Move_demo: C-SPY exit asserted DRVOFF and forced all PWM inputs low.`
- 退出前后 ZCAN 接收计数:`91664 -> 91664`
File diff suppressed because it is too large Load Diff
+233
View File
@@ -0,0 +1,233 @@
#ifndef MOTOR_CONTROL_H
#define MOTOR_CONTROL_H
#include <stdbool.h>
#include <stdint.h>
#define MOTOR_API_SUCCESS (0u)
#define MOTOR_API_FAILURE (1u)
#define MOTOR_PHYSICAL_VALUE_SCALE (100ul)
#define MOTOR_MODE0_DEFAULT_MAX_HALL_COUNT (480ul)
#define MOTOR_MODE0_MAX_POSITION_MM_X100 (10200ul)
#define MOTOR_MODE1_DEFAULT_MAX_HALL_COUNT (790ul)
#define MOTOR_MODE1_MAX_ANGLE_DEG_X100 (8500ul)
#define MOTOR_MODE2_DEFAULT_MAX_HALL_COUNT (1590ul)
#define MOTOR_MODE2_MAX_ANGLE_DEG_X100 (9000ul)
#define MOTOR_MODE3_DEFAULT_MAX_HALL_COUNT (960ul)
#define MOTOR_MODE3_MAX_POSITION_MM_X100 (24000ul)
#define MOTOR_SPEED_VALUE_SCALE (100ul)
#define MOTOR_SPEED_MIN_DUTY_PERCENT (50u)
#define MOTOR_SPEED_MAX_DUTY_PERCENT (95u)
#define MOTOR_SPEED_DUTY_STEP_PERCENT (5u)
#define MOTOR_MODE0_MAX_SPEED_PPS_X100 (8600ul)
#define MOTOR_MODE1_MAX_SPEED_PPS_X100 (6100ul)
#define MOTOR_MODE2_MAX_SPEED_PPS_X100 (9000ul)
#define MOTOR_MODE3_MAX_SPEED_PPS_X100 (8600ul)
typedef enum
{
MOTOR_MODE_0_LINEAR = 0,
MOTOR_MODE_1_ANGLE,
MOTOR_MODE_2_ANGLE,
MOTOR_MODE_3_LINEAR_TRACK,
MOTOR_MODE_COUNT
} motor_mode_t;
typedef enum
{
MOTOR_DIRECTION_STOPPED = 0,
MOTOR_DIRECTION_TOWARD_FAR,
MOTOR_DIRECTION_TOWARD_NEAR
} motor_direction_t;
typedef enum
{
MOTOR_EVENT_NONE = 0,
MOTOR_EVENT_TARGET_REACHED,
MOTOR_EVENT_NEAR_ENDPOINT,
MOTOR_EVENT_FAR_ENDPOINT,
MOTOR_EVENT_STALL,
MOTOR_EVENT_TIMEOUT,
MOTOR_EVENT_ABORTED,
MOTOR_EVENT_DRIVER_FAULT,
MOTOR_EVENT_CALIBRATION_FAILED
} motor_event_t;
typedef enum
{
MOTOR_CALIBRATE_FIND_NEAR = 0,
MOTOR_CALIBRATE_FIND_FAR,
MOTOR_CALIBRATE_FIND_BOTH
} motor_calibration_action_t;
typedef struct
{
motor_mode_t mode;
bool positionValid;
motor_direction_t direction;
motor_event_t lastEvent;
uint32_t hallCount;
uint32_t maxHallCount;
uint32_t valueX100;
uint32_t maxValueX100;
uint32_t positionMmX100;
uint32_t angleDegX100;
uint32_t percentX100;
} motor_position_info_t;
typedef struct
{
bool reverseAtEndpoint;
bool reverseAtStall;
} motor_safety_options_t;
typedef struct
{
bool nearEndpointFound;
bool farEndpointFound;
uint32_t measuredMaxHallCount;
} motor_calibration_result_t;
typedef struct
{
motor_mode_t mode;
bool enabled;
uint32_t durationMs;
uint32_t travelValueX100;
uint32_t plannedHallPulses;
uint32_t targetPulsesPerSecondX100;
uint32_t actualPulsesPerSecondX100;
uint8_t initialDutyPercent;
uint8_t currentDutyPercent;
} motor_speed_info_t;
typedef struct
{
uint32_t positionHallCount;
uint32_t maxHallCount;
uint32_t maxSpeedPulsesPerSecondX100;
uint32_t rawRisingEdges;
bool positionValid;
} motor_persistent_state_t;
/* Hardware initialization. SystemInit() and global IRQ enable remain owned by main. */
uint8_t Motor_Init(void);
/* Immediate GPIO-only safe state, also valid before Motor_Init(). */
void Motor_ForceSafeOff(void);
/* The Hall timebase is a precise 2 ms hardware interrupt. */
uint32_t Motor_GetTimeMs(void);
void Motor_SetWatchdogServiceEnabled(bool enabled);
/*
* Physical values use fixed point x100: 102.00 mm is 10200 and
* 85.25 degrees is 8525. positionValid is false until an endpoint has
* been found after power-up.
*
* Ownership contract: call all control APIs and Motor_ServiceAll from one
* main-loop/owner context. They are not ISR-safe, thread-safe, or reentrant.
* The four motors may still run concurrently after sequential non-blocking
* start calls from that owner.
*/
uint8_t Motor_GetPositionInfo(motor_mode_t mode, motor_position_info_t *info);
/* Each mode has independent endpoint and stall reverse switches. */
uint8_t Motor_SetSafetyOptions(motor_mode_t mode,
const motor_safety_options_t *options);
/* Open-loop primitives used by the standardized application controller. */
uint8_t Motor_SetFixedDutyPercent(motor_mode_t mode, uint8_t dutyPercent);
uint8_t Motor_StartOpenLoop(motor_mode_t mode, motor_direction_t direction);
bool Motor_IsMoving(motor_mode_t mode);
/*
* Restore/store Hall-domain state. These calls are accepted only while the
* selected motor is stopped. max speed uses Hall rising edges/s x100.
*/
uint8_t Motor_ApplyPersistentState(motor_mode_t mode,
const motor_persistent_state_t *state);
uint8_t Motor_GetPersistentState(motor_mode_t mode,
motor_persistent_state_t *state);
uint8_t Motor_SetMaximumSpeedPpsX100(motor_mode_t mode,
uint32_t maxSpeedPpsX100);
/*
* Pair an owner-context command with cancellation published asynchronously.
* An acknowledge succeeds when no cancellation is pending, or when sequence
* exactly matches it. It fails if a newer cancellation has already arrived.
*/
uint8_t Motor_AcknowledgeCommand(motor_mode_t mode, uint16_t sequence);
bool Motor_IsCommandCurrent(motor_mode_t mode, uint16_t sequence);
/*
* Start is non-blocking and permits all four modes to move concurrently.
* Wait and MoveToValue return 0 only after the target is reached; endpoint,
* stall, invalid position, invalid target, and timeout return 1.
* Replacing a target still ahead in the same direction does not restart PWM
* or refresh safety/timeout clocks. A direction change waits at least 100 ms
* and until Hall has been quiet for 100 ms. Crossing a target stops PWM once;
* residual Hall pulses remain in the global position and may leave the final
* count beyond the target. No automatic reverse correction is performed.
* If a safety reverse is enabled, Wait remains blocked until that reverse has
* stopped at its next safety event or at the absolute operation timeout.
*/
uint8_t Motor_StartMoveToValue(motor_mode_t mode, uint32_t targetValueX100);
uint8_t Motor_WaitForMove(motor_mode_t mode);
uint8_t Motor_MoveToValue(motor_mode_t mode, uint32_t targetValueX100);
uint8_t Motor_StopMode(motor_mode_t mode);
/*
* Configure closed-loop speed from a planned travel amount and duration.
* durationMs is milliseconds. travelValueX100 is mm x100 for mode 0 and
* degrees x100 for modes 1/2, and mm x100 for mode 3. This configures speed
* only; use an existing move API to start motion. After each real PWM start
* the controller waits
* 500 ms, then evaluates a 500 ms rolling Hall window every 100 ms. A speed
* error outside +/-10 percent changes that motor's duty by one 5 percent step.
*/
uint8_t Motor_SetSpeedPlan(motor_mode_t mode,
uint32_t durationMs,
uint32_t travelValueX100);
uint8_t Motor_DisableSpeedControl(motor_mode_t mode);
uint8_t Motor_GetSpeedInfo(motor_mode_t mode, motor_speed_info_t *info);
/*
* Calibration is blocking for the selected mode; other active modes continue.
* A new range is committed only after validation. Failure restores the old
* range, preserves observed motion, and invalidates absolute position because
* physical travel cannot be rolled back. Each leg requires Hall coast
* tracking to settle before/after travel; quiet wait is bounded to five seconds.
*/
uint8_t Motor_Calibrate(motor_mode_t mode,
motor_calibration_action_t action,
motor_calibration_result_t *result);
/* Call continuously from the main loop; the ISR still kills PWM immediately. */
void Motor_ServiceAll(void);
/* ISR-safe immediate PWM kill; bridge Hi-Z is completed by Motor_ServiceAll. */
void Motor_RequestAbortMaskFromIsr(uint8_t motorMask, uint16_t sequence);
void Motor_RequestAbortAllFromIsr(uint16_t sequence);
/* Irreversible until reset; unlike STOP, no sequence acknowledge can clear it. */
void Motor_LatchSafetyInhibitFromIsr(uint16_t sequence);
void Motor_StopAll(void);
/* A latched driver fault requires a power cycle before motion is accepted. */
bool Motor_IsDriverFaultLatched(void);
uint8_t Motor0_GetPosition(motor_position_info_t *info);
uint8_t Motor1_GetAngle(motor_position_info_t *info);
uint8_t Motor2_GetAngle(motor_position_info_t *info);
uint8_t Motor3_GetPosition(motor_position_info_t *info);
uint8_t Motor0_MoveToPositionMmX100(uint32_t positionMmX100);
uint8_t Motor1_MoveToAngleDegX100(uint32_t angleDegX100);
uint8_t Motor2_MoveToAngleDegX100(uint32_t angleDegX100);
uint8_t Motor3_MoveToPositionMmX100(uint32_t positionMmX100);
uint8_t Motor0_SetSpeedPlan(uint32_t durationMs, uint32_t distanceMmX100);
uint8_t Motor1_SetSpeedPlan(uint32_t durationMs, uint32_t angleDegX100);
uint8_t Motor2_SetSpeedPlan(uint32_t durationMs, uint32_t angleDegX100);
uint8_t Motor3_SetSpeedPlan(uint32_t durationMs, uint32_t distanceMmX100);
#endif /* MOTOR_CONTROL_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,185 @@
/* C-SPY host-side safety hook for the unified seat controller. */
execUserSetup()
{
/* A new Download and Debug session releases a previous debug-exit latch. */
__writeMemory32(0x00000000, 0x4027100C, "Memory");
__message "Seat_Controller_demo: C-SPY setup cleared the debug-session latch.\n";
}
execUserExit()
{
__var value;
__var safe;
/* Retain safe-off across debug/XRES/WDT resets. */
__writeMemory32(0x4D4F5645, 0x4027100C, "Memory");
/* Assert DRVOFF=P8.1 before touching PWM or pin muxing. */
__writeMemory32(0x00000002, 0x40310408, "Memory");
/* Disable TCPWM1 group-0 counters IN1/IN2/IN3/IN4 and Hall timebase. */
value = __readMemory32(0x40580600, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580600, "Memory");
value = __readMemory32(0x40580680, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580680, "Memory");
value = __readMemory32(0x40580700, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580700, "Memory");
value = __readMemory32(0x40580980, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580980, "Memory");
value = __readMemory32(0x40580A00, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580A00, "Memory");
/* Disable fan TCPWM60 and heater TCPWM65 before changing their muxes. */
value = __readMemory32(0x40581E00, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40581E00, "Memory");
value = __readMemory32(0x40582080, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40582080, "Memory");
/* Force P11.1/P11.2 fan paths and P16.7 heater path to GPIO low. */
__writeMemory32(0x00000006, 0x40310584, "Memory");
__writeMemory32(0x00000080, 0x40310804, "Memory");
value = __readMemory32(0x403000B0, "Memory");
__writeMemory32(value & 0xFFE0E0FF, 0x403000B0, "Memory");
value = __readMemory32(0x40300104, "Memory");
__writeMemory32(value & 0xE0FFFFFF, 0x40300104, "Memory");
value = __readMemory32(0x403105C4, "Memory");
__writeMemory32((value & 0xFFFFF00F) | 0x00000660,
0x403105C4, "Memory");
value = __readMemory32(0x40310844, "Memory");
__writeMemory32((value & 0x0FFFFFFF) | 0x60000000,
0x40310844, "Memory");
__writeMemory32(0x00000006, 0x40310584, "Memory");
__writeMemory32(0x00000080, 0x40310804, "Memory");
/* Preload IN1=P1.0, IN2/IN3=P5.4/P5.5 and IN4=P8.0 low. */
__writeMemory32(0x00000001, 0x40310084, "Memory");
__writeMemory32(0x00000030, 0x40310284, "Memory");
__writeMemory32(0x00000001, 0x40310404, "Memory");
/* Select GPIO for every DRV8718 PWM input. */
value = __readMemory32(0x40300010, "Memory");
__writeMemory32(value & 0xFFFFFFE0, 0x40300010, "Memory");
value = __readMemory32(0x40300054, "Memory");
__writeMemory32(value & 0xFFFFE0E0, 0x40300054, "Memory");
value = __readMemory32(0x40300080, "Memory");
__writeMemory32(value & 0xFFFFE0E0, 0x40300080, "Memory");
/* Strong output, input buffer off (CY_GPIO_DM_STRONG_IN_OFF = 0x6). */
value = __readMemory32(0x403100C4, "Memory");
__writeMemory32((value & 0xFFFFFFF0) | 0x00000006,
0x403100C4, "Memory");
value = __readMemory32(0x403102C4, "Memory");
__writeMemory32((value & 0xFF00FFFF) | 0x00660000,
0x403102C4, "Memory");
value = __readMemory32(0x40310444, "Memory");
__writeMemory32((value & 0xFFFFFF00) | 0x00000066,
0x40310444, "Memory");
/* Full drive strength for the same output pins. */
value = __readMemory32(0x403100CC, "Memory");
__writeMemory32(value & 0xFFFCFFFF, 0x403100CC, "Memory");
value = __readMemory32(0x403102CC, "Memory");
__writeMemory32(value & 0xF0FFFFFF, 0x403102CC, "Memory");
value = __readMemory32(0x4031044C, "Memory");
__writeMemory32(value & 0xFFF0FFFF, 0x4031044C, "Memory");
/* Reassert the final physical levels after mux/drive configuration. */
__writeMemory32(0x00000001, 0x40310084, "Memory");
__writeMemory32(0x00000030, 0x40310284, "Memory");
__writeMemory32(0x00000001, 0x40310404, "Memory");
__writeMemory32(0x00000002, 0x40310408, "Memory");
/* Verify the retained latch, every PWM counter, every PWM HSIOM field and
every physical output latch before C-SPY releases the target. */
safe = 1;
value = __readMemory32(0x4027100C, "Memory");
if(value != 0x4D4F5645)
{
safe = 0;
}
value = __readMemory32(0x40580600, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580680, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580700, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580980, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580A00, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40581E00, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40582080, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40310580, "Memory");
if(value & 0x00000006)
{
safe = 0;
}
value = __readMemory32(0x40310800, "Memory");
if(value & 0x00000080)
{
safe = 0;
}
value = __readMemory32(0x40300010, "Memory");
if(value & 0x0000001F)
{
safe = 0;
}
value = __readMemory32(0x40300054, "Memory");
if(value & 0x00001F1F)
{
safe = 0;
}
value = __readMemory32(0x40300080, "Memory");
if(value & 0x00001F1F)
{
safe = 0;
}
value = __readMemory32(0x40310080, "Memory");
if(value & 0x00000001)
{
safe = 0;
}
value = __readMemory32(0x40310280, "Memory");
if(value & 0x00000030)
{
safe = 0;
}
value = __readMemory32(0x40310400, "Memory");
if((value & 0x00000003) != 0x00000002)
{
safe = 0;
}
if(safe == 0)
{
__message "Seat_Controller_demo ERROR: C-SPY safe-off register verification failed.\n";
}
else
{
__message "Seat_Controller_demo: C-SPY exit forced motor, fan and heater outputs low.\n";
}
}
@@ -0,0 +1,213 @@
/* C-SPY program-only safety hook used by the authorized backend flasher.
* It forces every output safe but deliberately clears the debug-session
* marker so --leave_target_running can boot normal firmware afterwards. */
execUserSetup()
{
__var value;
/* Make the pre-download target safe before the flash loader or the new
application can run. The post-session block below repeats and verifies
the same invariants when C-SPY reaches the normal exit hook. */
__writeMemory32(0x00000002, 0x40310408, "Memory");
value = __readMemory32(0x40580600, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580600, "Memory");
value = __readMemory32(0x40580680, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580680, "Memory");
value = __readMemory32(0x40580700, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580700, "Memory");
value = __readMemory32(0x40580980, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580980, "Memory");
value = __readMemory32(0x40580A00, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580A00, "Memory");
value = __readMemory32(0x40581E00, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40581E00, "Memory");
value = __readMemory32(0x40582080, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40582080, "Memory");
__writeMemory32(0x00000001, 0x40310084, "Memory");
__writeMemory32(0x00000030, 0x40310284, "Memory");
__writeMemory32(0x00000001, 0x40310404, "Memory");
__writeMemory32(0x00000006, 0x40310584, "Memory");
__writeMemory32(0x00000080, 0x40310804, "Memory");
/* A new Download and Debug session releases a previous debug-exit latch. */
__writeMemory32(0x00000000, 0x4027100C, "Memory");
__message "Seat_Controller_demo: C-SPY setup forced outputs safe and cleared the debug-session latch.\n";
}
execUserExit()
{
__var value;
__var safe;
/* This is a program-only session, not an interactive debugger detach. */
__writeMemory32(0x00000000, 0x4027100C, "Memory");
/* Assert DRVOFF=P8.1 before touching PWM or pin muxing. */
__writeMemory32(0x00000002, 0x40310408, "Memory");
/* Disable TCPWM1 group-0 counters IN1/IN2/IN3/IN4 and Hall timebase. */
value = __readMemory32(0x40580600, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580600, "Memory");
value = __readMemory32(0x40580680, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580680, "Memory");
value = __readMemory32(0x40580700, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580700, "Memory");
value = __readMemory32(0x40580980, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580980, "Memory");
value = __readMemory32(0x40580A00, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40580A00, "Memory");
/* Disable fan TCPWM60 and heater TCPWM65 before changing their muxes. */
value = __readMemory32(0x40581E00, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40581E00, "Memory");
value = __readMemory32(0x40582080, "Memory");
__writeMemory32(value & 0x7FFFFFFF, 0x40582080, "Memory");
/* Force P11.1/P11.2 fan paths and P16.7 heater path to GPIO low. */
__writeMemory32(0x00000006, 0x40310584, "Memory");
__writeMemory32(0x00000080, 0x40310804, "Memory");
value = __readMemory32(0x403000B0, "Memory");
__writeMemory32(value & 0xFFE0E0FF, 0x403000B0, "Memory");
value = __readMemory32(0x40300104, "Memory");
__writeMemory32(value & 0xE0FFFFFF, 0x40300104, "Memory");
value = __readMemory32(0x403105C4, "Memory");
__writeMemory32((value & 0xFFFFF00F) | 0x00000660,
0x403105C4, "Memory");
value = __readMemory32(0x40310844, "Memory");
__writeMemory32((value & 0x0FFFFFFF) | 0x60000000,
0x40310844, "Memory");
__writeMemory32(0x00000006, 0x40310584, "Memory");
__writeMemory32(0x00000080, 0x40310804, "Memory");
/* Preload IN1=P1.0, IN2/IN3=P5.4/P5.5 and IN4=P8.0 low. */
__writeMemory32(0x00000001, 0x40310084, "Memory");
__writeMemory32(0x00000030, 0x40310284, "Memory");
__writeMemory32(0x00000001, 0x40310404, "Memory");
/* Select GPIO for every DRV8718 PWM input. */
value = __readMemory32(0x40300010, "Memory");
__writeMemory32(value & 0xFFFFFFE0, 0x40300010, "Memory");
value = __readMemory32(0x40300054, "Memory");
__writeMemory32(value & 0xFFFFE0E0, 0x40300054, "Memory");
value = __readMemory32(0x40300080, "Memory");
__writeMemory32(value & 0xFFFFE0E0, 0x40300080, "Memory");
/* Strong output, input buffer off (CY_GPIO_DM_STRONG_IN_OFF = 0x6). */
value = __readMemory32(0x403100C4, "Memory");
__writeMemory32((value & 0xFFFFFFF0) | 0x00000006,
0x403100C4, "Memory");
value = __readMemory32(0x403102C4, "Memory");
__writeMemory32((value & 0xFF00FFFF) | 0x00660000,
0x403102C4, "Memory");
value = __readMemory32(0x40310444, "Memory");
__writeMemory32((value & 0xFFFFFF00) | 0x00000066,
0x40310444, "Memory");
/* Full drive strength for the same output pins. */
value = __readMemory32(0x403100CC, "Memory");
__writeMemory32(value & 0xFFFCFFFF, 0x403100CC, "Memory");
value = __readMemory32(0x403102CC, "Memory");
__writeMemory32(value & 0xF0FFFFFF, 0x403102CC, "Memory");
value = __readMemory32(0x4031044C, "Memory");
__writeMemory32(value & 0xFFF0FFFF, 0x4031044C, "Memory");
/* Reassert the final physical levels after mux/drive configuration. */
__writeMemory32(0x00000001, 0x40310084, "Memory");
__writeMemory32(0x00000030, 0x40310284, "Memory");
__writeMemory32(0x00000001, 0x40310404, "Memory");
__writeMemory32(0x00000002, 0x40310408, "Memory");
/* Verify the retained latch, every PWM counter, every PWM HSIOM field and
every physical output latch before C-SPY releases the target. */
safe = 1;
value = __readMemory32(0x4027100C, "Memory");
if(value != 0x00000000)
{
safe = 0;
}
value = __readMemory32(0x40580600, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580680, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580700, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580980, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40580A00, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40581E00, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40582080, "Memory");
if(value & 0x80000000)
{
safe = 0;
}
value = __readMemory32(0x40310580, "Memory");
if(value & 0x00000006)
{
safe = 0;
}
value = __readMemory32(0x40310800, "Memory");
if(value & 0x00000080)
{
safe = 0;
}
value = __readMemory32(0x40300010, "Memory");
if(value & 0x0000001F)
{
safe = 0;
}
value = __readMemory32(0x40300054, "Memory");
if(value & 0x00001F1F)
{
safe = 0;
}
value = __readMemory32(0x40300080, "Memory");
if(value & 0x00001F1F)
{
safe = 0;
}
value = __readMemory32(0x40310080, "Memory");
if(value & 0x00000001)
{
safe = 0;
}
value = __readMemory32(0x40310280, "Memory");
if(value & 0x00000030)
{
safe = 0;
}
value = __readMemory32(0x40310400, "Memory");
if((value & 0x00000003) != 0x00000002)
{
safe = 0;
}
if(safe == 0)
{
__message "Seat_Controller_demo ERROR: C-SPY safe-off register verification failed.\n";
}
else
{
__message "Seat_Controller_demo: program-only exit forced all outputs low and released the debug latch.\n";
}
}
File diff suppressed because it is too large Load Diff