From e7f046d518a2670e025a1e09add10d593d548e9b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 24 Sep 2020 19:54:52 +0200 Subject: [PATCH] Add support for UART1 --- CHANGELOG.md | 1 + Firmware/Board/v3/board.cpp | 19 +++++++---- Firmware/communication/communication.cpp | 9 +++++- Firmware/communication/interface_uart.cpp | 7 ++-- Firmware/communication/interface_uart.h | 3 +- Firmware/odrive-interface.yaml | 23 ++++++++++--- docs/interfaces.md | 39 ++++++++++++----------- docs/resources.md | 7 +++- tools/odrive/tests/uart_ascii_test.py | 38 ++++++++++++++++++---- 9 files changed, 103 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a6c0c76..62a7a2a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * [Mechanical brake support](docs/mechanical-brakes.md) +* Support for UART1 on GPIO3 and GPIO4. UART0 (on GPIO1/2) and UART1 can currently not be enabled at the same time. ### Changed diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index d2b0afbb..c20e8568 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -21,7 +21,7 @@ Stm32SpiArbiter spi3_arbiter{&hspi3}; Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter; UART_HandleTypeDef* uart0 = &huart4; -UART_HandleTypeDef* uart1 = nullptr; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 +UART_HandleTypeDef* uart1 = &huart2; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 UART_HandleTypeDef* uart2 = nullptr; Drv8301 m0_gate_driver{ @@ -219,14 +219,14 @@ std::array alternate_functions[GPIO_COUNT] = { #if HW_VERSION_MINOR >= 3 /* GPIO1: */ {{{ODrive::GPIO_MODE_UART0, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, /* GPIO2: */ {{{ODrive::GPIO_MODE_UART0, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, - /* GPIO3: */ {{{ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, + /* GPIO3: */ {{{ODrive::GPIO_MODE_UART1, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, #else /* GPIO1: */ {{}}, /* GPIO2: */ {{}}, /* GPIO3: */ {{}}, #endif - /* GPIO4: */ {{{ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, + /* GPIO4: */ {{{ODrive::GPIO_MODE_UART1, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, /* GPIO5: */ {{}}, /* GPIO6: */ {{}}, /* GPIO7: */ {{}}, @@ -273,13 +273,18 @@ bool board_init() { MX_SPI3_Init(); MX_ADC3_Init(); MX_TIM2_Init(); - MX_UART4_Init(); MX_TIM5_Init(); MX_TIM13_Init(); - HAL_UART_DeInit(uart0); - uart0->Init.BaudRate = odrv.config_.uart0_baudrate; - HAL_UART_Init(uart0); + if (odrv.config_.enable_uart0) { + uart0->Init.BaudRate = odrv.config_.uart0_baudrate; + MX_UART4_Init(); + } + + if (odrv.config_.enable_uart1) { + uart1->Init.BaudRate = odrv.config_.uart1_baudrate; + MX_USART2_UART_Init(); + } if (odrv.config_.enable_i2c0) { // Set up the direction GPIO as input diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index faac6f59..715a23eb 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -41,8 +41,15 @@ size_t oscilloscope_pos = 0; void init_communication(void) { printf("hi!\r\n"); + // Dual UART operation not supported yet + if (odrv.config_.enable_uart0 && odrv.config_.enable_uart1) { + odrv.misconfigured_ = true; + } + if (odrv.config_.enable_uart0 && uart0) { - start_uart_server(); + start_uart_server(uart0); + } else if (odrv.config_.enable_uart1 && uart1) { + start_uart_server(uart1); } start_usb_server(); diff --git a/Firmware/communication/interface_uart.cpp b/Firmware/communication/interface_uart.cpp index 74fa02c0..8eff51d8 100644 --- a/Firmware/communication/interface_uart.cpp +++ b/Firmware/communication/interface_uart.cpp @@ -22,8 +22,7 @@ static uint32_t dma_last_rcv_idx; // static thread_local uint32_t deadline_ms = 0; osThreadId uart_thread = 0; -extern UART_HandleTypeDef* uart0; -static UART_HandleTypeDef* huart_ = uart0; // defined in board.cpp. +static UART_HandleTypeDef* huart_ = nullptr; const uint32_t stack_size_uart_thread = 4096; // Bytes @@ -102,7 +101,9 @@ static void uart_server_thread(void * ctx) { } // TODO: allow multiple UART server instances -void start_uart_server() { +void start_uart_server(UART_HandleTypeDef* huart) { + huart_ = huart; + // DMA is set up to receive in a circular buffer forever. // We dont use interrupts to fetch the data, instead we periodically read // data out of the circular buffer into a parse buffer, controlled by a state machine diff --git a/Firmware/communication/interface_uart.h b/Firmware/communication/interface_uart.h index a7df55bd..83b70e66 100644 --- a/Firmware/communication/interface_uart.h +++ b/Firmware/communication/interface_uart.h @@ -9,11 +9,12 @@ extern "C" { #endif #include +#include "usart.h" extern osThreadId uart_thread; extern const uint32_t stack_size_uart_thread; -void start_uart_server(void); +void start_uart_server(UART_HandleTypeDef* huart); void uart_poll(void); #ifdef __cplusplus diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 43126cbd..cb881b5b 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -99,13 +99,24 @@ interfaces: enable_uart0: type: bool - doc: Enables/disables UART0. You also need to set the corresponding GPIOs to GPIO_MODE_UART0. Changing this requires a reboot. - enable_uart1: {type: bool, doc: Not supported on ODrive v3.x.} + brief: Enables/disables UART0. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART0. + Refer to [interfaces](interfaces.md) to see which pins support UART0. + Changing this requires a reboot. + enable_uart0: + type: bool + brief: Enables/disables UART1. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART1. + Refer to [interfaces](interfaces.md) to see which pins support UART1. + Changing this requires a reboot. enable_uart2: {type: bool, doc: Not supported on ODrive v3.x.} uart0_baudrate: type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. doc: | - Defines the baudrate used on the UART interface. Some baudrates will have a small timing error due to hardware limitations. Here's an (incomplete) list of baudrates for ODrive v3.x: @@ -127,7 +138,11 @@ interfaces: For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). - uart1_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} + uart1_baudrate: + type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. + doc: See `uart0_baudrate` for details. uart2_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} enable_can0: type: bool diff --git a/docs/interfaces.md b/docs/interfaces.md index 0b9cb6c3..5de89a3b 100644 --- a/docs/interfaces.md +++ b/docs/interfaces.md @@ -18,25 +18,25 @@ The ODrive can be controlled over various ports and protocols. If you're comfort ## Pinout -| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | -|----|---------------|------------------------|-----------------------|-------------------|------------------|------------------|------------------|------------------|------------------|------------------------| -| 0 | _not a pin_ | | | | | | | | | | -| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | PWM0.0 | | | | | mechanical brake | -| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | PWM0.1 | | | | | mechanical brake | -| 3 | GPIO3 | general purpose | **analog input** | | PWM0.2 | | | | | mechanical brake | -| 4 | GPIO4 | general purpose | **analog input** | | PWM0.3 | | | | | mechanical brake | -| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | mechanical brake | -| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | mechanical brake | -| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | mechanical brake | -| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | mechanical brake | -| 9 | M0.A | general purpose | | | | | | **ENC0.A** | | | -| 10 | M0.B | general purpose | | | | | | **ENC0.B** | | | -| 11 | M0.Z | **general purpose** | | | | | | | | | -| 12 | M1.A | general purpose | | | | | I2C.SCL | | **ENC1.A** | | -| 13 | M1.B | general purpose | | | | | I2C.SDA | | **ENC1.B** | | -| 14 | M1.Z | **general purpose** | | | | | | | | | -| 15 | _not exposed_ | general purpose | | | | **CAN0.RX** | I2C.SCL | | | | -| 16 | _not exposed_ | general purpose | | | | **CAN0.TX** | I2C.SDA | | | | +| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_UART1` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | +|----|---------------|------------------------|-----------------------|-------------------|-------------------|------------------|------------------|------------------|------------------|------------------|------------------------| +| 0 | _not a pin_ | | | | | | | | | | | +| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | | PWM0.0 | | | | | mechanical brake | +| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | | PWM0.1 | | | | | mechanical brake | +| 3 | GPIO3 | general purpose | **analog input** | | **UART1.TX** | PWM0.2 | | | | | mechanical brake | +| 4 | GPIO4 | general purpose | **analog input** | | **UART1.RX** | PWM0.3 | | | | | mechanical brake | +| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | | mechanical brake | +| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 9 | M0.A | general purpose | | | | | | | **ENC0.A** | | | +| 10 | M0.B | general purpose | | | | | | | **ENC0.B** | | | +| 11 | M0.Z | **general purpose** | | | | | | | | | | +| 12 | M1.A | general purpose | | | | | | I2C.SCL | | **ENC1.A** | | +| 13 | M1.B | general purpose | | | | | | I2C.SDA | | **ENC1.B** | | +| 14 | M1.Z | **general purpose** | | | | | | | | | | +| 15 | _not exposed_ | general purpose | | | | | **CAN0.RX** | I2C.SCL | | | | +| 16 | _not exposed_ | general purpose | | | | | **CAN0.TX** | I2C.SDA | | | | (*) ODrive v3.5 and later
@@ -50,6 +50,7 @@ Notes: * Digital mode is a general purpose mode that can be used for these functions: step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS. * You must also connect GND between ODrive and your other board. * ODrive v3.3 and onward have 5V tolerant GPIO pins. +* Simultaneous operation of UART0 and UART1 is currently not supported. ## Native Protocol diff --git a/docs/resources.md b/docs/resources.md index 2d6fcb8f..e0408179 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -21,14 +21,17 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv | 13 | DMA1_Stream2_IRQn | 5 | | 15 | DMA1_Stream4_IRQn | 5 | | 16 | DMA1_Stream5_IRQn | 5 | +| 17 | DMA1_Stream6_IRQn | 5 | | 18 | ADC_IRQn | 5 | | 19 | CAN1_TX_IRQn | 6 | | 20 | CAN1_RX0_IRQn | 6 | | 21 | CAN1_RX1_IRQn | 6 | | 22 | CAN1_SCE_IRQn | 6 | | 25 | TIM1_UP_TIM10_IRQn | 0 | +| 38 | USART2_IRQn | 5 | | 44 | TIM8_UP_TIM13_IRQn | 0 | | 45 | TIM8_TRG_COM_TIM14_IRQn | 0 | +| 47 | DMA1_Stream7_IRQn | 5 | | 50 | TIM5_IRQn | 5 | | 51 | SPI3_IRQn | 5 | | 52 | UART4_IRQn | 5 | @@ -44,6 +47,8 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv | DMA1_Stream0 | 1 | 0 (SPI3_RX) | SPI | | DMA1_Stream2 | 0 | 4 (UART4_RX) | UART0 | | DMA1_Stream4 | 0 | 4 (UART4_TX) | UART0 | -| DMA1_Stream5 | 1 | 0 (SPI3_TX) | SPI | +| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART1 | +| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART1 | +| DMA1_Stream7 | 1 | 0 (SPI3_TX) | SPI | | DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | diff --git a/tools/odrive/tests/uart_ascii_test.py b/tools/odrive/tests/uart_ascii_test.py index 962f3797..7f568f3c 100644 --- a/tools/odrive/tests/uart_ascii_test.py +++ b/tools/odrive/tests/uart_ascii_test.py @@ -41,16 +41,40 @@ class TestUartAscii(): 'rx': (odrive.gpio1, True), 'tx': (odrive.gpio2, False) }, SerialPortComponent)) - yield (odrive, ports) + yield (odrive, 0, ports) - def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): - logger.debug('Enabling UART...') + # Enable the line below to manually test UART1. For this you need + # to manually move to the wires go to GPIO1/2 to GPIO3/4. The ones + # that normally go to GPIO3/4 have a low pass filter. + #yield (odrive, 1, ports) + + def run_test(self, odrive: ODriveComponent, uart_num: int, port: SerialPortComponent, logger: Logger): + logger.debug('Enabling UART {}...'.format(uart_num)) + # GPIOs might be in use by something other than UART and some components # might be configured so that they would fail in the later test. - odrive.erase_config_and_reboot() - odrive.handle.config.enable_uart0 = True - odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 - odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.disable_mappings() + odrive.handle.config.enable_uart0 = False + odrive.handle.config.uart0_baudrate = 115200 + odrive.handle.config.enable_uart1 = False + odrive.handle.config.uart1_baudrate = 115200 + odrive.handle.config.enable_uart2 = False + odrive.handle.config.uart2_baudrate = 115200 + + if uart_num == 0: + odrive.handle.config.enable_uart0 = True + odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 + odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.handle.config.gpio3_mode = GPIO_MODE_ANALOG_IN + odrive.handle.config.gpio4_mode = GPIO_MODE_ANALOG_IN + else: + odrive.handle.config.enable_uart1 = True + odrive.handle.config.gpio1_mode = GPIO_MODE_ANALOG_IN + odrive.handle.config.gpio2_mode = GPIO_MODE_ANALOG_IN + odrive.handle.config.gpio3_mode = GPIO_MODE_UART1 + odrive.handle.config.gpio4_mode = GPIO_MODE_UART1 + + odrive.save_config_and_reboot() with port.open(115200) as ser: # reset port to known state