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 "PeripheralPins.h" // For the Peripheral to Pin Definitions found in the individual Target's Platform
26 /******************************************************************************
28 ******************************************************************************/
31 static uint32_t serial_irq_ids
[UART_NUM
] = {0};
32 static uart_irq_handler irq_handler
;
34 int stdio_uart_inited
= 0;
37 void serial_init(serial_t
*obj
, PinName tx
, PinName rx
) {
38 int is_stdio_uart
= 0;
40 // determine the UART to use
41 UARTName uart_tx
= (UARTName
)pinmap_peripheral(tx
, PinMap_UART_TX
);
42 UARTName uart_rx
= (UARTName
)pinmap_peripheral(rx
, PinMap_UART_RX
);
43 UARTName uart
= (UARTName
)pinmap_merge(uart_tx
, uart_rx
);
44 MBED_ASSERT((int)uart
!= NC
);
46 obj
->uart
= (LPC_USART_Type
*)uart
;
47 LPC_SYSCON
->SYSAHBCLKCTRL
|= (1<<12);
49 // [TODO] Consider more elegant approach
50 // disconnect USBTX/RX mapping mux, for case when switching ports
52 pin_function(USBTX
, 0);
53 pin_function(USBRX
, 0);
56 // enable fifos and default rx trigger level
57 obj
->uart
->FCR
= 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
58 | 0 << 1 // Rx Fifo Reset
59 | 0 << 2 // Tx Fifo Reset
60 | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
63 obj
->uart
->IER
= 0 << 0 // Rx Data available irq enable
64 | 0 << 1 // Tx Fifo empty irq enable
65 | 0 << 2; // Rx Line Status irq enable
67 // set default baud rate and format
68 serial_baud (obj
, 9600);
69 serial_format(obj
, 8, ParityNone
, 1);
71 // pinout the chosen uart
72 pinmap_pinout(tx
, PinMap_UART_TX
);
73 pinmap_pinout(rx
, PinMap_UART_RX
);
75 // set rx/tx pins in PullUp mode
84 case UART_0
: obj
->index
= 0; break;
87 is_stdio_uart
= (uart
== STDIO_UART
) ? (1) : (0);
90 stdio_uart_inited
= 1;
91 memcpy(&stdio_uart
, obj
, sizeof(serial_t
));
95 void serial_free(serial_t
*obj
) {
96 serial_irq_ids
[obj
->index
] = 0;
100 // set the baud rate, taking in to account the current SystemFrequency
101 void serial_baud(serial_t
*obj
, int baudrate
) {
102 LPC_SYSCON
->UARTCLKDIV
= 0x1;
103 uint32_t PCLK
= SystemCoreClock
;
104 // First we check to see if the basic divide with no DivAddVal/MulVal
105 // ratio gives us an integer result. If it does, we set DivAddVal = 0,
106 // MulVal = 1. Otherwise, we search the valid ratio value range to find
107 // the closest match. This could be more elegant, using search methods
108 // and/or lookup tables, but the brute force method is not that much
109 // slower, and is more maintainable.
110 uint16_t DL
= PCLK
/ (16 * baudrate
);
112 uint8_t DivAddVal
= 0;
117 if ((PCLK
% (16 * baudrate
)) != 0) { // Checking for zero remainder
118 int err_best
= baudrate
, b
;
119 for (mv
= 1; mv
< 16 && !hit
; mv
++)
121 for (dav
= 0; dav
< mv
; dav
++)
123 // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
124 // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
125 // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
126 // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
127 // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
129 if ((mv
* PCLK
* 2) & 0x80000000) // 1 bit headroom
130 dlv
= ((((2 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 16) + 1) / 2;
131 else // 2 bits headroom, use more precision
132 dlv
= ((((4 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 32) + 1) / 2;
134 // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
138 // datasheet says if dav > 0 then DL must be >= 2
139 if ((dav
> 0) && (dlv
< 2))
142 // integer rearrangement of the baudrate equation (with rounding)
143 b
= ((PCLK
* mv
/ (dlv
* (dav
+ mv
) * 8)) + 1) / 2;
145 // check to see how we went
146 b
= abs(b
- baudrate
);
165 // set LCR[DLAB] to enable writing to divider registers
166 obj
->uart
->LCR
|= (1 << 7);
168 // set divider values
169 obj
->uart
->DLM
= (DL
>> 8) & 0xFF;
170 obj
->uart
->DLL
= (DL
>> 0) & 0xFF;
171 obj
->uart
->FDR
= (uint32_t) DivAddVal
<< 0
172 | (uint32_t) MulVal
<< 4;
175 obj
->uart
->LCR
&= ~(1 << 7);
178 void serial_format(serial_t
*obj
, int data_bits
, SerialParity parity
, int stop_bits
) {
179 MBED_ASSERT((stop_bits
== 1) || (stop_bits
== 2)); // 0: 1 stop bits, 1: 2 stop bits
180 MBED_ASSERT((data_bits
> 4) && (data_bits
< 9)); // 0: 5 data bits ... 3: 8 data bits
181 MBED_ASSERT((parity
== ParityNone
) || (parity
== ParityOdd
) || (parity
== ParityEven
) ||
182 (parity
== ParityForced1
) || (parity
== ParityForced0
));
187 int parity_enable
= 0, parity_select
= 0;
189 case ParityNone
: parity_enable
= 0; parity_select
= 0; break;
190 case ParityOdd
: parity_enable
= 1; parity_select
= 0; break;
191 case ParityEven
: parity_enable
= 1; parity_select
= 1; break;
192 case ParityForced1
: parity_enable
= 1; parity_select
= 2; break;
193 case ParityForced0
: parity_enable
= 1; parity_select
= 3; break;
198 obj
->uart
->LCR
= data_bits
<< 0
201 | parity_select
<< 4;
204 /******************************************************************************
205 * INTERRUPTS HANDLING
206 ******************************************************************************/
207 static inline void uart_irq(uint32_t iir
, uint32_t index
) {
208 // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
211 case 1: irq_type
= TxIrq
; break;
212 case 2: irq_type
= RxIrq
; break;
216 if (serial_irq_ids
[index
] != 0)
217 irq_handler(serial_irq_ids
[index
], irq_type
);
220 void uart0_irq() {uart_irq((LPC_USART
->IIR
>> 1) & 0x7, 0);}
222 void serial_irq_handler(serial_t
*obj
, uart_irq_handler handler
, uint32_t id
) {
223 irq_handler
= handler
;
224 serial_irq_ids
[obj
->index
] = id
;
227 void serial_irq_set(serial_t
*obj
, SerialIrq irq
, uint32_t enable
) {
228 IRQn_Type irq_n
= (IRQn_Type
)0;
230 switch ((int)obj
->uart
) {
231 case UART_0
: irq_n
=UART_IRQn
; vector
= (uint32_t)&uart0_irq
; break;
235 obj
->uart
->IER
|= 1 << irq
;
236 NVIC_SetVector(irq_n
, vector
);
237 NVIC_EnableIRQ(irq_n
);
239 int all_disabled
= 0;
240 SerialIrq other_irq
= (irq
== RxIrq
) ? (TxIrq
) : (RxIrq
);
242 obj
->uart
->IER
&= ~(1 << irq
);
243 all_disabled
= (obj
->uart
->IER
& (1 << other_irq
)) == 0;
246 NVIC_DisableIRQ(irq_n
);
250 /******************************************************************************
252 ******************************************************************************/
253 int serial_getc(serial_t
*obj
) {
254 while (!serial_readable(obj
));
255 return obj
->uart
->RBR
;
258 void serial_putc(serial_t
*obj
, int c
) {
259 while (!serial_writable(obj
));
263 int serial_readable(serial_t
*obj
) {
264 return obj
->uart
->LSR
& 0x01;
267 int serial_writable(serial_t
*obj
) {
268 return obj
->uart
->LSR
& 0x20;
271 void serial_clear(serial_t
*obj
) {
272 obj
->uart
->FCR
= 1 << 1 // rx FIFO reset
273 | 1 << 2 // tx FIFO reset
274 | 0 << 6; // interrupt depth
277 void serial_pinout_tx(PinName tx
) {
278 pinmap_pinout(tx
, PinMap_UART_TX
);
281 void serial_break_set(serial_t
*obj
) {
282 obj
->uart
->LCR
|= (1 << 6);
285 void serial_break_clear(serial_t
*obj
) {
286 obj
->uart
->LCR
&= ~(1 << 6);