/* * 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