mbed library sources
Dependents: frdm_kl05z_gpio_test
Fork of mbed-src by
Diff: targets/hal/TARGET_NXP/TARGET_LPC11U6X/serial_api.c
- 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; + } }