Files

307 lines
9.3 KiB
C++

/* Included into the generated main_cm0plus.c for the eight Android-controlled
* direction-test images. It intentionally lives in the same translation unit
* so it can reuse the proven static motor safety/event implementation without
* changing the bundled third_party reference source. */
static bool otaInteractiveDriverInitOk;
static bool otaInteractiveFinalized = true;
static void OtaInteractive_ClearResult(void)
{
simpleDirectionResult.risingEdges = 0ul;
simpleDirectionResult.totalEdges = 0ul;
simpleDirectionResult.activeMs = 0ul;
simpleDirectionResult.safetyWindowPulses = 0ul;
simpleDirectionResult.driverStatus = 0u;
simpleDirectionResult.driverFaults = 0u;
simpleDirectionResult.finalHallLevel = 0u;
simpleDirectionResult.stopReason =
SIMPLE_DIRECTION_STOP_REASON_NOT_STARTED;
simpleDirectionResult.armed = false;
simpleDirectionResult.prepared = false;
simpleDirectionResult.timingValid = false;
simpleDirectionResult.diagnosticsValid = false;
simpleDirectionRunActive = false;
simpleDirectionRunComplete = false;
}
static void OtaInteractive_Finalize(void)
{
uint8_t driverStatus = 0u;
bool statusReadOk;
bool stopOk;
if((false == simpleDirectionRunComplete) ||
(false != otaInteractiveFinalized))
{
return;
}
Motor_ServiceAll();
statusReadOk = Drv8718_ReadRegRaw(DRV8718_REG_IC_STAT1,
&driverStatus);
if(false != statusReadOk)
{
simpleDirectionResult.driverStatus = driverStatus;
simpleDirectionResult.driverFaults =
(uint16_t)(driverStatus & DRV8718_IC_STAT1_ALERT_MASK);
}
stopOk = (MOTOR_API_SUCCESS ==
Motor_StopMode((motor_mode_t)MOTOR_AUTOMATED_TEST_MODE));
if(false != stopOk)
{
stopOk = Motor_WaitForCoastToSettle(
(motor_mode_t)MOTOR_AUTOMATED_TEST_MODE);
}
simpleDirectionResult.diagnosticsValid = statusReadOk && stopOk;
SimpleDirectionTest_ImmediateAllOff();
(void)OtaSeat_SavePersistentState();
otaInteractiveFinalized = true;
}
static void OtaInteractive_Stop(void)
{
uint32_t savedIntrStatus;
if(false != simpleDirectionRunActive)
{
savedIntrStatus = Cy_SysLib_EnterCriticalSection();
Motor_RequestEventFromIsr(MOTOR_AUTOMATED_TEST_MODE,
MOTOR_EVENT_ABORTED);
Cy_SysLib_ExitCriticalSection(savedIntrStatus);
}
if((false == simpleDirectionRunComplete) &&
(false == otaInteractiveFinalized))
{
SimpleDirectionTest_ImmediateAllOff();
SimpleDirectionTest_CaptureStopFromIsr(
(uint32_t)(hallTimebaseTicks - simpleDirectionStartTicks),
Hall_ReadLevel(MOTOR_AUTOMATED_TEST_MODE),
SIMPLE_DIRECTION_STOP_REASON_ABORTED,
motorRuntime[MOTOR_AUTOMATED_TEST_MODE].recentWindowPulses);
}
OtaInteractive_Finalize();
}
static bool OtaInteractive_Start(void)
{
if((false != simpleDirectionRunActive) ||
(false != driverFaultLatched))
{
return false;
}
OtaInteractive_ClearResult();
simpleDirectionResult.armed = true;
simpleDirectionResult.prepared =
SimpleDirectionTest_Prepare(otaInteractiveDriverInitOk);
if(false == simpleDirectionResult.prepared)
{
simpleDirectionResult.prepared = false;
SimpleDirectionTest_ImmediateAllOff();
return false;
}
/* OtaInteractiveTest_Run() and every completed/stopped test use
* SimpleDirectionTest_ImmediateAllOff(), which asserts the external
* DRVOFF pin. The original one-shot direction test never starts again
* after that call, but an interactive OTA image must explicitly release
* DRVOFF before each new TEST_START. Without this transition the SPI
* bridge/PWM setup succeeds while the DRV8718 power outputs remain
* hardware-disabled (IC_STAT1 still reads 0x80 and Hall stays at zero). */
Cy_GPIO_Clr(DRV_DRVOFF_PORT, DRV_DRVOFF_PIN);
Cy_SysTick_DelayInUs(100ul);
if(false == SimpleDirectionTest_Start())
{
simpleDirectionResult.prepared = false;
SimpleDirectionTest_ImmediateAllOff();
return false;
}
otaInteractiveFinalized = false;
return true;
}
static uint32_t OtaInteractive_StateValue(void)
{
uint32_t value =
((uint32_t)simpleDirectionResult.stopReason <<
OTA_TEST_STATE_STOP_REASON_SHIFT) |
((uint32_t)MOTOR_AUTOMATED_TEST_MODE <<
OTA_TEST_STATE_MODE_SHIFT) |
((uint32_t)MOTOR_AUTOMATED_TEST_DIRECTION <<
OTA_TEST_STATE_DIRECTION_SHIFT);
if(false != simpleDirectionRunActive)
{
value |= OTA_TEST_STATE_ACTIVE;
}
if(false != simpleDirectionRunComplete)
{
value |= OTA_TEST_STATE_COMPLETE;
}
if(false != driverFaultLatched)
{
value |= OTA_TEST_STATE_DRIVER_FAULT;
}
if(false != simpleDirectionResult.prepared)
{
value |= OTA_TEST_STATE_PREPARED;
}
if(false != simpleDirectionResult.timingValid)
{
value |= OTA_TEST_STATE_TIMING_VALID;
}
if(false != simpleDirectionResult.diagnosticsValid)
{
value |= OTA_TEST_STATE_DIAGNOSTICS_VALID;
}
return value;
}
static uint32_t OtaInteractive_InfoValue(uint8_t selector)
{
switch((ota_test_info_selector_t)selector)
{
case OTA_TEST_INFO_STATE:
return OtaInteractive_StateValue();
case OTA_TEST_INFO_RISING_EDGES:
return simpleDirectionResult.risingEdges;
case OTA_TEST_INFO_TOTAL_EDGES:
return simpleDirectionResult.totalEdges;
case OTA_TEST_INFO_ACTIVE_MS:
return simpleDirectionResult.activeMs;
case OTA_TEST_INFO_SAFETY_WINDOW_PULSES:
return simpleDirectionResult.safetyWindowPulses;
case OTA_TEST_INFO_DRIVER_DIAGNOSTICS:
return ((uint32_t)simpleDirectionResult.driverFaults << 16u) |
(uint32_t)simpleDirectionResult.driverStatus;
default:
return 0ul;
}
}
static void OtaInteractive_Respond(const ota_can_control_frame_t *frame,
ota_status_t status,
uint32_t value)
{
(void)OtaCan_QueueResponse(frame->data[1], frame->data[2],
(uint8_t)status, value);
}
static void OtaInteractive_EnterBootloader(
const ota_can_control_frame_t *frame)
{
uint32_t waitMs;
OtaInteractive_Stop();
(void)OtaSeat_SavePersistentState();
OtaInteractive_Respond(frame, OTA_STATUS_OK,
OtaInteractive_StateValue());
for(waitMs = 0ul; waitMs < 100ul; waitMs++)
{
OtaCan_Service();
if(false != OtaCan_TxIdle())
{
break;
}
Cy_SysTick_DelayInUs(1000ul);
}
SimpleDirectionTest_ImmediateAllOff();
Ota_EnterBootloader();
}
static void OtaInteractive_HandleControl(
const ota_can_control_frame_t *frame)
{
uint8_t opcode = frame->data[2];
if(frame->data[0] != OTA_CONTROL_MAGIC)
{
OtaInteractive_Respond(frame, OTA_STATUS_BAD_MAGIC, 0ul);
return;
}
if(false != OtaSeat_HandleInfoControl(frame))
{
return;
}
switch((ota_opcode_t)opcode)
{
case OTA_OPCODE_TEST_INFO:
if(frame->data[3] > OTA_TEST_INFO_DRIVER_DIAGNOSTICS)
{
OtaInteractive_Respond(frame, OTA_STATUS_BAD_ARGUMENT,
frame->data[3]);
}
else
{
OtaInteractive_Respond(
frame, OTA_STATUS_OK,
OtaInteractive_InfoValue(frame->data[3]));
}
break;
case OTA_OPCODE_TEST_START:
if(false != OtaInteractive_Start())
{
OtaInteractive_Respond(frame, OTA_STATUS_OK,
OtaInteractive_StateValue());
}
else
{
OtaInteractive_Respond(frame, OTA_STATUS_BAD_STATE,
OtaInteractive_StateValue());
}
break;
case OTA_OPCODE_TEST_STOP:
OtaInteractive_Stop();
OtaInteractive_Respond(frame, OTA_STATUS_OK,
OtaInteractive_StateValue());
break;
case OTA_OPCODE_ENTER_BOOTLOADER:
OtaInteractive_EnterBootloader(frame);
break;
default:
OtaInteractive_Respond(frame, OTA_STATUS_BAD_OPCODE, opcode);
break;
}
}
static int OtaInteractiveTest_Run(bool driverInitOk)
{
ota_can_control_frame_t frame;
/* Keep the original one-shot runner linked for source-level parity; the
* interactive build intentionally dispatches through this loop instead. */
(void)SimpleDirectionTest_Run;
otaInteractiveDriverInitOk = driverInitOk;
OtaInteractive_ClearResult();
OtaSeat_RestorePersistentState();
SimpleDirectionTest_ImmediateAllOff();
OtaCan_DebugStart();
if(false == OtaCan_Init())
{
for(;;)
{
SimpleDirectionTest_ImmediateAllOff();
}
}
for(;;)
{
Motor_ServiceAll();
OtaCan_Service();
if((false != simpleDirectionRunComplete) &&
(false == otaInteractiveFinalized))
{
OtaInteractive_Finalize();
}
while(false != OtaCan_PopControl(&frame))
{
OtaInteractive_HandleControl(&frame);
OtaCan_Service();
}
__WFI();
}
}