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