Allow configuration of UART as RS485 or IrDA (IrDA not tested) Include simple test...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 22 Apr 2014 17:42:38 +0000 (19:42 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:04 +0000 (17:03 +0100)
drivers/serial.c
include/core/lpc_regs_12xx.h
include/drivers/serial.h

index 4ce6eb0..02527db 100644 (file)
@@ -31,6 +31,8 @@
 #include "core/system.h"
 #include "core/pio.h"
 #include "lib/string.h"
+#include "lib/utils.h"
+#include "drivers/serial.h"
 
 #define SERIAL_OUT_BUFF_SIZE 64
 struct uart_device
@@ -39,6 +41,8 @@ struct uart_device
        struct lpc_uart* regs;
        uint32_t baudrate;
        uint32_t config;
+       uint32_t capabilities;
+       uint32_t current_mode;
 
        /* Output buffer */
        volatile char out_buff[SERIAL_OUT_BUFF_SIZE];
@@ -47,7 +51,7 @@ struct uart_device
        volatile uint32_t out_length; /* actual position to add in out buffer */
 
        /* Input */
-       void (*rx_callback)(char); /* Possible RX callback */
+       void (*rx_callback)(uint8_t); /* Possible RX callback */
 };
 
 #define NUM_UARTS 2
@@ -61,6 +65,8 @@ static struct uart_device uarts[NUM_UARTS] = {
                .sending = 0,
                .out_lock = 0,
                .rx_callback = 0,
+               .capabilities = (SERIAL_CAP_UART | SERIAL_CAP_RS485),
+               .current_mode = SERIAL_MODE_UART,
        },
        {
                .num = 1,
@@ -71,6 +77,8 @@ static struct uart_device uarts[NUM_UARTS] = {
                .sending = 0,
                .out_lock = 0,
                .rx_callback = 0,
+               .capabilities = (SERIAL_CAP_UART | SERIAL_CAP_IRDA),
+               .current_mode = SERIAL_MODE_UART,
        },
 };
 
@@ -127,6 +135,7 @@ static void uart_start_sending(uint32_t uart_num)
 }
 
 
+
 /***************************************************************************** */
 /*    Serial Write
  *
@@ -172,7 +181,6 @@ int serial_write(uint32_t uart_num, const char *buf, uint32_t length)
 
 
 
-
 /***************************************************************************** */
 /*   UART Setup : private part : Clocks, Pins, Power and Mode   */
 
@@ -206,7 +214,7 @@ static void uart_clk_off(uint32_t uart_num)
        sys_ctrl->uart_clk_div[uart_num] = 0;
 }
 
-static uint32_t uart_mode_setup(uint32_t uart_num)
+static uint32_t uart_setup(uint32_t uart_num)
 {
        struct lpc_uart* uart = uarts[uart_num].regs; /* Get the right registers */
        uint32_t status = 0;
@@ -270,8 +278,68 @@ void uart_clk_update(void)
        }
 }
 
+/* Change uart mode to RS485
+ * return -ENODEV when the device does not support RS485 mode.
+ */
+int uart_set_mode_rs485(uint32_t uart_num, uint32_t control, uint8_t addr, uint8_t delay)
+{
+       struct uart_device* uart = NULL;
+       struct lpc_uart* uart_regs = NULL;
+       if (uart_num >= NUM_UARTS)
+               return -EINVAL;
+       uart = &uarts[uart_num];
+       uart_regs = uart->regs; /* Get the right registers */
+
+       if (!(uart->capabilities & SERIAL_CAP_RS485)) {
+               return -ENODEV;
+       }
+       /* Disable all other modes */
+       uart_regs->irda_ctrl = 0;
+
+       /* Set current mode */
+       uart->current_mode = SERIAL_MODE_RS485;
+       uart_regs->RS485_ctrl = (control & 0xFF);
+       uart_regs->RS485_addr_match = LPC_RS485_ADDR(addr);
+       uart_regs->RS485_dir_ctrl_delay = LPC_RS485_DIR_DELAY(delay);
+       return 0;
+}
+
+/* Change uart mode to IRDA
+ * pulse_width is the number of clock cycles for a pulse. Should dbe a power of 2.
+ * return -ENODEV when the device does not support IrDA mode.
+ */
+int uart_set_mode_irda(uint32_t uart_num, uint32_t control, uint16_t pulse_width)
+{
+       struct uart_device* uart = NULL;
+       struct lpc_uart* uart_regs = NULL;
+       uint8_t pulse_div = 0;
+
+       if (uart_num >= NUM_UARTS)
+               return -EINVAL;
+       uart = &uarts[uart_num];
+       uart_regs = uart->regs; /* Get the right registers */
+
+       if (!(uart->capabilities & SERIAL_CAP_IRDA)) {
+               return -ENODEV;
+       }
+       /* Disable all other modes */
+       uart_regs->RS485_ctrl = 0;
+
+       /* Set current mode */
+       uart->current_mode = SERIAL_MODE_IRDA;
+       /* Setup IrDA */
+       if (pulse_width < 2) {
+               pulse_div = 0;
+       } else {
+               pulse_div = (31 - clz(pulse_width & 0x1FF));
+       }
+       uart_regs->irda_ctrl = control | LPC_IRDA_PULSEDIV(pulse_div);
+       return 0;
+}
+
+
 /* Do we need to allow setting of other parameters ? (Other than 8n1) */
-int uart_on(uint32_t uart_num, uint32_t baudrate, void (*rx_callback)(char))
+int uart_on(uint32_t uart_num, uint32_t baudrate, void (*rx_callback)(uint8_t))
 {
        struct uart_def* uart = NULL;
        uint32_t status = 0;
@@ -291,7 +359,7 @@ int uart_on(uint32_t uart_num, uint32_t baudrate, void (*rx_callback)(char))
        /* Setup clock acording to baudrate */
        uart_clk_on(uart_num, baudrate);
        /* Setup mode, fifo, ... */
-       status = uart_mode_setup(uart_num);
+       status = uart_setup(uart_num);
        /* Enable interrupts back on */
        NVIC_EnableIRQ( uart->irq );
 
index 5070a5c..dfddff8 100644 (file)
@@ -491,7 +491,7 @@ struct lpc_uart
        volatile const uint32_t modem_status;  /* 0x018 : Modem status Register (R/ ) */
        volatile uint32_t scratch_pad;  /* 0x01C : Scratch Pad Register (R/W) */
        volatile uint32_t auto_baud_ctrl;  /* 0x020 : Auto-baud Control Register (R/W) */
-       uint32_t reserved_0;
+       volatile uint32_t irda_ctrl;       /* 0x024 : UART IrDA Control Register (R/W) */
        volatile uint32_t fractional_div;  /* 0x028 : Fractional Divider Register (R/W) */
        uint32_t reserved_1;
        volatile uint32_t transmit_enable;  /* 0x030 : Transmit Enable Register (R/W) */
@@ -531,6 +531,19 @@ struct lpc_uart
 #define LPC_UART_INT_RX        (0x2 << 1)
 #define LPC_UART_INT_RX_STATUS (0x3 << 1)
 #define LPC_UART_INT_TIMEOUT   (0x6 << 1)
+/* RS485 Control */
+#define LPC_RS485_ENABLE       (0x1 << 0)
+#define LPC_RS485_RX_DIS       (0x1 << 1)
+#define LPC_RS485_AUTO_ADDR_EN (0x1 << 2)
+#define LPC_RS485_DIR_PIN_RTS  (0x0 << 3)
+#define LPC_RS485_DIR_PIN_DTR  (0x1 << 3)
+#define LPC_RS485_AUTO_DIR_EN  (0x1 << 4)
+#define LPC_RS485_DIR_CTRL_INV (0x1 << 5)
+/* RS485 */
+#define LPC_RS485_ADDR(x)  ((x) & 0xFF)
+#define LPC_RS485_DIR_DELAY(x)  ((x) & 0xFF)
+/* IrDA */
+#define LPC_IRDA_PULSEDIV(x)  (((x) & 0x07) << 3)
 
 
 /***************************************************************************** */
index df1fcbd..dede5f0 100644 (file)
 #include <stdint.h>
 
 
+#define SERIAL_CAP_UART   (1 << 0)
+#define SERIAL_CAP_RS485  (1 << 1)
+#define SERIAL_CAP_IRDA   (1 << 2)
+
+#define SERIAL_MODE_UART  SERIAL_CAP_UART
+#define SERIAL_MODE_RS485 SERIAL_CAP_RS485
+#define SERIAL_MODE_IRDA  SERIAL_CAP_IRDA
+
 /***************************************************************************** */
 /*    Serial Write
  *
@@ -45,12 +53,21 @@ int serial_write(uint32_t uart_num, const char *buf, uint32_t length);
 /***************************************************************************** */
 /*   Public access to UART setup   */
 
+/* Change uart mode to RS485
+ * return -ENODEV when the device does not support RS485 mode.
+ */
+int uart_set_mode_rs485(uint32_t uart_num, uint32_t control, uint8_t addr, uint8_t delay);
+
+/* Change uart mode to IRDA
+ * return -ENODEV when the device does not support IrDA mode.
+ */
+int uart_set_mode_irda(uint32_t uart_num, uint32_t control, uint16_t pulse_width);
 
 /* Allow any change to the main clock to be forwarded to us */
 void uart_clk_update(void);
 
 /* Do we need to allow setting of other parameters ? (Other than 8n1) */
-int uart_on(uint32_t uart_num, uint32_t baudrate, void (*rx_callback)(char));
+int uart_on(uint32_t uart_num, uint32_t baudrate, void (*rx_callback)(uint8_t));
 
 void uart_off(uint32_t uart_num);