summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Hester <kevinh@geeksville.com>2014-04-16 10:01:13 -1000
committerLorenz Meier <lm@inf.ethz.ch>2014-05-20 07:36:25 +0200
commite00c3872821e0914eef066138f3d7cc73a473d4d (patch)
tree6aa9e10798fde43aeedd0909dcfeda1d24620a36
parente0ea1b36bd01a84f291994328004b7e331009eea (diff)
downloadnuttx-e00c3872821e0914eef066138f3d7cc73a473d4d.tar.gz
nuttx-e00c3872821e0914eef066138f3d7cc73a473d4d.tar.bz2
nuttx-e00c3872821e0914eef066138f3d7cc73a473d4d.zip
stm32: add sw workaround for broken stm32 hw RTS implementation
adds support for manually asserting nRTS when the OS buffer is nearly full and deasserting it when it has room again. 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). We assert nRTS from inside of up_recvchars. For deassertion we needed a new callback from the general serial device layer when characters have been dequed. onrxdeque was added to uart_ops_s for this purpose and implemented (currently) only on the stm32.
-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)
}