/*
|
* Copyright (c) 2019-2023 Beijing Hanwei Innovation Technology Ltd. Co. and
|
* its subsidiaries and affiliates (collectly called MKSEMI).
|
*
|
* All rights reserved.
|
*
|
* Redistribution and use in source and binary forms, with or without
|
* modification, are permitted provided that the following conditions are met:
|
*
|
* 1. Redistributions of source code must retain the above copyright notice,
|
* this list of conditions and the following disclaimer.
|
*
|
* 2. Redistributions in binary form, except as embedded into an MKSEMI
|
* integrated circuit in a product or a software update for such product,
|
* must reproduce the above copyright notice, this list of conditions and
|
* the following disclaimer in the documentation and/or other materials
|
* provided with the distribution.
|
*
|
* 3. Neither the name of MKSEMI nor the names of its contributors may be used
|
* to endorse or promote products derived from this software without
|
* specific prior written permission.
|
*
|
* 4. This software, with or without modification, must only be used with a
|
* MKSEMI integrated circuit.
|
*
|
* 5. Any software provided in binary form under this license must not be
|
* reverse engineered, decompiled, modified and/or disassembled.
|
*
|
* THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
* MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* DISCLAIMED. IN NO EVENT SHALL MKSEMI OR CONTRIBUTORS BE LIABLE FOR ANY
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*/
|
|
#include "mk_uart.h"
|
#include "mk_trace.h"
|
#include "mk_clock.h"
|
#include "mk_reset.h"
|
#include "mk_dma.h"
|
#include "mk_misc.h"
|
|
static struct UART_HANDLE_T uart_handle[UART_MAX_NUM] = {
|
{
|
.base = UART0,
|
.irq = UART0_IRQn,
|
.dma_rx_ch = DMA_CH4,
|
.dma_tx_ch = DMA_CH5,
|
},
|
{
|
.base = UART1,
|
.irq = UART1_IRQn,
|
.dma_rx_ch = DMA_CH6,
|
.dma_tx_ch = DMA_CH7,
|
},
|
};
|
//ÒÆÖ²
|
uint32_t SerialKeyPressed(uint8_t *key)//ÅжÏÊý¾ÝÊÇ·ñÊÕµ½µÄ MK8000ÐÞ¸Ä
|
{
|
uint32_t status = uart_handle[1].base->STATUS;
|
if (status & UART_STATUS_DR_MSK)
|
{
|
//Serial0PutString("³É¹¦½ÓÊÕing");
|
//uart_receive(UART_ID1,test_buf,10,NULL);
|
*key = (uint8_t)uart_handle[1].base->RX_DATA;
|
//uart_rx_fifo_clear(UART_ID1);
|
return 1;
|
}
|
else
|
{
|
return 0;
|
}
|
}
|
void SerialPutChar(uint8_t c)
|
{
|
while (uart_handle[0].base->TX_FL)
|
{
|
}
|
uart_send(UART_ID1, &c, 1, NULL);
|
}
|
|
void Serial_PutString(uint8_t *s)
|
{
|
while (*s != '\0')
|
{
|
SerialPutChar(*s);
|
s++;
|
}
|
}
|
void Serial0PutChar(uint8_t c)
|
{//ÅжÏÊý¾Ý»º´æÇøÎª¿Õ¼´ÉÏÒ»¸ö×Ö½ÚÊý¾ÝÒѾ±»Ë͵½·¢ËͼĴæÆ÷·¢ËͳöÈ¥ÁË
|
// wait TX FIFO empty
|
while (uart_handle[0].base->TX_FL)
|
{
|
}
|
uart_send(UART_ID0, &c, 1, NULL);
|
}
|
|
void Serial0_PutString(uint8_t *s)
|
{
|
while (*s != '\0')
|
{
|
Serial0PutChar(*s);
|
s++;
|
}
|
}
|
static const struct UART_DIVISOR_T baud_table[] = {
|
{89, 6, 32}, // 1200
|
{20, 3, 33}, // 2400
|
{69, 1, 40}, // 4800
|
{130, 0, 50}, // 9600
|
{65, 0, 50}, // 19200
|
{25, 0, 65}, // 38400
|
{19, 0, 57}, // 57600 -- 0.03%
|
{3, 0, 181}, // 115200 -- -0.25%
|
{6, 0, 45}, // 230400 -- 0.31%
|
{3, 0, 45}, // 460800 -- 0.31%
|
{2, 0, 34}, // 921600 -- -0.43%
|
{1, 0, 34}, // 1843200 -- -0.43%
|
{1, 0, 62}, // 1000000 -- -0.65%
|
{1, 0, 32}, // 2000000 -- -2.5%
|
};
|
|
#if UART_DMA_MODE_EN
|
static void uart_dma_callback(void *ch, uint32_t err_code);
|
static void uart_dma_abort_callback(void *ch, uint32_t err_code);
|
#endif
|
|
static int uart_state_set(enum UART_DEV_T id, enum UART_STATE_T state)
|
{
|
int ret = DRV_OK;
|
uint32_t lock = int_lock();
|
|
// update state
|
switch (uart_handle[id].state)
|
{
|
case UART_STATE_READY:
|
uart_handle[id].state = state;
|
break;
|
case UART_STATE_BUSY_RX:
|
if (state == UART_STATE_BUSY_TX)
|
{
|
uart_handle[id].state = UART_STATE_BUSY_TX_RX;
|
}
|
else
|
{
|
ret = DRV_BUSY;
|
}
|
break;
|
case UART_STATE_BUSY_TX:
|
if (state == UART_STATE_BUSY_RX)
|
{
|
uart_handle[id].state = UART_STATE_BUSY_TX_RX;
|
}
|
else
|
{
|
ret = DRV_BUSY;
|
}
|
break;
|
case UART_STATE_BUSY_TX_RX:
|
ret = DRV_BUSY;
|
break;
|
case UART_STATE_RESET:
|
case UART_STATE_TIMEOUT:
|
case UART_STATE_ERROR:
|
ret = DRV_ERROR;
|
break;
|
}
|
int_unlock(lock);
|
|
return ret;
|
}
|
|
static void uart_state_clear(enum UART_DEV_T id, enum UART_STATE_T state)
|
{
|
uint32_t lock = int_lock();
|
// update state
|
uart_handle[id].state &= ~state;
|
if (uart_handle[id].state == 0)
|
{
|
uart_handle[id].state = UART_STATE_READY;
|
}
|
int_unlock(lock);
|
}
|
|
enum UART_STATE_T uart_state_get(enum UART_DEV_T id)
|
{
|
return uart_handle[id].state;
|
}
|
|
bool uart_tx_in_progress(enum UART_DEV_T id)
|
{
|
return ((uart_handle[id].state & UART_STATE_BUSY_TX) && (uart_handle[id].base->STATUS & UART_STATUS_BUSY_MSK));
|
}
|
|
bool uart_fifo_busy(enum UART_DEV_T id)
|
{
|
// UART is not enabled but RX is pulled low
|
return ((uart_handle[id].state != UART_STATE_RESET) && (uart_handle[id].base->STATUS & UART_STATUS_BUSY_MSK));
|
}
|
|
void uart_rx_fifo_clear(enum UART_DEV_T id)
|
{
|
// clear FIFO
|
while (uart_handle[id].base->STATUS & UART_STATUS_RFNE_MSK)
|
{
|
uart_handle[id].base->RX_DATA;
|
}
|
}
|
|
void uart_baud_set(enum UART_DEV_T id, enum UART_BAUD_T baud)
|
{
|
ASSERT((id < UART_MAX_NUM) && (baud < BAUD_MAX), "Paramter invalid");
|
|
// fraction
|
clock_set_divider(id > 0 ? CLOCK_UART1_FDIV : CLOCK_UART0_FDIV, baud_table[baud].fraction);
|
|
// set DLAB to access DLL and DLH registers
|
uint32_t reg = uart_handle[id].base->CTRL1 | UART_CTRL1_DLAB_MSK;
|
uart_handle[id].base->CTRL1 = reg;
|
uint32_t dl = UART_DIVISOR_H(baud_table[baud].dlh) + baud_table[baud].dll;
|
uart_handle[id].base->DIVISOR = dl;
|
// clear DLAB
|
reg &= (uint32_t)~UART_CTRL1_DLAB_MSK;
|
uart_handle[id].base->CTRL1 = reg;
|
|
// need to wait at least x cycles after the baud rate is set where
|
// x = 32 * uart_divisor. Assuming here that the uart clock is ~2
|
// times slower than the processor clock.
|
delay_us(dl);
|
}
|
|
int uart_open(enum UART_DEV_T id, struct UART_CFG_T *config)
|
{
|
if ((id >= UART_MAX_NUM) && (config == NULL))
|
{
|
return DRV_ERROR;
|
}
|
else if (id == UART_ID0)
|
{
|
// enable UART0 clock
|
clock_enable(CLOCK_UART0);
|
reset_module(RESET_MODULE_UART0);
|
}
|
else if (id == UART_ID1)
|
{
|
// enable UART1 clock
|
clock_enable(CLOCK_UART1);
|
reset_module(RESET_MODULE_UART1);
|
}
|
|
// disable all uart interrupts
|
uart_handle[id].base->INTR_EN &= ~UART_INTR_EN_ALL_MSK;
|
// reset FIFO
|
uart_handle[id].base->CTRL0 = UART_CTRL0_RX_FIFO_RESET_MSK | UART_CTRL0_TX_FIFO_RESET_MSK;
|
// clear STATUS
|
uart_handle[id].base->INTR_CLR =
|
UART_INTR_CLR_THRE_INT_CLR_MSK | UART_INTR_CLR_BUSY_INT_CLR_MSK | UART_INTR_CLR_LSR_INT_CLR_MSK | UART_INTR_CLR_MSR_INT_CLR_MSK;
|
|
// reset device handle
|
uart_handle[id].tx_buff = NULL;
|
uart_handle[id].tx_callback = NULL;
|
uart_handle[id].tx_count = 0;
|
uart_handle[id].tx_size = 0;
|
uart_handle[id].rx_buff = NULL;
|
uart_handle[id].rx_callback = NULL;
|
uart_handle[id].rx_count = 0;
|
uart_handle[id].rx_size = 0;
|
uart_handle[id].dma_en = config->dma_en;
|
uart_handle[id].int_rx = config->int_rx;
|
uart_handle[id].int_tx = config->int_tx;
|
|
// line settings
|
uint32_t reg = (uint32_t)config->data | (uint32_t)(config->stop << UART_CTRL1_STOP_POS);
|
if (config->parity == UART_PARITY_ODD)
|
{
|
reg |= (UART_CTRL1_PARITY_EN_MSK);
|
}
|
else if (config->parity == UART_PARITY_EVEN)
|
{
|
reg |= (UART_CTRL1_PARITY_EN_MSK | UART_CTRL1_EVEN_PARITY_MSK);
|
}
|
else if (config->parity == UART_PARITY_FORCE1)
|
{
|
reg |= (UART_CTRL1_PARITY_EN_MSK | UART_CTRL1_STICK_PARITY_MSK);
|
}
|
else if (config->parity == UART_PARITY_FORCE0)
|
{
|
reg |= (UART_CTRL1_PARITY_EN_MSK | UART_CTRL1_EVEN_PARITY_MSK | UART_CTRL1_STICK_PARITY_MSK);
|
}
|
uart_handle[id].base->CTRL1 = reg;
|
|
// FIFO configure and enable
|
reg = UART_CTRL0_RX_TRIGGER(config->rx_level) | UART_CTRL0_TX_TRIGGER(config->tx_level) | UART_CTRL0_DMA_MODE(uart_handle[id].dma_en) |
|
UART_CTRL0_FIFO_ENABLE_MSK;
|
|
uart_handle[id].base->CTRL0 = reg;
|
uart_handle[id].ctrl0 = reg;
|
|
if (config->flow == UART_FLOW_CONTROL_AUTO)
|
{
|
uart_handle[id].base->CTRL2 = UART_CTRL2_FLOW_CTRL_EN_MSK | UART_CTRL2_RTS_MSK;
|
}
|
|
// baud rate
|
uart_baud_set(id, config->baud);
|
|
#if UART_INT_MODE_EN || UART_DMA_MODE_EN
|
// enable IRQ
|
if ((uart_handle[id].int_tx) || (uart_handle[id].int_rx) || (uart_handle[id].dma_en))
|
{
|
NVIC_SetPriority(uart_handle[id].irq, IRQ_PRIORITY_NORMAL);
|
NVIC_ClearPendingIRQ(uart_handle[id].irq);
|
NVIC_EnableIRQ(uart_handle[id].irq);
|
}
|
#endif
|
|
uart_handle[id].state = UART_STATE_READY;
|
return DRV_OK;
|
}
|
|
int uart_close(enum UART_DEV_T id)
|
{
|
if (id >= UART_MAX_NUM)
|
{
|
return DRV_ERROR;
|
}
|
|
// disable all uart interrupts
|
uart_handle[id].base->INTR_EN &= ~UART_INTR_EN_ALL_MSK;
|
// reset FIFO
|
uart_handle[id].base->CTRL0 = UART_CTRL0_RX_FIFO_RESET_MSK | UART_CTRL0_TX_FIFO_RESET_MSK;
|
// clear STATUS
|
uart_handle[id].base->INTR_CLR =
|
UART_INTR_CLR_THRE_INT_CLR_MSK | UART_INTR_CLR_BUSY_INT_CLR_MSK | UART_INTR_CLR_LSR_INT_CLR_MSK | UART_INTR_CLR_MSR_INT_CLR_MSK;
|
|
#if UART_INT_MODE_EN || UART_DMA_MODE_EN
|
if ((uart_handle[id].int_tx) || (uart_handle[id].int_rx) || (uart_handle[id].dma_en))
|
{
|
NVIC_DisableIRQ(uart_handle[id].irq);
|
NVIC_ClearPendingIRQ(uart_handle[id].irq);
|
}
|
#endif
|
|
if (id == UART_ID0)
|
{
|
// disable UART0 clock
|
clock_disable(CLOCK_UART0);
|
}
|
else if (id == UART_ID1)
|
{
|
// disable UART1 clock
|
clock_disable(CLOCK_UART1);
|
}
|
|
uart_handle[id].state = UART_STATE_RESET;
|
return DRV_OK;
|
}
|
|
#if defined(__GNUC__)
|
#pragma GCC diagnostic push
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
#endif
|
|
int uart_send(enum UART_DEV_T id, uint8_t *tx_buf, uint32_t len, drv_callback_t callback)
|
{
|
if ((tx_buf == 0) || (len == 0))
|
{
|
return DRV_ERROR;
|
}
|
|
// update state
|
int ret = uart_state_set(id, UART_STATE_BUSY_TX);
|
if (ret != DRV_OK)
|
{
|
return ret;
|
}
|
|
uart_handle[id].tx_buff = tx_buf;
|
uart_handle[id].tx_count = 0;
|
uart_handle[id].tx_size = len;
|
uart_handle[id].tx_callback = callback;
|
|
if (uart_handle[id].dma_en)
|
{
|
#if UART_DMA_MODE_EN
|
struct DMA_CH_CFG_T uart_tx_dma_cfg = {
|
.fifo_th = DMA_FIFO_TH_1,
|
.src_burst_size = DMA_SRC_BURST_SIZE_1,
|
.src_width = DMA_WIDTH_1B,
|
.dst_width = DMA_WIDTH_1B,
|
.src_addr_ctrl = DMA_ADDR_INC,
|
.dst_addr_ctrl = DMA_ADDR_FIXED,
|
.src_req_sel = DMA_REQ_MEM,
|
.dst_req_sel = (id == UART_ID1 ? DMA_REQ_UART1_TX : DMA_REQ_UART0_TX),
|
};
|
|
dma_open(uart_handle[id].dma_tx_ch, &uart_tx_dma_cfg);
|
dma_transfer(uart_handle[id].dma_tx_ch, tx_buf, (uint8_t *)&uart_handle[id].base->TX_DATA, len, uart_dma_callback);
|
#endif
|
}
|
else if (uart_handle[id].int_tx)
|
{
|
#if UART_INT_MODE_EN
|
// write data to FIFO
|
while ((uart_handle[id].tx_count < uart_handle[id].tx_size) && (uart_handle[id].base->STATUS & UART_STATUS_TFNF_MSK))
|
{
|
uart_handle[id].base->TX_DATA = uart_handle[id].tx_buff[uart_handle[id].tx_count++];
|
}
|
|
// enable interrupts
|
uart_handle[id].base->INTR_EN |= (UART_INTR_EN_PTIME_MSK | UART_INTR_EN_ETBEI_MSK);
|
#endif
|
}
|
else
|
{
|
#if UART_POLL_MODE_EN
|
// disable PTIME
|
uint32_t reg = uart_handle[id].base->INTR_EN;
|
if (reg & UART_INTR_EN_PTIME_MSK)
|
{
|
uart_handle[id].base->INTR_EN = reg & ~UART_INTR_EN_PTIME_MSK;
|
}
|
|
// polling
|
uint32_t status = 0U;
|
while (uart_handle[id].tx_count < uart_handle[id].tx_size)
|
{
|
status = uart_handle[id].base->STATUS;
|
if (status & (UART_STATUS_OE_MSK | UART_STATUS_PE_MSK | UART_STATUS_FE_MSK | UART_STATUS_BI_MSK | UART_STATUS_RFE_MSK))
|
{
|
// TODO: user handle the error case
|
uart_handle[id].base->INTR_CLR = UART_INTR_CLR_LSR_INT_CLR_MSK;
|
status |= UART_ERR_LINE;
|
ret = DRV_ERROR;
|
break;
|
}
|
|
if (uart_handle[id].base->STATUS & UART_STATUS_TFNF_MSK)
|
{
|
uart_handle[id].base->TX_DATA = uart_handle[id].tx_buff[uart_handle[id].tx_count++];
|
}
|
}
|
|
// wait TX FIFO empty
|
while (uart_handle[id].base->TX_FL)
|
{
|
}
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_TX);
|
|
if (uart_handle[id].tx_callback)
|
{
|
uart_handle[id].tx_callback(&id, status);
|
}
|
#endif
|
}
|
|
return ret;
|
}
|
|
int uart_receive(enum UART_DEV_T id, uint8_t *rx_buf, uint32_t len, drv_callback_t callback)
|
{
|
if ((rx_buf == 0) || (len == 0))
|
{
|
return DRV_ERROR;
|
}
|
|
// update state
|
int ret = uart_state_set(id, UART_STATE_BUSY_RX);
|
if (ret != DRV_OK)
|
{
|
return ret;
|
}
|
|
uart_handle[id].rx_buff = rx_buf;
|
uart_handle[id].rx_count = 0;
|
uart_handle[id].rx_size = len;
|
uart_handle[id].rx_callback = callback;
|
|
// fix TX output low level when host MCU reset
|
uint32_t err_code = uart_handle[id].base->STATUS;
|
if (err_code & (UART_STATUS_FE_MSK | UART_STATUS_BI_MSK | UART_STATUS_RFE_MSK))
|
{
|
// reset FIFO
|
uart_handle[id].base->CTRL0 = UART_CTRL0_RX_FIFO_RESET_MSK | UART_CTRL0_TX_FIFO_RESET_MSK;
|
// clear STATUS
|
uart_handle[id].base->INTR_CLR =
|
UART_INTR_CLR_THRE_INT_CLR_MSK | UART_INTR_CLR_BUSY_INT_CLR_MSK | UART_INTR_CLR_LSR_INT_CLR_MSK | UART_INTR_CLR_MSR_INT_CLR_MSK;
|
uart_handle[id].base->CTRL0 = uart_handle[id].ctrl0;
|
}
|
|
if (uart_handle[id].dma_en)
|
{
|
#if UART_DMA_MODE_EN
|
struct DMA_CH_CFG_T uart_rx_dma_cfg = {
|
.fifo_th = DMA_FIFO_TH_1,
|
.src_burst_size = DMA_SRC_BURST_SIZE_1,
|
.src_width = DMA_WIDTH_1B,
|
.dst_width = DMA_WIDTH_1B,
|
.src_addr_ctrl = DMA_ADDR_FIXED,
|
.dst_addr_ctrl = DMA_ADDR_INC,
|
.src_req_sel = (id == UART_ID1 ? DMA_REQ_UART1_RX : DMA_REQ_UART0_RX),
|
.dst_req_sel = DMA_REQ_MEM,
|
};
|
uart_handle[id].dma_rx_err_state = 0;
|
|
NVIC_ClearPendingIRQ(uart_handle[id].irq);
|
// Enable Receiver Line Status Interrupt, used to obtain the error interrupt event triggered by the receiver
|
uart_handle[id].base->INTR_EN = (UART_INTR_EN_ELCOLR_MSK | UART_INTR_EN_ELSI_MSK);
|
|
dma_open(uart_handle[id].dma_rx_ch, &uart_rx_dma_cfg);
|
dma_transfer(uart_handle[id].dma_rx_ch, (uint8_t *)&uart_handle[id].base->RX_DATA, rx_buf, len, uart_dma_callback);
|
#endif
|
}
|
else if (uart_handle[id].int_rx)
|
{
|
#if UART_INT_MODE_EN
|
// enable interrupts
|
uart_handle[id].base->INTR_EN |= (UART_INTR_EN_PTIME_MSK | UART_INTR_EN_ELCOLR_MSK | UART_INTR_EN_ELSI_MSK | UART_INTR_EN_ERBFI_MSK);
|
#endif
|
}
|
else
|
{
|
#if UART_POLL_MODE_EN
|
// disable PTIME
|
uint32_t reg = uart_handle[id].base->INTR_EN;
|
if (reg & UART_INTR_EN_PTIME_MSK)
|
{
|
uart_handle[id].base->INTR_EN = reg & ~UART_INTR_EN_PTIME_MSK;
|
}
|
|
uint32_t status = 0U;
|
// polling
|
while (uart_handle[id].rx_count < uart_handle[id].rx_size)
|
{
|
status = uart_handle[id].base->STATUS;
|
if (status & (UART_STATUS_OE_MSK | UART_STATUS_PE_MSK | UART_STATUS_FE_MSK | UART_STATUS_BI_MSK | UART_STATUS_RFE_MSK))
|
{
|
// TODO: user handle the error case
|
// Clear error bits
|
uart_handle[id].base->INTR_CLR = UART_INTR_CLR_LSR_INT_CLR_MSK;
|
status |= UART_ERR_LINE;
|
ret = DRV_ERROR;
|
break;
|
}
|
|
if (status & UART_STATUS_DR_MSK)
|
{
|
uint8_t data = (uint8_t)uart_handle[id].base->RX_DATA;
|
uart_handle[id].rx_buff[uart_handle[id].rx_count++] = data;
|
}
|
}
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_RX);
|
|
if (uart_handle[id].rx_callback)
|
{
|
uart_handle[id].rx_callback(&id, status);
|
}
|
#endif
|
}
|
|
return ret;
|
}
|
|
int uart_tx_abort_dma(enum UART_DEV_T id, drv_callback_t abort_tx_callback)
|
{
|
int ret = DRV_ERROR;
|
#if UART_DMA_MODE_EN
|
if (uart_handle[id].dma_en)
|
{
|
// reset tx FIFO, de-assert the DMA TX request
|
uart_handle[id].base->CTRL0 = uart_handle[id].ctrl0 | UART_CTRL0_TX_FIFO_RESET_MSK;
|
uart_handle[id].tx_abort_callback = abort_tx_callback;
|
ret = dma_abort(uart_handle[id].dma_tx_ch, uart_dma_abort_callback);
|
}
|
#endif
|
return ret;
|
}
|
|
int uart_rx_abort_dma(enum UART_DEV_T id, drv_callback_t abort_rx_callback)
|
{
|
int ret = DRV_ERROR;
|
#if UART_DMA_MODE_EN
|
if (uart_handle[id].dma_en)
|
{
|
// RX err - disable interrupts
|
uart_handle[id].base->INTR_EN &= ~(UART_INTR_EN_ELCOLR_MSK | UART_INTR_EN_ELSI_MSK);
|
// reset rx FIFO, de-assert the DMA RX request
|
uart_handle[id].base->CTRL0 = uart_handle[id].ctrl0 | UART_CTRL0_RX_FIFO_RESET_MSK;
|
uart_handle[id].rx_abort_callback = abort_rx_callback;
|
ret = dma_abort(uart_handle[id].dma_rx_ch, uart_dma_abort_callback);
|
}
|
#endif
|
return ret;
|
}
|
|
#if defined(__GNUC__)
|
#pragma GCC diagnostic pop
|
#endif
|
|
#if UART_DMA_MODE_EN
|
static void uart_dma_abort_callback(void *ch, uint32_t err_code)
|
{
|
uint8_t ch_num = *(uint8_t *)ch;
|
drv_callback_t usr_callback = NULL;
|
enum UART_DEV_T id;
|
|
if ((ch_num == uart_handle[UART_ID0].dma_tx_ch) || (ch_num == uart_handle[UART_ID1].dma_tx_ch))
|
{
|
id = (ch_num == uart_handle[UART_ID0].dma_tx_ch ? UART_ID0 : UART_ID1);
|
|
// TX dma abort
|
usr_callback = uart_handle[id].tx_abort_callback;
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_TX);
|
|
uart_handle[id].tx_abort_callback = NULL;
|
uart_handle[id].tx_buff = NULL;
|
uart_handle[id].tx_callback = NULL;
|
uart_handle[id].tx_count = 0;
|
uart_handle[id].tx_size = 0;
|
}
|
else if ((ch_num == uart_handle[UART_ID0].dma_rx_ch) || (ch_num == uart_handle[UART_ID1].dma_rx_ch))
|
{
|
id = (ch_num == uart_handle[UART_ID0].dma_rx_ch ? UART_ID0 : UART_ID1);
|
|
// RX dma abort
|
usr_callback = uart_handle[id].rx_abort_callback;
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_RX);
|
|
uart_handle[id].rx_abort_callback = NULL;
|
uart_handle[id].rx_buff = NULL;
|
uart_handle[id].rx_callback = NULL;
|
uart_handle[id].rx_count = 0;
|
uart_handle[id].rx_size = 0;
|
}
|
else
|
{
|
ASSERT(0, "Unexpected dma channel %d", ch_num);
|
}
|
|
// LOG_INFO(TRACE_MODULE_DRIVER, "uart_dma_abort_callback %d\r\n", ch_num);
|
|
if (usr_callback)
|
{
|
usr_callback(&id, err_code);
|
}
|
}
|
|
static void uart_dma_callback(void *ch, uint32_t err_code)
|
{
|
uint8_t ch_num = *(uint8_t *)ch;
|
drv_callback_t usr_callback = NULL;
|
enum UART_DEV_T id;
|
|
if ((ch_num == uart_handle[UART_ID0].dma_tx_ch) || (ch_num == uart_handle[UART_ID1].dma_tx_ch))
|
{
|
id = (ch_num == uart_handle[UART_ID0].dma_tx_ch ? UART_ID0 : UART_ID1);
|
|
if (err_code == DMA_INT_TYPE_DONE)
|
{
|
// TX done
|
usr_callback = uart_handle[id].tx_callback;
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_TX);
|
}
|
else
|
{
|
// update state
|
if ((uart_handle[id].state == UART_STATE_BUSY_TX_RX) || (uart_handle[id].state == UART_STATE_BUSY_TX))
|
{
|
uart_handle[id].state = UART_STATE_ERROR;
|
}
|
else
|
{
|
ASSERT(0, "Unexpected uart %d state %d", id, uart_handle[id].state);
|
}
|
}
|
uart_handle[id].tx_buff = NULL;
|
uart_handle[id].tx_callback = NULL;
|
uart_handle[id].tx_count = 0;
|
uart_handle[id].tx_size = 0;
|
}
|
else if ((ch_num == uart_handle[UART_ID0].dma_rx_ch) || (ch_num == uart_handle[UART_ID1].dma_rx_ch))
|
{
|
id = (ch_num == uart_handle[UART_ID0].dma_rx_ch ? UART_ID0 : UART_ID1);
|
|
if (err_code == DMA_INT_TYPE_DONE)
|
{
|
// If rx err occurs and UART rx also requests dma, this trigger of rx dma done will be ignored
|
if (uart_handle[id].dma_rx_err_state)
|
{
|
uart_handle[id].dma_rx_err_state = 0;
|
return;
|
}
|
|
// RX done
|
usr_callback = uart_handle[id].rx_callback;
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_RX);
|
}
|
else
|
{
|
// update state
|
if ((uart_handle[id].state == UART_STATE_BUSY_TX_RX) || (uart_handle[id].state == UART_STATE_BUSY_RX))
|
{
|
uart_handle[id].state = UART_STATE_ERROR;
|
}
|
else
|
{
|
ASSERT(0, "Unexpected uart %d state %d", id, uart_handle[id].state);
|
}
|
}
|
uart_handle[id].rx_buff = NULL;
|
uart_handle[id].rx_callback = NULL;
|
uart_handle[id].rx_count = 0;
|
uart_handle[id].rx_size = 0;
|
}
|
else
|
{
|
ASSERT(0, "Unexpected dma channel %d", ch_num);
|
}
|
|
if (usr_callback)
|
{
|
usr_callback(&id, err_code);
|
}
|
}
|
#endif
|
|
// NOTE: This interrupt handler updates variables in the 'handle' structure.
|
// Any other code which modifies these variables must be guarded against shared data issues.
|
void uart_irq_handler(enum UART_DEV_T id)
|
{
|
drv_callback_t usr_callback = NULL;
|
uint32_t err_code = 0;
|
|
// read interrupt ID
|
uint8_t iid = uart_handle[id].base->INTR_STATUS & 0x0f;
|
|
// If DMA is enabled, the uart interrupt handler is only used to handle the error events reported by the receiver
|
if (uart_handle[id].dma_en)
|
{
|
#if UART_DMA_MODE_EN
|
err_code = uart_handle[id].base->STATUS;
|
// iid == 0xC: The precondition that the timeout event will not be triggered is that the length of
|
// the received data is an integer multiple of the RX FIFO level
|
if ((err_code & UART_STATUS_ERROR_MSK) || (iid == 0x0C) || (iid == 0x01))
|
{
|
// RX err - disable interrupts
|
uart_handle[id].base->INTR_EN &= ~UART_INTR_EN_ELSI_MSK;
|
uart_handle[id].base->INTR_CLR = UART_INTR_CLR_LSR_INT_CLR_MSK;
|
uart_handle[id].dma_rx_err_state = UART_ERR_LINE | iid;
|
usr_callback = uart_handle[id].rx_callback;
|
err_code = UART_ERR_LINE | iid;
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_RX);
|
|
uart_handle[id].rx_buff = NULL;
|
uart_handle[id].rx_callback = NULL;
|
uart_handle[id].rx_count = 0;
|
uart_handle[id].rx_size = 0;
|
}
|
#endif
|
}
|
else
|
{
|
#if UART_INT_MODE_EN
|
switch (iid)
|
{
|
// modem status
|
case 0x0:
|
// clear int
|
uart_handle[id].base->INTR_CLR = UART_INTR_CLR_MSR_INT_CLR_MSK;
|
err_code = UART_ERR_MODEM | iid;
|
break;
|
|
// no interrupt pending
|
case 0x1:
|
break;
|
|
// TX_DATA empty
|
case 0x2:
|
// clear int
|
uart_handle[id].base->INTR_CLR = UART_INTR_CLR_THRE_INT_CLR_MSK;
|
if ((uart_handle[id].state == UART_STATE_BUSY_TX) || (uart_handle[id].state == UART_STATE_BUSY_TX_RX))
|
{
|
if (uart_handle[id].tx_count == uart_handle[id].tx_size)
|
{
|
// TX done - disable interrupt
|
uart_handle[id].base->INTR_EN &= ~UART_INTR_EN_ETBEI_MSK;
|
usr_callback = uart_handle[id].tx_callback;
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_TX);
|
|
uart_handle[id].tx_buff = NULL;
|
uart_handle[id].tx_callback = NULL;
|
uart_handle[id].tx_count = 0;
|
uart_handle[id].tx_size = 0;
|
}
|
else
|
{
|
// TX continue - write data to FIFO
|
while ((uart_handle[id].tx_count < uart_handle[id].tx_size) && (uart_handle[id].base->STATUS & UART_STATUS_TFNF_MSK))
|
{
|
uart_handle[id].base->TX_DATA = uart_handle[id].tx_buff[uart_handle[id].tx_count++];
|
}
|
}
|
}
|
else
|
{
|
// should not get this interrupt in any other state
|
ASSERT(0, "Uart %d state goes wrong %d", id, uart_handle[id].state);
|
}
|
break;
|
|
// timeout
|
case 0xc:
|
// No characters in or out of the RCVR FIFO during the last 4 character times and
|
// there is at least 1 character in it during this time
|
|
// received data available
|
case 0x4:
|
if ((uart_handle[id].state == UART_STATE_BUSY_RX) || (uart_handle[id].state == UART_STATE_BUSY_TX_RX))
|
{
|
// uint8_t len = uart_handle[id].base->RX_FL;
|
while ((uart_handle[id].rx_count < uart_handle[id].rx_size) && (uart_handle[id].base->STATUS & UART_STATUS_RFNE_MSK))
|
{
|
uint8_t data = (uint8_t)uart_handle[id].base->RX_DATA;
|
uart_handle[id].rx_buff[uart_handle[id].rx_count++] = data;
|
}
|
|
if (uart_handle[id].rx_count == uart_handle[id].rx_size)
|
{
|
// RX done - disable interrupts
|
uart_handle[id].base->INTR_EN &= ~(UART_INTR_EN_ELSI_MSK | UART_INTR_EN_ERBFI_MSK);
|
usr_callback = uart_handle[id].rx_callback;
|
|
// update state
|
uart_state_clear(id, UART_STATE_BUSY_RX);
|
|
uart_handle[id].rx_buff = NULL;
|
uart_handle[id].rx_callback = NULL;
|
uart_handle[id].rx_count = 0;
|
uart_handle[id].rx_size = 0;
|
}
|
}
|
else
|
{
|
// received unexpected data
|
}
|
break;
|
|
// receiver line status
|
case 0x6:
|
// clear int
|
uart_handle[id].base->INTR_CLR = UART_INTR_CLR_LSR_INT_CLR_MSK;
|
err_code = UART_ERR_LINE | iid;
|
break;
|
|
// busy detect
|
case 0x7:
|
// clear int
|
uart_handle[id].base->INTR_CLR = UART_INTR_CLR_BUSY_INT_CLR_MSK;
|
err_code = UART_ERR_BUSY | iid;
|
break;
|
|
default:
|
break;
|
}
|
#endif
|
}
|
|
if (usr_callback)
|
{
|
usr_callback(&id, err_code);
|
}
|
}
|
|
void UART0_IRQHandler(void)
|
{
|
uart_irq_handler(UART_ID0);
|
}
|
|
void UART1_IRQHandler(void)
|
{
|
uart_irq_handler(UART_ID1);
|
}
|
|
int uart_printf_init(enum UART_DEV_T id)
|
{
|
struct UART_CFG_T uart_print_cfg = {
|
.parity = UART_PARITY_NONE,
|
.stop = UART_STOP_BITS_1,
|
.data = UART_DATA_BITS_8,
|
.flow = UART_FLOW_CONTROL_NONE,
|
.tx_level = UART_TXFIFO_EMPTY,
|
.rx_level = UART_RXFIFO_CHAR_1,
|
.baud = BAUD_921600,
|
.dma_en = false,
|
.int_rx = false,
|
.int_tx = false,
|
};
|
return uart_open(id, &uart_print_cfg);
|
}
|
|
#if TRACE_EN
|
__attribute__((__format__(__printf__, 2, 0))) void uart_printf(enum UART_DEV_T id, const char *fmt, ...)
|
{
|
char buf[100] = {0};
|
int len;
|
|
va_list argp;
|
va_start(argp, fmt);
|
len = trace_format(buf, sizeof(buf), fmt, argp);
|
va_end(argp);
|
|
uart_send(id, (uint8_t *)buf, (uint32_t)(len > 0 ? len : 0), NULL);
|
}
|
|
#else
|
void uart_printf(enum UART_DEV_T id, const char *fmt, ...)
|
{
|
}
|
#endif
|