/******************************************************************************
文 件 名：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;



//将周期发送报文装入FIFO中
void CoCAN_Tx_Fill_FIFO(uint8_t cycleMsgID)
{
  //报文定义表中的序号（CoCANTxMsgTable）
  uint8_t Index = CoCANCycleMsg.TxIndex[cycleMsgID];

  //外发报文允许发送
  if ( *(CoCANTxMsgTable[Index].pStatus) & CAN_TX_EN )
  {
    //将报文信息填入FIFO中
    CoCANTxMsgFIFO.Index[CoCANTxMsgFIFO.Iptr] = Index;
    
    //FIFO处理
    if (CoCANTxMsgFIFO.Depth < CAN_TX_MSG_NUM)
      CoCANTxMsgFIFO.Depth++;

	//FIFO当前可写位置移位（循环移位）
    CoCANTxMsgFIFO.Iptr++;
    if (CoCANTxMsgFIFO.Iptr >= CAN_TX_MSG_NUM)
      CoCANTxMsgFIFO.Iptr = 0;
    
    //设置周期性报文下一次发送时间
    CoCANCycleMsg.TxTimer[cycleMsgID] = CoCANCtrl.Timer + CoCANTxMsgTable[Index].Cycle;
	//设置已发送TAG
    *(CoCANTxMsgTable[Index].pStatus) |= CAN_UPDATE;
  }
  //end
}

//从FIFO中发送一条报文
void CoCAN_Tx_SendOutOne()
{
  //一次只发送1条报文
  uint8_t 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;
  //end
}


/******************************************************************************
函数名：CoCAN_L_Data_Indication
功  能： 由链路层传递而来,用于指示数据的到达并传递数据
        该函数必须被下层(链路层)函数引用
参  数： Identifier ：CAN报文ID
        DLC        ：报文数据长度
        Data       ：报文数据内容
返回值：无
******************************************************************************/
void CoCAN_L_Data_Indication(uint32_t Identifier, uint8_t DLC, uint8_t *Data)
{
  uint8_t i;
  
  //检查是否允许接收报文
  if (CoCANCtrl.RxEnable != CoCAN_ENABLE)
  {
  if (Identifier==0x18FFA021)
  {}
  else
    return;
  }
  //检查FIFO是否已满
  if (CoCANRxMsgFIFO.Depth >= CoCAN_RX_MSG_FIFO_MAX_DEPTH)
    return;
  
  //拷贝报文
  CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Iptr].Identifier = Identifier;//ID
  for (i = 0; i < DLC; i++)
    CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Iptr].Data[i]  = Data[i];	//Data
  CoCANRxMsgFIFO.Msg[CoCANRxMsgFIFO.Iptr].DLC        = DLC;		//DLC
  
  //FIFO处理
  CoCANRxMsgFIFO.Depth++;
  CoCANRxMsgFIFO.Iptr++;
  if (CoCANRxMsgFIFO.Iptr >= CoCAN_RX_MSG_FIFO_MAX_DEPTH)
    CoCANRxMsgFIFO.Iptr = 0;
  
  //end
}

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

  //加载CAN_Communication_Matrix.h中消息数组信息
  for (Index = 0; Index < CoCAN_TOTAL_MSG_NUM; Index++)
  {
    Identifier = CANMsgAttrTable[Index].ID;
    
    //接收报文快速查找表 (CoCANRxMsgTable)
    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++;
    }
        
    //发送报文快速查找表(CoCANTxMsgTable)
    else
    {
      if(TxMsgNum == 0)           //快速查找表第一项的特殊处理
      {
        CoCANTxMsgTable[0].Identifier = Identifier;
        CoCANTxMsgTable[0].Index      = Index;
      }
      else                        //依据报文ID从大到小排列查找表
      {
        i = TxMsgNum;
		//发送报文按ID排序（从小到大排序）
        while (i)
        {
          //最后一个ID大于新插入的ID
          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;
  }

  //Can硬件初始化
  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  = 0x01;
  }
  
  //重置定时器
  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(CoCANCtrl.Mode == Mode)
  	return;

  //新状态记录
  CoCANCtrl.Mode = Mode;

  //CoCAN_MODE_INIT
  if (Mode == CoCAN_MODE_INIT)
    CoCANCtrl.Prescaler = 0;

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

/******************************************************************************
函数名：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++)      //0位为网络管理报文，不需要被重置  20190929   
        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)
    {
      //重置发送报文定时器
      for (i = 0; i < CoCANCycleMsg.TxNum; i++)    //0位为网络管理报文，开的时候不开网络管理
        CoCANCycleMsg.TxTimer[i] = CoCANTxMsgTable[CoCANCycleMsg.TxIndex[i]].Offset + CoCANCtrl.Timer + 20;
    }
	CoCANCtrl.TxEnable = CoCAN_ENABLE;
  }

  //关闭发送
  else
  {
    //原本不是关闭
    if (CoCANCtrl.TxEnable != CoCAN_DISABLE)
    {
	  //清空发送报文FIFO
	  
      CoCANTxMsgFIFO.Iptr  = 0;
      CoCANTxMsgFIFO.Optr  = 0;
      CoCANTxMsgFIFO.Depth = 0;
      //清除发送请求
      for (i = 0; i < CAN_TX_MSG_NUM; i++)    //0位为网络管理报文，关的时候不关网络管理     ,网络管理报文不是0位
      {
        //if(CoCANTxMsgTable[i].Identifier==0x18FEF717)          //20200301测试反馈第4条，仪表进入网络等待休眠状态，应无报文发出
        //{}
       // else
        CoCANTxMsgTable[i].TxReq = CoCAN_TX_IDLE;
      }
    }
	CoCANCtrl.TxEnable = CoCAN_DISABLE;
  }
  //end
}

/******************************************************************************
函数名：CoCAN_Search_Tx_Msg
功  能： 从发送报文快速查找表中找到目标报文ID的信息存储位置
参  数： Identifier：目标报文ID
返回值：该报文的信息在发送报文快速查找表中的存储位置
        0xFF表示未能找到报文
******************************************************************************/
//#pragma INLINE
uint8_t CoCAN_Search_Tx_Msg(uint32_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(uint32_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, j;
  uint8_t   Index;
  uint32_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;

	//end
  }
  
  //不在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];
         if(*CoCANRxMsgTable[Index].pStatus & CAN_UPDATE) 
        {
            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)      //20200702
      {
        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;
          //}
        }
      }
    }
  }
 /* else      20200306
   {
      //如果报文发送时间到,将要发送的报文装入发送FIFO
         for (i = 0; i < CoCANCycleMsg.TxNum; i++)
         {
             if(CoCANTxMsgTable[i].Identifier==0x18FEF717)
             {
              if (CoCANCycleMsg.TxTimer[i] <= CoCANCtrl.Timer)         //if(CoCANTxMsgTable[i].Identifier==0x18FEF717)
              {
                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++;
  //清除计时器最高位：计数值为 0 - 23767,最高位作为进位位
  if (CoCANCtrl.Timer > 0x7FFF)                  
  {
    //总计时器
    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(uint32_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(uint32_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))
    return;
  
  //进行初始化操作
  CoCANCtrl.Prescaler = 0;

  //允许周期性发送
  if (CoCANCtrl.TxEnable == CoCAN_ENABLE)
  {
    //周期发送报文发送
    for (i = 0; i < CoCANCycleMsg.TxNum; i++)
    {
      //如果报文发送时间到,将要发送的报文装入发送FIFO
      if (CoCANCycleMsg.TxTimer[i] == CoCANCtrl.Timer) 
        CoCAN_Tx_Fill_FIFO(i);
    }
  }
    
  //将报文发送FIFO中的报文发送出去
  if (CoCANTxMsgFIFO.Depth)
    CoCAN_Tx_SendOutOne();
  
  //********************************全局定时控制
  CoCANCtrl.Timer++;
  //定时器，清理最高位（计数值为 0 - 23767）
  if (CoCANCtrl.Timer > 0x7FFF)                  
  {
    CoCANCtrl.Timer &= 0x7FFF;
    for (i = 0; i < CoCANCycleMsg.TxNum; i++)
      CoCANCycleMsg.TxTimer[i] &= 0x7FFF;
  }
  //end
}

