summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nuttx/arch/arm/src/stm32/stm32_serial.c102
-rw-r--r--nuttx/drivers/serial/serial.c18
-rw-r--r--nuttx/include/nuttx/serial/serial.h21
3 files changed, 134 insertions, 7 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_serial.c b/nuttx/arch/arm/src/stm32/stm32_serial.c
index 89c31addc..336fb3a9c 100644
--- a/nuttx/arch/arm/src/stm32/stm32_serial.c
+++ b/nuttx/arch/arm/src/stm32/stm32_serial.c
@@ -310,6 +310,15 @@ struct up_dev_s
#endif
};
+/*
+ Current STM32s have broken hw based RTS behavior (they assert nRTS after every byte received)
+ To work around this the following define turns on software based management of RTS. The
+ software solution drives nRTS based on the # of free bytes in the nuttx rx buffer
+*/
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+#define HWRTS_BROKEN 1
+#endif
+
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@@ -329,6 +338,7 @@ static bool up_rxavailable(struct uart_dev_s *dev);
static void up_send(struct uart_dev_s *dev, int ch);
static void up_txint(struct uart_dev_s *dev, bool enable);
static bool up_txready(struct uart_dev_s *dev);
+static void up_recvchars(struct up_dev_s *priv);
#ifdef SERIAL_HAVE_DMA
static int up_dma_setup(struct uart_dev_s *dev);
@@ -340,6 +350,10 @@ static bool up_dma_rxavailable(struct uart_dev_s *dev);
static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg);
#endif
+#ifdef HWRTS_BROKEN
+static void up_onrxdeque(struct uart_dev_s *dev);
+#endif
+
#ifdef CONFIG_PM
static void up_pm_notify(struct pm_callback_s *cb, enum pm_state_e pmstate);
static int up_pm_prepare(struct pm_callback_s *cb, enum pm_state_e pmstate);
@@ -389,6 +403,9 @@ static const struct uart_ops_s g_uart_ops =
.txint = up_txint,
.txready = up_txready,
.txempty = up_txready,
+#ifdef HWRTS_BROKEN
+ .onrxdeque = up_onrxdeque,
+#endif
};
#endif
@@ -407,6 +424,9 @@ static const struct uart_ops_s g_uart_dma_ops =
.txint = up_txint,
.txready = up_txready,
.txempty = up_txready,
+#ifdef HWRTS_BROKEN
+ .onrxdeque = up_onrxdeque,
+#endif
};
#endif
@@ -1284,7 +1304,7 @@ static void up_set_format(struct uart_dev_s *dev)
regval = up_serialin(priv, STM32_USART_CR3_OFFSET);
regval &= ~(USART_CR3_CTSE|USART_CR3_RTSE);
-#ifdef CONFIG_SERIAL_IFLOWCONTROL
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(HWRTS_BROKEN)
if (priv->iflow && (priv->rts_gpio != 0))
{
regval |= USART_CR3_RTSE;
@@ -1339,7 +1359,11 @@ static int up_setup(struct uart_dev_s *dev)
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->rts_gpio != 0)
{
- stm32_configgpio(priv->rts_gpio);
+ uint32_t config = priv->rts_gpio;
+#ifdef HWRTS_BROKEN
+ config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT; /* Instead of letting hw manage this pin, we will bitbang */
+#endif
+ stm32_configgpio(config);
}
#endif
@@ -1654,7 +1678,7 @@ static int up_interrupt_common(struct up_dev_s *priv)
* RXNEIE: We cannot call uart_recvchards of RX interrupts are disabled.
*/
- uart_recvchars(&priv->dev);
+ up_recvchars(priv);
handled = true;
}
@@ -2251,7 +2275,7 @@ static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg)
if (priv->rxenable && up_dma_rxavailable(&priv->dev))
{
- uart_recvchars(&priv->dev);
+ up_recvchars(priv);
}
}
#endif
@@ -2451,7 +2475,7 @@ void up_serialinit(void)
for (i = 0; i < STM32_NUSART; i++)
{
-
+
/* Don't create a device for non configured ports */
if (uart_devs[i] == 0)
@@ -2554,6 +2578,74 @@ void stm32_serial_dma_poll(void)
#endif
/****************************************************************************
+ * Name: up_recvchars
+ *
+ * Description:
+ * This is a wrapper for the std uart_recvchars() function. It adds support
+ * for manually asserting nRTS when the OS buffer is nearly full and deasserting
+ * it when it has more room. This makes the RTS pin actually work in a useful
+ * way (the stm32 hw implementation is broken - it asserts nRTS as soon as
+ * more than one character is received)
+ *
+ ****************************************************************************/
+static void up_recvchars(struct up_dev_s *priv)
+{
+ uart_recvchars(&priv->dev);
+
+#ifdef HWRTS_BROKEN
+ if (priv->iflow && (priv->rts_gpio != 0))
+ {
+ // We've delivered the chars to the OS FIFO now update RTS state as needed
+ ssize_t numUsed = uart_numrxavail(&priv->dev);
+ uint8_t threshold = 16;
+ if (threshold > priv->dev.recv.size/4) {
+ threshold = priv->dev.recv.size/4;
+ }
+ // If we have less than 16 bytes left in the buffer then raise
+ // RTS to indicate the sender should stop sending
+ bool wantBackoff = (priv->dev.recv.size - numUsed < threshold);
+
+ if (wantBackoff)
+ stm32_gpiowrite(priv->rts_gpio, true); // high means please stop sending
+ }
+#endif
+}
+
+#ifdef HWRTS_BROKEN
+/****************************************************************************
+ * Name: up_onrxdeque
+ *
+ * Description:
+ * This is a wrapper for the std uart_recvchars() function. It adds support
+ * for manually asserting nRTS when the OS buffer is nearly full and deasserting
+ * it when it has more room. This makes the RTS pin actually work in a useful
+ * way (the stm32 hw implementation is broken - it asserts nRTS as soon as
+ * more than one character is received)
+ *
+ ****************************************************************************/
+static void up_onrxdeque(struct uart_dev_s *dev)
+{
+ struct up_dev_s *priv = (struct up_dev_s*)dev->priv;
+
+ if (priv->iflow && (priv->rts_gpio != 0))
+ {
+ // We've delivered the chars to the OS FIFO now update RTS state as needed
+ ssize_t numUsed = uart_numrxavail(dev);
+ uint8_t threshold = 32;
+ if (threshold > priv->dev.recv.size/2) {
+ threshold = priv->dev.recv.size/2;
+ }
+ // If we have 32 bytes free in the buffer then we tell the
+ // sender they can resume
+ bool wantResume = (priv->dev.recv.size - numUsed >= threshold);
+
+ if(wantResume)
+ stm32_gpiowrite(priv->rts_gpio, false); // nRTS low means ready to recv
+ }
+}
+#endif
+
+/****************************************************************************
* Name: up_putc
*
* Description:
diff --git a/nuttx/drivers/serial/serial.c b/nuttx/drivers/serial/serial.c
index cbe502cc9..06a13620a 100644
--- a/nuttx/drivers/serial/serial.c
+++ b/nuttx/drivers/serial/serial.c
@@ -552,6 +552,8 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen
dev->recv.tail = tail;
+ uart_onrxdeque(dev);
+
#ifdef CONFIG_SERIAL_TERMIOS
/* Do input processing if any is enabled */
@@ -1171,6 +1173,8 @@ static int uart_open(FAR struct file *filep)
dev->recv.head = 0;
dev->recv.tail = 0;
+ uart_onrxdeque(dev);
+
/* initialise termios state */
#ifdef CONFIG_SERIAL_TERMIOS
@@ -1355,4 +1359,16 @@ void uart_connected(FAR uart_dev_t *dev, bool connected)
#endif
-
+/************************************************************************************
+ * Name: uart_numrxavail
+ *
+ * Description:
+ * This function returns the number of characters that are currently available for
+ * reading.
+ *
+ ************************************************************************************/
+ssize_t uart_numrxavail(FAR uart_dev_t *dev)
+{
+ struct uart_buffer_s *buf = &dev->recv;
+ return (buf->head >= buf->tail) ? buf->head - buf->tail : buf->size - buf->tail + buf->head;
+}
diff --git a/nuttx/include/nuttx/serial/serial.h b/nuttx/include/nuttx/serial/serial.h
index 7f5ea9170..04143062a 100644
--- a/nuttx/include/nuttx/serial/serial.h
+++ b/nuttx/include/nuttx/serial/serial.h
@@ -47,7 +47,7 @@
#include <stdbool.h>
#include <semaphore.h>
#ifdef CONFIG_SERIAL_TERMIOS
-# include <termios.h>
+# include <termios.h>
#endif
#include <nuttx/fs/fs.h>
@@ -77,6 +77,7 @@
#define uart_txempty(dev) dev->ops->txempty(dev)
#define uart_send(dev,ch) dev->ops->send(dev,ch)
#define uart_receive(dev,s) dev->ops->receive(dev,s)
+#define uart_onrxdeque(dev) if(dev->ops->onrxdeque) dev->ops->onrxdeque(dev)
/************************************************************************************
* Public Types
@@ -180,6 +181,14 @@ struct uart_ops_s
*/
CODE bool (*txempty)(FAR struct uart_dev_s *dev);
+
+ /*
+ * Drivers can optionally provide this callback which is invoked any time
+ * received characters have been dequeued in uart_read(). The expected
+ * use-case is for reenabling nRTS if the driver is doing software assisted
+ * HW flow control.
+ */
+ CODE void (*onrxdeque)(FAR struct uart_dev_s *dev);
};
/* This is the device structure used by the driver. The caller of
@@ -335,6 +344,16 @@ void uart_datasent(FAR uart_dev_t *dev);
void uart_connected(FAR uart_dev_t *dev, bool connected);
#endif
+/************************************************************************************
+ * Name: uart_numrxavail
+ *
+ * Description:
+ * This function returns the number of characters that are currently available for
+ * reading.
+ *
+ ************************************************************************************/
+ssize_t uart_numrxavail(FAR uart_dev_t *dev);
+
#undef EXTERN
#if defined(__cplusplus)
}