/*!
 *     COPYRIGHT NOTICE
 *     Copyright (c) 2013,山外科技
 *     All rights reserved.
 *     技术讨论：山外论坛 http://www.vcan123.com
 *
 *     除注明出处外，以下所有内容版权均属山外科技所有，未经允许，不得用于商业用途，
 *     修改内容时必须保留山外科技的版权声明。
 *
 * @file       MK60_uart.c
 * @brief      uart串口函数
 * @author     山外科技
 * @version    v5.2
 * @date       2014-10-09
 */


#include "common.h"
#include "MK60_uart.h"

UART_MemMapPtr UARTN[UART_MAX] = {UART0_BASE_PTR, UART1_BASE_PTR, UART2_BASE_PTR, UART3_BASE_PTR, UART4_BASE_PTR, UART5_BASE_PTR}; //定义五个指针数组保存 UARTN 的地址


/*!
 *  @brief      初始化串口，设置波特率
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      baud        波特率，如9600、19200、56000、115200等
 *  @since      v5.0
 *  @note       UART所用的管脚在 App\Inc\PORT_cfg.h 里进行配置，
                printf所用的管脚和波特率在 App\Inc\MK60_conf.h 里进行配置
 *  Sample usage:       uart_init (UART3, 9600);        //初始化串口3，波特率为9600
 */
void uart_init (UARTn_e uratn, uint32 baud)
{
    register uint16 sbr, brfa;
    uint8 temp;
    uint32 sysclk;     //时钟

    /* 配置 UART功能的 复用管脚 */
    switch(uratn)
    {
    case UART0:
        SIM_SCGC4 |= SIM_SCGC4_UART0_MASK;      //使能 UART0 时钟

        if(UART0_RX_PIN == PTA1)
        {
            port_init( UART0_RX_PIN, ALT2);
        }
        else if((UART0_RX_PIN == PTA15) || (UART0_RX_PIN == PTB16) || (UART0_RX_PIN == PTD6)  )
        {
            port_init( UART0_RX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        if(UART0_TX_PIN == PTA2)
        {
            port_init( UART0_TX_PIN, ALT2);
        }
        else if((UART0_TX_PIN == PTA14) || (UART0_TX_PIN == PTB17) || (UART0_TX_PIN == PTD7) )
        {
            port_init( UART0_TX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        break;

    case UART1:
        SIM_SCGC4 |= SIM_SCGC4_UART1_MASK;

        if((UART1_RX_PIN == PTC3) || (UART1_RX_PIN == PTE1))
        {
            port_init( UART1_RX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        if((UART1_TX_PIN == PTC4) || (UART1_TX_PIN == PTE0))
        {
            port_init( UART1_TX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        break;

    case UART2:
        SIM_SCGC4 |= SIM_SCGC4_UART2_MASK;
        if(UART2_TX_PIN == PTD3)
        {
            port_init( UART2_TX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }


        if(UART2_RX_PIN == PTD2)
        {
            port_init( UART2_RX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        break;

    case UART3:
        SIM_SCGC4 |= SIM_SCGC4_UART3_MASK;

        if((UART3_RX_PIN == PTB10)|| (UART3_RX_PIN == PTC16) || (UART3_RX_PIN == PTE5) )
        {
            port_init( UART3_RX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        if((UART3_TX_PIN == PTB11) || (UART3_TX_PIN == PTC17) || (UART3_TX_PIN == PTE4) )
        {
            port_init( UART3_TX_PIN, ALT3);             //在PTB11上使能UART3_RXD
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }
        break;

    case UART4:
        SIM_SCGC1 |= SIM_SCGC1_UART4_MASK;

        if((UART4_RX_PIN == PTC14) || (UART4_RX_PIN == PTE25)  )
        {
            port_init( UART4_RX_PIN, ALT3);            //在PTC14上使能UART4_RXD
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        if((UART4_TX_PIN == PTC15) || (UART4_TX_PIN == PTE24)  )
        {
            port_init( UART4_TX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }
        break;

    case UART5:
        SIM_SCGC1 |= SIM_SCGC1_UART5_MASK;

        if((UART5_RX_PIN == PTD8) || (UART5_RX_PIN == PTE9))
        {
            port_init( UART5_RX_PIN, ALT3);
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }

        if((UART5_TX_PIN == PTD9) ||(UART5_TX_PIN == PTE8))
        {
            port_init( UART5_TX_PIN, ALT3);             //在PTD9上使能UART5_RXD
        }
        else
        {
            ASSERT(0);                           //上诉条件都不满足，直接断言失败了，设置管脚有误？
        }
        break;
    default:
        break;
    }

    //设置的时候，应该禁止发送接受
    UART_C2_REG(UARTN[uratn]) &= ~(0
                                   | UART_C2_TE_MASK
                                   | UART_C2_RE_MASK
                                  );


    //配置成8位无校验模式
    //设置 UART 数据格式、校验方式和停止位位数。通过设置 UART 模块控制寄存器 C1 实现；
    UART_C1_REG(UARTN[uratn]) |= (0
                                  //| UART_C2_M_MASK                    //9 位或 8 位模式选择 : 0 为 8位 ，1 为 9位（注释了表示0，即8位） （如果是9位，位8在UARTx_C3里）
                                  //| UART_C2_PE_MASK                   //奇偶校验使能（注释了表示禁用）
                                  //| UART_C2_PT_MASK                   //校验位类型 : 0 为 偶校验 ，1 为 奇校验
                                 );

    //计算波特率，串口0、1使用内核时钟，其它串口使用bus时钟
    if ((uratn == UART0) || (uratn == UART1))
    {
        sysclk = core_clk_khz * 1000;                                   //内核时钟
    }
    else
    {
        sysclk = bus_clk_khz * 1000;                                    //bus时钟
    }

    //UART 波特率 = UART 模块时钟 / (16 × (SBR[12:0] + BRFA))
    //不考虑 BRFA 的情况下， SBR = UART 模块时钟 / (16 * UART 波特率)
    sbr = (uint16)(sysclk / (baud * 16));
    if(sbr > 0x1FFF)sbr = 0x1FFF;                                       //SBR 是 13bit，最大为 0x1FFF

    //已知 SBR ，则 BRFA =  = UART 模块时钟 / UART 波特率 - 16 ×SBR[12:0]
    brfa = (sysclk / baud)  - (sbr * 16);
    ASSERT( brfa <= 0x1F);                  //断言，如果此值不符合条件，则设置的条件不满足寄存器的设置
                                            //可以通过增大波特率来解决这个问题

    //写 SBR
    temp = UART_BDH_REG(UARTN[uratn]) & (~UART_BDH_SBR_MASK);           //缓存 清空 SBR 的 UARTx_BDH的值
    UART_BDH_REG(UARTN[uratn]) = temp |  UART_BDH_SBR(sbr >> 8);        //先写入SBR高位
    UART_BDL_REG(UARTN[uratn]) = UART_BDL_SBR(sbr);                     //再写入SBR低位

    //写 BRFD
    temp = UART_C4_REG(UARTN[uratn]) & (~UART_C4_BRFA_MASK) ;           //缓存 清空 BRFA 的 UARTx_C4 的值
    UART_C4_REG(UARTN[uratn]) = temp |  UART_C4_BRFA(brfa);             //写入BRFA



    //设置FIFO(FIFO的深度是由硬件决定的，软件不能设置)
    UART_PFIFO_REG(UARTN[uratn]) |= (0
                                     | UART_PFIFO_TXFE_MASK               //使能TX FIFO(注释表示禁止)
                                     //| UART_PFIFO_TXFIFOSIZE(0)         //（只读）TX FIFO 大小，0为1字节，1~6为 2^(n+1)字节
                                     | UART_PFIFO_RXFE_MASK               //使能RX FIFO(注释表示禁止)
                                     //| UART_PFIFO_RXFIFOSIZE(0)         //（只读）RX FIFO 大小，0为1字节，1~6为 2^(n+1)字节
                                    );

    /* 允许发送和接收 */
    UART_C2_REG(UARTN[uratn]) |= (0
                                  | UART_C2_TE_MASK                     //发送使能
                                  | UART_C2_RE_MASK                     //接收使能
                                  //| UART_C2_TIE_MASK                  //发送中断或DMA传输请求使能（注释了表示禁用）
                                  //| UART_C2_TCIE_MASK                 //发送完成中断使能（注释了表示禁用）
                                  //| UART_C2_RIE_MASK                  //接收满中断或DMA传输请求使能（注释了表示禁用）
                                 );

    //设置是否允许接收和发送中断。通过设置 UART 模块的 C2 寄存器的
    //RIE 和 TIE 位实现。如果使能中断，必须首先实现中断服务程序；
}

/*!
 *  @brief      等待接受1个字节
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      ch          接收地址
 *  @since      v5.0
 *  @note       如果需要查询接收状态，可用 uart_query ，
                如果需要查询接收数据，可用 uart_querychar
 *  Sample usage:
                        char ch;
                        uart_getchar (UART3,&ch);   //等待接受1个字节，保存到 ch里
 */
void uart_getchar (UARTn_e uratn, char *ch)
{
    //等待接收到数据（使能了FIFO后，以下两种方法都可行，都是一样的意思）
    //while( !UART_RCFIFO_REG(UARTN[uratn]) );                                //等待接收到数据数量非0
    //while( (UART_SFIFO_REG(UARTN[uratn]) & UART_SFIFO_RXEMPT_MASK)) ;     //等待接收缓冲区/FIFO 非空的

    //如下方法是飞思卡尔官方例程提供的方法
    while (!(UART_S1_REG(UARTN[uratn]) & UART_S1_RDRF_MASK));       //等待接收满了

    // 获取接收到的8位数据
    *ch =  UART_D_REG(UARTN[uratn]);

    // 获取 9位数据，应该是（需要修改函数的返回类型）：
    // *ch =   ((( UARTx_C3_REG(UARTN[uratn]) & UART_C3_R8_MASK ) >> UART_C3_R8_SHIFT ) << 8)   |   UART_D_REG(UARTN[uratn]);  //返回9bit

}


/*!
 *  @brief      查询接收1个字符
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      ch          接收地址
 *  @return     1为接收成功，0为接收失败
 *  @since      v5.0
 *  @note       如果需要等待接收，可用 uart_getchar
 *  Sample usage:       char ch ;
                        if( uart_querychar (UART3,&ch) == 1)     //查询接收1个字符，保存到 ch里
                        {
                            printf("成功接收到一个字节");
                        }
 */
char uart_querychar (UARTn_e uratn, char *ch)
{
    if( UART_RCFIFO_REG(UARTN[uratn]) )         //查询是否接受到数据
    {
        *ch  =   UART_D_REG(UARTN[uratn]);      //接受到8位的数据
        return  1;                              //返回 1 表示接收成功
    }

    *ch = 0;                                    //接收不到，应该清空了接收区
    return 0;                                   //返回0表示接收失败
}

/*!
 *  @brief      查询接收字符串
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      str         接收地址
 *  @param      max_len     最大接收长度
 *  @return     接收到的字节数目
 *  @since      v5.0
 *  Sample usage:       char str[100];
                        uint32 num;
                        num = uart_querystr (UART3,&str,100);
                        if( num != 0 )
                        {
                            printf("成功接收到%d个字节:%s",num,str);
                        }
 */
uint32 uart_querystr (UARTn_e uratn, char *str, uint32 max_len)
{
    uint32 i = 0,j;

    for(j=0;j<10000;j++)                 // 10000 的作用是延时，可自行根据情况修改
    {
        while(uart_querychar(uratn, str + i)  )
        {
            j=0;
            if( *(str + i) == NULL )    //接收到字符串结束符
            {
                return i;
            }

            i++;
            if(i >= max_len)            //超过设定的最大值，退出
            {
                *(str + i) = 0;     //确保字符串结尾是0x00
                return i;
            }
        }
    }

    *(str + i) = 0;                     //确保字符串结尾是0x00
    return i;
}


/*!
 *  @brief      查询接收buff
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      str         接收地址
 *  @param      max_len     最大接收长度
 *  @return     接收到的字节数目
 *  @since      v5.0
 *  Sample usage:       char buff[100];
                        uint32 num;
                        num = uart_querybuff (UART3,&buff,100);
                        if( num != 0 )
                        {
                            printf("成功接收到%d个字节:%s",num,buff);
                        }
 */
uint32 uart_querybuff (UARTn_e uratn, char *buff, uint32 max_len)
{
    uint32 i = 0,j;
    for(j=0;j<10000;j++)                 // 10000 的作用是延时，可自行根据情况修改
    {
        while(uart_querychar(uratn, buff + i)  )
        {
            i++;
            if(i >= max_len)            //超过设定的最大值，退出
            {
                return i;
            }
        }
    }

    return i;
}


/*!
 *  @brief      串口发送一个字节
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      ch          需要发送的字节
 *  @since      v5.0
 *  @note       printf需要用到此函数
 *  @see        fputc
 *  Sample usage:       uart_putchar (UART3, 'A');  //发送字节'A'
 */
void uart_putchar (UARTn_e uratn, char ch)
{
    //等待发送缓冲区空
    while(!(UART_S1_REG(UARTN[uratn]) & UART_S1_TDRE_MASK));

    //发送数据
    UART_D_REG(UARTN[uratn]) = (uint8)ch;
}

/*!
 *  @brief      查询是否接受到一个字节
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @return     接收到数据返回1，没接收到数据返回0
 *  @since      v5.0
 *  Sample usage:       char ch;
                        if(uart_query (UART3) == 1)     //查询是否接收到数据
                        {
                            ch = uart_getchar (UART3);  //等待接收一个数据，保存到 ch里
                        }
 */
char uart_query (UARTn_e uratn)
{
    if(UART_RCFIFO_REG(UARTN[uratn]))                 //接收到数据数量非0
        //if(!(UART_SFIFO_REG(UARTN[uratn]) & UART_SFIFO_RXEMPT_MASK))
        //if(UART_S1_REG(UARTN[uratn]) & UART_S1_TDRE_MASK)
    {
        return 1;
    }
    else
    {
        return 0;
    }
    //return UART_RCFIFO_REG(UARTN[uratn]);
}

/*!
 *  @brief      发送指定len个字节长度数组 （包括 NULL 也会发送）
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      buff        数组地址
 *  @param      len         发送数组的长度
 *  @since      v5.0
 *  Sample usage:       uart_putbuff (UART3,"1234567", 3); //实际发送了3个字节'1','2','3'
 */
void uart_putbuff (UARTn_e uratn, uint8 *buff, uint32 len)
{
    while(len--)
    {
        uart_putchar(uratn, *buff);
        buff++;
    }
}


/*!
 *  @brief      发送字符串(遇 NULL 停止发送)
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @param      str         字符串地址
 *  @since      v5.0
 *  Sample usage:       uart_putstr (UART3,"1234567"); //实际发送了7个字节
 */
void uart_putstr (UARTn_e uratn, const uint8 *str)
{
    while(*str)
    {
        uart_putchar(uratn, *str++);
    }
}

/*!
 *  @brief      开串口接收中断
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @since      v5.0
 *  Sample usage:       uart_rx_irq_en(UART3);         //开串口3接收中断
 */
void uart_rx_irq_en(UARTn_e uratn)
{
    UART_C2_REG(UARTN[uratn]) |= UART_C2_RIE_MASK;                          //使能UART接收中断
    enable_irq((IRQn_t)((uratn << 1) + UART0_RX_TX_IRQn));                  //使能IRQ中断
}

/*!
 *  @brief      开串口发送中断
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @since      v5.0
 *  Sample usage:       uart_tx_irq_en(UART3);         //开串口3发送中断
 */
void uart_tx_irq_en(UARTn_e uratn)
{
    UART_C2_REG(UARTN[uratn]) |= UART_C2_TIE_MASK;                          //使能UART发送中断
    enable_irq((IRQn_t)((uratn << 1) + UART0_RX_TX_IRQn));                  //使能IRQ中断
}

/*!
 *  @brief      开串口发送完成中断
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @since      v5.0
 *  Sample usage:       uart_tx_irq_en(UART3);         //开串口3发送中断
 */
void uart_txc_irq_en(UARTn_e uratn)
{
    UART_C2_REG(UARTN[uratn]) |= UART_C2_TCIE_MASK;                         //使能UART发送中断
    enable_irq((IRQn_t)((uratn << 1) + UART0_RX_TX_IRQn));                  //使能IRQ中断
}

/*!
 *  @brief      关串口接收中断
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @since      v5.0
 *  Sample usage:       uart_rx_irq_dis(UART3);         //关串口3接收中断
 */
void uart_rx_irq_dis(UARTn_e uratn)
{
    UART_C2_REG(UARTN[uratn]) &= ~UART_C2_RIE_MASK;                         //禁止UART接收中断

    //如果发送中断还没有关，则不关闭IRQ
    if(!(UART_C2_REG(UARTN[uratn]) & (UART_C2_TIE_MASK | UART_C2_TCIE_MASK)) )
    {
        disable_irq((IRQn_t)((uratn << 1) + UART0_RX_TX_IRQn));             //关IRQ中断
    }
}

/*!
 *  @brief      关串口发送中断
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @since      v5.0
 *  Sample usage:       uart_tx_irq_dis(UART3);         //关串口3发送中断
 */
void uart_txc_irq_dis(UARTn_e uratn)
{
    UART_C2_REG(UARTN[uratn]) &= ~UART_C2_TCIE_MASK;                        //禁止UART发送完成中断

    //如果接收中断还没有关，则不关闭IRQ
    if(!(UART_C2_REG(UARTN[uratn]) & UART_C2_RIE_MASK) )
    {
        disable_irq((IRQn_t)((uratn << 1) + UART0_RX_TX_IRQn));             //关IRQ中断
    }
}

/*!
 *  @brief      关串口发送中断
 *  @param      UARTn_e       模块号（UART0~UART5）
 *  @since      v5.0
 *  Sample usage:       uart_tx_irq_dis(UART3);         //关串口3发送中断
 */
void uart_tx_irq_dis(UARTn_e uratn)
{
    UART_C2_REG(UARTN[uratn]) &= ~UART_C2_TIE_MASK;                         //禁止UART发送中断

    //如果接收中断还没有关，则不关闭IRQ
    if(!(UART_C2_REG(UARTN[uratn]) & UART_C2_RIE_MASK) )
    {
        disable_irq((IRQn_t)((uratn << 1) + UART0_RX_TX_IRQn));             //关IRQ中断
    }
}

/*!
 *  @brief      UART3测试中断服务函数
 *  @since      v5.0
 *  @warning    此函数需要用户根据自己需求完成，这里仅仅是提供一个模版
 *  Sample usage:       set_vector_handler(UART3_RX_TX_VECTORn , uart3_test_handler);    //把 uart3_handler 函数添加到中断向量表，不需要我们手动调用
 */
void uart3_test_handler(void)
{
    UARTn_e uratn = UART3;

    if(UART_S1_REG(UARTN[uratn]) & UART_S1_RDRF_MASK)   //接收数据寄存器满
    {
        //用户需要处理接收数据

    }

    if(UART_S1_REG(UARTN[uratn]) & UART_S1_TDRE_MASK )  //发送数据寄存器空
    {
        //用户需要处理发送数据

    }
}