/******************************************************************************
文 件 名：Communication_Over_CAN.c
功能描述：基于CAN总线的信号收发函数库文件
作    者：张暄
版    本：V1.3
日    期：2017.5.31
******************************************************************************/

#include "Communication_Over_CAN.h"
#pragma MESSAGE DISABLE C4301

CoCANTxMsgFIFOStruct     CoCANTxMsgFIFO;
CoCANRxMsgFIFOStruct     CoCANRxMsgFIFO;

CoCANTxMsgIDLookupStruct CoCANTxMsgTable[CAN_TX_MSG_NUM];
CoCANRxMsgIDLookupStruct CoCANRxMsgTable[CAN_RX_MSG_NUM];

CoCANCycleMsgStruct      CoCANCycleMsg;

CoCANCtrlStruct          CoCANCtrl;

/******************************************************************************
函数名：CoCAN_L_Data_Indication
功  能：由链路层传递而来,用于指示数据的到达并传递数据
        该函数必须被下层(链路层)函数引用
参  数：Identifier ：CAN报文ID
        DLC        ：报文数据长度
        Data       ：报文数据内容
返回值：无
******************************************************************************/
void CoCAN_L_Data_Indication ( uint16_t Identifier, uint8_t DLC, uint8_t *Data )
{
    uint8_t i;

    //检查是否允许接收报文
    if ( CoCANCtrl.RxEnable != CoCAN_ENABLE )
        return;

    //检查FIFO是否已满
    if ( CoCANRxMsgFIFO.Depth >= CoCAN_RX_MSG_FIFO_MAX_DEPTH )
        return;

    //拷贝报文
    CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Iptr].Identifier = Identifier;

    for ( i = 0; i < DLC; i++ )
        CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Iptr].Data[i]  = Data[i];

    CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Iptr].DLC        = DLC;
    //FIFO处理
    CoCANRxMsgFIFO.Depth++;
    CoCANRxMsgFIFO.Iptr++;

    if ( CoCANRxMsgFIFO.Iptr >= CoCAN_RX_MSG_FIFO_MAX_DEPTH )
        CoCANRxMsgFIFO.Iptr = 0;
}

/******************************************************************************
函数名：CoCAN_L_Data_Request
功  能：向链路层传递报文,并请求发送
参  数：Identifier：报文ID
        DLC       ：报文长度
        Data      ：报文数据
返回值：无
******************************************************************************/
#pragma INLINE
void CoCAN_L_Data_Request ( uint16_t Identifier, uint8_t DLC, uint8_t *Data )
{
    MSCAN0_L_Data_Request ( Identifier, DLC, Data, 2 );
}

/******************************************************************************
函数名：CoCAN_L_Data_Confirm
功  能：由链路层传递而来,用于指示数据的发送结果
        该函数必须被下层(链路层)函数引用
参  数：Identifier    ：CAN报文ID
        TransferStatus：报文发送结果：COMPLETE     发送完成
                                      NOT_COMPLETE 报文未能发送完成
返回值：无
******************************************************************************/
/*void CoCAN_L_Data_Confirm(uint16_t Identifier, uint8_t TransferStatus)
{
  uint8_t i;
  uint8_t Index;

  Index = CoCAN_Search_Tx_Msg(Identifier);

  if (Index != 0xFF)
  {
    //if (TransferStatus == COMPLETE)
    //{
      CoCANTxMsgTable[Index].TxReq = CoCAN_TX_IDLE;

      //周期性报文重置下一次发送时间
      i = CoCANTxMsgTable[Index].Index;
      if (i != 0xFF)
        CoCANCycleMsg.TxTimer[i] = CoCANCtrl.Timer + CoCANTxMsgTable[Index].Cycle;

      *CoCANTxMsgTable[Index].pStatus |= CAN_UPDATE;
    //}
    //else
    //{
    //  if (CoCANTxMsgTable[Index].TxReq == CoCAN_TX_ON)      //确有请求发送但发送失败则重新装入发送FIFO
    //  {
    //    CoCANTxMsgFIFO.Index[CoCANTxMsgFIFO.Iptr] = Index;

        //FIFO处理
    //    if (CoCANTxMsgFIFO.Depth < CAN_TX_MSG_NUM)
    //      CoCANTxMsgFIFO.Depth++;
    //    CoCANTxMsgFIFO.Iptr++;
    //    if (CoCANTxMsgFIFO.Iptr >= CAN_TX_MSG_NUM)
    //      CoCANTxMsgFIFO.Iptr = 0;

        //报文已请求发送
    //    CoCANTxMsgTable[Index].TxReq = CoCAN_TX_REQ;
    //  }
    //}
  }
}*/

/******************************************************************************
函数名：CoCAN_Init
功  能：CAN总线信号收发初始化函数
        用于初始化各信号初始值,初始化报文定时器,以及生成报文快速查找表
参  数：无
返回值：无
******************************************************************************/
void CoCAN_Init ( void )
{
    uint8_t   i;
    uint8_t   Index;
    uint8_t   RxMsgNum;
    uint8_t   TxMsgNum;
    uint16_t  Identifier;
    //生成报文快速查找表
    //快速查找表生成过程中Index成员被临时用于存放报文存储于属性表中的位置信息
    //该位置信息用于在后续过程中查找报文属性以填充报文快速查找表
    RxMsgNum             = 0;
    TxMsgNum             = 0;

    for ( Index = 0; Index < CoCAN_TOTAL_MSG_NUM; Index++ )
    {
        Identifier = CANMsgAttrTable[Index].ID;

        //找到接收报文,用于生成接收报文快速查找表
        if ( CANMsgAttrTable[Index].Dir == CAN_MSG_Rx )
        {
            if ( RxMsgNum == 0 )        //快速查找表第一项的特殊处理
            {
                CoCANRxMsgTable[0].Identifier = Identifier;
                CoCANRxMsgTable[0].Index      = Index;
            }
            else                        //依据报文ID从大到小排列查找表
            {
                i = RxMsgNum;

                while ( i )
                {
                    if ( CoCANRxMsgTable[i - 1].Identifier > Identifier )
                    {
                        CoCANRxMsgTable[i].Identifier = CoCANRxMsgTable[i - 1].Identifier;
                        CoCANRxMsgTable[i].Index      = CoCANRxMsgTable[i - 1].Index;
                        CoCANRxMsgTable[i - 1].Identifier = Identifier;
                        CoCANRxMsgTable[i - 1].Index      = Index;
                    }
                    else
                    {
                        CoCANRxMsgTable[i].Identifier = Identifier;
                        CoCANRxMsgTable[i].Index      = Index;
                        break;
                    }

                    i--;
                }
            }

            RxMsgNum++;
        }
        //找到发送报文,用于生成发送报文快速查找表
        else
        {
            if ( TxMsgNum == 0 )        //快速查找表第一项的特殊处理
            {
                CoCANTxMsgTable[0].Identifier = Identifier;
                CoCANTxMsgTable[0].Index      = Index;
            }
            else                        //依据报文ID从大到小排列查找表
            {
                i = TxMsgNum;

                while ( i )
                {
                    if ( CoCANTxMsgTable[i - 1].Identifier > Identifier )
                    {
                        CoCANTxMsgTable[i].Identifier = CoCANTxMsgTable[i - 1].Identifier;
                        CoCANTxMsgTable[i].Index      = CoCANTxMsgTable[i - 1].Index;
                        CoCANTxMsgTable[i - 1].Identifier = Identifier;
                        CoCANTxMsgTable[i - 1].Index      = Index;
                    }
                    else
                    {
                        CoCANTxMsgTable[i].Identifier = Identifier;
                        CoCANTxMsgTable[i].Index      = Index;
                        break;
                    }

                    i--;
                }
            }

            TxMsgNum++;
        }
    }

#if CoCAN_MSG_NUM_CHECK

    //属性表中的报文数量与定义的报文总数不一致时进入死循环等待看门狗复位
    if ( CoCAN_TOTAL_MSG_NUM != CAN_TX_MSG_NUM + CAN_RX_MSG_NUM )
        for ( ;; );

    //属性表中的报文数量与定义的报文数量不一致时进入死循环等待看门狗复位
    if ( ( TxMsgNum != CAN_TX_MSG_NUM ) || ( RxMsgNum != CAN_RX_MSG_NUM ) )
        for ( ;; );

#endif
    //完善报文查找表信息,生成周期性报文管理表
    CoCANCycleMsg.TxNum  = 0;
    CoCANCycleMsg.RxNum  = 0;

    //发送报文
    for ( i = 0; i < CAN_TX_MSG_NUM; i++ )
    {
        Index = CoCANTxMsgTable[i].Index;       //取出报文存储于属性表中的位置信息

        if ( CANMsgAttrTable[Index].Type == CAN_MSG_CYCLE )
        {
            CoCANTxMsgTable[i].Offset = CANMsgAttrTable[Index].Offset;
            CoCANTxMsgTable[i].Cycle  = CANMsgAttrTable[Index].Cycle;
            CoCANTxMsgTable[i].Index  = CoCANCycleMsg.TxNum;
            CoCANCycleMsg.TxIndex[CoCANCycleMsg.TxNum] = i;
            CoCANCycleMsg.TxNum++;
        }
        else
            CoCANTxMsgTable[i].Index = 0xFF;      //表示非周期性报文

        CoCANTxMsgTable[i].DLC     = CANMsgAttrTable[Index].DLCMax;
        CoCANTxMsgTable[i].pMsg    = CANMsgAttrTable[Index].pMsg;
        CoCANTxMsgTable[i].pInit   = CANMsgAttrTable[Index].pInit;
        CoCANTxMsgTable[i].pStatus = CANMsgAttrTable[Index].pStatus;
    }

    //接收报文
    for ( i = 0; i < CAN_RX_MSG_NUM; i++ )
    {
        Index = CoCANRxMsgTable[i].Index;       //取出报文存储于属性表中的位置信息

        if ( CANMsgAttrTable[Index].Type == CAN_MSG_CYCLE )
        {
            CoCANRxMsgTable[i].Cycle = CANMsgAttrTable[Index].Cycle;
            CoCANRxMsgTable[i].Index = CoCANCycleMsg.RxNum;
            CoCANCycleMsg.RxIndex[CoCANCycleMsg.RxNum] = i;
            CoCANCycleMsg.RxNum++;
        }
        else
            CoCANRxMsgTable[i].Index = 0xFF;      //表示非周期性报文

        CoCANRxMsgTable[i].DLCMax   = CANMsgAttrTable[Index].DLCMax;
        CoCANRxMsgTable[i].DLCMin   = CANMsgAttrTable[Index].DLCMin;
        CoCANRxMsgTable[i].pMsg     = CANMsgAttrTable[Index].pMsg;
        CoCANRxMsgTable[i].pInit    = CANMsgAttrTable[Index].pInit;
        CoCANRxMsgTable[i].pTimeOut = CANMsgAttrTable[Index].pTimeOut;
        CoCANRxMsgTable[i].pMask    = CANMsgAttrTable[Index].pMask;
        CoCANRxMsgTable[i].pStatus  = CANMsgAttrTable[Index].pStatus;
    }

    CoCAN_Reset();
    CoCAN_Set_Mode ( CoCAN_MODE_INIT );      //工作于初始化模式
}

/******************************************************************************
函数名：CoCAN_Reset
功  能：重置CAN总线收发状态,该函数将影响：
        1.接收/发送FIFO将被清空
        2.各信号恢复至初始值
        3.报文状态恢复至初始化状态
        4.所有发送请求将被取消
        5.接收/发送定时器将被重置
        调用本函数,不会影响CAN通信的接收/发送使能状态
参  数：无
返回值：无
******************************************************************************/
void CoCAN_Reset ( void )
{
    uint8_t i;
    uint8_t j;
    //初始化发送报文FIFO
    CoCANTxMsgFIFO.Iptr  = 0;
    CoCANTxMsgFIFO.Optr  = 0;
    CoCANTxMsgFIFO.Depth = 0;
    //初始化接收报文FIFO
    CoCANRxMsgFIFO.Iptr  = 0;
    CoCANRxMsgFIFO.Optr  = 0;
    CoCANRxMsgFIFO.Depth = 0;

    //发送报文
    for ( i = 0; i < CAN_TX_MSG_NUM; i++ )
    {
        //初始化报文信号值
        for ( j = 0; j < CoCANTxMsgTable[i].DLC; j++ )
            CoCANTxMsgTable[i].pMsg[j] = CoCANTxMsgTable[i].pInit[j];

        //初始化报文状态
        *CoCANTxMsgTable[i].pStatus  = 0x00;
        //清除发送请求
        CoCANTxMsgTable[i].TxReq   = CoCAN_TX_IDLE;
    }

    //接收报文
    for ( i = 0; i < CAN_RX_MSG_NUM; i++ )
    {
        //初始化报文
        for ( j = 0; j < CoCANRxMsgTable[i].DLCMax; j++ )
            CoCANRxMsgTable[i].pMsg[j] = CoCANRxMsgTable[i].pInit[j];

        //初始化报文状态
        *CoCANRxMsgTable[i].pStatus  = 0x00;
    }

    //重置定时器
    CoCANCtrl.Timer = 0;

    for ( i = 0; i < CoCANCycleMsg.TxNum; i++ )
        CoCANCycleMsg.TxTimer[i] = CoCANTxMsgTable[CoCANCycleMsg.TxIndex[i]].Offset;

    for ( i = 0; i < CoCANCycleMsg.RxNum; i++ )
        CoCANCycleMsg.RxTimer[i] = CoCANRxMsgTable[CoCANCycleMsg.RxIndex[i]].Cycle;
}

/******************************************************************************
函数名：CoCAN_Set_Mode
功  能：设置CAN通信的工作模式
参  数：Mode：CoCAN_MODE_INIT   - 初始化模式,报文将在后台由中断程序发送
              CoCAN_MODE_NORMAL - 正常工作模式,报文由报文发送程序进行发送
返回值：无
******************************************************************************/
void CoCAN_Set_Mode ( uint8_t Mode )
{
    uint8_t i;

    if ( Mode == CoCAN_MODE_INIT )
    {
        if ( CoCANCtrl.Mode != CoCAN_MODE_INIT )
        {
            CoCANCtrl.Mode = CoCAN_MODE_INIT;
            CoCANCtrl.Prescaler = 0;
        }
    }
    else
    {
        if ( CoCANCtrl.Mode != CoCAN_MODE_NORMAL )
        {
            CoCANCtrl.Mode = CoCAN_MODE_NORMAL;

            //重置接收报文定时器
            for ( i = 0; i < CoCANCycleMsg.RxNum; i++ )
                CoCANCycleMsg.RxTimer[i] = CoCANRxMsgTable[CoCANCycleMsg.RxIndex[i]].Cycle + CoCANCtrl.Timer;
        }
    }
}

/******************************************************************************
函数名：CoCAN_Rx_Enable
功  能：CAN总线通信接收功能使能/关闭
参  数：En：CoCAN_DISABLE        - 禁止CAN总线通信接收功能
            COCAN_PSEUDO_DISABLE - 禁止CAN总线通信接收功能,但不会产生报文掉线状态
            CoCAN_ENABLE         - CAN总线通信接收功能使能
返回值：无
******************************************************************************/
void CoCAN_Rx_Enable ( uint8_t En )
{
    uint8_t i;

    if ( ( En == CoCAN_DISABLE ) || ( En == COCAN_PSEUDO_DISABLE ) )
        CoCANCtrl.RxEnable = En;
    else
    {
        if ( CoCANCtrl.RxEnable != CoCAN_ENABLE )
        {
            CoCANCtrl.RxEnable = CoCAN_ENABLE;

            //重置接收报文定时器
            for ( i = 0; i < CoCANCycleMsg.RxNum; i++ )
                CoCANCycleMsg.RxTimer[i] = CoCANRxMsgTable[CoCANCycleMsg.RxIndex[i]].Cycle + CoCANCtrl.Timer;
        }
    }
}

/******************************************************************************
函数名：CoCAN_Tx_Enable
功  能：CAN总线通信发送功能使能/关闭
参  数：En：CoCAN_DISABLE - 禁止CAN总线通信发送功能
            CoCAN_ENABLE  - 使能CAN总线通信发送功能
返回值：无
******************************************************************************/
void CoCAN_Tx_Enable ( uint8_t En )
{
    uint8_t i;

    if ( En < CoCAN_ENABLE )
    {
        if ( CoCANCtrl.TxEnable == CoCAN_ENABLE )
        {
            //清空发送报文FIFO
            CoCANTxMsgFIFO.Iptr  = 0;
            CoCANTxMsgFIFO.Optr  = 0;
            CoCANTxMsgFIFO.Depth = 0;

            //清除发送请求
            for ( i = 0; i < CAN_TX_MSG_NUM; i++ )
                CoCANTxMsgTable[i].TxReq = CoCAN_TX_IDLE;
        }

        CoCANCtrl.TxEnable = CoCAN_DISABLE;
    }
    else
    {
        if ( CoCANCtrl.TxEnable != CoCAN_ENABLE )
        {
            CoCANCtrl.TxEnable = CoCAN_ENABLE;

            //重置发送报文定时器
            for ( i = 0; i < CoCANCycleMsg.TxNum; i++ )
                CoCANCycleMsg.TxTimer[i] = CoCANTxMsgTable[CoCANCycleMsg.TxIndex[i]].Offset + CoCANCtrl.Timer;
        }
    }
}

/******************************************************************************
函数名：CoCAN_Search_Tx_Msg
功  能：从发送报文快速查找表中找到目标报文ID的信息存储位置
参  数：Identifier：目标报文ID
返回值：该报文的信息在发送报文快速查找表中的存储位置
        0xFF表示未能找到报文
******************************************************************************/
#pragma INLINE
uint8_t CoCAN_Search_Tx_Msg ( uint16_t Identifier )
{
    uint8_t SearchStart;
    uint8_t SearchPoint;
    uint8_t SearchLen;
    SearchStart = 0;                //从发送快速查找表的底端起查找
    SearchLen   = CAN_TX_MSG_NUM;   //查找长度为整个查找表

    //不采用递归结构避免频繁出栈入栈造成的时间浪费
    while ( SearchLen )
    {
        SearchPoint = SearchStart + SearchLen / 2;

        if ( CoCANTxMsgTable[SearchPoint].Identifier == Identifier )
        {
            return SearchPoint;
        }
        else if ( CoCANTxMsgTable[SearchPoint].Identifier < Identifier )
        {
            //目标ID比查找点的ID值大,则查找查找点之后的查找表
            //因此下一次的查找起点为本次的查找点之后的一点,查找长度为剩余的查找表长度 - 1
            SearchStart = SearchPoint + 1;
            SearchLen   = SearchLen - SearchLen / 2 - 1;
        }
        else
        {
            //目标ID比查找点的ID值小,则查找查找点之前的查找表
            //因此下一次的查找起点仍为原查找起点不变,查找长度减半
            SearchLen   = SearchLen / 2;
        }
    }

    //搜索完毕未能找到目标ID
    return 0xFF;
}

/******************************************************************************
函数名：CoCAN_Search_Rx_Msg
功  能：从接收报文快速查找表中找到目标报文ID的信息存储位置
参  数：Identifier：目标报文ID
返回值：该报文的信息在接收报文快速查找表中的存储位置
        0xFF表示未能找到报文
******************************************************************************/
#pragma INLINE
uint8_t CoCAN_Search_Rx_Msg ( uint16_t Identifier )
{
    uint8_t SearchStart;
    uint8_t SearchPoint;
    uint8_t SearchLen;
    SearchStart = 0;                //从接收快速查找表的底端起查找
    SearchLen   = CAN_RX_MSG_NUM;   //查找长度为整个查找表

    //不采用递归结构避免频繁出栈入栈造成的时间浪费
    while ( SearchLen )
    {
        SearchPoint = SearchStart + SearchLen / 2;

        if ( CoCANRxMsgTable[SearchPoint].Identifier == Identifier )
        {
            return SearchPoint;
        }
        else if ( CoCANRxMsgTable[SearchPoint].Identifier < Identifier )
        {
            //目标ID比查找点的ID值大,则查找查找点之后的查找表
            //因此下一次的查找起点为本次的查找点之后的一点,查找长度为剩余的查找表长度 - 1
            SearchStart = SearchPoint + 1;
            SearchLen   = SearchLen - SearchLen / 2 - 1;
        }
        else
        {
            //目标ID比查找点的ID值小,则查找查找点之前的查找表
            //因此下一次的查找起点仍为原查找起点不变,查找长度减半
            SearchLen   = SearchLen / 2;
        }
    }

    //搜索完毕未能找到目标ID
    return 0xFF;
}

/******************************************************************************
函数名：CoCAN_Signal_Update_Service
功  能：CAN总线信号更新服务函数
        该函数作用有：1.将新收到的报文解析至各信号
                      2.监控报文是否有报文丢失
                      3.按时发出周期性发送报文
参  数：无
返回值：无
*******************************************************************************
注  意：该服务函数必须每1ms被调用一次
******************************************************************************/
void CoCAN_Signal_Update_Service ( void )
{
    uint8_t   i;
    uint8_t   j;
    uint8_t   Index;
    uint16_t  Identifier;
    //一旦调用该服务函数,说明系统已经脱离初始化阶段,则立即将CAN通信工作模式转为正常工作模式
    CoCAN_Set_Mode ( CoCAN_MODE_NORMAL );

    //从报文接收FIFO中收取报文
    while ( CoCANRxMsgFIFO.Depth )
    {
        Identifier = CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Optr].Identifier;
        Index      = CoCAN_Search_Rx_Msg ( Identifier );

        if ( Index != 0xFF )
        {
            //验证报文长度
            if ( ( CoCANRxMsgTable[Index].DLCMax >= CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Optr].DLC ) && \
                    ( CoCANRxMsgTable[Index].DLCMin <= CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Optr].DLC ) )
            {
                //重置下一次超时周期
                if ( CoCANRxMsgTable[Index].Index != 0xFF )
                    CoCANCycleMsg.RxTimer[CoCANRxMsgTable[Index].Index] = CoCANCtrl.Timer + CoCANRxMsgTable[Index].Cycle;

                //拷贝报文至解析Buffer
                for ( i = 0; i < CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Optr].DLC; i++ )
                    CoCANRxMsgTable[Index].pMsg[i] = CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Optr].Data[i];

                //清除报文丢失状态,置报文更新标志
                *CoCANRxMsgTable[Index].pStatus = CAN_UPDATE;
            }
        }

        //FIFO处理
        CoCANRxMsgFIFO.Depth--;
        CoCANRxMsgFIFO.Optr++;

        if ( CoCANRxMsgFIFO.Optr >= CoCAN_RX_MSG_FIFO_MAX_DEPTH )
            CoCANRxMsgFIFO.Optr = 0;
    }

    //不在COCAN_PSEUDO_DISABLE接收状态下时进行周期接收报文监控
    if ( CoCANCtrl.RxEnable != COCAN_PSEUDO_DISABLE )
    {
        for ( i = 0; i < CoCANCycleMsg.RxNum; i++ )
        {
            //如果报文掉线了,拷贝掉线默认值
            if ( CoCANCycleMsg.RxTimer[i] == CoCANCtrl.Timer )
            {
                Index = CoCANCycleMsg.RxIndex[i];

                for ( j = 0; j < CoCANRxMsgTable[Index].DLCMax; j++ )
                {
                    CoCANRxMsgTable[Index].pMsg[j] &= CoCANRxMsgTable[Index].pMask[j];
                    CoCANRxMsgTable[Index].pMsg[j] |= CoCANRxMsgTable[Index].pTimeOut[j];
                }

                //置报文丢失状态
                *CoCANRxMsgTable[Index].pStatus |= CAN_MSG_LOST;
            }
        }
    }

    if ( CoCANCtrl.TxEnable == CoCAN_ENABLE )
    {
        //周期发送报文发送
        for ( i = 0; i < CoCANCycleMsg.TxNum; i++ )
        {
            //如果报文发送时间到,将要发送的报文装入发送FIFO
            if ( CoCANCycleMsg.TxTimer[i] == CoCANCtrl.Timer )
            {
                Index = CoCANCycleMsg.TxIndex[i];

                if ( *CoCANTxMsgTable[Index].pStatus & CAN_TX_EN )
                {
                    //if (CoCANTxMsgTable[Index].TxReq == CoCAN_TX_IDLE)
                    //{
                    CoCANTxMsgFIFO.Index[CoCANTxMsgFIFO.Iptr] = Index;

                    //FIFO处理
                    if ( CoCANTxMsgFIFO.Depth < CAN_TX_MSG_NUM )
                        CoCANTxMsgFIFO.Depth++;

                    CoCANTxMsgFIFO.Iptr++;

                    if ( CoCANTxMsgFIFO.Iptr >= CAN_TX_MSG_NUM )
                        CoCANTxMsgFIFO.Iptr = 0;

                    //报文已请求发送
                    //CoCANTxMsgTable[Index].TxReq = CoCAN_TX_REQ;
                    //CoCANTxMsgTable[Index].TxReq = CoCAN_TX_IDLE;
                    //周期性报文重置下一次发送时间
                    CoCANCycleMsg.TxTimer[i] = CoCANCtrl.Timer + CoCANTxMsgTable[Index].Cycle;
                    *CoCANTxMsgTable[Index].pStatus |= CAN_UPDATE;
                    //}
                }
            }
        }
    }

    //将报文发送FIFO中的报文发送出去
    if ( CoCANTxMsgFIFO.Depth )
    {
        //一次只发送1条报文
        Index = CoCANTxMsgFIFO.Index[CoCANTxMsgFIFO.Optr];
        //报文发送中
        CoCANTxMsgTable[Index].TxReq = CoCAN_TX_ON;
        CoCAN_L_Data_Request ( CoCANTxMsgTable[Index].Identifier, CoCANTxMsgTable[Index].DLC, ( uint8_t * ) CoCANTxMsgTable[Index].pMsg );
        //FIFO处理
        CoCANTxMsgFIFO.Depth--;
        CoCANTxMsgFIFO.Optr++;

        if ( CoCANTxMsgFIFO.Optr >= CAN_TX_MSG_NUM )
            CoCANTxMsgFIFO.Optr = 0;
    }

    //全局定时控制
    CoCANCtrl.Timer++;

    if ( CoCANCtrl.Timer > 0x7FFF )                //计数值为 0 - 23767,最高位作为进位位
    {
        CoCANCtrl.Timer &= 0x7FFF;                   //主定时器循环至下一周期,各定时器清除进位位

        for ( i = 0; i < CoCANCycleMsg.TxNum; i++ )
            CoCANCycleMsg.TxTimer[i] &= 0x7FFF;

        for ( i = 0; i < CoCANCycleMsg.RxNum; i++ )
            CoCANCycleMsg.RxTimer[i] &= 0x7FFF;
    }
}

/******************************************************************************
函数名：CoCAN_Transmit_Message
功  能：请求发送报文
        被发送的报文既可以是周期型的,也可以是事件型的,但该报文在报文属性表中必
        须被定义为发送报文
        如果周期性发送报文在被禁止后又被重新使能,在使能的同时应调用此函数以重新
        启动报文的周期性发送
参  数：Identifier：报文ID
返回值：无
******************************************************************************/
void CoCAN_Transmit_Message ( uint16_t Identifier )
{
    uint8_t Index;

    if ( CoCANCtrl.TxEnable != CoCAN_ENABLE )
        return;

    Index = CoCAN_Search_Tx_Msg ( Identifier );

    if ( Index != 0xFF )            //报文位于发送快速查找表中
    {
        if ( *CoCANTxMsgTable[Index].pStatus & CAN_TX_EN )
        {
            if ( CoCANTxMsgTable[Index].TxReq == CoCAN_TX_IDLE )
            {
                CoCANTxMsgFIFO.Index[CoCANTxMsgFIFO.Iptr] = Index;

                //FIFO处理
                if ( CoCANTxMsgFIFO.Depth < CAN_TX_MSG_NUM )
                    CoCANTxMsgFIFO.Depth++;

                CoCANTxMsgFIFO.Iptr++;

                if ( CoCANTxMsgFIFO.Iptr >= CAN_TX_MSG_NUM )
                    CoCANTxMsgFIFO.Iptr = 0;

                //报文已请求发送
                CoCANTxMsgTable[Index].TxReq = CoCAN_TX_REQ;
            }
        }
    }
}

/******************************************************************************
函数名：CoCAN_Modify_Tx_Msg_Cycle
功  能：修改报文的发送周期
        该函数仅对周期性发送报文有效,若被修改的报文是事件型的或者是接收报文,则
        不会产生任何效果
        新周期会在下一次报文发送成功后生效
参  数：Identifier：报文ID
        Cycle     ：目标报文周期
返回值：无
******************************************************************************/
void CoCAN_Modify_Tx_Msg_Cycle ( uint16_t Identifier, uint16_t Cycle )
{
    uint8_t Index;
    Index = CoCAN_Search_Tx_Msg ( Identifier );

    if ( Index != 0xFF )                            //报文位于发送快速查找表中
        CoCANTxMsgTable[Index].Cycle = Cycle;
}

/******************************************************************************
函数名：CoCAN_Init_Mode_Tx_ISR
功  能：初始化模式下CAN通信报文发送中断服务函数
参  数：无
返回值：无
*******************************************************************************
注  意：该服务函数必须被嵌入到定时中断中
******************************************************************************/
void CoCAN_Init_Mode_Tx_ISR ( void )
{
    uint8_t   i;
    uint8_t   Index;

    if ( CoCANCtrl.Mode != CoCAN_MODE_INIT )
        return;

    CoCANCtrl.Prescaler++;

    if ( CoCANCtrl.Prescaler > ( uint8_t ) ( 1000 / API_INT_CYCLE ) )
    {
        CoCANCtrl.Prescaler = 0;

        if ( CoCANCtrl.TxEnable == CoCAN_ENABLE )
        {
            //周期发送报文发送
            for ( i = 0; i < CoCANCycleMsg.TxNum; i++ )
            {
                //如果报文发送时间到,将要发送的报文装入发送FIFO
                if ( CoCANCycleMsg.TxTimer[i] == CoCANCtrl.Timer )
                {
                    Index = CoCANCycleMsg.TxIndex[i];

                    if ( *CoCANTxMsgTable[Index].pStatus & CAN_TX_EN )
                    {
                        //if (CoCANTxMsgTable[Index].TxReq == CoCAN_TX_IDLE)
                        //{
                        CoCANTxMsgFIFO.Index[CoCANTxMsgFIFO.Iptr] = Index;

                        //FIFO处理
                        if ( CoCANTxMsgFIFO.Depth < CAN_TX_MSG_NUM )
                            CoCANTxMsgFIFO.Depth++;

                        CoCANTxMsgFIFO.Iptr++;

                        if ( CoCANTxMsgFIFO.Iptr >= CAN_TX_MSG_NUM )
                            CoCANTxMsgFIFO.Iptr = 0;

                        //报文已请求发送
                        //CoCANTxMsgTable[Index].TxReq = CoCAN_TX_REQ;
                        //CoCANTxMsgTable[Index].TxReq = CoCAN_TX_IDLE;
                        //周期性报文重置下一次发送时间
                        CoCANCycleMsg.TxTimer[i] = CoCANCtrl.Timer + CoCANTxMsgTable[Index].Cycle;
                        *CoCANTxMsgTable[Index].pStatus |= CAN_UPDATE;
                        //}
                    }
                }
            }
        }

        //将报文发送FIFO中的报文发送出去
        if ( CoCANTxMsgFIFO.Depth )
        {
            //一次只发送1条报文
            Index = CoCANTxMsgFIFO.Index[CoCANTxMsgFIFO.Optr];
            //报文发送中
            CoCANTxMsgTable[Index].TxReq = CoCAN_TX_ON;
            CoCAN_L_Data_Request ( CoCANTxMsgTable[Index].Identifier, CoCANTxMsgTable[Index].DLC, ( uint8_t * ) CoCANTxMsgTable[Index].pMsg );
            //FIFO处理
            CoCANTxMsgFIFO.Depth--;
            CoCANTxMsgFIFO.Optr++;

            if ( CoCANTxMsgFIFO.Optr >= CAN_TX_MSG_NUM )
                CoCANTxMsgFIFO.Optr = 0;
        }

        //全局定时控制
        CoCANCtrl.Timer++;

        if ( CoCANCtrl.Timer > 0x7FFF )                //计数值为 0 - 23767,最高位作为进位位
        {
            CoCANCtrl.Timer &= 0x7FFF;                   //主定时器循环至下一周期,各定时器清除进位位

            for ( i = 0; i < CoCANCycleMsg.TxNum; i++ )
                CoCANCycleMsg.TxTimer[i] &= 0x7FFF;
        }
    }
}
