Tim Barry / mbed_blinky_offset
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers serial_api.c Source File

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