diff --git a/board/board.h b/board/board.h index 154f32132ea9d6..5629a841d3f5fd 100644 --- a/board/board.h +++ b/board/board.h @@ -7,9 +7,12 @@ // ///// Board definition and detection ///// // #include "drivers/harness.h" #ifdef PANDA + #include "drivers/fan.h" + #include "drivers/rtc.h" #include "boards/white.h" #include "boards/grey.h" #include "boards/black.h" + #include "boards/uno.h" #else #include "boards/pedal.h" #endif @@ -23,6 +26,9 @@ void detect_board_type(void) { } 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 if(!detect_with_pull(GPIOB, 15, PULL_UP)) { + hw_type = HW_TYPE_UNO; + current_board = &board_uno; } else { hw_type = HW_TYPE_BLACK_PANDA; current_board = &board_black; @@ -31,7 +37,7 @@ void detect_board_type(void) { #ifdef PEDAL hw_type = HW_TYPE_PEDAL; current_board = &board_pedal; - #else + #else hw_type = HW_TYPE_UNKNOWN; puts("Hardware type is UNKNOWN!\n"); #endif @@ -60,6 +66,27 @@ void detect_configuration(void) { } // ///// Board functions ///// // +// TODO: Make these config options in the board struct bool board_has_gps(void) { - return ((hw_type == HW_TYPE_GREY_PANDA) || (hw_type == HW_TYPE_BLACK_PANDA)); -} \ No newline at end of file + return ((hw_type == HW_TYPE_GREY_PANDA) || (hw_type == HW_TYPE_BLACK_PANDA) || (hw_type == HW_TYPE_UNO)); +} + +bool board_has_gmlan(void) { + return ((hw_type == HW_TYPE_WHITE_PANDA) || (hw_type == HW_TYPE_GREY_PANDA)); +} + +bool board_has_obd(void) { + return ((hw_type == HW_TYPE_BLACK_PANDA) || (hw_type == HW_TYPE_UNO)); +} + +bool board_has_lin(void) { + return ((hw_type == HW_TYPE_WHITE_PANDA) || (hw_type == HW_TYPE_GREY_PANDA)); +} + +bool board_has_rtc(void) { + return (hw_type == HW_TYPE_UNO); +} + +bool board_has_relay(void) { + return ((hw_type == HW_TYPE_BLACK_PANDA) || (hw_type == HW_TYPE_UNO)); +} diff --git a/board/board_declarations.h b/board/board_declarations.h index 21eb140c3ef272..99b8c8cc97c624 100644 --- a/board/board_declarations.h +++ b/board/board_declarations.h @@ -8,6 +8,9 @@ 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); +typedef uint32_t (*board_read_current)(void); +typedef void (*board_set_ir_power)(uint8_t percentage); +typedef void (*board_set_fan_power)(uint8_t percentage); struct board { const char *board_type; @@ -21,6 +24,9 @@ struct board { board_set_can_mode set_can_mode; board_usb_power_mode_tick usb_power_mode_tick; board_check_ignition check_ignition; + board_read_current read_current; + board_set_ir_power set_ir_power; + board_set_fan_power set_fan_power; }; // ******************* Definitions ******************** @@ -30,6 +36,7 @@ struct board { #define HW_TYPE_GREY_PANDA 2U #define HW_TYPE_BLACK_PANDA 3U #define HW_TYPE_PEDAL 4U +#define HW_TYPE_UNO 5U // LED colors #define LED_RED 0U diff --git a/board/boards/black.h b/board/boards/black.h index bfe598ed18b901..f033e82b23ed34 100644 --- a/board/boards/black.h +++ b/board/boards/black.h @@ -133,6 +133,19 @@ bool black_check_ignition(void){ return harness_check_ignition(); } +uint32_t black_read_current(void){ + // No current sense on black panda + return 0U; +} + +void black_set_ir_power(uint8_t percentage){ + UNUSED(percentage); +} + +void black_set_fan_power(uint8_t percentage){ + UNUSED(percentage); +} + void black_init(void) { common_init_gpio(); @@ -154,9 +167,6 @@ void black_init(void) { 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); - // Turn on GPS load switch. black_set_gps_load_switch(true); @@ -214,5 +224,8 @@ const board board_black = { .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 + .check_ignition = black_check_ignition, + .read_current = black_read_current, + .set_fan_power = black_set_fan_power, + .set_ir_power = black_set_ir_power }; diff --git a/board/boards/common.h b/board/boards/common.h index d176e4eaf561a0..e33b2a2f0426c4 100644 --- a/board/boards/common.h +++ b/board/boards/common.h @@ -58,16 +58,18 @@ void peripherals_init(void){ #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_TIM3EN; // pedal and fan PWM + RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // gmlan_alt and IR PWM //RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; //RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; + RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config 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; + RCC->APB2ENR |= RCC_APB2ENR_TIM9EN; // slow loop } // Detection with internal pullup diff --git a/board/boards/grey.h b/board/boards/grey.h index 1927b5459196bb..1a39bce07fad9b 100644 --- a/board/boards/grey.h +++ b/board/boards/grey.h @@ -14,5 +14,8 @@ const board board_grey = { .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 + .check_ignition = white_check_ignition, + .read_current = white_read_current, + .set_fan_power = white_set_fan_power, + .set_ir_power = white_set_ir_power }; \ No newline at end of file diff --git a/board/boards/pedal.h b/board/boards/pedal.h index 9209a33ba79fdf..02612d3f0966e7 100644 --- a/board/boards/pedal.h +++ b/board/boards/pedal.h @@ -60,6 +60,19 @@ bool pedal_check_ignition(void){ return false; } +uint32_t pedal_read_current(void){ + // No current sense on pedal + return 0U; +} + +void pedal_set_ir_power(uint8_t percentage){ + UNUSED(percentage); +} + +void pedal_set_fan_power(uint8_t percentage){ + UNUSED(percentage); +} + void pedal_init(void) { common_init_gpio(); @@ -93,4 +106,7 @@ const board board_pedal = { .set_can_mode = pedal_set_can_mode, .usb_power_mode_tick = pedal_usb_power_mode_tick, .check_ignition = pedal_check_ignition, + .read_current = pedal_read_current, + .set_fan_power = pedal_set_fan_power, + .set_ir_power = pedal_set_ir_power }; \ No newline at end of file diff --git a/board/boards/uno.h b/board/boards/uno.h new file mode 100644 index 00000000000000..a371bf62b02970 --- /dev/null +++ b/board/boards/uno.h @@ -0,0 +1,247 @@ +// ///////////// // +// Uno + Harness // +// ///////////// // + +void uno_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 uno_enable_can_transcievers(bool enabled) { + for(uint8_t i=1U; i<=4U; i++){ + uno_enable_can_transciever(i, enabled); + } +} + +void uno_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 uno_set_gps_load_switch(bool enabled) { + set_gpio_output(GPIOC, 12, enabled); +} + +void uno_set_usb_power_mode(uint8_t mode) { + UNUSED(mode); + puts("Setting USB mode makes no sense on UNO\n"); +} + +void uno_set_esp_gps_mode(uint8_t mode) { + switch (mode) { + case ESP_GPS_DISABLED: + // GPS OFF + set_gpio_output(GPIOB, 1, 0); + set_gpio_output(GPIOC, 5, 0); + uno_set_gps_load_switch(false); + break; + case ESP_GPS_ENABLED: + // GPS ON + set_gpio_output(GPIOB, 1, 1); + set_gpio_output(GPIOC, 5, 1); + uno_set_gps_load_switch(true); + break; + case ESP_GPS_BOOTMODE: + set_gpio_output(GPIOB, 1, 1); + set_gpio_output(GPIOC, 5, 0); + uno_set_gps_load_switch(true); + break; + default: + puts("Invalid ESP/GPS mode\n"); + break; + } +} + +void uno_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 uno_set_bootkick(bool enabled){ + set_gpio_output(GPIOB, 14, !enabled); +} + +void uno_usb_power_mode_tick(uint64_t tcnt){ + if(tcnt == 3U){ + uno_set_bootkick(false); + } +} + +bool uno_check_ignition(void){ + // ignition is checked through harness + return harness_check_ignition(); +} + +void uno_set_usb_switch(bool phone){ + set_gpio_output(GPIOB, 3, phone); +} + +void uno_set_ir_power(uint8_t percentage){ + pwm_set(TIM4, 2, percentage); +} + +void uno_set_fan_power(uint8_t percentage){ + // Enable fan power only if percentage is non-zero. + set_gpio_output(GPIOA, 1, (percentage != 0U)); + fan_set_power(percentage); +} + +uint32_t uno_read_current(void){ + // No current sense on Uno + return 0U; +} + +void uno_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 PWM aka TIM3_CH3 + set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); + + // Initialize RTC + rtc_init(); + + // Turn on GPS load switch. + uno_set_gps_load_switch(true); + + // Turn on phone regulator + set_gpio_output(GPIOB, 4, 1); + + // Initialize IR PWM and set to 0% for now + set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4); + pwm_init(TIM4, 2); + uno_set_ir_power(0U); + + // Initialize fan and set to 10% + fan_init(); + uno_set_fan_power(5U); + + // Initialize harness + harness_init(); + + // Enable CAN transcievers + uno_enable_can_transcievers(true); + + // Disable LEDs + uno_set_led(LED_RED, false); + uno_set_led(LED_GREEN, false); + uno_set_led(LED_BLUE, false); + + // Set normal CAN mode + uno_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); + + // Switch to phone usb mode if harness connection is powered by less than 7V + if(adc_get_voltage() < 7000U){ + uno_set_usb_switch(true); + } else { + uno_set_usb_switch(false); + } + + // Bootkick phone + uno_set_bootkick(true); +} + +const harness_configuration uno_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_uno = { + .board_type = "Uno", + .harness_config = &uno_harness_config, + .init = uno_init, + .enable_can_transciever = uno_enable_can_transciever, + .enable_can_transcievers = uno_enable_can_transcievers, + .set_led = uno_set_led, + .set_usb_power_mode = uno_set_usb_power_mode, + .set_esp_gps_mode = uno_set_esp_gps_mode, + .set_can_mode = uno_set_can_mode, + .usb_power_mode_tick = uno_usb_power_mode_tick, + .check_ignition = uno_check_ignition, + .read_current = uno_read_current, + .set_fan_power = uno_set_fan_power, + .set_ir_power = uno_set_ir_power +}; diff --git a/board/boards/white.h b/board/boards/white.h index 0634c32104baf7..899ba8d4fef9f5 100644 --- a/board/boards/white.h +++ b/board/boards/white.h @@ -152,6 +152,10 @@ void white_set_can_mode(uint8_t mode){ } } +uint32_t white_read_current(void){ + return adc_get(ADCCHAN_CURRENT); +} + uint64_t marker = 0; void white_usb_power_mode_tick(uint64_t tcnt){ @@ -160,7 +164,7 @@ void white_usb_power_mode_tick(uint64_t tcnt){ #define CURRENT_THRESHOLD 0xF00U #define CLICKS 5U // 5 seconds to switch modes - uint32_t current = adc_get(ADCCHAN_CURRENT); + uint32_t current = white_read_current(); // ~0x9a = 500 ma // puth(current); puts("\n"); @@ -219,6 +223,14 @@ void white_usb_power_mode_tick(uint64_t tcnt){ #endif } +void white_set_ir_power(uint8_t percentage){ + UNUSED(percentage); +} + +void white_set_fan_power(uint8_t percentage){ + UNUSED(percentage); +} + bool white_check_ignition(void){ // ignition is on PA1 return !get_gpio_input(GPIOA, 1); @@ -317,5 +329,8 @@ const board board_white = { .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 + .check_ignition = white_check_ignition, + .read_current = white_read_current, + .set_fan_power = white_set_fan_power, + .set_ir_power = white_set_ir_power }; diff --git a/board/bootstub.c b/board/bootstub.c index 51ce6695db79ee..8ada20c7383224 100644 --- a/board/bootstub.c +++ b/board/bootstub.c @@ -33,6 +33,7 @@ const board *current_board; #include "drivers/clock.h" #include "drivers/llgpio.h" #include "drivers/adc.h" +#include "drivers/pwm.h" #include "board.h" diff --git a/board/drivers/can.h b/board/drivers/can.h index 349c9789eb6ddd..c9bf2d25436b35 100644 --- a/board/drivers/can.h +++ b/board/drivers/can.h @@ -176,7 +176,7 @@ void can_flip_buses(uint8_t bus1, uint8_t bus2){ // TODO: Cleanup with new abstraction void can_set_gmlan(uint8_t bus) { - if(hw_type != HW_TYPE_BLACK_PANDA){ + if(board_has_gmlan()){ // first, disable GMLAN on prev bus uint8_t prev_bus = can_num_lookup[3]; if (bus != prev_bus) { @@ -229,7 +229,7 @@ void can_set_obd(uint8_t harness_orientation, bool obd){ } else { puts("setting CAN2 to be normal\n"); } - if(hw_type == HW_TYPE_BLACK_PANDA){ + if(board_has_obd()){ if(obd != (bool)(harness_orientation == HARNESS_STATUS_NORMAL)){ // B5,B6: disable normal mode set_gpio_mode(GPIOB, 5, MODE_INPUT); @@ -246,7 +246,7 @@ void can_set_obd(uint8_t harness_orientation, bool obd){ set_gpio_mode(GPIOB, 13, MODE_INPUT); } } else { - puts("OBD CAN not available on non-black panda\n"); + puts("OBD CAN not available on this board\n"); } } diff --git a/board/drivers/fan.h b/board/drivers/fan.h new file mode 100644 index 00000000000000..d7326ec0b33d59 --- /dev/null +++ b/board/drivers/fan.h @@ -0,0 +1,36 @@ +void fan_init(void){ + // Init PWM speed control + pwm_init(TIM3, 3); + + // Init TACH interrupt + SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI2_PD; + EXTI->IMR |= (1U << 2); + EXTI->RTSR |= (1U << 2); + EXTI->FTSR |= (1U << 2); + NVIC_EnableIRQ(EXTI2_IRQn); +} + +void fan_set_power(uint8_t percentage){ + pwm_set(TIM3, 3, percentage); +} + +uint16_t fan_tach_counter = 0U; +uint16_t fan_rpm = 0U; + +// Can be way more acurate than this, but this is probably good enough for our purposes. + +// Call this every second +void fan_tick(void){ + // 4 interrupts per rotation + fan_rpm = fan_tach_counter * 15U; + fan_tach_counter = 0U; +} + +// TACH interrupt handler +void EXTI2_IRQHandler(void) { + volatile unsigned int pr = EXTI->PR & (1U << 2); + if ((pr & (1U << 2)) != 0U) { + fan_tach_counter++; + } + EXTI->PR = (1U << 2); +} \ No newline at end of file diff --git a/board/drivers/pwm.h b/board/drivers/pwm.h new file mode 100644 index 00000000000000..d2e1652c1ce4ff --- /dev/null +++ b/board/drivers/pwm.h @@ -0,0 +1,55 @@ +#define PWM_COUNTER_OVERFLOW 2000U // To get ~50kHz + +void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ + // Enable timer and auto-reload + TIM->CR1 = TIM_CR1_CEN | TIM_CR1_ARPE; + + // Set channel as PWM mode 1 and enable output + switch(channel){ + case 1U: + TIM->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE); + TIM->CCER |= TIM_CCER_CC1E; + break; + case 2U: + TIM->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE); + TIM->CCER |= TIM_CCER_CC2E; + break; + case 3U: + TIM->CCMR2 |= (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE); + TIM->CCER |= TIM_CCER_CC3E; + break; + case 4U: + TIM->CCMR2 |= (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE); + TIM->CCER |= TIM_CCER_CC4E; + break; + default: + break; + } + + // Set max counter value + TIM->ARR = PWM_COUNTER_OVERFLOW; + + // Update registers and clear counter + TIM->EGR |= TIM_EGR_UG; +} + +// TODO: Implement for 32-bit timers +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ + uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); + switch(channel){ + case 1U: + TIM->CCR1 = comp_value; + break; + case 2U: + TIM->CCR2 = comp_value; + break; + case 3U: + TIM->CCR3 = comp_value; + break; + case 4U: + TIM->CCR4 = comp_value; + break; + default: + break; + } +} \ No newline at end of file diff --git a/board/drivers/rtc.h b/board/drivers/rtc.h new file mode 100644 index 00000000000000..84670ffe0f69d1 --- /dev/null +++ b/board/drivers/rtc.h @@ -0,0 +1,93 @@ +#define RCC_BDCR_OPTIONS (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_0 | RCC_BDCR_LSEON) +#define RCC_BDCR_MASK (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL | RCC_BDCR_LSEMOD | RCC_BDCR_LSEBYP | RCC_BDCR_LSEON) + +#define YEAR_OFFSET 2000U + +typedef struct __attribute__((packed)) timestamp_t { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t weekday; + uint8_t hour; + uint8_t minute; + uint8_t second; +} timestamp_t; + +uint8_t to_bcd(uint16_t value){ + return (((value / 10U) & 0x0FU) << 4U) | ((value % 10U) & 0x0FU); +} + +uint16_t from_bcd(uint8_t value){ + return (((value & 0xF0U) >> 4U) * 10U) + (value & 0x0FU); +} + +void rtc_init(void){ + // Initialize RTC module and clock if not done already. + if((RCC->BDCR & RCC_BDCR_MASK) != RCC_BDCR_OPTIONS){ + puts("Initializing RTC\n"); + // Reset backup domain + RCC->BDCR |= RCC_BDCR_BDRST; + + // Disable write protection + PWR->CR |= PWR_CR_DBP; + + // Clear backup domain reset + RCC->BDCR &= ~(RCC_BDCR_BDRST); + + // Set RTC options + RCC->BDCR = RCC_BDCR_OPTIONS | (RCC->BDCR & (~RCC_BDCR_MASK)); + + // Enable write protection + PWR->CR &= ~(PWR_CR_DBP); + } +} + +void rtc_set_time(timestamp_t time){ + puts("Setting RTC time\n"); + + // Disable write protection + PWR->CR |= PWR_CR_DBP; + RTC->WPR = 0xCA; + RTC->WPR = 0x53; + + // Enable initialization mode + RTC->ISR |= RTC_ISR_INIT; + while((RTC->ISR & RTC_ISR_INITF) == 0){} + + // Set time + RTC->TR = (to_bcd(time.hour) << RTC_TR_HU_Pos) | (to_bcd(time.minute) << RTC_TR_MNU_Pos) | (to_bcd(time.second) << RTC_TR_SU_Pos); + RTC->DR = (to_bcd(time.year - YEAR_OFFSET) << RTC_DR_YU_Pos) | (time.weekday << RTC_DR_WDU_Pos) | (to_bcd(time.month) << RTC_DR_MU_Pos) | (to_bcd(time.day) << RTC_DR_DU_Pos); + + // Set options + RTC->CR = 0U; + + // Disable initalization mode + RTC->ISR &= ~(RTC_ISR_INIT); + + // Wait for synchronization + while((RTC->ISR & RTC_ISR_RSF) == 0){} + + // Re-enable write protection + RTC->WPR = 0x00; + PWR->CR &= ~(PWR_CR_DBP); +} + +timestamp_t rtc_get_time(void){ + // Wait until the register sync flag is set + while((RTC->ISR & RTC_ISR_RSF) == 0){} + + // Read time and date registers. Since our HSE > 7*LSE, this should be fine. + uint32_t time = RTC->TR; + uint32_t date = RTC->DR; + + // Parse values + timestamp_t result; + result.year = from_bcd((date & (RTC_DR_YT | RTC_DR_YU)) >> RTC_DR_YU_Pos) + YEAR_OFFSET; + result.month = from_bcd((date & (RTC_DR_MT | RTC_DR_MU)) >> RTC_DR_MU_Pos); + result.day = from_bcd((date & (RTC_DR_DT | RTC_DR_DU)) >> RTC_DR_DU_Pos); + result.weekday = ((date & RTC_DR_WDU) >> RTC_DR_WDU_Pos); + result.hour = from_bcd((time & (RTC_TR_HT | RTC_TR_HU)) >> RTC_TR_HU_Pos); + result.minute = from_bcd((time & (RTC_TR_MNT | RTC_TR_MNU)) >> RTC_TR_MNU_Pos); + result.second = from_bcd((time & (RTC_TR_ST | RTC_TR_SU)) >> RTC_TR_SU_Pos); + return result; +} \ No newline at end of file diff --git a/board/main.c b/board/main.c index 330598a806febe..3b5aa63ac7d58d 100644 --- a/board/main.c +++ b/board/main.c @@ -13,6 +13,7 @@ #include "drivers/llcan.h" #include "drivers/llgpio.h" #include "drivers/adc.h" +#include "drivers/pwm.h" #include "board.h" @@ -121,7 +122,7 @@ void set_safety_mode(uint16_t mode, int16_t param) { switch (mode) { case SAFETY_NOOUTPUT: set_intercept_relay(false); - if(hw_type == HW_TYPE_BLACK_PANDA){ + if(board_has_obd()){ current_board->set_can_mode(CAN_MODE_NORMAL); } can_silent = ALL_CAN_SILENT; @@ -129,7 +130,7 @@ void set_safety_mode(uint16_t mode, int16_t param) { case SAFETY_ELM327: set_intercept_relay(false); heartbeat_counter = 0U; - if(hw_type == HW_TYPE_BLACK_PANDA){ + if(board_has_obd()){ current_board->set_can_mode(CAN_MODE_OBD_CAN2); } can_silent = ALL_CAN_LIVE; @@ -137,7 +138,7 @@ void set_safety_mode(uint16_t mode, int16_t param) { default: set_intercept_relay(true); heartbeat_counter = 0U; - if(hw_type == HW_TYPE_BLACK_PANDA){ + if(board_has_obd()){ current_board->set_can_mode(CAN_MODE_NORMAL); } can_silent = ALL_CAN_LIVE; @@ -166,13 +167,7 @@ int get_health_pkt(void *dat) { } *health = dat; health->voltage_pkt = adc_get_voltage(); - - // No current sense on panda black - if(hw_type != HW_TYPE_BLACK_PANDA){ - health->current_pkt = adc_get(ADCCHAN_CURRENT); - } else { - health->current_pkt = 0; - } + health->current_pkt = current_board->read_current(); //Use the GPIO pin to determine ignition or use a CAN based logic health->ignition_line_pkt = (uint8_t)(current_board->check_ignition()); @@ -190,6 +185,12 @@ int get_health_pkt(void *dat) { return sizeof(*health); } +int get_rtc_pkt(void *dat) { + timestamp_t t = rtc_get_time(); + (void)memcpy(dat, &t, sizeof(t)); + return sizeof(t); +} + int usb_cb_ep1_in(void *usbdata, int len, bool hardwired) { UNUSED(hardwired); CAN_FIFOMailBox_TypeDef *reply = (CAN_FIFOMailBox_TypeDef *)usbdata; @@ -242,7 +243,68 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) unsigned int resp_len = 0; uart_ring *ur = NULL; int i; + timestamp_t t; switch (setup->b.bRequest) { + // **** 0xa0: get rtc time + case 0xa0: + resp_len = get_rtc_pkt(resp); + break; + // **** 0xa1: set rtc year + case 0xa1: + t = rtc_get_time(); + t.year = setup->b.wValue.w; + rtc_set_time(t); + break; + // **** 0xa2: set rtc month + case 0xa2: + t = rtc_get_time(); + t.month = setup->b.wValue.w; + rtc_set_time(t); + break; + // **** 0xa3: set rtc day + case 0xa3: + t = rtc_get_time(); + t.day = setup->b.wValue.w; + rtc_set_time(t); + break; + // **** 0xa4: set rtc weekday + case 0xa4: + t = rtc_get_time(); + t.weekday = setup->b.wValue.w; + rtc_set_time(t); + break; + // **** 0xa5: set rtc hour + case 0xa5: + t = rtc_get_time(); + t.hour = setup->b.wValue.w; + rtc_set_time(t); + break; + // **** 0xa6: set rtc minute + case 0xa6: + t = rtc_get_time(); + t.minute = setup->b.wValue.w; + rtc_set_time(t); + break; + // **** 0xa7: set rtc second + case 0xa7: + t = rtc_get_time(); + t.second = setup->b.wValue.w; + rtc_set_time(t); + break; + // **** 0xb0: set IR power + case 0xb0: + current_board->set_ir_power(setup->b.wValue.w); + break; + // **** 0xb1: set fan power + case 0xb1: + current_board->set_fan_power(setup->b.wValue.w); + break; + // **** 0xb2: get fan rpm + case 0xb2: + resp[0] = (fan_rpm & 0x00FFU); + resp[1] = ((fan_rpm & 0xFF00U) >> 8U); + resp_len = 2; + break; // **** 0xc0: get CAN debug info case 0xc0: puts("can tx: "); puth(can_tx_cnt); @@ -330,7 +392,7 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) break; // **** 0xdb: set GMLAN (white/grey) or OBD CAN (black) multiplexing mode case 0xdb: - if(hw_type == HW_TYPE_BLACK_PANDA){ + if(board_has_obd()){ if (setup->b.wValue.w == 1U) { // Enable OBD CAN current_board->set_can_mode(CAN_MODE_OBD_CAN2); @@ -578,8 +640,8 @@ uint64_t tcnt = 0; // called once per second // cppcheck-suppress unusedFunction ; used in headers not included in cppcheck -void TIM3_IRQHandler(void) { - if (TIM3->SR != 0) { +void TIM1_BRK_TIM9_IRQHandler(void) { + if (TIM9->SR != 0) { can_live = pending_can_live; current_board->usb_power_mode_tick(tcnt); @@ -597,6 +659,10 @@ void TIM3_IRQHandler(void) { puth(can_tx2_q.r_ptr); puts(" "); puth(can_tx2_q.w_ptr); puts("\n"); #endif + // Tick fan driver + fan_tick(); + //puts("Fan speed: "); puth((unsigned int) fan_rpm); puts("rpm\n"); + // set green LED to be controls allowed current_board->set_led(LED_GREEN, controls_allowed); @@ -622,7 +688,7 @@ void TIM3_IRQHandler(void) { // on to the next one tcnt += 1U; } - TIM3->SR = 0; + TIM9->SR = 0; } int main(void) { @@ -670,8 +736,7 @@ int main(void) { uart_init(&uart_ring_esp_gps, 115200); } - // there is no LIN on panda black - if(hw_type != HW_TYPE_BLACK_PANDA){ + if(board_has_lin()){ // enable LIN uart_init(&uart_ring_lin1, 10400); UART5->CR2 |= USART_CR2_LINEN; @@ -713,10 +778,9 @@ int main(void) { set_power_save_state(POWER_SAVE_STATUS_ENABLED); }*/ #endif - - // 48mhz / 65536 ~= 732 / 732 = 1 - timer_init(TIM3, 732); - NVIC_EnableIRQ(TIM3_IRQn); + // 1hz + timer_init(TIM9, 1464); + NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn); #ifdef DEBUG puts("DEBUG ENABLED\n"); diff --git a/board/power_saving.h b/board/power_saving.h index cf599282c9de42..a66f4616b722e6 100644 --- a/board/power_saving.h +++ b/board/power_saving.h @@ -34,16 +34,23 @@ void set_power_save_state(int state) { current_board->set_esp_gps_mode(ESP_GPS_DISABLED); } - if(hw_type != HW_TYPE_BLACK_PANDA){ + if(board_has_gmlan()){ // turn on GMLAN set_gpio_output(GPIOB, 14, enable); set_gpio_output(GPIOB, 15, enable); + } + if(board_has_lin()){ // turn on LIN set_gpio_output(GPIOB, 7, enable); set_gpio_output(GPIOA, 14, enable); } + // Switch IR and fan + // TODO: Remove powering these back up. Should be done on device + //current_board->set_ir_power(enable ? 50U : 0U); + current_board->set_fan_power(enable ? 5U : 0U); + power_save_status = state; } } diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h index 7df3d38b4333ff..f59e288812a7cf 100644 --- a/board/safety/safety_honda.h +++ b/board/safety/safety_honda.h @@ -156,7 +156,7 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { // FORCE CANCEL: safety check only relevant when spamming the cancel button in Bosch HW // ensuring that only the cancel button press is sent (VAL 2) when controls are off. // This avoids unintended engagements while still allowing resume spam - int bus_pt = ((hw_type == HW_TYPE_BLACK_PANDA) && honda_bosch_hardware)? 1 : 0; + int bus_pt = ((board_has_relay()) && honda_bosch_hardware)? 1 : 0; if ((addr == 0x296) && honda_bosch_hardware && !current_controls_allowed && (bus == bus_pt)) { if (((GET_BYTE(to_send, 0) >> 5) & 0x7) != 2) { @@ -210,8 +210,8 @@ static int honda_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { static int honda_bosch_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { int bus_fwd = -1; - int bus_rdr_cam = (hw_type == HW_TYPE_BLACK_PANDA) ? 2 : 1; // radar bus, camera side - int bus_rdr_car = (hw_type == HW_TYPE_BLACK_PANDA) ? 0 : 2; // radar bus, car side + int bus_rdr_cam = (board_has_relay()) ? 2 : 1; // radar bus, camera side + int bus_rdr_car = (board_has_relay()) ? 0 : 2; // radar bus, car side if (bus_num == bus_rdr_car) { bus_fwd = bus_rdr_cam; diff --git a/python/__init__.py b/python/__init__.py index 0ce4fa051f5740..326128d3ec306a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -1,5 +1,5 @@ # python library to interface with panda - +import datetime import binascii import struct import hashlib @@ -144,6 +144,7 @@ class Panda(object): HW_TYPE_GREY_PANDA = b'\x02' HW_TYPE_BLACK_PANDA = b'\x03' HW_TYPE_PEDAL = b'\x04' + HW_TYPE_UNO = b'\x05' def __init__(self, serial=None, claim=True): self._serial = serial @@ -382,6 +383,9 @@ def is_grey(self): def is_black(self): return self.get_type() == Panda.HW_TYPE_BLACK_PANDA + def is_uno(self): + return self.get_type() == Panda.HW_TYPE_UNO + def get_serial(self): dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20) hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4] @@ -592,3 +596,31 @@ def kline_recv(self, bus=2): def send_heartbeat(self): self._handle.controlWrite(Panda.REQUEST_OUT, 0xf3, 0, 0, b'') + + # ******************* RTC ******************* + def set_datetime(self, dt): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xa1, int(dt.year), 0, b'') + self._handle.controlWrite(Panda.REQUEST_OUT, 0xa2, int(dt.month), 0, b'') + self._handle.controlWrite(Panda.REQUEST_OUT, 0xa3, int(dt.day), 0, b'') + self._handle.controlWrite(Panda.REQUEST_OUT, 0xa4, int(dt.isoweekday()), 0, b'') + self._handle.controlWrite(Panda.REQUEST_OUT, 0xa5, int(dt.hour), 0, b'') + self._handle.controlWrite(Panda.REQUEST_OUT, 0xa6, int(dt.minute), 0, b'') + self._handle.controlWrite(Panda.REQUEST_OUT, 0xa7, int(dt.second), 0, b'') + + def get_datetime(self): + dat = self._handle.controlRead(Panda.REQUEST_IN, 0xa0, 0, 0, 8) + a = struct.unpack("HBBBBBB", dat) + return datetime.datetime(a[0], a[1], a[2], a[4], a[5], a[6]) + + # ******************* IR ******************* + def set_ir_power(self, percentage): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xb0, int(percentage), 0, b'') + + # ******************* Fan ****************** + def set_fan_power(self, percentage): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xb1, int(percentage), 0, b'') + + def get_fan_rpm(self): + dat = self._handle.controlRead(Panda.REQUEST_IN, 0xb2, 0, 0, 2) + a = struct.unpack("H", dat) + return a[0] \ No newline at end of file diff --git a/tests/fan_test.py b/tests/fan_test.py new file mode 100755 index 00000000000000..73856988281b19 --- /dev/null +++ b/tests/fan_test.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +import os +import sys +import time + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +power = 0 +if __name__ == "__main__": + p = Panda() + while True: + p.set_fan_power(power) + time.sleep(5) + print("Power: ", power, "RPM: ", str(p.get_fan_rpm())) + power += 10 + power %=100 diff --git a/tests/ir_test.py b/tests/ir_test.py new file mode 100755 index 00000000000000..caef9e4909d3d9 --- /dev/null +++ b/tests/ir_test.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +import os +import sys +import time + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +power = 0 +if __name__ == "__main__": + p = Panda() + while True: + p.set_ir_power(power) + print("Power: ", str(power)) + time.sleep(1) + power += 10 + power %=100 diff --git a/tests/rtc_test.py b/tests/rtc_test.py new file mode 100755 index 00000000000000..3732cb765f81e9 --- /dev/null +++ b/tests/rtc_test.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +import os +import sys +import datetime + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +if __name__ == "__main__": + p = Panda() + + p.set_datetime(datetime.datetime.now()) + print(p.get_datetime()) diff --git a/tests/safety/libpandasafety_py.py b/tests/safety/libpandasafety_py.py index 9edec08a830a88..87fb0db1da9c72 100644 --- a/tests/safety/libpandasafety_py.py +++ b/tests/safety/libpandasafety_py.py @@ -30,6 +30,8 @@ uint32_t CNT; } TIM_TypeDef; +bool board_has_relay(void); + void set_controls_allowed(bool c); bool get_controls_allowed(void); void set_long_controls_allowed(bool c); diff --git a/tests/safety/test.c b/tests/safety/test.c index 8b874a2f7a7f0e..cffc94906e6d60 100644 --- a/tests/safety/test.c +++ b/tests/safety/test.c @@ -34,6 +34,22 @@ struct sample_t volkswagen_torque_driver; TIM_TypeDef timer; TIM_TypeDef *TIM2 = &timer; +// from board_declarations.h +#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 +#define HW_TYPE_UNO 5U + +// from main_declarations.h +uint8_t hw_type = HW_TYPE_UNKNOWN; + +// from board.h +bool board_has_relay(void) { + return hw_type == HW_TYPE_BLACK_PANDA || hw_type == HW_TYPE_UNO; +} + // from config.h #define MIN(a,b) \ ({ __typeof__ (a) _a = (a); \ @@ -53,16 +69,6 @@ TIM_TypeDef *TIM2 = &timer; #define GET_BYTES_04(msg) ((msg)->RDLR) #define GET_BYTES_48(msg) ((msg)->RDHR) -// from board_declarations.h -#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 - -// from main_declarations.h -uint8_t hw_type = 0U; - #define UNUSED(x) (void)(x) #define PANDA diff --git a/tests/safety/test.sh b/tests/safety/test.sh index 2674281addd9f4..39d70ff615bee8 100755 --- a/tests/safety/test.sh +++ b/tests/safety/test.sh @@ -6,11 +6,12 @@ # HW_TYPE_GREY_PANDA 2U # HW_TYPE_BLACK_PANDA 3U # HW_TYPE_PEDAL 4U +# HW_TYPE_UNO 5U # Make sure test fails if one HW_TYPE fails set -e -for hw_type in 0 1 2 3 4 +for hw_type in 0 1 2 3 4 5 do echo "Testing HW_TYPE: $hw_type" HW_TYPE=$hw_type python -m unittest discover . diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py index 9fa999d227aba4..55a376a2e58a37 100755 --- a/tests/safety/test_honda.py +++ b/tests/safety/test_honda.py @@ -34,9 +34,9 @@ def _button_msg(self, buttons, msg): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = msg << 21 to_send[0].RDLR = buttons << 5 - is_panda_black = self.safety.get_hw_type() == 3 # black_panda + has_relay = self.safety.board_has_relay() honda_bosch_hardware = self.safety.get_honda_bosch_hardware() - bus = 1 if is_panda_black and honda_bosch_hardware else 0 + bus = 1 if has_relay and honda_bosch_hardware else 0 to_send[0].RDTR = bus << 4 return to_send diff --git a/tests/safety/test_honda_bosch.py b/tests/safety/test_honda_bosch.py index 7adaf8d39ad393..7571e1eddc4576 100755 --- a/tests/safety/test_honda_bosch.py +++ b/tests/safety/test_honda_bosch.py @@ -21,12 +21,13 @@ def _send_msg(self, bus, addr, length): return to_send def test_fwd_hook(self): - buss = list(range(0x0, 0x3)) - msgs = list(range(0x1, 0x800)) - is_panda_black = self.safety.get_hw_type() == 3 # black panda - bus_rdr_cam = 2 if is_panda_black else 1 - bus_rdr_car = 0 if is_panda_black else 2 - bus_pt = 1 if is_panda_black else 0 + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + #has_relay = self.safety.get_hw_type() == 3 # black panda + has_relay = self.safety.board_has_relay() + bus_rdr_cam = 2 if has_relay else 1 + bus_rdr_car = 0 if has_relay else 2 + bus_pt = 1 if has_relay else 0 blocked_msgs = [0xE4, 0x33D] for b in buss: