519e39e49 Changed heartbeat timeout to be 2 seconds on no ignition
996dc4049 Added heartbeat to black loopback testing
79b44cb7e bump version
59f581317 Black (#254)
096486693 no need to store safety only misra output anymore
git-subtree-dir: panda
git-subtree-split: 519e39e494c0b3dd0cf38581302788b779a03c7b
old-commit-hash: 30bb73d527
commatwo_master
parent
1b8e5096c2
commit
ae3c66c783
33 changed files with 1615 additions and 690 deletions
@ -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