CAN通信代码实现

该文详细介绍了如何使用STM32F103C8T6单片机进行CAN通信的实现,包括硬件连接、CAN通信的物理层和协议层基础知识,以及具体的STM32F103C8T6的CAN控制器设置和中断处理函数。通过示例代码展示了发送和接收数据的过程。
摘要由CSDN通过智能技术生成

基于STM32F103单片机的CAN通信代码实现

本文章对CAN的理论只做简单介绍,重点介绍怎么使用stm32f103单片机实现CAN通信。
文章实验使用stm32f103c8t6单片机,TJA1042T/3 CAN收发器,广成CAN分析仪,GCANTOOLS上位机软件(CAN分析仪官网下载)实现CAN报文的发送和接受。

CAN通信物理层简单介绍

更为详细的CAN基础资料可以参考瑞萨电子的《CAN入门书》。
CAN通信就是实现多设备之间的数据交换通信方式,没有主从结构,也没有固定的设备地址,通过标识符来确定信息是不是给自己的。使用
特点如下:
1、没有主从之分,所有单元都可以发送接收报文
2、每个设备没有固定的地址,通信双方(或多方)使用标识符来判断总线上的数据是不是给自己的
3、可以连接很多单元,理论上是无限个,但受总线的物理时间延迟等影响不能连接太多。
4、使用双绞线通信,抗干扰能力强,两根线一起干扰就约等于没有干扰,因为一个时刻的电平标准取决于两根线之间的压差,同时增加或减少电压时两线压差变化不大。
5、等等其他特点,这里不做赘述。

CAN协议层简单介绍

数据以帧来分类,包括数据帧,遥控帧等,遥控帧就是告诉别人我要接收标识符ID为XXX设备的数据,数据帧就是一个设备告诉别的设备我的这串数据是这些,标识符ID是XXX,大家看看我的ID,想要的就接收。错误帧、过载帧、帧间隔这里就不再赘述。
一个帧包含很多段,通过这些段来分辨这个是什么帧,什么标识符ID,什么数据,数据有多长,标识符是标准的(11位)还是扩展的(共29位)等等信息。拿数据帧的标准格式和扩展格式举例子:
数据帧组成标准格式中,起始就是这个帧从这里开始,仲裁段就是标识符ID段,同一时刻多个设备发送数据的话就根据这个标识符ID选出哪个先发送,从左到右看谁的0最多谁先发(就是所有ID一起与门运算),RTR位就是这个帧是数据帧还是远程帧,IDE位就是这个帧的ID是扩展ID还是标准ID,r0只作填充用,DLC段表示后面的Data段有多少个字节(最多8个字节),CRC校验段就是校验段,界定符就是界定CRC校验数据结束,ACK就是应答之前接受的数据没问题,结束段就是表示这个帧到这里结束。
扩展格式中,标识符后面的是SRR位,作填充用,无实际意义,就是为了和标准格式的位置对齐,IDE位为显形电平,表示这个帧的标识符是扩展标识符,扩展标识符右边的RTR右边位R1,也是作填充作用,为了对齐用。

stm32f103c8t6 CAN控制器介绍

以下详细信息参考stm32中文参考手册V10。
不同的单片机具备的CAN控制器功能不同,stm32中文参考手册V10的介绍比较全面,注意区分。
bxCAN特点:
1、3个发送邮箱
2、3级深度的2个接收FIFO
3、可变的过滤器组
4、初始化、正常和睡眠模式
5、调试有静默模式、环回模式、环回静默模式
6、标识符过滤

硬件连接

在这里插入图片描述

程序代码实现

CAN.h文件就是几个函数声明,不作展示。

CAN.c文件

#include "stm32f10x.h"  
#include "CAN.h"  

#define CAN_Rx_GPIO			GPIOA
#define CAN_Rx_Pin			GPIO_Pin_11
#define CAN_Tx_GPIO			GPIOA
#define CAN_Tx_Pin			GPIO_Pin_12



//CAN模式初始化函数
void CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	CAN_InitTypeDef CAN_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	CAN_FilterInitTypeDef CAN_FilterInitStructure;

	//GPIO复用功能时钟开启,CAN1外设时钟开启
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA,ENABLE);	 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE );

	GPIO_InitStructure.GPIO_Pin = CAN_Tx_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
	GPIO_Init(CAN_Tx_GPIO, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = CAN_Rx_Pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
	GPIO_Init(CAN_Rx_GPIO, &GPIO_InitStructure); //初始化 IO
	
	CAN_DeInit(CAN1);
	CAN_InitStructure.CAN_ABOM = ENABLE;   		//禁用自动总线关闭模式			
	CAN_InitStructure.CAN_AWUM = DISABLE;   	//禁用自动唤醒模式				
	CAN_InitStructure.CAN_Mode = mode;      	//模式选择						
	CAN_InitStructure.CAN_NART = DISABLE;    	//非自动重传模式				
	CAN_InitStructure.CAN_RFLM = DISABLE;   	//禁用接收FIFO锁定模式			
	CAN_InitStructure.CAN_SJW = tsjw;      	 	//同步跳转宽度					
	CAN_InitStructure.CAN_BS1 = tbs1;       	//位时间段1的长度				
	CAN_InitStructure.CAN_BS2 = tbs2;       	//位时间段2的长度			
	CAN_InitStructure.CAN_Prescaler = brp;  	//分频系数						
	CAN_InitStructure.CAN_TTCM = DISABLE;   	//关闭时间触发通信模式			
	CAN_InitStructure.CAN_TXFP = DISABLE;   	//关闭发送FIFO优先级模式		
	CAN_Init(CAN1, &CAN_InitStructure); 
	CAN_FilterInitStructure.CAN_FilterNumber = 0;    //配置CAN过滤器组中的第一个过滤器
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;    //使用标识符屏蔽位来过滤CAN消息
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;   //使用32位的模式和掩码来过滤CAN消息
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;		//下面四个全是0表示接收所有标识符消息
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; 
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;  //过滤器匹配结果应该被存储在FIFO0缓冲区中
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;   //启用CAN过滤器
	CAN_FilterInit(&CAN_FilterInitStructure); 
	CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//开启中断
	
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  	NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;//中断通道选择
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  	NVIC_Init(&NVIC_InitStructure);
}

//复制接受CAN结构体数据到发送结构体数据中,并发送数据
void SendMessage(void)
{
	u8 i ;
	CanTxMsg bufCAN;
	bufCAN.ExtId 	= RxMessage.ExtId;
	bufCAN.StdId   	= RxMessage.StdId;
	bufCAN.IDE    	= RxMessage.IDE;
	bufCAN.RTR   	= RxMessage.RTR;
	bufCAN.DLC   	= RxMessage.DLC;
	for(i = 0;i < 8;i++)
	{
		bufCAN.Data[i] = RxMessage.Data[i];
	}
	CAN_Transmit(CAN1,&bufCAN);
}



//CAN接受中断处理函数
void USB_LP_CAN1_RX0_IRQHandler(void)   
{
	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);				//接收数据到RxMessage中
	SendMessage();													发送接收到的数据
	CAN_FIFORelease(CAN1,CAN_FIFO0);						//释放接收邮箱FIFO0
	CAN_ClearITPendingBit(CAN1,CAN_IT_FF0);			//清除FIFO0接收中断标志位
} 

main.c文件

#include "stm32f10x.h"   
#include "CAN.h"


//演示代码,实现上电发送报文

int main(void)
{
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_2tq,CAN_BS1_3tq,24,CAN_Mode_Normal);
	/*
	波特率=(72M/2)/((3+2+1)*12)=500K
							*60=100K	
							*24 = 250K
	*/
	while(1)
	{
	}

}

实现效果

标准帧的发送和接受
在这里插入图片描述

扩展帧的发送和接受

在这里插入图片描述

通过上述代码,实现基本的CAN数据传输,本文完。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值