MSCAN1.c 13.5 KB
Newer Older
崔立宝's avatar
崔立宝 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
/******************************************************************************
文 件 名:MSCAN0.h
功能描述:MSCAN0总线收发控制函数库文件
作    者:张暄
版    本:V1.0
日    期:2016.5.5
******************************************************************************/

#include "MSCAN0.h"
uint8_t    WakeUpFlag;
uint8_t    WakeUpID;
uint16_t                      MSCAN0TxID[3];
MSCAN0BusoffMonitorStruct     MSCAN0Busoff;
extern uint8_t NM_RECEIVE;

extern void CoCAN_L_Data_Indication(uint16_t Identifier, uint8_t DLC, uint8_t *Data);
extern void CoCAN_L_Data_Confirm(uint16_t Identifier, uint8_t TransferStatus);

extern void DoCAN_L_Data_Indication(uint16_t Identifier, uint8_t dlc, uint8_t *pData);
extern void DoCAN_L_Data_Confirm(uint16_t Identifier, uint8_t TransferStatus);

extern FUNC(void, CANNM_CODE) CanNm_RxIndication
(
  PduIdType RxPudId,
  const uint16 RxId,
  P2CONST(PduInfoType, AUTOMATIC, CANNM_APPL_DATA)PduInfoPtr
);

extern FUNC(void, CANNM_CODE) CanNm_TxConfirmation
(
  PduIdType TxPudId
);

/******************************************************************************
函数名:MSCAN0_L_Data_Request
功  能:该函数由上层调用,用于请求向总线上发送一组报文
参  数:Identifier:报文ID
        DLC       :报文长度
        Data      :报文数据
返回值:无
******************************************************************************/
void MSCAN0_L_Data_Request(uint16_t Identifier, uint8_t DLC, uint8_t *Data)
{
  uint8_t  i;
  uint8_t  BufDetect;
  uint8_t  BufSel;

  //发送数据长度大于8或MSCAN与CAN BUS不同步或3个发送缓冲区都没有空闲时或ID无效,上报发送失败并退出发送
  if ((DLC > 8) ||	(CAN0CTL0_SYNCH == 0) || (CAN0TFLG & 0x07 == 0) || (Identifier > 0x07FF))
  {
    MSCAN0_L_Data_Confirm(Identifier, NOT_COMPLETE);
    return;
  }

  CAN0TBSEL = CAN0TFLG;   		              //选择指向空闲的发送缓冲区(多个空闲选择最低的)
  BufSel    = CAN0TBSEL;

  BufDetect = 0x01;
  for (i = 0; i < 3; i++)
  {
    if (BufDetect == BufSel)
    {
      if (MSCAN0TxID[i] != 0xFFFF)           //前一次报文发送未得到结果但是Buffer被意外清空
      {
        MSCAN0_L_Data_Confirm(MSCAN0TxID[i], NOT_COMPLETE);
        MSCAN0TxID[i] = 0xFFFF;
      }

      MSCAN0TxID[i] = Identifier;            //保存ID
    }

    BufDetect <<= 1;
  }

  CAN0TXIDR0 = (uint8_t)(Identifier >> 3);  //写入标识符ID
  CAN0TXIDR1 = (uint8_t)(Identifier << 5);
  CAN0TXIDR1_SRR = 0;
  CAN0TXIDR1_IDE = 0;

  for (i = 0; i < DLC; i++)                 //写入数据
    *((&CAN0TXDSR0) + i) = Data[i];

  CAN0TXDLR = DLC;

  //CAN0TXTBPR = 0;                         //设置优先级,各Buffer优先级相同,则BufferID越小,优先级越高
  CAN0TFLG  = BufSel;                       //启动发送
  CAN0TIER |= BufSel;                       //启动相应CAN发送中断
}

/******************************************************************************
函数名:MSCAN0_L_Data_Indication
功  能:该函数用于向上层指示一帧报文的到来,并同时将报文数据传递至上层
        注意:1.使用时应将上层的报文传递函数包含于本函数之中,报文到来时则会调用
                相应的函数传递报文数据
              2.本函数与中断相关,被调用的上层的报文传递函数应尽可能简短
参  数:Identifier:报文ID
        DLC       :报文长度
        Data      :报文数据
返回值:无
******************************************************************************/
void MSCAN0_L_Data_Indication(uint16_t Identifier, uint8_t DLC, uint8_t *Data)
{
  PduInfoType NW_MessageBuffer;

  if( ( Identifier >= 0x400 ) && ( Identifier <= 0x47F ) )  //网络管理报文
  {
    NW_MessageBuffer.SduDataPtr = Data;
    NW_MessageBuffer.SduLength = DLC;
    CanNm_RxIndication(0, 0, &NW_MessageBuffer);
    NM_RECEIVE = 1;

  }
  else    if ((Identifier == 0x7E3) || (Identifier == 0x7EB))
  {
    DoCAN_L_Data_Indication(Identifier, DLC, Data);
  }
  else
  {
    //应用报文
    if( sleepFlg == NET_WORK_STATE )
    {
      CoCAN_L_Data_Indication(Identifier, DLC, Data);

    }
    else if( sleepFlg == PREPARE_BUS_SLEEP_STATE )
    {


    }
    else if( sleepFlg == BUS_SLEEP_STATE )
    {

    }
    else if( sleepFlg == START_INDICATION_STATE )
    {
      CoCAN_L_Data_Indication(Identifier, DLC, Data);
    }
  }

  //  if ((Identifier == 0x7E3) || (Identifier == 0x7EB))
  //    DoCAN_L_Data_Indication(Identifier, DLC, Data);
  //  else
  //   CoCAN_L_Data_Indication(Identifier, DLC, Data);
}

/******************************************************************************
函数名:MSCAN0_L_Data_Confirm
功  能:该函数用于向上层指示请求发送的报文的发送结果
        注意:1.使用时应将上层的报文发送结果传递函数包含于本函数之中,报文发送事
                件产生时则会调用相应的函数传递报文发送结果
              2.本函数与中断相关,被调用的上层的报文发送结果传递函数应尽可能简短
参  数:Identifier    :报文ID
        TransferStatus:报文发送结果:COMPLETE     发送完成
                                      NOT_COMPLETE 报文未能发送完成
返回值:无
******************************************************************************/
void MSCAN0_L_Data_Confirm(uint16_t Identifier, uint8_t TransferStatus)
{
  if( ( Identifier >= 0x400 ) && ( Identifier <= 0x47F ) )  //网络管理报文
  {
    CanNm_TxConfirmation(0);
  }
  else if (Identifier == 0x7E3)
    DoCAN_L_Data_Confirm(Identifier, TransferStatus);
  else
  {
    CoCAN_L_Data_Confirm(Identifier, TransferStatus);
  }

  //if (Identifier == 0x7E3)
  //   DoCAN_L_Data_Confirm(Identifier, TransferStatus);
  // else
  //   CoCAN_L_Data_Confirm(Identifier, TransferStatus);
}

/******************************************************************************
函数名:MSCAN0_Bus_Off_Monitoring_Service
功  能:MSCAN0 Bus-off状态监控服务,监控Bus-off状态并尝试恢复
参  数:无
返回值:无
*******************************************************************************
注  意:该服务函数必须每20ms被调用一次
******************************************************************************/
void MSCAN0_Bus_Off_Monitoring_Service(void)
{
#if !MSCAN0_BUS_OFF_AUTO_RECOVERY

#if MSCAN0_BUS_LIMP_MODE_ENABLE
  if (MSCAN0Busoff.Status == MSCAN0_BUS_LIMP)
    return;	                                  //CAN总线已经在跛行模式(多次恢复失败),则不再恢复
#endif

  if (CAN0MISC_BOHOLD)                          //如果Bus-off发生
  {



    MSCAN0Busoff.Timer++;
    if (MSCAN0Busoff.Timer >= MSCAN0_BUS_OFF_RECOVERY_TIME / 20)
    {
      MSCAN0Busoff.Timer = 0;

      MSCAN0Busoff.Status = MSCAN0_BUS_OFF;     //记录Bus-off状态

      CAN0MISC_BOHOLD = 1;                      //写1清零,请求恢复Bus-off

#if MSCAN0_BUS_LIMP_MODE_ENABLE
      MSCAN0Busoff.Cnt++;
      if (MSCAN0Busoff.Cnt > MSCAN0_BUS_LIMP_MODE_THRESHOLD)
      {
        MSCAN0Busoff.Cnt = 0;
        CAN0MISC_BOHOLD = 1;                      //写1清零,请求恢复Bus-off
        MSCAN0Busoff.Status = MSCAN0_BUS_LIMP;  //进入CAN总线跛行模式
        CAN0_STB = 1;                       //进入省电模式
        MSCAN0_Init();				                  //通过初始化终止所有CAN收发
        CAN0RIER_RXFIE = 0;   		              //禁止CAN接收中断
        CAN0RIER_WUPIE = 0;                     //关闭唤醒中断
        CAN0CTL0_WUPE  = 0;                     //禁止唤醒使能
        if (CAN0RFLG_WUPIF)
          CAN0RFLG_WUPIF = 1;                   //如果有唤醒中断请求标志也一并清除(写1清零)
      }
#endif
    }
  }
  else                                          //总线正常,没有Bus-off发生
  {
    MSCAN0Busoff.Status = MSCAN0_BUS_STABLE;
    MSCAN0Busoff.Timer = 0;

#if MSCAN0_BUS_LIMP_MODE_ENABLE
    MSCAN0Busoff.Cnt   = 0;
#endif
  }
#endif
}

/******************************************************************************
函数名:MSCAN0_Get_Bus_Status
功  能:获取MSCAN0总线状态
参  数:无
返回值:MSCAN0_BUS_STABLE:总线正常,稳定工作
        MSCAN0_BUS_OFF   :总线处于Bus-off状态
        MSCAN0_BUS_LIMP  :总线处于跛行状态(Bus-off后多次恢复均失败而关闭总线)
******************************************************************************/
uint8_t MSCAN0_Get_Bus_Status(void)
{
  return MSCAN0Busoff.Status;
}

/******************************************************************************
函数名:MSCAN0_Init
功  能:MSCAN0初始化程序
参  数:无
返回值:无
******************************************************************************/
void MSCAN0_Init(void)
{
  uint8_t i;

  CAN0CTL0 = 0x01;            //请求 MSCAN进入初始化模式
  while(!(CAN0CTL1_INITAK));  //等待进入初始化模式

  CAN0IDAC = 0;   		        //配置屏蔽验收寄存器的工作方式  two 32-bit filter

  CAN0IDAR0 = 0x00;     	    //设置验收寄存器
  CAN0IDAR1 = 0x00;
  CAN0IDAR2 = 0x00;
  CAN0IDAR3 = 0x00;

  CAN0IDMR0 = 0xFF;           //设置屏蔽寄存器 (这里设置为所有报文ID都接收)
  CAN0IDMR1 = 0xFF;
  CAN0IDMR2 = 0xFF;
  CAN0IDMR3 = 0xFF;

  CAN0IDAR4 = 0x00;      	    //设置验收寄存器
  CAN0IDAR5 = 0x00;
  CAN0IDAR6 = 0x00;
  CAN0IDAR7 = 0x00;

  CAN0IDMR4 = 0xFF;           //设置屏蔽寄存器 (这里设置为所有报文ID都接收)
  CAN0IDMR5 = 0xFF;
  CAN0IDMR6 = 0xFF;
  CAN0IDMR7 = 0xFF;

  CAN0CTL1_CANE   = 1;        //使能MSCAN模块
  CAN0CTL1_CLKSRC = 0;        //选择OSCCLK(8MHz)为MSCAN的时钟源
  CAN0CTL1_LISTEN = 0;        //正常运行模式(非监听模式)

#if MSCAN0_BUS_OFF_AUTO_RECOVERY
  CAN0CTL1_BORM = 0;	      //自动总线BUS_OFF恢复
#else
  CAN0CTL1_BORM = 1;	      //总线BUS_OFF由用户控制恢复
#endif

  CAN0CTL1_WUPM = 1;		      //给唤醒加滤波

  CAN0BTR0 = MSCAN0_BTR0_VALUE;   //波特率设置
  CAN0BTR1 = MSCAN0_BTR1_VALUE;

  CAN0CTL0 = 0x00;            //退出初始化模式
  while (CAN0CTL1_INITAK);    //等待退出初始化模式
  while (!CAN0CTL0_SYNCH);    //等待MSCAN与CAN总线同步


  CAN0TIER = 0x00;            //禁止CAN发送中断
  CAN0RIER_RXFIE = 1;   		  //使能CAN接收中断
  CAN0RIER_WUPIE = 1;

  CAN0_STB = 0;   	          //使能CAN收发器

  MSCAN0Busoff.Status = MSCAN0_BUS_STABLE;
  MSCAN0Busoff.Timer = 0;

#if MSCAN0_BUS_LIMP_MODE_ENABLE
  MSCAN0Busoff.Cnt   = 0;
#endif

  for(i = 0; i < 3; i++)
    MSCAN0TxID[i] = 0xFFFF;   //0xFFFF表示无效ID
}

/******************************************************************************
函数名:MSCAN0_Low_Power_Mode
功  能:令MSCAN0进入低功耗模式
参  数:无
返回值:无
******************************************************************************/
void MSCAN0_Low_Power_Mode(void)
{
  //MSCAN进入低功耗模式
  MSCAN0_Init();               //透过初始化终止CAN收发
  //	CAN0_STB = 1;   	          //使能CAN收发器
  CAN0RIER_RXFIE = 0;   			//禁止CAN接收中断
  if(CAN0RFLG_WUPIF)          //唤醒中断请求标志 = 1 则写1清0
    CAN0RFLG_WUPIF = 1;
  CAN0CTL0_WUPE = 1;			    //唤醒使能

  //置1后,当MSCAN所有发送完成(发送队列空),接受也完成(CAN总线空闲)后,
  //MSCAN 进入SLEEP模式,并且置 CAN0CTL1_SLPAK=1表示确认
  //在置此位前 CAN0RFLG_WUPIF 必须清除

  //当CAN0CTL0_SLPRQ=1,且CAN0CTL1_SLPAK=1 ,表示MSCAN已经进入睡眠模式
  CAN0CTL0_SLPRQ = 1;			    //请求置MSCAN进入睡眠模式

  //当MSCAN进入睡眠模式后,如果 CAN0CTL0_WUPE = 1 且收到CAN报文后,MSCAN退出睡眠,且CAN0RFLG_WUPIF置1
  //同时 CAN0CTL0_SLPRQ,CAN0CTL1_SLPAK 也将自动清0.

  //在MSCAN进入睡眠模式后,软件也可以主动置CAN0CTL0_SLPRQ = 0,使得MSCAN退出睡眠,CAN0CTL1_SLPAK 也将自动清0.
  //当 CAN0CTL0_SLPRQ = 1后,如果MSCAN还没有进入睡眠模式,那么不能主动清CAN0CTL0_SLPRQ = 0.

  CAN0TARQ = 0x07;            //请求终止所有报文发送.
}

#pragma CODE_SEG __NEAR_SEG NON_BANKED   	//置所有中断函数放在FLASH的非分页区

/******************************************************************************
函数名:MSCAN0_TX_ISR
功  能:MSCAN0发送中断服务函数。
        当有CAN报文发送完毕或被中止发送时,执行此函数
参  数:无
返回值:无
******************************************************************************/
void interrupt MSCAN0_TX_ISR(void)
{
  uint8_t i;
  uint8_t TxFlag;
  uint8_t TxBuf;
  uint8_t BufDetect;

  do
  {
    TxFlag = CAN0TFLG;

    TxBuf = CAN0TIER & TxFlag;              //获取发送完毕或被中止发送的报文Buffer
    CAN0TIER &= ~TxBuf;                     //关闭发送完毕或被中止发送的报文Buffer对应的中断

    BufDetect = 1;
    for (i = 0; i < 3; i++)
    {
      if (BufDetect & TxBuf)
      {
        if (BufDetect & CAN0TAAK)           //报文被成功中止发送
          MSCAN0_L_Data_Confirm(MSCAN0TxID[i], NOT_COMPLETE);
        else                                //报文发送成功
          MSCAN0_L_Data_Confirm(MSCAN0TxID[i], COMPLETE);

        MSCAN0TxID[i] = 0xFFFF;             //清除报文记录
      }

      BufDetect <<= 1;
    }
  }
  while (TxFlag != CAN0TFLG);               //避免中断处理期间有新的报文发送完毕
}

/******************************************************************************
函数名:MSCAN0_RX_ISR
功  能:MSCAN0接收中断服务函数。
        当有CAN报文送达时,接收CAN报文
参  数:无
返回值:无
******************************************************************************/
void interrupt MSCAN0_RX_ISR(void)
{
  uint16_t CANMsgID;

  CAN0RIER_RXFIE = 0;                      //先禁止MSCAN接收中断

  if (CAN0RXIDR1_IDE || CAN0RXIDR1_SRR)    //如果收到的数据为扩展帧,或为远程帧
  {
    CAN0RFLG_RXF = 1;                      //清接收中断标志
    CAN0RIER_RXFIE = 1;                    //重新启动MSCAN接收中断

    return;
  }

  CANMsgID = ((uint16_t)(CAN0RXIDR0 << 3)) | ((uint16_t)(CAN0RXIDR1 >> 5));

  MSCAN0_L_Data_Indication(CANMsgID, CAN0RXDLR & 0x0F, &CAN0RXDSR0);

  CAN0RFLG_RXF = 1;                        //清接收中断标志
  CAN0RIER_RXFIE = 1;                      //重新启动MSCAN接收中断
}

/******************************************************************************
函数名:MSCAN0_WAKEUP_ISR
功  能:MSCAN0唤醒中断
参  数:无
返回值:无
******************************************************************************/
void interrupt MSCAN0_WAKEUP_ISR(void)
{
  CAN0RFLG_WUPIF = 1;
  WakeUpFlag = 1;
}

uint8_t GetWakeUPFLAG(void)
{
  return    WakeUpFlag;
}

#pragma CODE_SEG DEFAULT

Std_ReturnType CanIf_Transmit(PduIdType CanTxPduId, PduInfoType *PduInfoPtr)
{
  MSCAN0_L_Data_Request( 0x402, (uint8_t)PduInfoPtr->SduLength, (uint8_t *)PduInfoPtr->SduDataPtr );
  return E_OK;
}