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