OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6374|回复: 1

STM8 CAN通讯 总结

[复制链接]

9

主题

141

帖子

1

精华

高级会员

Rank: 4

积分
606
金钱
606
注册时间
2017-1-3
在线时间
65 小时
发表于 2018-9-19 15:47:08 | 显示全部楼层 |阅读模式
使用了STM8S208S6这个8位的单片机来构建一个CAN通讯网络,使用相同的两块板子。
当然最首先的CAN的基本原理我们就不进行赘述了,当时自己在代码中也主要是对于CAN的配置的过程中遇到了问题,特别是对于屏蔽寄存器的配置,这一部分花费了我一段时间,
所以大家一定要看看CAN的寄存器的配置,多去琢磨一下。弄懂之后就会好一些了
首先看看CAN的初始化函数

[mw_shl_code=c,true]void CANBus_Init(void)
{
  u8 byCAN_FilterID1;
  u8 byCAN_FilterID2;
  u8 byCAN_FilterID3;
  u8 byCAN_FilterID4;
  u8 byCAN_FilterIDMask1;
  u8 byCAN_FilterIDMask2;
  u8 byCAN_FilterIDMask3;
  u8 byCAN_FilterIDMask4;
  CAN_FilterNumber_TypeDef sctCAN_FilterNumber;
  CAN_FilterMode_TypeDef sctCAN_FilterMode;
  CAN_FilterScale_TypeDef sctCAN_FilterScale;
  CAN_InitStatus_TypeDef CAN_InitState;
  
  CAN_DeInit();
  GPIO_Init(GPIOG,GPIO_PIN_0,GPIO_MODE_OUT_PP_HIGH_FAST);
  GPIO_Init(GPIOG,GPIO_PIN_1,GPIO_MODE_IN_FL_NO_IT);  
  CAN_InitState = CAN_Init(CAN_MasterCtrl_AllDisabled,
                           CAN_Mode_Normal,/                             CAN_SynJumpWidth_1TimeQuantum,
                           CAN_BitSeg1_8TimeQuantum,
                           CAN_BitSeg2_7TimeQuantum,
                           2);
  
  if(CAN_InitState == CAN_InitStatus_Success)
    printf("CAN Init done\r\n");
  else
    printf("CAN Init failed\r\n");
  
  sctCAN_FilterNumber = CAN_FilterNumber_0;
  sctCAN_FilterMode = CAN_FilterMode_IdMask;
  sctCAN_FilterScale = CAN_FilterScale_32Bit;
  byCAN_FilterID1=0x1E;  
  byCAN_FilterID2=0x20;
  byCAN_FilterID3=0;
  byCAN_FilterID4=0;
  byCAN_FilterIDMask1=0xFF;
  byCAN_FilterIDMask2=0xE0;
  byCAN_FilterIDMask3=0;
  byCAN_FilterIDMask4=0;   
  
  CAN_FilterInit(sctCAN_FilterNumber,
                 ENABLE,
                 sctCAN_FilterMode,
                 sctCAN_FilterScale,
                 byCAN_FilterID1,
                 byCAN_FilterID2,
                 byCAN_FilterID3,
                 byCAN_FilterID4,
                 byCAN_FilterIDMask1,
                 byCAN_FilterIDMask2,
                 byCAN_FilterIDMask3,
                 byCAN_FilterIDMask4);
  
  
  CAN_ITConfig(CAN_IT_FMP, ENABLE);
}
[/mw_shl_code]


在这里我们设置CAN工作在正常模式,也就是可以进行正常的数据收发。当然在进行调试的时候可以使用Loopback模式,在这个模式下面发送数据的时候,接收邮箱是可以接收到自己发送出去的数据的。可以用这个模式来测试CAN总线是否有工作。 接下来就是配置CAN总线的波特率了,根据计算公式波特率= 总线频率/(分频系数*(Tq+BS1+BS2));其中Tq, BS1, BS2这些是用来监视CAN总线,设置采样点同步时间什么的,大致了解就行了,在我的工程当中,将会使用16M Hz内部高速时钟(HSI),分频系数设置为2,那么根据计算16M/2/(1+7+8) = 500K Hz,也就是说CAN总线的波特率为500KHz. 接下来就是对于本身的ID以及屏蔽位寄存器的设置了。我们在这里是使用标准帧通信,然后采用屏蔽过滤组0,选择屏蔽位模式,32位的过滤寄存器。下面着重讲一下如何设置这几个寄存器,我准备将CANID设置为0XF1, 全匹配接收。首先我们看看屏蔽寄存器的构成,对于每个过滤组一共有8个寄存器
111.png
这里8个过滤寄存器是分为高位和低位的,而具体要怎么使用这4个高位和低位寄存器是跟在FMR1寄存器中的设置有关的,因为FMHxFMLx的设置直接影响到我们怎么使用这8个寄存器。这里我们是设置均工作在屏蔽位模式也就是FMH0 FML0均设置为0 222.png
在这种模式下,低位的4byte的寄存器将会用来存储ID标识符,后4byte会用来存储屏蔽位的设置。按照上上个图里面的设置,我们要设置的0XF1 = 000 1111 0001 bit(注意标准帧是11bit的,所以我们再前面加上三个bit0) 333.png
转换成十六进制就是Identifier: 0x1E 0x20 0x00 0x00Mask: 0Xff 0xE0 0x00 0x00对应到代码里的设置就如下所示 byCAN_FilterID1=0x1E;   byCAN_FilterID2=0x20; byCAN_FilterID3=0; byCAN_FilterID4=0; byCAN_FilterIDMask1=0xFF; byCAN_FilterIDMask2=0xE0; byCAN_FilterIDMask3=0; byCAN_FilterIDMask4=0;   当然在调试过程中,可以先不设屏蔽位,就是把所有的FilterIDMaskx都设置为0,这样CAN总线上的所有的帧都能收到最后再ENABLE接收中断CAN_ITConfig(CAN_IT_FMP, ENABLE); 这样CAN的初始化就完成了。 接下来是CAN的接收了,所有的接收应该都是在中断里面进行的,由于我们设置了屏蔽位,所以只有跟自己的ID相匹配的数据帧才能接收到。[mw_shl_code=c,true]INTERRUPT_HANDLER(CAN_RX_IRQHandler, 8)
{
    CAN_Receive();
    CAN_Frame_Handler(&CAN_FRAME);
    printf("CAN Rx IRQ \r\n");
}
[/mw_shl_code]
在中断函数中,CAN_Receive() STM8的库函数,相当于是把所有接收到的数据从FIFO缓冲区转到RAM中,而函数CAN_Frame_Handler(&CAN_FRAME) 是我们自己定义的,用来解析所接收到的数据,具体代码如下[mw_shl_code=c,true]void CAN_Frame_Handler(CAN_FrameType *frame)
{
  u8 i=0;
  u8 buff[12];
  CAN_Page_TypeDef can_page = CAN_GetSelectedPage();
  
  frame->ID = CAN_GetReceivedId();                    //get frame ID
  frame->IDE = CAN_GetReceivedIDE();                  //get IDE
  frame->RTR = CAN_GetReceivedRTR();                  //get RTR
  frame->DLC = CAN_GetReceivedDLC();                  //get DLC
  frame->FMI = CAN_GetReceivedFMI();                  //get FMI
  
  buff[0]=(u8)((frame->ID) >> 24);
  buff[1]=(u8)((frame->ID) >> 16);  
  buff[2]=(u8)((frame->ID) >> 8);  
  buff[3]=(u8)(frame->ID);

  
  for(i=0;i<8;i++)                                    //get 8 Byte data
  {
    frame->Data = CAN_GetReceivedData(i);
    buff[4+i]=frame->Data;
  }
  CAN_Rx_Frame_Flag=1;                        //set flag to indicate received data frame
  CAN_FIFORelease();
  CAN_SelectPage(can_page);
}
[/mw_shl_code]

这个就是把一个帧里面的数据都解析出来了,在最后面设置一个flag,这样就可以根据收到的数据进行相应的动作了 接下来讲一下发送数据,具体代码如下[mw_shl_code=c,true]CAN_TxStatus_TypeDef CAN_Send_Frame(CAN_FrameType *frame)
{
    CAN_TxStatus_TypeDef CAN_TxStatus = CAN_TxStatus_NoMailBox;
    CAN_Page_TypeDef can_page = CAN_GetSelectedPage();
  

    if ((CAN->TPR & CAN_TPR_TME0) == CAN_TPR_TME0)          //choose a empty mailbox
    {
        CAN_TxStatus = CAN_TxStatus_MailBox0Ok;
    }
    else if ((CAN->TPR & CAN_TPR_TME1) == CAN_TPR_TME1)
    {
        CAN_TxStatus = CAN_TxStatus_MailBox1Ok;
    }
    else if ((CAN->TPR & CAN_TPR_TME2) == CAN_TPR_TME2)
    {
        CAN_TxStatus = CAN_TxStatus_MailBox2Ok;
    }
    else
    {
        CAN_TxStatus = CAN_TxStatus_NoMailBox;
    }
  
    if(CAN_TxStatus != CAN_TxStatus_NoMailBox)            //transmit starts
    {
      CAN->PSR = (u8)CAN_TxStatus;


      frame->ID &= (u16)CAN_STDID_SIZE;
      CAN->Page.TxMailbox.MIDR1 = (u8)((frame->ID>>6) | (frame->RTR)) ;
      CAN->Page.TxMailbox.MIDR2 = (u8)(frame->ID<<2);      
      
      CAN->Page.TxMailbox.MDLCR &= (u8)0xF0;
      CAN->Page.TxMailbox.MDLCR |= (u8)(frame->DLC);
      
      CAN->Page.TxMailbox.MDAR1 = frame->Data[0];
      CAN->Page.TxMailbox.MDAR2 = frame->Data[1];
      CAN->Page.TxMailbox.MDAR3 = frame->Data[2];
      CAN->Page.TxMailbox.MDAR4 = frame->Data[3];
      CAN->Page.TxMailbox.MDAR5 = frame->Data[4];
      CAN->Page.TxMailbox.MDAR6 = frame->Data[5];
      CAN->Page.TxMailbox.MDAR7 = frame->Data[6];
      CAN->Page.TxMailbox.MDAR8 = frame->Data[7];
      
      CAN->Page.TxMailbox.MCSR |= CAN_MCSR_TXRQ;
    }
    else
      printf("no empty mailbox\r\n");
    CAN_SelectPage(can_page);
    return (CAN_TxStatus_TypeDef)CAN_TxStatus;
}
[/mw_shl_code]

这个函数比较容易看明白,说得直白点,就是按照帧格式去把一帧的数据填满就行了。当然在最开始我们要选择一个空的发送邮箱,然后把帧ID填入,数据填入,最后置上CAN_MCSR寄存器中的TXRQ位,数据帧就发送出去了。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

231

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2872
金钱
2872
注册时间
2018-1-24
在线时间
258 小时
发表于 2018-9-19 16:35:49 | 显示全部楼层
有没有CAN通信协议来一份,比如充电桩方面的
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-19 23:43

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表