feat: add seat firmware workbench and OTA delivery

This commit is contained in:
vee
2026-09-16 17:55:30 +08:00
parent 3eeca2ea75
commit ab8e43050c
70 changed files with 22594 additions and 316 deletions
@@ -0,0 +1,79 @@
/***************************************************************************//**
* \file main_cm0plus.c
*
* CYT4BFBCJE rev_d CM0+ entry point for the passenger-seat timed-only ECU.
*******************************************************************************/
#include "cy_project.h"
#include "cy_device_headers.h"
#include "bsp_seat_backrest_heater.h"
#include "bsp_seat_fan.h"
#include "motor_control.h"
#include "safety_manager.h"
#include "seat_move_config.h"
#include "seat_timed_application.h"
volatile uint32_t g_seatTimedResetReason = 0u;
volatile uint32_t g_seatTimedInitResult = MOTOR_API_FAILURE;
static void SeatTimed_HoldSafeOff(void)
{
for(;;)
{
Motor_ForceSafeOff();
(void)BspSeatBackrestHeater_ForceOff();
(void)BspSeatFan_ForceOff();
SafetyManager_KickWatchdog();
Cy_SysTick_DelayInUs(SEAT_MOVE_TASK_PERIOD_US);
}
}
int main(void)
{
(void)BspSeatBackrestHeater_ForceOff();
(void)BspSeatFan_ForceOff();
SystemInit();
Motor_ForceSafeOff();
if(!BspSeatBackrestHeater_ForceOff() || !BspSeatFan_ForceOff())
{
SeatTimed_HoldSafeOff();
}
g_seatTimedResetReason = Cy_SysReset_GetResetReason();
Cy_SysReset_ClearAllResetReasons();
SafetyManager_Init(g_seatTimedResetReason);
if(SafetyManager_IsInhibited())
{
SeatTimed_HoldSafeOff();
}
__enable_irq();
g_seatTimedInitResult = Motor_Init();
if(g_seatTimedInitResult != MOTOR_API_SUCCESS)
{
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
SeatTimed_HoldSafeOff();
}
Motor_SetWatchdogServiceEnabled(true);
if(!SeatTimedApplication_Init())
{
SeatTimed_HoldSafeOff();
}
for(;;)
{
SeatTimedApplication_Service();
if(SafetyManager_IsInhibited())
{
Motor_ForceSafeOff();
(void)BspSeatBackrestHeater_ForceOff();
(void)BspSeatFan_ForceOff();
}
SafetyManager_KickWatchdog();
Cy_SysTick_DelayInUs(SEAT_MOVE_TASK_PERIOD_US);
}
}
/* [] END OF FILE */
@@ -0,0 +1,332 @@
#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;
static bool hilWatchdogResetArmed = false;
static bool hilWatchdogRecovered = 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);
}
static bool SafetyManager_WaitForWatchdogState(bool enabled)
{
uint32_t remaining = 200ul;
uint32_t expected = enabled ? 1ul : 0ul;
/* WDT ENABLE is an asynchronous request bit; ENABLED is the synchronized
* LF-clock state. Configuration writes made before disable completes are
* ignored by the hardware. */
while((WDT->unCTL.stcField.u1ENABLED != expected) && (remaining > 0ul))
{
Cy_SysTick_DelayInUs(1u);
remaining--;
}
return (WDT->unCTL.stcField.u1ENABLED == expected);
}
static void SafetyManager_ClearHilWatchdogCredential(void)
{
BACKUP->unBREG[SEAT_MOVE_HIL_WDT_MAGIC_BREG_INDEX].u32Register = 0u;
BACKUP->unBREG[SEAT_MOVE_HIL_WDT_TOKEN_BREG_INDEX].u32Register = 0u;
BACKUP->unBREG[SEAT_MOVE_HIL_WDT_CHECK_BREG_INDEX].u32Register = 0u;
__DSB();
}
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 watchdogStopped;
bool debuggerAttached;
uint32_t marker;
uint32_t hilMagic;
uint32_t hilToken;
uint32_t hilCheck;
bool authorizedHilReset;
bool markerOk = true;
/* A watchdog instance is retained across its own reset on this device.
* Stop it before reading credentials or initializing peripherals; the
* 100 ms bench timeout otherwise permits a reset loop. */
Cy_WDT_Disable();
watchdogStopped = SafetyManager_WaitForWatchdogState(false);
debuggerAttached = SafetyManager_DebuggerAttached();
marker = SafetyManager_ReadDebugMarker();
hilMagic = BACKUP->unBREG[
SEAT_MOVE_HIL_WDT_MAGIC_BREG_INDEX].u32Register;
hilToken = BACKUP->unBREG[
SEAT_MOVE_HIL_WDT_TOKEN_BREG_INDEX].u32Register;
hilCheck = BACKUP->unBREG[
SEAT_MOVE_HIL_WDT_CHECK_BREG_INDEX].u32Register;
authorizedHilReset =
(hilMagic == SEAT_MOVE_HIL_WDT_MARKER) &&
(hilCheck == (hilMagic ^ hilToken ^ 0xFFFFFFFFu)) &&
(((resetReason & watchdogMask) != 0ul) ||
((resetReason & CY_SYSRESET_SOFT) != 0ul));
latchedFault = SAFETY_FAULT_NONE;
watchdogInitialized = false;
hilWatchdogResetArmed = false;
watchdogReset = ((resetReason & watchdogMask) != 0ul) ||
authorizedHilReset;
hilWatchdogRecovered = authorizedHilReset;
if((resetReason & CY_SYSRESET_PORVDDD) != 0ul)
{
markerOk = SafetyManager_WriteDebugMarker(0u);
marker = SafetyManager_ReadDebugMarker();
SafetyManager_ClearHilWatchdogCredential();
hilWatchdogRecovered = false;
}
else if(hilWatchdogRecovered)
{
/* Consume before CAN comes online: transport retries cannot turn one
* authenticated reset into a second physical reset. */
SafetyManager_ClearHilWatchdogCredential();
}
else if(hilMagic == SEAT_MOVE_HIL_WDT_MARKER)
{
/* XRES/programming without a valid reset reason is not completion. */
SafetyManager_ClearHilWatchdogCredential();
}
#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 && !hilWatchdogRecovered)
{
latchedFault = SAFETY_FAULT_BOOT_RECOVERY;
}
else if(debuggerAttached &&
!SafetyManager_WriteDebugMarker(SEAT_MOVE_DEBUG_ACTIVE_MAGIC))
{
latchedFault = SAFETY_FAULT_DEBUG_DETACH;
}
/* The system-WDT configuration register is retained across some reset
* paths and AUTO_SERVICE is not reset by Cy_WDT_Init(). If that bit is
* left enabled, stopping the two software service paths can never prove a
* real watchdog reset because hardware services the counter at WARN_LIMIT.
* Configure only while stopped, and make software the sole WDT owner. */
if(!watchdogStopped)
{
latchedFault = SAFETY_FAULT_DRIVER;
return;
}
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_SetAutoService(CY_WDT_DISABLE);
Cy_WDT_SetDebugRun(CY_WDT_ENABLE);
Cy_WDT_Lock();
if((WDT->unUPPER_LIMIT.stcField.u32UPPER_LIMIT !=
SEAT_MOVE_WDT_TIMEOUT_TICKS) ||
(WDT->unCONFIG.stcField.u1UPPER_ACTION !=
(uint32_t)CY_WDT_LOW_UPP_ACTION_RESET) ||
(WDT->unCONFIG.stcField.u1AUTO_SERVICE !=
(uint32_t)CY_WDT_DISABLE))
{
latchedFault = SAFETY_FAULT_DRIVER;
return;
}
Cy_WDT_Enable();
if(!SafetyManager_WaitForWatchdogState(true))
{
latchedFault = SAFETY_FAULT_DRIVER;
return;
}
Cy_WDT_ClearWatchdog();
Cy_SysTick_DelayInUs(1000u);
if(Cy_WDT_GetCount() == 0ul)
{
latchedFault = SAFETY_FAULT_DRIVER;
return;
}
watchdogInitialized = true;
SafetyManager_KickWatchdog();
}
void SafetyManager_KickWatchdog(void)
{
if((false != watchdogInitialized) && !hilWatchdogResetArmed)
{
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;
}
bool SafetyManager_TriggerHilWatchdogReset(uint16_t token)
{
uint32_t check;
if(!watchdogInitialized || hilWatchdogResetArmed)
{
return false;
}
BACKUP->unBREG[SEAT_MOVE_HIL_WDT_MAGIC_BREG_INDEX].u32Register =
SEAT_MOVE_HIL_WDT_MARKER;
BACKUP->unBREG[SEAT_MOVE_HIL_WDT_TOKEN_BREG_INDEX].u32Register = token;
check = SEAT_MOVE_HIL_WDT_MARKER ^ (uint32_t)token ^ 0xFFFFFFFFu;
BACKUP->unBREG[SEAT_MOVE_HIL_WDT_CHECK_BREG_INDEX].u32Register = check;
__DSB();
if((BACKUP->unBREG[
SEAT_MOVE_HIL_WDT_MAGIC_BREG_INDEX].u32Register !=
SEAT_MOVE_HIL_WDT_MARKER) ||
(BACKUP->unBREG[
SEAT_MOVE_HIL_WDT_CHECK_BREG_INDEX].u32Register != check))
{
SafetyManager_ClearHilWatchdogCredential();
return false;
}
/* Long-running motor calibration/coast loops normally service WDT
* directly so they remain safe even while the application loop is busy.
* The armed HIL case must suspend both service paths or a real watchdog
* reset can never occur. Startup re-enables this after the reset. */
Motor_SetWatchdogServiceEnabled(false);
Cy_WDT_ClearWatchdog();
hilWatchdogResetArmed = true;
return true;
}
void SafetyManager_CommitHilWatchdogReset(void)
{
if(!hilWatchdogResetArmed)
{
return;
}
/* This bench's connected CMSIS-DAP can suppress the WDT reset output even
* after C-SPY detaches. First prove physical WDT expiry, then use the
* reference project's authenticated processor-reset fallback. */
while(Cy_WDT_GetCount() < SEAT_MOVE_WDT_TIMEOUT_TICKS)
{
__NOP();
}
__DSB();
NVIC_SystemReset();
for(;;)
{
__NOP();
}
}
bool SafetyManager_WasHilWatchdogRecovered(void)
{
return hilWatchdogRecovered;
}
@@ -0,0 +1,30 @@
#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);
bool SafetyManager_TriggerHilWatchdogReset(uint16_t token);
void SafetyManager_CommitHilWatchdogReset(void);
bool SafetyManager_WasHilWatchdogRecovered(void);
#endif /* SAFETY_MANAGER_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,154 @@
#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_RANGE_CALIBRATION,
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;
uint32_t hallSpeedPpsX100;
uint32_t positionPercentX100;
uint32_t positionMmX100;
uint32_t horizontalMmX100;
uint32_t verticalMmX100;
uint32_t angleDegX100;
uint8_t dutyPercent;
uint8_t retryCount;
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_MoveAbsoluteNormalized(motor_mode_t mode,
uint32_t targetPercentX100,
uint32_t timeoutMs,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveRelativeNormalized(motor_mode_t mode,
int32_t deltaPercentX100,
uint32_t timeoutMs,
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);
/* Keep the same speed plan while allowing a bounded scheduler/settling grace
* before the manager reports TIMEOUT. */
bool SeatMotor_MoveAbsoluteInTimeWithGrace(motor_mode_t mode,
uint32_t targetValueX100,
uint32_t durationMs,
uint32_t deadlineGraceMs,
uint32_t commandSequence);
/* Hall-coordinate variants used by firmware-owned synchronized trajectories.
* The target is checked against the live calibrated MaxHall before conversion
* to the motor's physical value. */
bool SeatMotor_MoveAbsoluteHallDuty(motor_mode_t mode,
uint32_t targetHallCount,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveAbsoluteHallInTime(motor_mode_t mode,
uint32_t targetHallCount,
uint32_t durationMs,
uint32_t commandSequence);
bool SeatMotor_MoveAbsoluteHallInTimeWithGrace(motor_mode_t mode,
uint32_t targetHallCount,
uint32_t durationMs,
uint32_t deadlineGraceMs,
uint32_t commandSequence);
/* Range calibration is interruptible by Motor_RequestAbortAllFromIsr(). */
bool SeatMotor_CalibrateRange(motor_mode_t mode,
uint32_t legTimeoutMs,
uint32_t commandSequence);
/* Re-establish zero at the known near endpoint while retaining the confirmed
* default MaxHall. This performs one near-seek only, not a full-range test. */
bool SeatMotor_HomeKnownRange(motor_mode_t mode,
uint32_t legTimeoutMs,
uint32_t commandSequence);
bool SeatMotor_StartMaximumSpeedCalibration(motor_mode_t mode,
uint32_t commandSequence);
bool SeatMotorManager_ClearFault(motor_mode_t mode);
/* Optional endpoint-based position recovery primitive. */
bool SeatMotor_RecoverPosition(motor_mode_t mode, uint32_t savedHallCount);
#endif /* SEAT_MOTOR_MANAGER_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
#ifndef SEAT_TIMED_APPLICATION_H
#define SEAT_TIMED_APPLICATION_H
#include <stdbool.h>
#include <stdint.h>
bool SeatTimedApplication_Init(void);
void SeatTimedApplication_Service(void);
void SeatTimedApplication_StopAll(uint16_t sequence);
#endif /* SEAT_TIMED_APPLICATION_H */
@@ -0,0 +1,687 @@
#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_bspSeatBackrestHeaterStartFailStage = 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)
{
g_bspSeatBackrestHeaterStartFailStage = 0u;
if(!BspSeatBackrestHeater_SetDutyPercent(dutyPercent))
{
g_bspSeatBackrestHeaterStartFailStage = 1u;
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)
{
g_bspSeatBackrestHeaterStartFailStage = 2u;
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)
{
g_bspSeatBackrestHeaterStartFailStage = 3u;
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,62 @@
#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);
/*
* Start-path diagnostic for bench/HIL fault isolation.
* 0 = no start failure, 1 = duty/compare write, 2 = counter did not run,
* 3 = P16.7 HSIOM switch/readback.
*/
extern volatile uint32_t g_bspSeatBackrestHeaterStartFailStage;
/* 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,332 @@
#include "bsp_seat_current_adc.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "seat_current_adc_config.h"
#define SEAT_CURRENT_ADC_DIV_ROUND_UP(a, b) (((a) + (b) - 1u) / (b))
typedef struct
{
volatile stc_GPIO_PRT_t *gpioPort;
uint32_t gpioPin;
en_hsiom_sel_t gpioHsiom;
volatile stc_PASS_SAR_CH_t *adcChannel;
cy_en_adc_pin_address_t pinAddress;
} bsp_seat_current_adc_profile_t;
static const bsp_seat_current_adc_profile_t s_profiles[] =
{
{
SEAT_CURRENT_M0_PORT,
SEAT_CURRENT_M0_PIN,
SEAT_CURRENT_M0_GPIO_HSIOM,
SEAT_CURRENT_M0_CHANNEL,
SEAT_CURRENT_M0_PIN_ADDRESS,
},
{
SEAT_CURRENT_M1_PORT,
SEAT_CURRENT_M1_PIN,
SEAT_CURRENT_M1_GPIO_HSIOM,
SEAT_CURRENT_M1_CHANNEL,
SEAT_CURRENT_M1_PIN_ADDRESS,
},
{
SEAT_CURRENT_M2_PORT,
SEAT_CURRENT_M2_PIN,
SEAT_CURRENT_M2_GPIO_HSIOM,
SEAT_CURRENT_M2_CHANNEL,
SEAT_CURRENT_M2_PIN_ADDRESS,
},
{
SEAT_CURRENT_M3_PORT,
SEAT_CURRENT_M3_PIN,
SEAT_CURRENT_M3_GPIO_HSIOM,
SEAT_CURRENT_M3_CHANNEL,
SEAT_CURRENT_M3_PIN_ADDRESS,
},
{
SEAT_CURRENT_HEATER_PORT,
SEAT_CURRENT_HEATER_PIN,
SEAT_CURRENT_HEATER_GPIO_HSIOM,
SEAT_CURRENT_HEATER_CHANNEL,
SEAT_CURRENT_HEATER_PIN_ADDRESS,
},
{
SEAT_CURRENT_FAN_PORT,
SEAT_CURRENT_FAN_PIN,
SEAT_CURRENT_FAN_GPIO_HSIOM,
SEAT_CURRENT_FAN_CHANNEL,
SEAT_CURRENT_FAN_PIN_ADDRESS,
},
};
typedef char bsp_seat_current_adc_profile_count_must_match[
((sizeof(s_profiles) / sizeof(s_profiles[0])) ==
(uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT) ? 1 : -1];
static bool s_initialized = false;
static bool s_sampleActive = false;
static uint16_t s_lastRaw[BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT];
static bool s_lastRawValid[BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT];
static bool BspSeatCurrentAdc_ConfigClock(uint32_t *actualClockHz);
static bool BspSeatCurrentAdc_WaitDone(
volatile stc_PASS_SAR_CH_t *channel);
static void BspSeatCurrentAdc_ClearStatus(
volatile stc_PASS_SAR_CH_t *channel);
bool BspSeatCurrentAdc_Init(void)
{
uint32_t actualClockHz;
uint32_t samplingCycles;
uint32_t index;
cy_stc_gpio_pin_config_t pinConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_ANALOG,
.hsiom = HSIOM_SEL_GPIO,
.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 = CY_ADC_PIN_ADDRESS_AN0,
.portAddress = SEAT_CURRENT_ADC_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_CURRENT_ADC_FULL_SCALE,
.mask.grpDone = true,
.mask.grpCancelled = false,
.mask.grpOverflow = false,
.mask.chRange = false,
.mask.chPulse = false,
.mask.chOverflow = false,
};
if(s_initialized)
{
return true;
}
for(index = 0u;
index < (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT;
index++)
{
pinConfig.hsiom = s_profiles[index].gpioHsiom;
if(Cy_GPIO_Pin_Init(s_profiles[index].gpioPort,
s_profiles[index].gpioPin,
&pinConfig) != CY_GPIO_SUCCESS)
{
return false;
}
s_lastRaw[index] = 0u;
s_lastRawValid[index] = false;
}
if(!BspSeatCurrentAdc_ConfigClock(&actualClockHz))
{
return false;
}
samplingCycles = (uint32_t)SEAT_CURRENT_ADC_DIV_ROUND_UP(
(SEAT_CURRENT_ADC_SAMPLE_TIME_MIN_NS * (uint64_t)actualClockHz),
1000000000ull);
if(samplingCycles == 0u)
{
samplingCycles = 1u;
}
channelConfig.sampleTime = (uint16_t)samplingCycles;
Cy_Adc_DeInit(SEAT_CURRENT_ADC_INSTANCE);
if(Cy_Adc_Init(SEAT_CURRENT_ADC_INSTANCE,
&adcConfig) != CY_ADC_SUCCESS)
{
return false;
}
for(index = 0u;
index < (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT;
index++)
{
channelConfig.pinAddress = s_profiles[index].pinAddress;
if(Cy_Adc_Channel_Init(s_profiles[index].adcChannel,
&channelConfig) != CY_ADC_SUCCESS)
{
Cy_Adc_DeInit(SEAT_CURRENT_ADC_INSTANCE);
return false;
}
Cy_Adc_Channel_Enable(s_profiles[index].adcChannel);
}
s_initialized = true;
return true;
}
bool BspSeatCurrentAdc_Sample(bsp_seat_current_adc_signal_t signal,
uint16_t *raw)
{
uint32_t index = (uint32_t)signal;
uint16_t sample = 0u;
cy_stc_adc_ch_status_t resultStatus = { false };
volatile stc_PASS_SAR_CH_t *channel;
bool success = false;
if(!s_initialized || s_sampleActive ||
(index >= (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT))
{
return false;
}
s_sampleActive = true;
channel = s_profiles[index].adcChannel;
BspSeatCurrentAdc_ClearStatus(channel);
Cy_Adc_Channel_SoftwareTrigger(channel);
if(BspSeatCurrentAdc_WaitDone(channel) &&
(Cy_Adc_Channel_GetResult(channel,
&sample,
&resultStatus) == CY_ADC_SUCCESS) &&
resultStatus.valid)
{
sample = (uint16_t)(sample & SEAT_CURRENT_ADC_FULL_SCALE);
s_lastRaw[index] = sample;
s_lastRawValid[index] = true;
if(raw != NULL)
{
*raw = sample;
}
success = true;
}
BspSeatCurrentAdc_ClearStatus(channel);
s_sampleActive = false;
return success;
}
bool BspSeatCurrentAdc_GetLastRaw(bsp_seat_current_adc_signal_t signal,
uint16_t *raw)
{
uint32_t index = (uint32_t)signal;
if((raw == NULL) ||
(index >= (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT) ||
!s_lastRawValid[index])
{
return false;
}
*raw = s_lastRaw[index];
return true;
}
static bool BspSeatCurrentAdc_ConfigClock(uint32_t *actualClockHz)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup = Cy_SysClk_GetClockGroup(SEAT_CURRENT_ADC_PCLK);
if((actualClockHz == NULL) ||
(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS))
{
return false;
}
divider = SEAT_CURRENT_ADC_DIV_ROUND_UP(
periClockHz, SEAT_CURRENT_ADC_OPERATION_FREQ_MAX_HZ);
if((divider == 0u) || (divider > 65536u))
{
return false;
}
if(Cy_SysClk_PeriphDisableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(
SEAT_CURRENT_ADC_PCLK,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphEnableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
*actualClockHz = periClockHz / divider;
return true;
}
static bool BspSeatCurrentAdc_WaitDone(
volatile stc_PASS_SAR_CH_t *channel)
{
uint32_t timeout = SEAT_CURRENT_ADC_POLL_TIMEOUT;
cy_stc_adc_interrupt_source_t status = { false };
do
{
(void)Cy_Adc_Channel_GetInterruptStatus(channel, &status);
if(status.grpDone)
{
return true;
}
timeout--;
} while(timeout > 0u);
return false;
}
static void BspSeatCurrentAdc_ClearStatus(
volatile stc_PASS_SAR_CH_t *channel)
{
cy_stc_adc_interrupt_source_t clearSource =
{
.grpDone = true,
.grpCancelled = true,
.grpOverflow = true,
.chRange = false,
.chPulse = false,
.chOverflow = true,
};
(void)Cy_Adc_Channel_ClearInterruptStatus(channel, &clearSource);
}
@@ -0,0 +1,27 @@
#ifndef BSP_SEAT_CURRENT_ADC_H
#define BSP_SEAT_CURRENT_ADC_H
#include <stdbool.h>
#include <stdint.h>
typedef enum
{
BSP_SEAT_CURRENT_ADC_MOTOR0 = 0,
BSP_SEAT_CURRENT_ADC_MOTOR1,
BSP_SEAT_CURRENT_ADC_MOTOR2,
BSP_SEAT_CURRENT_ADC_MOTOR3,
BSP_SEAT_CURRENT_ADC_HEATER,
BSP_SEAT_CURRENT_ADC_FAN,
BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT
} bsp_seat_current_adc_signal_t;
/* Initializes PASS0 SAR1 once and enables every shared seat-current channel. */
bool BspSeatCurrentAdc_Init(void);
/* Main-loop polling API. It must not be called from an ISR. */
bool BspSeatCurrentAdc_Sample(bsp_seat_current_adc_signal_t signal,
uint16_t *raw);
bool BspSeatCurrentAdc_GetLastRaw(bsp_seat_current_adc_signal_t signal,
uint16_t *raw);
#endif /* BSP_SEAT_CURRENT_ADC_H */
@@ -0,0 +1,659 @@
#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;
volatile uint32_t g_bspSeatFanStartFailStage = 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)
{
g_bspSeatFanStartFailStage = 0u;
if(!BspSeatFan_SetDutyPercent(dutyPercent))
{
g_bspSeatFanStartFailStage = 1u;
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)
{
g_bspSeatFanStartFailStage = 2u;
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)
{
g_bspSeatFanStartFailStage = 3u;
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);
}
@@ -0,0 +1,52 @@
#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);
/* Read-only HIL diagnostic: 0=none, 1=compare, 2=counter, 3=HSIOM. */
extern volatile uint32_t g_bspSeatFanStartFailStage;
/* 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 */
@@ -0,0 +1,400 @@
#include "bsp_seat_move_can.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "motor_control.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(
SEAT_MOVE_CAN_COMMAND_ID,
SEAT_MOVE_CAN_COMMAND_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;
if((message->idConfig.identifier == SEAT_MOVE_CAN_COMMAND_ID) &&
(message->dataConfig.dataLengthCode == SEAT_MOVE_CAN_DLC) &&
(((uint8_t)(message->dataConfig.data[0] & 0xFFu)) == 0x09u) &&
(((uint8_t)((message->dataConfig.data[0] >> 8u) & 0xFFu)) == 0x10u) &&
(message->dataConfig.data[1] == 0u))
{
Motor_RequestAbortAllFromIsr(
(uint16_t)(message->dataConfig.data[0] >> 16u));
}
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;
}
}
@@ -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,155 @@
#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)
/*
* Do not sample at the 10 ms PWM period. Equal periods phase-lock the ADC
* to one PWM point and can report zero current continuously for a healthy
* low-duty heater. Seven milliseconds walks through every PWM phase while
* remaining slow enough for the existing SAR ADC service.
*/
#define SEAT_BACKREST_HEATER_SENSE_SAMPLE_MS (7u)
#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_RESISTOR_OHM (1200u)
#define SEAT_BACKREST_HEATER_SENSE_KILIS (5000u)
/* 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 */
@@ -0,0 +1,71 @@
#ifndef SEAT_CURRENT_ADC_CONFIG_H
#define SEAT_CURRENT_ADC_CONFIG_H
#include "cy_project.h"
#include "cy_device_headers.h"
/*
* PASS0 SAR1 is shared by all seat-current measurements. The schematic
* connects VREFH and the DRV8718 AREF rail to filtered 5 V, so raw-to-voltage
* calculations must use 5000 mV rather than the GPIO-domain 3.3 V supply.
*
* Logical motor mapping:
* M0 horizontal : ZYTTKZDY-AD, P10.6, ADC[1]_2
* M1 seat pan : JXBKZDY-AD, P12.1, ADC[1]_5
* M2 backrest : ZYKBKZDY-AD, P10.7, ADC[1]_3
* M3 legrest : ZYGDKZDY-AD, P10.5, ADC[1]_1
* heater current : KBJRD-AD, P12.2, ADC[1]_6
* fan current : FSDY-AD, P12.6, ADC[1]_10
*/
#define SEAT_CURRENT_ADC_INSTANCE PASS0_SAR1
#define SEAT_CURRENT_ADC_PCLK PCLK_PASS0_CLOCK_SAR1
#define SEAT_CURRENT_ADC_DIVIDER_NUMBER (4u)
#define SEAT_CURRENT_ADC_REFERENCE_MV (5000u)
#define SEAT_CURRENT_ADC_FULL_SCALE (4095u)
#define SEAT_CURRENT_ADC_OPERATION_FREQ_MAX_HZ (13340000u)
#define SEAT_CURRENT_ADC_SAMPLE_TIME_MIN_NS (600ull)
#define SEAT_CURRENT_ADC_POLL_TIMEOUT (100000u)
#define SEAT_CURRENT_M0_PORT GPIO_PRT10
#define SEAT_CURRENT_M0_PIN (6u)
#define SEAT_CURRENT_M0_GPIO_HSIOM P10_6_GPIO
#define SEAT_CURRENT_M0_CHANNEL PASS0_SAR1_CH2
#define SEAT_CURRENT_M0_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN2
#define SEAT_CURRENT_M1_PORT GPIO_PRT12
#define SEAT_CURRENT_M1_PIN (1u)
#define SEAT_CURRENT_M1_GPIO_HSIOM P12_1_GPIO
#define SEAT_CURRENT_M1_CHANNEL PASS0_SAR1_CH5
#define SEAT_CURRENT_M1_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN5
#define SEAT_CURRENT_M2_PORT GPIO_PRT10
#define SEAT_CURRENT_M2_PIN (7u)
#define SEAT_CURRENT_M2_GPIO_HSIOM P10_7_GPIO
#define SEAT_CURRENT_M2_CHANNEL PASS0_SAR1_CH3
#define SEAT_CURRENT_M2_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN3
#define SEAT_CURRENT_M3_PORT GPIO_PRT10
#define SEAT_CURRENT_M3_PIN (5u)
#define SEAT_CURRENT_M3_GPIO_HSIOM P10_5_GPIO
#define SEAT_CURRENT_M3_CHANNEL PASS0_SAR1_CH1
#define SEAT_CURRENT_M3_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN1
#define SEAT_CURRENT_HEATER_PORT GPIO_PRT12
#define SEAT_CURRENT_HEATER_PIN (2u)
#define SEAT_CURRENT_HEATER_GPIO_HSIOM P12_2_GPIO
#define SEAT_CURRENT_HEATER_CHANNEL PASS0_SAR1_CH6
#define SEAT_CURRENT_HEATER_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN6
#define SEAT_CURRENT_FAN_PORT GPIO_PRT12
#define SEAT_CURRENT_FAN_PIN (6u)
#define SEAT_CURRENT_FAN_GPIO_HSIOM P12_6_GPIO
#define SEAT_CURRENT_FAN_CHANNEL PASS0_SAR1_CH10
#define SEAT_CURRENT_FAN_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN10
#define SEAT_CURRENT_ADC_PORT_ADDRESS CY_ADC_PORT_ADDRESS_SARMUX0
#if !defined(CYT4BFBCJE) || !defined(CY_MCU_rev_d)
#error "Seat current ADC mapping requires CYT4BFBCJE rev_d headers."
#endif
#endif /* SEAT_CURRENT_ADC_CONFIG_H */
@@ -0,0 +1,139 @@
#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)
/* FSDY-AD is P12.6 / ADC[1]_10 on the shared seat-current ADC. */
#define SEAT_FAN_CURRENT_SAMPLE_MS (10u)
/*
* 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,150 @@
#ifndef SEAT_MOVE_CONFIG_H
#define SEAT_MOVE_CONFIG_H
#include "cy_project.h"
#include "cy_device_headers.h"
/* This configuration is intentionally tied to the timed-only IAR target. */
#if !defined(PASSENGER_SEAT_TIMED_ONLY)
#error "Build this source only from Passenger_Seat_Timed_Controller_demo."
#endif
#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_CONCURRENT_MOTORS (4u)
/* Timed-only CAN limits. ParamA is duration in ms. */
#define SEAT_TIMED_MIN_DURATION_MS (300u)
#define SEAT_TIMED_M0_MAX_DURATION_MS (30000u) /* horizontal */
#define SEAT_TIMED_M1_MAX_DURATION_MS (20000u) /* seat-pan */
#define SEAT_TIMED_M2_MAX_DURATION_MS (30000u) /* backrest */
#define SEAT_TIMED_M3_MAX_DURATION_MS (25000u) /* legrest */
#define SEAT_TIMED_LEVEL_MIN_X10 (300u)
#define SEAT_TIMED_LEVEL_MAX_X10 (1000u)
/* Logical MotorID and connected DRV8718 channel are identical. */
#define SEAT_MOVE_LOGICAL_M0_PHYSICAL_MODE (0u)
#define SEAT_MOVE_LOGICAL_M1_PHYSICAL_MODE (1u)
#define SEAT_MOVE_LOGICAL_M2_PHYSICAL_MODE (2u)
#define SEAT_MOVE_LOGICAL_M3_PHYSICAL_MODE (3u)
/* Retained by the verified motor manager; timed requests provide duty. */
#define SEAT_MOVE_M0_DEFAULT_TIMEOUT_MS (12000u)
#define SEAT_MOVE_M1_DEFAULT_TIMEOUT_MS (8000u)
#define SEAT_MOVE_M2_DEFAULT_TIMEOUT_MS (12000u)
#define SEAT_MOVE_M3_DEFAULT_TIMEOUT_MS (30000u)
#define SEAT_MOVE_M0_DEFAULT_DUTY_PERCENT (90u)
#define SEAT_MOVE_M1_DEFAULT_DUTY_PERCENT (90u)
#define SEAT_MOVE_M2_DEFAULT_DUTY_PERCENT (90u)
#define SEAT_MOVE_M3_DEFAULT_DUTY_PERCENT (50u)
#define SEAT_MOVE_M3_BREAKAWAY_DUTY_PERCENT (50u)
#define SEAT_MOVE_POSITION_TARGET_HALL_TOLERANCE (4u)
/* CAN4, classic CAN, 500 kbit/s. */
#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_MOVE_CAN_CURRENT_DIAGNOSTIC_ID (0x304u)
#define SEAT_MOVE_CAN_FIRMWARE_ID_ID (0x307u)
#define SEAT_MOVE_FIRMWARE_REVISION (15u)
#define SEAT_MOVE_FIRMWARE_BUILD_ID (17u)
#define SEAT_MOVE_SYSTEM_STATUS_PERIOD_MS (100u)
#define SEAT_MOVE_MOTOR_STATUS_POLL_MS (25u)
#define SEAT_MOVE_DIAGNOSTIC_POLL_MS (25u)
#define SEAT_MOVE_FIRMWARE_ID_PERIOD_MS (500u)
/* Current-aware stall detection remains active for timed movement. */
#define SEAT_MOVE_MOTOR_CURRENT_SAMPLE_MS (10u)
#define SEAT_MOVE_MOTOR_CURRENT_MAX_AGE_MS (100u)
#define SEAT_MOVE_MOTOR_CURRENT_BASELINE_FILTER_SHIFT (3u)
#define SEAT_MOVE_MOTOR_STALL_CURRENT_CONFIRM_MS (200u)
#define SEAT_MOVE_MOTOR_NO_MOTION_TIMEOUT_MS (5000u)
#define SEAT_MOVE_M0_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_M1_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_M2_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_M3_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_MOTOR_CURRENT_MAX_ERRORS (3u)
/* Debug-detach and watchdog safety. HIL registers remain internal to the
* reused safety manager; this image exposes no HIL CAN command/filter. */
#define SEAT_MOVE_DEBUG_SESSION_BREG_INDEX (3u)
#define SEAT_MOVE_DEBUG_ACTIVE_MAGIC (0x4D4F5641u)
#define SEAT_MOVE_DEBUG_EXIT_MAGIC (0x4D4F5645u)
#define SEAT_MOVE_WDT_TIMEOUT_TICKS (3200u)
#define SEAT_MOVE_HIL_WDT_MAGIC_BREG_INDEX (0u)
#define SEAT_MOVE_HIL_WDT_TOKEN_BREG_INDEX (1u)
#define SEAT_MOVE_HIL_WDT_CHECK_BREG_INDEX (2u)
#define SEAT_MOVE_HIL_WDT_MARKER (0x48494C36u)
#if !defined(CYT4BFBCJE) || !defined(CY_MCU_rev_d)
#error "Timed passenger-seat firmware requires CYT4BFBCJE rev_d."
#endif
#if (SEAT_MOVE_ACTIVE_MOTOR_MASK != 0x0Fu)
#error "All four passenger-seat motor outputs must be enabled."
#endif
#if (SEAT_MOVE_MAX_CONCURRENT_MOTORS == 0u) || \
(SEAT_MOVE_MAX_CONCURRENT_MOTORS > SEAT_MOVE_MOTOR_COUNT)
#error "Concurrent motor budget must be between one and four."
#endif
#if ((SEAT_TIMED_MIN_DURATION_MS == 0u) || \
(SEAT_TIMED_M0_MAX_DURATION_MS > 65535u) || \
(SEAT_TIMED_M1_MAX_DURATION_MS > 65535u) || \
(SEAT_TIMED_M2_MAX_DURATION_MS > 65535u) || \
(SEAT_TIMED_M3_MAX_DURATION_MS > 65535u))
#error "Timed-command duration limits must fit ParamA."
#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_MOVE_CAN_CURRENT_DIAGNOSTIC_ID > 0x7FFu) || \
(SEAT_MOVE_CAN_FIRMWARE_ID_ID > 0x7FFu))
#error "Seat movement CAN identifiers must be 11-bit standard IDs."
#endif
#if (SEAT_MOVE_FIRMWARE_REVISION > 0x0Fu) || \
(SEAT_MOVE_FIRMWARE_BUILD_ID > 0xFFu)
#error "Firmware identity fields exceed their CAN encoding."
#endif
#if ((SEAT_MOVE_MOTOR_CURRENT_SAMPLE_MS == 0u) || \
(SEAT_MOVE_MOTOR_CURRENT_MAX_AGE_MS < \
SEAT_MOVE_MOTOR_CURRENT_SAMPLE_MS) || \
(SEAT_MOVE_MOTOR_STALL_CURRENT_CONFIRM_MS == 0u) || \
(SEAT_MOVE_MOTOR_NO_MOTION_TIMEOUT_MS <= \
SEAT_MOVE_MOTOR_STALL_CURRENT_CONFIRM_MS) || \
(SEAT_MOVE_MOTOR_CURRENT_MAX_ERRORS == 0u))
#error "Motor current/stall timing configuration is invalid."
#endif
#endif /* SEAT_MOVE_CONFIG_H */
@@ -0,0 +1,25 @@
# Build information — 2026-09-16
- Project: `Passenger_Seat_Timed_Controller_demo.ewp`
- Configuration: `rev_d`
- IAR command-line builder: 9.2.2.10770
- IAR ARM compiler/linker: 9.40.1.364
- Result: 0 errors, 0 warnings
- Read-only code: 40,626 bytes
- Read-only data: 1,977 bytes
- Read-write data: 6,182 bytes
Artifacts:
| File | SHA-256 |
|---|---|
| `IAR/rev_d/Exe/Passenger_Seat_Timed_Controller_demo.hex` | `7B47002B7B1A882A90717CE2727D60349B6C58CF0C1E95E48094C85EDB376CDC` |
| `IAR/rev_d/Exe/Passenger_Seat_Timed_Controller_demo.out` | `B0C740CB2BE02008BC03803B0A3422D817ABD1CF2BF977156294FB04BBDFAB7F` |
The linker map contains `SeatMotor_MoveTimedDuty` as the sole linked
`SeatMotor_Move*`/calibration/homing entry point. No dynamic-mode, position
store, or thermal-controller application symbol is linked. The HEX contains
the `PST1`, revision 15, build 17 identity payload.
This result is a compile/link verification only. No target was flashed and no
physical actuator movement was performed during this build.
@@ -0,0 +1,88 @@
# 副驾座椅四轴定时控制固件
本目录是从 `Seat_Controller_demo` 独立出的副驾定时控制版本。原工程保持不变;本工程的 CAN 应用层只允许四轴定时运动,不提供位置、相对位移、回零、标定、动态模式或热控能力。
## IAR 工程
- 工程文件:`IAR/Passenger_Seat_Timed_Controller_demo.ewp`
- 目标器件:Infineon TRAVEO T2G `CYT4BFBCJE`,`rev_d`
- IAR 工程宏:`PASSENGER_SEAT_TIMED_ONLY=1`
- HEX 输出:`Passenger_Seat_Timed_Controller_demo.hex`
- 固件身份帧:CAN `0x307`,字节 0..3 为 ASCII `PST1`,Build ID 为 `17`
工程复用原固件已验证的 DRV8718、Hall、电流检测、堵转/端点保护、看门狗和调试器退出安全逻辑。加热和风扇应用没有链接进固件;其 BSP 只用于启动及异常路径中强制输出关闭。
## 四轴能力
| MotorID | 机构 | 最短时间 | 最长时间 | 实际占空比限制 |
|---:|---|---:|---:|---:|
| 0 | 水平前后 | 300 ms | 30000 ms | 50%..95% |
| 1 | 坐垫升降 | 300 ms | 20000 ms | 50%..95% |
| 2 | 靠背角度 | 300 ms | 30000 ms | 50%..95% |
| 3 | 腿托升降 | 300 ms | 25000 ms | 固定上限 50% |
四轴可并发,启动间隔至少 100 ms。同一轴收到更新的定时命令时,先停止该轴,再执行新命令;不影响其他轴。定时运动不依赖绝对位置有效性,但端点、堵转、电流和驱动故障保护始终生效。
## CAN 命令
CAN4 使用经典 CAN、标准 11 位 ID、500 kbit/s。接收过滤器只接受 `0x300`,DLC 必须为 8。
`0x300` 的 8 字节按小端编码:
| 字节 | 字段 |
|---|---|
| B0 | Opcode |
| B1 | Control:bit1:0 MotorID,bit3:2 Direction,bit5:4 ProtocolVersion=1,bit7:6=0 |
| B3:B2 | Sequence,uint16 |
| B5:B4 | ParamA,uint16 |
| B7:B6 | ParamB,uint16 |
支持的 Opcode:
| Opcode | 功能 | Direction | ParamA | ParamB |
|---:|---|---|---|---|
| `0x03` | 定时运动 | `1` 向远端,`2` 向近端 | 时间,ms | 请求占空比 ×10,范围 300..1000 |
| `0x08` | 停止单轴 | `0` | `0` | `0` |
| `0x09` | 停止全部 | `0`,MotorID=`0` | `0` | `0` |
| `0x0C` | 查询单轴状态 | `0` | `0` | `0` |
| `0x0D` | 清除单轴任务故障 | `0` | `0xC33C` | `0` |
请求占空比会被安全范围约束:M0..M2 最终为 50%..95%,M3 不超过 50%。`STOP_ALL` 不受序列号新旧限制,可用于随时重新同步和急停;其他命令要求 16 位 Sequence 按环形序列递增,完全相同的重复帧返回 Duplicate。
以下运动 Opcode 固定返回 `BAD_OPCODE(3)`:
- `0x01` 绝对位置
- `0x02` 相对位置
- `0x0A` 行程标定
- `0x0B` 已知行程回零
- `0x0E` 启动动态模式
- `0x0F` 停止动态模式
热控和 HIL 自愈帧不在硬件接收过滤器中,`0x306` 动态模式状态也不再发送。
## 状态反馈
保留与原座椅协议兼容的诊断帧:
- `0x301`:系统状态、ACK、拒绝原因、活动/故障轴掩码
- `0x302`:单轴任务状态;定时运行状态值为 `3`
- `0x303`:Hall、速度和故障诊断
- `0x304`:电流诊断
- `0x307`:固件身份 `PST1`
`0x301` 中动态模式位和标定位始终为 0。位置有效位及百分比只作为 Hall 诊断信息返回,不用于目标位置控制。
## 构建与校验
1. 用 IAR Embedded Workbench for Arm 打开 `IAR/Passenger_Seat_Timed_Controller_demo.ewp`。
2. 选择 `rev_d` 配置并 Build。
3. 烧录前通过 `0x307` 确认目标固件身份为 `PST1`。
4. 烧录和台架运动测试期间保证座椅机构无人员/障碍物,先验证 `STOP_ALL`,再逐轴低时长测试。
仓库静态校验:
```text
python firmware/t2g/Passenger_Seat_Timed_Controller_demo/Tests/audit_timed_only.py
```
该脚本检查 IAR XML、工程引用、定时版编译宏、CAN 接收过滤器、允许的 Opcode,以及位置/动态/热控模块未被链接。
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,277 @@
#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)
/*
* 2026-07-30 endpoint-to-endpoint range calibration. Bench verification
* established identity mapping: logical M0..M3 use physical mode 0..3.
*/
#define MOTOR_MODE0_DEFAULT_MAX_HALL_COUNT (480ul) /* horizontal */
#define MOTOR_MODE0_MAX_POSITION_MM_X100 (21500ul)
#define MOTOR_MODE1_DEFAULT_MAX_HALL_COUNT (374ul) /* seat pan */
#define MOTOR_MODE1_MAX_VERTICAL_MM_X100 (5600ul)
#define MOTOR_MODE1_MAX_HORIZONTAL_MM_X100 (5100ul)
/* Compatibility name used by the generic scalar position API. */
#define MOTOR_MODE1_MAX_ANGLE_DEG_X100 \
MOTOR_MODE1_MAX_VERTICAL_MM_X100
#define MOTOR_MODE2_DEFAULT_MAX_HALL_COUNT (917ul) /* backrest */
#define MOTOR_MODE2_MAX_ANGLE_DEG_X100 (6800ul)
#define MOTOR_MODE3_DEFAULT_MAX_HALL_COUNT (647ul) /* legrest */
#define MOTOR_MODE3_MAX_ANGLE_DEG_X100 (7200ul)
/* Compatibility name used by the generic scalar position API. */
#define MOTOR_MODE3_MAX_POSITION_MM_X100 \
MOTOR_MODE3_MAX_ANGLE_DEG_X100
#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 horizontalMmX100;
uint32_t verticalMmX100;
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
{
motor_mode_t mode;
bool sampleValid;
bool baselineValid;
bool highCurrent;
bool hallActivityLow;
bool calibrating;
uint16_t raw;
uint16_t baselineRaw;
uint16_t deltaRaw;
uint16_t stallThresholdRaw;
uint32_t sampleAgeMs;
} motor_current_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: 215.00 mm is 21500 and
* 68.00 degrees is 6800. positionValid is restored from a verified clean
* record or becomes true after a known endpoint has been found.
*
* 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);
uint8_t Motor_GetCurrentInfo(motor_mode_t mode, motor_current_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);
/* Complete a commanded stop plus bounded Hall-coast tracking before a
* safety decision that requires the mechanism to be physically settled. */
bool Motor_WaitUntilStopped(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_ValidatePersistentState(motor_mode_t mode,
const motor_persistent_state_t *state);
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);
/* Bench HIL support: invalidate only the stopped axis' absolute-position
* confidence while preserving its measured Hall count and confirmed range.
* Production command handling never calls this API. */
uint8_t Motor_InvalidatePosition(motor_mode_t mode);
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.
* legTimeoutMs is the independent motion timeout applied to each endpoint leg.
*/
uint8_t Motor_Calibrate(motor_mode_t mode,
motor_calibration_action_t action,
uint32_t legTimeoutMs,
motor_calibration_result_t *result);
/* Application override used to keep CAN/status/safety services alive while
* the selected motor executes the blocking endpoint-calibration primitive. */
void Motor_CalibrationServiceHook(void);
/* 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_GetPosition(motor_position_info_t *info);
uint8_t Motor2_GetAngle(motor_position_info_t *info);
uint8_t Motor3_GetAngle(motor_position_info_t *info);
uint8_t Motor0_MoveToPositionMmX100(uint32_t positionMmX100);
uint8_t Motor1_MoveToVerticalMmX100(uint32_t verticalMmX100);
uint8_t Motor2_MoveToAngleDegX100(uint32_t angleDegX100);
uint8_t Motor3_MoveToAngleDegX100(uint32_t angleDegX100);
uint8_t Motor0_SetSpeedPlan(uint32_t durationMs, uint32_t distanceMmX100);
uint8_t Motor1_SetSpeedPlan(uint32_t durationMs, uint32_t verticalMmX100);
uint8_t Motor2_SetSpeedPlan(uint32_t durationMs, uint32_t angleDegX100);
uint8_t Motor3_SetSpeedPlan(uint32_t durationMs, uint32_t angleDegX100);
#endif /* MOTOR_CONTROL_H */
@@ -0,0 +1,185 @@
/* C-SPY host-side safety hook for the timed-only passenger-seat controller. */
execUserSetup()
{
/* A new Download and Debug session releases a previous debug-exit latch. */
__writeMemory32(0x00000000, 0x4027100C, "Memory");
__message "Passenger_Seat_Timed_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 "Passenger_Seat_Timed_Controller_demo ERROR: C-SPY safe-off register verification failed.\n";
}
else
{
__message "Passenger_Seat_Timed_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 "Passenger_Seat_Timed_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 "Passenger_Seat_Timed_Controller_demo ERROR: C-SPY safe-off register verification failed.\n";
}
else
{
__message "Passenger_Seat_Timed_Controller_demo: program-only exit forced all outputs low and released the debug latch.\n";
}
}
@@ -0,0 +1,109 @@
from __future__ import annotations
import re
import unittest
import xml.etree.ElementTree as ET
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EWP = ROOT / "IAR" / "Passenger_Seat_Timed_Controller_demo.ewp"
APP = ROOT / "App" / "seat_timed_application.c"
CAN_BSP = ROOT / "Bsp" / "bsp_seat_move_can.c"
CONFIG = ROOT / "Config" / "seat_move_config.h"
class TimedOnlyFirmwareAudit(unittest.TestCase):
def test_iar_project_is_valid_xml_and_local_references_exist(self) -> None:
tree = ET.parse(EWP)
project_dir = EWP.parent
missing: list[str] = []
for element in tree.findall(".//file/name"):
value = element.text or ""
if not value.startswith("$PROJ_DIR$"):
continue
relative = value.removeprefix("$PROJ_DIR$\\").replace("\\", "/")
if not (project_dir / relative).resolve().is_file():
missing.append(value)
self.assertEqual([], missing)
def test_iar_target_declares_timed_only_and_excludes_other_apps(self) -> None:
text = EWP.read_text(encoding="utf-8")
self.assertIn("PASSENGER_SEAT_TIMED_ONLY=1", text)
self.assertIn("seat_timed_application.c", text)
self.assertIn("Passenger_Seat_Timed_Controller_demo.hex", text)
for forbidden in (
"seat_move_application",
"seat_dynamic_mode",
"seat_move_store",
"seat_controller_thermal",
"..\\App\\seat_backrest_heater.c</name>",
"bsp_seat_backrest_heater_adc",
):
self.assertNotIn(forbidden, text)
def test_can_hardware_filter_accepts_only_command_id(self) -> None:
text = CAN_BSP.read_text(encoding="utf-8")
block = re.search(
r"s_standardIdFilters\[\]\s*=\s*\{(?P<body>.*?)\};",
text,
re.DOTALL,
)
self.assertIsNotNone(block)
body = block.group("body")
self.assertEqual(2, body.count("SEAT_MOVE_CAN_COMMAND_ID"))
self.assertNotRegex(body, r"0x(?:29E|2AA|3CE|42A|2F0)u")
self.assertNotIn("HIL_SELF_HEAL", body)
def test_application_exposes_only_timed_motion_dispatch(self) -> None:
text = APP.read_text(encoding="utf-8")
self.assertIn("SeatMotor_MoveTimedDuty", text)
for forbidden_call in (
"SeatMotor_MoveAbsolute",
"SeatMotor_MoveRelative",
"SeatMotor_CalibrateRange",
"SeatMotor_HomeKnownRange",
"SeatDynamicMode_",
"SeatMoveStore_",
"SeatControllerThermal_",
):
self.assertNotIn(forbidden_call, text)
accepted = set(
re.findall(
r"case\s+(SEAT_TIMED_OPCODE_[A-Z_]+)\s*:",
text[text.index("static bool SeatTimed_ParseCommand") :
text.index("static bool SeatTimed_ParametersValid")],
)
)
self.assertEqual(
{
"SEAT_TIMED_OPCODE_MOVE_TIMED",
"SEAT_TIMED_OPCODE_STOP_MOTOR",
"SEAT_TIMED_OPCODE_STOP_ALL",
"SEAT_TIMED_OPCODE_QUERY_STATUS",
"SEAT_TIMED_OPCODE_CLEAR_FAULT",
},
accepted,
)
def test_axis_specific_duration_limits_are_present(self) -> None:
text = CONFIG.read_text(encoding="utf-8")
expected = {
"SEAT_TIMED_MIN_DURATION_MS": "300",
"SEAT_TIMED_M0_MAX_DURATION_MS": "30000",
"SEAT_TIMED_M1_MAX_DURATION_MS": "20000",
"SEAT_TIMED_M2_MAX_DURATION_MS": "30000",
"SEAT_TIMED_M3_MAX_DURATION_MS": "25000",
}
for name, value in expected.items():
self.assertRegex(
text,
rf"#define\s+{name}\s+\({value}u\)",
)
self.assertNotIn("SEAT_MOVE_CAN_DYNAMIC_STATUS_ID", text)
self.assertNotIn("SEAT_MOVE_CAN_HIL_SELF_HEAL_REQUEST_ID", text)
if __name__ == "__main__":
unittest.main(verbosity=2)
+10 -2
View File
@@ -2,7 +2,14 @@
`Seat_Controller_demo/` is the repository-owned IAR project used by the real
seat-bench executor. It contains the 2026-07-30 heater, ventilation, four-axis
motion, SMV8 position memory, Hall feedback and ADC current-diagnostic code.
time/position motion, SMV8 position memory, Hall feedback and ADC
current-diagnostic code.
`Passenger_Seat_Timed_Controller_demo/` is the separately buildable
passenger-seat variant. Its CAN application exposes four-axis timed movement
only; position, homing, calibration, dynamic-mode and thermal commands are not
linked into the application. See its `Docs/README_Passenger_Seat_Timed_Controller.md`
for the command contract and generated HEX identity (`PST1`).
The matching Cypress 7.9.0 build dependencies are vendored beside it:
@@ -10,7 +17,8 @@ The matching Cypress 7.9.0 build dependencies are vendored beside it:
firmware/t2g/
├── common/
├── tviibh8m/
└── Seat_Controller_demo/
├── Seat_Controller_demo/
└── Passenger_Seat_Timed_Controller_demo/
```
This preserves the IAR project's `../../common` and `../../tviibh8m` paths and
@@ -13,6 +13,7 @@
#include "bsp_seat_backrest_heater.h"
#include "bsp_seat_fan.h"
#include "seat_controller_thermal.h"
#include "seat_feature_profile.h"
volatile uint32_t g_seatMoveResetReason = 0u;
volatile uint32_t g_seatMoveInitResult = MOTOR_API_FAILURE;
@@ -59,11 +60,13 @@ int main(void)
}
Motor_SetWatchdogServiceEnabled(true);
#if SEAT_FEATURE_THERMAL
if(!SeatControllerThermal_Init())
{
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
SeatMove_HoldSafeOff();
}
#endif
if(!SeatMoveApplication_Init())
{
SeatMove_HoldSafeOff();
@@ -72,7 +75,9 @@ int main(void)
for(;;)
{
SeatMoveApplication_Service();
#if SEAT_FEATURE_THERMAL
SeatControllerThermal_Service1ms();
#endif
if(SafetyManager_IsInhibited())
{
Motor_ForceSafeOff();
@@ -7,6 +7,7 @@
#include "seat_backrest_heater.h"
#include "seat_backrest_heater_config.h"
#include "seat_fan_config.h"
#include "seat_feature_profile.h"
#define SEAT_CONTROLLER_THERMAL_CONTROL_ID (0x29Eu)
#define SEAT_CONTROLLER_THERMAL_CONFIG_ID (0x2AAu)
@@ -247,6 +248,7 @@ static void SeatControllerThermal_HandleControl29e(
fanGear = (uint8_t)((frame->data0 >> 16u) & 0x0Fu);
heaterGear = (uint8_t)((frame->data0 >> 20u) & 0x0Fu);
#if SEAT_FEATURE_VENTILATION
if(fanOn == 2u)
{
SeatControllerThermal_StopFan(1u);
@@ -266,7 +268,14 @@ static void SeatControllerThermal_HandleControl29e(
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopFan(0u);
}
#else
(void)fanOn;
(void)fanMode;
(void)fanGear;
SeatControllerThermal_StopFan(1u);
#endif
#if SEAT_FEATURE_HEATING
if(heaterOn == 2u)
{
SeatControllerThermal_StopHeater(1u);
@@ -286,6 +295,11 @@ static void SeatControllerThermal_HandleControl29e(
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopHeater(0u);
}
#else
(void)heaterOn;
(void)heaterGear;
SeatControllerThermal_StopHeater(1u);
#endif
}
static void SeatControllerThermal_HandleConfig2aa(
@@ -308,16 +322,23 @@ static void SeatControllerThermal_HandleConfig2aa(
g_seatControllerThermalInvalidCount++;
return;
}
#if SEAT_FEATURE_VENTILATION
s_fanConfiguredMaxGear = (fanConfig == 1u) ? 1u :
((fanConfig == 2u) ? 2u : 3u);
#endif
#if SEAT_FEATURE_HEATING
s_heaterConfiguredMaxLevel = (heaterConfig == 1u) ? 1u :
((heaterConfig == 2u) ? 2u : 3u);
#endif
#if SEAT_FEATURE_VENTILATION
if(s_fanRunning &&
!SeatControllerThermal_RequestFan(s_fanLastVehicleGear))
{
g_seatControllerThermalOutputFaultStage = 4u;
SeatControllerThermal_LatchOutputFault();
}
#endif
#if SEAT_FEATURE_HEATING
if(g_seatBackrestHeaterAppliedLevel !=
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
@@ -327,11 +348,13 @@ static void SeatControllerThermal_HandleConfig2aa(
SeatControllerThermal_LatchOutputFault();
}
}
#endif
}
static void SeatControllerThermal_HandleMode3ce(
const bsp_seat_move_can_frame_t *frame)
{
#if SEAT_FEATURE_HEATING
uint8_t mode;
if(frame->dlc != SEAT_FAN_CAN_DLC)
{
@@ -347,6 +370,10 @@ static void SeatControllerThermal_HandleMode3ce(
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopHeater(0u);
}
#else
(void)frame;
SeatControllerThermal_StopHeater(1u);
#endif
}
static void SeatControllerThermal_HandleSwitch42a(
@@ -363,6 +390,7 @@ static void SeatControllerThermal_HandleSwitch42a(
}
fanSwitch = (uint8_t)(frame->data0 & 0x03u);
heaterSwitch = (uint8_t)((frame->data0 >> 2u) & 0x03u);
#if SEAT_FEATURE_VENTILATION
if(fanSwitch == 1u)
{
if(!SeatControllerThermal_RequestFan(s_fanLastVehicleGear))
@@ -375,6 +403,11 @@ static void SeatControllerThermal_HandleSwitch42a(
{
SeatControllerThermal_StopFan(1u);
}
#else
(void)fanSwitch;
SeatControllerThermal_StopFan(1u);
#endif
#if SEAT_FEATURE_HEATING
if(heaterSwitch == 1u)
{
if(!SeatControllerThermal_RequestHeater(s_heaterLastVehicleGear))
@@ -387,10 +420,15 @@ static void SeatControllerThermal_HandleSwitch42a(
{
SeatControllerThermal_StopHeater(1u);
}
#else
(void)heaterSwitch;
SeatControllerThermal_StopHeater(1u);
#endif
}
static bool SeatControllerThermal_RequestFan(uint8_t vehicleGear)
{
#if SEAT_FEATURE_VENTILATION
uint8_t physicalGear = (vehicleGear >= 3u) ? 3u : vehicleGear;
if((vehicleGear == 0u) || (vehicleGear > 5u))
{
@@ -418,10 +456,15 @@ static bool SeatControllerThermal_RequestFan(uint8_t vehicleGear)
return BspSeatFan_SetDutyPercent(s_fanTargetDutyPercent);
}
return true;
#else
(void)vehicleGear;
return false;
#endif
}
static bool SeatControllerThermal_RequestHeater(uint8_t vehicleGear)
{
#if SEAT_FEATURE_HEATING
uint8_t physicalLevel = (vehicleGear >= 3u) ? 3u : vehicleGear;
if((vehicleGear == 0u) || (vehicleGear > 5u) || !s_heaterModeValid)
{
@@ -440,6 +483,10 @@ static bool SeatControllerThermal_RequestHeater(uint8_t vehicleGear)
g_seatControllerHeaterFeedbackRaw = physicalLevel + 1u;
s_feedbackPending = true;
return true;
#else
(void)vehicleGear;
return false;
#endif
}
static void SeatControllerThermal_StopFan(uint8_t feedbackRaw)
@@ -8,6 +8,7 @@
#include "safety_manager.h"
#include "seat_controller_thermal.h"
#include "seat_dynamic_mode.h"
#include "seat_feature_profile.h"
#include "seat_motor_manager.h"
#include "seat_move_config.h"
#include "seat_move_store.h"
@@ -336,7 +337,9 @@ void SeatMoveApplication_Service(void)
}
else
{
#if SEAT_FEATURE_THERMAL
SeatControllerThermal_HandleFrame(&frame);
#endif
}
}
SeatMove_ServiceDispatcher();
@@ -363,7 +366,9 @@ void Motor_CalibrationServiceHook(void)
}
else
{
#if SEAT_FEATURE_THERMAL
SeatControllerThermal_HandleFrame(&frame);
#endif
}
}
if(g_bspSeatMoveCanBusOff != 0u)
@@ -533,15 +538,28 @@ static bool SeatMove_ParseCommand(const bsp_seat_move_can_frame_t *frame,
{
case SEAT_MOVE_OPCODE_MOVE_ABSOLUTE:
case SEAT_MOVE_OPCODE_MOVE_RELATIVE:
#if SEAT_FEATURE_POSITION_CONTROL
break;
#else
*reject = SEAT_MOVE_REJECT_BAD_OPCODE;
return false;
#endif
case SEAT_MOVE_OPCODE_MOVE_TIMED:
case SEAT_MOVE_OPCODE_STOP_MOTOR:
case SEAT_MOVE_OPCODE_STOP_ALL:
break;
case SEAT_MOVE_OPCODE_CALIBRATE_RANGE:
case SEAT_MOVE_OPCODE_HOME_KNOWN_RANGE:
case SEAT_MOVE_OPCODE_QUERY_STATUS:
case SEAT_MOVE_OPCODE_CLEAR_FAULT:
case SEAT_MOVE_OPCODE_START_DYNAMIC_MODE:
case SEAT_MOVE_OPCODE_STOP_DYNAMIC_MODE:
#if SEAT_FEATURE_POSITION_CONTROL
break;
#else
*reject = SEAT_MOVE_REJECT_BAD_OPCODE;
return false;
#endif
case SEAT_MOVE_OPCODE_QUERY_STATUS:
case SEAT_MOVE_OPCODE_CLEAR_FAULT:
break;
default:
*reject = SEAT_MOVE_REJECT_BAD_OPCODE;
@@ -0,0 +1,27 @@
#ifndef SEAT_FEATURE_PROFILE_H
#define SEAT_FEATURE_PROFILE_H
/*
* Repository default is the complete controller. The workbench build script
* replaces this header only inside its isolated staging tree so every OTA
* task receives the exact capability profile selected by the user.
*/
#define SEAT_FEATURE_POSITION_CONTROL (1)
#define SEAT_FEATURE_VENTILATION (1)
#define SEAT_FEATURE_HEATING (1)
#if ((SEAT_FEATURE_POSITION_CONTROL != 0) && \
(SEAT_FEATURE_POSITION_CONTROL != 1))
#error "SEAT_FEATURE_POSITION_CONTROL must be 0 or 1"
#endif
#if ((SEAT_FEATURE_VENTILATION != 0) && (SEAT_FEATURE_VENTILATION != 1))
#error "SEAT_FEATURE_VENTILATION must be 0 or 1"
#endif
#if ((SEAT_FEATURE_HEATING != 0) && (SEAT_FEATURE_HEATING != 1))
#error "SEAT_FEATURE_HEATING must be 0 or 1"
#endif
#define SEAT_FEATURE_THERMAL \
((SEAT_FEATURE_VENTILATION != 0) || (SEAT_FEATURE_HEATING != 0))
#endif /* SEAT_FEATURE_PROFILE_H */
@@ -2359,6 +2359,9 @@
<file>
<name>$PROJ_DIR$\..\Config\seat_move_config.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\Config\seat_feature_profile.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\Config\seat_backrest_heater_config.h</name>
</file>
@@ -1,12 +1,12 @@
:020000041000EA
:100000000000022801EC00103DF4001055F1001032
:1000000000000228F5EB0010E1F30010F9F00010F9
:1000100000000000000000000000000000000000E0
:1000200000000000000000000000000015F20010B9
:10003000000000000000000041F4001045F4001032
:10002000000000000000000000000000B9F1001016
:100030000000000000000000E5F30010E9F30010EC
:10004000CDE50010F1E5001015E6001039E60010CE
:100050005DE6001081E60010A5E60010C9E600107C
:1000600049F400104DF4001051F4001055F4001044
:1000700059F400105DF4001061F4001065F40010F4
:10006000EDF30010F1F30010F5F30010F9F30010B8
:10007000FDF3001001F4001005F4001009F4001065
:1000800000B5010009B2002905D40121C006C00E47
:100090008140C648016000BD00B5010009B20029D9
:1000A00005D40121C006C00E8140C148016000BDD9
@@ -78,7 +78,7 @@
:1004C000060091204000FFF779FFF106C90E009168
:1004D00071090B051B0D5B1E2200D2B20321C0B2B5
:1004E000FFF7F8FED3BD80B51300A74A13600023C1
:1004F000137203F048FE01BD24F3001000B5C0B232
:1004F000137203F048FE01BDC8F2001000B5C0B28F
:10050000042801D2012000E00020C0B200BD00B5E7
:10051000401A00B2012801DB012000E00020C0B237
:1005200000BD00B5BE490978002901D000201FE0B8
@@ -313,8 +313,8 @@
:101370002000FFF723F801220321C0B2FFF762F833
:10138000884C200003F0A2FE00228749200003F0D1
:1013900063FD2000FEF720FF13BD0000FC1B0028AA
:1013A00034E700108CEB0010C21C00283CEE00104B
:1013B0002CF30010CD100010F2B5060000273C0001
:1013A00034E7001080EB0010C21C0028B8EC0010DD
:1013B000D0F20010CD100010F2B5060000273C005E
:1013C0000098002801D1002077E0380000990880BB
:1013D00003F0AEFBCD490978002903D003F0ACFB44
:1013E00000206AE00121C94A117003F0A5FB6D4D90
@@ -343,9 +343,9 @@
:10155000FFF7D5FF002806D068460078E4B2A04225
:1015600001D1012000E00020C0B232BD80B5B44AF4
:1015700000211160017C1172C168806802F003FED5
:1015800001BD00006012002808F00010000658405D
:1015800001BD000060120028ACEF001000065840BA
:1015900080065840000758408009584000B4C404F1
:1015A0000024F400000064408CEE0010F8B5012621
:1015A0000024F4000000644030EE0010F8B501267D
:1015B0003000002468460470564804700121A14898
:1015C000FEF7B8FDC24F01213800FEF7B3FD9E4D76
:1015D000280003F003FE01219C48FEF7ABFD280024
@@ -1109,12 +1109,12 @@
:104530009BB21B0A9BB2174C14401B02FF221202B3
:104540001A4022430B78052B05D10979C9B2FF2304
:104550009A43114304E04978FF239A43C9B21143B7
:10456000016300221000F2BD0CF40010FFFFFBFFFE
:10456000016300221000F2BDB0F30010FFFFFBFF5B
:10457000FFFFFCFFFFFFFFF8FFFFFFBFFFFBFFFF99
:10458000FFFDFFFFFFFEFFFFFFCFFFFF10F4001056
:10459000FFFF00FFFF00FFFF14F4001018F40010ED
:1045A000FFF3FFFFFFFCFFFF1CF4001020F40010DE
:1045B00024F400100168802212060A43026070474A
:10458000FFFDFFFFFFFEFFFFFFCFFFFFB4F30010B3
:10459000FFFF00FFFF00FFFFB8F30010BCF30010A7
:1045A000FFF3FFFFFFFCFFFFC0F30010C4F3001098
:1045B000C8F300100168802212060A4302607047A7
:1045C000016849004908016070474068704701610F
:1045D00070470069704781607047006A704700004B
:1045E00030B4434351435D18010C0C00130C5C4381
@@ -1169,7 +1169,7 @@
:1048F000142060432818007C787103A914206043B9
:104900002818C06802F0EAFC0028E7D0300002F066
:10491000E1FC002003E0012071490870012019B07A
:10492000F0BD000024F3001000000440FF00FFFF72
:10492000F0BD0000C8F2001000000440FF00FFFFCF
:10493000FF0000FFF8B584B00E00C0B20290002561
:1049400028006946088004212A0003AC200002F0F8
:1049500033FC2C0062480078002806D0674F387876
@@ -1191,16 +1191,16 @@
:104A500001D0002018E0631E042201212800FFF786
:104A6000EFFD002801D000200EE0042201212800E3
:104A7000FFF771FE002801D0002005E0009821001A
:104A8000FFF7C0FB38600120F2BD0000FCF200100F
:104A8000FFF7C0FB38600120F2BD0000A0F200106B
:104A90007CB505001D4C062100226E46300002F058
:104AA0008BFB6946280002F011FE6846007800285A
:104AB00003D1641E002CF4D101E0012000E00020AD
:104AC00076BD000014F10010E0B511490CC9083999
:104AC00076BD0000B8F00010E0B511490CC90839F6
:104AD00069460CC10839694602F0A2FD07BD000015
:104AE000B71C0028901B0028A81B00282CEA0010E7
:104AE000B71C0028901B0028A81B002820EA0010F3
:104AF000FFC99A3B00CA9A3B00109040B81C00289E
:104B00005F8DCB00608DCB0001000100A08601000D
:104B100004F300100000000000000000000000008E
:104B1000A8F20010000000000000000000000000EB
:104B2000801C800803D0401C801EFCD100BF704751
:104B3000EFF3108072B6704780F3108870474030F2
:104B4000016810220A430260704740300168082261
@@ -1234,10 +1234,10 @@
:104D0000A54394000C2222402A43C264174A146827
:104D10004C228A5C1A400725AC4322434D240C5D8B
:104D200023407024A2431C01702323401343436596
:104D3000497D816700221000F2BD0000F8F30010E9
:104D3000497D816700221000F2BD00009CF3001045
:104D4000FFFFFBFFFFFFFCFFFFFFFFF8FFFFFFBFC1
:104D5000FCF30010FFFF00FFFF00FFFF00F4001056
:104D600004F40010FFF3FFFFFFFCFFFF08F4001046
:104D5000A0F30010FFFF00FFFF00FFFFA4F300100F
:104D6000A8F30010FFF3FFFFFFFCFFFFACF3001000
:104D70000168802212060A43026070477830016899
:104D800001220A4302607047C16F0800C007C00FCC
:104D90007047010070310A68012313430B60006FF4
@@ -1246,7 +1246,7 @@
:104DC0000B79002B01D00122FFE763005B08D207BB
:104DD0001A430B4B0024095F89005A5000E0064833
:104DE00030BD00B5074A1368002B03D0126800B22B
:104DF0008000115000BD000001005600F4F30010C7
:104DF0008000115000BD00000100560098F3001023
:104E0000008020402412002870B4103A03D378C9DF
:104E1000103A78C0FBD2530701D318C918C001D586
:104E200008C908C0D20704D30B880380891C801CE2
@@ -1296,8 +1296,8 @@
:1050E0000021C1239B00C15080239B00C25081221C
:1050F00092008150124A8150154A8150114A8150C4
:10510000114A8150134A8150134A81507047000060
:105110000140AA00DCF30010E0F30010E4F30010FB
:10512000E8F30010FF3FFFFFFFFEFFFFFFFFFFFC64
:105110000140AA0080F3001084F3001088F300100F
:105120008CF30010FF3FFFFFFFFEFFFFFFFFFFFCC0
:10513000FFFDFFFFFFFBFFFFFFF7FFFF0F00000377
:10514000C80E0000C80F0000880F0000EFCDAB00B4
:10515000880E0000080F0000480F0000F8B504009A
@@ -1313,7 +1313,7 @@
:1051F00002F0DAFE0020E075E52040000A49086070
:10520000002002F031FF2C0030681049FEF7FAFF51
:10521000002122000023FFF7E3F9FFF79FFF70BD95
:10522000181B00288CED0010F01B002810E000E097
:10522000181B0028A0ED0010F01B002810E000E083
:10523000BE1C0028FFFFFF00010000FFFFFFFFFF73
:1052400014E000E0941C002818E000E040420F0049
:1052500043490A680860704742490A680860704715
@@ -1402,8 +1402,8 @@
:1057800000B5114B1870114B1970002A05D0117813
:105790000F4B197091880F4A1180C0B2002803D0B6
:1057A00001209949087002E0002097490870012003
:1057B0009649087000BD0000AC1A002834F40010AF
:1057C000D0F2001038F40010D71C0028D81C002894
:1057B0009649087000BD0000AC1A0028D8F300100C
:1057C00074F20010DCF30010D71C0028D81C00284D
:1057D000D91C0028B01C0028F8B50500002D05D004
:1057E0000C00002C02D01600002E01D1002039E060
:1057F0000A2100222700380001F0DEFC68682070D2
@@ -1866,7 +1866,7 @@
:107480009205C02000041040084300998860E08AFB
:10749000390C090480B20843218B82B208041043DE
:1074A0000099C86021001A31009800F055F801E0F9
:1074B00020480190019805B0F0BD00004CF3001089
:1074B00020480190019805B0F0BD0000F0F20010E6
:1074C000FFFEFFFFFFFDFFFFFFFBFFFFFFFFFFDFF3
:1074D0003830016801220A43026070473830016881
:1074E00001229143016070473C30016801220A4348
@@ -1874,8 +1874,8 @@
:1075000014D05B6A1C000C80190F0C00E407E40F18
:107510001470990F0C00E407E40F9470590F0C00DD
:10752000E407E40F5470D90FD17000E0014810BD9A
:10753000FFFFFFBF0100DA0050F3001054F300100A
:1075400058F300105CF3001060F30010FFFCFFFF25
:10753000FFFFFFBF0100DA00F4F20010F8F20010C4
:10754000FCF2001000F3001004F30010FFFCFFFF3A
:10755000FFF7FFFFFFF8FFFFF0B50A000023190057
:107560006F4C2468002A49D05579002D01D001269E
:1075700000E01E006B4D2540B4028026F60034402A
@@ -1905,8 +1905,8 @@
:1076F000E407E40FCC7053081C00E407E40F4C705F
:1077000013001C00E407E40F0C7092081300DB0761
:10771000DB0F8B7000E0014810BD00000100DA00B3
:1077200064F30010FFFBFFFFFFFDFFFFFFFEFFFF05
:1077300068F3001000B5012027490A68520802408A
:1077200008F30010FFFBFFFFFFFDFFFFFFFEFFFF61
:107730000CF3001000B5012027490A6852080240E6
:10774000002A01D0022001E00968084000BD274856
:1077500001680800C007C00F704700B5C21E022AAA
:1077600001D3012002E0C01E0860002000BD1CB54E
@@ -1929,7 +1929,7 @@
:107870008007800F32BD0000401626400F48006888
:10788000000AC0B270470E480068000AC0B27047D4
:10789000000000000019264010002740181B002897
:1078A000D4EC00105B080000F01B002840122640BA
:1078A00008ED00105B080000F01B00284012264085
:1078B00004480068000AC0B2704700001010204061
:1078C0000C1020400810204038B50400072C0ED3BF
:1078D000124D18221249280000F066FB0020E875BE
@@ -1937,7 +1937,7 @@
:1078F000A10040584007400F010009B2042907D1F8
:107900000448A1004058C106C90E80204000084329
:1079100000B232BD0001264000122640181B00288C
:1079200030ED0010F01B00281E4909688A03920BF5
:1079200054ED0010F01B00281E4909688A03920BD1
:107930000260090E0A00D207D20FC2711749096806
:107940000A00D204D20C82800A00120CD2B2028148
:10795000090CC9B24181264909680F220B00134066
@@ -2030,7 +2030,7 @@
:107EC000002805D002280BD006D303280CD00FE0E1
:107ED00000982860002032BD009840082860F9E72B
:107EE000009880082860F5E70098C0082860F1E74E
:107EF000002028600320EEE7181B002830ED00105A
:107EF000002028600320EEE7181B002854ED001036
:107F000063090000F01B0028001626407F090000CE
:107F1000001926401CB5040069460020FFF7AFFF9A
:107F20000100C9B2002903D000212160C0B209E0DC
@@ -2352,7 +2352,7 @@
:1092E0000C482A00D2B2805CFFF724FE002808D088
:1092F00068463221405C002803D020000124AC40A5
:1093000004436D1C2800C0B20428E8D32000C0B27A
:109310000FB030BD28F40010C027090000B5017857
:109310000FB030BDCCF30010C027090000B50178B4
:109320000A290ED0417804290BD04178062908D0AB
:109330004178072905D04178082902D040780928CA
:1093400001D1012000E00020C0B200BDF1B590B015
@@ -2378,7 +2378,7 @@
:10948000691848832B895A2270682100C9B24900A3
:10949000415A8B482700FFB2C05DFFF719FC002836
:1094A000E1D1002000E00120F2BD0000FFFF00003C
:1094B0009CF00010781A0028F8B590B00700002F33
:1094B00040F00010781A0028F8B590B00700002F8F
:1094C00004D008003B7BC0B2984201D30020BEE02C
:1094D000B8680B00DBB218246343C0180190904EAB
:1094E00002207070F17001980078B07032610198BC
@@ -2411,13 +2411,13 @@
:10969000641C2000C0B2042810D22B89B27D2000A7
:1096A000C0B240002818418D1E482700FFB2C05D9F
:1096B000FFF70EFB0028EBD1002000E00120F2BDF7
:1096C00028F4001038B5164D2C89641C200080B297
:1096C000CCF3001038B5164D2C89641C200080B2F4
:1096D000F8F7A0FFF8F750FE200080B2FEF756FF23
:1096E0002C8103206870F9F724FB286131BD38B55F
:1096F0000A000B4BD978491C0C00057BE4B2AC4244
:1097000003D30021DC88641CDC80C9B2FFF7D4FEDF
:10971000002801D1FFF7D6FF31BD000030750000F1
:10972000781A002828F40010F8B5C0B0002001F025
:10972000781A0028CCF30010F8B5C0B0002001F082
:1097300046FD20A9002000F098FA070069460120A4
:1097400000F093FA04003800C0B2022801D10125CC
:1097500000E000252000C0B2022801D1012600E06F
@@ -2591,7 +2591,7 @@
:10A1D0001500EDB22D015D196D6865601500EDB2D9
:10A1E0002D015D19AD68A5601500EDB22D015B195B
:10A1F0001B7B2373521C1300DBB2102B00D30022F5
:10A200000A70FAF799FC012032BD000024F3001017
:10A200000A70FAF799FC012032BD0000C8F2001074
:10A2100000000440FF00FFFFFF0000FFF1B594B015
:10A220000D0016005021002422006F463800FCF774
:10A23000C3FF149880210901884203D24E48006868
@@ -2639,7 +2639,7 @@
:10A4D00059188C60027070BD2817002800B5C0B2F2
:10A4E000002802D101200F49086000BDE01B0028B0
:10A4F000DC1B0028D01B0028D41B0028005A620255
:10A500006CEF0010A0EF0010D4EF0010800131407C
:10A5000010EF001044EF001078EF00108001314090
:10A51000C81B0028D81B0028B91C0028BA1C00281A
:10A52000CC1B0028E41B002800B5BFF34F8F3D482B
:10A53000AE490860BFF34F8F00BFFDE7AC490A6822
@@ -2875,9 +2875,9 @@
:10B39000D2B26946002002F027F900280CD0094DEE
:10B3A000182209492800FCF7FFFDEC75F520074934
:10B3B00008600020FCF758FE05B0F0BD00E100E099
:10B3C0001CED00E0181B0028E8ED0010F01B002821
:10B3C0001CED00E0181B0028ECED0010F01B00281D
:10B3D0008BB100100800244000F4244000F5244004
:10B3E000BB1C0028BC1C002814F3001000B500286A
:10B3E000BB1C0028BC1C0028B8F2001000B50028C7
:10B3F00001D0002901D1A04808E0A04A904206D11E
:10B4000000200880802080014860002000BD9C4A08
:10B41000904205D18020800102000A804860F4E754
@@ -2930,7 +2930,7 @@
:10B70000AD5DA4B2ADB2AC42EBD370BD000452400B
:10B7100000065240000852400000544000025440CD
:10B7200000045440000654400008544058080028C3
:10B730000CF3001001085240301B0028F1B5A8B0EE
:10B73000B0F2001001085240301B0028F1B5A8B04B
:10B740000020009000202898FFF7BAFE0700002F85
:10B7500029D0289E306D97490140002902D02898B1
:10B76000FFF73EFF0125306DC00C284000281CD19A
@@ -2970,7 +2970,7 @@
:10B98000009A120A14400C43046078680028D3D04F
:10B9900013AA012101207B689847CDE70648289922
:10B9A000BC22895809094907490F415C891C0020BC
:10B9B000B4E70000EEE0F73F0CF30010F1B594B0EF
:10B9B000B4E70000EEE0F73FB0F20010F1B594B04C
:10B9C000D34800681290D34800681190D2480768A5
:10B9D000D2480668D24800681090D24800680F909C
:10B9E000D14800680490D14800680E90D0480068A3
@@ -3024,15 +3024,15 @@
:10BCE000EB589B0814339B049B0C11409A001040A6
:10BCF0000843C0216850802252021498000C0004AE
:10BD00006946898A4018801806998B08002125E029
:10BD10006CF3001070F3001074F3001078F300104F
:10BD20007CF3001080F3001084F3001088F30010FF
:10BD30008CF3001090F3001094F3001098F30010AF
:10BD40009CF30010A0F30010A4F30010A8F300105F
:10BD5000ACF3001000260660001D491C9942F9D37F
:10BD100010F3001014F3001018F300101CF30010BF
:10BD200020F3001024F3001028F300102CF300106F
:10BD300030F3001034F3001038F300103CF300101F
:10BD400040F3001044F3001048F300104CF30010CF
:10BD500050F3001000260660001D491C9942F9D3DB
:10BD600001990120084302210143CF480840217D69
:10BD7000012901D1012100E00021CC4B0340480200
:10BD80008021890008401843217D01290CD101211F
:10BD90000BE000BFB0F30010B4F30010010062002C
:10BD90000BE000BF54F3001058F3001001006200E4
:10BDA000FFFF00FF0300FFFF0021C14B034008021B
:10BDB000802149000840184380218843402188435E
:10BDC0002021884310218843042188430190019851
@@ -3046,7 +3046,7 @@
:10BE4000E67F0E40F027B8433701F0263E40064318
:10BE50002020205C01400F20864331432220205CBB
:10BE6000012811D1012610E0FFFF80FFFCFF000038
:10BE7000FFF8FFFFFFFFFF800CF30010FFFFFFC084
:10BE7000FFF8FFFFFFFFFF80B0F20010FFFFFFC0E1
:10BE8000FFFFC0FFFFFFFFBF00268F480840F105FE
:10BE90008026360431400143E9602320215C1940AB
:10BEA0000B9E8A4830400E02FE21C90131400143F9
@@ -3126,7 +3126,7 @@
:10C34000684600788540D0203D500020F2BD0000B6
:10C35000301B002801006200FFFFFF1F030A0006D8
:10C36000330A0000FFFFFFDFFFFFFFBFFFFF7FFF7C
:10C37000FFFFF0FFFFFFDFFFFFFFEFFF0CF30010F9
:10C37000FFFFF0FFFFFFDFFFFFFFEFFFB0F2001056
:10C3800010B50200002A03D00800C0B2202801DB4B
:10C39000002021E000200123CC241459CC401C4073
:10C3A000002C09D0D4201058C8401840002801D1D2
@@ -3229,7 +3229,7 @@
:10C9B0002000F7F705FE4D492000F7F77BFC002823
:10C9C00001D000200FE02000F7F707FE4849884219
:10C9D00007D12000F7F7FDFD4649884201D101202B
:10C9E00000E00020C0B210BD24F3001070B5424C2E
:10C9E00000E00020C0B210BDC8F2001070B5424C8B
:10C9F0000021216000F046F8002803D101202060CA
:10CA000000203AE0374E002201213000FFF76FFE90
:10CA1000354D37492800F7F7DEFD2800F7F7CAFD46
@@ -3245,7 +3245,7 @@
:10CAB00032BD10B50C48F7F783FD0A4C0022012166
:10CAC000200000F07BF800220221200000F076F820
:10CAD00010BD0000FF0000FF00FFFF00FFFCFFFF94
:10CAE00038EF001080053140001E584018EB001050
:10CAE000DCEE001080053140001E58400CEB0010B9
:10CAF0001F4E0000204E0000C01B0028F8B5002586
:10CB000000956846FBF706FA002801D0002045E0B2
:10CB1000274C0098A04205D300982100F7F772FB3C
@@ -3316,7 +3316,7 @@
:10CF20001AE0534C2000F7F74BFB52492000F7F76B
:10CF3000C1F9002801D000200EE02000F7F74DFBDA
:10CF40004D49884206D12000F7F743FB002801D164
:10CF5000012000E00020C0B210BD000024F300104A
:10CF5000012000E00020C0B210BD0000C8F20010A7
:10CF600070B5464C0021216000F046F8002803D13E
:10CF70000120206000203AE03B4E002207213000D3
:10CF8000FFF777FE3A4D3C492800F7F724FB2800CD
@@ -3333,8 +3333,8 @@
:10D03000FF0000FF80B50E48F7F7C2FA00F07AF85B
:10D0400000200A49086001BD80B50948F7F7BDFA1C
:10D05000C10B0800C007C00F02BD000000FFFF00A9
:10D06000FFFCFFFF04EF001000083140B81B002850
:10D0700080205840A4EA00101F4E0000BC1B00286E
:10D06000FFFCFFFFA8EE001000083140B81B0028AD
:10D070008020584098EA00101F4E0000BC1B00287A
:10D08000F8B5002500956846FAF744FF002801D05E
:10D09000002045E0354C0098A04205D300982100BF
:10D0A000F7F7B0F8002901D0002039E000982100FE
@@ -3388,11 +3388,11 @@
:10D3A000300029001022F7F749FD00F017F8012896
:10D3B00003D1032008E0022006E000F00FF8002867
:10D3C00001D1002000E00420FEBD0000181B002851
:10D3D00024E90010F01B00280010224010B5274C53
:10D3D000A4E90010F01B00280010224010B5274CD3
:10D3E00010212000FAF762FE2068000F0007A0213C
:10D3F0000906884201D1002009E02068000F0007DB
:10D40000F0210906884201D1012000E0022010BD70
:10D41000ECF30010F0F30010000020400000224068
:10D4100090F3001094F30010000020400000224020
:10D42000FFFFFF0F38B50400FFF7D8FF0500280005
:10D43000C0B2022801D142480DE0002C04D02000E7
:10D4400027491022F7F7FAFCEDB2002D01D1002098
@@ -3411,7 +3411,7 @@
:10D5100001D1012600E002262800F040C00705D511
:10D5200009480168002901D0006880472A0CA9B287
:10D530002000FFF746FE70BDFFFFFF00801B0028A4
:10D54000FFFFFF0F1CF30010E81B0028E5D40010BC
:10D54000FFFFFF0FC0F20010E81B0028E5D4001019
:10D550000000204050480068410B0800C007C00F81
:10D5600070474D480068010B0800C007C00F7047A6
:10D5700000B54A48012102680A43026002680A4075
@@ -3445,7 +3445,7 @@
:10D73000294820188021490288420FD201200EE09A
:10D740008020000255496118814201D2012006E083
:10D7500053496118814201D2012000E0002010BD30
:10D76000FFFF00FF7CF2001010B50400FFF7F9FE88
:10D76000FFFF00FF20F2001010B50400FFF7F9FEE4
:10D77000002810D1F02000062018FE21C90388429D
:10D7800001D2002026E0654820188021C902884285
:10D790001FD200201EE0FE208003F0210906611840
@@ -3504,7 +3504,7 @@
:10DAE00015001E003800FFF7EFFF00280DD12900B8
:10DAF0003800FFF7E5FF31003800FFF7E3FF2100B2
:10DB00003800FFF7D5FF002000E00248F2BD00001A
:10DB1000D8F3001001008A00F8B584B0F7F715FCBF
:10DB10007CF3001001008A00F8B584B0F7F715FC1B
:10DB2000BA49012708680322904338430860B8487F
:10DB30000A6802400A60B7490A6803239A433A43D5
:10DB40000A600A6802400A60B3490A6803239A43DC
@@ -3589,8 +3589,8 @@
:10E030004018FA218900F6F7E5F828602868C0033F
:10E040001749086007B030BD041626400C19264059
:10E050001C1926404016264044162640141226401D
:10E06000E0F2001040122640080020400804204042
:10E070000815264064F20010181B00286CEC0010F4
:10E0600084F200104012264008002040080420409E
:10E070000815264008F20010181B002860EC00105C
:10E08000F01B0028941C0028981C00283F420F0019
:10E0900040420F00A01C00289C1C0028E703000041
:10E0A000A41C0028F3B595B0170000210D0008004E
@@ -3672,7 +3672,7 @@
:10E5600000210120FFF7ECFE0020FFF7DDFE012077
:10E57000FFF7DAFE10BD000000E100E000E400E07B
:10E580001CED00E000132040FFFFF7FF8013204048
:10E59000A013204044F3001064EC001068EC00105D
:10E59000A0132040E8F2001058EC00105CEC0010D2
:10E5A00080000000001D00280000001008ED00E0C1
:10E5B00000B5010009B2002905D40121C006C00E32
:10E5C00081404A48016000BD00B5FEE780B548487B
@@ -3694,23 +3694,23 @@
:10E6C0000620FFF775FF01BD80B51848006818489F
:10E6D0000068C10F002905D007498005800D800022
:10E6E000085880470720FFF763FF01BD80E200E084
:10E6F000B8F300100011204048090028BCF30010B6
:10E7000004112040C0F3001008112040C4F3001091
:10E710000C112040C8F3001010112040CCF3001061
:10E7200014112040D0F3001018112040D4F3001031
:10E730001C11204034F1001090F200108002314092
:10E6F0005CF30010001120404809002860F300106E
:10E700000411204064F300100811204068F3001049
:10E710000C1120406CF300101011204070F3001019
:10E720001411204074F300101811204078F30010E9
:10E730001C112040D8F0001034F20010800231404B
:10E740000400000008000000800658403A01000064
:10E7500000093140020000000A0F0D07E00100002F
:10E76000FC53000098210000F4010000E8030000C1
:10E77000F4010000C0F10010A0F20010800231404E
:10E77000F401000064F1001044F200108002314006
:10E780000500000008000000000758403B010000A1
:10E7900000053140010000000AF0D0707601000051
:10E7A000E0150000D41700005E010000E80300003F
:10E7B000F4010000DCF10010B0F200100004314060
:10E7B000F401000080F1001054F200100004314018
:10E7C00000000000080000008009584040010000DF
:10E7D0000007314000000000090F0D0795030000FD
:10E7E000901A000028230000F4010000E803000054
:10E7F000F4010000F8F10010C0F200108000314078
:10E7F000F40100009CF1001064F200108000314030
:10E800000000000008000000000658403901000028
:10E81000000631400500000009F0D07087020000BA
:10E82000201C000098210000F4010000D007000027
@@ -3729,169 +3729,169 @@
:10E8F00075FCECE7F41B0028281200280648006885
:10E9000070470020C04304490860044908600020A3
:10E91000034908607047000000182640041826408C
:10E9200008102640443A5C6875616E672E787565FC
:10E930007765695C4769745C436F676E6974697673
:10E94000655F4167656E745C2E636F6465785F74A4
:10E950006D705C736561742D6669726D7761726547
:10E960002D6275696C642D3031363932303434396A
:10E97000363564343762306139303435613463613F
:10E98000646131636231385C636F6D6D6F6E5C73AF
:10E9900072635C647269766572735C6970635C63F0
:10E9A000795F6970635F6472762E6800F1B4016804
:10E9B0004118436858080A1800988068DB0701D599
:10E9C0004B46C01891422DD00D78491C03240326D4
:10E9D0002E4002D10E78F61C491C2B090F2B07D1B3
:10E9E0000B780F33491C03E00F78491C0770401C5B
:10E9F000761EF9D1002BE5D00E78491CAD082C40CD
:10EA0000032C01D10C78491C2402341965424419A5
:10EA10000025AE1E9E42D5D02678641C0670401C90
:10EA20006D1CF6E700980C30F2BC704700053140D1
:10EA30000600000000000000801890400200000066
:10EA40000006314001000000000000004019904025
:10EA50000500000000053140070000000000000034
:10EA6000C018904003000000000531400500000080
:10EA700000000000401890400100000000063140F6
:10EA80000200000000000000801990400600000015
:10EA9000000631400600000000000000801A90408F
:10EAA0000A00000004000000000000000000000058
:10EAB00000000000010003031F4E000000000000E2
:10E9200008102640F1B401684118436858080A18D5
:10E9300000988068DB0701D54B46C01891422DD066
:10E940000D78491C032403262E4002D10E78F61CB4
:10E95000491C2B090F2B07D10B780F33491C03E0FF
:10E960000F78491C0770401C761EF9D1002BE5D0AA
:10E970000E78491CAD082C40032C01D10C78491CA1
:10E9800024023419654244190025AE1E9E42D5D09A
:10E990002678641C0670401C6D1CF6E700980C304D
:10E9A000F2BC7047443A5C50726F6A6563745C35C0
:10E9B00031315C4E375765625C2E636F6465785FFA
:10E9C000746D705C736561742D6669726D776172C8
:10E9D000652D6275696C642D326530363863373762
:10E9E00063306635346431386233393863383339EB
:10E9F00063663830633463625C636F6D6D6F6E5C49
:10EA00007372635C647269766572735C6970635C6F
:10EA100063795F6970635F6472762E68000000003E
:10EA20000005314006000000000000008018904002
:10EA3000020000000006314001000000000000005C
:10EA4000401990400500000000053140070000001B
:10EA500000000000C0189040030000000005314095
:10EA60000500000000000000401890400100000078
:10EA700000063140020000000000000080199040B4
:10EA80000600000000063140060000000000000003
:10EA9000801A90400A0000000400000000000000FE
:10EAA0000000000000000000010003031F4E0000F2
:10EAB0000000000000000000000000000000000056
:10EAC0000000000000000000000000000000000046
:10EAD0000000000000000000000000000000000036
:10EAE000000000000203000000000000030000001E
:10EAF0000000000003000000000000000300000010
:10EB000000000000030000000000000003000000FF
:10EB100001000000010200000400000000000000ED
:10EB20000000000000000000010003031F4E000071
:10EB30000000000000000000204E00000000000067
:10EB400000000000000000000000000000000000C5
:10EB500000000000000000000203000000000000B0
:10EB6000030000000000000003000000000000009F
:10EB7000030000000000000003000000000000008F
:10EB8000030000000100000001020000040000007A
:10EB9000000000000000000000000000010003036E
:10EBA00063000000000000000000000032000000D0
:10EAD0000000000000000000020300000000000031
:10EAE0000300000000000000030000000000000020
:10EAF0000300000000000000030000000000000010
:10EB000003000000010000000102000004000000FA
:10EB100000000000000000000000000001000303EE
:10EB20001F4E00000000000000000000204E00000A
:10EB300000000000000000000000000000000000D5
:10EB400000000000000000000000000002030000C0
:10EB500000000000030000000000000003000000AF
:10EB6000000000000300000000000000030000009F
:10EB7000000000000300000001000000010200008E
:10EB80000400000000000000000000000000000081
:10EB9000010003036300000000000000000000000B
:10EBA0003200000000000000000000000000000033
:10EBB0000000000000000000000000000000000055
:10EBC0000000000000000000000000000203000040
:10EBD000000000000300000000000000030000002F
:10EBE000000000000300000000000000030000001F
:10EBF000000000000300000001000000010200000E
:10EC000072B6104810490860BFF34F8F0120C0044E
:10EC10000E490A6802430A600D490A6802430A6005
:10EC20000C490A6802430A60002000210D4A03C211
:10EC30006A45FCD1084FB847084FB847084FB84756
:10EC4000FEE700000000001008ED00E00013204087
:10EC500080132040A0132040B9E30010CDF0001035
:10EC600031F2001000FF0128FFFF0128443A5C68E0
:10EC700075616E672E7875657765695C4769745C48
:10EC8000436F676E69746976655F4167656E745C32
:10EC90006669726D776172655C7432675C747669FF
:10ECA000696268386D5C7372635C73797374656DE7
:10ECB0005C7265765F645C73797374656D5F74769E
:10ECC00069696268386D5F636D30706C75732E634F
:10ECD00000000000443A5C6875616E672E787565C7
:10ECE0007765695C4769745C436F676E69746976C0
:10ECF000655F4167656E745C6669726D77617265A8
:10ED00005C7432675C747669696268386D5C7372D2
:10ED1000635C647269766572735C737973636C6B40
:10ED20005C63795F737973636C6B2E68000000001D
:10ED3000443A5C6875616E672E7875657765695CC5
:10ED40004769745C436F676E69746976655F416794
:10ED5000656E745C6669726D776172655C7432674A
:10ED60005C747669696268386D5C7372635C647246
:10ED700069766572735C737973636C6B5C63795FDE
:10ED8000737973636C6B2E6300000000443A5C6817
:10ED900075616E672E7875657765695C4769745C27
:10EDA000436F676E69746976655F4167656E745C11
:10EDB0006669726D776172655C7432675C636F6DF2
:10EDC0006D6F6E5C7372635C647269766572735C9E
:10EDD0007379737469636B5C63795F737973746956
:10EDE000636B2E6300000000443A5C6875616E67D7
:10EDF0002E7875657765695C4769745C436F676EEB
:10EE000069746976655F4167656E745C6669726D89
:10EE1000776172655C7432675C636F6D6D6F6E5C99
:10EE20007372635C6D775C666C6173685C63795F59
:10EE30006D775F666C6173682E6300009F0F000042
:10EE400000000000000000000000000000000000C2
:10EE500000000300000000000000000000000000AF
:10EE6000000300000000000003000000000000009C
:10EE7000030000000000000003000000000000008C
:10EE8000030000000100000000000000010000007D
:10EE90000000000000000000100000001000000052
:10EEA0001000000001000001000000000000000050
:10EBC000020300000000000003000000000000003D
:10EBD000030000000000000003000000000000002F
:10EBE000030000000000000003000000010000001E
:10EBF0000102000072B6104810490860BFF34F8F41
:10EC00000120C0040E490A6802430A600D490A68DF
:10EC100002430A600C490A6802430A60002000218E
:10EC20000D4A03C26A45FCD1084FB847084FB847A0
:10EC3000084FB847FEE700000000001008ED00E0B4
:10EC40000013204080132040A0132040B9E300109F
:10EC500071F00010D5F1001000FF0128FFFF01281E
:10EC6000443A5C50726F6A6563745C3531315C4E56
:10EC7000375765625C6669726D776172655C743284
:10EC8000675C747669696268386D5C7372635C7323
:10EC9000797374656D5C7265765F645C73797374A7
:10ECA000656D5F747669696268386D5F636D307039
:10ECB0006C75732E630000009F0F000000000000C1
:10ECC0000000000000000000000000000000030041
:10ECD0000000000000000000000000000003000031
:10ECE000000000000300000000000000030000001E
:10ECF000000000000300000000000000030000000E
:10ED00000100000000000000443A5C50726F6A6528
:10ED100063745C3531315C4E375765625C6669728D
:10ED20006D776172655C7432675C7476696962687C
:10ED3000386D5C7372635C647269766572735C7360
:10ED40007973636C6B5C63795F737973636C6B2E3F
:10ED500068000000443A5C50726F6A6563745C3509
:10ED600031315C4E375765625C6669726D776172EE
:10ED7000655C7432675C747669696268386D5C736F
:10ED800072635C647269766572735C737973636CC9
:10ED90006B5C63795F737973636C6B2E6300000047
:10EDA000443A5C50726F6A6563745C3531315C4E15
:10EDB000375765625C6669726D776172655C743243
:10EDC000675C636F6D6D6F6E5C7372635C647269B8
:10EDD000766572735C7379737469636B5C63795F76
:10EDE0007379737469636B2E63000000443A5C505E
:10EDF000726F6A6563745C3531315C4E375765629A
:10EE00005C6669726D776172655C7432675C636FB2
:10EE10006D6D6F6E5C7372635C6D775C666C617355
:10EE2000685C63795F6D775F666C6173682E630001
:10EE300001000000000000000000000010000000C1
:10EE400010000000100000000100000100000000A0
:10EE500000000000000000000000000000000000B2
:10EE600000000000000000000000000070B401235A
:10EE7000002413E00168001D194202D04D466918B4
:10EE8000491E0C60091D121F042AFAD20D009607B4
:10EE900001D50C80AD1C1A4000D02C700268001DFA
:10EEA000002AE7D170BC7047000000000600000097
:10EEB0000000000000000000000000000000000052
:10EEC000000000000000000070B40123002413E0E3
:10EED0000168001D194202D04D466918491E0C6098
:10EEE000091D121F042AFAD20D00960701D50C80C5
:10EEF000AD1C1A4000D02C700268001D002AE7D11A
:10EF000070BC704700000000060000000000000018
:10EF100000000000000000000000000000000000F1
:10EEC0000000000000000000000000000000000042
:10EED0000000000000000000000000000000000032
:10EEE000060000000000000000000000000000001C
:10EEF0000000000000000000000000000000000012
:10EF00000000000000000000000000000000000001
:10EF100000000000080000001500000000000000D4
:10EF200000000000000000000000000000000000E1
:10EF300000000000000000000000000006000000CB
:10EF400000000000000000000000000000000000C1
:10EF300000000000000000000000000000000000D1
:10EF400000000000010000000E000000150000009D
:10EF500000000000000000000000000000000000B1
:10EF600000000000000000000000000000000000A1
:10EF70000800000015000000000000000000000074
:10EF7000000000000000000001000000060000008A
:10EF80000000000000000000000000000000000081
:10EF90000000000000000000000000000000000071
:10EFA000010000000E00000015000000000000003D
:10EFB0000000000000000000000000000000000051
:10EFA0000000000000000000000000000000000061
:10EFB000060000000000000000000000000000004B
:10EFC0000000000000000000000000000000000041
:10EFD000000000000100000006000000000000002A
:10EFE0000000000000000000000000000000000021
:10EFF0000000000000000000000000000000000011
:10F0000000000000000000000000000006000000FA
:10F0100000000000000000000000000000000000F0
:10F0200000000000000000000000000000000000E0
:10F0300000000000000000000000000002000000CE
:10F0400000000000983A00000E01FA00CC01900187
:10F05000020000000300000000000000983A0000D9
:10F06000720182001C02CC010200000005000000B9
:10F070000000000030750000F6001901BD02EE012D
:10F0800002000000060000000000000030750000D3
:10F0900087018C009501940102000000010000002E
:10F0A00034F300103CF000100200000030F40010B7
:10F0B00008003200020000003CF300106CF0001069
:10F0C000020000002CF400100C00320010B50749BB
:10F0D00079441831064C7C44163404E0081D0A6853
:10F0E000891888470100A142F8D110BD080000002E
:10F0F00024000000D5FDFFFFE409000008130028EC
:10F1000000000000A9F8FFFF5E0300003A010000C4
:10F1100000080028000003010000000001000000BA
:10F12000000000000000000001000000FF0F0100CF
:10F13000000000004D4F544F525F494E325F48422D
:10F14000375F4842385F484F52495A4F4E54414CFE
:10F150000000000004207146084202D0EFF309804D
:10F1600001E0EFF3088000B50149884700BD0000C9
:10F170006F80001000F00EF8002801D0FFF7A6FF06
:10F1800000BF00BF002000BF00BFFFF75FFB00F023
:10F1900003F800000020704780B500F001F801BDC1
:10F1A0000746384600F002F8FBE7000080B500BFD4
:10F1B00000BF024A11001820ABBEFBE72600020088
:10F1C0004D4F544F525F494E335F4842355F48427E
:10F1D000365F534541545F50414E00004D4F544FF0
:10F1E000525F494E345F4842335F4842345F424188
:10F1F000434B5245535400004D4F544F525F494EBC
:10F20000315F4842315F4842325F4C454752455377
:10F210005400000004207146084201D0EFF3098039
:10F22000EFF3088000B50149884700BDF1F20010F6
:10F230000548014603B4684680F3098800BF00BF53
:10F2400000BF00BFFFF796FFA5EDF5FE9E029E0AE8
:10F25000AA02AA0ACE03CE0B2A042A0C0003000B32
:10F26000F002F00A00127A00000000000024F4000E
:10F27000008000000080000000800000010002000B
:10F28000040008001000200040008000000100027F
:10F29000484F52495A4F4E54414C5F48414C4C00E4
:10F2A000534541545F50414E5F48414C4C00000073
:10F2B0004241434B524553545F48414C4C0000007F
:10F2C0004C4547524553545F48414C4C00000000A8
:10F2D000E02E0000401F0000E02E0000307500000E
:10F2E0000003000100040103020200020003000504
:10F2F0008169891E0978C9B2016070470000000069
:10F3000000010101010101000001000002030405E8
:10F3100006080C10000000000000000002000000C1
:10F32000010000000403000113140015C7010300CD
:10F33000010000004001BE00F401AE0187018C0015
:10F340009501940100080028FFFF0128000000003B
:10EFD0000000000000000000000000000000000031
:10EFE0000200000000000000983A00000E01FA0044
:10EFF000CC019001020000000300000000000000AE
:10F00000983A0000720182001C02CC01020000004C
:10F01000050000000000000030750000F600190136
:10F02000BD02EE010200000006000000000000002A
:10F030003075000087018C009501940102000000EA
:10F0400001000000D8F20010E0EF00100200000004
:10F05000D4F300100800320002000000E0F20010BB
:10F0600010F0001002000000D0F300100C0032007D
:10F0700010B5074979441831064C7C44163404E035
:10F08000081D0A68891888470100A142F8D110BDFF
:10F090000800000024000000D5FDFFFFE409000087
:10F0A00008130028000000007DF8FFFF5E03000049
:10F0B0003A010000000800280000030100000000E1
:10F0C000010000000000000000000000010000003E
:10F0D000FF0F0100000000004D4F544F525F494E9A
:10F0E000325F4842375F4842385F484F52495A4F73
:10F0F0004E54414C0000000004207146084202D0EA
:10F10000EFF3098001E0EFF3088000B5014988477B
:10F1100000BD00006F80001000F00EF8002801D044
:10F12000FFF7A6FF00BF00BF002000BF00BFFFF732
:10F130008DFB00F003F800000020704780B500F060
:10F1400001F801BD0746384600F002F8FBE7000071
:10F1500080B500BF00BF024A11001820ABBEFBE71C
:10F16000260002004D4F544F525F494E335F4842D4
:10F17000355F4842365F534541545F50414E000071
:10F180004D4F544F525F494E345F4842335F4842BF
:10F19000345F4241434B5245535400004D4F544F4E
:10F1A000525F494E315F4842315F4842325F4C45C1
:10F1B000475245535400000004207146084201D0D4
:10F1C000EFF30980EFF3088000B50149884700BDDF
:10F1D00095F200100548014603B4684680F309889B
:10F1E00000BF00BF00BF00BFFFF796FFA5EDF5FE13
:10F1F0009E029E0AAA02AA0ACE03CE0B2A042A0C59
:10F200000003000BF002F00A00127A000000000078
:10F210000024F40000800000008000000080000056
:10F2200001000200040008001000200040008000DF
:10F2300000010002484F52495A4F4E54414C5F481A
:10F24000414C4C00534541545F50414E5F48414C46
:10F250004C0000004241434B524553545F48414CDF
:10F260004C0000004C4547524553545F48414C4CBC
:10F2700000000000E02E0000401F0000E02E000013
:10F2800030750000000300010004010302020002C7
:10F29000000300058169891E0978C9B201607047C1
:10F2A0000000000000010101010101000001000057
:10F2B0000203040506080C10000000000000000016
:10F2C00002000000010000000403000113140015F7
:10F2D000C7010300010000004001BE00F401AE01BF
:10F2E00087018C009501940100080028FFFF012888
:10F2F000000000000000000000000000000000000E
:10F3000000000000000000000000000000000000FD
:10F3100000000000000000000000000000000000ED
:10F3200000000000000000000000000000000000DD
:10F3300000000000000000000000000000000000CD
:10F3400000000000000000000000000000000000BD
:10F3500000000000000000000000000000000000AD
:10F36000000000000000000000000000000000009D
:10F37000000000000000000000000000000000008D
@@ -3899,27 +3899,27 @@
:10F39000000000000000000000000000000000006D
:10F3A000000000000000000000000000000000005D
:10F3B000000000000000000000000000000000004D
:10F3C000000000000000000000000000000000003D
:10F3D000000000000000000000000000000000002D
:10F3E000000000000000000000000000000000001D
:10F3F000000000000000000000000000000000000D
:10F4000000000000000000000000000000000000FC
:10F4100000000000000000000000000000000000EC
:10F4200000000000000000000001020332325A32E6
:10F430005A5A5A32000102035A5A5A32FEE7000061
:10F44000FEE70000FEE70000FEE70000FEE7000028
:10F45000FEE70000FEE70000FEE70000FEE7000018
:10F46000FEE70000FEE740045500020001000150E5
:10F470000325A40010000132DD0CA00409000401E2
:10F4800001000112060E834CF2286004FFFFFF1FEB
:10F49000020225920807F0010352400001F2060221
:10F4A00018F2060418F2060618F2060818F3060009
:10F4B0005478F10618F2060418F2060618F2050848
:10F4C00018F002FFC9E5001004F1FF04F1FF04F198
:10F4D000FF04F1FF04F1FF04F1FF04F1FF04F14721
:10F4E000046C0248090028060AF321060001F123F2
:10F4F00034F2520868000A01010102030203010FFD
:10F500000F010100000000000000000000000000EA
:10F3C0000000000000000000000000000001020337
:10F3D00032325A325A5A5A32000102035A5A5A32B7
:10F3E000FEE70000FEE70000FEE70000FEE7000089
:10F3F000FEE70000FEE70000FEE70000FEE7000079
:10F40000FEE70000FEE70000FEE7400455000200B2
:10F41000010001500325A40010000132DD0CA004FE
:10F420000900040101000112060E83F0F1286004B6
:10F43000FFFFFF1F020225920807F0010352400060
:10F4400001F2060218F2060418F2060618F206087F
:10F4500018F306005478F10618F2060418F20606AE
:10F4600018F2050818F002FFC9E5001004F1FF04C6
:10F47000F1FF04F1FF04F1FF04F1FF04F1FF04F1D7
:10F48000FF04F147046C0248090028060AF321062C
:10F490000001F12334F2520868000A01010102035D
:10F4A0000203010F0F010100000000000000000036
:10F4B000000000000000000000000000000000004C
:10F4C000000000000000000000000000000000003C
:10F4D000000000000000000000000000000000002C
:10F4E000000000000000000000000000000000001C
:10F4F000000000000000000000000000000000000C
:10F5000000000000000000000000000000000000FB
:10F5100000000000000000000000000000000000EB
:10F5200000000000000000000000000000000000DB
:10F5300000000000000000000000000000000000CB
@@ -3937,11 +3937,6 @@
:10F5F000000000000000000000000000000000000B
:10F6000000000000000000000000000000000000FA
:10F6100000000000000000000000000000000000EA
:10F6200000000000000000000000000000000000DA
:10F6300000000000000000000000000000000000CA
:10F6400000000000000000000000000000000000BA
:10F6500000000000000000000000000000000000AA
:10F66000000000000000000000000000000000009A
:0DF67000000000000000000000000000008D
:040000051000F231C4
:01F6200000E9
:040000051000F1D521
:00000001FF