IQ.Pilot Release Commit @ d23c019
This commit is contained in:
@@ -138,9 +138,11 @@ bus_config_t bus_config[PANDA_CAN_CNT] = {
|
||||
void can_init_all(void) {
|
||||
for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) {
|
||||
bus_config[i].canfd_enabled = false;
|
||||
// Preserve can_data_speed (default 20000U = 2Mbps) through safety model changes.
|
||||
// Auto-detection in can_rx() sets canfd_enabled=true when the first FD frame arrives;
|
||||
// keeping data speed nonzero lets the FDCAN peripheral handle FD frames immediately.
|
||||
// NOTE: do NOT reset can_data_speed here. Matching stock panda, can_init_all() only clears
|
||||
// canfd_enabled (re-discovered on RX via canfd_auto). The old "#ifndef CANFD: can_data_speed = 0U"
|
||||
// fired on every board because CANFD is never defined, zeroing the FD data-phase bitrate on every
|
||||
// set_safety_model() -> CAN-FD frames then failed with form/stuff errors and never re-enabled,
|
||||
// breaking all CAN-FD cars (e.g. Kia EV6) after fingerprinting. can_data_speed is only used by fdcan.
|
||||
can_clear(can_queues[i]);
|
||||
(void)can_init(i);
|
||||
}
|
||||
@@ -169,6 +171,7 @@ void ignition_can_hook(CANPacket_t *msg) {
|
||||
static int tesla_gear = TESLA_DI_GEAR_P;
|
||||
static int toyota_gear = TOYOTA_GEAR_P;
|
||||
static int toyota_hybrid_gear = TOYOTA_HYBRID_GEAR_P;
|
||||
static bool toyota_hybrid_gear_seen = false;
|
||||
|
||||
// GM exception
|
||||
if ((msg->addr == 0x1F1U) && (len == 8)) {
|
||||
@@ -218,8 +221,10 @@ void ignition_can_hook(CANPacket_t *msg) {
|
||||
ignition_can_cnt = 0U;
|
||||
}
|
||||
|
||||
// Toyota/Lexus exception
|
||||
if ((msg->addr == 0x3BCU) && (len == 8)) {
|
||||
// Toyota/Lexus exception. SecOC hybrids (e.g. Sienna 4th gen) report gear on
|
||||
// GEAR_PACKET_HYBRID (0x127); their GEAR_PACKET (0x3BC) does not read Park, so once
|
||||
// the hybrid gear packet is seen, don't let 0x3BC override it (Park -> ignition off).
|
||||
if ((msg->addr == 0x3BCU) && (len == 8) && !toyota_hybrid_gear_seen) {
|
||||
int gear = msg->data[1] & 0x3FU;
|
||||
if ((gear == 0) || (gear == 1) || (gear == 8) || (gear == 16) || (gear == 32)) {
|
||||
toyota_gear = gear;
|
||||
@@ -231,24 +236,14 @@ void ignition_can_hook(CANPacket_t *msg) {
|
||||
if ((msg->addr == 0x127U) && (len == 8)) {
|
||||
int gear = (msg->data[5] >> 4U) & 0xFU;
|
||||
if (gear <= 4) {
|
||||
toyota_hybrid_gear_seen = true;
|
||||
toyota_hybrid_gear = gear;
|
||||
ignition_can = toyota_hybrid_gear != TOYOTA_HYBRID_GEAR_P;
|
||||
ignition_can_cnt = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
// Volkswagen MEB exception
|
||||
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;
|
||||
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;
|
||||
|
||||
|
||||
@@ -117,6 +117,22 @@ static void __attribute__ ((noinline)) enable_fpu(void) {
|
||||
#define HEARTBEAT_IGNITION_CNT_OFF 2U
|
||||
|
||||
// called at 8Hz
|
||||
static volatile bool tick_sample_pending = false;
|
||||
|
||||
// All runtime ADC sampling runs here, in thread context. ADC conversions
|
||||
// busy-wait for tens to hundreds of microseconds, and all interrupts share one
|
||||
// NVIC priority: sampling in the 8Hz tick interrupt delayed the SPI slave's
|
||||
// RX DMA re-arm long enough for the host to clock into an unarmed peripheral
|
||||
// (checksum failures -> NACK retry storms -> multi-second CAN blackouts).
|
||||
static void tick_sample_poll(void) {
|
||||
if (tick_sample_pending) {
|
||||
tick_sample_pending = false;
|
||||
harness_tick();
|
||||
voltage_mV = current_board->read_voltage_mV();
|
||||
current_mA = current_board->read_current_mA();
|
||||
}
|
||||
}
|
||||
|
||||
static void tick_handler(void) {
|
||||
static uint32_t siren_countdown = 0; // siren plays while countdown > 0
|
||||
static uint32_t controls_allowed_countdown = 0;
|
||||
@@ -131,7 +147,7 @@ static void tick_handler(void) {
|
||||
|
||||
// tick drivers at 8Hz
|
||||
fan_tick();
|
||||
harness_tick();
|
||||
tick_sample_pending = true; // ADC sampling deferred to thread context (see tick_sample_poll)
|
||||
simple_watchdog_kick();
|
||||
sound_tick();
|
||||
|
||||
@@ -304,6 +320,10 @@ int main(void) {
|
||||
current_board->set_can_mode(CAN_MODE_NORMAL);
|
||||
harness_init();
|
||||
|
||||
// seed the ADC caches before interrupts are live
|
||||
voltage_mV = current_board->read_voltage_mV();
|
||||
current_mA = current_board->read_current_mA();
|
||||
|
||||
// panda has an FPU, let's use it!
|
||||
enable_fpu();
|
||||
|
||||
@@ -347,6 +367,7 @@ int main(void) {
|
||||
|
||||
// LED should keep on blinking all the time
|
||||
while (true) {
|
||||
tick_sample_poll();
|
||||
if (power_save_status == POWER_SAVE_STATUS_DISABLED) {
|
||||
#ifdef DEBUG_FAULTS
|
||||
if (fault_status == FAULT_STATUS_NONE) {
|
||||
@@ -357,6 +378,7 @@ int main(void) {
|
||||
delay(fade >> 4);
|
||||
led_set(LED_RED, false);
|
||||
delay((MAX_LED_FADE - fade) >> 4);
|
||||
tick_sample_poll();
|
||||
}
|
||||
|
||||
for (uint32_t fade = MAX_LED_FADE; fade > 0U; fade -= 1U) {
|
||||
@@ -364,6 +386,7 @@ int main(void) {
|
||||
delay(fade >> 4);
|
||||
led_set(LED_RED, false);
|
||||
delay((MAX_LED_FADE - fade) >> 4);
|
||||
tick_sample_poll();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FAULTS
|
||||
|
||||
@@ -9,8 +9,10 @@ static int get_health_pkt(void *dat) {
|
||||
struct health_t * health = (struct health_t*)dat;
|
||||
|
||||
health->uptime_pkt = uptime_cnt;
|
||||
health->voltage_pkt = current_board->read_voltage_mV();
|
||||
health->current_pkt = current_board->read_current_mA();
|
||||
// cached values sampled in thread context (see tick_sample_poll in main.c);
|
||||
// reading the ADC here would busy-wait in interrupt context
|
||||
health->voltage_pkt = voltage_mV;
|
||||
health->current_pkt = current_mA;
|
||||
|
||||
health->ignition_line_pkt = (uint8_t)(harness_check_ignition());
|
||||
health->ignition_can_pkt = ignition_can;
|
||||
|
||||
@@ -13,6 +13,10 @@ extern uint8_t hw_type;
|
||||
extern board *current_board;
|
||||
extern uint32_t uptime_cnt;
|
||||
|
||||
// ADC results, sampled in thread context (see tick_sample_poll in main.c)
|
||||
extern uint32_t voltage_mV;
|
||||
extern uint32_t current_mA;
|
||||
|
||||
// heartbeat state
|
||||
extern uint32_t heartbeat_counter;
|
||||
extern bool heartbeat_lost;
|
||||
|
||||
@@ -5,6 +5,10 @@ uint8_t hw_type = 0;
|
||||
board *current_board;
|
||||
uint32_t uptime_cnt = 0;
|
||||
|
||||
// ADC results, sampled in thread context (see tick_sample_poll in main.c)
|
||||
uint32_t voltage_mV = 0;
|
||||
uint32_t current_mA = 0;
|
||||
|
||||
// heartbeat state
|
||||
uint32_t heartbeat_counter = 0;
|
||||
bool heartbeat_lost = false;
|
||||
|
||||
Reference in New Issue
Block a user