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 * Ported to NXP LPC43XX by Micromint USA <support@micromint.com>
18 // math.h required for floating point operations for baud rate calculation
23 #include "serial_api.h"
26 #include "mbed_error.h"
29 /******************************************************************************
31 ******************************************************************************/
34 // SCU mode for UART pins
35 #define SCU_PINIO_UART_TX SCU_MODE_PULLDOWN
36 #define SCU_PINIO_UART_RX SCU_PINIO_PULLNONE
38 static const PinMap PinMap_UART_TX
[] = {
39 {P1_13
, UART_1
, (SCU_PINIO_UART_TX
| 1)},
40 {P1_15
, UART_2
, (SCU_PINIO_UART_TX
| 1)},
41 {P2_0
, UART_0
, (SCU_PINIO_UART_TX
| 1)},
42 {P2_3
, UART_3
, (SCU_PINIO_UART_TX
| 2)},
43 {P2_10
, UART_2
, (SCU_PINIO_UART_TX
| 2)},
44 {P3_4
, UART_1
, (SCU_PINIO_UART_TX
| 4)},
45 {P4_1
, UART_3
, (SCU_PINIO_UART_TX
| 6)},
46 {P5_6
, UART_1
, (SCU_PINIO_UART_TX
| 4)},
47 {P6_4
, UART_0
, (SCU_PINIO_UART_TX
| 2)},
48 {P7_1
, UART_2
, (SCU_PINIO_UART_TX
| 6)},
49 {P9_3
, UART_3
, (SCU_PINIO_UART_TX
| 7)},
50 {P9_5
, UART_0
, (SCU_PINIO_UART_TX
| 7)},
51 {PA_1
, UART_2
, (SCU_PINIO_UART_TX
| 3)},
52 {PC_13
, UART_1
, (SCU_PINIO_UART_TX
| 2)},
53 {PE_11
, UART_1
, (SCU_PINIO_UART_TX
| 2)},
54 {PF_2
, UART_3
, (SCU_PINIO_UART_TX
| 1)},
55 {PF_10
, UART_0
, (SCU_PINIO_UART_TX
| 1)},
59 static const PinMap PinMap_UART_RX
[] = {
60 {P1_14
, UART_1
, (SCU_PINIO_UART_RX
| 1)},
61 {P1_16
, UART_2
, (SCU_PINIO_UART_RX
| 1)},
62 {P2_1
, UART_0
, (SCU_PINIO_UART_RX
| 1)},
63 {P2_4
, UART_3
, (SCU_PINIO_UART_RX
| 2)},
64 {P2_11
, UART_2
, (SCU_PINIO_UART_RX
| 2)},
65 {P3_5
, UART_1
, (SCU_PINIO_UART_RX
| 4)},
66 {P4_2
, UART_3
, (SCU_PINIO_UART_RX
| 6)},
67 {P5_7
, UART_1
, (SCU_PINIO_UART_RX
| 4)},
68 {P6_5
, UART_0
, (SCU_PINIO_UART_RX
| 2)},
69 {P7_2
, UART_2
, (SCU_PINIO_UART_RX
| 6)},
70 {P9_4
, UART_3
, (SCU_PINIO_UART_RX
| 7)},
71 {P9_6
, UART_0
, (SCU_PINIO_UART_RX
| 7)},
72 {PA_2
, UART_2
, (SCU_PINIO_UART_RX
| 3)},
73 {PC_14
, UART_1
, (SCU_PINIO_UART_RX
| 2)},
74 {PE_12
, UART_1
, (SCU_PINIO_UART_RX
| 2)},
75 {PF_3
, UART_3
, (SCU_PINIO_UART_RX
| 1)},
76 {PF_11
, UART_0
, (SCU_PINIO_UART_RX
| 1)},
80 #if (DEVICE_SERIAL_FC)
81 // RTS/CTS PinMap for flow control
82 static const PinMap PinMap_UART_RTS
[] = {
83 {P1_9
, UART_1
, (SCU_PINIO_FAST
| 1)},
84 {P5_2
, UART_1
, (SCU_PINIO_FAST
| 4)},
85 {PC_3
, UART_1
, (SCU_PINIO_FAST
| 2)},
86 {PE_5
, UART_1
, (SCU_PINIO_FAST
| 2)},
90 static const PinMap PinMap_UART_CTS
[] = {
91 {P1_11
, UART_1
, (SCU_PINIO_FAST
| 1)},
92 {P5_4
, UART_1
, (SCU_PINIO_FAST
| 4),
93 {PC_2
, UART_1
, (SCU_PINIO_FAST
| 2)},
94 {PE_7
, UART_1
, (SCU_PINIO_FAST
| 2)},
99 static uart_irq_handler irq_handler
;
101 int stdio_uart_inited
= 0;
104 struct serial_global_data_s
{
105 uint32_t serial_irq_id
;
106 gpio_t sw_rts
, sw_cts
;
107 uint8_t count
, rx_irq_set_flow
, rx_irq_set_api
;
110 static struct serial_global_data_s uart_data
[UART_NUM
];
112 void serial_init(serial_t
*obj
, PinName tx
, PinName rx
) {
113 int is_stdio_uart
= 0;
115 // determine the UART to use
116 UARTName uart_tx
= (UARTName
)pinmap_peripheral(tx
, PinMap_UART_TX
);
117 UARTName uart_rx
= (UARTName
)pinmap_peripheral(rx
, PinMap_UART_RX
);
118 UARTName uart
= (UARTName
)pinmap_merge(uart_tx
, uart_rx
);
119 if ((int)uart
== NC
) {
120 error("Serial pinout mapping failed");
123 obj
->uart
= (LPC_USART_T
*)uart
;
125 // enable fifos and default rx trigger level
126 obj
->uart
->FCR
= 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
127 | 0 << 1 // Rx Fifo Reset
128 | 0 << 2 // Tx Fifo Reset
129 | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
132 obj
->uart
->IER
= 0 << 0 // Rx Data available irq enable
133 | 0 << 1 // Tx Fifo empty irq enable
134 | 0 << 2; // Rx Line Status irq enable
136 // set default baud rate and format
137 is_stdio_uart
= (uart
== STDIO_UART
) ? (1) : (0);
138 serial_baud (obj
, is_stdio_uart
? 115200 : 9600);
139 serial_format(obj
, 8, ParityNone
, 1);
141 // pinout the chosen uart
142 pinmap_pinout(tx
, PinMap_UART_TX
);
143 pinmap_pinout(rx
, PinMap_UART_RX
);
145 // set rx/tx pins in PullUp mode
147 pin_mode(tx
, PullUp
);
150 pin_mode(rx
, PullUp
);
154 case UART_0
: obj
->index
= 0; break;
155 case UART_1
: obj
->index
= 1; break;
156 case UART_2
: obj
->index
= 2; break;
157 case UART_3
: obj
->index
= 3; break;
159 uart_data
[obj
->index
].sw_rts
.pin
= NC
;
160 uart_data
[obj
->index
].sw_cts
.pin
= NC
;
161 serial_set_flow_control(obj
, FlowControlNone
, NC
, NC
);
164 stdio_uart_inited
= 1;
165 memcpy(&stdio_uart
, obj
, sizeof(serial_t
));
169 void serial_free(serial_t
*obj
) {
170 uart_data
[obj
->index
].serial_irq_id
= 0;
174 // set the baud rate, taking in to account the current SystemFrequency
175 void serial_baud(serial_t
*obj
, int baudrate
) {
176 uint32_t PCLK
= SystemCoreClock
;
178 // First we check to see if the basic divide with no DivAddVal/MulVal
179 // ratio gives us an integer result. If it does, we set DivAddVal = 0,
180 // MulVal = 1. Otherwise, we search the valid ratio value range to find
181 // the closest match. This could be more elegant, using search methods
182 // and/or lookup tables, but the brute force method is not that much
183 // slower, and is more maintainable.
184 uint16_t DL
= PCLK
/ (16 * baudrate
);
186 uint8_t DivAddVal
= 0;
191 if ((PCLK
% (16 * baudrate
)) != 0) { // Checking for zero remainder
192 int err_best
= baudrate
, b
;
193 for (mv
= 1; mv
< 16 && !hit
; mv
++)
195 for (dav
= 0; dav
< mv
; dav
++)
197 // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
198 // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
199 // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
200 // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
201 // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
203 if ((mv
* PCLK
* 2) & 0x80000000) // 1 bit headroom
204 dlv
= ((((2 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 16) + 1) / 2;
205 else // 2 bits headroom, use more precision
206 dlv
= ((((4 * mv
* PCLK
) / (baudrate
* (dav
+ mv
))) / 32) + 1) / 2;
208 // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
212 // datasheet says if dav > 0 then DL must be >= 2
213 if ((dav
> 0) && (dlv
< 2))
216 // integer rearrangement of the baudrate equation (with rounding)
217 b
= ((PCLK
* mv
/ (dlv
* (dav
+ mv
) * 8)) + 1) / 2;
219 // check to see how we went
220 b
= abs(b
- baudrate
);
239 // set LCR[DLAB] to enable writing to divider registers
240 obj
->uart
->LCR
|= (1 << 7);
242 // set divider values
243 obj
->uart
->DLM
= (DL
>> 8) & 0xFF;
244 obj
->uart
->DLL
= (DL
>> 0) & 0xFF;
245 obj
->uart
->FDR
= (uint32_t) DivAddVal
<< 0
246 | (uint32_t) MulVal
<< 4;
249 obj
->uart
->LCR
&= ~(1 << 7);
252 void serial_format(serial_t
*obj
, int data_bits
, SerialParity parity
, int stop_bits
) {
253 // 0: 1 stop bits, 1: 2 stop bits
254 if (stop_bits
!= 1 && stop_bits
!= 2) {
255 error("Invalid stop bits specified");
259 // 0: 5 data bits ... 3: 8 data bits
260 if (data_bits
< 5 || data_bits
> 8) {
261 error("Invalid number of bits (%d) in serial format, should be 5..8", data_bits
);
265 int parity_enable
, parity_select
;
267 case ParityNone
: parity_enable
= 0; parity_select
= 0; break;
268 case ParityOdd
: parity_enable
= 1; parity_select
= 0; break;
269 case ParityEven
: parity_enable
= 1; parity_select
= 1; break;
270 case ParityForced1
: parity_enable
= 1; parity_select
= 2; break;
271 case ParityForced0
: parity_enable
= 1; parity_select
= 3; break;
273 error("Invalid serial parity setting");
277 obj
->uart
->LCR
= data_bits
<< 0
280 | parity_select
<< 4;
283 /******************************************************************************
284 * INTERRUPTS HANDLING
285 ******************************************************************************/
286 static inline void uart_irq(uint32_t iir
, uint32_t index
, LPC_USART_T
*puart
) {
287 // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
290 case 1: irq_type
= TxIrq
; break;
291 case 2: irq_type
= RxIrq
; break;
294 if ((RxIrq
== irq_type
) && (NC
!= uart_data
[index
].sw_rts
.pin
)) {
295 gpio_write(&uart_data
[index
].sw_rts
, 1);
296 // Disable interrupt if it wasn't enabled by other part of the application
297 if (!uart_data
[index
].rx_irq_set_api
)
298 puart
->IER
&= ~(1 << RxIrq
);
300 if (uart_data
[index
].serial_irq_id
!= 0)
301 if ((irq_type
!= RxIrq
) || (uart_data
[index
].rx_irq_set_api
))
302 irq_handler(uart_data
[index
].serial_irq_id
, irq_type
);
305 void uart0_irq() {uart_irq((LPC_USART0
->IIR
>> 1) & 0x7, 0, (LPC_USART_T
*)LPC_USART0
);}
306 void uart1_irq() {uart_irq((LPC_UART1
->IIR
>> 1) & 0x7, 1, (LPC_USART_T
*)LPC_UART1
);}
307 void uart2_irq() {uart_irq((LPC_USART2
->IIR
>> 1) & 0x7, 2, (LPC_USART_T
*)LPC_USART2
);}
308 void uart3_irq() {uart_irq((LPC_USART3
->IIR
>> 1) & 0x7, 3, (LPC_USART_T
*)LPC_USART3
);}
310 void serial_irq_handler(serial_t
*obj
, uart_irq_handler handler
, uint32_t id
) {
311 irq_handler
= handler
;
312 uart_data
[obj
->index
].serial_irq_id
= id
;
315 static void serial_irq_set_internal(serial_t
*obj
, SerialIrq irq
, uint32_t enable
) {
316 IRQn_Type irq_n
= (IRQn_Type
)0;
318 switch ((int)obj
->uart
) {
319 case UART_0
: irq_n
=USART0_IRQn
; vector
= (uint32_t)&uart0_irq
; break;
320 case UART_1
: irq_n
=UART1_IRQn
; vector
= (uint32_t)&uart1_irq
; break;
321 case UART_2
: irq_n
=USART2_IRQn
; vector
= (uint32_t)&uart2_irq
; break;
322 case UART_3
: irq_n
=USART3_IRQn
; vector
= (uint32_t)&uart3_irq
; break;
326 obj
->uart
->IER
|= 1 << irq
;
327 NVIC_SetVector(irq_n
, vector
);
328 NVIC_EnableIRQ(irq_n
);
329 } else if ((TxIrq
== irq
) || (uart_data
[obj
->index
].rx_irq_set_api
+ uart_data
[obj
->index
].rx_irq_set_flow
== 0)) { // disable
330 int all_disabled
= 0;
331 SerialIrq other_irq
= (irq
== RxIrq
) ? (TxIrq
) : (RxIrq
);
332 obj
->uart
->IER
&= ~(1 << irq
);
333 all_disabled
= (obj
->uart
->IER
& (1 << other_irq
)) == 0;
335 NVIC_DisableIRQ(irq_n
);
339 void serial_irq_set(serial_t
*obj
, SerialIrq irq
, uint32_t enable
) {
341 uart_data
[obj
->index
].rx_irq_set_api
= enable
;
342 serial_irq_set_internal(obj
, irq
, enable
);
345 #if (DEVICE_SERIAL_FC)
346 static void serial_flow_irq_set(serial_t
*obj
, uint32_t enable
) {
347 uart_data
[obj
->index
].rx_irq_set_flow
= enable
;
348 serial_irq_set_internal(obj
, RxIrq
, enable
);
352 /******************************************************************************
354 ******************************************************************************/
355 int serial_getc(serial_t
*obj
) {
356 while (!serial_readable(obj
));
357 int data
= obj
->uart
->RBR
;
358 if (NC
!= uart_data
[obj
->index
].sw_rts
.pin
) {
359 gpio_write(&uart_data
[obj
->index
].sw_rts
, 0);
360 obj
->uart
->IER
|= 1 << RxIrq
;
365 void serial_putc(serial_t
*obj
, int c
) {
366 while (!serial_writable(obj
));
368 uart_data
[obj
->index
].count
++;
371 int serial_readable(serial_t
*obj
) {
372 return obj
->uart
->LSR
& 0x01;
375 int serial_writable(serial_t
*obj
) {
377 if (NC
!= uart_data
[obj
->index
].sw_cts
.pin
)
378 isWritable
= (gpio_read(&uart_data
[obj
->index
].sw_cts
) == 0) && (obj
->uart
->LSR
& 0x40); //If flow control: writable if CTS low + UART done
380 if (obj
->uart
->LSR
& 0x20)
381 uart_data
[obj
->index
].count
= 0;
382 else if (uart_data
[obj
->index
].count
>= 16)
388 void serial_clear(serial_t
*obj
) {
389 obj
->uart
->FCR
= 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
390 | 1 << 1 // rx FIFO reset
391 | 1 << 2 // tx FIFO reset
392 | 0 << 6; // interrupt depth
395 void serial_pinout_tx(PinName tx
) {
396 pinmap_pinout(tx
, PinMap_UART_TX
);
399 void serial_break_set(serial_t
*obj
) {
400 obj
->uart
->LCR
|= (1 << 6);
403 void serial_break_clear(serial_t
*obj
) {
404 obj
->uart
->LCR
&= ~(1 << 6);
407 void serial_set_flow_control(serial_t
*obj
, FlowControl type
, PinName rxflow
, PinName txflow
) {
408 #if (DEVICE_SERIAL_FC)