]> git.gir.st - tmk_keyboard.git/blob - tool/mbed/mbed-sdk/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC408X/TARGET_LPC4088_DM/serial_api.c
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[tmk_keyboard.git] / tool / mbed / mbed-sdk / libraries / mbed / targets / hal / TARGET_NXP / TARGET_LPC408X / TARGET_LPC4088_DM / serial_api.c
1 /* mbed Microcontroller Library
2 * Copyright (c) 2006-2013 ARM Limited
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16 // math.h required for floating point operations for baud rate calculation
17 #include <math.h>
18 #include <string.h>
19 #include <stdlib.h>
20
21 #include "serial_api.h"
22 #include "cmsis.h"
23 #include "pinmap.h"
24 #include "mbed_error.h"
25
26 /******************************************************************************
27 * INITIALIZATION
28 ******************************************************************************/
29 static const PinMap PinMap_UART_TX[] = {
30 {P0_0, UART_3, 2},
31 {P0_2, UART_0, 1},
32 {P0_25, UART_3, 3},
33 {P4_22, UART_2, 2},
34 {P5_4, UART_4, 4},
35 {NC , NC , 0}
36 };
37
38 static const PinMap PinMap_UART_RX[] = {
39 {P0_1 , UART_3, 2},
40 {P0_3 , UART_0, 1},
41 {P0_26, UART_3, 3},
42 {P4_23, UART_2, 2},
43 {P5_3, UART_4, 4},
44 {NC , NC , 0}
45 };
46
47 #define UART_NUM 5
48
49 static uint32_t serial_irq_ids[UART_NUM] = {0};
50 static uart_irq_handler irq_handler;
51
52 int stdio_uart_inited = 0;
53 serial_t stdio_uart;
54
55 void serial_init(serial_t *obj, PinName tx, PinName rx) {
56 int is_stdio_uart = 0;
57
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);
63
64 obj->uart = (LPC_UART_TypeDef *)uart;
65 // enable power
66 switch (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;
72 }
73
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
79
80 // disable irqs
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
84
85 // set default baud rate and format
86 serial_baud (obj, 9600);
87 serial_format(obj, 8, ParityNone, 1);
88
89 // pinout the chosen uart
90 pinmap_pinout(tx, PinMap_UART_TX);
91 pinmap_pinout(rx, PinMap_UART_RX);
92
93 // set rx/tx pins in PullUp mode
94 if (tx != NC) {
95 pin_mode(tx, PullUp);
96 }
97 if (rx != NC) {
98 pin_mode(rx, PullUp);
99 }
100
101 switch (uart) {
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;
107 }
108
109 is_stdio_uart = (uart == STDIO_UART) ? (1) : (0);
110
111 if (is_stdio_uart) {
112 stdio_uart_inited = 1;
113 memcpy(&stdio_uart, obj, sizeof(serial_t));
114 }
115 }
116
117 void serial_free(serial_t *obj) {
118 serial_irq_ids[obj->index] = 0;
119 }
120
121 // serial_baud
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;
125
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);
133
134 uint8_t DivAddVal = 0;
135 uint8_t MulVal = 1;
136 int hit = 0;
137 uint16_t dlv;
138 uint8_t mv, dav;
139 if ((PCLK % (16 * baudrate)) != 0) { // Checking for zero remainder
140 int err_best = baudrate, b;
141 for (mv = 1; mv < 16 && !hit; mv++)
142 {
143 for (dav = 0; dav < mv; dav++)
144 {
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
150
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;
155
156 // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
157 if (dlv == 0)
158 dlv = 1;
159
160 // datasheet says if dav > 0 then DL must be >= 2
161 if ((dav > 0) && (dlv < 2))
162 dlv = 2;
163
164 // integer rearrangement of the baudrate equation (with rounding)
165 b = ((PCLK * mv / (dlv * (dav + mv) * 8)) + 1) / 2;
166
167 // check to see how we went
168 b = abs(b - baudrate);
169 if (b < err_best)
170 {
171 err_best = b;
172
173 DL = dlv;
174 MulVal = mv;
175 DivAddVal = dav;
176
177 if (b == baudrate)
178 {
179 hit = 1;
180 break;
181 }
182 }
183 }
184 }
185 }
186
187 // set LCR[DLAB] to enable writing to divider registers
188 obj->uart->LCR |= (1 << 7);
189
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;
195
196 // clear LCR[DLAB]
197 obj->uart->LCR &= ~(1 << 7);
198 }
199
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));
205
206 stop_bits -= 1;
207 data_bits -= 5;
208
209 int parity_enable, parity_select;
210 switch (parity) {
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;
216 default:
217 break;
218 }
219
220 obj->uart->LCR = data_bits << 0
221 | stop_bits << 2
222 | parity_enable << 3
223 | parity_select << 4;
224 }
225
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
231 SerialIrq irq_type;
232 switch (iir) {
233 case 1: irq_type = TxIrq; break;
234 case 2: irq_type = RxIrq; break;
235 default: return;
236 }
237
238 if (serial_irq_ids[index] != 0)
239 irq_handler(serial_irq_ids[index], irq_type);
240 }
241
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);}
247
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;
251 }
252
253 void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
254 IRQn_Type irq_n = (IRQn_Type)0;
255 uint32_t vector = 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;
262 }
263
264 if (enable) {
265 obj->uart->IER |= 1 << irq;
266 NVIC_SetVector(irq_n, vector);
267 NVIC_EnableIRQ(irq_n);
268 } else { // disable
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;
273 if (all_disabled)
274 NVIC_DisableIRQ(irq_n);
275 }
276 }
277
278 /******************************************************************************
279 * READ/WRITE
280 ******************************************************************************/
281 int serial_getc(serial_t *obj) {
282 while (!serial_readable(obj));
283 return obj->uart->RBR;
284 }
285
286 void serial_putc(serial_t *obj, int c) {
287 while (!serial_writable(obj));
288 obj->uart->THR = c;
289 }
290
291 int serial_readable(serial_t *obj) {
292 return obj->uart->LSR & 0x01;
293 }
294
295 int serial_writable(serial_t *obj) {
296 return obj->uart->LSR & 0x20;
297 }
298
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
304 }
305
306 void serial_pinout_tx(PinName tx) {
307 pinmap_pinout(tx, PinMap_UART_TX);
308 }
309
310 void serial_break_set(serial_t *obj) {
311 obj->uart->LCR |= (1 << 6);
312 }
313
314 void serial_break_clear(serial_t *obj) {
315 obj->uart->LCR &= ~(1 << 6);
316 }
317
Imprint / Impressum