summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/stm32/stm32_serial.c
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 /nuttx/arch/arm/src/stm32/stm32_serial.c
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.
Diffstat (limited to 'nuttx/arch/arm/src/stm32/stm32_serial.c')
-rw-r--r--nuttx/arch/arm/src/stm32/stm32_serial.c102
1 files changed, 97 insertions, 5 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: