From 31f8a0d862f699d0d06593085aa16a8cd657bccc Mon Sep 17 00:00:00 2001 From: Greg Hogan Date: Thu, 18 Jun 2020 11:17:00 -0700 Subject: [PATCH] K line fix (#559) * enable UART RX interrupts * update debug prints for python3 * improve kline functions and fix checksum * k-line wake-up conforming to KWP2000 fast init * fix timing * toggle k and l line together by default * k-line wakeup using timer * k and l were flipped * fix misra compliance --- board/drivers/uart.h | 22 ++++++++++++---- board/main.c | 62 +++++++++++++++++++++++++------------------- python/__init__.py | 38 ++++++++++++--------------- 3 files changed, 70 insertions(+), 52 deletions(-) diff --git a/board/drivers/uart.h b/board/drivers/uart.h index cbf5c599..4ab791f6 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -273,15 +273,27 @@ void uart_set_baud(USART_TypeDef *u, unsigned int baud) { void uart_init(uart_ring *q, int baud) { // Register interrupts (max data rate: 115200 baud) - REGISTER_INTERRUPT(USART1_IRQn, USART1_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_1) - REGISTER_INTERRUPT(USART2_IRQn, USART2_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_2) - REGISTER_INTERRUPT(USART3_IRQn, USART3_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_3) - REGISTER_INTERRUPT(UART5_IRQn, UART5_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_5) - REGISTER_INTERRUPT(DMA2_Stream5_IRQn, DMA2_Stream5_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_UART_DMA) // Called twice per buffer + if(q->uart == USART1){ + REGISTER_INTERRUPT(USART1_IRQn, USART1_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_1) + } else if (q->uart == USART2){ + REGISTER_INTERRUPT(USART2_IRQn, USART2_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_2) + } else if (q->uart == USART3){ + REGISTER_INTERRUPT(USART3_IRQn, USART3_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_3) + } else if (q->uart == UART5){ + REGISTER_INTERRUPT(UART5_IRQn, UART5_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_5) + } else { + // UART not used. Skip registering interrupts + } + if(q->dma_rx){ + REGISTER_INTERRUPT(DMA2_Stream5_IRQn, DMA2_Stream5_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_UART_DMA) // Called twice per buffer + } // Set baud and enable peripheral with TX and RX mode uart_set_baud(q->uart, baud); q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; + if ((q->uart == USART2) || (q->uart == USART3) || (q->uart == UART5)) { + q->uart->CR1 |= USART_CR1_RXNEIE; + } // Enable UART interrupts if(q->uart == USART1){ diff --git a/board/main.c b/board/main.c index 8e62e69f..8b67750c 100644 --- a/board/main.c +++ b/board/main.c @@ -249,8 +249,11 @@ 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; - int i; + 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: @@ -557,38 +560,45 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) case 0xe7: set_power_save_state(setup->b.wValue.w); break; - // **** 0xf0: do k-line wValue pulse on uart2 for Acura + // **** 0xf0: k-line/l-line wake-up pulse for KWP2000 fast initialization case 0xf0: - if (setup->b.wValue.w == 1U) { - GPIOC->ODR &= ~(1U << 10); - GPIOC->MODER &= ~GPIO_MODER_MODER10_1; - GPIOC->MODER |= GPIO_MODER_MODER10_0; - } else { - GPIOC->ODR &= ~(1U << 12); - GPIOC->MODER &= ~GPIO_MODER_MODER12_1; - GPIOC->MODER |= GPIO_MODER_MODER12_0; + 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); } - for (i = 0; i < 80; i++) { - delay(8000); - if (setup->b.wValue.w == 1U) { - GPIOC->ODR |= (1U << 10); - GPIOC->ODR &= ~(1U << 10); - } else { - GPIOC->ODR |= (1U << 12); - GPIOC->ODR &= ~(1U << 12); + // 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 (setup->b.wValue.w == 1U) { - GPIOC->MODER &= ~GPIO_MODER_MODER10_0; - GPIOC->MODER |= GPIO_MODER_MODER10_1; - } else { - GPIOC->MODER &= ~GPIO_MODER_MODER12_0; - GPIOC->MODER |= GPIO_MODER_MODER12_1; + if (k_wakeup) { + set_gpio_mode(GPIOC, 12, MODE_ALTERNATE); } - - delay(140 * 9000); + 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: diff --git a/python/__init__.py b/python/__init__.py index 7c979b57..9eb8c98b 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -1,6 +1,5 @@ # python library to interface with panda import datetime -import binascii import struct import hashlib import socket @@ -44,7 +43,7 @@ def parse_can_buffer(dat): address = f1 >> 21 dddat = ddat[8:8 + (f2 & 0xF)] if DEBUG: - print(" R %x: %s" % (address, binascii.hexlify(dddat))) + print(f" R 0x{address:x}: 0x{dddat.hex()}") ret.append((address, f2 >> 16, dddat, (f2 >> 4) & 0xFF)) return ret @@ -489,7 +488,7 @@ class Panda(object): for addr, _, dat, bus in arr: assert len(dat) <= 8 if DEBUG: - print(" W %x: %s" % (addr, binascii.hexlify(dat))) + print(f" W 0x{addr:x}: 0x{dat.hex()}") if addr >= 0x800: rir = (addr << 3) | transmit | extended else: @@ -572,10 +571,11 @@ class Panda(object): # ******************* kline ******************* # pulse low for wakeup - def kline_wakeup(self): + def kline_wakeup(self, k=True, l=True): + assert k or l, "must specify k-line, l-line, or both" if DEBUG: print("kline wakeup...") - self._handle.controlWrite(Panda.REQUEST_OUT, 0xf0, 0, 0, b'') + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf0, 2 if k and l else int(l), 0, b'') if DEBUG: print("kline wakeup done") @@ -587,7 +587,7 @@ class Panda(object): if len(ret) == 0: break elif DEBUG: - print("kline drain: " + binascii.hexlify(ret)) + print(f"kline drain: 0x{ret.hex()}") bret += ret return bytes(bret) @@ -596,35 +596,31 @@ class Panda(object): while len(echo) != cnt: ret = self._handle.controlRead(Panda.REQUEST_OUT, 0xe0, bus, 0, cnt - len(echo)) if DEBUG and len(ret) > 0: - print("kline recv: " + binascii.hexlify(ret)) + print(f"kline recv: 0x{ret.hex()}") echo += ret return bytes(echo) def kline_send(self, x, bus=2, checksum=True): - def get_checksum(dat): - result = 0 - result += sum(map(ord, dat)) if isinstance(b'dat', str) else sum(dat) - result = -result - return struct.pack("B", result % 0x100) - self.kline_drain(bus=bus) if checksum: - x += get_checksum(x) + x += bytes([sum(x) % 0x100]) for i in range(0, len(x), 0xf): ts = x[i:i + 0xf] if DEBUG: - print("kline send: " + binascii.hexlify(ts)) + print(f"kline send: 0x{ts.hex()}") self._handle.bulkWrite(2, bytes([bus]) + ts) echo = self.kline_ll_recv(len(ts), bus=bus) if echo != ts: - print("**** ECHO ERROR %d ****" % i) - print(binascii.hexlify(echo)) - print(binascii.hexlify(ts)) + print(f"**** ECHO ERROR {i} ****") + print(f"0x{echo.hex()}") + print(f"0x{ts.hex()}") assert echo == ts - def kline_recv(self, bus=2): - msg = self.kline_ll_recv(2, bus=bus) - msg += self.kline_ll_recv(ord(msg[1]) - 2, bus=bus) + def kline_recv(self, bus=2, header_len=4): + # read header (last byte is length) + msg = self.kline_ll_recv(header_len, bus=bus) + # read data (add one byte to length for checksum) + msg += self.kline_ll_recv(msg[-1]+1, bus=bus) return msg def send_heartbeat(self):