mbed library sources

Fork of mbed-src by mbed official

Revision:
220:aa774fb95d17
Parent:
186:2e805bf06ee4
Child:
227:7bd0639b8911
--- a/targets/hal/TARGET_NXP/TARGET_LPC11U6X/serial_api.c	Mon Jun 02 09:45:07 2014 +0100
+++ b/targets/hal/TARGET_NXP/TARGET_LPC11U6X/serial_api.c	Tue Jun 03 10:30:56 2014 +0100
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 // math.h required for floating point operations for baud rate calculation
 #include <math.h>
 #include <string.h>
@@ -24,22 +25,35 @@
 #include "error.h"
 
 #if DEVICE_SERIAL
-#warning "[TODO] support from UART_1 to UART_4"
+
 /******************************************************************************
  * INITIALIZATION
  ******************************************************************************/
 
-#define UART_NUM    1
+#define UART_NUM      5
+
+// CFG
+#define UART_EN       (0x01<<0)
+
+// CTL
+#define TXBRKEN       (0x01<<1)
+
+// STAT
+#define RXRDY         (0x01<<0)
+#define TXRDY         (0x01<<2)
+#define DELTACTS      (0x01<<5)
+#define RXBRK         (0x01<<10)
+#define DELTARXBRK    (0x01<<11)
 
 static const PinMap PinMap_UART_TX[] = {
     {P0_19, UART_0, 1},
     {P1_18, UART_0, 2},
     {P1_27, UART_0, 2},
-//    {P1_18, UART_1, 2},
-//    {P1_0 , UART_2, 3},
-//    {P1_23, UART_2, 3},
-//    {P2_4 , UART_3, 1},
-//    {P2_12, UART_4, 1},
+    {P1_8 , UART_1, 2},
+    {P1_0 , UART_2, 3},
+    {P1_23, UART_2, 3},
+    {P2_4 , UART_3, 1},
+    {P2_12, UART_4, 1},
     { NC  , NC    , 0}
 };
 
@@ -47,11 +61,11 @@
     {P0_18, UART_0, 1},
     {P1_17, UART_0, 2},
     {P1_26, UART_0, 2},
-//    {P1_2 , UART_1, 3},
-//    {P0_20, UART_2, 2},
-//    {P1_6 , UART_2, 2},
-//    {P2_3 , UART_3, 1},
-//    {P2_11, UART_4, 1},
+    {P1_2 , UART_1, 3},
+    {P0_20, UART_2, 2},
+    {P1_6 , UART_2, 2},
+    {P2_3 , UART_3, 1},
+    {P2_11, UART_4, 1},
     {NC   , NC    , 0}
 };
 
@@ -72,27 +86,55 @@
         error("Serial pinout mapping failed");
     }
     
-    obj->uart = (LPC_USART0_Type *)uart;
-    LPC_SYSCON->SYSAHBCLKCTRL |= ((1<<12) | (1<<20) | (1<<21) | (1<<22));
-    
-    // [TODO] Consider more elegant approach
-    // disconnect USBTX/RX mapping mux, for case when switching ports
-#ifdef USBTX
-    pin_function(USBTX, 0);
-    pin_function(USBRX, 0);
-#endif
+    switch (uart) {
+        case UART_0:
+            obj->index = 0;
+            LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 12);
+            break;
+        case UART_1:
+            obj->index = 1;
+            LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 20);
+            LPC_SYSCON->PRESETCTRL |= (1 << 5);
+            break;
+        case UART_2:
+            obj->index = 2;
+            LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 21);
+            LPC_SYSCON->PRESETCTRL |= (1 << 6);
+            break;
+        case UART_3:
+            obj->index = 3;
+            LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 22);
+            LPC_SYSCON->PRESETCTRL |= (1 << 7);
+            break;
+        case UART_4:
+            obj->index = 4;
+            LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 22);
+            LPC_SYSCON->PRESETCTRL |= (1 << 8);
+            break;
+    }
 
-    // enable fifos and default rx trigger level
-    obj->uart->FCR = 1 << 0  // FIFO Enable - 0 = Disables, 1 = Enabled
-                   | 0 << 1  // Rx Fifo Clear
-                   | 0 << 2  // Tx Fifo Clear
-                   | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
+    if (obj->index == 0)
+        obj->uart = (LPC_USART0_Type *)uart;
+    else
+        obj->mini_uart = (LPC_USART4_Type *)uart;
     
-    // disable irqs
-    obj->uart->IER = 0 << 0  // Rx Data available irq enable
-                   | 0 << 1  // Tx Fifo empty irq enable
-                   | 0 << 2; // Rx Line Status irq enable
-    
+    if (obj->index == 0) {
+        // enable fifos and default rx trigger level
+        obj->uart->FCR = 1 << 0  // FIFO Enable - 0 = Disables, 1 = Enabled
+                       | 0 << 1  // Rx Fifo Clear
+                       | 0 << 2  // Tx Fifo Clear
+                       | 0 << 6; // Rx irq trigger level - 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars
+        // disable irqs
+        obj->uart->IER = 0 << 0  // Rx Data available irq enable
+                       | 0 << 1  // Tx Fifo empty irq enable
+                       | 0 << 2; // Rx Line Status irq enable
+    }
+    else {
+        // Clear all status bits
+        obj->mini_uart->STAT = (DELTACTS | DELTARXBRK);
+        // Enable UART
+        obj->mini_uart->CFG |= UART_EN;
+    }
     // set default baud rate and format
     serial_baud  (obj, 9600);
     serial_format(obj, 8, ParityNone, 1);
@@ -105,17 +147,9 @@
     pin_mode(tx, PullUp);
     pin_mode(rx, PullUp);
     
-    switch (uart) {
-        case UART_0: obj->index = 0; break;
-        case UART_1: obj->index = 1; break;
-        case UART_2: obj->index = 2; break;
-        case UART_3: obj->index = 3; break;
-        case UART_4: obj->index = 4; break;
-    }
-    
     is_stdio_uart = (uart == STDIO_UART) ? (1) : (0);
     
-    if (is_stdio_uart) {
+    if (is_stdio_uart && (obj->index == 0)) {
         stdio_uart_inited = 1;
         memcpy(&stdio_uart, obj, sizeof(serial_t));
     }
@@ -128,80 +162,93 @@
 // serial_baud
 // set the baud rate, taking in to account the current SystemFrequency
 void serial_baud(serial_t *obj, int baudrate) {
-    LPC_SYSCON->USART0CLKDIV = 0x1;
-    uint32_t PCLK = SystemCoreClock;
-    // First we check to see if the basic divide with no DivAddVal/MulVal
-    // ratio gives us an integer result. If it does, we set DivAddVal = 0,
-    // MulVal = 1. Otherwise, we search the valid ratio value range to find
-    // the closest match. This could be more elegant, using search methods
-    // and/or lookup tables, but the brute force method is not that much
-    // slower, and is more maintainable.
-    uint16_t DL = PCLK / (16 * baudrate);
-    
-    uint8_t DivAddVal = 0;
-    uint8_t MulVal = 1;
-    int hit = 0;
-    uint16_t dlv;
-    uint8_t mv, dav;
-    if ((PCLK % (16 * baudrate)) != 0) {     // Checking for zero remainder
-        int err_best = baudrate, b;
-        for (mv = 1; mv < 16 && !hit; mv++)
-        {
-            for (dav = 0; dav < mv; dav++)
+    LPC_SYSCON->USART0CLKDIV = 1;
+    LPC_SYSCON->FRGCLKDIV = 1;
+
+    if (obj->index == 0) {
+        uint32_t PCLK = SystemCoreClock;
+        // First we check to see if the basic divide with no DivAddVal/MulVal
+        // ratio gives us an integer result. If it does, we set DivAddVal = 0,
+        // MulVal = 1. Otherwise, we search the valid ratio value range to find
+        // the closest match. This could be more elegant, using search methods
+        // and/or lookup tables, but the brute force method is not that much
+        // slower, and is more maintainable.
+        uint16_t DL = PCLK / (16 * baudrate);
+        
+        uint8_t DivAddVal = 0;
+        uint8_t MulVal = 1;
+        int hit = 0;
+        uint16_t dlv;
+        uint8_t mv, dav;
+        if ((PCLK % (16 * baudrate)) != 0) {     // Checking for zero remainder
+            int err_best = baudrate, b;
+            for (mv = 1; mv < 16 && !hit; mv++)
             {
-                // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
-                // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
-                // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
-                // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
-                // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
+                for (dav = 0; dav < mv; dav++)
+                {
+                    // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
+                    // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
+                    // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
+                    // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
+                    // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
 
-                if ((mv * PCLK * 2) & 0x80000000) // 1 bit headroom
-                    dlv = ((((2 * mv * PCLK) / (baudrate * (dav + mv))) / 16) + 1) / 2;
-                else // 2 bits headroom, use more precision
-                    dlv = ((((4 * mv * PCLK) / (baudrate * (dav + mv))) / 32) + 1) / 2;
+                    if ((mv * PCLK * 2) & 0x80000000) // 1 bit headroom
+                        dlv = ((((2 * mv * PCLK) / (baudrate * (dav + mv))) / 16) + 1) / 2;
+                    else // 2 bits headroom, use more precision
+                        dlv = ((((4 * mv * PCLK) / (baudrate * (dav + mv))) / 32) + 1) / 2;
 
-                // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
-                if (dlv == 0)
-                    dlv = 1;
+                    // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
+                    if (dlv == 0)
+                        dlv = 1;
 
-                // datasheet says if dav > 0 then DL must be >= 2
-                if ((dav > 0) && (dlv < 2))
-                    dlv = 2;
+                    // datasheet says if dav > 0 then DL must be >= 2
+                    if ((dav > 0) && (dlv < 2))
+                        dlv = 2;
+
+                    // integer rearrangement of the baudrate equation (with rounding)
+                    b = ((PCLK * mv / (dlv * (dav + mv) * 8)) + 1) / 2;
 
-                // integer rearrangement of the baudrate equation (with rounding)
-                b = ((PCLK * mv / (dlv * (dav + mv) * 8)) + 1) / 2;
-
-                // check to see how we went
-                b = abs(b - baudrate);
-                if (b < err_best)
-                {
-                    err_best  = b;
+                    // check to see how we went
+                    b = abs(b - baudrate);
+                    if (b < err_best)
+                    {
+                        err_best  = b;
 
-                    DL        = dlv;
-                    MulVal    = mv;
-                    DivAddVal = dav;
+                        DL        = dlv;
+                        MulVal    = mv;
+                        DivAddVal = dav;
 
-                    if (b == baudrate)
-                    {
-                        hit = 1;
-                        break;
+                        if (b == baudrate)
+                        {
+                            hit = 1;
+                            break;
+                        }
                     }
                 }
             }
         }
+        
+        // set LCR[DLAB] to enable writing to divider registers
+        obj->uart->LCR |= (1 << 7);
+        
+        // set divider values
+        obj->uart->DLM = (DL >> 8) & 0xFF;
+        obj->uart->DLL = (DL >> 0) & 0xFF;
+        obj->uart->FDR = (uint32_t) DivAddVal << 0
+                       | (uint32_t) MulVal    << 4;
+        
+        // clear LCR[DLAB]
+        obj->uart->LCR &= ~(1 << 7);
     }
-    
-    // set LCR[DLAB] to enable writing to divider registers
-    obj->uart->LCR |= (1 << 7);
-    
-    // set divider values
-    obj->uart->DLM = (DL >> 8) & 0xFF;
-    obj->uart->DLL = (DL >> 0) & 0xFF;
-    obj->uart->FDR = (uint32_t) DivAddVal << 0
-                   | (uint32_t) MulVal    << 4;
-    
-    // clear LCR[DLAB]
-    obj->uart->LCR &= ~(1 << 7);
+    else {
+        uint32_t UARTSysClk = SystemCoreClock / LPC_SYSCON->FRGCLKDIV;
+        obj->mini_uart->BRG = UARTSysClk / 16 / baudrate - 1;
+        
+        LPC_SYSCON->UARTFRGDIV = 0xFF;
+        LPC_SYSCON->UARTFRGMULT = ( ((UARTSysClk / 16) * (LPC_SYSCON->UARTFRGDIV + 1)) /
+                                    (baudrate * (obj->mini_uart->BRG + 1))
+                                  ) - (LPC_SYSCON->UARTFRGDIV + 1);
+    }
 }
 
 void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) {
@@ -211,35 +258,57 @@
     }
     stop_bits -= 1;
     
-    // 0: 5 data bits ... 3: 8 data bits
-    if (data_bits < 5 || data_bits > 8) {
-        error("Invalid number of bits (%d) in serial format, should be 5..8", data_bits);
+    if (obj->index == 0) {
+        // 0: 5 data bits ... 3: 8 data bits
+        if (data_bits < 5 || data_bits > 8) {
+            error("Invalid number of bits (%d) in serial format, should be 5..8", data_bits);
+        }
+        data_bits -= 5;
+    
+        int parity_enable, parity_select;
+        switch (parity) {
+            case ParityNone: parity_enable = 0; parity_select = 0; break;
+            case ParityOdd : parity_enable = 1; parity_select = 0; break;
+            case ParityEven: parity_enable = 1; parity_select = 1; break;
+            case ParityForced1: parity_enable = 1; parity_select = 2; break;
+            case ParityForced0: parity_enable = 1; parity_select = 3; break;
+            default:
+                error("Invalid serial parity setting");
+                return;
+        }
+        
+        obj->uart->LCR = data_bits       << 0
+                       | stop_bits       << 2
+                       | parity_enable   << 3
+                       | parity_select   << 4;
     }
-    data_bits -= 5;
-
-    int parity_enable, parity_select;
-    switch (parity) {
-        case ParityNone: parity_enable = 0; parity_select = 0; break;
-        case ParityOdd : parity_enable = 1; parity_select = 0; break;
-        case ParityEven: parity_enable = 1; parity_select = 1; break;
-        case ParityForced1: parity_enable = 1; parity_select = 2; break;
-        case ParityForced0: parity_enable = 1; parity_select = 3; break;
-        default:
-            error("Invalid serial parity setting");
-            return;
+    else {
+        // 0: 7 data bits ... 2: 9 data bits
+        if (data_bits < 7 || data_bits > 9) {
+            error("Invalid number of bits (%d) in serial format, should be 7..9", data_bits);
+        }
+        data_bits -= 7;
+        
+        int paritysel;
+        switch (parity) {
+            case ParityNone: paritysel = 0; break;
+            case ParityEven: paritysel = 2; break;
+            case ParityOdd : paritysel = 3; break;
+            default:
+                error("Invalid serial parity setting");
+                return;
+        }
+        obj->mini_uart->CFG = (data_bits << 2)
+                            | (paritysel << 4)
+                            | (stop_bits << 6)
+                            | UART_EN;
     }
-    
-    obj->uart->LCR = data_bits            << 0
-                   | stop_bits            << 2
-                   | parity_enable        << 3
-                   | parity_select        << 4;
 }
 
 /******************************************************************************
  * INTERRUPTS HANDLING
  ******************************************************************************/
 static inline void uart_irq(uint32_t iir, uint32_t index) {
-    // [Chapter 14] LPC17xx UART0/2/3: UARTn Interrupt Handling
     SerialIrq irq_type;
     switch (iir) {
         case 1: irq_type = TxIrq; break;
@@ -258,22 +327,22 @@
 
 void uart1_irq()
 {
-    //uart_irq((LPC_USART4->IIR >> 1) & 0x7, 1);
+    uart_irq((LPC_USART1->STAT & (1 << 2)) ? 2 : 1, 1);
 }
 
 void uart2_irq()
 {
-    //uart_irq((LPC_USART4->IIR >> 1) & 0x7, 2);
+    uart_irq((LPC_USART1->STAT & (1 << 2)) ? 2 : 1, 2);
 }
 
 void uart3_irq()
 {
-    //uart_irq((LPC_USART4->IIR >> 1) & 0x7, 3);
+    uart_irq((LPC_USART1->STAT & (1 << 2)) ? 2 : 1, 3);
 }
 
 void uart4_irq()
 {
-    //uart_irq((LPC_USART4->IIR >> 1) & 0x7, 4);
+    uart_irq((LPC_USART1->STAT & (1 << 2)) ? 2 : 1, 4);
 }
 
 void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) {
@@ -293,16 +362,27 @@
     }
     
     if (enable) {
-        obj->uart->IER |= (1 << irq);
+        if (obj->index == 0) {
+            obj->uart->IER |= (1 << irq);
+        }
+        else {
+            obj->mini_uart->INTENSET = (1 << ((irq == RxIrq) ? 0 : 2));
+        }
         NVIC_SetVector(irq_n, vector);
         NVIC_EnableIRQ(irq_n);
     } else { // disable
         int all_disabled = 0;
         SerialIrq other_irq = (irq == RxIrq) ? (TxIrq) : (RxIrq);
 
-        obj->uart->IER &= ~(1 << irq);
-        all_disabled = (obj->uart->IER & (1 << other_irq)) == 0;
-        
+        if (obj->index == 0) {
+            obj->uart->IER &= ~(1 << irq);
+            all_disabled = (obj->uart->IER & (1 << other_irq)) == 0;
+        }
+        else {
+            obj->mini_uart->INTENSET &= ~(1 << ((irq == RxIrq) ? 0 : 2));
+            all_disabled = (obj->mini_uart->INTENSET & (1 << ((other_irq == RxIrq) ? 0 : 2))) == 0;
+         }
+
         if (all_disabled)
             NVIC_DisableIRQ(irq_n);
     }
@@ -313,26 +393,51 @@
  ******************************************************************************/
 int serial_getc(serial_t *obj) {
     while (!serial_readable(obj));
-    return obj->uart->RBR;
+    if (obj->index == 0) {
+        return obj->uart->RBR;
+    }
+    else {
+        return obj->mini_uart->RXDAT;
+    }
 }
 
 void serial_putc(serial_t *obj, int c) {
     while (!serial_writable(obj));
-    obj->uart->THR = c;
+    if (obj->index == 0) {
+        obj->uart->THR = c;
+    }
+    else {
+        obj->mini_uart->TXDAT = c;
+    }
 }
 
 int serial_readable(serial_t *obj) {
-    return obj->uart->LSR & 0x01;
+    if (obj->index == 0) {
+        return obj->uart->LSR & 0x01;
+    }
+    else {
+        return obj->mini_uart->STAT & RXRDY;
+    }
 }
 
 int serial_writable(serial_t *obj) {
-    return obj->uart->LSR & 0x20;
+    if (obj->index == 0) {
+        return obj->uart->LSR & 0x20;
+    }
+    else {
+        return obj->mini_uart->STAT & TXRDY;
+    }
 }
 
 void serial_clear(serial_t *obj) {
-    obj->uart->FCR = 1 << 1  // rx FIFO reset
-                   | 1 << 2  // tx FIFO reset
-                   | 0 << 6; // interrupt depth
+    if (obj->index == 0) {
+        obj->uart->FCR = 1 << 1  // rx FIFO reset
+                       | 1 << 2  // tx FIFO reset
+                       | 0 << 6; // interrupt depth
+    }
+    else {
+        obj->mini_uart->STAT = 0;
+    }
 }
 
 void serial_pinout_tx(PinName tx) {
@@ -340,11 +445,21 @@
 }
 
 void serial_break_set(serial_t *obj) {
-    obj->uart->LCR |= (1 << 6);
+    if (obj->index == 0) {
+        obj->uart->LCR |= (1 << 6);
+    }
+    else {
+        obj->mini_uart->CTL |= TXBRKEN;
+    }
 }
 
 void serial_break_clear(serial_t *obj) {
-    obj->uart->LCR &= ~(1 << 6);
+    if (obj->index == 0) {
+        obj->uart->LCR &= ~(1 << 6);
+    }
+    else {
+        obj->mini_uart->CTL &= ~TXBRKEN;
+    }
 }