From 73f6b486766233a4f2631cf1c5bbd7b0a1782433 Mon Sep 17 00:00:00 2001 From: KurtE Date: Wed, 27 Apr 2022 11:26:08 -0700 Subject: [PATCH] [mimxrt (teensy) Allow Any GPIO pin for RS485 pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code was setup that allowed you to specify an RTS pin to be used as an RS485 direction pin, however there are no RTS pins that are exposed on any of the Teensy 4.x boards. Instead Arduino code base allowed you to specify any GPIO pin to work instead. So I added the code in to facilitate this. In addition the alternative code to wrap your own GPIO pin set high and low around call(s) to uart.write() will not currently work, unless maybe you fudge it and add your own delays as the write will return after the last byte was pushed onto the UART’s hardware FIFO queue and as such if you then immediately set the IO pin low, it will corrupt your output stream. The code I added detects that you are setup to use the RS485 pin and before it returns will wait for the UART’s Transfer complete status flag to be set. --- ports/mimxrt10xx/common-hal/busio/UART.c | 86 +++++++++++++++++++++--- ports/mimxrt10xx/common-hal/busio/UART.h | 3 + 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/ports/mimxrt10xx/common-hal/busio/UART.c b/ports/mimxrt10xx/common-hal/busio/UART.c index 77a2f97974..71de53f07f 100644 --- a/ports/mimxrt10xx/common-hal/busio/UART.c +++ b/ports/mimxrt10xx/common-hal/busio/UART.c @@ -39,6 +39,17 @@ #include "periph.h" #include "fsl_lpuart.h" +#include "fsl_gpio.h" +// ========================================================== +// Debug code +// ========================================================== +#define ENABLE_DEBUG_PRINTING 0 +#if ENABLE_DEBUG_PRINTING +#define DBGPrintf mp_printf +#else +#define DBGPrintf(p,...) +#endif + // arrays use 0 based numbering: UART1 is stored at index 0 #define MAX_UART 8 @@ -90,6 +101,7 @@ void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { common_hal_never_reset_pin(self->rx); common_hal_never_reset_pin(self->rts); common_hal_never_reset_pin(self->cts); + common_hal_never_reset_pin(self->rs485_dir); } void common_hal_busio_uart_construct(busio_uart_obj_t *self, @@ -108,9 +120,12 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, mp_raise_ValueError(translate("Invalid word/bit length")); } + DBGPrintf(&mp_plat_print, "uart_construct: tx:%p rx:%p rts:%p cts:%p rs485:%p\n", tx, rx, rts, cts, rs485_dir); + // We are transmitting one direction if one pin is NULL and the other isn't. bool is_onedirection = (rx == NULL) != (tx == NULL); bool uart_taken = false; + bool use_rts_for_rs485 = false; const uint32_t rx_count = MP_ARRAY_SIZE(mcu_uart_rx_list); const uint32_t tx_count = MP_ARRAY_SIZE(mcu_uart_tx_list); @@ -187,11 +202,14 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Filter for sane settings for RS485 if (rs485_dir != NULL) { + DBGPrintf(&mp_plat_print, "\t(485 pin): gpio:%p #:%x Mux: %x %x cfg:%x reset:%x %x\n", + rs485_dir->gpio, rs485_dir->number, rs485_dir->mux_idx, rs485_dir->mux_reg, rs485_dir->cfg_reg, + rs485_dir->mux_reset, rs485_dir->pad_reset); if ((rts != NULL) || (cts != NULL)) { mp_raise_ValueError(translate("Cannot specify RTS or CTS in RS485 mode")); } - // For IMXRT the RTS pin is used for RS485 direction - rts = rs485_dir; + // For IMXRT the RTS pin is used for RS485 direction ???? - Can be will try + // it if this is an rts pin. } else { if (rs485_invert) { mp_raise_ValueError(translate("RS485 inversion specified when not in RS485 mode")); @@ -202,16 +220,22 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const uint32_t rts_count = MP_ARRAY_SIZE(mcu_uart_rts_list); const uint32_t cts_count = MP_ARRAY_SIZE(mcu_uart_cts_list); - if (rts != NULL) { + if ((rts != NULL) || (rs485_dir != NULL)) { for (uint32_t i = 0; i < rts_count; ++i) { if (mcu_uart_rts_list[i].bank_idx == rx_config->bank_idx) { if (mcu_uart_rts_list[i].pin == rts) { rts_config = &mcu_uart_rts_list[i]; break; + } else if (mcu_uart_rts_list[i].pin == rs485_dir) { + rts_config = &mcu_uart_rts_list[i]; + use_rts_for_rs485 = true; + rts = rs485_dir; + rs485_dir = NULL; + break; } } } - if (rts_config == NULL) { + if ((rts != NULL) && (rts_config == NULL)) { mp_raise_ValueError_varg(translate("Invalid %q pin"), MP_QSTR_RTS); } } @@ -225,7 +249,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, } } } - if (cts == NULL) { + if (cts_config == NULL) { mp_raise_ValueError_varg(translate("Invalid %q pin"), MP_QSTR_CTS); } } @@ -257,7 +281,32 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, config_periph_pin(cts_config); self->cts = cts; } + if (rs485_dir) { + DBGPrintf(&mp_plat_print, "\tInit rs485 pin\n"); + // lets configure this pin as standard GPIO output pin. + claim_pin(rs485_dir); + #define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_ALT5 5U + IOMUXC_SetPinMux(rs485_dir->mux_reg, IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_ALT5, 0, 0, 0, 0); + DBGPrintf(&mp_plat_print, "\tAfter IOMUXC_SetPinMux\n"); + IOMUXC_SetPinConfig(0, 0, 0, 0, rs485_dir->cfg_reg, + IOMUXC_SW_PAD_CTL_PAD_HYS(1) + | IOMUXC_SW_PAD_CTL_PAD_PUS(0) + | IOMUXC_SW_PAD_CTL_PAD_PUE(0) + | IOMUXC_SW_PAD_CTL_PAD_PKE(1) + | IOMUXC_SW_PAD_CTL_PAD_ODE(0) + | IOMUXC_SW_PAD_CTL_PAD_SPEED(2) + | IOMUXC_SW_PAD_CTL_PAD_DSE(1) + | IOMUXC_SW_PAD_CTL_PAD_SRE(0)); + DBGPrintf(&mp_plat_print, "\tAfter IOMUXC_SetPinConfig\n"); + + const gpio_pin_config_t config = { kGPIO_DigitalOutput, rs485_invert, kGPIO_NoIntmode }; + GPIO_PinInit(rs485_dir->gpio, rs485_dir->number, &config); + DBGPrintf(&mp_plat_print, "\tAfter GPIO_PinInit\n"); + self->rs485_dir = rs485_dir; + self->rs485_invert = rs485_invert; + + } lpuart_config_t config = { 0 }; LPUART_GetDefaultConfig(&config); @@ -279,7 +328,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Before we init, setup RS485 direction pin // ..unfortunately this isn't done by the driver library uint32_t modir = (self->uart->MODIR) & ~(LPUART_MODIR_TXRTSPOL_MASK | LPUART_MODIR_TXRTSE_MASK); - if (rs485_dir != NULL) { + if (use_rts_for_rs485) { modir |= LPUART_MODIR_TXRTSE_MASK; if (rs485_invert) { modir |= LPUART_MODIR_TXRTSPOL_MASK; @@ -311,6 +360,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, claim_pin(self->rx); } + DBGPrintf(&mp_plat_print, "\t<< Init completed >>\n"); } bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { @@ -330,9 +380,16 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { common_hal_reset_pin(self->rx); common_hal_reset_pin(self->tx); + common_hal_reset_pin(self->cts); + common_hal_reset_pin(self->rts); + common_hal_reset_pin(self->rs485_dir); self->rx = NULL; self->tx = NULL; + self->cts = NULL; + self->rts = NULL; + self->rs485_dir = NULL; + } // Read characters. @@ -393,8 +450,21 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, if (self->tx == NULL) { mp_raise_ValueError(translate("No TX pin")); } - - LPUART_WriteBlocking(self->uart, data, len); + if (self->rs485_dir && len) { + GPIO_PinWrite(self->rs485_dir->gpio, self->rs485_dir->number, !self->rs485_invert); + LPUART_WriteBlocking(self->uart, data, len); + // Probably need to verify we have completed output. + uint32_t dont_hang_count = 0xffff; + while (dont_hang_count--) { + if (LPUART_GetStatusFlags(self->uart) & kLPUART_TransmissionCompleteFlag) { + break; // hardware says it completed. + } + } + GPIO_PinWrite(self->rs485_dir->gpio, self->rs485_dir->number, self->rs485_invert); + } else { + // could combine with above but would go through two ifs + LPUART_WriteBlocking(self->uart, data, len); + } return len; } diff --git a/ports/mimxrt10xx/common-hal/busio/UART.h b/ports/mimxrt10xx/common-hal/busio/UART.h index fb3fd3245a..bc8374aabb 100644 --- a/ports/mimxrt10xx/common-hal/busio/UART.h +++ b/ports/mimxrt10xx/common-hal/busio/UART.h @@ -50,6 +50,9 @@ typedef struct { const mcu_pin_obj_t *tx; const mcu_pin_obj_t *cts; const mcu_pin_obj_t *rts; + const mcu_pin_obj_t *rs485_dir; + bool rs485_invert; + } busio_uart_obj_t; void uart_reset(void);