/* * This file is part of the libopencm3 project. * * Copyright (C) 2012 Benjamin Vernoux <titanmkd@gmail.com> * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see <http://www.gnu.org/licenses/>. */ #include <libopencm3/lpc43xx/uart.h> #include <libopencm3/lpc43xx/cgu.h> #define UART_SRC_32K 0x00 #define UART_SRC_IRC 0x01 #define UART_SRC_ENET_RX 0x02 #define UART_SRC_ENET_TX 0x03 #define UART_SRC_GP_CLKIN 0x04 #define UART_SRC_XTAL 0x06 #define UART_SRC_PLL0USB 0x07 #define UART_SRC_PLL0AUDIO 0x08 #define UART_SRC_PLL1 0x09 #define UART_SRC_IDIVA 0x0C #define UART_SRC_IDIVB 0x0D #define UART_SRC_IDIVC 0x0E #define UART_SRC_IDIVD 0x0F #define UART_SRC_IDIVE 0x10 #define UART_CGU_AUTOBLOCK_CLOCK_BIT 11 /* clock source selection (5 bits) */ #define UART_CGU_BASE_CLK_SEL_SHIFT 24 uint32_t dummy_read; /* * UART Init function */ void uart_init(uart_num_t uart_num, uart_databit_t data_nb_bits, uart_stopbit_t data_nb_stop, uart_parity_t data_parity, uint16_t uart_divisor, uint8_t uart_divaddval, uint8_t uart_mulval) { uint32_t lcr_config; uint32_t uart_port; uart_port = uart_num; switch (uart_num) { case UART0_NUM: /* use PLL1 as clock source for UART0 */ CGU_BASE_UART0_CLK = (1<<UART_CGU_AUTOBLOCK_CLOCK_BIT) | (CGU_SRC_PLL1<<UART_CGU_BASE_CLK_SEL_SHIFT); break; case UART1_NUM: /* use PLL1 as clock source for UART1 */ CGU_BASE_UART1_CLK = (1<<UART_CGU_AUTOBLOCK_CLOCK_BIT) | (CGU_SRC_PLL1<<UART_CGU_BASE_CLK_SEL_SHIFT); break; case UART2_NUM: /* use PLL1 as clock source for UART2 */ CGU_BASE_UART2_CLK = (1<<UART_CGU_AUTOBLOCK_CLOCK_BIT) | (CGU_SRC_PLL1<<UART_CGU_BASE_CLK_SEL_SHIFT); break; case UART3_NUM: /* use PLL1 as clock source for UART3 */ CGU_BASE_UART3_CLK = (1<<UART_CGU_AUTOBLOCK_CLOCK_BIT) | (CGU_SRC_PLL1<<UART_CGU_BASE_CLK_SEL_SHIFT); break; default: return; /* error */ } /* FIFOs RX/TX Enabled and Reset RX/TX FIFO (DMA Mode is also cleared)*/ UART_FCR(uart_port) = (UART_FCR_FIFO_EN | UART_FCR_RX_RS | UART_FCR_TX_RS); /* Disable FIFO */ UART_FCR(uart_port) = 0; /* Dummy read (to clear existing data) */ while (UART_LSR(uart_port) & UART_LSR_RDR) { dummy_read = UART_RBR(uart_port); } /* Wait end of TX & disable TX */ UART_TER(uart_port) = UART_TER_TXEN; /* Wait for current transmit complete */ while (!(UART_LSR(uart_port) & UART_LSR_THRE)); /* Disable Tx */ UART_TER(uart_port) = 0; /* Disable interrupt */ UART_IER(uart_port) = 0; /* Set LCR to default state */ UART_LCR(uart_port) = 0; /* Set ACR to default state */ UART_ACR(uart_port) = 0; /* Dummy Read to Clear Status */ dummy_read = UART_LSR(uart_port); /* Table 835. USART Fractional Divider Register: UARTbaudrate = PCLK / ( 16* (((256*DLM)+ DLL)*(1+(DivAddVal/MulVal))) ) The value of MULVAL and DIVADDVAL should comply to the following conditions: 1. 1 <= MULVAL <= 15 2. 0 <= DIVADDVAL <= 14 3. DIVADDVAL < MULVAL */ /* Set DLAB Bit */ UART_LCR(uart_port) |= UART_LCR_DLAB_EN; UART_DLM(uart_port) = UART_LOAD_DLM(uart_divisor); UART_DLL(uart_port) = UART_LOAD_DLL(uart_divisor); /* Clear DLAB Bit */ UART_LCR(uart_port) &= (~UART_LCR_DLAB_EN) & UART_LCR_BITMASK; UART_FDR(uart_port) = UART_FDR_BITMASK & (UART_FDR_MULVAL(uart_mulval) | UART_FDR_DIVADDVAL(uart_divaddval)); /* Read LCR config & Force Enable of Divisor Latches Access */ lcr_config = (UART_LCR(uart_port) & UART_LCR_DLAB_EN) & UART_LCR_BITMASK; lcr_config |= data_nb_bits; /* Set Nb Data Bits */ lcr_config |= data_nb_stop; /* Set Nb Stop Bits */ lcr_config |= data_parity; /* Set Data Parity */ /* Write LCR (only 8bits) */ UART_LCR(uart_port) = (lcr_config & UART_LCR_BITMASK); /* Enable TX */ UART_TER(uart_port) = UART_TER_TXEN; } /* * This Function return if data are received or not received. */ uart_rx_data_ready_t uart_rx_data_ready(uart_num_t uart_num) { uint32_t uart_port; uint8_t uart_status; uart_rx_data_ready_t data_ready; uart_port = uart_num; uart_status = UART_LSR(uart_port) & 0xFF; /* Check Error */ if ((uart_status & UART_LSR_ERROR_MASK) == 0) { /* No errors check if data is ready */ if ((uart_status & UART_LSR_RDR) == 0) { data_ready = UART_RX_NO_DATA; } else { data_ready = UART_RX_DATA_READY; } } else { /* UART Error */ data_ready = UART_RX_DATA_ERROR; } return data_ready; } /* * This Function Wait until Data RX Ready, and return Data Read from UART. */ uint8_t uart_read(uart_num_t uart_num) { uint32_t uart_port; uint8_t uart_val; uart_port = uart_num; /* Wait Until Data Received (Rx Data Not Ready) */ while ((UART_LSR(uart_port) & UART_LSR_RDR) == 0); uart_val = (UART_RBR(uart_port) & UART_RBR_MASKBIT); return uart_val; } /* * This Function Wait until Data RX Ready, and return Data Read from UART. */ uint8_t uart_read_timeout(uart_num_t uart_num, uint32_t rx_timeout_nb_cycles, uart_error_t *error) { uint32_t uart_port; uint8_t uart_val; uint32_t counter; uart_port = uart_num; /* Wait Until Data Received (Rx Data Not Ready) */ counter = 0; while ((UART_LSR(uart_port) & UART_LSR_RDR) == 0) { if (rx_timeout_nb_cycles > 0) { counter++; if (counter >= rx_timeout_nb_cycles) { *error = UART_TIMEOUT_ERROR; return 0; } } } uart_val = (UART_RBR(uart_port) & UART_RBR_MASKBIT); /* Clear error */ *error = UART_NO_ERROR; return uart_val; } /* This Function Wait Data TX Ready, and Write Data to UART if rx_timeout_nb_cycles = 0 Infinite wait */ void uart_write(uart_num_t uart_num, uint8_t data) { uint32_t uart_port; uart_port = uart_num; /* Wait Until FIFO not full */ while ((UART_LSR(uart_port) & UART_LSR_THRE) == 0); UART_THR(uart_port) = data; }