| /* |
| * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /****************************************************************************** |
| * @file ck_usart.c |
| * @brief CSI Source File for usart Driver |
| * @version V1.0 |
| * @date 02. June 2017 |
| ******************************************************************************/ |
| #include <stdbool.h> |
| #include "csi_core.h" |
| #include "drv_usart.h" |
| #include "ck_usart.h" |
| #include "soc.h" |
| |
| #define ERR_USART(errno) (CSI_DRV_ERRNO_USART_BASE | errno) |
| |
| /* |
| * setting config may be accessed when the USART is not |
| * busy(USR[0]=0) and the DLAB bit(LCR[7]) is set. |
| */ |
| |
| #define WAIT_USART_IDLE(addr)\ |
| do { \ |
| int32_t timecount = 0; \ |
| while ((addr->USR & USR_UART_BUSY) && (timecount < UART_BUSY_TIMEOUT)) {\ |
| timecount++;\ |
| }\ |
| if (timecount >= UART_BUSY_TIMEOUT) {\ |
| return ERR_USART(EDRV_TIMEOUT);\ |
| } \ |
| } while(0) |
| |
| #define USART_NULL_PARAM_CHK(para) \ |
| do { \ |
| if (para == NULL) { \ |
| return ERR_USART(EDRV_PARAMETER); \ |
| } \ |
| } while (0) |
| |
| typedef struct { |
| uint32_t base; |
| uint32_t irq; |
| usart_event_cb_t cb_event; ///< Event callback |
| void *cb_arg; |
| uint32_t rx_total_num; |
| uint32_t tx_total_num; |
| uint8_t *rx_buf; |
| uint8_t *tx_buf; |
| volatile uint32_t rx_cnt; |
| volatile uint32_t tx_cnt; |
| volatile uint32_t tx_busy; |
| volatile uint32_t rx_busy; |
| } dw_usart_priv_t; |
| |
| extern int32_t target_usart_init(pin_t tx, pin_t rx, uint32_t *base, uint32_t *irq); |
| |
| static dw_usart_priv_t usart_instance[CONFIG_USART_NUM]; |
| |
| static const usart_capabilities_t usart_capabilities = { |
| .asynchronous = 1, /* supports USART (Asynchronous) mode */ |
| .synchronous_master = 0, /* supports Synchronous Master mode */ |
| .synchronous_slave = 0, /* supports Synchronous Slave mode */ |
| .single_wire = 0, /* supports USART Single-wire mode */ |
| .event_tx_complete = 1, /* Transmit completed event */ |
| .event_rx_timeout = 0, /* Signal receive character timeout event */ |
| }; |
| |
| /** |
| \brief set the bautrate of usart. |
| \param[in] addr usart base to operate. |
| \param[in] baudrate. |
| \param[in] apbfreq the frequence of the apb. |
| \return error code |
| */ |
| static int32_t dw_usart_set_baudrate(dw_usart_reg_t *addr, uint32_t baudrate, uint32_t apbfreq) |
| { |
| WAIT_USART_IDLE(addr); |
| |
| /* baudrate=(seriak clock freq)/(16*divisor); algorithm :rounding*/ |
| uint32_t divisor = ((apbfreq * 10) / baudrate) >> 4; |
| |
| if ((divisor % 10) >= 5) { |
| divisor = (divisor / 10) + 1; |
| } else { |
| divisor = divisor / 10; |
| } |
| |
| addr->LCR |= LCR_SET_DLAB; |
| /* DLL and DLH is lower 8-bits and higher 8-bits of divisor.*/ |
| addr->DLL = divisor & 0xff; |
| addr->DLH = (divisor >> 8) & 0xff; |
| /* |
| * The DLAB must be cleared after the baudrate is setted |
| * to access other registers. |
| */ |
| addr->LCR &= (~LCR_SET_DLAB); |
| |
| return 0; |
| } |
| |
| /** |
| \brief enable or disable parity. |
| \param[in] addr usart base to operate. |
| \param[in] parity ODD=8, EVEN=16, or NONE=0. |
| \return error code |
| */ |
| |
| static int32_t dw_usart_set_parity(dw_usart_reg_t *addr, usart_parity_e parity) |
| { |
| WAIT_USART_IDLE(addr); |
| |
| switch (parity) { |
| case USART_PARITY_NONE: |
| /*CLear the PEN bit(LCR[3]) to disable parity.*/ |
| addr->LCR &= (~LCR_PARITY_ENABLE); |
| break; |
| |
| case USART_PARITY_ODD: |
| /* Set PEN and clear EPS(LCR[4]) to set the ODD parity. */ |
| addr->LCR |= LCR_PARITY_ENABLE; |
| addr->LCR &= LCR_PARITY_ODD; |
| break; |
| |
| case USART_PARITY_EVEN: |
| /* Set PEN and EPS(LCR[4]) to set the EVEN parity.*/ |
| addr->LCR |= LCR_PARITY_ENABLE; |
| addr->LCR |= LCR_PARITY_EVEN; |
| break; |
| |
| default: |
| return ERR_USART(EDRV_USART_PARITY); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| \brief set the stop bit. |
| \param[in] addr usart base to operate. |
| \param[in] stopbit two possible value: USART_STOP_BITS_1 and USART_STOP_BITS_2. |
| \return error code |
| */ |
| static int32_t dw_usart_set_stopbit(dw_usart_reg_t *addr, usart_stop_bits_e stopbit) |
| { |
| WAIT_USART_IDLE(addr); |
| |
| switch (stopbit) { |
| case USART_STOP_BITS_1: |
| /* Clear the STOP bit to set 1 stop bit*/ |
| addr->LCR &= LCR_STOP_BIT1; |
| break; |
| |
| case USART_STOP_BITS_2: |
| /* |
| * If the STOP bit is set "1",we'd gotten 1.5 stop |
| * bits when DLS(LCR[1:0]) is zero, else 2 stop bits. |
| */ |
| addr->LCR |= LCR_STOP_BIT2; |
| break; |
| |
| default: |
| return ERR_USART(EDRV_USART_STOP_BITS); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| \brief the transmit data length,and we have four choices:5, 6, 7, and 8 bits. |
| \param[in] addr usart base to operate. |
| \param[in] databits the data length that user decides. |
| \return error code |
| */ |
| static int32_t dw_usart_set_databit(dw_usart_reg_t *addr, usart_data_bits_e databits) |
| { |
| WAIT_USART_IDLE(addr); |
| /* The word size decides by the DLS bits(LCR[1:0]), and the |
| * corresponding relationship between them is: |
| * DLS word size |
| * 00 -- 5 bits |
| * 01 -- 6 bits |
| * 10 -- 7 bits |
| * 11 -- 8 bits |
| */ |
| |
| switch (databits) { |
| case USART_DATA_BITS_5: |
| addr->LCR &= LCR_WORD_SIZE_5; |
| break; |
| |
| case USART_DATA_BITS_6: |
| addr->LCR &= 0xfd; |
| addr->LCR |= LCR_WORD_SIZE_6; |
| break; |
| |
| case USART_DATA_BITS_7: |
| addr->LCR &= 0xfe; |
| addr->LCR |= LCR_WORD_SIZE_7; |
| break; |
| |
| case USART_DATA_BITS_8: |
| addr->LCR |= LCR_WORD_SIZE_8; |
| break; |
| |
| default: |
| return ERR_USART(EDRV_USART_DATA_BITS); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| \brief get character in query mode. |
| \param[in] instance usart instance to operate. |
| \param[in] the pointer to the recieve charater. |
| \return error code |
| */ |
| int32_t csi_usart_getchar(usart_handle_t handle, uint8_t *ch) |
| { |
| dw_usart_priv_t *usart_priv = handle; |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| |
| while (!(addr->LSR & LSR_DATA_READY)); |
| |
| *ch = addr->RBR; |
| |
| return 0; |
| } |
| |
| /** |
| \brief transmit character in query mode. |
| \param[in] instance usart instance to operate. |
| \param[in] ch the input charater |
| \return error code |
| */ |
| int32_t csi_usart_putchar(usart_handle_t handle, uint8_t ch) |
| { |
| dw_usart_priv_t *usart_priv = handle; |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| |
| while ((!(addr->LSR & DW_LSR_TRANS_EMPTY))); |
| |
| addr->THR = ch; |
| |
| return 0; |
| |
| } |
| |
| /** |
| \brief interrupt service function for transmitter holding register empty. |
| \param[in] usart_priv usart private to operate. |
| */ |
| static void dw_usart_intr_threshold_empty(dw_usart_priv_t *usart_priv) |
| { |
| if (usart_priv->tx_total_num == 0) { |
| return; |
| } |
| |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| |
| addr->THR = *((uint8_t *)usart_priv->tx_buf); |
| usart_priv->tx_cnt++; |
| usart_priv->tx_buf++; |
| |
| if (usart_priv->tx_cnt >= usart_priv->tx_total_num) { |
| addr->IER &= (~IER_THRE_INT_ENABLE); |
| |
| while ((!(addr->LSR & DW_LSR_TEMT))); |
| |
| usart_priv->tx_cnt = 0; |
| usart_priv->tx_busy = 0; |
| usart_priv->tx_buf = NULL; |
| usart_priv->tx_total_num = 0; |
| |
| if (usart_priv->cb_event) { |
| usart_priv->cb_event(USART_EVENT_SEND_COMPLETE, usart_priv->cb_arg); |
| } |
| } |
| |
| } |
| |
| /** |
| \brief interrupt service function for receiver data available. |
| \param[in] usart_priv usart private to operate. |
| */ |
| static void dw_usart_intr_recv_data(dw_usart_priv_t *usart_priv) |
| { |
| if (usart_priv->cb_event && (usart_priv->rx_total_num == 0)) { |
| usart_priv->cb_event(USART_EVENT_RECEIVED, usart_priv->cb_arg); |
| return; |
| } |
| |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| uint8_t data = addr->RBR; |
| |
| if ((usart_priv->rx_total_num == 0) || (usart_priv->rx_buf == NULL)) { |
| return; |
| } |
| |
| *((uint8_t *)usart_priv->rx_buf) = data; |
| usart_priv->rx_cnt++; |
| usart_priv->rx_buf++; |
| |
| if (usart_priv->rx_cnt >= usart_priv->rx_total_num) { |
| usart_priv->rx_cnt = 0; |
| usart_priv->rx_buf = NULL; |
| usart_priv->rx_busy = 0; |
| usart_priv->rx_total_num = 0; |
| |
| if (usart_priv->cb_event) { |
| usart_priv->cb_event(USART_EVENT_RECEIVE_COMPLETE, usart_priv->cb_arg); |
| } |
| } |
| |
| } |
| |
| /** |
| \brief the interrupt service function. |
| \param[in] index of usart instance. |
| */ |
| void dw_usart_irqhandler(int32_t idx) |
| { |
| dw_usart_priv_t *usart_priv = &usart_instance[idx]; |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| |
| uint8_t intr_state = addr->IIR & 0xf; |
| |
| switch (intr_state) { |
| case DW_IIR_THR_EMPTY: /* interrupt source:transmitter holding register empty */ |
| dw_usart_intr_threshold_empty(usart_priv); |
| break; |
| |
| case DW_IIR_RECV_DATA: /* interrupt source:receiver data available or receiver fifo trigger level reached */ |
| dw_usart_intr_recv_data(usart_priv); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /** |
| \brief Get driver capabilities. |
| \param[in] handle usart handle to operate. |
| \return \ref usart_capabilities_t |
| */ |
| usart_capabilities_t csi_usart_get_capabilities(usart_handle_t handle) |
| { |
| return usart_capabilities; |
| } |
| |
| /** |
| \brief Initialize USART Interface. 1. Initializes the resources needed for the USART interface 2.registers event callback function |
| \param[in] usart pin of tx |
| \param[in] usart pin of rx |
| \param[in] cb_event Pointer to \ref usart_event_cb_t |
| \return return usart handle if success |
| */ |
| usart_handle_t csi_usart_initialize(pin_t tx, pin_t rx, usart_event_cb_t cb_event, void *cb_arg) |
| { |
| uint32_t base = 0u; |
| uint32_t irq = 0u; |
| |
| int32_t idx = target_usart_init(tx, rx, &base, &irq); |
| |
| if (idx < 0 || idx >= CONFIG_USART_NUM) { |
| return NULL; |
| } |
| |
| dw_usart_priv_t *usart_priv = &usart_instance[idx]; |
| usart_priv->base = base; |
| usart_priv->irq = irq; |
| usart_priv->cb_event = cb_event; |
| usart_priv->cb_arg = cb_arg; |
| |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| |
| /* enable received data available */ |
| addr->IER = IER_RDA_INT_ENABLE; |
| drv_nvic_enable_irq(usart_priv->irq); |
| |
| return usart_priv; |
| } |
| |
| /** |
| \brief De-initialize UART Interface. stops operation and releases the software resources used by the interface |
| \param[in] handle usart handle to operate. |
| \return error code |
| */ |
| int32_t csi_usart_uninitialize(usart_handle_t handle) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| |
| dw_usart_priv_t *usart_priv = handle; |
| |
| drv_nvic_disable_irq(usart_priv->irq); |
| usart_priv->cb_event = NULL; |
| |
| return 0; |
| } |
| |
| /** |
| \brief config usart mode. |
| \param[in] handle usart handle to operate. |
| \param[in] sysclk configured system clock. |
| \param[in] mode \ref usart_mode_e |
| \param[in] parity \ref usart_parity_e |
| \param[in] stopbits \ref usart_stop_bits_e |
| \param[in] bits \ref usart_data_bits_e |
| \param[in] baud configured baud |
| \return error code |
| */ |
| int32_t csi_usart_config(usart_handle_t handle, |
| uint32_t sysclk, |
| uint32_t baud, |
| usart_mode_e mode, |
| usart_parity_e parity, |
| usart_stop_bits_e stopbits, |
| usart_data_bits_e bits) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| dw_usart_priv_t *usart_priv = handle; |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| |
| /* control the data_bit of the usart*/ |
| int32_t ret = dw_usart_set_baudrate(addr, baud, sysclk); |
| |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* control the parity of the usart*/ |
| ret = dw_usart_set_parity(addr, parity); |
| |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* control the stopbit of the usart*/ |
| ret = dw_usart_set_stopbit(addr, stopbits); |
| |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = dw_usart_set_databit(addr, bits); |
| |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return 0; |
| } |
| /** |
| \brief config usart default tx value. used in syn mode |
| \param[in] handle usart handle to operate. |
| \param[in] value default tx value |
| \return error code |
| */ |
| int32_t csi_usart_set_default_tx_value(usart_handle_t handle, uint32_t value) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| return ERR_USART(EDRV_UNSUPPORTED); |
| } |
| |
| /** |
| \brief Start sending data to UART transmitter,(received data is ignored). |
| The function is non-blocking,UART_EVENT_TRANSFER_COMPLETE is signaled when transfer completes. |
| csi_usart_get_status can indicates if transmission is still in progress or pending |
| \param[in] handle usart handle to operate. |
| \param[in] data Pointer to buffer with data to send to UART transmitter. data_type is : uint8_t for 1..8 data bits, uint16_t for 9..16 data bits,uint32_t for 17..32 data bits, |
| \param[in] num Number of data items to send |
| \return error code |
| */ |
| int32_t csi_usart_send(usart_handle_t handle, const void *data, uint32_t num) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| USART_NULL_PARAM_CHK(data); |
| |
| if (num == 0) { |
| return ERR_USART(EDRV_PARAMETER); |
| } |
| |
| dw_usart_priv_t *usart_priv = handle; |
| uint8_t *source = NULL; |
| source = (uint8_t *)data; |
| |
| usart_priv->tx_buf = (uint8_t *)data; |
| usart_priv->tx_total_num = num; |
| usart_priv->tx_cnt = 0; |
| usart_priv->tx_busy = 1; |
| |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| /* enable the interrupt*/ |
| addr->IER |= IER_THRE_INT_ENABLE; |
| return 0; |
| } |
| |
| /** |
| \brief Abort Send data to UART transmitter |
| \param[in] handle usart handle to operate. |
| \return error code |
| */ |
| int32_t csi_usart_abort_send(usart_handle_t handle) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| dw_usart_priv_t *usart_priv = handle; |
| |
| usart_priv->tx_cnt = usart_priv->tx_total_num; |
| return 0; |
| } |
| |
| /** |
| \brief Start receiving data from UART receiver.transmits the default value as specified by csi_usart_set_default_tx_value |
| \param[in] handle usart handle to operate. |
| \param[out] data Pointer to buffer for data to receive from UART receiver |
| \param[in] num Number of data items to receive |
| \return error code |
| */ |
| int32_t csi_usart_receive(usart_handle_t handle, void *data, uint32_t num) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| USART_NULL_PARAM_CHK(data); |
| |
| uint8_t *dest = NULL; |
| dw_usart_priv_t *usart_priv = handle; |
| dest = (uint8_t *)data; |
| |
| usart_priv->rx_buf = (uint8_t *)data; // Save receive buffer usart |
| usart_priv->rx_total_num = num; // Save number of data to be received |
| usart_priv->rx_cnt = 0; |
| usart_priv->rx_busy = 1; |
| |
| return 0; |
| |
| } |
| |
| /** |
| \brief query data from UART receiver FIFO. |
| \param[in] handle usart handle to operate. |
| \param[out] data Pointer to buffer for data to receive from UART receiver |
| \param[in] num Number of data items to receive |
| \return receive fifo data num |
| */ |
| int32_t csi_usart_receive_query(usart_handle_t handle, void *data, uint32_t num) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| USART_NULL_PARAM_CHK(data); |
| |
| dw_usart_priv_t *usart_priv = handle; |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| int32_t recv_num = 0; |
| |
| while (addr->LSR & 0x1) { |
| *((uint8_t *)data++) = addr->RBR; |
| recv_num++; |
| |
| if (recv_num >= num) { |
| break; |
| } |
| } |
| |
| return recv_num; |
| |
| } |
| |
| /** |
| \brief Abort Receive data from UART receiver |
| \param[in] handle usart handle to operate. |
| \return error code |
| */ |
| int32_t csi_usart_abort_receive(usart_handle_t handle) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| dw_usart_priv_t *usart_priv = handle; |
| |
| usart_priv->rx_cnt = usart_priv->rx_total_num; |
| return 0; |
| } |
| |
| /** |
| \brief Start sending/receiving data to/from UART transmitter/receiver. |
| \param[in] handle usart handle to operate. |
| \param[in] data_out Pointer to buffer with data to send to USART transmitter |
| \param[out] data_in Pointer to buffer for data to receive from USART receiver |
| \param[in] num Number of data items to transfer |
| \return error code |
| */ |
| int32_t csi_usart_transfer(usart_handle_t handle, const void *data_out, void *data_in, uint32_t num) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| return ERR_USART(EDRV_UNSUPPORTED); |
| } |
| |
| /** |
| \brief abort sending/receiving data to/from USART transmitter/receiver. |
| \param[in] handle usart handle to operate. |
| \return error code |
| */ |
| int32_t csi_usart_abort_transfer(usart_handle_t handle) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| return ERR_USART(EDRV_UNSUPPORTED); |
| } |
| |
| /** |
| \brief Get USART status. |
| \param[in] handle usart handle to operate. |
| \return USART status \ref usart_status_t |
| */ |
| usart_status_t csi_usart_get_status(usart_handle_t handle) |
| { |
| usart_status_t usart_status; |
| dw_usart_priv_t *usart_priv = handle; |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| uint32_t line_status_reg = addr->LSR; |
| |
| usart_status.tx_busy = usart_priv->tx_busy; |
| usart_status.rx_busy = usart_priv->rx_busy; |
| |
| if (line_status_reg & DW_LSR_BI) { |
| usart_status.rx_break = 1; |
| } |
| |
| if (line_status_reg & DW_LSR_FE) { |
| usart_status.rx_framing_error = 1; |
| } |
| |
| if (line_status_reg & DW_LSR_PE) { |
| usart_status.rx_parity_error = 1; |
| } |
| |
| return usart_status; |
| } |
| |
| /** |
| \brief control the transmit. |
| \param[in] handle usart handle to operate. |
| \param[in] 1 - enable the transmitter. 0 - disable the transmitter |
| \return error code |
| */ |
| int32_t csi_usart_control_tx(usart_handle_t handle, uint32_t enable) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| return 0; |
| } |
| |
| /** |
| \brief control the receive. |
| \param[in] handle usart handle to operate. |
| \param[in] 1 - enable the receiver. 0 - disable the receiver |
| \return error code |
| */ |
| int32_t csi_usart_control_rx(usart_handle_t handle, uint32_t enable) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| return 0; |
| } |
| |
| /** |
| \brief control the break. |
| \param[in] handle usart handle to operate. |
| \param[in] 1- Enable continuous Break transmission,0 - disable continuous Break transmission |
| \return error code |
| */ |
| int32_t csi_usart_control_break(usart_handle_t handle, uint32_t enable) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| return ERR_USART(EDRV_UNSUPPORTED); |
| } |
| |
| /** |
| \brief flush receive/send data. |
| \param[in] handle usart handle to operate. |
| \param[in] type \ref usart_flush_type_e. |
| \return error code |
| */ |
| int32_t csi_usart_flush(usart_handle_t handle, usart_flush_type_e type) |
| { |
| USART_NULL_PARAM_CHK(handle); |
| |
| dw_usart_priv_t *usart_priv = handle; |
| dw_usart_reg_t *addr = (dw_usart_reg_t *)(usart_priv->base); |
| |
| if (type == USART_FLUSH_WRITE) { |
| while ((!(addr->LSR & DW_LSR_TEMT))); |
| } else if (type == USART_FLUSH_READ) { |
| while (addr->LSR & 0x1) { |
| addr->RBR; |
| } |
| } else { |
| return ERR_USART(EDRV_PARAMETER); |
| } |
| |
| return 0; |
| } |