fix for mbed lib issue 3 (i2c problem) see also https://mbed.org/users/mbed_official/code/mbed/issues/3 affected implementations: LPC812, LPC11U24, LPC1768, LPC2368, LPC4088

Fork of mbed-src by mbed official

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 // math.h required for floating point operations for baud rate calculation
00017 #include <math.h>
00018 #include <string.h>
00019 
00020 #include "serial_api.h"
00021 #include "cmsis.h"
00022 #include "pinmap.h"
00023 #include "error.h"
00024 
00025 /******************************************************************************
00026  * INITIALIZATION
00027  ******************************************************************************/
00028 static const PinMap PinMap_UART_TX[] = {
00029     {P0_0,  UART_3, 2},
00030     {P0_2,  UART_0, 1},
00031     {P0_10, UART_2, 1},
00032     {P0_15, UART_1, 1},
00033     {P1_29, UART_4, 5},
00034     {P0_25, UART_3, 3},
00035     {P2_0 , UART_1, 2},
00036     {P2_8 , UART_2, 2},
00037     {P3_16, UART_1, 3},
00038     {P4_22, UART_2, 2},
00039     {P4_28, UART_3, 2},
00040     {P5_4,  UART_4, 4},
00041     {NC   , NC    , 0}
00042 };
00043 
00044 static const PinMap PinMap_UART_RX[] = {
00045     {P0_1 , UART_3, 2},
00046     {P0_3 , UART_0, 1},
00047     {P0_11, UART_2, 1},
00048     {P0_16, UART_1, 1},
00049     {P0_26, UART_3, 3},
00050     {P2_1 , UART_1, 2},
00051     {P2_9 , UART_2, 2},
00052     {P3_17, UART_1, 3},
00053     {P4_23, UART_2, 2},
00054     {P4_29, UART_3, 2},
00055     {P5_3,  UART_4, 4},
00056     {NC   , NC    , 0}
00057 };
00058 
00059 #define UART_NUM    5
00060 
00061 static uint32_t serial_irq_ids[UART_NUM] = {0};
00062 static uart_irq_handler irq_handler;
00063 
00064 int stdio_uart_inited = 0;
00065 serial_t stdio_uart;
00066 
00067 void serial_init(serial_t *obj, PinName tx, PinName rx) {
00068     int is_stdio_uart = 0;
00069     
00070     // determine the UART to use
00071     UARTName uart_tx = (UARTName)pinmap_peripheral(tx, PinMap_UART_TX);
00072     UARTName uart_rx = (UARTName)pinmap_peripheral(rx, PinMap_UART_RX);
00073     UARTName uart = (UARTName)pinmap_merge(uart_tx, uart_rx);
00074     if ((int)uart == NC) {
00075         error("Serial pinout mapping failed");
00076     }
00077     
00078     obj->uart = (LPC_UART_TypeDef *)uart;
00079     // enable power
00080     switch (uart) {
00081         case UART_0: LPC_SC->PCONP |= 1 <<  3; break;
00082         case UART_1: LPC_SC->PCONP |= 1 <<  4; break;
00083         case UART_2: LPC_SC->PCONP |= 1 << 24; break;
00084         case UART_3: LPC_SC->PCONP |= 1 << 25; break;
00085         case UART_4: LPC_SC->PCONP |= 1 <<  8; break;
00086     }
00087     
00088     // enable fifos and default rx trigger level
00089     obj->uart->FCR = 1 << 0  // FIFO Enable - 0 = Disables, 1 = Enabled
00090                    | 0 << 1  // Rx Fifo Reset
00091                    | 0 << 2  // Tx Fifo Reset
00092                    | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
00093 
00094     // disable irqs
00095     obj->uart->IER = 0 << 0  // Rx Data available irq enable
00096                    | 0 << 1  // Tx Fifo empty irq enable
00097                    | 0 << 2; // Rx Line Status irq enable
00098     
00099     // set default baud rate and format
00100     serial_baud  (obj, 9600);
00101     serial_format(obj, 8, ParityNone, 1);
00102     
00103     // pinout the chosen uart
00104     pinmap_pinout(tx, PinMap_UART_TX);
00105     pinmap_pinout(rx, PinMap_UART_RX);
00106     
00107     // set rx/tx pins in PullUp mode
00108     pin_mode(tx, PullUp);
00109     pin_mode(rx, PullUp);
00110     
00111     switch (uart) {
00112         case UART_0: obj->index = 0; break;
00113         case UART_1: obj->index = 1; break;
00114         case UART_2: obj->index = 2; break;
00115         case UART_3: obj->index = 3; break;
00116         case UART_4: obj->index = 4; break;
00117     }
00118     
00119     is_stdio_uart = (uart == STDIO_UART) ? (1) : (0);
00120     
00121     if (is_stdio_uart) {
00122         stdio_uart_inited = 1;
00123         memcpy(&stdio_uart, obj, sizeof(serial_t));
00124     }
00125 }
00126 
00127 void serial_free(serial_t *obj) {
00128     serial_irq_ids[obj->index] = 0;
00129 }
00130 
00131 // serial_baud
00132 // set the baud rate, taking in to account the current SystemFrequency
00133 void serial_baud(serial_t *obj, int baudrate) {
00134     uint32_t PCLK = PeripheralClock ;
00135     
00136     // First we check to see if the basic divide with no DivAddVal/MulVal
00137     // ratio gives us an integer result. If it does, we set DivAddVal = 0,
00138     // MulVal = 1. Otherwise, we search the valid ratio value range to find
00139     // the closest match. This could be more elegant, using search methods
00140     // and/or lookup tables, but the brute force method is not that much
00141     // slower, and is more maintainable.
00142     uint16_t DL = PCLK / (16 * baudrate);
00143 
00144     uint8_t DivAddVal = 0;
00145     uint8_t MulVal = 1;
00146     int hit = 0;
00147     uint16_t dlv;
00148     uint8_t mv, dav;
00149     if ((PCLK % (16 * baudrate)) != 0) {     // Checking for zero remainder
00150         float err_best = (float) baudrate;
00151         uint16_t dlmax = DL;
00152         for ( dlv = (dlmax/2); (dlv <= dlmax) && !hit; dlv++) {
00153             for ( mv = 1; mv <= 15; mv++) {
00154                 for ( dav = 1; dav < mv; dav++) {
00155                     float ratio = 1.0f + ((float) dav / (float) mv);
00156                     float calcbaud = (float)PCLK / (16.0f * (float) dlv * ratio);
00157                     float err = fabs(((float) baudrate - calcbaud) / (float) baudrate);
00158                     if (err < err_best) {
00159                         DL = dlv;
00160                         DivAddVal = dav;
00161                         MulVal = mv;
00162                         err_best = err;
00163                         if (err < 0.001f) {
00164                             hit = 1;
00165                         }
00166                     }
00167                 }
00168             }
00169         }
00170     }
00171     
00172     // set LCR[DLAB] to enable writing to divider registers
00173     obj->uart->LCR |= (1 << 7);
00174     
00175     // set divider values
00176     obj->uart->DLM = (DL >> 8) & 0xFF;
00177     obj->uart->DLL = (DL >> 0) & 0xFF;
00178     obj->uart->FDR = (uint32_t) DivAddVal << 0
00179                    | (uint32_t) MulVal    << 4;
00180     
00181     // clear LCR[DLAB]
00182     obj->uart->LCR &= ~(1 << 7);
00183 }
00184 
00185 void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) {
00186     // 0: 1 stop bits, 1: 2 stop bits
00187     if (stop_bits != 1 && stop_bits != 2) {
00188         error("Invalid stop bits specified");
00189     }
00190     stop_bits -= 1;
00191     
00192     // 0: 5 data bits ... 3: 8 data bits
00193     if (data_bits < 5 || data_bits > 8) {
00194         error("Invalid number of bits (%d) in serial format, should be 5..8", data_bits);
00195     }
00196     data_bits -= 5;
00197 
00198     int parity_enable, parity_select;
00199     switch (parity) {
00200         case ParityNone: parity_enable = 0; parity_select = 0; break;
00201         case ParityOdd : parity_enable = 1; parity_select = 0; break;
00202         case ParityEven: parity_enable = 1; parity_select = 1; break;
00203         case ParityForced1: parity_enable = 1; parity_select = 2; break;
00204         case ParityForced0: parity_enable = 1; parity_select = 3; break;
00205         default:
00206             error("Invalid serial parity setting");
00207             return;
00208     }
00209     
00210     obj->uart->LCR = data_bits            << 0
00211                    | stop_bits            << 2
00212                    | parity_enable        << 3
00213                    | parity_select        << 4;
00214 }
00215 
00216 /******************************************************************************
00217  * INTERRUPTS HANDLING
00218  ******************************************************************************/
00219 static inline void uart_irq(uint32_t iir, uint32_t index) {
00220     // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
00221     SerialIrq irq_type;
00222     switch (iir) {
00223         case 1: irq_type = TxIrq; break;
00224         case 2: irq_type = RxIrq; break;
00225         default: return;
00226     }
00227     
00228     if (serial_irq_ids[index] != 0)
00229         irq_handler(serial_irq_ids[index], irq_type);
00230 }
00231 
00232 void uart0_irq() {uart_irq((LPC_UART0->IIR >> 1) & 0x7, 0);}
00233 void uart1_irq() {uart_irq((LPC_UART1->IIR >> 1) & 0x7, 1);}
00234 void uart2_irq() {uart_irq((LPC_UART2->IIR >> 1) & 0x7, 2);}
00235 void uart3_irq() {uart_irq((LPC_UART3->IIR >> 1) & 0x7, 3);}
00236 void uart4_irq() {uart_irq((LPC_UART4->IIR >> 1) & 0x7, 4);}
00237 
00238 void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) {
00239     irq_handler = handler;
00240     serial_irq_ids[obj->index] = id;
00241 }
00242 
00243 void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) {
00244     IRQn_Type  irq_n = (IRQn_Type )0;
00245     uint32_t vector = 0;
00246     switch ((int)obj->uart) {
00247         case UART_0: irq_n=UART0_IRQn; vector = (uint32_t)&uart0_irq; break;
00248         case UART_1: irq_n=UART1_IRQn; vector = (uint32_t)&uart1_irq; break;
00249         case UART_2: irq_n=UART2_IRQn; vector = (uint32_t)&uart2_irq; break;
00250         case UART_3: irq_n=UART3_IRQn ; vector = (uint32_t)&uart3_irq; break;
00251         case UART_4: irq_n=UART4_IRQn ; vector = (uint32_t)&uart4_irq; break;
00252     }
00253     
00254     if (enable) {
00255         obj->uart->IER |= 1 << irq;
00256         NVIC_SetVector(irq_n, vector);
00257         NVIC_EnableIRQ(irq_n);
00258     } else { // disable
00259         int all_disabled = 0;
00260         SerialIrq other_irq = (irq == RxIrq) ? (TxIrq) : (RxIrq);
00261         obj->uart->IER &= ~(1 << irq);
00262         all_disabled = (obj->uart->IER & (1 << other_irq)) == 0;
00263         if (all_disabled)
00264             NVIC_DisableIRQ(irq_n);
00265     }
00266 }
00267 
00268 /******************************************************************************
00269  * READ/WRITE
00270  ******************************************************************************/
00271 int serial_getc(serial_t *obj) {
00272     while (!serial_readable(obj));
00273     return obj->uart->RBR;
00274 }
00275 
00276 void serial_putc(serial_t *obj, int c) {
00277     while (!serial_writable(obj));
00278     obj->uart->THR = c;
00279     
00280     uint32_t lsr = obj->uart->LSR;
00281     lsr = lsr;
00282     uint32_t thr = obj->uart->THR;
00283     thr = thr;
00284 }
00285 
00286 int serial_readable(serial_t *obj) {
00287     return obj->uart->LSR & 0x01;
00288 }
00289 
00290 int serial_writable(serial_t *obj) {
00291     return obj->uart->LSR & 0x20;
00292 }
00293 
00294 void serial_clear(serial_t *obj) {
00295     obj->uart->FCR = 1 << 1  // rx FIFO reset
00296                    | 1 << 2  // tx FIFO reset
00297                    | 0 << 6; // interrupt depth
00298 }
00299 
00300 void serial_pinout_tx(PinName tx) {
00301     pinmap_pinout(tx, PinMap_UART_TX);
00302 }