[mimxrt (teensy) Allow Any GPIO pin for RS485 pin

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.
This commit is contained in:
KurtE 2022-04-27 11:26:08 -07:00
parent 862188d2ee
commit 73f6b48676
2 changed files with 81 additions and 8 deletions

View File

@ -39,6 +39,17 @@
#include "periph.h" #include "periph.h"
#include "fsl_lpuart.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 // arrays use 0 based numbering: UART1 is stored at index 0
#define MAX_UART 8 #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->rx);
common_hal_never_reset_pin(self->rts); common_hal_never_reset_pin(self->rts);
common_hal_never_reset_pin(self->cts); 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, 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")); 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. // We are transmitting one direction if one pin is NULL and the other isn't.
bool is_onedirection = (rx == NULL) != (tx == NULL); bool is_onedirection = (rx == NULL) != (tx == NULL);
bool uart_taken = false; 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 rx_count = MP_ARRAY_SIZE(mcu_uart_rx_list);
const uint32_t tx_count = MP_ARRAY_SIZE(mcu_uart_tx_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 // Filter for sane settings for RS485
if (rs485_dir != NULL) { 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)) { if ((rts != NULL) || (cts != NULL)) {
mp_raise_ValueError(translate("Cannot specify RTS or CTS in RS485 mode")); mp_raise_ValueError(translate("Cannot specify RTS or CTS in RS485 mode"));
} }
// For IMXRT the RTS pin is used for RS485 direction // For IMXRT the RTS pin is used for RS485 direction ???? - Can be will try
rts = rs485_dir; // it if this is an rts pin.
} else { } else {
if (rs485_invert) { if (rs485_invert) {
mp_raise_ValueError(translate("RS485 inversion specified when not in RS485 mode")); 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 rts_count = MP_ARRAY_SIZE(mcu_uart_rts_list);
const uint32_t cts_count = MP_ARRAY_SIZE(mcu_uart_cts_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) { 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].bank_idx == rx_config->bank_idx) {
if (mcu_uart_rts_list[i].pin == rts) { if (mcu_uart_rts_list[i].pin == rts) {
rts_config = &mcu_uart_rts_list[i]; rts_config = &mcu_uart_rts_list[i];
break; 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); 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); 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); config_periph_pin(cts_config);
self->cts = cts; 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_config_t config = { 0 };
LPUART_GetDefaultConfig(&config); 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 // Before we init, setup RS485 direction pin
// ..unfortunately this isn't done by the driver library // ..unfortunately this isn't done by the driver library
uint32_t modir = (self->uart->MODIR) & ~(LPUART_MODIR_TXRTSPOL_MASK | LPUART_MODIR_TXRTSE_MASK); 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; modir |= LPUART_MODIR_TXRTSE_MASK;
if (rs485_invert) { if (rs485_invert) {
modir |= LPUART_MODIR_TXRTSPOL_MASK; modir |= LPUART_MODIR_TXRTSPOL_MASK;
@ -311,6 +360,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
claim_pin(self->rx); claim_pin(self->rx);
} }
DBGPrintf(&mp_plat_print, "\t<< Init completed >>\n");
} }
bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { 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->rx);
common_hal_reset_pin(self->tx); 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->rx = NULL;
self->tx = NULL; self->tx = NULL;
self->cts = NULL;
self->rts = NULL;
self->rs485_dir = NULL;
} }
// Read characters. // 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) { if (self->tx == NULL) {
mp_raise_ValueError(translate("No TX pin")); mp_raise_ValueError(translate("No TX pin"));
} }
if (self->rs485_dir && len) {
GPIO_PinWrite(self->rs485_dir->gpio, self->rs485_dir->number, !self->rs485_invert);
LPUART_WriteBlocking(self->uart, data, len); 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; return len;
} }

View File

@ -50,6 +50,9 @@ typedef struct {
const mcu_pin_obj_t *tx; const mcu_pin_obj_t *tx;
const mcu_pin_obj_t *cts; const mcu_pin_obj_t *cts;
const mcu_pin_obj_t *rts; const mcu_pin_obj_t *rts;
const mcu_pin_obj_t *rs485_dir;
bool rs485_invert;
} busio_uart_obj_t; } busio_uart_obj_t;
void uart_reset(void); void uart_reset(void);