/*******************************************************************************
*
*
* File:         demo_hal_uart.c
* Description：  测试说明：将电脑端USB转串口分别连接UART0，1测试
* Version：      V0.1
* Date：         2023-07-09
* Author：       kc
 *******************************************************************************/

/*******************************************************************************
* 1.Included files
*******************************************************************************/
#include "stdarg.h"
#include "stdlib.h"
#include "stdio.h"
#include "test_cfg_global.h"
#include "hal_uart.h"
#include "hal_gpio.h"
#include "tau_log.h"
#include "tau_delay.h"
#include "demo_hal_uart.h"

#if _MODULE_DEMO_UART_EN
/*******************************************************************************
* 2.Global constant and macro definitions using #define
*******************************************************************************/
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "uart-log"

#define UART_TEST_AUTO                1

#define TEST_UART_BAUDRATE            115200

/*******************************************************************************
* 3.Global function and Global variable declarations
*******************************************************************************/

/**
 * @brief 测试case
 */
typedef enum
{
    UART_CASE_BLOCKING = 0,
    UART_CASE_NONE_BLOCKING,
    UART_CASE_DMA,
    UART_CASE_MAX,
    UART_CASE_LOOP_BACK,
    UART_CASE_DEINIT,
} uart_case_e;

typedef void (*unit_test_func)();
typedef struct unit_test_entry_t
{
    char case_name[128];                 /* Case名称 */
    unit_test_func case_func;            /* Case处理函数 */
} unit_test_entry_t;

/*******************************************************************************
* 4.Global variable extern declarations
*******************************************************************************/
static char sg_uart_log[256] = {0};
static char sg_uart_send_buff[1024] = {0};
static char sg_uart_recv_buff[1024] = {0};
static uart_case_e sg_uart_case = UART_CASE_BLOCKING;
static hal_uart_num_e sg_uart_cur_num = HAL_UART_0;
volatile static bool sg_uart_case_done = true;
static hal_uart_config_t s_huart;

/*******************************************************************************
* 5.Global function prototypes
*******************************************************************************/
void STRFMT(const char *fmt, ...)
{
    va_list ap;//初始化指向可变参数列表的指针
    //将第一个可变参数的地址付给ap，即ap指向可变参数列表的开始
    va_start(ap, fmt);
    //将参数fmt、ap指向的可变参数一起转换成格式化字符串，放string数组中，其作用同sprintf（），只是参数类型不同
    vsprintf(sg_uart_log, fmt, ap);
    va_end(ap);    //ap付值为0，没什么实际用处，主要是为程序健壮性
}

#define TEST_LOG(num,format,...)                                           \
    do {                                                                   \
        STRFMT(format, ##__VA_ARGS__);                                     \
        hal_uart_send_blocking(num, (uint8_t *)sg_uart_log, strlen(sg_uart_log));            \
    } while (0)
/**************************************************************************
* @name : TEST_UART_PinMux_Init
* @brief : UART 脚位功能初始化
* @param[in] :
* @return :
* @retval :
**************************************************************************/
static void test_uart_mode_init(void)
{
    //UART 0
    hal_gpio_set_mode(IO_PAD_UART0_TX, PIN2_MODE_UART0_TX);
    hal_gpio_set_mode(IO_PAD_UART0_RX, PIN36_MODE_UART0_RX);
    //UART 1 只有唯一一组
    //hal_gpio_set_mode(IO_PAD_UART1_TX, PIN14_MODE_UART1_TX);
    //hal_gpio_set_mode(IO_PAD_TD_TP_RESX, PIN13_MODE_UART1_RX);
}

/**************************************************************************
* @name :   test_uart_deinit
* @brief :  uart注销
* @param[in] :
* @return :
* @retval :
**************************************************************************/
static void test_uart_common_deinit(hal_uart_num_e num)
{
    delayMs(500);
    hal_uart_deinit(num);
}

/**************************************************************************
* @name :   test_uart_normal_init
* @brief :  uart 普通模式初始化
* @param[in] :
* @return :
* @retval :
**************************************************************************/
static void test_uart_common_init(hal_uart_num_e num)
{
    hal_uart_config_t huart = {0};
    huart.baudrate = TEST_UART_BAUDRATE;
    huart.data_width = HAL_UART_DATAWIDTH_8;
    huart.parity = HAL_UART_PARITY_NO;
    huart.stopbits = HAL_UART_STOPBIT_1;
    hal_uart_init(num, &huart);
}

/**
* @brief  获取端口号
* @param  无
* @retval 无
*/
static char test_uart_get_input_str(hal_uart_num_e num)
{
    char str = 0;
    hal_uart_recv_blocking(num, (uint8_t *)&str, 1);
    return str;
}

/**
* @brief  获取输入字符串个数
* @param  无
* @retval 无
*/
static uint32_t test_uart_get_input_num(hal_uart_num_e num)
{
    uint32_t recv = 0;
    char str[10] = {0};
    uint8_t i = 0;

    TEST_LOG(num, "input recv num and space to exit:");

    while (1)
    {
        hal_uart_recv_blocking(num, (uint8_t *)&str[i], 1);
        if (str[i] >= '0' && str[i] <= '9')
        {
            TEST_LOG(num, "%c", (char)str[i]);
            i++;
        }
        else if (str[i] == 32)
        {
            str[i] = 0;
            TEST_LOG(num, "\n");
            break;
        }
    }

    recv = atol(str);
    return recv;
}

/**
* @brief  复位uart所在PAD为GPIO mode
* @param  无
* @retval 无
*/
static void test_uart_reset_uart_mode(void)
{
    hal_gpio_init_input(IO_PAD_AP_SPIS_MOSI);
    hal_gpio_init_input(IO_PAD_UART0_TX);
    hal_gpio_init_input(IO_PAD_AP_SPIS_MISO);
    hal_gpio_init_input(IO_PAD_UART0_RX);
    hal_gpio_init_input(IO_PAD_AP_TPRSTN);
    hal_gpio_init_input(IO_PAD_AP_PWMEN);
    hal_gpio_init_input(IO_PAD_TD_SPIM_MOSI);
    hal_gpio_init_input(IO_PAD_UART1_TX);
    hal_gpio_init_input(IO_PAD_TD_TP_RESX);
}

/**
* @brief  在UART2上设置UART1的TX和RX PAD
* @param  无
* @retval 无
*/
static void test_uart_set_uart_mode(void)
{
    char str = 0;
    hal_uart_num_e uart_org_num = sg_uart_cur_num;

    TEST_LOG(uart_org_num, "select UART(0/1):\n");
    TEST_LOG(uart_org_num, "Enter:");
    str = test_uart_get_input_str(uart_org_num);
    TEST_LOG(uart_org_num, "%c\n\n", str);
    sg_uart_cur_num = (hal_uart_num_e)(str - '0');

    if (HAL_UART_1 == sg_uart_cur_num) /* 测试UART1 */
    {
        //sel tx
        TEST_LOG(uart_org_num, "select UART1 TX:\n");
        TEST_LOG(uart_org_num, "0:IO_PAD_UART1_TX->PIN14_MODE_UART1_TX\n");
        //sel rx
        TEST_LOG(uart_org_num, "select UART1 RX:\n");
        TEST_LOG(uart_org_num, "0:IO_PAD_TD_TP_RESX->PIN13_MODE_UART1_RX\n");
        TEST_LOG(uart_org_num, "change the connect pin and press space to continue \n");
        delayMs(200);

        test_uart_reset_uart_mode();
        hal_gpio_set_mode(IO_PAD_UART1_TX, PIN14_MODE_UART1_TX);
        hal_gpio_set_mode(IO_PAD_TD_TP_RESX, PIN13_MODE_UART1_RX);
        hal_uart_deinit(HAL_UART_0);
        test_uart_common_init(HAL_UART_1);
    }
    else
    {
        uint8_t sel_tx, sel_rx;
        //sel tx
        TEST_LOG(uart_org_num, "select UART0 TX:\n");
        TEST_LOG(uart_org_num, "0:IO_PAD_UART0_TX->PIN2_MODE_UART0_TX\n");
        TEST_LOG(uart_org_num, "1:IO_PAD_AP_SPIS_MOSI->PIN33_MODE_UART0_TX  \n");
        TEST_LOG(uart_org_num, "2:IO_PAD_TD_SPIM_MOSI->PIN4_MODE_UART0_TX\n");
        TEST_LOG(uart_org_num, "Enter:");
        str = test_uart_get_input_str(uart_org_num);
        TEST_LOG(uart_org_num, "%c\n\n", str);
        sel_tx = str - '0';

        //sel rx
        TEST_LOG(uart_org_num, "select UART0 RX:\n");
        TEST_LOG(uart_org_num, "0:IO_PAD_UART0_RX->PIN36_MODE_UART0_RX\n");
        TEST_LOG(uart_org_num, "1:IO_PAD_AP_SPIS_MISO->PIN30_MODE_UART0_RX\n");
        TEST_LOG(uart_org_num, "2:IO_PAD_AP_TPRSTN->PIN18_MODE_UART0_RX\n");
        TEST_LOG(uart_org_num, "3:IO_PAD_AP_PWMEN->PIN17_MODE_UART0_RX\n");
        TEST_LOG(uart_org_num, "4:IO_PAD_UART1_TX->PIN14_MODE_UART0_RX\n");
        TEST_LOG(uart_org_num, "Enter:");
        str = test_uart_get_input_str(uart_org_num);
        TEST_LOG(uart_org_num, "%c\n\n", str);
        sel_rx = str - '0';

        TEST_LOG(uart_org_num, "change the connect pin and press space to continue \n");
        delayMs(500);
        test_uart_reset_uart_mode();

        switch (sel_tx)
        {
        case 0:
            hal_gpio_set_mode(IO_PAD_UART0_TX, PIN2_MODE_UART0_TX);
            break;

        case 1:
            hal_gpio_set_mode(IO_PAD_AP_SPIS_MOSI, PIN33_MODE_UART0_TX);
            break;

        case 2:
            hal_gpio_set_mode(IO_PAD_TD_SPIM_MOSI, PIN4_MODE_UART0_TX);
            break;

        default:
            break;
        }

        switch (sel_rx)
        {
        case 0:
            hal_gpio_set_mode(IO_PAD_UART0_RX, PIN36_MODE_UART0_RX);
            break;

        case 1:
            hal_gpio_set_mode(IO_PAD_AP_SPIS_MISO, PIN30_MODE_UART0_RX);
            break;

        case 2:
            hal_gpio_set_mode(IO_PAD_AP_TPRSTN, PIN18_MODE_UART0_RX);
            break;

        case 3:
            hal_gpio_set_mode(IO_PAD_AP_PWMEN, PIN17_MODE_UART0_RX);
            break;

        case 4:
            hal_gpio_set_mode(IO_PAD_UART1_TX, PIN14_MODE_UART0_RX);
            break;

        default:
            break;
        }
        hal_uart_deinit(HAL_UART_1);
        test_uart_common_init(HAL_UART_0);
    }
    while (1)
    {
        str = test_uart_get_input_str(sg_uart_cur_num);
        if (str == 32)
        {
            break;
        }
    }
}

/**
* @brief  是否重复
* @param  无
* @retval 无
*/
static bool test_uart_sel_continue()
{
    uint8_t str = 0;
    delayMs(500);
    test_uart_common_deinit(sg_uart_cur_num);
    test_uart_common_init(sg_uart_cur_num);
    TEST_LOG(sg_uart_cur_num, " y repeat \n");
    TEST_LOG(sg_uart_cur_num, " n exit \n");
    while (1)
    {
        str = test_uart_get_input_str(sg_uart_cur_num);
        switch (str)
        {
        case 'y':
            TEST_LOG(sg_uart_cur_num, "y\n");
            return true;

        case 'n':
            TEST_LOG(sg_uart_cur_num, "n\n");
            return false;

        default:
            continue;
        }
    }
}

/**************************************************************************
* @name : test_uart_blocking
* @brief :  UART TX，RX阻塞读写。
* @param[in] :
* @return :
* @retval :
**************************************************************************/
static void test_uart_blocking(void)
{
    uint32_t recv = 0;
    delayMs(500);
    test_uart_common_deinit(sg_uart_cur_num);
    test_uart_common_init(sg_uart_cur_num);
    delayMs(500);
    TEST_LOG(sg_uart_cur_num, "test uart%d start.\n", sg_uart_cur_num);

    //test recv
    recv = test_uart_get_input_num(sg_uart_cur_num);
    TEST_LOG(sg_uart_cur_num, "input %d char one time:\n", recv);
    memset(sg_uart_recv_buff, 0, recv + 1);
    if (!hal_uart_recv_blocking(sg_uart_cur_num, (uint8_t *)sg_uart_recv_buff, recv))
    {
        TEST_LOG(sg_uart_cur_num, "an error has occurred!\n");
    }
    TEST_LOG(sg_uart_cur_num, "%s\n", sg_uart_recv_buff);

    //test send
    TEST_LOG(sg_uart_cur_num, "send:\n");
    strcpy(sg_uart_send_buff, sg_uart_recv_buff);
    if (!hal_uart_send_blocking(sg_uart_cur_num, (uint8_t *)sg_uart_send_buff, strlen(sg_uart_send_buff)))
    {
        TEST_LOG(sg_uart_cur_num, "an error has occurred!\n");
    }

    TEST_LOG(sg_uart_cur_num, "\n");

    sg_uart_case_done = true;
}

/**************************************************************************
* @name : test_uart_trans_cb
* @brief :  用户回调函数
* @param[in] :
* @return :
* @retval :
**************************************************************************/
static void test_uart_trans_cb(status_t status, void *user_data)
{
    switch (status)
    {
    case STATUS_UART_RX_IDLE:
    case STATUS_UART_TX_IDLE:
        *(bool *)user_data = true;
        break;

    default:
        break;
    }
}

/**************************************************************************
* @name : test_uart_none_blocking
* @brief :  UART TX，RX非阻塞读写。
* @param[in] :
* @return :
* @retval :
**************************************************************************/
static void test_uart_none_blocking(void)
{
    uint32_t recv = 0;
    volatile bool int_done = false;
    delayMs(500);
    test_uart_common_deinit(sg_uart_cur_num);
    s_huart.baudrate = TEST_UART_BAUDRATE;
    s_huart.data_width = HAL_UART_DATAWIDTH_8;
    s_huart.parity = HAL_UART_PARITY_NO;
    s_huart.stopbits = HAL_UART_STOPBIT_1;
    s_huart.callback = test_uart_trans_cb;
    s_huart.user_data = &int_done;
    hal_uart_init(sg_uart_cur_num, &s_huart);
    delayMs(500);
    TEST_LOG(sg_uart_cur_num, "test uart%d start...\n", sg_uart_cur_num);
    TEST_LOG(sg_uart_cur_num, "input and output 10 char:\n");
    int_done = false;
    memset(sg_uart_recv_buff, 0, recv + 1);
    while (!hal_uart_recv_none_blocking(sg_uart_cur_num, (uint8_t *)sg_uart_recv_buff, 10))
    {
        //TEST_LOG(sg_uart_cur_num, "device busy!\n");
    }
    while (!int_done);

    //test send
    int_done = false;
    strcpy(sg_uart_send_buff, sg_uart_recv_buff);
    while (!hal_uart_send_none_blocking(sg_uart_cur_num, (uint8_t *)sg_uart_send_buff, strlen(sg_uart_send_buff)))
    {
        //TEST_LOG(sg_uart_cur_num, "device busy!\n");
    }
    while (!int_done);

    TEST_LOG(sg_uart_cur_num, "\n");

    sg_uart_case_done = true;
}


/**************************************************************************
* @name : test_uart_dma_transmit
* @brief :  UART DMA读写测试
* @param[in] :
* @return :
* @retval :
**************************************************************************/
static void test_uart_dma_transmit(void)
{
    uint32_t recv = 0;
    char str = 0;
    bool int_done = false;

    while (1)
    {
        TEST_LOG(sg_uart_cur_num, "test uart%d start...\n", sg_uart_cur_num);
        recv = test_uart_get_input_num(sg_uart_cur_num);
        TEST_LOG(sg_uart_cur_num, "please input %d char once:\n", recv);

        test_uart_common_deinit(sg_uart_cur_num);
        s_huart.baudrate = TEST_UART_BAUDRATE;
        s_huart.data_width = HAL_UART_DATAWIDTH_8;
        s_huart.parity = HAL_UART_PARITY_NO;
        s_huart.stopbits = HAL_UART_STOPBIT_1;
        s_huart.callback = test_uart_trans_cb;
        s_huart.user_data = &int_done;
        hal_uart_init(sg_uart_cur_num, &s_huart);

        int_done = false;
        memset(sg_uart_recv_buff, 0, recv + 1);
        while (!hal_uart_dma_recv(sg_uart_cur_num, (uint8_t *)sg_uart_recv_buff, recv))
        {
            //TEST_LOG(sg_uart_cur_num, "device busy!\n");
        }
        while (!int_done)
        {
            delayMs(500);
        };

        TEST_LOG(sg_uart_cur_num, "%s\n", sg_uart_recv_buff);
        TEST_LOG(sg_uart_cur_num, "please input %d char twice:\n", recv);

        int_done = false;
        memset(sg_uart_recv_buff, 0, recv + 1);
        while (!hal_uart_dma_recv(sg_uart_cur_num, (uint8_t *)sg_uart_recv_buff, recv))
        {
            //TEST_LOG(sg_uart_cur_num, "device busy!\n");
        }
        while (!int_done)
        {
            delayMs(100);
            //TEST_LOG("recv dma not done!!!\n");
        };
        TEST_LOG(sg_uart_cur_num, "%s\n", sg_uart_recv_buff);

        int_done = false;
        strcpy(sg_uart_send_buff, "uart dma tx test comp once!\n");
        while (!hal_uart_dma_send(sg_uart_cur_num, (uint8_t *)sg_uart_send_buff, strlen(sg_uart_send_buff)))
        {
            //TEST_LOG(sg_uart_cur_num, "device busy!\n");
        }
        while (!int_done);

        int_done = false;
        strcpy(sg_uart_send_buff, "uart dma tx test comp twice!\n");
        while (!hal_uart_dma_send(sg_uart_cur_num, (uint8_t *)sg_uart_send_buff, strlen(sg_uart_send_buff)))
        {
            //TEST_LOG(sg_uart_cur_num, "device busy!\n");
        }
        while (!int_done);

        if (sg_uart_cur_num == HAL_UART_0)
        {
            hal_uart_dma_path_close(HAL_UART0_DMA_PATH_TX);
            hal_uart_dma_path_close(HAL_UART0_DMA_PATH_RX);
            //TEST_LOG(sg_uart_cur_num, "close uart0 dma channel\n");
        }
        else
        {
            hal_uart_dma_path_close(HAL_UART1_DMA_PATH_TX);
            hal_uart_dma_path_close(HAL_UART1_DMA_PATH_RX);
            //TEST_LOG(sg_uart_cur_num, "close uart1 dma channel\n");
        }
        delayMs(500);
        /* 用普通方式初始化uart */
        test_uart_common_deinit(sg_uart_cur_num);
        test_uart_common_init(sg_uart_cur_num);

        TEST_LOG(sg_uart_cur_num, "0:continue\n");
        TEST_LOG(sg_uart_cur_num, "1:exit\n");
        TEST_LOG(sg_uart_cur_num, "Enter:");
        while (1)
        {
            str = test_uart_get_input_str(sg_uart_cur_num);
            if ('0' == str)
            {
                TEST_LOG(sg_uart_cur_num, "%c\n", str);
                break;
            }
            else if ('1' == str)
            {
                TEST_LOG(sg_uart_cur_num, "%c\n", str);
                sg_uart_case_done = true;
                return;
            }
        }
    }
}


static const unit_test_entry_t sg_uart_unit_test_table[] =
{
    {"blocking case ...",      test_uart_blocking},
    {"none blocking case ...", test_uart_none_blocking},
    {"dma trans case ...",     test_uart_dma_transmit},
    {"end", NULL},
};

#if UART_TEST_AUTO
/**
* @brief  uart测试用例切换
* @param  无
* @retval 无
*/
static void test_uart_auto_case(void)
{
    for (sg_uart_case = UART_CASE_BLOCKING; sg_uart_case < UART_CASE_MAX; sg_uart_case++)
    {
        TEST_LOG(sg_uart_cur_num, "%s\n", sg_uart_unit_test_table[sg_uart_case].case_name);

        sg_uart_case_done = false;

        /*配置并启动定时器*/
        sg_uart_unit_test_table[sg_uart_case].case_func();

        /*等待超时中断*/
        while (!sg_uart_case_done);

        TEST_LOG(sg_uart_cur_num, "test case pass \n");
        TEST_LOG(sg_uart_cur_num, "\n");
    }
}
#else
/**
* @brief  uart测试用例切换
* @param  无
* @retval 无
*/
static void test_uart_unit_case(uint8_t case_num)
{
    TEST_LOG(sg_uart_cur_num, "%s\n", sg_uart_unit_test_table[case_num].case_name);

    sg_uart_case_done = false;

    /*配置并启动定时器*/
    sg_uart_unit_test_table[case_num].case_func();

    /*等待超时中断*/
    while (!sg_uart_case_done);

    TEST_LOG(sg_uart_cur_num, "test case pass!!\n");
}
#endif

/**************************************************************************
* @name :  demo_hal_uart_case
* @brief :
* @param[in] :
* @return :
* @retval :
**************************************************************************/
void demo_hal_uart_case(void)
{
    test_uart_common_init(sg_uart_cur_num);
    test_uart_mode_init();
    while (1)
    {
        test_uart_set_uart_mode();

#if UART_TEST_AUTO
        test_uart_auto_case();
#else
        test_uart_unit_case(sg_uart_case);
#endif

        if (!test_uart_sel_continue())
        {
            break;
        }
    }
    TEST_LOG(sg_uart_cur_num, "uart test done!\n");
}
#endif
