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
[] = {
32 {P2_8
, UART_0
, 0x02},
33 {P3_5
, UART_0
, 0x02},
34 {P3_0
, UART_0
, 0x03},
35 {P1_7
, UART_0
, 0x01},
39 static const PinMap PinMap_UART_RX
[] = {
40 {P2_7
, UART_0
, 0x02},
41 {P3_4
, UART_0
, 0x02},
42 {P3_1
, UART_0
, 0x03},
43 {P1_6
, UART_0
, 0x01},
47 static uint32_t serial_irq_ids
[UART_NUM
] = {0};
48 static uart_irq_handler irq_handler
;
50 int stdio_uart_inited
= 0;
53 void serial_init(serial_t
*obj
, PinName tx
, PinName rx
) {
54 int is_stdio_uart
= 0;
56 // determine the UART to use
57 UARTName uart_tx
= (UARTName
)pinmap_peripheral(tx
, PinMap_UART_TX
);
58 UARTName uart_rx
= (UARTName
)pinmap_peripheral(rx
, PinMap_UART_RX
);
59 UARTName uart
= (UARTName
)pinmap_merge(uart_tx
, uart_rx
);
60 MBED_ASSERT((int)uart
!= NC
);
62 obj
->uart
= (LPC_UART_TypeDef
*)uart
;
63 LPC_SYSCON
->SYSAHBCLKCTRL
|= (1<<12);
65 // enable fifos and default rx trigger level
66 obj
->uart
->FCR
= 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
67 | 0 << 1 // Rx Fifo Reset
68 | 0 << 2 // Tx Fifo Reset
69 | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
72 obj
->uart
->IER
= 0 << 0 // Rx Data available irq enable
73 | 0 << 1 // Tx Fifo empty irq enable
74 | 0 << 2; // Rx Line Status irq enable
76 // set default baud rate and format
77 serial_baud (obj
, 9600);
78 serial_format(obj
, 8, ParityNone
, 1);
80 // pinout the chosen uart
81 pinmap_pinout(tx
, PinMap_UART_TX
);
82 pinmap_pinout(rx
, PinMap_UART_RX
);
84 // set rx/tx pins in PullUp mode
93 case UART_0
: obj
->index
= 0; break;
96 is_stdio_uart
= (uart
== STDIO_UART
) ? (1) : (0);
99 stdio_uart_inited
= 1;
100 memcpy(&stdio_uart
, obj
, sizeof(serial_t
));
104 void serial_free(serial_t
*obj
) {
105 serial_irq_ids
[obj
->index
] = 0;
109 // set the baud rate, taking in to account the current SystemFrequency
110 void serial_baud(serial_t
*obj
, int baudrate
) {
111 LPC_SYSCON
->UARTCLKDIV
= 0x1;
112 uint32_t PCLK
= SystemCoreClock
;
113 // First we check to see if the basic divide with no DivAddVal/MulVal
114 // ratio gives us an integer result. If it does, we set DivAddVal = 0,
115 // MulVal = 1. Otherwise, we search the valid ratio value range to find
116 // the closest match. This could be more elegant, using search methods
117 // and/or lookup tables, but the brute force method is not that much
118 // slower, and is more maintainable.
119 uint16_t DL
= PCLK
/ (16 * baudrate
);
121 uint8_t DivAddVal
= 0;
126 if ((PCLK
% (16 * baudrate
)) != 0) { // Checking for zero remainder
127 int err_best
= baudrate
, b
;
128 for (mv
= 1; mv
< 16 && !hit
; mv
++)
130 for (dav
= 0; dav
< mv
; dav
++)
132 // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
133 // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
134 // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
135 // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
136 // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
138 if ((mv
* PCLK
* 2) & 0x80000000) // 1 bit headroom
139 dlv
= ((((2 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 16) + 1) / 2;
140 else // 2 bits headroom, use more precision
141 dlv
= ((((4 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 32) + 1) / 2;
143 // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
147 // datasheet says if dav > 0 then DL must be >= 2
148 if ((dav
> 0) && (dlv
< 2))
151 // integer rearrangement of the baudrate equation (with rounding)
152 b
= ((PCLK
* mv
/ (dlv
* (dav
+ mv
) * 8)) + 1) / 2;
154 // check to see how we went
155 b
= abs(b
- baudrate
);
174 // set LCR[DLAB] to enable writing to divider registers
175 obj
->uart
->LCR
|= (1 << 7);
177 // set divider values
178 obj
->uart
->DLM
= (DL
>> 8) & 0xFF;
179 obj
->uart
->DLL
= (DL
>> 0) & 0xFF;
180 obj
->uart
->FDR
= (uint32_t) DivAddVal
<< 0
181 | (uint32_t) MulVal
<< 4;
184 obj
->uart
->LCR
&= ~(1 << 7);
187 void serial_format(serial_t
*obj
, int data_bits
, SerialParity parity
, int stop_bits
) {
188 MBED_ASSERT((stop_bits
== 1) || (stop_bits
== 2)); // 0: 1 stop bits, 1: 2 stop bits
189 MBED_ASSERT((data_bits
> 4) && (data_bits
< 9)); // 0: 5 data bits ... 3: 8 data bits
190 MBED_ASSERT((parity
== ParityNone
) || (parity
== ParityOdd
) || (parity
== ParityEven
) ||
191 (parity
== ParityForced1
) || (parity
== ParityForced0
));
196 int parity_enable
, parity_select
;
198 case ParityNone
: parity_enable
= 0; parity_select
= 0; break;
199 case ParityOdd
: parity_enable
= 1; parity_select
= 0; break;
200 case ParityEven
: parity_enable
= 1; parity_select
= 1; break;
201 case ParityForced1
: parity_enable
= 1; parity_select
= 2; break;
202 case ParityForced0
: parity_enable
= 1; parity_select
= 3; break;
207 obj
->uart
->LCR
= data_bits
<< 0
210 | parity_select
<< 4;
213 /******************************************************************************
214 * INTERRUPTS HANDLING
215 ******************************************************************************/
216 static inline void uart_irq(uint32_t iir
, uint32_t index
) {
217 // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
220 case 1: irq_type
= TxIrq
; break;
221 case 2: irq_type
= RxIrq
; break;
225 if (serial_irq_ids
[index
] != 0)
226 irq_handler(serial_irq_ids
[index
], irq_type
);
229 void uart0_irq() {uart_irq((LPC_UART
->IIR
>> 1) & 0x7, 0);}
231 void serial_irq_handler(serial_t
*obj
, uart_irq_handler handler
, uint32_t id
) {
232 irq_handler
= handler
;
233 serial_irq_ids
[obj
->index
] = id
;
236 void serial_irq_set(serial_t
*obj
, SerialIrq irq
, uint32_t enable
) {
237 IRQn_Type irq_n
= (IRQn_Type
)0;
239 switch ((int)obj
->uart
) {
242 vector
= (uint32_t)&uart0_irq
;
249 obj
->uart
->IER
|= 1 << irq
;
250 NVIC_SetVector(irq_n
, vector
);
251 NVIC_EnableIRQ(irq_n
);
253 int all_disabled
= 0;
254 SerialIrq other_irq
= (irq
== RxIrq
) ? (TxIrq
) : (RxIrq
);
256 obj
->uart
->IER
&= ~(1 << irq
);
257 all_disabled
= (obj
->uart
->IER
& (1 << other_irq
)) == 0;
260 NVIC_DisableIRQ(irq_n
);
264 /******************************************************************************
266 ******************************************************************************/
267 int serial_getc(serial_t
*obj
) {
268 while (!serial_readable(obj
));
269 return obj
->uart
->RBR
;
272 void serial_putc(serial_t
*obj
, int c
) {
273 while (!serial_writable(obj
));
277 int serial_readable(serial_t
*obj
) {
278 return obj
->uart
->LSR
& 0x01;
281 int serial_writable(serial_t
*obj
) {
282 return obj
->uart
->LSR
& 0x20;
285 void serial_clear(serial_t
*obj
) {
286 obj
->uart
->FCR
= 1 << 1 // rx FIFO reset
287 | 1 << 2 // tx FIFO reset
288 | 0 << 6; // interrupt depth
291 void serial_pinout_tx(PinName tx
) {
292 pinmap_pinout(tx
, PinMap_UART_TX
);
295 void serial_break_clear(serial_t
*obj
) {
296 obj
->uart
->LCR
&= ~(1 << 6);
299 void serial_break_set(serial_t
*obj
) {
300 obj
->uart
->LCR
|= 1 << 6;