CAN通讯协议详解
阅读引言部分: 本文旨在为需要了解CAN总线的相关读者提供一个简明扼要的介绍。 也可认为也可以说是从B站等学习相关笔记并加以整理分享。 文章具体介绍了CAN总线的基本概念及其应用领域。 并详细说明了如何利用STM32开发板实现CAN通信功能。 其中图片内容均源自Bilibili上爱上半导体博主发布的相关资源。
CAN高清教学视频
CAN高清教学视频
目录
1.CAN总线详细讲解
2.STM32的CAN总线通信的简单引入
1.CAN总线详细讲解
CAN通讯总线的简单介绍
Can总线主要应用于汽车领域,在技术上代表Control every network这一概念,在其中其作用是建立局域网来进行通信。

控制器局域网, 该通信总线应用最多的是汽车领域。

局域网络也是大家耳熟能详的一个概念,它指的是将多台设备通过路由器连接起来构成的一个小型网络环境,这样一来,这些设备之间就能实现通信了

那CAN 和这个也类似,在汽车领域中使用的官方术语称为Ecu, 而电子控制单元则是实现车辆功能的核心组件

意思是电子控制单元代表了一台小型计算设备。这台设备内部集成了一个供电系统。其中单片机驱动系统是汽车内部最小的控制模块。这样可以让Ecu之间进行通讯

ECU是汽车中的最小控制单元
为了使ECU之间能够实现通信的目的, 人们开发出了can通讯协议, 并因此减少了所需线束的数量

如果不用看总线的话,则ECU之间会采用直接通信方式,并且这将会导致较长的铜丝连接线路以及种类繁多的连接线路。然而通过CAN网络连接这么多Ecu只需要将它们连接到同一个主线上就能实现局域网内的通信,并且显著降低了线路长度。那么让我们深入探讨一下这个看总线是如何实现内部通讯工作的。

CAN的通讯实现

为了实现通信的目的, 必须具备专门的接收发送芯片。
其输入输出引脚上, 高电平输入应当对应低电平输出。
这种基本电路的基本逻辑很容易掌握, 从而为后续学习打下基础。

但是通过CAN收发器之后的普通信号会被转换为差分信号;差分线由两根线组成

如果我们将单片机配置为向CAN收发器发送低电压水平,则其两条线上的输出分别为3.5V与1.5V之间的显著差异(即两伏),这表明逻辑零状态;而在发送高电压水平时(即通常情况下),其两条线之间的电压差通常为零伏(表示逻辑一),这种信号传输采用差分方式以提高抗干扰能力

同样的情况下查看收发器的信息时,我们可以将接收的差分信号转换为普通的电池信号后发送到单片机上。采用这种差分信号有何优点?

那么这样做带来了哪些好处呢?若仅具备一条线路,在某一处面临外来干扰时,则会导致电源产生电压突变进而造成输出信号失真;因此无法实现远距离传输;而现代通信技术通常采用的是双线差分调制方式以确保信息传递的准确性

而CAN通讯则采用了两根线路协同工作的方式,并且采用了双线缠绕的技术。即使在通信过程中受到电磁干扰的影响,在这种情况下也会导致两条线路同时遭受干扰,并且它们之间的压差始终保持恒定。因此,在这种稳定的通信环境下能够确保信息传输不受外界干擾影響,并且从而实现远距离信号传输的能力达到1000米



CAN的数据帧
接下来我们具体阐述一下CAN通讯所传输的内容是什么样的。我们可以观察到的是一个标准的数据帧

第一位是起始位,它一定得是逻辑0

随后的这串数字为识别码。通过这串独特的编码方式,则能够明确指示该信息的目的接收设备。每个设备都拥有独一无二的这串数字标识符。


下一个二进制位用于标识数据包或远程请求报。当传输的数据类型为远程请求时(即该字段设置为1),则表示接收方需要执行响应操作;而对于一系列连续的数据报(即该字段设置为0),则表示接收方无需执行响应操作。

后续的6位为控制码,在其结构中首位置称为IDE位。该位置则分为两种情况:一种是标准格式下的IDE位设置方法与扩展格式下的设置方式有所差异。


在标准格式中存在一个由...构成的字段,在其对应的识别码中有...个数字;相比之下,在扩展版本中其对应的识别码同样包含...个数字,并且这些数字的最低有效位置为...
下面一位是预留位,它是逻辑零

接下来的4位是DLC 位及数据长度代码,它的二进制编码是零到八,


当数据为8时,则占用了1个字节(8 bits)。反之,则占用了64个字节(64 bits)。

随后包含着16位CRC码以及循环冗余校验位的技术也被应用到这里面来实现数据传输过程中的准确无误

然后是2位Ac k 码的第一位是确认操发送端传输的是逻辑1到接收端回复逻辑0以表示应答;第二位Ac k 码是界定位它一定是逻辑1的作用是将后面的数据区分开

最后是7位结束位,这7位都是逻辑一表示数据帧传输结束

这就是一串标准数据帧,如果用差分信号表示它的电瓶是这样的。

由于查看总线上接入了大量的设备,在这种情况下如果有两个设备同时发送信息,则需要通过11位识别码来确定哪个设备最先发送信息。这个识别码不仅具有设备的身份标识功能,并且还决定了它们在优先级上的先后顺序。例如,在当前场景下有两组数据是同时被发送出去的


应当如何判断呢?当总线上同时呈现逻辑零与逻辑一时,在这种情况下总线将会被设置为逻辑零状态。这样一来,在此之后上面的那个数据帧将不再进行发送操作了。这就是我对开通讯的理解吧!



2.STM32的CAN总线通信的简单引入
该协议(CAN:Controller Area Network)是一种先进的串行通信总线,在工业自动化、车辆电子设备等领域的关键协议中得到了广泛应用。在STM32系列数字控制器中普遍配备了专用的CAN控制器芯片以实现高效的网络通信功能。本文将深入探讨该协议的工作原理及实现方案,并提供基于STM32平台的具体开发方法与实例分析
CAN总线通信基本原理:
一种基于多种主机的分布式串行通信系统,CAN总线通过多节点实现高速数据传输并支持优先级控制。该系统采用两根不同的线路:分别是CANH与CANL,它们以差分信号的方式传递信息。该系统应用CSMA/CD协议(即基于载波的多路访问/冲突检测)作为冲突检测机制,从而能够有效避免冲突的发生。
通过STM32平台进行CAN总线通信的部署,首先需要完成对CAN硬件的配置与初始化工作;随后可以借助相应的API函数完成数据发送与接收操作。

以下是一个使用STM32的CAN总线实现数据发送和接收的示例代码:
#include "stm32xxxx.h"
CAN_HandleTypeDef hcan1;
void CAN_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
// 使能CAN时钟
__HAL_RCC_CAN1_CLK_ENABLE();
// 配置CAN引脚
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF_CAN1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置CAN控制器
hcan1.Instance = CAN1;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.AutoBusOff = ENABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = DISABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_3TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
HAL_CAN_Init(&hcan1);
}
void CAN_SendData(uint8_t* pData, uint32_t size)
{
CAN_TxHeaderTypeDef TxHeader;
TxHeader.StdId = 0x123;
TxHeader.ExtId = 0;
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.DLC = size;
TxHeader.TransmitGlobalTime = DISABLE;
uint32_t TxMailbox;
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, pData, &TxMailbox);
// 等待发送完成
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3) {}
}
void CAN_ReceiveData(void)
{
CAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[8];
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, RxData);
// 处理接收到的数据
}
int main(void)
{
HAL_Init();
CAN_Init();
while (1)
{
// 主循环代码
// 发送数据
uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
CAN_SendData(data, sizeof(data));
// 接收数据
CAN_ReceiveData();
}
}
cpp

在本代码中首先进行了硬件初始化(采用CAN_Init函数)。接着调用HAL_CAN_AddTxMessage函数发送数据以及调用HAL_CAN_GetRxMessage函数接收数据。在主循环期间我们可以添加其他逻辑并利用这两个库函数完成通信任务。
通过设置CAN控制器及引脚配置,并编写相关代码,在STM32开发板上较为容易地完成CAN总线通信功能。采用CAN总线技术可实现高速、高效的分布式数据传输,并广泛应用于工业自动化控制、车辆电子系统等场景。
