Added RTS/CTS flow control

Currently implemented only for LPC1768. On this platform, when hardware
flow control is not directly supported, it will be emulated.
Also added "not_implemented.c" as a placeholder for various HAL functions
that might not be implemented on all platforms (in this particular case,
serial_set_flow_control). These are weak implementations that default to a
"not implemented" error message.
pull/135/head
Bogdan Marinescu 2013-12-03 10:21:41 +02:00
parent 72a9529287
commit fbeb52d613
8 changed files with 185 additions and 20 deletions

View File

@ -51,6 +51,13 @@ public:
TxIrq
};
enum Flow {
Disabled = 0,
RTS,
CTS,
RTSCTS
};
/** Set the transmission format used by the serial port
*
* @param bits The number of bits in a word (5-8; default = 8)
@ -99,6 +106,14 @@ public:
/** Generate a break condition on the serial line
*/
void send_break();
/** Set the flow control type on the serial port
*
* @param type the flow control type (Disabled, RTS, CTS, RTSCTS)
* @param flow1 the first flow control pin (RTS for RTS or RTSCTS, CTS for CTS)
* @param flow2 the second flow control pin (CTS for RTSCTS)
*/
void set_flow_control(Flow type, PinName flow1=NC, PinName flow2=NC);
static void _irq_handler(uint32_t id, SerialIrq irq_type);

View File

@ -81,6 +81,26 @@ void SerialBase::send_break() {
serial_break_clear(&_serial);
}
void SerialBase::set_flow_control(Flow type, PinName flow1, PinName flow2) {
FlowControl flow_type = (FlowControl)type;
switch(type) {
case RTS:
serial_set_flow_control(&_serial, flow_type, flow1, NC);
break;
case CTS:
serial_set_flow_control(&_serial, flow_type, NC, flow1);
break;
case RTSCTS:
serial_set_flow_control(&_serial, flow_type, flow1, flow2);
break;
default:
break;
}
}
} // namespace mbed
#endif

View File

@ -0,0 +1,28 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Default versions of various HAL functions that might not be implemented for some platforms.
#include "toolchain.h"
#include "serial_api.h"
#include "error.h"
WEAK void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow);
WEAK void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow) {
if (FlowControlNone != type)
error("hardware flow control not implemented on this platform");
}

View File

@ -44,17 +44,22 @@ uint32_t pinmap_merge(uint32_t a, uint32_t b) {
return (uint32_t)NC;
}
uint32_t pinmap_peripheral(PinName pin, const PinMap* map) {
if (pin == (PinName)NC)
return (uint32_t)NC;
uint32_t pinmap_find_peripheral(PinName pin, const PinMap* map) {
while (map->pin != NC) {
if (map->pin == pin)
return map->peripheral;
map++;
}
// no mapping available
error("pinmap not found for peripheral");
return (uint32_t)NC;
}
uint32_t pinmap_peripheral(PinName pin, const PinMap* map) {
uint32_t peripheral = (uint32_t)NC;
if (pin == (PinName)NC)
return (uint32_t)NC;
peripheral = pinmap_find_peripheral(pin, map);
if ((uint32_t)NC == peripheral) // no mapping available
error("pinmap not found for peripheral");
return peripheral;
}

View File

@ -34,6 +34,7 @@ void pin_mode (PinName pin, PinMode mode);
uint32_t pinmap_peripheral(PinName pin, const PinMap* map);
uint32_t pinmap_merge (uint32_t a, uint32_t b);
void pinmap_pinout (PinName pin, const PinMap *map);
uint32_t pinmap_find_peripheral(PinName pin, const PinMap* map);
#ifdef __cplusplus
}

View File

@ -37,6 +37,13 @@ typedef enum {
TxIrq
} SerialIrq;
typedef enum {
FlowControlNone,
FlowControlRTS,
FlowControlCTS,
FlowControlRTSCTS
} FlowControl;
typedef void (*uart_irq_handler)(uint32_t id, SerialIrq event);
typedef struct serial_s serial_t;
@ -60,6 +67,8 @@ void serial_break_clear(serial_t *obj);
void serial_pinout_tx(PinName tx);
void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow);
#ifdef __cplusplus
}
#endif

View File

@ -20,6 +20,7 @@
#include "PortNames.h"
#include "PeripheralNames.h"
#include "PinNames.h"
#include "gpio_object.h"
#ifdef __cplusplus
extern "C" {
@ -47,7 +48,6 @@ struct pwmout_s {
struct serial_s {
LPC_UART_TypeDef *uart;
int index;
uint8_t count;
};
struct analogin_s {
@ -71,8 +71,6 @@ struct spi_s {
LPC_SSP_TypeDef *spi;
};
#include "gpio_object.h"
#ifdef __cplusplus
}
#endif

View File

@ -21,6 +21,7 @@
#include "cmsis.h"
#include "pinmap.h"
#include "error.h"
#include "gpio_api.h"
/******************************************************************************
* INITIALIZATION
@ -51,12 +52,35 @@ static const PinMap PinMap_UART_RX[] = {
{NC , NC , 0}
};
static const PinMap PinMap_UART_RTS[] = {
{P0_22, UART_1, 1},
{P2_7, UART_1, 2},
{NC, NC, 0}
};
static const PinMap PinMap_UART_CTS[] = {
{P0_17, UART_1, 1},
{P2_2, UART_1, 2},
{NC, NC, 0}
};
#define UART_MCR_RTSEN_MASK (1 << 6)
#define UART_MCR_CTSEN_MASK (1 << 7)
#define UART_MCR_FLOWCTRL_MASK (UART_MCR_RTSEN_MASK | UART_MCR_CTSEN_MASK)
static uint32_t serial_irq_ids[UART_NUM] = {0};
static uart_irq_handler irq_handler;
int stdio_uart_inited = 0;
serial_t stdio_uart;
struct serial_global_data_s {
gpio_t sw_rts, sw_cts;
uint8_t count, initialized, irq_set_flow, irq_set_api;
};
static struct serial_global_data_s uart_data[UART_NUM];
void serial_init(serial_t *obj, PinName tx, PinName rx) {
int is_stdio_uart = 0;
@ -106,7 +130,11 @@ void serial_init(serial_t *obj, PinName tx, PinName rx) {
case UART_2: obj->index = 2; break;
case UART_3: obj->index = 3; break;
}
obj->count = 0;
if (!uart_data[obj->index].initialized) {
uart_data[obj->index].sw_rts.pin = NC;
uart_data[obj->index].sw_cts.pin = NC;
uart_data[obj->index].initialized = 1;
}
is_stdio_uart = (uart == STDIO_UART) ? (1) : (0);
@ -234,7 +262,9 @@ static inline void uart_irq(uint32_t iir, uint32_t index) {
case 2: irq_type = RxIrq; break;
default: return;
}
if ((RxIrq == irq_type) && (uart_data[index].sw_rts.pin != NC))
gpio_write(&uart_data[index].sw_rts, 1);
if (serial_irq_ids[index] != 0)
irq_handler(serial_irq_ids[index], irq_type);
}
@ -249,7 +279,7 @@ void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) {
serial_irq_ids[obj->index] = id;
}
void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
static void serial_irq_set_internal(serial_t *obj, SerialIrq irq, uint32_t enable) {
IRQn_Type irq_n = (IRQn_Type)0;
uint32_t vector = 0;
switch ((int)obj->uart) {
@ -263,7 +293,7 @@ void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
obj->uart->IER |= 1 << irq;
NVIC_SetVector(irq_n, vector);
NVIC_EnableIRQ(irq_n);
} else { // disable
} else if (uart_data[obj->index].irq_set_api + uart_data[obj->index].irq_set_flow == 0) { // disable
int all_disabled = 0;
SerialIrq other_irq = (irq == RxIrq) ? (TxIrq) : (RxIrq);
obj->uart->IER &= ~(1 << irq);
@ -273,18 +303,30 @@ void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
}
}
void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
uart_data[obj->index].irq_set_api = enable;
serial_irq_set_internal(obj, irq, enable);
}
static void serial_flow_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
uart_data[obj->index].irq_set_flow = enable;
serial_irq_set_internal(obj, irq, enable);
}
/******************************************************************************
* READ/WRITE
******************************************************************************/
int serial_getc(serial_t *obj) {
while (!serial_readable(obj));
if (NC != uart_data[obj->index].sw_rts.pin)
gpio_write(&uart_data[obj->index].sw_rts, 0);
return obj->uart->RBR;
}
void serial_putc(serial_t *obj, int c) {
while (!serial_writable(obj));
obj->uart->THR = c;
obj->count++;
uart_data[obj->index].count++;
}
int serial_readable(serial_t *obj) {
@ -293,11 +335,14 @@ int serial_readable(serial_t *obj) {
int serial_writable(serial_t *obj) {
int isWritable = 1;
if (obj->uart->LSR & 0x20)
obj->count = 0;
else if (obj->count >= 16)
isWritable = 0;
if (NC != uart_data[obj->index].sw_cts.pin)
isWritable = gpio_read(&uart_data[obj->index].sw_cts) == 0;
if (isWritable) {
if (obj->uart->LSR & 0x20)
uart_data[obj->index].count = 0;
else if (uart_data[obj->index].count >= 16)
isWritable = 0;
}
return isWritable;
}
@ -320,3 +365,47 @@ void serial_break_clear(serial_t *obj) {
obj->uart->LCR &= ~(1 << 6);
}
void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow) {
// Only UART1 has hardware flow control on LPC176x
LPC_UART1_TypeDef *uart1 = (uint32_t)obj->uart == (uint32_t)LPC_UART1 ? LPC_UART1 : NULL;
int index = obj->index;
// First, disable flow control completely
if (uart1)
uart1->MCR = uart1->MCR & ~UART_MCR_FLOWCTRL_MASK;
serial_flow_irq_set(obj, RxIrq, 0);
uart_data[index].sw_rts.pin = uart_data[index].sw_cts.pin = NC;
if (FlowControlNone == type)
return;
// Check type(s) of flow control to use
UARTName uart_rts = (UARTName)pinmap_find_peripheral(rxflow, PinMap_UART_RTS);
UARTName uart_cts = (UARTName)pinmap_find_peripheral(txflow, PinMap_UART_CTS);
if (((FlowControlCTS == type) || (FlowControlRTSCTS == type)) && (NC != txflow)) {
// Can this be enabled in hardware?
if ((UART_1 == uart_cts) && (NULL != uart1)) {
// Enable auto-CTS mode
uart1->MCR |= UART_MCR_CTSEN_MASK;
} else {
// Can't enable in hardware, use software emulation
gpio_init(&uart_data[index].sw_cts, txflow, PIN_INPUT);
}
}
if (((FlowControlRTS == type) || (FlowControlRTSCTS == type)) && (NC != rxflow)) {
// Enable FIFOs, trigger level of 1 char on RX FIFO
obj->uart->FCR = 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
| 1 << 1 // Rx Fifo Reset
| 1 << 2 // Tx Fifo Reset
| 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
// Can this be enabled in hardware?
if ((UART_1 == uart_rts) && (NULL != uart1)) {
// Enable auto-RTS mode
uart1->MCR |= UART_MCR_RTSEN_MASK;
} else { // can't enable in hardware, use software emulation
gpio_init(&uart_data[index].sw_rts, rxflow, PIN_OUTPUT);
gpio_write(&uart_data[index].sw_rts, 0);
// Enable RX interrupt
serial_flow_irq_set(obj, RxIrq, 1);
}
}
}