Files
zTC1/mico-os/platform/MCU/RTL8711/peripherals/platform_uart.c
2025-03-11 15:54:45 +08:00

680 lines
20 KiB
C

/**
******************************************************************************
* @file paltform_uart.c
* @author William Xu
* @version V1.0.0
* @date 05-May-2014
* @brief This file provide UART driver functions.
******************************************************************************
* UNPUBLISHED PROPRIETARY SOURCE CODE
* Copyright (c) 2016 MXCHIP Inc.
*
* The contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of MXCHIP Corporation.
******************************************************************************
*/
#include "mico_rtos.h"
#include "mico_platform.h"
#include "platform.h"
#include "platform_peripheral.h"
#include "debug.h"
#include "pinmap.h"
#define mico_log(M, ...) custom_log("MICO", M, ##__VA_ARGS__)
#define mico_log_trace() custom_log_trace("MICO")
/******************************************************
* Constants
******************************************************/
/******************************************************
* Enumerations
******************************************************/
/******************************************************
* Type Definitions
******************************************************/
/******************************************************
* Structures
******************************************************/
/******************************************************
* Variables Definitions
******************************************************/
static const PinMap PinMap_UART_TX[] = {
{PC_3, RTL_PIN_PERI(UART0, 0, S0), RTL_PIN_FUNC(UART0, S0)},
{PE_0, RTL_PIN_PERI(UART0, 0, S1), RTL_PIN_FUNC(UART0, S1)},
{PA_7, RTL_PIN_PERI(UART0, 0, S2), RTL_PIN_FUNC(UART0, S2)},
{PD_3, RTL_PIN_PERI(UART1, 1, S0), RTL_PIN_FUNC(UART1, S0)},
{PE_4, RTL_PIN_PERI(UART1, 1, S1), RTL_PIN_FUNC(UART1, S1)},
{PB_5, RTL_PIN_PERI(UART1, 1, S2), RTL_PIN_FUNC(UART1, S2)},
{PA_4, RTL_PIN_PERI(UART2, 2, S0), RTL_PIN_FUNC(UART2, S0)},
{PC_9, RTL_PIN_PERI(UART2, 2, S1), RTL_PIN_FUNC(UART2, S1)},
{PD_7, RTL_PIN_PERI(UART2, 2, S2), RTL_PIN_FUNC(UART2, S2)},
{NC, NC, 0}
};
static const PinMap PinMap_UART_RX[] = {
{PC_0, RTL_PIN_PERI(UART0, 0, S0), RTL_PIN_FUNC(UART0, S0)},
{PE_3, RTL_PIN_PERI(UART0, 0, S1), RTL_PIN_FUNC(UART0, S1)},
{PA_6, RTL_PIN_PERI(UART0, 0, S2), RTL_PIN_FUNC(UART0, S2)},
{PD_0, RTL_PIN_PERI(UART1, 1, S0), RTL_PIN_FUNC(UART1, S0)},
{PE_7, RTL_PIN_PERI(UART1, 1, S1), RTL_PIN_FUNC(UART1, S1)},
{PB_4, RTL_PIN_PERI(UART1, 1, S2), RTL_PIN_FUNC(UART1, S2)},
{PA_0, RTL_PIN_PERI(UART2, 2, S0), RTL_PIN_FUNC(UART2, S0)},
{PC_6, RTL_PIN_PERI(UART2, 2, S1), RTL_PIN_FUNC(UART2, S1)},
{PD_4, RTL_PIN_PERI(UART2, 2, S2), RTL_PIN_FUNC(UART2, S2)},
{NC, NC, 0}
};
static uint8_t tmp_data_out;
#define UART_NUM (3)
#define SERIAL_TX_IRQ_EN 0x01
#define SERIAL_RX_IRQ_EN 0x02
#define SERIAL_TX_DMA_EN 0x01
#define SERIAL_RX_DMA_EN 0x02
//static uint32_t serial_irq_ids[UART_NUM] = {0, 0, 0};
//static uart_irq_handler irq_handler[UART_NUM];
//static uint32_t serial_irq_en[UART_NUM]={0, 0, 0};
#ifdef CONFIG_GDMA_EN
static uint32_t serial_dma_en[UART_NUM] = {0, 0, 0};
static HAL_GDMA_OP UartGdmaOp;
#endif
extern u32 ConfigDebugErr;
extern u32 ConfigDebuginfo;
/******************************************************
* Static Function Declarations
******************************************************/
/* Interrupt service functions - called from interrupt vector table */
#ifndef NO_MICO_RTOS
static void thread_wakeup(void *arg);
static void RX_PIN_WAKEUP_handler(void *arg);
#endif
/******************************************************
* Function Definitions
******************************************************/
OSStatus rtk_uart_init( platform_uart_driver_t* driver, platform_uart_t* peripheral, const platform_uart_config_t* config, ring_buffer_t* optional_ring_buffer )
{
OSStatus err = kNoErr;
uint32_t uart_tx, uart_rx;
uint32_t uart_sel;
uint8_t uart_idx;
PHAL_RUART_OP pHalRuartOp;
PHAL_RUART_ADAPTER pHalRuartAdapter;
#ifdef CONFIG_GDMA_EN
PUART_DMA_CONFIG pHalRuartDmaCfg;
PHAL_GDMA_OP pHalGdmaOp=&UartGdmaOp;
#endif
ConfigDebugErr &= (~_DBG_UART_);
ConfigDebugInfo &= (~_DBG_UART_);
//rtk_uart_deinit(driver);
// Determine the UART to use (UART0, UART1, or UART3)
uart_tx = pinmap_peripheral(peripheral->tx, PinMap_UART_TX);
uart_rx = pinmap_peripheral(peripheral->rx, PinMap_UART_RX);
uart_sel = pinmap_merge(uart_tx, uart_rx);
uart_idx = RTL_GET_PERI_IDX(uart_sel);
if (unlikely(uart_idx == (uint8_t)NC)) {
DBG_UART_ERR("%s: Cannot find matched UART\n", __FUNCTION__);
return kParamErr;
}
pHalRuartOp = &(peripheral->hal_uart_op);
pHalRuartAdapter = &(peripheral->hal_uart_adp);
if ((NULL == pHalRuartOp) || (NULL == pHalRuartAdapter)) {
DBG_UART_ERR("%s: Allocate Adapter Failed\n", __FUNCTION__);
return kParamErr;
}
HalRuartOpInit((VOID*)pHalRuartOp);
#ifdef CONFIG_GDMA_EN
HalGdmaOpInit((VOID*)pHalGdmaOp);
pHalRuartDmaCfg = &peripheral->uart_gdma_cfg;
pHalRuartDmaCfg->pHalGdmaOp = pHalGdmaOp;
pHalRuartDmaCfg->pTxHalGdmaAdapter = &peripheral->uart_gdma_adp_tx;
pHalRuartDmaCfg->pRxHalGdmaAdapter = &peripheral->uart_gdma_adp_rx;
_memset((void*)(pHalRuartDmaCfg->pTxHalGdmaAdapter), 0, sizeof(HAL_GDMA_ADAPTER));
_memset((void*)(pHalRuartDmaCfg->pRxHalGdmaAdapter), 0, sizeof(HAL_GDMA_ADAPTER));
#endif
pHalRuartOp->HalRuartAdapterLoadDef(pHalRuartAdapter, uart_idx);
pHalRuartAdapter->PinmuxSelect = RTL_GET_PERI_SEL(uart_sel);
//pHalRuartAdapter->BaudRate = 9600;
pHalRuartAdapter->IrqHandle.Priority = 6;
pHalRuartAdapter->BaudRate = config->baud_rate;
// HalRuartInit(pHalRuartAdapter);
//HalRuartSetBaudRate((VOID*)pHalRuartAdapter);
switch ( config->data_width)
{
case DATA_WIDTH_7BIT :
pHalRuartAdapter->WordLen = RUART_WLS_7BITS;
break;
case DATA_WIDTH_8BIT:
pHalRuartAdapter->WordLen = RUART_WLS_8BITS;
break;
default:
err = kParamErr;
goto exit;
}
switch ( config->parity )
{
case NO_PARITY:
pHalRuartAdapter->Parity = RUART_PARITY_DISABLE;
break;
case EVEN_PARITY:
pHalRuartAdapter->Parity = RUART_PARITY_ENABLE;
pHalRuartAdapter->ParityType = RUART_EVEN_PARITY;
break;
case ODD_PARITY:
pHalRuartAdapter->Parity = RUART_PARITY_ENABLE;
pHalRuartAdapter->ParityType = RUART_ODD_PARITY;
break;
default:
err = kParamErr;
goto exit;
}
switch ( config->stop_bits )
{
case STOP_BITS_1:
pHalRuartAdapter->StopBit = RUART_STOP_BIT_1;
break;
case STOP_BITS_2:
pHalRuartAdapter->StopBit = RUART_STOP_BIT_2;
break;
default:
err = kParamErr;
goto exit;
}
switch ( config->flow_control )
{
case FLOW_CONTROL_DISABLED:
case FLOW_CONTROL_RTS:
pHalRuartAdapter->FlowControl = AUTOFLOW_DISABLE;
break;
case FLOW_CONTROL_CTS:
case FLOW_CONTROL_CTS_RTS:
pHalRuartAdapter->FlowControl = AUTOFLOW_ENABLE;
break;
default:
err = kParamErr;
goto exit;
}
//pHalRuartOp->HalRuartInit(pHalRuartAdapter);
//pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter);
//pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter);
HalRuartInit(pHalRuartAdapter);
HalRuartSetBaudRateRtl8195a(pHalRuartAdapter);
pHalRuartAdapter->TxCompCallback = (void(*)(void*))platform_uart_tx_dma_irq;
pHalRuartAdapter->TxCompCbPara = (void*)driver;
if(!optional_ring_buffer){
pHalRuartAdapter->RxCompCallback = (void(*)(void*))platform_uart_rx_dma_irq;
pHalRuartAdapter->RxCompCbPara = (void*)driver;
}else{
pHalRuartAdapter->RxDRCallback = (void(*)(void*))platform_uart_irq;
pHalRuartAdapter->RxDRCbPara = (void*)driver;
pHalRuartAdapter->Interrupts |= RUART_IER_ERBI | RUART_IER_ELSI;
HalRuartSetIMRRtl8195a (pHalRuartAdapter);
}
//pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter);
//pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter);
pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter);
pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter);
exit:
return err;
}
OSStatus rtk_uart_deinit( platform_uart_driver_t* driver)
{
OSStatus err = kNoErr;
PHAL_RUART_ADAPTER pHalRuartAdapter;
#ifdef CONFIG_GDMA_EN
u8 uart_idx;
PUART_DMA_CONFIG pHalRuartDmaCfg;
#endif
pHalRuartAdapter = &(driver->peripheral->hal_uart_adp);
HalRuartDeInit(pHalRuartAdapter);
#ifdef CONFIG_GDMA_EN
uart_idx = pHalRuartAdapter->UartIndex;
pHalRuartDmaCfg = &driver->peripheral->uart_gdma_cfg;
if (serial_dma_en[uart_idx] & SERIAL_RX_DMA_EN) {
HalRuartRxGdmaDeInit(pHalRuartDmaCfg);
serial_dma_en[uart_idx] &= ~SERIAL_RX_DMA_EN;
}
if (serial_dma_en[uart_idx] & SERIAL_TX_DMA_EN) {
HalRuartTxGdmaDeInit(pHalRuartDmaCfg);
serial_dma_en[uart_idx] &= ~SERIAL_TX_DMA_EN;
}
#endif
exit:
return err;
}
// return the byte count received before timeout, or error(<0)
OSStatus rtk_uart_recv_stream_timeout (platform_uart_driver_t* driver, char *prxbuf, uint32_t len, uint32_t timeout_ms, void *force_cs)
{
PHAL_RUART_OP pHalRuartOp;
PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(driver->peripheral->hal_uart_adp);
uint32_t TimeoutCount=0, StartCount;
int ret;
void (*task_yield)(void);
task_yield = NULL;
pHalRuartOp = &(driver->peripheral->hal_uart_op);
ret = pHalRuartOp->HalRuartIntRecv(pHalRuartAdapter, (u8*)prxbuf, len);
if ((ret == HAL_OK) && (timeout_ms > 0)) {
TimeoutCount = (timeout_ms*1000/TIMER_TICK_US);
StartCount = HalTimerOp.HalTimerReadCount(1);
task_yield = (void(*)(void))force_cs;
while (pHalRuartAdapter->State & HAL_UART_STATE_BUSY_RX) {
if (HAL_TIMEOUT == RuartIsTimeout(StartCount, TimeoutCount)) {
ret = pHalRuartOp->HalRuartStopRecv((VOID*)pHalRuartAdapter);
ret = pHalRuartOp->HalRuartResetRxFifo((VOID*)pHalRuartAdapter);
pHalRuartAdapter->Status = HAL_UART_STATUS_TIMEOUT;
return kTimeoutErr;
}
if (NULL != task_yield) {
task_yield();
}
}
return kNoErr;
}else {
return kGeneralErr;
}
}
int rtk_uart_readable(platform_uart_driver_t* driver)
{
PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(driver->peripheral->hal_uart_adp);
u8 uart_idx = pHalRuartAdapter->UartIndex;
if ((HAL_RUART_READ32(uart_idx, RUART_LINE_STATUS_REG_OFF)) & RUART_LINE_STATUS_REG_DR) {
return 1;
}
else {
return 0;
}
}
int rtk_uart_getc(platform_uart_driver_t* driver)
{
PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(driver->peripheral->hal_uart_adp);
u8 uart_idx = pHalRuartAdapter->UartIndex;
while (!rtk_uart_readable(driver));
return (int)((HAL_RUART_READ32(uart_idx, RUART_REV_BUF_REG_OFF)) & 0xFF);
}
OSStatus platform_uart_init( platform_uart_driver_t* driver, const platform_uart_t* peripheral, const platform_uart_config_t* config, ring_buffer_t* optional_ring_buffer )
{
OSStatus err = kNoErr;
platform_mcu_powersave_disable();
require_action_quiet( ( driver != NULL ) && ( peripheral != NULL ) && ( config != NULL ), exit, err = kParamErr);
require_action_quiet( (optional_ring_buffer == NULL) || ((optional_ring_buffer->buffer != NULL ) && (optional_ring_buffer->size != 0)), exit, err = kParamErr);
driver->rx_size = 0;
driver->tx_size = 0;
driver->last_transmit_result = kNoErr;
driver->last_receive_result = kNoErr;
driver->peripheral = (platform_uart_t*)peripheral;
#ifndef NO_MICO_RTOS
mico_rtos_init_semaphore( &driver->tx_complete, 1 );
mico_rtos_init_semaphore( &driver->rx_complete, 1 );
mico_rtos_init_semaphore( &driver->sem_wakeup, 1 );
mico_rtos_init_mutex ( &driver->tx_mutex );
#else
driver->tx_complete = false;
driver->rx_complete = false;
#endif
err = rtk_uart_init(driver, (platform_uart_t*)peripheral, config, optional_ring_buffer);
if (optional_ring_buffer != NULL){
/* Note that the ring_buffer should've been initialised first */
driver->rx_buffer = optional_ring_buffer;
driver->rx_size = 0;
//platform_uart_receive_bytes( uart, optional_rx_buffer->buffer, optional_rx_buffer->size, 0 );
}
exit:
MicoMcuPowerSaveConfig(true);
return err;
}
OSStatus platform_uart_deinit( platform_uart_driver_t* driver )
{
uint8_t uart_number;
OSStatus err = kNoErr;
platform_mcu_powersave_disable();
require_action_quiet( ( driver != NULL ), exit, err = kParamErr);
err = rtk_uart_deinit(driver);
#ifndef NO_MICO_RTOS
mico_rtos_deinit_semaphore( &driver->rx_complete );
mico_rtos_deinit_semaphore( &driver->tx_complete );
mico_rtos_deinit_mutex( &driver->tx_mutex );
#else
driver->rx_complete = false;
driver->tx_complete = false;
#endif
driver->rx_size = 0;
driver->tx_size = 0;
driver->last_transmit_result = kNoErr;
driver->last_receive_result = kNoErr;
exit:
platform_mcu_powersave_enable();
return err;
}
OSStatus platform_uart_transmit_bytes( platform_uart_driver_t* driver, const uint8_t* data_out, uint32_t size )
{
OSStatus err = kNoErr;
#ifndef NO_MICO_RTOS
mico_rtos_lock_mutex(&driver->tx_mutex);
#endif
if( size == 1 )
{
tmp_data_out = *data_out;
data_out = &tmp_data_out;
}
platform_mcu_powersave_disable();
PHAL_RUART_OP pHalRuartOp;
PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(driver->peripheral->hal_uart_adp);
u8 uart_idx = pHalRuartAdapter->UartIndex;
int32_t ret;
pHalRuartOp = &(driver->peripheral->hal_uart_op);
if ((serial_dma_en[uart_idx] & SERIAL_TX_DMA_EN)==0) {
PUART_DMA_CONFIG pHalRuartDmaCfg;
pHalRuartDmaCfg = &driver->peripheral->uart_gdma_cfg;
if (HAL_OK == HalRuartTxGdmaInit(pHalRuartOp, pHalRuartAdapter, pHalRuartDmaCfg)) {
serial_dma_en[uart_idx] |= SERIAL_TX_DMA_EN;
}
else {
return kGeneralErr;
}
}
HalRuartEnterCritical(pHalRuartAdapter);
ret = pHalRuartOp->HalRuartDmaSend(pHalRuartAdapter, (u8*)data_out, size);
HalRuartExitCritical(pHalRuartAdapter);
if(ret != HAL_OK)
return kGeneralErr;
/* Wait for transmission complete */
#ifndef NO_MICO_RTOS
mico_rtos_get_semaphore( &driver->tx_complete, MICO_NEVER_TIMEOUT );
#else
while( driver->tx_complete == false );
driver->tx_complete = false;
#endif
/* Disable DMA and clean up */
driver->tx_size = 0;
err = driver->last_transmit_result;
exit:
platform_mcu_powersave_enable();
#ifndef NO_MICO_RTOS
mico_rtos_unlock_mutex(&driver->tx_mutex);
#endif
return err;
}
OSStatus platform_uart_receive_bytes( platform_uart_driver_t* driver, uint8_t* data_in, uint32_t expected_data_size, uint32_t timeout_ms )
{
OSStatus err = kNoErr;
//platform_mcu_powersave_disable();
require_action_quiet( ( driver != NULL ) && ( data_in != NULL ) && ( expected_data_size != 0 ), exit, err = kParamErr);
if ( driver->rx_buffer != NULL )
{
/* Check if ring buffer already contains the required amount of data. */
if ( expected_data_size > ring_buffer_used_space( driver->rx_buffer ) )
{
/* Set rx_size and wait in rx_complete semaphore until data reaches rx_size or timeout occurs */
driver->rx_size = expected_data_size;
if(expected_data_size <= ring_buffer_used_space( driver->rx_buffer ))
{
goto end;
}
#ifndef NO_MICO_RTOS
if ( mico_rtos_get_semaphore( &driver->rx_complete, timeout_ms) != kNoErr )
{
driver->rx_size = 0;
return kTimeoutErr;
}
#else
driver->rx_complete = false;
if(expected_data_size <= ring_buffer_used_space( driver->rx_buffer ))
{
goto end;
}
int delay_start = mico_get_time_no_os();
while(driver->rx_complete == false){
if(mico_get_time_no_os() >= delay_start + timeout_ms && timeout_ms != MICO_NEVER_TIMEOUT){
driver->rx_size = 0;
return kTimeoutErr;
}
}
#endif
end:
/* Reset rx_size to prevent semaphore being set while nothing waits for the data */
driver->rx_size = 0;
}
// Grab data from the buffer
uint32_t read_size = 0;
ring_buffer_read(driver->rx_buffer, data_in, expected_data_size, &read_size);
}
else
{
err = rtk_uart_recv_stream_timeout( driver, data_in, expected_data_size, timeout_ms, NULL );
}
exit:
//platform_mcu_powersave_enable();
return err;
}
uint32_t platform_uart_get_length_in_buffer( platform_uart_driver_t* driver )
{
return ring_buffer_used_space( driver->rx_buffer );
}
#if 0
uint8_t platform_uart_get_port_number( USART_TypeDef* uart )
{
if ( uart == USART1 )
{
return 0;
}
else if ( uart == USART2 )
{
return 1;
}
else if ( uart == USART3 )
{
return 2;
}
else if ( uart == UART4 )
{
return 3;
}
else if ( uart == UART5 )
{
return 4;
}
else if ( uart == USART6 )
{
return 5;
}
else
{
return 0xff;
}
}
#endif
#ifndef NO_MICO_RTOS
static void thread_wakeup(void *arg)
{
#if 0
platform_uart_driver_t* driver = arg;
while(1){
if( mico_rtos_get_semaphore( driver->sem_wakeup, 1000) != kNoErr )
{
platform_gpio_irq_enable( driver->peripheral->pin_rx, IRQ_TRIGGER_FALLING_EDGE, RX_PIN_WAKEUP_handler, driver );
platform_mcu_powersave_enable( );
}
}
#endif
}
#endif
/******************************************************
* Interrupt Service Routines
******************************************************/
#ifndef NO_MICO_RTOS
void RX_PIN_WAKEUP_handler(void *arg)
{
#if 0
(void)arg;
platform_uart_driver_t* driver = arg;
uint32_t uart_number;
platform_gpio_enable_clock( driver->peripheral->pin_rx );
uart_number = platform_uart_get_port_number( driver->peripheral->port );
uart_peripheral_clock_functions[ uart_number ]( uart_peripheral_clocks[ uart_number ], ENABLE );
/* Enable DMA peripheral clock */
if ( driver->peripheral->tx_dma_config.controller == DMA1 )
{
RCC->AHB1ENR |= RCC_AHB1Periph_DMA1;
}
else
{
RCC->AHB1ENR |= RCC_AHB1Periph_DMA2;
}
platform_gpio_irq_disable( driver->peripheral->pin_rx );
platform_mcu_powersave_disable( );
mico_rtos_set_semaphore( &driver->sem_wakeup );
#endif
}
#endif
void platform_uart_irq( platform_uart_driver_t* driver )
{
int status;
uint8_t rxData;
//platform_uart_port_t* uart = (platform_uart_port_t*) driver->peripheral->port;
rxData = rtk_uart_getc(driver);
ring_buffer_write( driver->rx_buffer, &rxData,1 );
status++;
//printf("\r\nnuart irq %d\r\n", status);
// Notify thread if sufficient data are available
if ( ( driver->rx_size > 0 ) &&
( ring_buffer_used_space( driver->rx_buffer ) >= driver->rx_size ) )
{
#ifndef NO_MICO_RTOS
mico_rtos_set_semaphore( &driver->rx_complete );
#else
driver->rx_complete = true;
#endif
driver->rx_size = 0;
}
}
void platform_uart_tx_dma_irq( platform_uart_driver_t* driver )
{
#ifndef NO_MICO_RTOS
/* Set semaphore regardless of result to prevent waiting thread from locking up */
mico_rtos_set_semaphore( &driver->tx_complete );
#else
driver->tx_complete = true;
#endif
}
void platform_uart_rx_dma_irq( platform_uart_driver_t* driver )
{
mico_log("uart irq");
/* Set semaphore regardless of result to prevent waiting thread from locking up */
#ifndef NO_MICO_RTOS
mico_rtos_set_semaphore( &driver->rx_complete );
#else
driver->rx_complete = true;
#endif
}