diff --git a/ports/mimxrt10xx/common-hal/busio/UART.c b/ports/mimxrt10xx/common-hal/busio/UART.c index 77a2f97974..57eaada7ef 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. @@ -371,6 +428,10 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t uint32_t recvd = 0; LPUART_TransferGetReceiveCount(self->uart, &self->handle, &recvd); LPUART_TransferAbortReceive(self->uart, &self->handle); + if (recvd == 0) { + *errcode = EAGAIN; + return MP_STREAM_ERROR; + } return recvd; } @@ -393,8 +454,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); diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 1819748992..06a0fb128d 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -63,7 +63,12 @@ //| :param int receiver_buffer_size: the character length of the read buffer (0 to disable). (When a character is 9 bits the buffer will be 2 * receiver_buffer_size bytes.) //| //| *New in CircuitPython 4.0:* ``timeout`` has incompatibly changed units from milliseconds to seconds. -//| The new upper limit on ``timeout`` is meant to catch mistaken use of milliseconds.""" +//| The new upper limit on ``timeout`` is meant to catch mistaken use of milliseconds. +//| +//| .. note:: RS485 support on i.MX and Raspberry Pi RP2040 is implemented in software. +//| The timing for the ``rs485_dir`` pin signal is done on a best-effort basis, and may not meet +//| RS485 specifications intermittently. +//| """ //| ... //| typedef struct {