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
21 #include "serial_api.h"
24 #include "mbed_error.h"
26 /******************************************************************************
28 ******************************************************************************/
29 static const PinMap PinMap_UART_TX
[] = {
38 static const PinMap PinMap_UART_RX
[] = {
49 static uint32_t serial_irq_ids
[UART_NUM
] = {0};
50 static uart_irq_handler irq_handler
;
52 int stdio_uart_inited
= 0;
55 void serial_init(serial_t
*obj
, PinName tx
, PinName rx
) {
56 int is_stdio_uart
= 0;
58 // determine the UART to use
59 UARTName uart_tx
= (UARTName
)pinmap_peripheral(tx
, PinMap_UART_TX
);
60 UARTName uart_rx
= (UARTName
)pinmap_peripheral(rx
, PinMap_UART_RX
);
61 UARTName uart
= (UARTName
)pinmap_merge(uart_tx
, uart_rx
);
62 MBED_ASSERT((int)uart
!= NC
);
64 obj
->uart
= (LPC_UART_TypeDef
*)uart
;
67 case UART_0
: LPC_SC
->PCONP
|= 1 << 3; break;
68 case UART_1
: LPC_SC
->PCONP
|= 1 << 4; break;
69 case UART_2
: LPC_SC
->PCONP
|= 1 << 24; break;
70 case UART_3
: LPC_SC
->PCONP
|= 1 << 25; break;
71 case UART_4
: LPC_SC
->PCONP
|= 1 << 8; break;
74 // enable fifos and default rx trigger level
75 obj
->uart
->FCR
= 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
76 | 0 << 1 // Rx Fifo Reset
77 | 0 << 2 // Tx Fifo Reset
78 | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
81 obj
->uart
->IER
= 0 << 0 // Rx Data available irq enable
82 | 0 << 1 // Tx Fifo empty irq enable
83 | 0 << 2; // Rx Line Status irq enable
85 // set default baud rate and format
86 serial_baud (obj
, 9600);
87 serial_format(obj
, 8, ParityNone
, 1);
89 // pinout the chosen uart
90 pinmap_pinout(tx
, PinMap_UART_TX
);
91 pinmap_pinout(rx
, PinMap_UART_RX
);
93 // set rx/tx pins in PullUp mode
102 case UART_0
: obj
->index
= 0; break;
103 case UART_1
: obj
->index
= 1; break;
104 case UART_2
: obj
->index
= 2; break;
105 case UART_3
: obj
->index
= 3; break;
106 case UART_4
: obj
->index
= 4; break;
109 is_stdio_uart
= (uart
== STDIO_UART
) ? (1) : (0);
112 stdio_uart_inited
= 1;
113 memcpy(&stdio_uart
, obj
, sizeof(serial_t
));
117 void serial_free(serial_t
*obj
) {
118 serial_irq_ids
[obj
->index
] = 0;
122 // set the baud rate, taking in to account the current SystemFrequency
123 void serial_baud(serial_t
*obj
, int baudrate
) {
124 uint32_t PCLK
= PeripheralClock
;
126 // First we check to see if the basic divide with no DivAddVal/MulVal
127 // ratio gives us an integer result. If it does, we set DivAddVal = 0,
128 // MulVal = 1. Otherwise, we search the valid ratio value range to find
129 // the closest match. This could be more elegant, using search methods
130 // and/or lookup tables, but the brute force method is not that much
131 // slower, and is more maintainable.
132 uint16_t DL
= PCLK
/ (16 * baudrate
);
134 uint8_t DivAddVal
= 0;
139 if ((PCLK
% (16 * baudrate
)) != 0) { // Checking for zero remainder
140 int err_best
= baudrate
, b
;
141 for (mv
= 1; mv
< 16 && !hit
; mv
++)
143 for (dav
= 0; dav
< mv
; dav
++)
145 // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
146 // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
147 // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
148 // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
149 // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
151 if ((mv
* PCLK
* 2) & 0x80000000) // 1 bit headroom
152 dlv
= ((((2 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 16) + 1) / 2;
153 else // 2 bits headroom, use more precision
154 dlv
= ((((4 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 32) + 1) / 2;
156 // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
160 // datasheet says if dav > 0 then DL must be >= 2
161 if ((dav
> 0) && (dlv
< 2))
164 // integer rearrangement of the baudrate equation (with rounding)
165 b
= ((PCLK
* mv
/ (dlv
* (dav
+ mv
) * 8)) + 1) / 2;
167 // check to see how we went
168 b
= abs(b
- baudrate
);
187 // set LCR[DLAB] to enable writing to divider registers
188 obj
->uart
->LCR
|= (1 << 7);
190 // set divider values
191 obj
->uart
->DLM
= (DL
>> 8) & 0xFF;
192 obj
->uart
->DLL
= (DL
>> 0) & 0xFF;
193 obj
->uart
->FDR
= (uint32_t) DivAddVal
<< 0
194 | (uint32_t) MulVal
<< 4;
197 obj
->uart
->LCR
&= ~(1 << 7);
200 void serial_format(serial_t
*obj
, int data_bits
, SerialParity parity
, int stop_bits
) {
201 MBED_ASSERT((stop_bits
== 1) || (stop_bits
== 2)); // 0: 1 stop bits, 1: 2 stop bits
202 MBED_ASSERT((data_bits
> 4) && (data_bits
< 9)); // 0: 5 data bits ... 3: 8 data bits
203 MBED_ASSERT((parity
== ParityNone
) || (parity
== ParityOdd
) || (parity
== ParityEven
) ||
204 (parity
== ParityForced1
) || (parity
== ParityForced0
));
209 int parity_enable
, parity_select
;
211 case ParityNone
: parity_enable
= 0; parity_select
= 0; break;
212 case ParityOdd
: parity_enable
= 1; parity_select
= 0; break;
213 case ParityEven
: parity_enable
= 1; parity_select
= 1; break;
214 case ParityForced1
: parity_enable
= 1; parity_select
= 2; break;
215 case ParityForced0
: parity_enable
= 1; parity_select
= 3; break;
220 obj
->uart
->LCR
= data_bits
<< 0
223 | parity_select
<< 4;
226 /******************************************************************************
227 * INTERRUPTS HANDLING
228 ******************************************************************************/
229 static inline void uart_irq(uint32_t iir
, uint32_t index
) {
230 // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
233 case 1: irq_type
= TxIrq
; break;
234 case 2: irq_type
= RxIrq
; break;
238 if (serial_irq_ids
[index
] != 0)
239 irq_handler(serial_irq_ids
[index
], irq_type
);
242 void uart0_irq() {uart_irq((LPC_UART0
->IIR
>> 1) & 0x7, 0);}
243 void uart1_irq() {uart_irq((LPC_UART1
->IIR
>> 1) & 0x7, 1);}
244 void uart2_irq() {uart_irq((LPC_UART2
->IIR
>> 1) & 0x7, 2);}
245 void uart3_irq() {uart_irq((LPC_UART3
->IIR
>> 1) & 0x7, 3);}
246 void uart4_irq() {uart_irq((LPC_UART4
->IIR
>> 1) & 0x7, 4);}
248 void serial_irq_handler(serial_t
*obj
, uart_irq_handler handler
, uint32_t id
) {
249 irq_handler
= handler
;
250 serial_irq_ids
[obj
->index
] = id
;
253 void serial_irq_set(serial_t
*obj
, SerialIrq irq
, uint32_t enable
) {
254 IRQn_Type irq_n
= (IRQn_Type
)0;
256 switch ((int)obj
->uart
) {
257 case UART_0
: irq_n
=UART0_IRQn
; vector
= (uint32_t)&uart0_irq
; break;
258 case UART_1
: irq_n
=UART1_IRQn
; vector
= (uint32_t)&uart1_irq
; break;
259 case UART_2
: irq_n
=UART2_IRQn
; vector
= (uint32_t)&uart2_irq
; break;
260 case UART_3
: irq_n
=UART3_IRQn
; vector
= (uint32_t)&uart3_irq
; break;
261 case UART_4
: irq_n
=UART4_IRQn
; vector
= (uint32_t)&uart4_irq
; break;
265 obj
->uart
->IER
|= 1 << irq
;
266 NVIC_SetVector(irq_n
, vector
);
267 NVIC_EnableIRQ(irq_n
);
269 int all_disabled
= 0;
270 SerialIrq other_irq
= (irq
== RxIrq
) ? (TxIrq
) : (RxIrq
);
271 obj
->uart
->IER
&= ~(1 << irq
);
272 all_disabled
= (obj
->uart
->IER
& (1 << other_irq
)) == 0;
274 NVIC_DisableIRQ(irq_n
);
278 /******************************************************************************
280 ******************************************************************************/
281 int serial_getc(serial_t
*obj
) {
282 while (!serial_readable(obj
));
283 return obj
->uart
->RBR
;
286 void serial_putc(serial_t
*obj
, int c
) {
287 while (!serial_writable(obj
));
291 int serial_readable(serial_t
*obj
) {
292 return obj
->uart
->LSR
& 0x01;
295 int serial_writable(serial_t
*obj
) {
296 return obj
->uart
->LSR
& 0x20;
299 void serial_clear(serial_t
*obj
) {
300 obj
->uart
->FCR
= 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
301 | 1 << 1 // rx FIFO reset
302 | 1 << 2 // tx FIFO reset
303 | 0 << 6; // interrupt depth
306 void serial_pinout_tx(PinName tx
) {
307 pinmap_pinout(tx
, PinMap_UART_TX
);
310 void serial_break_set(serial_t
*obj
) {
311 obj
->uart
->LCR
|= (1 << 6);
314 void serial_break_clear(serial_t
*obj
) {
315 obj
->uart
->LCR
&= ~(1 << 6);