CAN通信入门

发布于:2025-09-07 ⋅ 阅读:(17) ⋅ 点赞:(0)

1CAN总线简介

CAN总线(Controller Area Network Bus)控制器局域网总线

CAN总线是由BOSCH公司开发的一种简洁易用、传输速度快、易扩展、可靠性高的串行通信总线,广泛应用于汽车、嵌入式、工业控制等领域

CAN总线特征:

两根通信线(CAN_H、CAN_L),线路少

差分信号通信,抗干扰能力强

高速CAN(ISO11898):125k~1Mbps, <40m

低速CAN(ISO11519):10k~125kbps, <1km

异步,无需时钟线,通信速率由设备各自约定

半双工,可挂载多设备,多设备同时发送数据时通过仲裁判断先后顺序

11位/29位报文ID,用于区分消息功能,同时决定优先级

可配置1~8字节的有效载荷

可实现广播式和请求式两种传输方式 应答、CRC校验、位填充、位同步、错误处理等特性

1.1常见的通信协议对比

1.2CAN硬件电路

1.3CAN电平标准

1.4CAN总线帧格式

1数据帧

 数据帧各个部分用途简介

2遥控帧

3错误帧

4过载帧

5帧间隔

1.5位填充

波形示例

接收方数据采样

1.6错误类型

2STM32CAN外设的简介

2.1CAN收发器电路

2.2CAN框图

2.3CAN的基本结构

2.4发送流程

RQCP:请求完成

TXOK:发送成功

TME:发送邮

TXRQ:请求发送控制位

NART: 自动重传控制位

1接收过程

2发送和接收的配置位

3标识符过滤器

屏蔽模式就是只看哪些位就可以通过,比如大量的温度数据进入

屏蔽寄存器里面写1就是必须匹配,写0可以不匹配

过滤器配置示例(映像右边有几位就左移几位)

2.5测试模式

2.6工作模式

STM32的位时间特性(与协议有些不同)

收数据之间要看队列里面是否有报文     

CAN.c

void My_CANInit(void)
{
	//初始化时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);
	
	//GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//CAN初始化
	CAN_InitTypeDef CAN_InitStructure;
	CAN_InitStructure.CAN_ABOM = DISABLE;
	CAN_InitStructure.CAN_AWUM = DISABLE;
	CAN_InitStructure.CAN_NART = DISABLE;
	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
	CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;
	CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
	CAN_InitStructure.CAN_Prescaler = 48;
	CAN_InitStructure.CAN_RFLM = DISABLE;
	CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;
	CAN_InitStructure.CAN_TTCM = DISABLE;
	CAN_InitStructure.CAN_TXFP = DISABLE;
	CAN_Init(CAN1,&CAN_InitStructure);
	//过滤器初始化
	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
	CAN_FilterInitStructure.CAN_FilterNumber = 0;
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
	CAN_FilterInit(&CAN_FilterInitStructure);
}
//发送函数
void MyCAN_Transmit(uint32_t ID,uint8_t Length,uint8_t* Data)
{
	CanTxMsg Txmessage;
	for(uint8_t i = 0;i<Length;i++)
	{
		Txmessage.Data[i] = Data[i];
	}
	Txmessage.DLC = Length;//数据个数
	Txmessage.ExtId = ID;
	Txmessage.IDE = CAN_Id_Standard;
	Txmessage.RTR = CAN_RTR_Data;
	Txmessage.StdId = ID;
	uint8_t TransmitMailbox = CAN_Transmit(CAN1,&Txmessage);
	//等待发送完成
	uint32_t Timeout = 0;
	while(CAN_TransmitStatus(CAN1,TransmitMailbox)!=CAN_TxStatus_Ok)
	{
		Timeout++;
		if(Timeout>100000)
		{
			break;
		}
	}
}
//是否接收到数据判断函数
uint8_t MyCAN_ReceiveFlah(void)
{
	if(CAN_MessagePending(CAN1,CAN_FIFO0)>0)//看队列是否有数据,有的话,main.c里面取
	{
		return 1;
	}
	return 0;
	
}
//接收函数
void MyCAN_Receive(uint32_t * ID,uint8_t* Length,uint8_t* Data)
{
	CanRxMsg Rxmessage;
	CAN_Receive(CAN1,CAN_FIFO0,&Rxmessage);
	
	if(Rxmessage.IDE==CAN_Id_Standard)
	{
		*ID=Rxmessage.StdId;
	}
	else
	{
		*ID=Rxmessage.ExtId;
	}
	if(Rxmessage.RTR==CAN_RTR_Data)
	{
		*Length = Rxmessage.DLC;
		for(uint8_t i = 0;i<*Length;i++)
		{
			Data[i] = Rxmessage.Data[i];
		}
		
	}
	else
	{
		//
	}
}

本文章只是介绍基础的CAN总线通信,还有各种功能,等到实际需求的时候再去实现


网站公告

今日签到

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