1 /* mbed Microcontroller Library
2 * Copyright (c) 2006-2013 ARM Limited
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 // math.h required for floating point operations for baud rate calculation
17 #include "mbed_assert.h"
22 #include "serial_api.h"
26 /******************************************************************************
28 ******************************************************************************/
31 static const PinMap PinMap_UART_TX
[] = {
38 static const PinMap PinMap_UART_RX
[] = {
45 static uint32_t serial_irq_ids
[UART_NUM
] = {0};
46 static uart_irq_handler irq_handler
;
48 int stdio_uart_inited
= 0;
51 void serial_init(serial_t
*obj
, PinName tx
, PinName rx
) {
52 int is_stdio_uart
= 0;
54 // determine the UART to use
55 UARTName uart_tx
= (UARTName
)pinmap_peripheral(tx
, PinMap_UART_TX
);
56 UARTName uart_rx
= (UARTName
)pinmap_peripheral(rx
, PinMap_UART_RX
);
57 UARTName uart
= (UARTName
)pinmap_merge(uart_tx
, uart_rx
);
58 MBED_ASSERT((int)uart
!= NC
);
60 obj
->uart
= (LPC_USART_Type
*)uart
;
61 LPC_SYSCON
->SYSAHBCLKCTRL
|= (1<<12);
63 // [TODO] Consider more elegant approach
64 // disconnect USBTX/RX mapping mux, for case when switching ports
65 //pin_function(USBTX, 0);
66 //pin_function(USBRX, 0);
68 // enable fifos and default rx trigger level
69 obj
->uart
->FCR
= 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
70 | 0 << 1 // Rx Fifo Reset
71 | 0 << 2 // Tx Fifo Reset
72 | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
75 obj
->uart
->IER
= 0 << 0 // Rx Data available irq enable
76 | 0 << 1 // Tx Fifo empty irq enable
77 | 0 << 2; // Rx Line Status irq enable
79 // set default baud rate and format
80 serial_baud (obj
, 9600);
81 serial_format(obj
, 8, ParityNone
, 1);
83 // pinout the chosen uart
84 pinmap_pinout(tx
, PinMap_UART_TX
);
85 pinmap_pinout(rx
, PinMap_UART_RX
);
87 // set rx/tx pins in PullUp mode
96 case UART_0
: obj
->index
= 0; break;
99 is_stdio_uart
= (uart
== STDIO_UART
) ? (1) : (0);
102 stdio_uart_inited
= 1;
103 memcpy(&stdio_uart
, obj
, sizeof(serial_t
));
107 void serial_free(serial_t
*obj
) {
108 serial_irq_ids
[obj
->index
] = 0;
112 // set the baud rate, taking in to account the current SystemFrequency
113 void serial_baud(serial_t
*obj
, int baudrate
) {
114 LPC_SYSCON
->UARTCLKDIV
= 0x1;
115 uint32_t PCLK
= SystemCoreClock
;
116 // First we check to see if the basic divide with no DivAddVal/MulVal
117 // ratio gives us an integer result. If it does, we set DivAddVal = 0,
118 // MulVal = 1. Otherwise, we search the valid ratio value range to find
119 // the closest match. This could be more elegant, using search methods
120 // and/or lookup tables, but the brute force method is not that much
121 // slower, and is more maintainable.
122 uint16_t DL
= PCLK
/ (16 * baudrate
);
124 uint8_t DivAddVal
= 0;
129 if ((PCLK
% (16 * baudrate
)) != 0) { // Checking for zero remainder
130 int err_best
= baudrate
, b
;
131 for (mv
= 1; mv
< 16 && !hit
; mv
++)
133 for (dav
= 0; dav
< mv
; dav
++)
135 // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
136 // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
137 // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
138 // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
139 // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
141 if ((mv
* PCLK
* 2) & 0x80000000) // 1 bit headroom
142 dlv
= ((((2 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 16) + 1) / 2;
143 else // 2 bits headroom, use more precision
144 dlv
= ((((4 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 32) + 1) / 2;
146 // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
150 // datasheet says if dav > 0 then DL must be >= 2
151 if ((dav
> 0) && (dlv
< 2))
154 // integer rearrangement of the baudrate equation (with rounding)
155 b
= ((PCLK
* mv
/ (dlv
* (dav
+ mv
) * 8)) + 1) / 2;
157 // check to see how we went
158 b
= abs(b
- baudrate
);
177 // set LCR[DLAB] to enable writing to divider registers
178 obj
->uart
->LCR
|= (1 << 7);
180 // set divider values
181 obj
->uart
->DLM
= (DL
>> 8) & 0xFF;
182 obj
->uart
->DLL
= (DL
>> 0) & 0xFF;
183 obj
->uart
->FDR
= (uint32_t) DivAddVal
<< 0
184 | (uint32_t) MulVal
<< 4;
187 obj
->uart
->LCR
&= ~(1 << 7);
190 void serial_format(serial_t
*obj
, int data_bits
, SerialParity parity
, int stop_bits
) {
191 MBED_ASSERT((stop_bits
== 1) || (stop_bits
== 2)); // 0: 1 stop bits, 1: 2 stop bits
192 MBED_ASSERT((data_bits
> 4) && (data_bits
< 9)); // 0: 5 data bits ... 3: 8 data bits
193 MBED_ASSERT((parity
== ParityNone
) || (parity
== ParityOdd
) || (parity
== ParityEven
) ||
194 (parity
== ParityForced1
) || (parity
== ParityForced0
));
199 int parity_enable
, parity_select
;
201 case ParityNone
: parity_enable
= 0; parity_select
= 0; break;
202 case ParityOdd
: parity_enable
= 1; parity_select
= 0; break;
203 case ParityEven
: parity_enable
= 1; parity_select
= 1; break;
204 case ParityForced1
: parity_enable
= 1; parity_select
= 2; break;
205 case ParityForced0
: parity_enable
= 1; parity_select
= 3; break;
210 obj
->uart
->LCR
= data_bits
<< 0
213 | parity_select
<< 4;
216 /******************************************************************************
217 * INTERRUPTS HANDLING
218 ******************************************************************************/
219 static inline void uart_irq(uint32_t iir
, uint32_t index
) {
220 // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
223 case 1: irq_type
= TxIrq
; break;
224 case 2: irq_type
= RxIrq
; break;
228 if (serial_irq_ids
[index
] != 0)
229 irq_handler(serial_irq_ids
[index
], irq_type
);
232 void uart0_irq() {uart_irq((LPC_USART
->IIR
>> 1) & 0x7, 0);}
234 void serial_irq_handler(serial_t
*obj
, uart_irq_handler handler
, uint32_t id
) {
235 irq_handler
= handler
;
236 serial_irq_ids
[obj
->index
] = id
;
239 void serial_irq_set(serial_t
*obj
, SerialIrq irq
, uint32_t enable
) {
240 IRQn_Type irq_n
= (IRQn_Type
)0;
242 switch ((int)obj
->uart
) {
243 case UART_0
: irq_n
=USART_IRQn
; vector
= (uint32_t)&uart0_irq
; break;
247 obj
->uart
->IER
|= 1 << irq
;
248 NVIC_SetVector(irq_n
, vector
);
249 NVIC_EnableIRQ(irq_n
);
251 int all_disabled
= 0;
252 SerialIrq other_irq
= (irq
== RxIrq
) ? (TxIrq
) : (RxIrq
);
254 obj
->uart
->IER
&= ~(1 << irq
);
255 all_disabled
= (obj
->uart
->IER
& (1 << other_irq
)) == 0;
258 NVIC_DisableIRQ(irq_n
);
262 /******************************************************************************
264 ******************************************************************************/
265 int serial_getc(serial_t
*obj
) {
266 while (!serial_readable(obj
));
267 return obj
->uart
->RBR
;
270 void serial_putc(serial_t
*obj
, int c
) {
271 while (!serial_writable(obj
));
275 int serial_readable(serial_t
*obj
) {
276 return obj
->uart
->LSR
& 0x01;
279 int serial_writable(serial_t
*obj
) {
280 return obj
->uart
->LSR
& 0x20;
283 void serial_clear(serial_t
*obj
) {
284 obj
->uart
->FCR
= 1 << 1 // rx FIFO reset
285 | 1 << 2 // tx FIFO reset
286 | 0 << 6; // interrupt depth
289 void serial_pinout_tx(PinName tx
) {
290 pinmap_pinout(tx
, PinMap_UART_TX
);
293 void serial_break_set(serial_t
*obj
) {
294 obj
->uart
->LCR
|= (1 << 6);
297 void serial_break_clear(serial_t
*obj
) {
298 obj
->uart
->LCR
&= ~(1 << 6);