Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
serial_api.c
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2006-2013 ARM Limited 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 #include "serial_api.h" 00017 00018 #if DEVICE_SERIAL 00019 00020 // math.h required for floating point operations for baud rate calculation 00021 #include <math.h> 00022 00023 #include <string.h> 00024 00025 #include "cmsis.h" 00026 #include "pinmap.h" 00027 #include "error.h" 00028 00029 /****************************************************************************** 00030 * INITIALIZATION 00031 ******************************************************************************/ 00032 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) 00033 00034 #define UART_NUM 4 00035 00036 static const PinMap PinMap_UART_TX[] = { 00037 {P0_0, UART_3, 2}, 00038 {P0_2, UART_0, 1}, 00039 {P0_10, UART_2, 1}, 00040 {P0_15, UART_1, 1}, 00041 {P0_25, UART_3, 3}, 00042 {P2_0 , UART_1, 2}, 00043 {P2_8 , UART_2, 2}, 00044 {P4_28, UART_3, 3}, 00045 {NC , NC , 0} 00046 }; 00047 00048 static const PinMap PinMap_UART_RX[] = { 00049 {P0_1 , UART_3, 2}, 00050 {P0_3 , UART_0, 1}, 00051 {P0_11, UART_2, 1}, 00052 {P0_16, UART_1, 1}, 00053 {P0_26, UART_3, 3}, 00054 {P2_1 , UART_1, 2}, 00055 {P2_9 , UART_2, 2}, 00056 {P4_29, UART_3, 3}, 00057 {NC , NC , 0} 00058 }; 00059 00060 00061 #elif defined(TARGET_LPC11U24) 00062 00063 #define UART_NUM 1 00064 00065 static const PinMap PinMap_UART_TX[] = { 00066 {P0_19, UART_0, 1}, 00067 {P1_13, UART_0, 3}, 00068 {P1_27, UART_0, 2}, 00069 { NC , NC , 0} 00070 }; 00071 00072 static const PinMap PinMap_UART_RX[] = { 00073 {P0_18, UART_0, 1}, 00074 {P1_14, UART_0, 3}, 00075 {P1_26, UART_0, 2}, 00076 {NC , NC , 0} 00077 }; 00078 00079 00080 #elif defined(TARGET_LPC812) 00081 00082 #define UART_NUM 3 00083 00084 static const SWM_Map SWM_UART_TX[] = { 00085 {0, 0}, 00086 {1, 8}, 00087 {2, 16}, 00088 }; 00089 00090 static const SWM_Map SWM_UART_RX[] = { 00091 {0, 8}, 00092 {1, 16}, 00093 {2, 24}, 00094 }; 00095 00096 // bit flags for used UARTs 00097 static unsigned char uart_used = 0; 00098 static int get_available_uart(void) { 00099 int i; 00100 for (i=0; i<3; i++) { 00101 if ((uart_used & (1 << i)) == 0) 00102 return i; 00103 } 00104 return -1; 00105 } 00106 00107 #define UART_EN (0x01<<0) 00108 00109 #define CTS_DELTA (0x01<<5) 00110 #define RXBRK (0x01<<10) 00111 #define DELTA_RXBRK (0x01<<11) 00112 00113 #define RXRDY (0x01<<0) 00114 #define TXRDY (0x01<<2) 00115 00116 static uint32_t UARTSysClk; 00117 #endif 00118 00119 static uint32_t serial_irq_ids[UART_NUM] = {0}; 00120 static uart_irq_handler irq_handler; 00121 00122 int stdio_uart_inited = 0; 00123 serial_t stdio_uart; 00124 00125 void serial_init(serial_t *obj, PinName tx, PinName rx) { 00126 int is_stdio_uart = 0; 00127 00128 #ifdef TARGET_LPC812 00129 int uart_n = get_available_uart(); 00130 if (uart_n == -1) { 00131 error("No available UART"); 00132 } 00133 obj->index = uart_n; 00134 obj->uart = (LPC_USART_TypeDef *)(LPC_USART0_BASE + (0x4000 * uart_n)); 00135 uart_used |= (1 << uart_n); 00136 00137 const SWM_Map *swm; 00138 uint32_t regVal; 00139 00140 swm = &SWM_UART_TX[uart_n]; 00141 regVal = LPC_SWM->PINASSIGN[swm->n] & ~(0xFF << swm->offset); 00142 LPC_SWM->PINASSIGN[swm->n] = regVal | (tx << swm->offset); 00143 00144 swm = &SWM_UART_RX[uart_n]; 00145 regVal = LPC_SWM->PINASSIGN[swm->n] & ~(0xFF << swm->offset); 00146 LPC_SWM->PINASSIGN[swm->n] = regVal | (rx << swm->offset); 00147 00148 /* uart clock divided by 1 */ 00149 LPC_SYSCON->UARTCLKDIV = 1; 00150 00151 /* disable uart interrupts */ 00152 NVIC_DisableIRQ((IRQn_Type )(UART0_IRQn + uart_n)); 00153 00154 /* Enable UART clock */ 00155 LPC_SYSCON->SYSAHBCLKCTRL |= (1 << (14 + uart_n)); 00156 00157 /* Peripheral reset control to UART, a "1" bring it out of reset. */ 00158 LPC_SYSCON->PRESETCTRL &= ~(0x1 << (3 + uart_n)); 00159 LPC_SYSCON->PRESETCTRL |= (0x1 << (3 + uart_n)); 00160 00161 UARTSysClk = SystemCoreClock / LPC_SYSCON->UARTCLKDIV; 00162 00163 // set default baud rate and format 00164 serial_baud (obj, 9600); 00165 serial_format(obj, 8, ParityNone, 1); 00166 00167 /* Clear all status bits. */ 00168 obj->uart->STAT = CTS_DELTA | DELTA_RXBRK; 00169 00170 /* enable uart interrupts */ 00171 NVIC_EnableIRQ((IRQn_Type )(UART0_IRQn + uart_n)); 00172 00173 /* Enable UART interrupt */ 00174 // obj->uart->INTENSET = RXRDY | TXRDY | DELTA_RXBRK; 00175 00176 /* Enable UART */ 00177 obj->uart->CFG |= UART_EN; 00178 00179 is_stdio_uart = ((tx == USBTX) && (rx == USBRX)); 00180 #else 00181 // determine the UART to use 00182 UARTName uart_tx = (UARTName)pinmap_peripheral(tx, PinMap_UART_TX); 00183 UARTName uart_rx = (UARTName)pinmap_peripheral(rx, PinMap_UART_RX); 00184 UARTName uart = (UARTName)pinmap_merge(uart_tx, uart_rx); 00185 if ((int)uart == NC) { 00186 error("Serial pinout mapping failed"); 00187 } 00188 00189 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) 00190 obj->uart = (LPC_UART_TypeDef *)uart; 00191 // enable power 00192 switch (uart) { 00193 case UART_0: LPC_SC->PCONP |= 1 << 3; break; 00194 case UART_1: LPC_SC->PCONP |= 1 << 4; break; 00195 case UART_2: LPC_SC->PCONP |= 1 << 24; break; 00196 case UART_3: LPC_SC->PCONP |= 1 << 25; break; 00197 } 00198 00199 #elif defined(TARGET_LPC11U24) 00200 obj->uart = (LPC_USART_Type *)uart; 00201 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12); 00202 00203 // [TODO] Consider more elegant approach 00204 // disconnect USBTX/RX mapping mux, for case when switching ports 00205 pin_function(USBTX, 0); 00206 pin_function(USBRX, 0); 00207 #endif 00208 00209 // enable fifos and default rx trigger level 00210 obj->uart->FCR = 1 << 0 // FIFO Enable - 0 = Disables, 1 = Enabled 00211 | 0 << 1 // Rx Fifo Reset 00212 | 0 << 2 // Tx Fifo Reset 00213 | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars 00214 00215 // disable irqs 00216 obj->uart->IER = 0 << 0 // Rx Data available irq enable 00217 | 0 << 1 // Tx Fifo empty irq enable 00218 | 0 << 2; // Rx Line Status irq enable 00219 00220 // set default baud rate and format 00221 serial_baud (obj, 9600); 00222 serial_format(obj, 8, ParityNone, 1); 00223 00224 // pinout the chosen uart 00225 pinmap_pinout(tx, PinMap_UART_TX); 00226 pinmap_pinout(rx, PinMap_UART_RX); 00227 00228 // set rx/tx pins in PullUp mode 00229 pin_mode(tx, PullUp); 00230 pin_mode(rx, PullUp); 00231 00232 switch (uart) { 00233 case UART_0: obj->index = 0; break; 00234 #if (UART_NUM > 1) 00235 case UART_1: obj->index = 1; break; 00236 #endif 00237 #if (UART_NUM > 2) 00238 case UART_2: obj->index = 2; break; 00239 #endif 00240 #if (UART_NUM > 3) 00241 case UART_3: obj->index = 3; break; 00242 #endif 00243 } 00244 00245 is_stdio_uart = (uart == STDIO_UART) ? (1) : (0); 00246 #endif 00247 00248 if (is_stdio_uart) { 00249 stdio_uart_inited = 1; 00250 memcpy(&stdio_uart, obj, sizeof(serial_t)); 00251 } 00252 } 00253 00254 void serial_free(serial_t *obj) { 00255 #ifdef TARGET_LPC812 00256 uart_used &= ~(1 << obj->index); 00257 #endif 00258 serial_irq_ids[obj->index] = 0; 00259 } 00260 00261 // serial_baud 00262 // set the baud rate, taking in to account the current SystemFrequency 00263 void serial_baud(serial_t *obj, int baudrate) { 00264 #ifdef TARGET_LPC812 00265 /* Integer divider: 00266 BRG = UARTSysClk/(Baudrate * 16) - 1 00267 00268 Frational divider: 00269 FRG = ((UARTSysClk / (Baudrate * 16 * (BRG + 1))) - 1) 00270 00271 where 00272 FRG = (LPC_SYSCON->UARTFRDADD + 1) / (LPC_SYSCON->UARTFRDSUB + 1) 00273 00274 (1) The easiest way is set SUB value to 256, -1 encoded, thus SUB 00275 register is 0xFF. 00276 (2) In ADD register value, depending on the value of UartSysClk, 00277 baudrate, BRG register value, and SUB register value, be careful 00278 about the order of multiplier and divider and make sure any 00279 multiplier doesn't exceed 32-bit boundary and any divider doesn't get 00280 down below one(integer 0). 00281 (3) ADD should be always less than SUB. 00282 */ 00283 obj->uart->BRG = UARTSysClk / 16 / baudrate - 1; 00284 00285 LPC_SYSCON->UARTFRGDIV = 0xFF; 00286 LPC_SYSCON->UARTFRGMULT = ( ((UARTSysClk / 16) * (LPC_SYSCON->UARTFRGDIV + 1)) / 00287 (baudrate * (obj->uart->BRG + 1)) 00288 ) - (LPC_SYSCON->UARTFRGDIV + 1); 00289 00290 #else 00291 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) 00292 // The LPC2300 and LPC1700 have a divider and a fractional divider to control the 00293 // baud rate. The formula is: 00294 // 00295 // Baudrate = (1 / PCLK) * 16 * DL * (1 + DivAddVal / MulVal) 00296 // where: 00297 // 1 < MulVal <= 15 00298 // 0 <= DivAddVal < 14 00299 // DivAddVal < MulVal 00300 // 00301 // set pclk to /1 00302 switch ((int)obj->uart) { 00303 case UART_0: LPC_SC->PCLKSEL0 &= ~(0x3 << 6); LPC_SC->PCLKSEL0 |= (0x1 << 6); break; 00304 case UART_1: LPC_SC->PCLKSEL0 &= ~(0x3 << 8); LPC_SC->PCLKSEL0 |= (0x1 << 8); break; 00305 case UART_2: LPC_SC->PCLKSEL1 &= ~(0x3 << 16); LPC_SC->PCLKSEL1 |= (0x1 << 16); break; 00306 case UART_3: LPC_SC->PCLKSEL1 &= ~(0x3 << 18); LPC_SC->PCLKSEL1 |= (0x1 << 18); break; 00307 default: error("serial_baud"); break; 00308 } 00309 00310 uint32_t PCLK = SystemCoreClock ; 00311 00312 #elif defined(TARGET_LPC11U24) 00313 LPC_SYSCON->UARTCLKDIV = 0x1; 00314 uint32_t PCLK = SystemCoreClock ; 00315 #endif 00316 00317 // First we check to see if the basic divide with no DivAddVal/MulVal 00318 // ratio gives us an integer result. If it does, we set DivAddVal = 0, 00319 // MulVal = 1. Otherwise, we search the valid ratio value range to find 00320 // the closest match. This could be more elegant, using search methods 00321 // and/or lookup tables, but the brute force method is not that much 00322 // slower, and is more maintainable. 00323 uint16_t DL = PCLK / (16 * baudrate); 00324 00325 uint8_t DivAddVal = 0; 00326 uint8_t MulVal = 1; 00327 int hit = 0; 00328 uint16_t dlv; 00329 uint8_t mv, dav; 00330 if ((PCLK % (16 * baudrate)) != 0) { // Checking for zero remainder 00331 float err_best = (float) baudrate; 00332 uint16_t dlmax = DL; 00333 for ( dlv = (dlmax/2); (dlv <= dlmax) && !hit; dlv++) { 00334 for ( mv = 1; mv <= 15; mv++) { 00335 for ( dav = 1; dav < mv; dav++) { 00336 float ratio = 1.0 + ((float) dav / (float) mv); 00337 float calcbaud = (float)PCLK / (16.0 * (float) dlv * ratio); 00338 float err = fabs(((float) baudrate - calcbaud) / (float) baudrate); 00339 if (err < err_best) { 00340 DL = dlv; 00341 DivAddVal = dav; 00342 MulVal = mv; 00343 err_best = err; 00344 if (err < 0.001) { 00345 hit = 1; 00346 } 00347 } 00348 } 00349 } 00350 } 00351 } 00352 00353 // set LCR[DLAB] to enable writing to divider registers 00354 obj->uart->LCR |= (1 << 7); 00355 00356 // set divider values 00357 obj->uart->DLM = (DL >> 8) & 0xFF; 00358 obj->uart->DLL = (DL >> 0) & 0xFF; 00359 obj->uart->FDR = (uint32_t) DivAddVal << 0 00360 | (uint32_t) MulVal << 4; 00361 00362 // clear LCR[DLAB] 00363 obj->uart->LCR &= ~(1 << 7); 00364 #endif 00365 } 00366 00367 void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) { 00368 // 0: 1 stop bits, 1: 2 stop bits 00369 if (stop_bits != 1 && stop_bits != 2) { 00370 error("Invalid stop bits specified"); 00371 } 00372 stop_bits -= 1; 00373 00374 #ifdef TARGET_LPC812 00375 // 0: 7 data bits ... 2: 9 data bits 00376 if (data_bits < 7 || data_bits > 9) { 00377 error("Invalid number of bits (%d) in serial format, should be 7..9", data_bits); 00378 } 00379 data_bits -= 7; 00380 00381 int paritysel; 00382 switch (parity) { 00383 case ParityNone: paritysel = 0; break; 00384 case ParityEven: paritysel = 2; break; 00385 case ParityOdd : paritysel = 3; break; 00386 default: 00387 error("Invalid serial parity setting"); 00388 return; 00389 } 00390 00391 obj->uart->CFG = (data_bits << 2) 00392 | (paritysel << 4) 00393 | (stop_bits << 6); 00394 #else 00395 // 0: 5 data bits ... 3: 8 data bits 00396 if (data_bits < 5 || data_bits > 8) { 00397 error("Invalid number of bits (%d) in serial format, should be 5..8", data_bits); 00398 } 00399 data_bits -= 5; 00400 00401 int parity_enable, parity_select; 00402 switch (parity) { 00403 case ParityNone: parity_enable = 0; parity_select = 0; break; 00404 case ParityOdd : parity_enable = 1; parity_select = 0; break; 00405 case ParityEven: parity_enable = 1; parity_select = 1; break; 00406 case ParityForced1: parity_enable = 1; parity_select = 2; break; 00407 case ParityForced0: parity_enable = 1; parity_select = 3; break; 00408 default: 00409 error("Invalid serial parity setting"); 00410 return; 00411 } 00412 00413 obj->uart->LCR = data_bits << 0 00414 | stop_bits << 2 00415 | parity_enable << 3 00416 | parity_select << 4; 00417 #endif 00418 } 00419 00420 /****************************************************************************** 00421 * INTERRUPTS HANDLING 00422 ******************************************************************************/ 00423 static inline void uart_irq(uint32_t iir, uint32_t index) { 00424 // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling 00425 SerialIrq irq_type; 00426 switch (iir) { 00427 case 1: irq_type = TxIrq; break; 00428 case 2: irq_type = RxIrq; break; 00429 default: return; 00430 } 00431 00432 if (serial_irq_ids[index] != 0) 00433 irq_handler(serial_irq_ids[index], irq_type); 00434 } 00435 00436 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) 00437 void uart0_irq() {uart_irq((LPC_UART0->IIR >> 1) & 0x7, 0);} 00438 void uart1_irq() {uart_irq((LPC_UART1->IIR >> 1) & 0x7, 1);} 00439 void uart2_irq() {uart_irq((LPC_UART2->IIR >> 1) & 0x7, 2);} 00440 void uart3_irq() {uart_irq((LPC_UART3->IIR >> 1) & 0x7, 3);} 00441 00442 #elif defined(TARGET_LPC11U24) 00443 void uart0_irq() {uart_irq((LPC_USART->IIR >> 1) & 0x7, 0);} 00444 00445 #elif defined(TARGET_LPC812) 00446 void uart0_irq() {uart_irq((LPC_USART0->STAT & (1 << 2)) ? 2 : 1, 0);} 00447 void uart1_irq() {uart_irq((LPC_USART1->STAT & (1 << 2)) ? 2 : 1, 1);} 00448 void uart2_irq() {uart_irq((LPC_USART2->STAT & (1 << 2)) ? 2 : 1, 2);} 00449 00450 #endif 00451 00452 void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) { 00453 irq_handler = handler; 00454 serial_irq_ids[obj->index] = id; 00455 } 00456 00457 void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) { 00458 IRQn_Type irq_n = (IRQn_Type )0; 00459 uint32_t vector = 0; 00460 switch ((int)obj->uart) { 00461 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) 00462 case UART_0: irq_n=UART0_IRQn ; vector = (uint32_t)&uart0_irq; break; 00463 case UART_1: irq_n=UART1_IRQn ; vector = (uint32_t)&uart1_irq; break; 00464 case UART_2: irq_n=UART2_IRQn ; vector = (uint32_t)&uart2_irq; break; 00465 case UART_3: irq_n=UART3_IRQn ; vector = (uint32_t)&uart3_irq; break; 00466 #elif defined(TARGET_LPC11U24) 00467 case UART_0: irq_n=UART_IRQn ; vector = (uint32_t)&uart0_irq; break; 00468 #elif defined(TARGET_LPC812) 00469 case LPC_USART0_BASE: irq_n=UART0_IRQn ; vector = (uint32_t)&uart0_irq; break; 00470 case LPC_USART1_BASE: irq_n=UART1_IRQn ; vector = (uint32_t)&uart1_irq; break; 00471 case LPC_USART2_BASE: irq_n=UART2_IRQn ; vector = (uint32_t)&uart2_irq; break; 00472 #endif 00473 } 00474 00475 if (enable) { 00476 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24) 00477 obj->uart->IER |= 1 << irq; 00478 #elif defined(TARGET_LPC812) 00479 obj->uart->INTENSET = (1 << ((irq == RxIrq) ? 0 : 2)); 00480 #endif 00481 NVIC_SetVector(irq_n, vector); 00482 NVIC_EnableIRQ(irq_n); 00483 } else { // disable 00484 int all_disabled = 0; 00485 SerialIrq other_irq = (irq == RxIrq) ? (TxIrq) : (RxIrq); 00486 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC11U24) 00487 obj->uart->IER &= ~(1 << irq); 00488 all_disabled = (obj->uart->IER & (1 << other_irq)) == 0; 00489 #elif defined(TARGET_LPC812) 00490 obj->uart->INTENSET &= ~(1 << ((irq == RxIrq) ? 0 : 2)); 00491 all_disabled = (obj->uart->INTENSET & (1 << ((other_irq == RxIrq) ? 0 : 2))) == 0; 00492 #endif 00493 if (all_disabled) 00494 NVIC_DisableIRQ(irq_n); 00495 } 00496 } 00497 00498 /****************************************************************************** 00499 * READ/WRITE 00500 ******************************************************************************/ 00501 int serial_getc(serial_t *obj) { 00502 while (!serial_readable(obj)); 00503 #ifdef TARGET_LPC812 00504 return obj->uart->RXDATA; 00505 #else 00506 return obj->uart->RBR; 00507 #endif 00508 } 00509 00510 void serial_putc(serial_t *obj, int c) { 00511 while (!serial_writable(obj)); 00512 #ifdef TARGET_LPC812 00513 obj->uart->TXDATA = c; 00514 #else 00515 obj->uart->THR = c; 00516 #endif 00517 } 00518 00519 int serial_readable(serial_t *obj) { 00520 #ifdef TARGET_LPC812 00521 return obj->uart->STAT & RXRDY; 00522 #else 00523 return obj->uart->LSR & 0x01; 00524 #endif 00525 } 00526 00527 int serial_writable(serial_t *obj) { 00528 #ifdef TARGET_LPC812 00529 return obj->uart->STAT & TXRDY; 00530 #else 00531 return obj->uart->LSR & 0x20; 00532 #endif 00533 } 00534 00535 void serial_clear(serial_t *obj) { 00536 #ifdef TARGET_LPC812 00537 // [TODO] 00538 #else 00539 obj->uart->FCR = 1 << 1 // rx FIFO reset 00540 | 1 << 2 // tx FIFO reset 00541 | 0 << 6; // interrupt depth 00542 #endif 00543 } 00544 00545 void serial_pinout_tx(PinName tx) { 00546 #ifdef TARGET_LPC812 00547 00548 #else 00549 pinmap_pinout(tx, PinMap_UART_TX); 00550 #endif 00551 } 00552 00553 #endif
Generated on Thu Jul 14 2022 07:43:04 by
1.7.2