]> git.gir.st - tmk_keyboard.git/blob - tmk_core/tool/mbed/mbed-sdk/libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC13XX/serial_api.c
remove experimental return, cleanup slash_question key
[tmk_keyboard.git] / tmk_core / tool / mbed / mbed-sdk / libraries / mbed / targets / hal / TARGET_NXP / TARGET_LPC13XX / 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 "mbed_assert.h"
18 #include <math.h>
19 #include <string.h>
20 #include <stdlib.h>
21
22 #include "serial_api.h"
23 #include "cmsis.h"
24 #include "pinmap.h"
25
26 /******************************************************************************
27 * INITIALIZATION
28 ******************************************************************************/
29 #define UART_NUM 1
30
31 static const PinMap PinMap_UART_TX[] = {
32 {P0_19, UART_0, 1},
33 {P1_13, UART_0, 3},
34 {P1_27, UART_0, 2},
35 { NC , NC , 0}
36 };
37
38 static const PinMap PinMap_UART_RX[] = {
39 {P0_18, UART_0, 1},
40 {P1_14, UART_0, 3},
41 {P1_26, UART_0, 2},
42 {NC , NC , 0}
43 };
44
45 static uint32_t serial_irq_ids[UART_NUM] = {0};
46 static uart_irq_handler irq_handler;
47
48 int stdio_uart_inited = 0;
49 serial_t stdio_uart;
50
51 void serial_init(serial_t *obj, PinName tx, PinName rx) {
52 int is_stdio_uart = 0;
53
54 // determine the UART to use
55 UARTName uart_tx = (UARTName)pinmap_peripheral(tx, PinMap_UART_TX);
56 UARTName uart_rx = (UARTName)pinmap_peripheral(rx, PinMap_UART_RX);
57 UARTName uart = (UARTName)pinmap_merge(uart_tx, uart_rx);
58 MBED_ASSERT((int)uart != NC);
59
60 obj->uart = (LPC_USART_Type *)uart;
61 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);
62
63 // [TODO] Consider more elegant approach
64 // disconnect USBTX/RX mapping mux, for case when switching ports
65 //pin_function(USBTX, 0);
66 //pin_function(USBRX, 0);
67
68 // enable fifos and default rx trigger level
69 obj->uart->FCR = 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled
70 | 0 << 1 // Rx Fifo Reset
71 | 0 << 2 // Tx Fifo Reset
72 | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
73
74 // disable irqs
75 obj->uart->IER = 0 << 0 // Rx Data available irq enable
76 | 0 << 1 // Tx Fifo empty irq enable
77 | 0 << 2; // Rx Line Status irq enable
78
79 // set default baud rate and format
80 serial_baud (obj, 9600);
81 serial_format(obj, 8, ParityNone, 1);
82
83 // pinout the chosen uart
84 pinmap_pinout(tx, PinMap_UART_TX);
85 pinmap_pinout(rx, PinMap_UART_RX);
86
87 // set rx/tx pins in PullUp mode
88 if (tx != NC) {
89 pin_mode(tx, PullUp);
90 }
91 if (rx != NC) {
92 pin_mode(rx, PullUp);
93 }
94
95 switch (uart) {
96 case UART_0: obj->index = 0; break;
97 }
98
99 is_stdio_uart = (uart == STDIO_UART) ? (1) : (0);
100
101 if (is_stdio_uart) {
102 stdio_uart_inited = 1;
103 memcpy(&stdio_uart, obj, sizeof(serial_t));
104 }
105 }
106
107 void serial_free(serial_t *obj) {
108 serial_irq_ids[obj->index] = 0;
109 }
110
111 // serial_baud
112 // set the baud rate, taking in to account the current SystemFrequency
113 void serial_baud(serial_t *obj, int baudrate) {
114 LPC_SYSCON->UARTCLKDIV = 0x1;
115 uint32_t PCLK = SystemCoreClock;
116 // First we check to see if the basic divide with no DivAddVal/MulVal
117 // ratio gives us an integer result. If it does, we set DivAddVal = 0,
118 // MulVal = 1. Otherwise, we search the valid ratio value range to find
119 // the closest match. This could be more elegant, using search methods
120 // and/or lookup tables, but the brute force method is not that much
121 // slower, and is more maintainable.
122 uint16_t DL = PCLK / (16 * baudrate);
123
124 uint8_t DivAddVal = 0;
125 uint8_t MulVal = 1;
126 int hit = 0;
127 uint16_t dlv;
128 uint8_t mv, dav;
129 if ((PCLK % (16 * baudrate)) != 0) { // Checking for zero remainder
130 int err_best = baudrate, b;
131 for (mv = 1; mv < 16 && !hit; mv++)
132 {
133 for (dav = 0; dav < mv; dav++)
134 {
135 // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
136 // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
137 // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
138 // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
139 // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
140
141 if ((mv * PCLK * 2) & 0x80000000) // 1 bit headroom
142 dlv = ((((2 * mv * PCLK) / (baudrate * (dav + mv))) / 16) + 1) / 2;
143 else // 2 bits headroom, use more precision
144 dlv = ((((4 * mv * PCLK) / (baudrate * (dav + mv))) / 32) + 1) / 2;
145
146 // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
147 if (dlv == 0)
148 dlv = 1;
149
150 // datasheet says if dav > 0 then DL must be >= 2
151 if ((dav > 0) && (dlv < 2))
152 dlv = 2;
153
154 // integer rearrangement of the baudrate equation (with rounding)
155 b = ((PCLK * mv / (dlv * (dav + mv) * 8)) + 1) / 2;
156
157 // check to see how we went
158 b = abs(b - baudrate);
159 if (b < err_best)
160 {
161 err_best = b;
162
163 DL = dlv;
164 MulVal = mv;
165 DivAddVal = dav;
166
167 if (b == baudrate)
168 {
169 hit = 1;
170 break;
171 }
172 }
173 }
174 }
175 }
176
177 // set LCR[DLAB] to enable writing to divider registers
178 obj->uart->LCR |= (1 << 7);
179
180 // set divider values
181 obj->uart->DLM = (DL >> 8) & 0xFF;
182 obj->uart->DLL = (DL >> 0) & 0xFF;
183 obj->uart->FDR = (uint32_t) DivAddVal << 0
184 | (uint32_t) MulVal << 4;
185
186 // clear LCR[DLAB]
187 obj->uart->LCR &= ~(1 << 7);
188 }
189
190 void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) {
191 MBED_ASSERT((stop_bits == 1) || (stop_bits == 2)); // 0: 1 stop bits, 1: 2 stop bits
192 MBED_ASSERT((data_bits > 4) && (data_bits < 9)); // 0: 5 data bits ... 3: 8 data bits
193 MBED_ASSERT((parity == ParityNone) || (parity == ParityOdd) || (parity == ParityEven) ||
194 (parity == ParityForced1) || (parity == ParityForced0));
195
196 stop_bits -= 1;
197 data_bits -= 5;
198
199 int parity_enable, parity_select;
200 switch (parity) {
201 case ParityNone: parity_enable = 0; parity_select = 0; break;
202 case ParityOdd : parity_enable = 1; parity_select = 0; break;
203 case ParityEven: parity_enable = 1; parity_select = 1; break;
204 case ParityForced1: parity_enable = 1; parity_select = 2; break;
205 case ParityForced0: parity_enable = 1; parity_select = 3; break;
206 default:
207 break;
208 }
209
210 obj->uart->LCR = data_bits << 0
211 | stop_bits << 2
212 | parity_enable << 3
213 | parity_select << 4;
214 }
215
216 /******************************************************************************
217 * INTERRUPTS HANDLING
218 ******************************************************************************/
219 static inline void uart_irq(uint32_t iir, uint32_t index) {
220 // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
221 SerialIrq irq_type;
222 switch (iir) {
223 case 1: irq_type = TxIrq; break;
224 case 2: irq_type = RxIrq; break;
225 default: return;
226 }
227
228 if (serial_irq_ids[index] != 0)
229 irq_handler(serial_irq_ids[index], irq_type);
230 }
231
232 void uart0_irq() {uart_irq((LPC_USART->IIR >> 1) & 0x7, 0);}
233
234 void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) {
235 irq_handler = handler;
236 serial_irq_ids[obj->index] = id;
237 }
238
239 void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
240 IRQn_Type irq_n = (IRQn_Type)0;
241 uint32_t vector = 0;
242 switch ((int)obj->uart) {
243 case UART_0: irq_n=USART_IRQn ; vector = (uint32_t)&uart0_irq; break;
244 }
245
246 if (enable) {
247 obj->uart->IER |= 1 << irq;
248 NVIC_SetVector(irq_n, vector);
249 NVIC_EnableIRQ(irq_n);
250 } else { // disable
251 int all_disabled = 0;
252 SerialIrq other_irq = (irq == RxIrq) ? (TxIrq) : (RxIrq);
253
254 obj->uart->IER &= ~(1 << irq);
255 all_disabled = (obj->uart->IER & (1 << other_irq)) == 0;
256
257 if (all_disabled)
258 NVIC_DisableIRQ(irq_n);
259 }
260 }
261
262 /******************************************************************************
263 * READ/WRITE
264 ******************************************************************************/
265 int serial_getc(serial_t *obj) {
266 while (!serial_readable(obj));
267 return obj->uart->RBR;
268 }
269
270 void serial_putc(serial_t *obj, int c) {
271 while (!serial_writable(obj));
272 obj->uart->THR = c;
273 }
274
275 int serial_readable(serial_t *obj) {
276 return obj->uart->LSR & 0x01;
277 }
278
279 int serial_writable(serial_t *obj) {
280 return obj->uart->LSR & 0x20;
281 }
282
283 void serial_clear(serial_t *obj) {
284 obj->uart->FCR = 1 << 1 // rx FIFO reset
285 | 1 << 2 // tx FIFO reset
286 | 0 << 6; // interrupt depth
287 }
288
289 void serial_pinout_tx(PinName tx) {
290 pinmap_pinout(tx, PinMap_UART_TX);
291 }
292
293 void serial_break_set(serial_t *obj) {
294 obj->uart->LCR |= (1 << 6);
295 }
296
297 void serial_break_clear(serial_t *obj) {
298 obj->uart->LCR &= ~(1 << 6);
299 }
300
Imprint / Impressum