STM32实现CAN通讯

发布于:2025-06-27 ⋅ 阅读:(21) ⋅ 点赞:(0)

经过了一段时间的学习,终于把前几年不太明白的CAN通讯给搞明白了,其实还是比较简单的,就是踩了很多坑之后才会的。

先来看看接线吧!我用的MCU是STM32F103ZET6,内部含有1个CAN外设,CAN模块在网上买了一个现成的模块TJA1050,之前买了一个芯片TJA1040,做实验时总是不成功,没有找到原因,所以直接买个模块吧,也贵不了几块钱,全当做吃饭时多吃了一个鸡腿吧!

另外一头是链接了一个USBCAN模块

MCU使用的是:

这是学习这个CAN铜须所需的最小的代价了。

好了先来看看实验效果吧:

好,下面就是程序了:

can.c文件:

#include "can.h"

void Can_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	CAN_InitTypeDef CAN_InitStruct;
	CAN_InitStruct.CAN_ABOM = DISABLE;  //AutoBusOff(自动离线恢复)
	CAN_InitStruct.CAN_AWUM = DISABLE;  //AutoWakeUp(自动唤醒)
	CAN_InitStruct.CAN_BS1 = CAN_BS1_4tq;  // 36M/ 500K(波特率) = 72/pre(9)=8-1=7=bs1+bs2
	CAN_InitStruct.CAN_BS2 = CAN_BS2_3tq;  //36M/((bs1+bs2+1)*pre)
	CAN_InitStruct.CAN_Mode = CAN_Mode_Normal;  //Mode(CAN工作模式)
	CAN_InitStruct.CAN_NART = ENABLE;  //Non-AutoRetransmission 控制CAN报文的自动重传
	CAN_InitStruct.CAN_Prescaler = 9;
	CAN_InitStruct.CAN_RFLM = DISABLE;  //ReceiveFifoLocked(接收FIFO锁定)
	CAN_InitStruct.CAN_SJW = CAN_SJW_2tq;  //SyncJumpWidth(同步跳转宽度,SJW)
	CAN_InitStruct.CAN_TTCM = DISABLE;   //TimeTriggeredMode(时间触发模式)
	CAN_InitStruct.CAN_TXFP = DISABLE;   //Transmit FIFO Priority,发送FIFO优先级
	CAN_Init(CAN1,&CAN_InitStruct);
	
	CAN_FilterInitTypeDef CAN_FilterInitStruct;
	CAN_FilterInitStruct.CAN_FilterActivation = ENABLE;  //FilterActivation(过滤器使能)
	CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;  //FilterFIFOAssignment(过滤器关联的接收FIFO)
	CAN_FilterInitStruct.CAN_FilterIdHigh = 0x01;
	CAN_FilterInitStruct.CAN_FilterIdLow = 0;
	CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0;
	CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0;
	CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdList;
	CAN_FilterInitStruct.CAN_FilterNumber = 0;
	CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_16bit;
	CAN_FilterInit(&CAN_FilterInitStruct);
}
/*
	CAN发送数据的函数
	参数1:要发送数据的数组
	参数2:要发送数据的数组中元素的个数
	返回值: 0:超时,发送失败
			 1:发送成功
*/
u8 Can_Send_Data(u8*buf, u8 len)
{
	u8 i=0;
	u8 mbox;
	CanTxMsg TxMessage;
	TxMessage.StdId = 0x12;  //标准ID(11位)
	TxMessage.ExtId = 0;  //扩展ID(29位)
	TxMessage.IDE = 0;   // 标识符扩展(0:标准帧,1:扩展帧)
	TxMessage.RTR = 0;   // 远程传输请求(0:数据帧,1:远程帧)
	TxMessage.DLC = len;   // 数据长度(0-8字节)
	for(i=0; i<len; i++)
	{
		TxMessage.Data[i] = buf[i];
	}
	mbox = CAN_Transmit(CAN1,&TxMessage);
	i=0;
	while(CAN_TransmitStatus(CAN1,mbox) == CAN_TxStatus_Failed && (i<=0xFFF)) i++;
	if(i>=0xFFF) return 0;
	return 1;
}
/*
	Can接收数据的函数
	参数: 接收到数据后数据要存放的数组的地址
	返回值: 0:没有接收到数据
		   非0:接收到数据,存储到出入的数组地址中,返回接收的数据的字节数
*/
u8 Can_Receive_Data(u8 * buf)
{
	u8 i=0;
	CanRxMsg RxMessage;
	if(CAN_MessagePending(CAN1,CAN_FIFO0)==0) return 0;
	CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);
	for(i=0; i<RxMessage.DLC; i++)
	{
		buf[i] = RxMessage.Data[i];
	}
	return RxMessage.DLC;
}

can.h文件:

#ifndef __CAN__H
#define __CAN__H

#include "system.h"

void Can_Init(void);

u8 Can_Send_Data(u8*buf, u8 len);

u8 Can_Receive_Data(u8 * buf);

#endif

最后就是主函数了main.c:

#include "led.h"
#include "Serail1.h"
#include "can.h"

u8 a[8]={11,12,13,14,15,16,17,18};
u8 b[8];
u8 i=0;

int main(void)
{
	Serail1_Init();
	Serail2_Init();
	led_Init();
	Can_Init();
	
	printf("开始发送数据\r\n");
	Can_Send_Data(a,8);
	while(1)
	{
		if(Can_Receive_Data(b))
		{
			printf("接收到数据:");
			for(i=0;i<8;i++)
			{
				printf("%d ",b[i]);
			}
			printf("\r\n");
		}
	}
}

就上面这些主要的东东,编译后下载就能够实现单片机和电脑之间通过CAN来交互数据了。

下面我们来看下 CAN 协议具有哪些特点:

①多主控制。 在总线空闲时, 所有单元都可以发送消息(多主控制) ,而两个以上的单元同时开始发送消息时, 根据标识符( Identifier 以下称为ID) 决定优先级。 ID 并不是表示发送的目的地址, 而是表示访问总线的消息的优先级。 两个以上的单元同时开始发送消息时, 对各消息 ID 的每个位进行逐个仲裁比较。 仲裁获胜(被判定为优先级最高) 的单元可继续发送消息, 仲裁失利的单元则立刻停止发送而进行接收工作。

②系统的柔软性。 与总线相连的单元没有类似于“地址” 的信息。 因此在总线上增加单元时, 连接在总线上的其它单元的软硬件及应用层都不需要改变。

③通信速度较快, 通信距离远。 最高 1Mbps(距离小于 40M), 最远可达 10KM(速率低于 5Kbps) 。

④具有错误检测、 错误通知和错误恢复功能。 所有单元都可以检测错误(错误检测功能), 检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单元一旦检测出错误, 会强制结束当前的发送。 强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能) 。

⑤故障封闭功能。 CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等) 还是持续的数据错误(如单元内部故障、 驱动器故障、 断线等) 。由此功能, 当总线上发生持续数据错误时, 可将引起此故障的单元从总线上隔离出去。

⑥连接节点多。 CAN 总线是可同时连接多个单元的总线。 可连接的单元总数理论上是没有限制的。 但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。 降低通信速度, 可连接的单元数增加; 提高通信速度, 则可连接的单元数减少。


网站公告

今日签到

点亮在社区的每一天
去签到