From 70cc3a8f6813f51b3cbac7c967b00f5e3630d970 Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Tue, 22 Apr 2014 19:42:38 +0200 Subject: [PATCH] Allow configuration of UART as RS485 or IrDA (IrDA not tested) Include simple test to forward characters received on RS485 to the other UART. --- drivers/serial.c | 78 +++++++++++++++++++++++++++++++++--- include/core/lpc_regs_12xx.h | 15 ++++++- include/drivers/serial.h | 19 ++++++++- 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/drivers/serial.c b/drivers/serial.c index 4ce6eb0..02527db 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -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 ); diff --git a/include/core/lpc_regs_12xx.h b/include/core/lpc_regs_12xx.h index 5070a5c..dfddff8 100644 --- a/include/core/lpc_regs_12xx.h +++ b/include/core/lpc_regs_12xx.h @@ -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) /***************************************************************************** */ diff --git a/include/drivers/serial.h b/include/drivers/serial.h index df1fcbd..dede5f0 100644 --- a/include/drivers/serial.h +++ b/include/drivers/serial.h @@ -25,6 +25,14 @@ #include +#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); -- 2.43.0