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

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

3天内不再提示

用STM32F103做CAN的收发通信的程序思路与代码分享

STM32嵌入式开发 来源:qq_36355662 作者:qq_36355662 2022-06-14 16:13 次阅读

(本文为qq_36355662创作文章在此特别鸣谢!)

CAN通信

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

adb1c77c-e89e-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总线详解。

一般也都是使用标识符列表模式,这里使用的也是标识符列表模式。滤波过程举例如下:

add4a8fa-e89e-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文件

#ifndef __CAN_H
#define __CAN_H
#include "sys.h"
void CAN1_Init(void);
u8 CAN_SetMsg(u8 Data1,u8 Data2);
u8 CAN_GetMsg(u8 *msg1,u8 *msg2);
#endif /* __CAN_H */

主机相关代码

这里主机代码大部分是和从机类似的,就只贴出不同的地方了。
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 */

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

    关注

    3

    文章

    85

    浏览量

    17621
  • CAN
    CAN
    +关注

    关注

    56

    文章

    2459

    浏览量

    459025
  • STM32
    +关注

    关注

    2239

    文章

    10664

    浏览量

    348508
  • STM32F103
    +关注

    关注

    33

    文章

    474

    浏览量

    62545

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

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

收藏 人收藏

    评论

    相关推荐

    不同STM32芯片之间的CAN通信不正常

    现有STM32F103C8T6最小系统板两块(带can收发器),STM32F405RGT6最小系统板两块(带can
    发表于 12-03 08:49

    请问怎么实现串口升级stm32F103内的程序

    小弟想实现串口升级stm32F103内的程序,但是没什么头绪,想问下stm32能否串口1实现程序的升级?有没有什么
    发表于 12-28 09:09

    基于STM32F103 RET6芯片+PCA82C250使用CAN通讯换回模式测试CAN收发

    测试9 总结1 博客内容博客内容基于STM32F103 RET6芯片(Keil.STM32F1xx_DFP.2.3.0.pack)+PCA82C250,使用CAN通讯换回模式测试CAN
    发表于 08-06 07:03

    STM32F103VET6的CAN通信代码

    STM32F103VET6的CAN通信代码分享STM32F103VET6单片机之间的CAN
    发表于 08-19 08:39

    浅析stm32f103 CAN

    。mailbox 0~3 发送优先级:CAN协议帧ID或FIFO 决定邮箱优先级, CAN_MCR->TXFP选择优先级模式。CAN接收数据CAN总线 -> Filter -> Mai
    发表于 08-19 06:08

    基于STM32F103实现CAN数据收发的功能

    。基于rt-thread,已经有CAN的驱动框架,可以快速实现CAN数据的收发。这里基于STM32F103 实现CAN数据
    发表于 08-19 07:20

    STM32F103 CAN总线配置与通信学习记录

    STM32F103 CAN总线配置与通信小记总线概念在此只非常简要的介绍 CAN 是Controller Area Network 的缩写
    发表于 08-20 07:48

    STM32F103通信协议

    总体思路:主机:STM32F407从机:STM32F103通信协议:RS485主机-----串口2----RS485--------------------RS485—串口3-----
    发表于 08-24 06:25

    浅析STM32F103系列CAN控制器

    高速CAN有哪些主要应用?怎样去设计STM32F103 CAN控制器的硬件部分?怎样去设计STM32F103 CAN控制器的软件部分?
    发表于 10-27 07:50

    如何使用STM32F103CAN收发通信

    如何使用STM32F103CAN收发通信
    发表于 12-15 07:36

    STM32F103 CAN模板程序

    STM32F103 CAN模板程序
    发表于 11-09 11:08 47次下载
    <b class='flag-5'>STM32F103</b> <b class='flag-5'>CAN</b>模板<b class='flag-5'>程序</b>

    LMT70代码基于stm32f103

    LMT70测温代码基于stm32f103免费下载。
    发表于 05-06 11:16 20次下载

    STM32F103学习笔记三 串口通信

    STM32F103学习笔记三 串口通信
    发表于 11-25 09:06 71次下载
    <b class='flag-5'>STM32F103</b>学习笔记三   串口<b class='flag-5'>通信</b>

    STM32F103 硬件I2C主从机通信

    STM32F103 硬件I2C主从机通信
    发表于 12-07 12:06 53次下载
    <b class='flag-5'>STM32F103</b> 硬件I2C主从机<b class='flag-5'>通信</b>

    STM32F103点灯程序(寄存器)

    STM32F103点灯程序(寄存器)
    发表于 12-08 17:21 20次下载
    <b class='flag-5'>STM32F103</b>点灯<b class='flag-5'>程序</b>(寄存器)