0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用STM32F103做CAN的收发通信

STM32嵌入式开发 来源:STM32嵌入式开发 作者:STM32嵌入式开发 2022-07-06 11:01 次阅读

CAN通信

CAN 是Controller Area Network 的缩写(以下称为CAN),该通信使用的是ISO11898标准,该标准的物理层特征如下图所示。

95bfe748-f6c2-11ec-ba43-dac502259ad0.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总线详解

一般也都是使用标识符列表模式,这里使用的也是标识符列表模式。滤波过程举例如下:
95d70536-f6c2-11ec-ba43-dac502259ad0.png

在程序中就是:


		
//要过滤的ID高位 CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;  //要过滤的ID低位                 CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //过滤器屏蔽标识符的高16位值CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;   //过滤器屏蔽标识符的低16位值         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文件:

#include "can.h"

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

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

void CAN1_Init(void){    GPIO_InitTypeDef GPIO_InitStructure;     NVIC_InitTypeDef NVIC_InitStructure;    CAN_InitTypeDef        CAN_InitStructure;    CAN_FilterInitTypeDef  CAN_FilterInitStructure;

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

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

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

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

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

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

    #ifdef  VECT_TAB_RAM        /* Set the Vector Table base location at 0x20000000 */       NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);     #else  /* VECT_TAB_FLASH  */      /* Set the Vector Table base location at 0x08000000 */       NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);       #endif

    /* enabling interrupt */    NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;;    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_Init(&NVIC_InitStructure);

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

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

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

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

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

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

/* 发送两个字节的数据*/u8 CAN_SetMsg(u8 Data1,u8 Data2){     u8 mbox;    u16 i=0;     CanTxMsg TxMessage;  

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

    //发送数据    mbox= CAN_Transmit(CAN1, &TxMessage);      while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))        i++;    //等待发送结束    if(i>=0XFFF)        return 0;    return 1;}u8 CAN_GetMsg(u8 *msg1,u8 *msg2){    if(Rx_flag == 1)//发现数据    {        *msg1=RxBuf[0];        *msg2=RxBuf[1];        Rx_flag=0;//数据已经取走,可以更新数据        return 1;    }else        return 0;}/* USB中断和CAN接收中断服务程序,USB跟CAN公用I/O,这里只用到CAN的中断。*/void USB_LP_CAN1_RX0_IRQHandler(void){

  CanRxMsg RxMessage;

  RxMessage.StdId=0x00;  RxMessage.ExtId=0x00;  RxMessage.IDE=0;  RxMessage.DLC=0;  RxMessage.FMI=0;  RxMessage.Data[0]=0x00;  RxMessage.Data[1]=0x00;    

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

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

}

		

can.h文件

95e760c0-f6c2-11ec-ba43-dac502259ad0.png

主机相关代码

这里主机代码大部分是和从机类似的,就只贴出不同的地方了。 can.c文件:

#include "can.h"

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

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

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

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

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

    RxMessage.StdId=0x00;    RxMessage.ExtId=0x00;    RxMessage.IDE=0;    RxMessage.DLC=0;    RxMessage.FMI=0;

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

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

} 

/* 发送两个字节的数据*/u8 CAN_SendMsg(u8* data1, u8* data2){     u8 mbox;    u16 i=0;     CanTxMsg TxMessage;  

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

    //发送数据    mbox= CAN_Transmit(CAN1, &TxMessage);      while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))        i++;    //等待发送结束    if(i>=0XFFF)        return 0;//发送失败    return 1;//发送成功 }u8 CAN_GetMsg(u8 *msg1,u8 *msg2){    if(Rx_flag == 1)//发现数据    {        *msg1=CAN_RX_BUF[0];        *msg2=CAN_RX_BUF[1];        Rx_flag=0;//数据已经取走,可以更新数据        return 1;    }else        return 0;}void Clear_canBuffer(void){    Rx_flag=0;//清楚接收标志位    memset(CAN_RX_BUF, 0, sizeof(u8)*CAN_RX_LEN);//清空缓冲区}u8 Check_canRX(void){    return (Rx_flag == 6);}

		

can.h文件:

#ifndef __CAN_H#define __CAN_H

#include "sys.h"#include "string.h"

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

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

void CAN1_Init(void);u8 CAN_SendMsg(u8* data1, u8* data2);u8 CAN_GetMsg(u8 *msg1,u8 *msg2);

#endif /* __CAN_H */

文章出处:【微信公众号:STM32嵌入式开发】欢迎添加关注!文章转载请注明出处。

审核编辑:符乾江
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • CAN
    CAN
    +关注

    关注

    57

    文章

    2702

    浏览量

    463201
  • STM32
    +关注

    关注

    2265

    文章

    10858

    浏览量

    354439

原文标题:使用STM32F103做CAN的收发通信

文章出处:【微信号:c-stm32,微信公众号:STM32嵌入式开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    STM32F103xC,STM32F103xD,STM32F103xE中文资料

    电子发烧友网站提供《STM32F103xC,STM32F103xD,STM32F103xE中文资料.pdf》资料免费下载
    发表于 06-17 14:12 1次下载

    STM32F103CAN调试器接到该板子后给其发消息,调试器会显示总线数据错误的原因?

    项目里有个CAN收发相关的板子,用的是STM32F103芯片。在与上位机的CAN交互过程中经常会出现通讯错误的情况,表现在上位机发送报文后没有收到回复。用
    发表于 05-27 07:42

    stm32f103 CANbus控制器对于扩展帧格式的srr位怎么设置成低电平?

    这周 用stm32f103芯片自带的can控制器 驱动外部的can发射器 ,用扩展帧发现 srr位一直为高电平。这个可以通过软件设置成低电平吗? 外部发射芯片是nxp a82c250can
    发表于 05-17 07:44

    stm32f302和stm32f103的ADC区别是什么?

    请问stm32f302和stm32f103的ADC除了stm32f103有2个ADC(ADC1和ADC2),stm32f302只有1个ADC外,在使用ADC+DMA采样多通道的模拟信号
    发表于 05-15 08:03

    求助,关于STM32F103 CAN通信的几个疑问求解

    使用STM32F103作为CAN从机发送和接收数据,从机是被动发送,在收到主机特定命令帧后才会发送数据,其余时候只接收:问题1:单个发送没有问题,多个CAN并在一起,其中某个会出现只能接收并未
    发表于 04-18 07:30

    请问STM32F103能否USB打印机?

    STM32F103能否USB打印机? 虚拟成打印机接收数据,再从其它数据口(SPI或UART)传出打印数据。找不到这样的例程。
    发表于 04-16 06:29

    STM32F103RB无法进入CAN中断的原因?怎么解决?

    鄙人买了一块NUCLEO F103RB的开发板,第一次玩STM系列的片子。使用STM32Cube和STM32MX开发,确实很好用,快捷方便。 IO,定时器等等资源很快就调试完了。现在有个小问题,请教
    发表于 04-09 07:14

    如何用STM32F103傅里叶变换?

    Hi,想问下,用STM32F103傅里叶变换,请问例程在那里下载?
    发表于 03-27 07:52

    请问STM32F103 I2C通信和SPI通信哪种方式更稳定?

    STM32F103这颗料I2C通信和SPI通信哪种方式更稳定?
    发表于 03-22 06:24

    STM32F103如何使用BOOT下载程序?

    STM32F103如何使用BOOT下载程序
    发表于 03-15 07:21

    ARM系列STM32F103芯片的解密方法

    本文介绍ARM系列STM32F103芯片的解密方法,其内核是Cortex-M3,内存从16K-512K都有。
    发表于 02-28 11:20 1529次阅读

    stm32f103 flash模拟eeprom

    STM32F103是意法半导体(STMicroelectronics)推出的一款32位单片机系列,该系列芯片具有高性能和丰富的外设接口,广泛应用于工业控制、消费电子、汽车电子等领域。其中
    的头像 发表于 01-09 11:21 1863次阅读

    在GD32F103移植STM32F103代码

    使用相同FLASH和管脚数量相同的芯片,例如GDF103C8T6移植STM32F103C8T6程序。虽然两个款芯片的寄存器地址以及架构基本相同。但是需要注意的是GD32F10x主频是108兆,但是
    的头像 发表于 12-15 13:37 1542次阅读
    在GD32<b class='flag-5'>F103</b>移植<b class='flag-5'>STM32F103</b>代码

    gd32f103可以完全替代stm32f103

    GD32F103是中国厂商GigaDevice推出的一款低成本、高性能的微控制器产品线。STM32F103是意法半导体(STMicroelectronics)的旗舰产品之一,享有良好的声誉和广泛
    的头像 发表于 12-07 11:26 4888次阅读

    基于STM32F103的直流电机pid调节闭环控制

    基于STM32F103的直流电机pid调节的闭环控制,主要模块:NOKIA5110显示屏红外测速模块
    发表于 11-27 16:50 8次下载