commit
						98cd6147c3
					
				
				 33 changed files with 1615 additions and 690 deletions
			
			
		@ -1 +1 @@ | 
				
			||||
v1.4.1 | 
				
			||||
v1.4.2 | 
				
			||||
@ -0,0 +1,61 @@ | 
				
			||||
// ///////////////////////////////////////////////////////////// //
 | 
				
			||||
// Hardware abstraction layer for all different supported boards //
 | 
				
			||||
// ///////////////////////////////////////////////////////////// //
 | 
				
			||||
#include "board_declarations.h" | 
				
			||||
#include "boards/common.h" | 
				
			||||
 | 
				
			||||
// ///// Board definition and detection ///// //
 | 
				
			||||
#include "drivers/harness.h" | 
				
			||||
#ifdef PANDA | 
				
			||||
  #include "boards/white.h" | 
				
			||||
  #include "boards/grey.h" | 
				
			||||
  #include "boards/black.h" | 
				
			||||
#else | 
				
			||||
  #include "boards/pedal.h" | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
void detect_board_type(void) { | 
				
			||||
  #ifdef PANDA | 
				
			||||
    // SPI lines floating: white (TODO: is this reliable?)
 | 
				
			||||
    if((detect_with_pull(GPIOA, 4, PULL_DOWN)) || (detect_with_pull(GPIOA, 5, PULL_DOWN)) || (detect_with_pull(GPIOA, 6, PULL_DOWN)) || (detect_with_pull(GPIOA, 7, PULL_DOWN))){ | 
				
			||||
      hw_type = HW_TYPE_WHITE_PANDA; | 
				
			||||
      current_board = &board_white; | 
				
			||||
    } else if(detect_with_pull(GPIOA, 13, PULL_DOWN)) { // Rev AB deprecated, so no pullup means black. In REV C, A13 is pulled up to 5V with a 10K
 | 
				
			||||
      hw_type = HW_TYPE_GREY_PANDA; | 
				
			||||
      current_board = &board_grey; | 
				
			||||
    } else { | 
				
			||||
      hw_type = HW_TYPE_BLACK_PANDA; | 
				
			||||
      current_board = &board_black; | 
				
			||||
    } | 
				
			||||
  #else | 
				
			||||
    #ifdef PEDAL | 
				
			||||
      hw_type = HW_TYPE_PEDAL; | 
				
			||||
      current_board = &board_pedal; | 
				
			||||
    #else  | 
				
			||||
      hw_type = HW_TYPE_UNKNOWN; | 
				
			||||
      puts("Hardware type is UNKNOWN!\n"); | 
				
			||||
    #endif | 
				
			||||
  #endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
// ///// Configuration detection ///// //
 | 
				
			||||
bool has_external_debug_serial = 0; | 
				
			||||
bool is_entering_bootmode = 0; | 
				
			||||
 | 
				
			||||
void detect_configuration(void) { | 
				
			||||
  // detect if external serial debugging is present
 | 
				
			||||
  has_external_debug_serial = detect_with_pull(GPIOA, 3, PULL_DOWN); | 
				
			||||
 | 
				
			||||
  #ifdef PANDA | 
				
			||||
    // check if the ESP is trying to put me in boot mode
 | 
				
			||||
    is_entering_bootmode = !detect_with_pull(GPIOB, 0, PULL_UP); | 
				
			||||
  #else | 
				
			||||
    is_entering_bootmode = 0; | 
				
			||||
  #endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ///// Board functions ///// //
 | 
				
			||||
bool board_has_gps(void) { | 
				
			||||
  return ((hw_type == HW_TYPE_GREY_PANDA) || (hw_type == HW_TYPE_BLACK_PANDA)); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,57 @@ | 
				
			||||
// ******************** Prototypes ********************
 | 
				
			||||
typedef void (*board_init)(void); | 
				
			||||
typedef void (*board_enable_can_transciever)(uint8_t transciever, bool enabled); | 
				
			||||
typedef void (*board_enable_can_transcievers)(bool enabled); | 
				
			||||
typedef void (*board_set_led)(uint8_t color, bool enabled); | 
				
			||||
typedef void (*board_set_usb_power_mode)(uint8_t mode); | 
				
			||||
typedef void (*board_set_esp_gps_mode)(uint8_t mode); | 
				
			||||
typedef void (*board_set_can_mode)(uint8_t mode); | 
				
			||||
typedef void (*board_usb_power_mode_tick)(uint64_t tcnt); | 
				
			||||
typedef bool (*board_check_ignition)(void); | 
				
			||||
 | 
				
			||||
struct board { | 
				
			||||
  const char *board_type; | 
				
			||||
  const harness_configuration *harness_config; | 
				
			||||
  board_init init; | 
				
			||||
  board_enable_can_transciever enable_can_transciever; | 
				
			||||
  board_enable_can_transcievers enable_can_transcievers; | 
				
			||||
  board_set_led set_led; | 
				
			||||
  board_set_usb_power_mode set_usb_power_mode; | 
				
			||||
  board_set_esp_gps_mode set_esp_gps_mode; | 
				
			||||
  board_set_can_mode set_can_mode; | 
				
			||||
  board_usb_power_mode_tick usb_power_mode_tick; | 
				
			||||
  board_check_ignition check_ignition; | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
// ******************* Definitions ********************
 | 
				
			||||
// These should match the enum in cereal/log.capnp
 | 
				
			||||
#define HW_TYPE_UNKNOWN 0U | 
				
			||||
#define HW_TYPE_WHITE_PANDA 1U | 
				
			||||
#define HW_TYPE_GREY_PANDA 2U | 
				
			||||
#define HW_TYPE_BLACK_PANDA 3U | 
				
			||||
#define HW_TYPE_PEDAL 4U | 
				
			||||
 | 
				
			||||
// LED colors
 | 
				
			||||
#define LED_RED 0U | 
				
			||||
#define LED_GREEN 1U | 
				
			||||
#define LED_BLUE 2U | 
				
			||||
 | 
				
			||||
// USB power modes
 | 
				
			||||
#define USB_POWER_NONE 0U | 
				
			||||
#define USB_POWER_CLIENT 1U | 
				
			||||
#define USB_POWER_CDP 2U | 
				
			||||
#define USB_POWER_DCP 3U | 
				
			||||
 | 
				
			||||
// ESP modes
 | 
				
			||||
#define ESP_GPS_DISABLED 0U | 
				
			||||
#define ESP_GPS_ENABLED 1U | 
				
			||||
#define ESP_GPS_BOOTMODE 2U | 
				
			||||
 | 
				
			||||
// CAN modes
 | 
				
			||||
#define CAN_MODE_NORMAL 0U | 
				
			||||
#define CAN_MODE_GMLAN_CAN2 1U | 
				
			||||
#define CAN_MODE_GMLAN_CAN3 2U | 
				
			||||
#define CAN_MODE_OBD_CAN2 3U | 
				
			||||
 | 
				
			||||
// ********************* Globals **********************
 | 
				
			||||
uint8_t usb_power_mode = USB_POWER_NONE; | 
				
			||||
@ -0,0 +1,188 @@ | 
				
			||||
// ///////////////////// //
 | 
				
			||||
// Black Panda + Harness //
 | 
				
			||||
// ///////////////////// //
 | 
				
			||||
 | 
				
			||||
void black_enable_can_transciever(uint8_t transciever, bool enabled) { | 
				
			||||
  switch (transciever){ | 
				
			||||
    case 1U: | 
				
			||||
      set_gpio_output(GPIOC, 1, !enabled); | 
				
			||||
      break; | 
				
			||||
    case 2U: | 
				
			||||
      set_gpio_output(GPIOC, 13, !enabled); | 
				
			||||
      break; | 
				
			||||
    case 3U: | 
				
			||||
      set_gpio_output(GPIOA, 0, !enabled); | 
				
			||||
      break; | 
				
			||||
    case 4U: | 
				
			||||
      set_gpio_output(GPIOB, 10, !enabled); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void black_enable_can_transcievers(bool enabled) { | 
				
			||||
  for(uint8_t i=1; i<=4U; i++) | 
				
			||||
    black_enable_can_transciever(i, enabled); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void black_set_led(uint8_t color, bool enabled) { | 
				
			||||
  switch (color){ | 
				
			||||
    case LED_RED: | 
				
			||||
      set_gpio_output(GPIOC, 9, !enabled); | 
				
			||||
      break; | 
				
			||||
     case LED_GREEN: | 
				
			||||
      set_gpio_output(GPIOC, 7, !enabled); | 
				
			||||
      break; | 
				
			||||
    case LED_BLUE: | 
				
			||||
      set_gpio_output(GPIOC, 6, !enabled); | 
				
			||||
      break;  
 | 
				
			||||
    default: | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void black_set_usb_power_mode(uint8_t mode){ | 
				
			||||
  usb_power_mode = mode; | 
				
			||||
  puts("Trying to set USB power mode on black panda. This is not supported.\n"); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void black_set_esp_gps_mode(uint8_t mode) { | 
				
			||||
  switch (mode) { | 
				
			||||
    case ESP_GPS_DISABLED: | 
				
			||||
      // ESP OFF
 | 
				
			||||
      set_gpio_output(GPIOC, 14, 0); | 
				
			||||
      set_gpio_output(GPIOC, 5, 0); | 
				
			||||
      break; | 
				
			||||
    case ESP_GPS_ENABLED: | 
				
			||||
      // ESP ON
 | 
				
			||||
      set_gpio_output(GPIOC, 14, 1); | 
				
			||||
      set_gpio_output(GPIOC, 5, 1); | 
				
			||||
      break; | 
				
			||||
    case ESP_GPS_BOOTMODE: | 
				
			||||
      set_gpio_output(GPIOC, 14, 1); | 
				
			||||
      set_gpio_output(GPIOC, 5, 0); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("Invalid ESP/GPS mode\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void black_set_can_mode(uint8_t mode){ | 
				
			||||
  switch (mode) { | 
				
			||||
    case CAN_MODE_NORMAL: | 
				
			||||
    case CAN_MODE_OBD_CAN2: | 
				
			||||
      if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(car_harness_status == HARNESS_STATUS_NORMAL)) { | 
				
			||||
        // B12,B13: disable OBD mode
 | 
				
			||||
        set_gpio_mode(GPIOB, 12, MODE_INPUT); | 
				
			||||
        set_gpio_mode(GPIOB, 13, MODE_INPUT); | 
				
			||||
 | 
				
			||||
        // B5,B6: normal CAN2 mode
 | 
				
			||||
        set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); | 
				
			||||
        set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); | 
				
			||||
      } else { | 
				
			||||
        // B5,B6: disable normal CAN2 mode
 | 
				
			||||
        set_gpio_mode(GPIOB, 5, MODE_INPUT); | 
				
			||||
        set_gpio_mode(GPIOB, 6, MODE_INPUT); | 
				
			||||
 | 
				
			||||
        // B12,B13: OBD mode
 | 
				
			||||
        set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); | 
				
			||||
        set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); | 
				
			||||
      }      
 | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("Tried to set unsupported CAN mode: "); puth(mode); puts("\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void black_usb_power_mode_tick(uint64_t tcnt){ | 
				
			||||
  UNUSED(tcnt); | 
				
			||||
  // Not applicable
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool black_check_ignition(void){ | 
				
			||||
  // ignition is checked through harness
 | 
				
			||||
  return harness_check_ignition(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void black_init(void) { | 
				
			||||
  common_init_gpio(); | 
				
			||||
 | 
				
			||||
  // A8,A15: normal CAN3 mode
 | 
				
			||||
  set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); | 
				
			||||
  set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); | 
				
			||||
 | 
				
			||||
  // C0: OBD_SBU1 (orientation detection)
 | 
				
			||||
  // C3: OBD_SBU2 (orientation detection)
 | 
				
			||||
  set_gpio_mode(GPIOC, 0, MODE_ANALOG); | 
				
			||||
  set_gpio_mode(GPIOC, 3, MODE_ANALOG); | 
				
			||||
 | 
				
			||||
  // C10: OBD_SBU1_RELAY (harness relay driving output)
 | 
				
			||||
  // C11: OBD_SBU2_RELAY (harness relay driving output)
 | 
				
			||||
  set_gpio_mode(GPIOC, 10, MODE_OUTPUT); | 
				
			||||
  set_gpio_mode(GPIOC, 11, MODE_OUTPUT); | 
				
			||||
  set_gpio_output_type(GPIOC, 10, OUTPUT_TYPE_OPEN_DRAIN); | 
				
			||||
  set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); | 
				
			||||
  set_gpio_output(GPIOC, 10, 1); | 
				
			||||
  set_gpio_output(GPIOC, 11, 1); | 
				
			||||
 | 
				
			||||
  // C8: FAN aka TIM3_CH3
 | 
				
			||||
  set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); | 
				
			||||
 | 
				
			||||
  // C12: GPS load switch. Turn on permanently for now
 | 
				
			||||
  set_gpio_output(GPIOC, 12, true); | 
				
			||||
  //set_gpio_output(GPIOC, 12, false); //TODO: stupid inverted switch on prototype
 | 
				
			||||
 | 
				
			||||
  // Initialize harness
 | 
				
			||||
  harness_init(); | 
				
			||||
 | 
				
			||||
  // Enable CAN transcievers
 | 
				
			||||
  black_enable_can_transcievers(true); | 
				
			||||
 | 
				
			||||
  // Disable LEDs
 | 
				
			||||
  black_set_led(LED_RED, false); | 
				
			||||
  black_set_led(LED_GREEN, false); | 
				
			||||
  black_set_led(LED_BLUE, false); | 
				
			||||
 | 
				
			||||
  // Set normal CAN mode
 | 
				
			||||
  black_set_can_mode(CAN_MODE_NORMAL); | 
				
			||||
 | 
				
			||||
  // flip CAN0 and CAN2 if we are flipped
 | 
				
			||||
  if (car_harness_status == HARNESS_STATUS_NORMAL) { | 
				
			||||
    can_flip_buses(0, 2); | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  // init multiplexer
 | 
				
			||||
  can_set_obd(car_harness_status, false); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
const harness_configuration black_harness_config = { | 
				
			||||
  .has_harness = true, | 
				
			||||
  .GPIO_SBU1 = GPIOC, | 
				
			||||
  .GPIO_SBU2 = GPIOC, | 
				
			||||
  .GPIO_relay_normal = GPIOC, | 
				
			||||
  .GPIO_relay_flipped = GPIOC, | 
				
			||||
  .pin_SBU1 = 0, | 
				
			||||
  .pin_SBU2 = 3, | 
				
			||||
  .pin_relay_normal = 10, | 
				
			||||
  .pin_relay_flipped = 11, | 
				
			||||
  .adc_channel_SBU1 = 10, | 
				
			||||
  .adc_channel_SBU2 = 13 | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const board board_black = { | 
				
			||||
  .board_type = "Black", | 
				
			||||
  .harness_config = &black_harness_config, | 
				
			||||
  .init = black_init, | 
				
			||||
  .enable_can_transciever = black_enable_can_transciever, | 
				
			||||
  .enable_can_transcievers = black_enable_can_transcievers, | 
				
			||||
  .set_led = black_set_led, | 
				
			||||
  .set_usb_power_mode = black_set_usb_power_mode, | 
				
			||||
  .set_esp_gps_mode = black_set_esp_gps_mode, | 
				
			||||
  .set_can_mode = black_set_can_mode, | 
				
			||||
  .usb_power_mode_tick = black_usb_power_mode_tick, | 
				
			||||
  .check_ignition = black_check_ignition | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,82 @@ | 
				
			||||
#ifdef STM32F4 | 
				
			||||
  #include "stm32f4xx_hal_gpio_ex.h" | 
				
			||||
#else | 
				
			||||
  #include "stm32f2xx_hal_gpio_ex.h" | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
// Common GPIO initialization
 | 
				
			||||
void common_init_gpio(void){ | 
				
			||||
  // TODO: Is this block actually doing something???
 | 
				
			||||
  // pull low to hold ESP in reset??
 | 
				
			||||
  // enable OTG out tied to ground
 | 
				
			||||
  GPIOA->ODR = 0; | 
				
			||||
  GPIOB->ODR = 0; | 
				
			||||
  GPIOA->PUPDR = 0; | 
				
			||||
  GPIOB->AFR[0] = 0; | 
				
			||||
  GPIOB->AFR[1] = 0; | 
				
			||||
 | 
				
			||||
  // C2: Voltage sense line
 | 
				
			||||
  set_gpio_mode(GPIOC, 2, MODE_ANALOG); | 
				
			||||
 | 
				
			||||
  // A11,A12: USB
 | 
				
			||||
  set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG_FS); | 
				
			||||
  set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG_FS); | 
				
			||||
  GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12; | 
				
			||||
 | 
				
			||||
  // A9,A10: USART 1 for talking to the ESP / GPS
 | 
				
			||||
  set_gpio_alternate(GPIOA, 9, GPIO_AF7_USART1); | 
				
			||||
  set_gpio_alternate(GPIOA, 10, GPIO_AF7_USART1); | 
				
			||||
 | 
				
			||||
   // B8,B9: CAN 1
 | 
				
			||||
  #ifdef STM32F4 | 
				
			||||
    set_gpio_alternate(GPIOB, 8, GPIO_AF8_CAN1); | 
				
			||||
    set_gpio_alternate(GPIOB, 9, GPIO_AF8_CAN1); | 
				
			||||
  #else | 
				
			||||
    set_gpio_alternate(GPIOB, 8, GPIO_AF9_CAN1); | 
				
			||||
    set_gpio_alternate(GPIOB, 9, GPIO_AF9_CAN1); | 
				
			||||
  #endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Peripheral initialization
 | 
				
			||||
void peripherals_init(void){ | 
				
			||||
  // enable GPIOB, UART2, CAN, USB clock
 | 
				
			||||
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; | 
				
			||||
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; | 
				
			||||
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; | 
				
			||||
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; | 
				
			||||
 | 
				
			||||
  RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_USART2EN; | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_USART3EN; | 
				
			||||
  #ifdef PANDA | 
				
			||||
    RCC->APB1ENR |= RCC_APB1ENR_UART5EN; | 
				
			||||
  #endif | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_CAN2EN; | 
				
			||||
  #ifdef CAN3 | 
				
			||||
    RCC->APB1ENR |= RCC_APB1ENR_CAN3EN; | 
				
			||||
  #endif | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_DACEN; | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;  // main counter
 | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;  // slow loop and pedal
 | 
				
			||||
  RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;  // gmlan_alt
 | 
				
			||||
  //RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
 | 
				
			||||
  //RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
 | 
				
			||||
  RCC->APB2ENR |= RCC_APB2ENR_USART1EN; | 
				
			||||
  RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; | 
				
			||||
  //RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
 | 
				
			||||
  RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; | 
				
			||||
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; | 
				
			||||
  RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Detection with internal pullup
 | 
				
			||||
#define PULL_EFFECTIVE_DELAY 10 | 
				
			||||
bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { | 
				
			||||
  set_gpio_mode(GPIO, pin, MODE_INPUT); | 
				
			||||
  set_gpio_pullup(GPIO, pin, mode); | 
				
			||||
  for (volatile int i=0; i<PULL_EFFECTIVE_DELAY; i++); | 
				
			||||
  bool ret = get_gpio_input(GPIO, pin); | 
				
			||||
  set_gpio_pullup(GPIO, pin, PULL_NONE); | 
				
			||||
  return ret; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,18 @@ | 
				
			||||
// ////////// //
 | 
				
			||||
// Grey Panda //
 | 
				
			||||
// ////////// //
 | 
				
			||||
 | 
				
			||||
// Most hardware functionality is similar to white panda
 | 
				
			||||
const board board_grey = { | 
				
			||||
  .board_type = "Grey", | 
				
			||||
  .harness_config = &white_harness_config, | 
				
			||||
  .init = white_init, | 
				
			||||
  .enable_can_transciever = white_enable_can_transciever, | 
				
			||||
  .enable_can_transcievers = white_enable_can_transcievers, | 
				
			||||
  .set_led = white_set_led, | 
				
			||||
  .set_usb_power_mode = white_set_usb_power_mode, | 
				
			||||
  .set_esp_gps_mode = white_set_esp_gps_mode, | 
				
			||||
  .set_can_mode = white_set_can_mode, | 
				
			||||
  .usb_power_mode_tick = white_usb_power_mode_tick, | 
				
			||||
  .check_ignition = white_check_ignition | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,96 @@ | 
				
			||||
// ///// //
 | 
				
			||||
// Pedal //
 | 
				
			||||
// ///// //
 | 
				
			||||
 | 
				
			||||
void pedal_enable_can_transciever(uint8_t transciever, bool enabled) { | 
				
			||||
  switch (transciever){ | 
				
			||||
    case 1: | 
				
			||||
      set_gpio_output(GPIOB, 3, !enabled); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void pedal_enable_can_transcievers(bool enabled) { | 
				
			||||
  pedal_enable_can_transciever(1U, enabled); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void pedal_set_led(uint8_t color, bool enabled) { | 
				
			||||
  switch (color){ | 
				
			||||
    case LED_RED: | 
				
			||||
      set_gpio_output(GPIOB, 10, !enabled); | 
				
			||||
      break; | 
				
			||||
     case LED_GREEN: | 
				
			||||
      set_gpio_output(GPIOB, 11, !enabled); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void pedal_set_usb_power_mode(uint8_t mode){ | 
				
			||||
  usb_power_mode = mode; | 
				
			||||
  puts("Trying to set USB power mode on pedal. This is not supported.\n"); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void pedal_set_esp_gps_mode(uint8_t mode) { | 
				
			||||
  UNUSED(mode); | 
				
			||||
  puts("Trying to set ESP/GPS mode on pedal. This is not supported.\n"); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void pedal_set_can_mode(uint8_t mode){ | 
				
			||||
  switch (mode) { | 
				
			||||
    case CAN_MODE_NORMAL: | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("Tried to set unsupported CAN mode: "); puth(mode); puts("\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void pedal_usb_power_mode_tick(uint64_t tcnt){ | 
				
			||||
  UNUSED(tcnt); | 
				
			||||
  // Not applicable
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool pedal_check_ignition(void){ | 
				
			||||
  // not supported on pedal
 | 
				
			||||
  return false; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void pedal_init(void) { | 
				
			||||
  common_init_gpio(); | 
				
			||||
 | 
				
			||||
  // C0, C1: Throttle inputs
 | 
				
			||||
  set_gpio_mode(GPIOC, 0, MODE_ANALOG); | 
				
			||||
  set_gpio_mode(GPIOC, 1, MODE_ANALOG); | 
				
			||||
  // DAC outputs on A4 and A5
 | 
				
			||||
  //   apparently they don't need GPIO setup
 | 
				
			||||
 | 
				
			||||
  // Enable transciever
 | 
				
			||||
  pedal_enable_can_transcievers(true); | 
				
			||||
 | 
				
			||||
  // Disable LEDs
 | 
				
			||||
  pedal_set_led(LED_RED, false); | 
				
			||||
  pedal_set_led(LED_GREEN, false); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
const harness_configuration pedal_harness_config = { | 
				
			||||
  .has_harness = false | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const board board_pedal = { | 
				
			||||
  .board_type = "Pedal", | 
				
			||||
  .harness_config = &pedal_harness_config, | 
				
			||||
  .init = pedal_init, | 
				
			||||
  .enable_can_transciever = pedal_enable_can_transciever, | 
				
			||||
  .enable_can_transcievers = pedal_enable_can_transcievers, | 
				
			||||
  .set_led = pedal_set_led, | 
				
			||||
  .set_usb_power_mode = pedal_set_usb_power_mode, | 
				
			||||
  .set_esp_gps_mode = pedal_set_esp_gps_mode, | 
				
			||||
  .set_can_mode = pedal_set_can_mode, | 
				
			||||
  .usb_power_mode_tick = pedal_usb_power_mode_tick, | 
				
			||||
  .check_ignition = pedal_check_ignition, | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,306 @@ | 
				
			||||
// /////////// //
 | 
				
			||||
// White Panda //
 | 
				
			||||
// /////////// //
 | 
				
			||||
 | 
				
			||||
void white_enable_can_transciever(uint8_t transciever, bool enabled) { | 
				
			||||
  switch (transciever){ | 
				
			||||
    case 1U: | 
				
			||||
      set_gpio_output(GPIOC, 1, !enabled); | 
				
			||||
      break; | 
				
			||||
    case 2U: | 
				
			||||
      set_gpio_output(GPIOC, 13, !enabled); | 
				
			||||
      break; | 
				
			||||
    case 3U: | 
				
			||||
      set_gpio_output(GPIOA, 0, !enabled); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void white_enable_can_transcievers(bool enabled) { | 
				
			||||
  for(uint8_t i=1; i<=3U; i++) | 
				
			||||
    white_enable_can_transciever(i, enabled); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void white_set_led(uint8_t color, bool enabled) { | 
				
			||||
  switch (color){ | 
				
			||||
    case LED_RED: | 
				
			||||
      set_gpio_output(GPIOC, 9, !enabled); | 
				
			||||
      break; | 
				
			||||
     case LED_GREEN: | 
				
			||||
      set_gpio_output(GPIOC, 7, !enabled); | 
				
			||||
      break; | 
				
			||||
    case LED_BLUE: | 
				
			||||
      set_gpio_output(GPIOC, 6, !enabled); | 
				
			||||
      break;  
 | 
				
			||||
    default: | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void white_set_usb_power_mode(uint8_t mode){ | 
				
			||||
  bool valid_mode = true; | 
				
			||||
  switch (mode) { | 
				
			||||
    case USB_POWER_CLIENT: | 
				
			||||
      // B2,A13: set client mode
 | 
				
			||||
      set_gpio_output(GPIOB, 2, 0); | 
				
			||||
      set_gpio_output(GPIOA, 13, 1); | 
				
			||||
      break; | 
				
			||||
    case USB_POWER_CDP: | 
				
			||||
      // B2,A13: set CDP mode
 | 
				
			||||
      set_gpio_output(GPIOB, 2, 1); | 
				
			||||
      set_gpio_output(GPIOA, 13, 1); | 
				
			||||
      break; | 
				
			||||
    case USB_POWER_DCP: | 
				
			||||
      // B2,A13: set DCP mode on the charger (breaks USB!)
 | 
				
			||||
      set_gpio_output(GPIOB, 2, 0); | 
				
			||||
      set_gpio_output(GPIOA, 13, 0); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      valid_mode = false; | 
				
			||||
      puts("Invalid usb power mode\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  if (valid_mode) { | 
				
			||||
    usb_power_mode = mode; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void white_set_esp_gps_mode(uint8_t mode) { | 
				
			||||
  switch (mode) { | 
				
			||||
    case ESP_GPS_DISABLED: | 
				
			||||
      // ESP OFF
 | 
				
			||||
      set_gpio_output(GPIOC, 14, 0); | 
				
			||||
      set_gpio_output(GPIOC, 5, 0); | 
				
			||||
      break; | 
				
			||||
    case ESP_GPS_ENABLED: | 
				
			||||
      // ESP ON
 | 
				
			||||
      set_gpio_output(GPIOC, 14, 1); | 
				
			||||
      set_gpio_output(GPIOC, 5, 1); | 
				
			||||
      break; | 
				
			||||
    case ESP_GPS_BOOTMODE: | 
				
			||||
      set_gpio_output(GPIOC, 14, 1); | 
				
			||||
      set_gpio_output(GPIOC, 5, 0); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("Invalid ESP/GPS mode\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void white_set_can_mode(uint8_t mode){ | 
				
			||||
  switch (mode) { | 
				
			||||
    case CAN_MODE_NORMAL: | 
				
			||||
      // B12,B13: disable GMLAN mode
 | 
				
			||||
      set_gpio_mode(GPIOB, 12, MODE_INPUT); | 
				
			||||
      set_gpio_mode(GPIOB, 13, MODE_INPUT); | 
				
			||||
 | 
				
			||||
      // B3,B4: disable GMLAN mode
 | 
				
			||||
      set_gpio_mode(GPIOB, 3, MODE_INPUT); | 
				
			||||
      set_gpio_mode(GPIOB, 4, MODE_INPUT); | 
				
			||||
 | 
				
			||||
      // B5,B6: normal CAN2 mode
 | 
				
			||||
      set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); | 
				
			||||
      set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); | 
				
			||||
 | 
				
			||||
      // A8,A15: normal CAN3 mode
 | 
				
			||||
      set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); | 
				
			||||
      set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); | 
				
			||||
      break; | 
				
			||||
    case CAN_MODE_GMLAN_CAN2: | 
				
			||||
      // B5,B6: disable CAN2 mode
 | 
				
			||||
      set_gpio_mode(GPIOB, 5, MODE_INPUT); | 
				
			||||
      set_gpio_mode(GPIOB, 6, MODE_INPUT); | 
				
			||||
 | 
				
			||||
      // B3,B4: disable GMLAN mode
 | 
				
			||||
      set_gpio_mode(GPIOB, 3, MODE_INPUT); | 
				
			||||
      set_gpio_mode(GPIOB, 4, MODE_INPUT); | 
				
			||||
 | 
				
			||||
      // B12,B13: GMLAN mode
 | 
				
			||||
      set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); | 
				
			||||
      set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); | 
				
			||||
 | 
				
			||||
      // A8,A15: normal CAN3 mode
 | 
				
			||||
      set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); | 
				
			||||
      set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3);   
 | 
				
			||||
      break; | 
				
			||||
    case CAN_MODE_GMLAN_CAN3: | 
				
			||||
      // A8,A15: disable CAN3 mode
 | 
				
			||||
      set_gpio_mode(GPIOA, 8, MODE_INPUT); | 
				
			||||
      set_gpio_mode(GPIOA, 15, MODE_INPUT); | 
				
			||||
 | 
				
			||||
      // B12,B13: disable GMLAN mode
 | 
				
			||||
      set_gpio_mode(GPIOB, 12, MODE_INPUT); | 
				
			||||
      set_gpio_mode(GPIOB, 13, MODE_INPUT); | 
				
			||||
 | 
				
			||||
      // B3,B4: GMLAN mode
 | 
				
			||||
      set_gpio_alternate(GPIOB, 3, GPIO_AF11_CAN3); | 
				
			||||
      set_gpio_alternate(GPIOB, 4, GPIO_AF11_CAN3); | 
				
			||||
 | 
				
			||||
      // B5,B6: normal CAN2 mode
 | 
				
			||||
      set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); | 
				
			||||
      set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); | 
				
			||||
      break;  
 | 
				
			||||
    default: | 
				
			||||
      puts("Tried to set unsupported CAN mode: "); puth(mode); puts("\n"); | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint64_t marker = 0; | 
				
			||||
void white_usb_power_mode_tick(uint64_t tcnt){ | 
				
			||||
  #ifndef BOOTSTUB | 
				
			||||
  #define CURRENT_THRESHOLD 0xF00U | 
				
			||||
  #define CLICKS 5U // 5 seconds to switch modes
 | 
				
			||||
 | 
				
			||||
  uint32_t current = adc_get(ADCCHAN_CURRENT); | 
				
			||||
 | 
				
			||||
  // ~0x9a = 500 ma
 | 
				
			||||
  // puth(current); puts("\n");
 | 
				
			||||
 | 
				
			||||
  switch (usb_power_mode) { | 
				
			||||
    case USB_POWER_CLIENT: | 
				
			||||
      if ((tcnt - marker) >= CLICKS) { | 
				
			||||
        if (!is_enumerated) { | 
				
			||||
          puts("USBP: didn't enumerate, switching to CDP mode\n"); | 
				
			||||
          // switch to CDP
 | 
				
			||||
          white_set_usb_power_mode(USB_POWER_CDP); | 
				
			||||
          marker = tcnt; | 
				
			||||
        } | 
				
			||||
      } | 
				
			||||
      // keep resetting the timer if it's enumerated
 | 
				
			||||
      if (is_enumerated) { | 
				
			||||
        marker = tcnt; | 
				
			||||
      } | 
				
			||||
      break; | 
				
			||||
    case USB_POWER_CDP: | 
				
			||||
      // On the EON, if we get into CDP mode we stay here. No need to go to DCP.
 | 
				
			||||
      #ifndef EON | 
				
			||||
        // been CLICKS clicks since we switched to CDP
 | 
				
			||||
        if ((tcnt-marker) >= CLICKS) { | 
				
			||||
          // measure current draw, if positive and no enumeration, switch to DCP
 | 
				
			||||
          if (!is_enumerated && (current < CURRENT_THRESHOLD)) { | 
				
			||||
            puts("USBP: no enumeration with current draw, switching to DCP mode\n"); | 
				
			||||
            white_set_usb_power_mode(USB_POWER_DCP); | 
				
			||||
            marker = tcnt; | 
				
			||||
          } | 
				
			||||
        } | 
				
			||||
        // keep resetting the timer if there's no current draw in CDP
 | 
				
			||||
        if (current >= CURRENT_THRESHOLD) { | 
				
			||||
          marker = tcnt; | 
				
			||||
        } | 
				
			||||
      #endif | 
				
			||||
      break; | 
				
			||||
    case USB_POWER_DCP: | 
				
			||||
      // been at least CLICKS clicks since we switched to DCP
 | 
				
			||||
      if ((tcnt-marker) >= CLICKS) { | 
				
			||||
        // if no current draw, switch back to CDP
 | 
				
			||||
        if (current >= CURRENT_THRESHOLD) { | 
				
			||||
          puts("USBP: no current draw, switching back to CDP mode\n"); | 
				
			||||
          white_set_usb_power_mode(USB_POWER_CDP); | 
				
			||||
          marker = tcnt; | 
				
			||||
        } | 
				
			||||
      } | 
				
			||||
      // keep resetting the timer if there's current draw in DCP
 | 
				
			||||
      if (current < CURRENT_THRESHOLD) { | 
				
			||||
        marker = tcnt; | 
				
			||||
      } | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      puts("USB power mode invalid\n");  // set_usb_power_mode prevents assigning invalid values
 | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
  #else | 
				
			||||
  UNUSED(tcnt); | 
				
			||||
  #endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool white_check_ignition(void){ | 
				
			||||
  // ignition is on PA1
 | 
				
			||||
  return !get_gpio_input(GPIOA, 1); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void white_init(void) { | 
				
			||||
  common_init_gpio(); | 
				
			||||
 | 
				
			||||
  // C3: current sense
 | 
				
			||||
  set_gpio_mode(GPIOC, 3, MODE_ANALOG); | 
				
			||||
 | 
				
			||||
  // A1: started_alt
 | 
				
			||||
  set_gpio_pullup(GPIOA, 1, PULL_UP); | 
				
			||||
 | 
				
			||||
  // A2, A3: USART 2 for debugging
 | 
				
			||||
  set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); | 
				
			||||
  set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); | 
				
			||||
 | 
				
			||||
  // A4, A5, A6, A7: SPI
 | 
				
			||||
  set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1); | 
				
			||||
  set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1); | 
				
			||||
  set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1); | 
				
			||||
  set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1); | 
				
			||||
 | 
				
			||||
  // Set USB power mode
 | 
				
			||||
  white_set_usb_power_mode(USB_POWER_CLIENT); | 
				
			||||
 | 
				
			||||
  // B12: GMLAN, ignition sense, pull up
 | 
				
			||||
  set_gpio_pullup(GPIOB, 12, PULL_UP); | 
				
			||||
 | 
				
			||||
  /* GMLAN mode pins:
 | 
				
			||||
      M0(B15)  M1(B14)  mode | 
				
			||||
      ======================= | 
				
			||||
      0        0        sleep | 
				
			||||
      1        0        100kbit | 
				
			||||
      0        1        high voltage wakeup | 
				
			||||
      1        1        33kbit (normal) | 
				
			||||
  */ | 
				
			||||
  set_gpio_output(GPIOB, 14, 1); | 
				
			||||
  set_gpio_output(GPIOB, 15, 1); | 
				
			||||
 | 
				
			||||
  // B7: K-line enable
 | 
				
			||||
  set_gpio_output(GPIOB, 7, 1); | 
				
			||||
 | 
				
			||||
  // C12, D2: Setup K-line (UART5)
 | 
				
			||||
  set_gpio_alternate(GPIOC, 12, GPIO_AF8_UART5); | 
				
			||||
  set_gpio_alternate(GPIOD, 2, GPIO_AF8_UART5); | 
				
			||||
  set_gpio_pullup(GPIOD, 2, PULL_UP); | 
				
			||||
 | 
				
			||||
  // L-line enable
 | 
				
			||||
  set_gpio_output(GPIOA, 14, 1); | 
				
			||||
 | 
				
			||||
  // C10, C11: L-Line setup (USART3)
 | 
				
			||||
  set_gpio_alternate(GPIOC, 10, GPIO_AF7_USART3); | 
				
			||||
  set_gpio_alternate(GPIOC, 11, GPIO_AF7_USART3); | 
				
			||||
  set_gpio_pullup(GPIOC, 11, PULL_UP); | 
				
			||||
 | 
				
			||||
  // Enable CAN transcievers
 | 
				
			||||
  white_enable_can_transcievers(true); | 
				
			||||
 | 
				
			||||
  // Disable LEDs
 | 
				
			||||
  white_set_led(LED_RED, false); | 
				
			||||
  white_set_led(LED_GREEN, false); | 
				
			||||
  white_set_led(LED_BLUE, false); | 
				
			||||
 | 
				
			||||
  // Set normal CAN mode
 | 
				
			||||
  white_set_can_mode(CAN_MODE_NORMAL); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
const harness_configuration white_harness_config = { | 
				
			||||
  .has_harness = false | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const board board_white = { | 
				
			||||
  .board_type = "White", | 
				
			||||
  .harness_config = &white_harness_config, | 
				
			||||
  .init = white_init, | 
				
			||||
  .enable_can_transciever = white_enable_can_transciever, | 
				
			||||
  .enable_can_transcievers = white_enable_can_transcievers, | 
				
			||||
  .set_led = white_set_led, | 
				
			||||
  .set_usb_power_mode = white_set_usb_power_mode, | 
				
			||||
  .set_esp_gps_mode = white_set_esp_gps_mode, | 
				
			||||
  .set_can_mode = white_set_can_mode, | 
				
			||||
  .usb_power_mode_tick = white_usb_power_mode_tick, | 
				
			||||
  .check_ignition = white_check_ignition | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,130 @@ | 
				
			||||
uint8_t car_harness_status = 0U; | 
				
			||||
#define HARNESS_STATUS_NC 0U | 
				
			||||
#define HARNESS_STATUS_NORMAL 1U | 
				
			||||
#define HARNESS_STATUS_FLIPPED 2U | 
				
			||||
 | 
				
			||||
// Threshold voltage (mV) for either of the SBUs to be below before deciding harness is connected
 | 
				
			||||
#define HARNESS_CONNECTED_THRESHOLD 2500U | 
				
			||||
 | 
				
			||||
struct harness_configuration { | 
				
			||||
  const bool has_harness; | 
				
			||||
  GPIO_TypeDef *GPIO_SBU1;  
 | 
				
			||||
  GPIO_TypeDef *GPIO_SBU2; | 
				
			||||
  GPIO_TypeDef *GPIO_relay_normal; | 
				
			||||
  GPIO_TypeDef *GPIO_relay_flipped; | 
				
			||||
  uint8_t pin_SBU1; | 
				
			||||
  uint8_t pin_SBU2; | 
				
			||||
  uint8_t pin_relay_normal; | 
				
			||||
  uint8_t pin_relay_flipped; | 
				
			||||
  uint8_t adc_channel_SBU1; | 
				
			||||
  uint8_t adc_channel_SBU2; | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
// this function will be the API for tici
 | 
				
			||||
void set_intercept_relay(bool intercept) { | 
				
			||||
  if (car_harness_status != HARNESS_STATUS_NC) { | 
				
			||||
    if (intercept) { | 
				
			||||
      puts("switching harness to intercept (relay on)\n"); | 
				
			||||
    } else { | 
				
			||||
      puts("switching harness to passthrough (relay off)\n"); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if(car_harness_status == HARNESS_STATUS_NORMAL){ | 
				
			||||
      set_gpio_output(current_board->harness_config->GPIO_relay_normal, current_board->harness_config->pin_relay_normal, !intercept); | 
				
			||||
    } else { | 
				
			||||
      set_gpio_output(current_board->harness_config->GPIO_relay_flipped, current_board->harness_config->pin_relay_flipped, !intercept); | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool harness_check_ignition(void) { | 
				
			||||
  bool ret = false; | 
				
			||||
  switch(car_harness_status){ | 
				
			||||
    case HARNESS_STATUS_NORMAL: | 
				
			||||
      ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2); | 
				
			||||
      break; | 
				
			||||
    case HARNESS_STATUS_FLIPPED: | 
				
			||||
      ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1); | 
				
			||||
      break; | 
				
			||||
    default: | 
				
			||||
      break; | 
				
			||||
  } | 
				
			||||
  return ret; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// TODO: refactor to use harness config
 | 
				
			||||
void harness_setup_ignition_interrupts(void){ | 
				
			||||
  if(car_harness_status == HARNESS_STATUS_NORMAL){ | 
				
			||||
    SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI3_PC; | 
				
			||||
    EXTI->IMR |= (1U << 3); | 
				
			||||
    EXTI->RTSR |= (1U << 3); | 
				
			||||
    EXTI->FTSR |= (1U << 3); | 
				
			||||
    puts("setup interrupts: normal\n"); | 
				
			||||
  } else if(car_harness_status == HARNESS_STATUS_FLIPPED) { | 
				
			||||
    SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI0_PC; | 
				
			||||
    EXTI->IMR |= (1U << 0); | 
				
			||||
    EXTI->RTSR |= (1U << 0); | 
				
			||||
    EXTI->FTSR |= (1U << 0); | 
				
			||||
    NVIC_EnableIRQ(EXTI1_IRQn); | 
				
			||||
    puts("setup interrupts: flipped\n"); | 
				
			||||
  } else { | 
				
			||||
    puts("tried to setup ignition interrupts without harness connected\n"); | 
				
			||||
  } | 
				
			||||
  NVIC_EnableIRQ(EXTI0_IRQn); | 
				
			||||
  NVIC_EnableIRQ(EXTI3_IRQn); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t harness_detect_orientation(void) { | 
				
			||||
  uint8_t ret = HARNESS_STATUS_NC; | 
				
			||||
 | 
				
			||||
  #ifndef BOOTSTUB | 
				
			||||
  uint32_t sbu1_voltage = adc_get(current_board->harness_config->adc_channel_SBU1); | 
				
			||||
  uint32_t sbu2_voltage = adc_get(current_board->harness_config->adc_channel_SBU2); | 
				
			||||
 | 
				
			||||
  // Detect connection and orientation
 | 
				
			||||
  if((sbu1_voltage < HARNESS_CONNECTED_THRESHOLD) || (sbu2_voltage < HARNESS_CONNECTED_THRESHOLD)){ | 
				
			||||
    if (sbu1_voltage < sbu2_voltage) { | 
				
			||||
      // orientation normal
 | 
				
			||||
      ret = HARNESS_STATUS_NORMAL; | 
				
			||||
    } else { | 
				
			||||
      // orientation flipped
 | 
				
			||||
      ret = HARNESS_STATUS_FLIPPED; | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
  #endif | 
				
			||||
 | 
				
			||||
  return ret; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void harness_init(void) { | 
				
			||||
  // delay such that the connection is fully made before trying orientation detection
 | 
				
			||||
  current_board->set_led(LED_BLUE, true); | 
				
			||||
  delay(10000000); | 
				
			||||
  current_board->set_led(LED_BLUE, false); | 
				
			||||
 | 
				
			||||
  // try to detect orientation
 | 
				
			||||
  uint8_t ret = harness_detect_orientation(); | 
				
			||||
  if (ret != HARNESS_STATUS_NC) { | 
				
			||||
    puts("detected car harness with orientation "); puth2(ret); puts("\n"); | 
				
			||||
    car_harness_status = ret; | 
				
			||||
 | 
				
			||||
    // set the SBU lines to be inputs before using the relay. The lines are not 5V tolerant in ADC mode!
 | 
				
			||||
    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); | 
				
			||||
 | 
				
			||||
    // now we have orientation, set pin ignition detection
 | 
				
			||||
    if(car_harness_status == HARNESS_STATUS_NORMAL){ | 
				
			||||
      set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT); | 
				
			||||
    } else { | 
				
			||||
      set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT); | 
				
			||||
    }      
 | 
				
			||||
 | 
				
			||||
    // keep busses connected by default
 | 
				
			||||
    set_intercept_relay(false); | 
				
			||||
 | 
				
			||||
    // setup ignition interrupts
 | 
				
			||||
    harness_setup_ignition_interrupts(); | 
				
			||||
  } else { | 
				
			||||
    puts("failed to detect car harness!\n"); | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,14 @@ | 
				
			||||
// ******************** Prototypes ********************
 | 
				
			||||
void puts(const char *a); | 
				
			||||
void puth(unsigned int i); | 
				
			||||
void puth2(unsigned int i); | 
				
			||||
typedef struct board board; | 
				
			||||
typedef struct harness_configuration harness_configuration; | 
				
			||||
void can_flip_buses(uint8_t bus1, uint8_t bus2); | 
				
			||||
void can_set_obd(uint8_t harness_orientation, bool obd); | 
				
			||||
 | 
				
			||||
// ********************* Globals **********************
 | 
				
			||||
uint8_t hw_type = 0; | 
				
			||||
const board *current_board; | 
				
			||||
bool is_enumerated = 0; | 
				
			||||
uint32_t heartbeat_counter = 0; | 
				
			||||
@ -0,0 +1,11 @@ | 
				
			||||
// ******************** Prototypes ********************
 | 
				
			||||
void puts(const char *a); | 
				
			||||
void puth(unsigned int i); | 
				
			||||
void puth2(unsigned int i); | 
				
			||||
typedef struct board board; | 
				
			||||
typedef struct harness_configuration harness_configuration; | 
				
			||||
 | 
				
			||||
// ********************* Globals **********************
 | 
				
			||||
uint8_t hw_type = 0; | 
				
			||||
const board *current_board; | 
				
			||||
bool is_enumerated = 0; | 
				
			||||
@ -0,0 +1,139 @@ | 
				
			||||
#!/usr/bin/env python | 
				
			||||
 | 
				
			||||
# Loopback test between black panda (+ harness and power) and white/grey panda | 
				
			||||
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test. | 
				
			||||
# To be sure, the test should be run with both harness orientations | 
				
			||||
 | 
				
			||||
from __future__ import print_function | 
				
			||||
import os | 
				
			||||
import sys | 
				
			||||
import time | 
				
			||||
import random | 
				
			||||
import argparse | 
				
			||||
 | 
				
			||||
from hexdump import hexdump | 
				
			||||
from itertools import permutations | 
				
			||||
 | 
				
			||||
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) | 
				
			||||
from panda import Panda | 
				
			||||
 | 
				
			||||
def get_test_string(): | 
				
			||||
  return b"test"+os.urandom(10) | 
				
			||||
 | 
				
			||||
def run_test(sleep_duration): | 
				
			||||
  pandas = Panda.list() | 
				
			||||
  print(pandas) | 
				
			||||
 | 
				
			||||
  # make sure two pandas are connected | 
				
			||||
  if len(pandas) != 2: | 
				
			||||
    print("Connect white/grey and black panda to run this test!") | 
				
			||||
    assert False | 
				
			||||
 | 
				
			||||
  # connect | 
				
			||||
  pandas[0] = Panda(pandas[0]) | 
				
			||||
  pandas[1] = Panda(pandas[1]) | 
				
			||||
 | 
				
			||||
  # find out which one is black | 
				
			||||
  type0 = pandas[0].get_type() | 
				
			||||
  type1 = pandas[1].get_type() | 
				
			||||
 | 
				
			||||
  black_panda = None | 
				
			||||
  other_panda = None | 
				
			||||
   | 
				
			||||
  if type0 == "\x03" and type1 != "\x03": | 
				
			||||
    black_panda = pandas[0] | 
				
			||||
    other_panda = pandas[1] | 
				
			||||
  elif type0 != "\x03" and type1 == "\x03": | 
				
			||||
    black_panda = pandas[1] | 
				
			||||
    other_panda = pandas[0] | 
				
			||||
  else: | 
				
			||||
    print("Connect white/grey and black panda to run this test!") | 
				
			||||
    assert False | 
				
			||||
 | 
				
			||||
  # disable safety modes | 
				
			||||
  black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) | 
				
			||||
  other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) | 
				
			||||
 | 
				
			||||
  # test health packet | 
				
			||||
  print("black panda health", black_panda.health()) | 
				
			||||
  print("other panda health", other_panda.health()) | 
				
			||||
 | 
				
			||||
  # test black -> other | 
				
			||||
  test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration) | 
				
			||||
  test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration) | 
				
			||||
	 | 
				
			||||
 | 
				
			||||
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): | 
				
			||||
  if direction: | 
				
			||||
    print("***************** TESTING (BLACK --> OTHER) *****************") | 
				
			||||
  else: | 
				
			||||
    print("***************** TESTING (OTHER --> BLACK) *****************") | 
				
			||||
 | 
				
			||||
  for send_bus, obd, recv_buses in test_array: | 
				
			||||
    black_panda.send_heartbeat() | 
				
			||||
    other_panda.send_heartbeat() | 
				
			||||
    print("\ntest can: ", send_bus, " OBD: ", obd) | 
				
			||||
     | 
				
			||||
    # set OBD on black panda | 
				
			||||
    black_panda.set_gmlan(True if obd else None) | 
				
			||||
 | 
				
			||||
    # clear and flush | 
				
			||||
    if direction: | 
				
			||||
      black_panda.can_clear(send_bus) | 
				
			||||
    else: | 
				
			||||
      other_panda.can_clear(send_bus) | 
				
			||||
 | 
				
			||||
    for recv_bus in recv_buses: | 
				
			||||
      if direction: | 
				
			||||
        other_panda.can_clear(recv_bus) | 
				
			||||
      else: | 
				
			||||
	black_panda.can_clear(recv_bus) | 
				
			||||
     | 
				
			||||
    black_panda.can_recv() | 
				
			||||
    other_panda.can_recv() | 
				
			||||
 | 
				
			||||
    # send the characters | 
				
			||||
    at = random.randint(1, 2000) | 
				
			||||
    st = get_test_string()[0:8] | 
				
			||||
    if direction: | 
				
			||||
      black_panda.can_send(at, st, send_bus) | 
				
			||||
    else: | 
				
			||||
      other_panda.can_send(at, st, send_bus) | 
				
			||||
    time.sleep(0.1) | 
				
			||||
 | 
				
			||||
    # check for receive | 
				
			||||
    if direction: | 
				
			||||
      cans_echo = black_panda.can_recv() | 
				
			||||
      cans_loop = other_panda.can_recv() | 
				
			||||
    else: | 
				
			||||
      cans_echo = other_panda.can_recv() | 
				
			||||
      cans_loop = black_panda.can_recv() | 
				
			||||
 | 
				
			||||
    loop_buses = [] | 
				
			||||
    for loop in cans_loop: | 
				
			||||
      print("  Loop on bus", str(loop[3])) | 
				
			||||
      loop_buses.append(loop[3]) | 
				
			||||
    if len(cans_loop) == 0: | 
				
			||||
      print("  No loop") | 
				
			||||
     | 
				
			||||
    # test loop buses | 
				
			||||
    recv_buses.sort() | 
				
			||||
    loop_buses.sort() | 
				
			||||
    assert recv_buses == loop_buses | 
				
			||||
    print("  TEST PASSED") | 
				
			||||
 | 
				
			||||
    time.sleep(sleep_duration) | 
				
			||||
  print("\n") | 
				
			||||
 | 
				
			||||
if __name__ == "__main__": | 
				
			||||
  parser = argparse.ArgumentParser() | 
				
			||||
  parser.add_argument("-n", type=int, help="Number of test iterations to run") | 
				
			||||
  parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) | 
				
			||||
  args = parser.parse_args() | 
				
			||||
 | 
				
			||||
  if args.n is None: | 
				
			||||
    while True: | 
				
			||||
      run_test(sleep_duration=args.sleep) | 
				
			||||
  else: | 
				
			||||
    for i in range(args.n): | 
				
			||||
      run_test(sleep_duration=args.sleep) | 
				
			||||
@ -1,2 +1,17 @@ | 
				
			||||
#!/usr/bin/env sh | 
				
			||||
python -m unittest discover . | 
				
			||||
 | 
				
			||||
# Loop over all hardware types: | 
				
			||||
#   HW_TYPE_UNKNOWN 0U | 
				
			||||
#   HW_TYPE_WHITE_PANDA 1U | 
				
			||||
#   HW_TYPE_GREY_PANDA 2U | 
				
			||||
#   HW_TYPE_BLACK_PANDA 3U | 
				
			||||
#   HW_TYPE_PEDAL 4U | 
				
			||||
 | 
				
			||||
# Make sure test fails if one HW_TYPE fails | 
				
			||||
set -e | 
				
			||||
 | 
				
			||||
for hw_type in 0 1 2 3 4 | 
				
			||||
do | 
				
			||||
  echo "Testing HW_TYPE: $hw_type" | 
				
			||||
  HW_TYPE=$hw_type python -m unittest discover . | 
				
			||||
done | 
				
			||||
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue