你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】使用STM32F103做CAN的收发通信

[复制链接]
STMCU小助手 发布时间:2021-11-7 15:31
CAN通信
    CAN 是Controller Area Network 的缩写(以下称为CAN),该通信使用的是ISO11898标准,该标准的物理层特征如下图所示。
)857N86HK1E0[0K2MXK36X3.png
    CAN协议是通过以下5种类型的帧进行的:
  • 数据帧
  • 摇控帧
  • 错误帧
  • 过载帧
  • 帧间隔

    另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID。
    大部分系统使用的都是数据帧 ,我这里使用的也是数据帧。
    数据帧一般由7个段构成,即:
(1) 帧起始。表示数据帧开始的段。
(2) 仲裁段。表示该帧优先级的段。
(3) 控制段。表示数据的字节数及保留位的段。
(4) 数据段。数据的内容,一帧可发送0~8个字节的数据。
(5) CRC段。检查帧的传输错误的段。
(6) ACK段。表示确认正常接收的段。
(7) 帧结束。表示数据帧结束的段。
    明确了数据帧概念,还需要理解一下过滤器的作用。
    STM32的标识符屏蔽滤波目的是减少了CPU处理CAN通信的开销。STM32的过滤器组最多有28个(互联型),但是STM32F103ZET6只有14个(增强型),每个滤波器组x由2个32为寄存器,CAN_FxR1和CAN_FxR2组成。
    STM32每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:
  • 1个32位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
  • 2个16位过滤器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位

    此外过滤器可配置为,屏蔽位模式和标识符列表模式。
    在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。
    而在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。相关文章:CAN总线详解
    一般也都是使用标识符列表模式,这里使用的也是标识符列表模式。滤波过程举例如下:
PERMNQ{)L%G}{986WRA~Q]W.png

    在程序中就是:
  1. //要过滤的ID高位
  2. CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;  
  3. //要过滤的ID低位                 
  4. CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF;
  5. //过滤器屏蔽标识符的高16位值
  6. CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;   
  7. //过滤器屏蔽标识符的低16位值         
  8. CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;
复制代码

    这里的CAN_FilterId和CAN_FilterMaskId是配合使用的,意思是CAN_FilterId指出需要屏蔽ID的什么内容,什么格式;CAN_FilterMaskId是指CAN_FilterId的每一位是否需要过滤,若CAN_FilterMaskId在某位上是1的话,ID对应位上的数值就必须和CAN_FilterId该位上的一样,保持一致,反之则是“不关心”。
上述程序的设置的含义就是:只接收来自0x1314的数据,屏蔽其他ID的数据。
程序思路
    这里准备做一个主机与从机的通信,主要用扩展标识符ExtId来区分,分配的标识符是:
主机:0x1314
从机:0x1311
    主机负责接收所有从机的数据,不需要过滤,用扩展标识符ExtId来区分不同从机的数据;主机还可以向不同从机发送信息。而从机则只接收来自主机的数据,同样用扩展标识符ExtId来区分是否是发向自己的数据;同时,也能够向主机发送信息。
相关代码
    代码也是非常简单的,这里贴出了主机和从机的can.c和can.h两个文件。
从机相关代码
can.c文件:

  1. #include "can.h"


  2. /* 在中断处理函数中返回 */
  3. //__IO uint32_t ret = 0;


  4. //接收数据缓冲器
  5. u8 RxBuf[5];
  6. u8 Rx_flag=0;


  7. void CAN1_Init(void)
  8. {
  9.     GPIO_InitTypeDef GPIO_InitStructure;
  10.     NVIC_InitTypeDef NVIC_InitStructure;
  11.     CAN_InitTypeDef        CAN_InitStructure;
  12.     CAN_FilterInitTypeDef  CAN_FilterInitStructure;


  13.     /* 复用功能和GPIOB端口时钟使能*/   
  14.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);                                                                     


  15.     /* CAN1 模块时钟使能 */
  16.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);


  17.     /* Configure CAN pin: RX */  // PB8
  18.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  19.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;       //上拉输入
  20.     GPIO_Init(GPIOB, &GPIO_InitStructure);


  21.     /* Configure CAN pin: TX */   // PB9
  22.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  23.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出
  24.     GPIO_Init(GPIOB, &GPIO_InitStructure);


  25.     //#define GPIO_Remap_CAN    GPIO_Remap1_CAN1 本实验没有用到重映射I/O
  26.     GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);


  27.     //CAN_NVIC_Configuration(); //CAN中断初始化   
  28.     /* Configure the NVIC Preemption Priority Bits */  
  29.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);


  30.     #ifdef  VECT_TAB_RAM  
  31.       /* Set the Vector Table base location at 0x20000000 */
  32.       NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
  33.     #else  /* VECT_TAB_FLASH  */
  34.       /* Set the Vector Table base location at 0x08000000 */
  35.       NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);   
  36.     #endif


  37.     /* enabling interrupt */
  38.     NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;;
  39.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  40.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  41.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  42.     NVIC_Init(&NVIC_InitStructure);


  43.     //CAN_INIT();//CA初始化N模块
  44.     /* CAN register init */
  45.     CAN_DeInit(CAN1);                       //将外设CAN的全部寄存器重设为缺省值
  46.     CAN_StructInit(&CAN_InitStructure);     //把CAN_InitStruct中的每一个参数按缺省值填入


  47.     /* CAN cell init */
  48.     CAN_InitStructure.CAN_TTCM=DISABLE;         //没有使能时间触发模式
  49.     CAN_InitStructure.CAN_ABOM=DISABLE;         //没有使能自动离线管理
  50.     CAN_InitStructure.CAN_AWUM=DISABLE;         //没有使能自动唤醒模式
  51.     CAN_InitStructure.CAN_NART=DISABLE;         //没有使能非自动重传模式
  52.     CAN_InitStructure.CAN_RFLM=DISABLE;         //没有使能接收FIFO锁定模式
  53.     CAN_InitStructure.CAN_TXFP=DISABLE;         //没有使能发送FIFO优先级
  54.     CAN_InitStructure.CAN_Mode=CAN_Mode_Normal; //CAN设置为正常模式
  55.     CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;      //重新同步跳跃宽度1个时间单位
  56.     CAN_InitStructure.CAN_BS1=CAN_BS1_3tq;      //时间段1为3个时间单位
  57.     CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;      //时间段2为2个时间单位
  58.     CAN_InitStructure.CAN_Prescaler=60;         //时间单位长度为60
  59.     CAN_Init(CAN1,&CAN_InitStructure);          //波特率为:72M/2/60(1+3+2)=0.1 即波特率为100KBPs


  60.     // CAN filter init 过滤器,注意,只接收主机发过来的数据,屏蔽其他数据
  61.     CAN_FilterInitStructure.CAN_FilterNumber=1;                     //指定过滤器为1
  62.     CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;   //指定过滤器为标识符屏蔽位模式
  63.     CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //过滤器位宽为32位


  64.     //CAN_FilterInitStructure.CAN_FilterIdHigh= (((u32)0x1314<<3)&0xFFFF0000)>>16;  
  65.     CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;                  //要过滤的ID高位
  66.     CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要过滤的ID低位


  67.     CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;            //过滤器屏蔽标识符的高16位值
  68.     CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;             //过滤器屏蔽标识符的低16位值
  69.     CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;     //设定了指向过滤器的FIFO为0
  70.     CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;            //使能过滤器
  71.     CAN_FilterInit(&CAN_FilterInitStructure);                       //按上面的参数初始化过滤器


  72.     /* CAN FIFO0 message pending interrupt enable */
  73.     CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE);                         //使能FIFO0消息挂号中断
  74. }


  75. /* 发送两个字节的数据*/
  76. u8 CAN_SetMsg(u8 Data1,u8 Data2)
  77. {
  78.     u8 mbox;
  79.     u16 i=0;
  80.     CanTxMsg TxMessage;  


  81.     TxMessage.StdId=0x0000;     //标准标识符为0x00
  82.     TxMessage.ExtId=0x1311;     //扩展标识符0x1311,可以更改该标识符以示区分不同从机
  83.     TxMessage.IDE=CAN_ID_EXT;   //使用扩展标识符
  84.     TxMessage.RTR=CAN_RTR_DATA; //为数据帧
  85.     TxMessage.DLC=2;            //消息的数据长度为2个字节
  86.     TxMessage.Data[0]=Data1;    //第一个字节数据
  87.     TxMessage.Data[1]=Data2;    //第二个字节数据


  88.     //发送数据
  89.     mbox= CAN_Transmit(CAN1, &TxMessage);  
  90.     while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))
  91.         i++;    //等待发送结束
  92.     if(i>=0XFFF)
  93.         return 0;
  94.     return 1;
  95. }
  96. u8 CAN_GetMsg(u8 *msg1,u8 *msg2)
  97. {
  98.     if(Rx_flag == 1)//发现数据
  99.     {
  100.         *msg1=RxBuf[0];
  101.         *msg2=RxBuf[1];
  102.         Rx_flag=0;//数据已经取走,可以更新数据
  103.         return 1;
  104.     }else
  105.         return 0;
  106. }
  107. /* USB中断和CAN接收中断服务程序,USB跟CAN公用I/O,这里只用到CAN的中断。 */
  108. void USB_LP_CAN1_RX0_IRQHandler(void)
  109. {


  110.   CanRxMsg RxMessage;


  111.   RxMessage.StdId=0x00;
  112.   RxMessage.ExtId=0x00;
  113.   RxMessage.IDE=0;
  114.   RxMessage.DLC=0;
  115.   RxMessage.FMI=0;
  116.   RxMessage.Data[0]=0x00;
  117.   RxMessage.Data[1]=0x00;   


  118.   CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的数据  


  119.   if(Rx_flag == 0)//数据已取走或者缓冲器为空
  120.     {
  121.         RxBuf[0]=RxMessage.Data[0];
  122.         RxBuf[1]=RxMessage.Data[1];
  123.         Rx_flag=1;//数据已经备好,等待取走
  124.     }


  125. }
复制代码

can.h文件
NQOI@[X5(~C{N5(@A93Y4.png
主机相关代码
    这里主机代码大部分是和从机类似的,就只贴出不同的地方了。
can.c文件:

  1. #include "can.h"


  2. /* 在中断处理函数中返回 */
  3. //__IO uint32_t ret = 0;


  4. void CAN1_Init(void)
  5. {
  6.     ......//以上与从机部分相同


  7.     //CAN filter init 过滤器,已经设置为任意,可以通过ExtId标识符区分从机代号
  8.     CAN_FilterInitStructure.CAN_FilterNumber=1;                     //指定过滤器为1
  9.     CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;   //指定过滤器为标识符屏蔽位模式
  10.     CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //过滤器位宽为32位
  11.     CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;                //过滤器标识符的高16位值
  12.     CAN_FilterInitStructure.CAN_FilterIdLow=CAN_ID_EXT|CAN_RTR_DATA;//过滤器标识符的低16位值
  13.     CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;            //过滤器屏蔽标识符的高16位值
  14.     CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;             //过滤器屏蔽标识符的低16位值
  15.     CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;     //设定了指向过滤器的FIFO为0
  16.     CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;            //使能过滤器
  17.     CAN_FilterInit(&CAN_FilterInitStructure);                       //按上面的参数初始化过滤器


  18.     /* CAN FIFO0 message pending interrupt enable */
  19.     CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE);                         //使能FIFO0消息挂号中断
  20. }


  21. //接收数据缓冲器
  22. u8 CAN_RX_BUF[CAN_RX_LEN]={0};     //接收缓冲,最大USART_REC_LEN个字节.
  23. //接收标志位
  24. u8 Rx_flag=0;
  25. /* USB中断和CAN接收中断服务程序,USB跟CAN公用I/O,这里只用到CAN的中断。 */
  26. void USB_LP_CAN1_RX0_IRQHandler(void)
  27. {
  28.     u8 i=0;
  29.     CanRxMsg RxMessage;


  30.     RxMessage.StdId=0x00;
  31.     RxMessage.ExtId=0x00;
  32.     RxMessage.IDE=0;
  33.     RxMessage.DLC=0;
  34.     RxMessage.FMI=0;


  35.     CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的数据  


  36.     if(Rx_flag == 0)//数据已取走或者缓冲器为空
  37.     {
  38.         if((RxMessage.DLC) == 2)//是否收到2位字节数据
  39.         {
  40.              CAN_RX_BUF[0]=RxMessage.Data[0];
  41.              CAN_RX_BUF[1]=RxMessage.Data[1];     
  42.         }
  43.     }


  44. }


  45. /* 发送两个字节的数据*/
  46. u8 CAN_SendMsg(u8* data1, u8* data2)
  47. {
  48.     u8 mbox;
  49.     u16 i=0;
  50.     CanTxMsg TxMessage;  


  51.     TxMessage.StdId=0x0000;     //标准标识符为0x00
  52.     TxMessage.ExtId=0x1314;     //扩展标识符0x0000
  53.     TxMessage.IDE=CAN_ID_EXT;   //使用扩展标识符
  54.     TxMessage.RTR=CAN_RTR_DATA; //为数据帧
  55.     TxMessage.DLC=2;            //消息的数据长度为2个字节
  56.     TxMessage.Data[0]=Data1;    //第一个字节数据
  57.     TxMessage.Data[1]=Data2;    //第二个字节数据


  58.     //发送数据
  59.     mbox= CAN_Transmit(CAN1, &TxMessage);  
  60.     while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))
  61.         i++;    //等待发送结束
  62.     if(i>=0XFFF)
  63.         return 0;//发送失败
  64.     return 1;//发送成功
  65. }
  66. u8 CAN_GetMsg(u8 *msg1,u8 *msg2)
  67. {
  68.     if(Rx_flag == 1)//发现数据
  69.     {
  70.         *msg1=CAN_RX_BUF[0];
  71.         *msg2=CAN_RX_BUF[1];
  72.         Rx_flag=0;//数据已经取走,可以更新数据
  73.         return 1;
  74.     }else
  75.         return 0;
  76. }
  77. void Clear_canBuffer(void)
  78. {
  79.     Rx_flag=0;//清楚接收标志位
  80.     memset(CAN_RX_BUF, 0, sizeof(u8)*CAN_RX_LEN);//清空缓冲区
  81. }
  82. u8 Check_canRX(void)
  83. {
  84.     return (Rx_flag == 6);
  85. }
复制代码

can.h文件:

  1. #ifndef __CAN_H
  2. #define __CAN_H


  3. #include "sys.h"
  4. #include "string.h"


  5. #define CAN_RX_LEN          30          //定义最大接收字节数


  6. extern u8  CAN_RX_BUF[CAN_RX_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符


  7. void CAN1_Init(void);
  8. u8 CAN_SendMsg(u8* data1, u8* data2);
  9. u8 CAN_GetMsg(u8 *msg1,u8 *msg2);


  10. #endif /* __CAN_H */
复制代码

收藏 评论0 发布时间:2021-11-7 15:31

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版