From 390b8bce815b09ab68403c5e7ffc1f3bd7138d89 Mon Sep 17 00:00:00 2001 From: Greg Hogan Date: Fri, 10 Jul 2020 14:18:24 -0700 Subject: [PATCH] k-line 5 baud init (#565) * k-line slow init * k-line slow init LED bit blink * fix misra violations * better names for k-line methods * debug prints match names * switch to timer * use TIM4 until I figure out TIM5 * tickle faster * fix bit bug and add stop bit * TIM5 working * USB return after addr sent * fix l-line reset * fix misra violations * blink for the ones instead of the zeros * k-line 5 baud fault type * lin check * use TIM5 or wakeup * better names --- board/boards/common.h | 2 +- board/drivers/kline_init.h | 119 +++++++++++++++++++++++++++++++++++++ board/drivers/usb.h | 7 ++- board/faults.h | 1 + board/main.c | 56 ++++++----------- python/__init__.py | 8 +++ 6 files changed, 151 insertions(+), 42 deletions(-) create mode 100644 board/drivers/kline_init.h diff --git a/board/boards/common.h b/board/boards/common.h index 73addfe..7343809 100644 --- a/board/boards/common.h +++ b/board/boards/common.h @@ -60,7 +60,7 @@ void peripherals_init(void){ RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // main counter 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_TIM5EN; // k-line init RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // interrupt timer RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config RCC->APB2ENR |= RCC_APB2ENR_USART1EN; diff --git a/board/drivers/kline_init.h b/board/drivers/kline_init.h new file mode 100644 index 0000000..a8a859c --- /dev/null +++ b/board/drivers/kline_init.h @@ -0,0 +1,119 @@ +void TIM5_IRQ_Handler(void); + +void setup_timer5(void) { + // register interrupt + REGISTER_INTERRUPT(TIM5_IRQn, TIM5_IRQ_Handler, 1050000U, FAULT_INTERRUPT_RATE_KLINE_INIT) + + // setup + register_set(&(TIM5->PSC), (48-1), 0xFFFFU); // Tick on 1 us + register_set(&(TIM5->CR1), TIM_CR1_CEN, 0x3FU); // Enable + register_set(&(TIM5->ARR), (5000-1), 0xFFFFFFFFU); // Reset every 5 ms + + // in case it's disabled + NVIC_EnableIRQ(TIM5_IRQn); + + // run the interrupt + register_set(&(TIM5->DIER), TIM_DIER_UIE, 0x5F5FU); // Update interrupt + TIM5->SR = 0; +} + +bool k_init = false; +bool l_init = false; +void setup_kline(bool bitbang) { + if (bitbang) { + if (k_init) { + set_gpio_output(GPIOC, 12, true); + } + if (l_init) { + set_gpio_output(GPIOC, 10, true); + } + } else { + if (k_init) { + set_gpio_mode(GPIOC, 12, MODE_ALTERNATE); + } + if (l_init) { + set_gpio_mode(GPIOC, 10, MODE_ALTERNATE); + } + } +} + +void set_bitbanged_kline(bool marking) { + // tickle needs to be super fast (so logic level doesn't change) + ENTER_CRITICAL(); + if (k_init) { + register_set_bits(&(GPIOC->ODR), (1U << 12)); + if (!marking) { + register_clear_bits(&(GPIOC->ODR), (1U << 12)); + } + } + if (l_init) { + register_set_bits(&(GPIOC->ODR), (1U << 10)); + if (!marking) { + register_clear_bits(&(GPIOC->ODR), (1U << 10)); + } + } + EXIT_CRITICAL(); + // blink blue LED each time line is pulled low + current_board->set_led(LED_BLUE, marking); +} + +uint16_t kline_data = 0; +uint16_t kline_data_len = 0; +uint16_t kline_bit_count = 0; +uint16_t kline_tick_count = 0; +uint16_t kline_ticks_per_bit = 0; + +void TIM5_IRQ_Handler(void) { + if ((TIM5->SR & TIM_SR_UIF) && (kline_data != 0U)) { + if (kline_bit_count < kline_data_len) { + bool marking = (kline_data & (1U << kline_bit_count)) != 0U; + set_bitbanged_kline(marking); + } else { + register_clear_bits(&(TIM5->DIER), TIM_DIER_UIE); // No update interrupt + register_set(&(TIM5->CR1), 0U, 0x3FU); // Disable timer + setup_kline(false); + kline_data = 0U; + USB_WritePacket(NULL, 0, 0); // required call (so send nothing) + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } + kline_tick_count++; + if ((kline_tick_count % kline_ticks_per_bit) == 0U) { + kline_bit_count++; + } + } + TIM5->SR = 0; +} + +bool bitbang_five_baud_addr(bool k, bool l, uint8_t addr) { + bool result = false; + if (kline_data == 0U) { + k_init = k; + l_init = l; + kline_data = (addr << 1) + 0x200U; // add start/stop bits + kline_data_len = 10U; + kline_bit_count = 0; + kline_tick_count = 0; + kline_ticks_per_bit = 40U; // 200ms == 5bps + setup_kline(true); + setup_timer5(); + result = true; + } + return result; +} + +bool bitbang_wakeup(bool k, bool l) { + bool result = false; + if (kline_data == 0U) { + k_init = k; + l_init = l; + kline_data = 2U; // low then high + kline_data_len = 2U; + kline_bit_count = 0; + kline_tick_count = 0; + kline_ticks_per_bit = 5U; // 25ms == 40bps + setup_kline(true); + setup_timer5(); + result = true; + } + return result; +} diff --git a/board/drivers/usb.h b/board/drivers/usb.h index 44262df..e85af95 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -662,8 +662,11 @@ void usb_setup(void) { break; default: resp_len = usb_cb_control_msg(&setup, resp, 1); - USB_WritePacket(resp, MIN(resp_len, setup.b.wLength.w), 0); - USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + // response pending if -1 was returned + if (resp_len != -1) { + USB_WritePacket(resp, MIN(resp_len, setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } } } diff --git a/board/faults.h b/board/faults.h index aa5db34..2c031cf 100644 --- a/board/faults.h +++ b/board/faults.h @@ -22,6 +22,7 @@ #define FAULT_INTERRUPT_RATE_TIM1 (1U << 16) #define FAULT_INTERRUPT_RATE_TIM3 (1U << 17) #define FAULT_REGISTER_DIVERGENT (1U << 18) +#define FAULT_INTERRUPT_RATE_KLINE_INIT (1U << 19) // Permanent faults #define PERMANENT_FAULTS 0U diff --git a/board/main.c b/board/main.c index 76f6225..497ea5d 100644 --- a/board/main.c +++ b/board/main.c @@ -25,6 +25,7 @@ #include "drivers/uart.h" #include "drivers/usb.h" #include "drivers/gmlan_alt.h" +#include "drivers/kline_init.h" #include "drivers/timer.h" #include "drivers/clock.h" @@ -249,11 +250,7 @@ void usb_cb_enumeration_complete() { int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) { unsigned int resp_len = 0; uart_ring *ur = NULL; - uint32_t ts; - uint32_t ts_timer; timestamp_t t; - bool k_wakeup; - bool l_wakeup; switch (setup->b.bRequest) { // **** 0xa0: get rtc time case 0xa0: @@ -562,43 +559,13 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) break; // **** 0xf0: k-line/l-line wake-up pulse for KWP2000 fast initialization case 0xf0: - k_wakeup = (setup->b.wValue.w == 0U) || (setup->b.wValue.w == 2U); - l_wakeup = (setup->b.wValue.w == 1U) || (setup->b.wValue.w == 2U); - - ts = TIM2->CNT; - ts_timer = ts; - if (k_wakeup) { - set_gpio_output(GPIOC, 12, false); - } - if (l_wakeup) { - set_gpio_output(GPIOC, 10, false); - } - - // hold low for 25 ms - while (get_ts_elapsed(TIM2->CNT, ts) < 25000U) { - // toggle pin every 5 ms to reset TXD dominant time-out timer - if (get_ts_elapsed(TIM2->CNT, ts_timer) >= 5000U) { - ts_timer = TIM2->CNT; - if (k_wakeup) { - register_set_bits(&(GPIOC->ODR), (1U << 12)); - register_clear_bits(&(GPIOC->ODR), (1U << 12)); - } - if (l_wakeup) { - register_set_bits(&(GPIOC->ODR), (1U << 10)); - register_clear_bits(&(GPIOC->ODR), (1U << 10)); - } + if(board_has_lin()) { + bool k = (setup->b.wValue.w == 0U) || (setup->b.wValue.w == 2U); + bool l = (setup->b.wValue.w == 1U) || (setup->b.wValue.w == 2U); + if (bitbang_wakeup(k, l)) { + resp_len = -1; // do not clear NAK yet (wait for bit banging to finish) } } - - if (k_wakeup) { - set_gpio_mode(GPIOC, 12, MODE_ALTERNATE); - } - if (l_wakeup) { - set_gpio_mode(GPIOC, 10, MODE_ALTERNATE); - } - // hold high until 49ms have passed - // (start communication needs to follow 49ms to 51ms after start of wakeup) - while (get_ts_elapsed(TIM2->CNT, ts) < 49000U) {} break; // **** 0xf1: Clear CAN ring buffer. case 0xf1: @@ -628,6 +595,17 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) heartbeat_counter = 0U; break; } + // **** 0xf4: k-line/l-line 5 baud initialization + case 0xf4: + if(board_has_lin()) { + bool k = (setup->b.wValue.w == 0U) || (setup->b.wValue.w == 2U); + bool l = (setup->b.wValue.w == 1U) || (setup->b.wValue.w == 2U); + uint8_t five_baud_addr = (setup->b.wIndex.w & 0xFFU); + if (bitbang_five_baud_addr(k, l, five_baud_addr)) { + resp_len = -1; // do not clear NAK yet (wait for bit banging to finish) + } + } + break; default: puts("NO HANDLER "); puth(setup->b.bRequest); diff --git a/python/__init__.py b/python/__init__.py index 9eb8c98..605977d 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -579,6 +579,14 @@ class Panda(object): if DEBUG: print("kline wakeup done") + def kline_5baud(self, addr, k=True, l=True): + assert k or l, "must specify k-line, l-line, or both" + if DEBUG: + print("kline 5 baud...") + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf4, 2 if k and l else int(l), addr, b'') + if DEBUG: + print("kline 5 baud done") + def kline_drain(self, bus=2): # drain buffer bret = bytearray()