feat: add VCU status feedback and map DEBUG safety inputs

This commit is contained in:
shenlefengnai-rgb
2026-10-03 21:52:14 +08:00
parent dbbda5624a
commit 1e7b2e7f96
305 changed files with 32906 additions and 68 deletions
+2
View File
@@ -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
View File
@@ -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;
}