IQ.Pilot Release Commit @ 661a2de
This commit is contained in:
@@ -238,13 +238,36 @@ void ignition_can_hook(CANPacket_t *msg) {
|
||||
}
|
||||
|
||||
// Volkswagen MEB exception
|
||||
// GE_Fahrstufe: 5=P, 6=R, 7=N, 8/9=D, 10=E, 13/14=T; 0/1/15 transitional/init/error.
|
||||
// Both gear messages are latched independently and OR'd: cars broadcast one
|
||||
// authoritative source (Gateway_73 on ALT_GEAR platforms), and a stale parked
|
||||
// reading on the unused one must not veto the real one.
|
||||
static bool vw_meb_getriebe_out_of_p = false;
|
||||
static bool vw_meb_gateway_out_of_p = false;
|
||||
|
||||
// Getriebe_11->GE_Fahrstufe
|
||||
if ((msg->addr == 0xADU) && (len == 8)) {
|
||||
int fahrstufe = (msg->data[5] >> 2) & 0xFU;
|
||||
vw_meb_getriebe_out_of_p = (fahrstufe >= 6) && (fahrstufe <= 14);
|
||||
}
|
||||
|
||||
// Gateway_73->GE_Fahrstufe
|
||||
if ((msg->addr == 0x3DCU) && (len == 8)) {
|
||||
int fahrstufe = msg->data[5] & 0xFU;
|
||||
vw_meb_gateway_out_of_p = (fahrstufe >= 6) && (fahrstufe <= 14);
|
||||
}
|
||||
|
||||
if ((msg->addr == 0x3C0U) && (len == 4)) {
|
||||
int counter = msg->data[1] & 0xFU;
|
||||
|
||||
static int prev_counter_vw_meb = -1;
|
||||
if ((counter == ((prev_counter_vw_meb + 1) % 16)) && (prev_counter_vw_meb != -1)) {
|
||||
// Klemmen_Status_01->ZAS_Kl_15
|
||||
ignition_can = ((msg->data[2] >> 1) & 1U) != 0U;
|
||||
// Klemmen_Status_01->ZAS_Kl_15, gated on gear out of P: the gateway broadcasts
|
||||
// ZAS_Kl_15=1 with a live counter during network wake with the car off (unlock,
|
||||
// app poll), flapping onroad while parked. No gear message is broadcast while
|
||||
// parked-and-asleep, so gear-unseen means parked.
|
||||
bool vw_meb_out_of_park = vw_meb_getriebe_out_of_p || vw_meb_gateway_out_of_p;
|
||||
ignition_can = (((msg->data[2] >> 1) & 1U) != 0U) && vw_meb_out_of_park;
|
||||
ignition_can_cnt = 0U;
|
||||
}
|
||||
prev_counter_vw_meb = counter;
|
||||
|
||||
@@ -14,8 +14,10 @@ void set_intercept_relay(bool intercept, bool ignition_relay) {
|
||||
harness.relay_driven = true;
|
||||
}
|
||||
|
||||
// wait until we're not reading the analog voltages anymore
|
||||
while (harness.sbu_adc_lock) {}
|
||||
// The relay pins are separate from the SBU sense pins, so no need to wait for
|
||||
// orientation sampling: it runs in thread context, sees relay_driven set, and
|
||||
// discards its result. Spinning on sbu_adc_lock here would deadlock when called
|
||||
// from interrupt context while thread-context sampling holds the lock.
|
||||
|
||||
if (harness.status == HARNESS_STATUS_NORMAL) {
|
||||
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay);
|
||||
@@ -31,20 +33,25 @@ void set_intercept_relay(bool intercept, bool ignition_relay) {
|
||||
}
|
||||
|
||||
bool harness_check_ignition(void) {
|
||||
bool ret = false;
|
||||
// The SBU pins are in analog mode while orientation sampling is in flight
|
||||
// (thread context), so a digital read would return 0. Return the last good
|
||||
// value instead of spinning: this is called from interrupt context, where
|
||||
// waiting for preempted thread-context sampling to finish would deadlock.
|
||||
bool ret = harness.ignition_line;
|
||||
|
||||
// wait until we're not reading the analog voltages anymore
|
||||
while (harness.sbu_adc_lock) {}
|
||||
|
||||
switch(harness.status){
|
||||
case HARNESS_STATUS_NORMAL:
|
||||
ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1);
|
||||
break;
|
||||
case HARNESS_STATUS_FLIPPED:
|
||||
ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (!harness.sbu_adc_lock) {
|
||||
switch(harness.status){
|
||||
case HARNESS_STATUS_NORMAL:
|
||||
ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1);
|
||||
break;
|
||||
case HARNESS_STATUS_FLIPPED:
|
||||
ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2);
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
harness.ignition_line = ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -81,6 +88,12 @@ static uint8_t harness_detect_orientation(void) {
|
||||
set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT);
|
||||
set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT);
|
||||
harness.sbu_adc_lock = false;
|
||||
|
||||
// This runs in thread context and can be preempted by an interrupt driving
|
||||
// the relay mid-sample, which changes the SBU line voltages; discard
|
||||
if (harness.relay_driven) {
|
||||
ret = harness.status;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
#define HARNESS_STATUS_NORMAL 1U
|
||||
#define HARNESS_STATUS_FLIPPED 2U
|
||||
|
||||
// orientation sampling runs in thread context and shares this state with
|
||||
// interrupt handlers, so the fields are volatile
|
||||
struct harness_t {
|
||||
uint8_t status;
|
||||
uint16_t sbu1_voltage_mV;
|
||||
uint16_t sbu2_voltage_mV;
|
||||
bool relay_driven;
|
||||
bool sbu_adc_lock;
|
||||
volatile uint8_t status;
|
||||
volatile uint16_t sbu1_voltage_mV;
|
||||
volatile uint16_t sbu2_voltage_mV;
|
||||
volatile bool ignition_line;
|
||||
volatile bool relay_driven;
|
||||
volatile bool sbu_adc_lock;
|
||||
};
|
||||
extern struct harness_t harness;
|
||||
|
||||
|
||||
@@ -74,6 +74,10 @@ void init_interrupts(bool check_rate_limit){
|
||||
|
||||
for(uint16_t i=0U; i<NUM_INTERRUPTS; i++){
|
||||
interrupts[i].handler = unused_interrupt_handler;
|
||||
// Default priority, lowered so the comms link can preempt everything else and
|
||||
// re-arm its DMA (see IRQ_PRIORITY_COMMS). Shared state is guarded by
|
||||
// ENTER_CRITICAL, which masks all interrupts regardless of priority.
|
||||
NVIC_SetPriority((IRQn_Type)i, IRQ_PRIORITY_DEFAULT);
|
||||
}
|
||||
|
||||
// Init interrupt timer for a 1s interval
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
// The SPI slave must re-arm its RX DMA at every protocol turnaround before the
|
||||
// master clocks the next phase. Without preemption that re-arm waits behind any
|
||||
// in-flight handler (CAN RX under bus load), the master clocks into an unarmed
|
||||
// peripheral, and the transfer fails its checksum -> NACK retry storms.
|
||||
#define IRQ_PRIORITY_COMMS 0U
|
||||
#define IRQ_PRIORITY_DEFAULT 2U
|
||||
|
||||
typedef struct interrupt {
|
||||
IRQn_Type irq_type;
|
||||
void (*handler)(void);
|
||||
|
||||
Reference in New Issue
Block a user