feat: add VCU status feedback and map DEBUG safety inputs
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
/* CAN1 is 500 kbit/s; CAN2 is 250 kbit/s. Both share IDs and parsing. */
|
||||
#define AMI_CAN_ID_DRIVETRAIN_DISPLAY 0x182U
|
||||
#define AMI_CAN_ID_VCU_STATUS 0x183U
|
||||
#define AMI_CAN_ID_SDC_HEARTBEAT 0x6A0U
|
||||
#define AMI_CAN_ID_SDC_ADC 0x6A1U
|
||||
#define AMI_CAN_ID_SDC_FREQUENCY 0x6A2U
|
||||
@@ -19,6 +20,7 @@
|
||||
#define AMI_ADC_TIMEOUT_MS 1500U
|
||||
#define AMI_BMS_TIMEOUT_MS 2000U
|
||||
#define AMI_DRIVETRAIN_TIMEOUT_MS 2000U
|
||||
#define AMI_VCU_STATUS_TIMEOUT_MS 500U
|
||||
#define AMI_SCREEN_BOOT_DELAY_MS 800U
|
||||
#define AMI_SCREEN_RESYNC_MS 5000U
|
||||
|
||||
|
||||
+92
-14
@@ -42,6 +42,11 @@ static volatile uint32_t adc_last_rx_ms;
|
||||
static volatile uint32_t bms_status_last_rx_ms;
|
||||
static volatile uint32_t bms_temperature_last_rx_ms;
|
||||
static volatile uint32_t drivetrain_last_rx_ms;
|
||||
static volatile uint32_t vcu_status_last_rx_ms;
|
||||
/* Task 7 = unknown; lock/EBS 0 = unknown, 1 = green, 2 = red. */
|
||||
static volatile uint8_t vcu_display_state[3] = {7U, 0U, 0U};
|
||||
static volatile uint8_t vcu_display_dirty;
|
||||
static volatile bool vcu_status_received;
|
||||
/* Preserve the exact source units even where the HMI has integer controls. */
|
||||
static volatile uint16_t sdc_adc_mv;
|
||||
static volatile uint16_t sdc_frequency_hz[4];
|
||||
@@ -246,12 +251,59 @@ static void SetDashValue(uint32_t index, int32_t value)
|
||||
SetDashValid(index, true);
|
||||
}
|
||||
|
||||
static void UpdateDebugFromSdcGpio(uint16_t levels)
|
||||
{
|
||||
/* PD order supplied by the wiring table; HMI dN order is different. */
|
||||
static const uint8_t debug_index[15] = {
|
||||
0U, 5U, 2U, 6U, 7U, 1U, 3U, 4U,
|
||||
8U, 9U, 10U, 11U, 12U, 13U, 14U
|
||||
};
|
||||
for (uint32_t bit = 0U; bit < 15U; ++bit)
|
||||
{
|
||||
uint32_t index = debug_index[bit];
|
||||
/* All fifteen inputs: low = normal, high = fault. */
|
||||
uint8_t state = (levels & (1U << bit)) ? 2U : 1U;
|
||||
if (display_state.debug_state[index] != state)
|
||||
{
|
||||
display_state.debug_state[index] = state;
|
||||
display_dirty |= DIRTY_DEBUG(index);
|
||||
}
|
||||
}
|
||||
/* d15 (12V_Control) has no source in the fifteen-input table. */
|
||||
}
|
||||
|
||||
static bool DecodeCan(uint32_t identifier, uint32_t id_type,
|
||||
uint32_t length, const uint8_t *data)
|
||||
{
|
||||
uint32_t now = HAL_GetTick();
|
||||
if (id_type == FDCAN_STANDARD_ID)
|
||||
{
|
||||
if (identifier == AMI_CAN_ID_VCU_STATUS)
|
||||
{
|
||||
/* Custom VCU feedback; requests never update these actual states. */
|
||||
if ((length != 8U) || (data[7] != 1U) || (data[0] > 6U) ||
|
||||
(data[1] > 1U) || (data[2] > 1U) || ((data[3] & 0xF8U) != 0U) ||
|
||||
(data[5] != 0U) || (data[6] != 0U))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint8_t states[3] = {
|
||||
(data[3] & 1U) ? data[0] : 7U,
|
||||
(data[3] & 2U) ? (data[1] ? 1U : 2U) : 0U,
|
||||
(data[3] & 4U) ? (data[2] ? 1U : 2U) : 0U
|
||||
};
|
||||
for (uint32_t i = 0U; i < 3U; ++i)
|
||||
{
|
||||
if (vcu_display_state[i] != states[i])
|
||||
{
|
||||
vcu_display_state[i] = states[i];
|
||||
vcu_display_dirty |= (uint8_t)(1U << i);
|
||||
}
|
||||
}
|
||||
vcu_status_last_rx_ms = now;
|
||||
vcu_status_received = true;
|
||||
return true;
|
||||
}
|
||||
if (identifier == AMI_CAN_ID_DRIVETRAIN_DISPLAY)
|
||||
{
|
||||
/* Version 1 display input only; no motor actuation is performed. */
|
||||
@@ -305,7 +357,7 @@ static bool DecodeCan(uint32_t identifier, uint32_t id_type,
|
||||
((uint32_t)data[6] << 16) | ((uint32_t)data[7] << 24);
|
||||
sdc_gpio_received = true;
|
||||
debug_last_rx_ms = now;
|
||||
/* No PD0..PD14 -> named safety signals / normal polarity mapping. */
|
||||
UpdateDebugFromSdcGpio(sdc_gpio_state);
|
||||
return true;
|
||||
}
|
||||
if ((identifier == AMI_CAN_ID_SDC_DAC_ACK) && (length == 4U))
|
||||
@@ -379,7 +431,9 @@ static void ConfigureCanPort(FDCAN_HandleTypeDef *hfdcan)
|
||||
if (HAL_FDCAN_ConfigFilter(hfdcan, &filter) != HAL_OK) { Error_Handler(); }
|
||||
|
||||
filter.FilterIndex = 3U;
|
||||
filter.FilterType = FDCAN_FILTER_DUAL;
|
||||
filter.FilterID1 = AMI_CAN_ID_DRIVETRAIN_DISPLAY;
|
||||
filter.FilterID2 = AMI_CAN_ID_VCU_STATUS;
|
||||
if (HAL_FDCAN_ConfigFilter(hfdcan, &filter) != HAL_OK) { Error_Handler(); }
|
||||
|
||||
filter.IdType = FDCAN_EXTENDED_ID;
|
||||
@@ -507,6 +561,15 @@ static void UpdateTimeoutsAndLeds(void)
|
||||
__disable_irq();
|
||||
/* Sample time after excluding RX IRQs: a newer timestamp must not underflow. */
|
||||
now = HAL_GetTick();
|
||||
if (vcu_status_received &&
|
||||
((now - vcu_status_last_rx_ms) > AMI_VCU_STATUS_TIMEOUT_MS))
|
||||
{
|
||||
vcu_display_state[0] = 7U;
|
||||
vcu_display_state[1] = 0U;
|
||||
vcu_display_state[2] = 0U;
|
||||
vcu_display_dirty |= 7U;
|
||||
vcu_status_received = false;
|
||||
}
|
||||
if ((adc_last_rx_ms == 0U) || ((now - adc_last_rx_ms) > AMI_ADC_TIMEOUT_MS))
|
||||
{
|
||||
SetDashValid(7U, false);
|
||||
@@ -528,28 +591,31 @@ static void UpdateTimeoutsAndLeds(void)
|
||||
SetDashValid(4U, false);
|
||||
SetDashValid(5U, false);
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
/* Recheck within the RX critical section to avoid erasing a new snapshot. */
|
||||
debug_timeout = (debug_last_rx_ms == 0U) ||
|
||||
((now - debug_last_rx_ms) > AMI_CAN_DATA_TIMEOUT_MS);
|
||||
if (debug_timeout && !debug_timeout_applied)
|
||||
{
|
||||
uint32_t i;
|
||||
__disable_irq();
|
||||
for (i = 0U; i < 16U; ++i)
|
||||
{
|
||||
display_state.debug_state[i] = 0U;
|
||||
}
|
||||
display_dirty |= DIRTY_DEBUG_ALL;
|
||||
__enable_irq();
|
||||
debug_timeout_applied = true;
|
||||
}
|
||||
else if (!debug_timeout)
|
||||
{
|
||||
debug_timeout_applied = false;
|
||||
}
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
static void SendNextDirtyValue(void)
|
||||
{
|
||||
static const char *const vcu_prefix[3] = {
|
||||
"current_task=", "lock_state=", "ebs_state="
|
||||
};
|
||||
static const char *const dash_prefix[8] = {
|
||||
"dash.speed.val=", "dash.ts.val=", "dash.mct.val=", "dash.dl.val=",
|
||||
"dash.djwd.val=", "dash.mcuwd.val=", "dash.dtwd.val=", "dash.lvbat.val="
|
||||
@@ -560,6 +626,7 @@ static void SendNextDirtyValue(void)
|
||||
};
|
||||
static uint32_t cursor;
|
||||
uint32_t dirty;
|
||||
uint8_t vcu_dirty;
|
||||
uint32_t bit;
|
||||
uint32_t index;
|
||||
int32_t value;
|
||||
@@ -573,23 +640,31 @@ static void SendNextDirtyValue(void)
|
||||
|
||||
__disable_irq();
|
||||
dirty = display_dirty;
|
||||
vcu_dirty = vcu_display_dirty;
|
||||
__enable_irq();
|
||||
if (dirty == 0U)
|
||||
if ((dirty == 0U) && (vcu_dirty == 0U))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fair rotation prevents rapid source updates from starving other fields. */
|
||||
for (uint32_t offset = 0U; offset < 32U; ++offset)
|
||||
for (uint32_t offset = 0U; offset < 35U; ++offset)
|
||||
{
|
||||
index = (cursor + offset) & 31U;
|
||||
if ((dirty & (1UL << index)) != 0U) { break; }
|
||||
index = (cursor + offset) % 35U;
|
||||
if ((index < 32U) ? ((dirty & (1UL << index)) != 0U)
|
||||
: ((vcu_dirty & (1U << (index - 32U))) != 0U)) { break; }
|
||||
}
|
||||
bit = 1UL << index;
|
||||
cursor = (index + 1U) & 31U;
|
||||
bit = 1UL << ((index < 32U) ? index : index - 32U);
|
||||
cursor = (index + 1U) % 35U;
|
||||
|
||||
__disable_irq();
|
||||
if (index < 8U)
|
||||
if (index >= 32U)
|
||||
{
|
||||
value = vcu_display_state[index - 32U];
|
||||
prefix = vcu_prefix[index - 32U];
|
||||
vcu_display_dirty &= (uint8_t)~bit;
|
||||
}
|
||||
else if (index < 8U)
|
||||
{
|
||||
value = display_state.dash_value[index];
|
||||
prefix = dash_prefix[index];
|
||||
@@ -617,13 +692,14 @@ static void SendNextDirtyValue(void)
|
||||
prefix = debug_prefix;
|
||||
}
|
||||
}
|
||||
display_dirty &= ~bit;
|
||||
if (index < 32U) { display_dirty &= ~bit; }
|
||||
__enable_irq();
|
||||
|
||||
if (!StartScreenAssignment(prefix, value))
|
||||
{
|
||||
__disable_irq();
|
||||
display_dirty |= bit;
|
||||
if (index < 32U) { display_dirty |= bit; }
|
||||
else { vcu_display_dirty |= (uint8_t)bit; }
|
||||
__enable_irq();
|
||||
}
|
||||
}
|
||||
@@ -633,6 +709,7 @@ void AMI_App_Init(void)
|
||||
HAL_GPIO_WritePin(STATUS_LED1_GPIO_Port, STATUS_LED1_Pin, LedInactiveLevel());
|
||||
HAL_GPIO_WritePin(STATUS_LED2_GPIO_Port, STATUS_LED2_Pin, AMI_LED_ACTIVE_LEVEL);
|
||||
display_dirty = DIRTY_DASH_ALL | DIRTY_DEBUG_ALL | DIRTY_COLOR_ALL;
|
||||
vcu_display_dirty = 7U;
|
||||
ConfigureCan();
|
||||
RestartScreenRx();
|
||||
}
|
||||
@@ -649,6 +726,7 @@ void AMI_App_Service(void)
|
||||
{
|
||||
__disable_irq();
|
||||
display_dirty |= DIRTY_DASH_ALL | DIRTY_DEBUG_ALL | DIRTY_COLOR_ALL;
|
||||
vcu_display_dirty |= 7U;
|
||||
__enable_irq();
|
||||
last_screen_sync_ms = now;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user