You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/01/02 21:47:41 UTC
[incubator-nuttx] branch pr29 updated: LPC17xx serial updates (#29)
This is an automated email from the ASF dual-hosted git repository.
gnutt pushed a commit to branch pr29
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/pr29 by this push:
new 8510107 LPC17xx serial updates (#29)
8510107 is described below
commit 85101078cd937ef7d48f776ae18e6ae5c20d6515
Author: macman88 <jj...@gmail.com>
AuthorDate: Thu Jan 2 15:47:32 2020 -0600
LPC17xx serial updates (#29)
* Save CONFIG_ARCH_BOARD_CUSTOM when running 'make savedefconfig'
* Don't compile up_earlyserialinit if USE_EARLYSERIALINIT is not defined
* Added support for RS485 direction control on LPC17xx UART1
* First pass at fractional baud rate divider on LPC17xx/40xx
* Added support for fractional divider to console UART
---
arch/arm/src/lpc17xx_40xx/Kconfig | 30 ++++
arch/arm/src/lpc17xx_40xx/lpc17_40_lowputc.c | 38 +++--
arch/arm/src/lpc17xx_40xx/lpc17_40_serial.c | 216 +++++++++++++++++++++++----
arch/arm/src/lpc17xx_40xx/lpc17_40_serial.h | 1 +
arch/arm/src/lpc43xx/lpc43_uart.c | 4 +-
5 files changed, 247 insertions(+), 42 deletions(-)
diff --git a/arch/arm/src/lpc17xx_40xx/Kconfig b/arch/arm/src/lpc17xx_40xx/Kconfig
index ec9a595..c6de373 100644
--- a/arch/arm/src/lpc17xx_40xx/Kconfig
+++ b/arch/arm/src/lpc17xx_40xx/Kconfig
@@ -561,6 +561,36 @@ config LPC17_40_UART1_RINGINDICATOR
---help---
Enable UART1 ring indicator
+config LPC17_40_UART1_RS485
+ bool "RS-485 on UART1"
+ default n
+ depends on LPC17_40_UART1
+ ---help---
+ Enable RS-485 interface on UART1. Your board config will have to
+ provide GPIO_UART1_RS485_DIR pin definition.
+
+config LPC17_40_RS485_DIR_POLARITY
+ int "UART1 RS-485 DIR pin polarity"
+ default 1
+ range 0 1
+ depends on LPC17_40_UART1_RS485
+ ---help---
+ Polarity of DIR pin for RS-485 on UART1. Set to state on DIR pin which
+ enables TX (0 - low / nTXEN, 1 - high / TXEN).
+
+config LPC17_40_UART1_RS485_DIR_DTR
+ bool "UART1 RS-485 DIR pin use DTR"
+ default n
+ depends on LPC17_40_UART1_RS485
+ ---help---
+ Selects between RTS and DTR pins for RS485 DIR. This must correspond to
+ the GPIO_USART1_RS485_DIR pin specified in your board config. The DTR pin
+ will be used if selected, the RTS pin will be used otherwise.
+
+config LPC17_40_UART_USE_FRACTIONAL_DIVIDER
+ bool "Use fractional divider for UART baud rate"
+ default n
+
endmenu
menu "ADC driver options"
diff --git a/arch/arm/src/lpc17xx_40xx/lpc17_40_lowputc.c b/arch/arm/src/lpc17xx_40xx/lpc17_40_lowputc.c
index 166050b..8e041ca 100644
--- a/arch/arm/src/lpc17xx_40xx/lpc17_40_lowputc.c
+++ b/arch/arm/src/lpc17xx_40xx/lpc17_40_lowputc.c
@@ -154,9 +154,6 @@
* And for the LPC178x/40xx, the PCLK is determined by the global divisor setting in
* the PLKSEL register.
*
- * Ignoring the fractional divider for now. (If you want to extend this driver
- * to support the fractional divider, see lpc43xx_uart.c. The LPC43xx uses
- * the same peripheral and that logic could easily leveraged here).
*/
#ifdef LPC178x_40xx
@@ -165,6 +162,10 @@
# define CONSOLE_NUMERATOR BOARD_PCLK_FREQUENCY
#else
+# ifdef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
+# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK
+# define CONSOLE_NUMERATOR (LPC17_40_CCLK)
+# else
/* Calculate and optimal PCLKSEL0/1 divisor.
* First, check divisor == 1. This works if the upper limit is met:
*
@@ -181,9 +182,9 @@
* BAUD <= CCLK / 16 / MinDL
*/
-# if CONSOLE_BAUD < (LPC17_40_CCLK / 16 / UART_MINDL)
-# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK
-# define CONSOLE_NUMERATOR (LPC17_40_CCLK)
+# if CONSOLE_BAUD < (LPC17_40_CCLK / 16 / UART_MINDL)
+# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK
+# define CONSOLE_NUMERATOR (LPC17_40_CCLK)
/* Check divisor == 2. This works if:
*
@@ -196,9 +197,9 @@
* BAUD <= CCLK / 8 / MinDL
*/
-# elif CONSOLE_BAUD < (LPC17_40_CCLK / 8 / UART_MINDL)
-# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK2
-# define CONSOLE_NUMERATOR (LPC17_40_CCLK / 2)
+# elif CONSOLE_BAUD < (LPC17_40_CCLK / 8 / UART_MINDL)
+# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK2
+# define CONSOLE_NUMERATOR (LPC17_40_CCLK / 2)
/* Check divisor == 4. This works if:
*
@@ -211,9 +212,9 @@
* BAUD <= CCLK / 4 / MinDL
*/
-# elif CONSOLE_BAUD < (LPC17_40_CCLK / 4 / UART_MINDL)
-# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK4
-# define CONSOLE_NUMERATOR (LPC17_40_CCLK / 4)
+# elif CONSOLE_BAUD < (LPC17_40_CCLK / 4 / UART_MINDL)
+# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK4
+# define CONSOLE_NUMERATOR (LPC17_40_CCLK / 4)
/* Check divisor == 8. This works if:
*
@@ -226,9 +227,10 @@
* BAUD <= CCLK / 2 / MinDL
*/
-# else /* if CONSOLE_BAUD < (LPC17_40_CCLK / 2 / UART_MINDL) */
-# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK8
-# define CONSOLE_NUMERATOR (LPC17_40_CCLK / 8)
+# else /* if CONSOLE_BAUD < (LPC17_40_CCLK / 2 / UART_MINDL) */
+# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK8
+# define CONSOLE_NUMERATOR (LPC17_40_CCLK / 8)
+# endif
# endif
#endif /* LPC178x_40xx */
@@ -396,6 +398,7 @@ void lpc17_40_lowsetup(void)
putreg32(UART_FCR_FIFOEN | UART_FCR_RXTRIGGER_8,
CONSOLE_BASE + LPC17_40_UART_FCR_OFFSET);
+#ifndef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
/* Disable FDR (fractional divider),
* ignored by baudrate calculation => has to be disabled
*/
@@ -403,15 +406,20 @@ void lpc17_40_lowsetup(void)
putreg32((1 << UART_FDR_MULVAL_SHIFT) + (0 << UART_FDR_DIVADDVAL_SHIFT),
CONSOLE_BASE + LPC17_40_UART_FDR_OFFSET);
+#endif
/* Set up the LCR and set DLAB=1 */
putreg32(CONSOLE_LCR_VALUE | UART_LCR_DLAB,
CONSOLE_BASE + LPC17_40_UART_LCR_OFFSET);
+#ifdef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
+ up_setbaud(CONSOLE_BASE, CONSOLE_NUMERATOR, CONSOLE_BAUD);
+#else
/* Set the BAUD divisor */
putreg32(CONSOLE_DL >> 8, CONSOLE_BASE + LPC17_40_UART_DLM_OFFSET);
putreg32(CONSOLE_DL & 0xff, CONSOLE_BASE + LPC17_40_UART_DLL_OFFSET);
+#endif
/* Clear DLAB */
diff --git a/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.c b/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.c
index c0b6e0a..95b4c8e 100644
--- a/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.c
+++ b/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.c
@@ -547,20 +547,157 @@ static inline void up_enablebreaks(struct up_dev_s *priv, bool enable)
up_serialout(priv, LPC17_40_UART_LCR_OFFSET, lcr);
}
+#ifdef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
+/****************************************************************************
+ * Name: lpc17_40_setbaud
+ *
+ * Description:
+ * Configure the UART divisors to accomplish the desired BAUD given the
+ * UART base frequency.
+ *
+ * This computationally intensive algorithm is based on the same logic
+ * used in the NXP sample code.
+ *
+ ****************************************************************************/
+
+void up_setbaud(uintptr_t uartbase, uint32_t basefreq, uint32_t baud)
+{
+
+ uint32_t lcr; /* Line control register value */
+ uint32_t dl; /* Best DLM/DLL full value */
+ uint32_t mul; /* Best FDR MULVALL value */
+ uint32_t divadd; /* Best FDR DIVADDVAL value */
+ uint32_t best; /* Error value associated with best {dl, mul, divadd} */
+ uint32_t cdl; /* Candidate DLM/DLL full value */
+ uint32_t cmul; /* Candidate FDR MULVALL value */
+ uint32_t cdivadd; /* Candidate FDR DIVADDVAL value */
+ uint32_t errval; /* Error value associated with the candidate */
+
+ /* The UART buad is given by:
+ *
+ * Fbaud = Fbase * mul / (mul + divadd) / (16 * dl)
+ * dl = Fbase * mul / (mul + divadd) / Fbaud / 16
+ * = Fbase * mul / ((mul + divadd) * Fbaud * 16)
+ * = ((Fbase * mul) >> 4) / ((mul + divadd) * Fbaud)
+ *
+ * Where the value of MULVAL and DIVADDVAL comply with:
+ *
+ * 0 < mul < 16
+ * 0 <= divadd < mul
+ */
+
+ best = UINT32_MAX;
+ divadd = 0;
+ mul = 0;
+ dl = 0;
+
+ /* Try each multiplier value in the valid range */
+
+ for (cmul = 1 ; cmul < 16; cmul++)
+ {
+ /* Try each divider value in the valid range */
+
+ for (cdivadd = 0 ; cdivadd < cmul ; cdivadd++)
+ {
+ /* Candidate:
+ * dl = ((Fbase * mul) >> 4) / ((mul + cdivadd) * Fbaud)
+ * (dl << 32) = (Fbase << 28) * cmul / ((mul + cdivadd) * Fbaud)
+ */
+
+ uint64_t dl64 = ((uint64_t)basefreq << 28) * cmul /
+ ((cmul + cdivadd) * baud);
+
+ /* The lower 32-bits of this value is the error */
+
+ errval = (uint32_t)(dl64 & 0x00000000ffffffffull);
+
+ /* The upper 32-bits is the candidate DL value */
+
+ cdl = (uint32_t)(dl64 >> 32);
+
+ /* Round up */
+
+ if (errval > (1 << 31))
+ {
+ errval = -errval;
+ cdl++;
+ }
+
+ /* Check if the resulting candidate DL value is within range */
+
+ if (cdl < 1 || cdl > 65536)
+ {
+ /* No... try a different divadd value */
+
+ continue;
+ }
+
+ /* Is this the best combination that we have seen so far? */
+
+ if (errval < best)
+ {
+ /* Yes.. then the candidate is out best guess so far */
+
+ best = errval;
+ dl = cdl;
+ divadd = cdivadd;
+ mul = cmul;
+
+ /* If the new best guess is exact (within our precision), then
+ * we are finished.
+ */
+
+ if (best == 0)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ DEBUGASSERT(dl > 0);
+
+ /* Enter DLAB=1 */
+
+ lcr = getreg32(uartbase + LPC17_40_UART_LCR_OFFSET);
+ putreg32(lcr | UART_LCR_DLAB, uartbase + LPC17_40_UART_LCR_OFFSET);
+
+ /* Save the divider values */
+
+ putreg32(dl >> 8, uartbase + LPC17_40_UART_DLM_OFFSET);
+ putreg32(dl & 0xff, uartbase + LPC17_40_UART_DLL_OFFSET);
+
+ /* Clear DLAB */
+
+ putreg32(lcr & ~UART_LCR_DLAB, uartbase + LPC17_40_UART_LCR_OFFSET);
+
+ /* Then save the fractional divider values */
+
+ putreg32((mul << UART_FDR_MULVAL_SHIFT) | (divadd << UART_FDR_DIVADDVAL_SHIFT),
+ uartbase + LPC17_40_UART_FDR_OFFSET);
+}
+# ifdef LPC176x
+static inline uint32_t lpc17_40_uartcclkdiv(uint32_t baud)
+{
+/*
+ * If we're using the fractional divider, assume that the full PCLK speed
+ * will be acceptable.
+ */
+ return SYSCON_PCLKSEL_CCLK;
+}
+# endif
+#else
/************************************************************************************
* Name: lpc17_40_uartcclkdiv
*
* Description:
- * Select a CCLK divider to produce the UART PCLK. The stratey is to select the
+ * Select a CCLK divider to produce the UART PCLK. The strategy is to select the
* smallest divisor that results in an solution within range of the 16-bit
* DLM and DLL divisor:
*
* PCLK = CCLK / divisor
* BAUD = PCLK / (16 * DL)
*
- * Ignoring the fractional divider for now. (If you want to extend this driver
- * to support the fractional divider, see lpc43xx_uart.c. The LPC43xx uses
- * the same peripheral and that logic could easily leveraged here).
*
* For the LPC176x the PCLK is determined by the UART-specific divisor in
* PCLKSEL0 or PCLKSEL1:
@@ -576,7 +713,7 @@ static inline void up_enablebreaks(struct up_dev_s *priv, bool enable)
*
************************************************************************************/
-#ifdef LPC176x
+# ifdef LPC176x
static inline uint32_t lpc17_40_uartcclkdiv(uint32_t baud)
{
/* Ignoring the fractional divider, the BAUD is given by:
@@ -662,8 +799,8 @@ static inline uint32_t lpc17_40_uartcclkdiv(uint32_t baud)
return SYSCON_PCLKSEL_CCLK8;
}
}
-#endif /* LPC176x */
-
+# endif /* LPC176x */
+#endif /* CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER */
/************************************************************************************
* Name: lpc17_40_uart0config, uart1config, uart2config, and uart3config
*
@@ -734,7 +871,20 @@ static inline void lpc17_40_uart1config(void)
putreg32(regval, LPC17_40_SYSCON_PCLKSEL0);
#endif
- /* Step 3: Configure I/O pins */
+ /* Step 3: Configure RS-485 control register */
+#ifdef CONFIG_LPC17_40_UART1_RS485
+ regval = getreg32(LPC17_40_UART1_RS485CTRL);
+ regval |= UART_RS485CTRL_DCTRL;
+#if (CONFIG_LPC17_40_RS485_DIR_POLARITY == 1)
+ regval |= UART_RS485CTRL_OINV;
+#endif
+#ifdef CONFIG_LPC17_40_UART1_RS485_DIR_DTR
+ regval |= UART_RS485CTRL_SEL;
+#endif
+ putreg32(regval, LPC17_40_UART1_RS485CTRL);
+#endif
+
+ /* Step 4: Configure I/O pins */
lpc17_40_configgpio(GPIO_UART1_TXD);
lpc17_40_configgpio(GPIO_UART1_RXD);
@@ -748,6 +898,11 @@ static inline void lpc17_40_uart1config(void)
lpc17_40_configgpio(GPIO_UART1_RI);
#endif
#endif
+
+#ifdef CONFIG_LPC17_40_UART1_RS485
+ lpc17_40_configgpio(GPIO_UART1_RS485_DIR);
+#endif
+
leave_critical_section(flags);
};
#endif
@@ -821,13 +976,10 @@ static inline void lpc17_40_uart3config(void)
* BAUD = PCLK / (16 * DL), or
* DL = PCLK / BAUD / 16
*
- * Ignoring the fractional divider for now. (If you want to extend this driver
- * to support the fractional divider, see lpc43xx_uart.c. The LPC43xx uses
- * the same peripheral and that logic could easily leveraged here).
*
************************************************************************************/
-
-#ifdef LPC176x
+#ifndef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
+# ifdef LPC176x
static inline uint32_t lpc17_40_uartdl(uint32_t baud, uint8_t divcode)
{
uint32_t num;
@@ -859,6 +1011,7 @@ static inline uint32_t lpc17_40_uartdl(uint32_t baud)
{
return (uint32_t)BOARD_PCLK_FREQUENCY / (baud << 4);
}
+# endif
#endif
/****************************************************************************
@@ -878,7 +1031,9 @@ static int up_setup(struct uart_dev_s *dev)
{
#ifndef CONFIG_SUPPRESS_UART_CONFIG
struct up_dev_s *priv = (struct up_dev_s *)dev->priv;
+# ifndef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
uint16_t dl;
+# endif
uint32_t lcr;
/* Clear fifos */
@@ -920,27 +1075,31 @@ static int up_setup(struct uart_dev_s *dev)
lcr |= (UART_LCR_PE | UART_LCR_PS_EVEN);
}
+#ifndef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
/* Disable FDR (fractional divider),
* ignored by baudrate calculation => has to be disabled
*/
up_serialout(priv, LPC17_40_UART_FDR_OFFSET,
(1 << UART_FDR_MULVAL_SHIFT) + (0 << UART_FDR_DIVADDVAL_SHIFT));
+#endif
/* Enter DLAB=1 */
up_serialout(priv, LPC17_40_UART_LCR_OFFSET, (lcr | UART_LCR_DLAB));
/* Set the BAUD divisor */
-
-#ifdef LPC176x
- dl = lpc17_40_uartdl(priv->baud, priv->cclkdiv);
+#ifdef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
+ up_setbaud(priv->uartbase, LPC17_40_CCLK / priv->cclkdiv, priv->baud);
#else
+# ifdef LPC176x
+ dl = lpc17_40_uartdl(priv->baud, priv->cclkdiv);
+# else
dl = lpc17_40_uartdl(priv->baud);
-#endif
+# endif
up_serialout(priv, LPC17_40_UART_DLM_OFFSET, dl >> 8);
up_serialout(priv, LPC17_40_UART_DLL_OFFSET, dl & 0xff);
-
+#endif
/* Clear DLAB */
up_serialout(priv, LPC17_40_UART_LCR_OFFSET, lcr);
@@ -1212,8 +1371,10 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg)
case TCSETS:
{
struct termios *termiosp = (struct termios *)arg;
+# ifndef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
uint32_t lcr; /* Holds current values of line control register */
uint16_t dl; /* Divisor latch */
+# endif
if (!termiosp)
{
@@ -1233,10 +1394,12 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg)
/* TODO: Re-calculate the optimal CCLK divisor for the new baud and
* and reset the divider in the CLKSEL0/1 register.
*/
-
-#if 0 /* ifdef LPC176x */
+# ifdef CONFIG_LPC17_40_UART_USE_FRACTIONAL_DIVIDER
+ up_setbaud(priv->uartbase, LPC17_40_CCLK / priv->cclkdiv, priv->baud);
+# else
+# if 0 /* ifdef LPC176x */
priv->cclkdiv = lpc17_40_uartcclkdiv(priv->baud);
-#endif
+# endif
/* DLAB open latch */
/* REVISIT: Shouldn't we just call up_setup() to do all of the following? */
@@ -1245,17 +1408,18 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg)
/* Set the BAUD divisor */
-#ifdef LPC176x
+# ifdef LPC176x
dl = lpc17_40_uartdl(priv->baud, priv->cclkdiv);
-#else
+# else
dl = lpc17_40_uartdl(priv->baud);
-#endif
+# endif
up_serialout(priv, LPC17_40_UART_DLM_OFFSET, dl >> 8);
up_serialout(priv, LPC17_40_UART_DLL_OFFSET, dl & 0xff);
/* Clear DLAB */
up_serialout(priv, LPC17_40_UART_LCR_OFFSET, lcr);
+# endif
}
break;
#endif
@@ -1421,7 +1585,7 @@ static bool up_txempty(struct uart_dev_s *dev)
* very early in the boot sequence.
*
****************************************************************************/
-
+#ifdef USE_EARLYSERIALINIT
void up_earlyserialinit(void)
{
/* Configure all UARTs (except the CONSOLE UART) and disable interrupts */
@@ -1475,6 +1639,8 @@ void up_earlyserialinit(void)
#endif
}
+#endif
+
/****************************************************************************
* Name: up_serialinit
*
diff --git a/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.h b/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.h
index 624deaa..d4a9fd9 100644
--- a/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.h
+++ b/arch/arm/src/lpc17xx_40xx/lpc17_40_serial.h
@@ -129,5 +129,6 @@
/************************************************************************************
* Public Functions
************************************************************************************/
+void up_setbaud(uintptr_t uartbase, uint32_t basefreq, uint32_t baud);
#endif /* __ARCH_ARM_SRC_LPC17XX_40XX_LPC17_40_SERIAL_H */
diff --git a/arch/arm/src/lpc43xx/lpc43_uart.c b/arch/arm/src/lpc43xx/lpc43_uart.c
index 766edc9..69b3171 100644
--- a/arch/arm/src/lpc43xx/lpc43_uart.c
+++ b/arch/arm/src/lpc43xx/lpc43_uart.c
@@ -619,7 +619,7 @@ void lpc43_setbaud(uintptr_t uartbase, uint32_t basefreq, uint32_t baud)
mul = 0;
dl = 0;
- /* Try each mulitplier value in the valid range */
+ /* Try each multiplier value in the valid range */
for (cmul = 1 ; cmul < 16; cmul++)
{
@@ -690,7 +690,7 @@ void lpc43_setbaud(uintptr_t uartbase, uint32_t basefreq, uint32_t baud)
lcr = getreg32(uartbase + LPC43_UART_LCR_OFFSET);
putreg32(lcr | UART_LCR_DLAB, uartbase + LPC43_UART_LCR_OFFSET);
- /* Save then divider values */
+ /* Save the divider values */
putreg32(dl >> 8, uartbase + LPC43_UART_DLM_OFFSET);
putreg32(dl & 0xff, uartbase + LPC43_UART_DLL_OFFSET);