目录

CAN 总线

CAN是 Controller Area Network 的简称, 德国BOSCH公司开发, 并最终成为国际标准(ISO 11898), 是国际上应用最广泛的现场总线之一, CAN 已经成为汽车计算机控制系统和嵌入式工业控制局域网事实上的标准.

CAN 总线的物理连接

相对于近距离传输的I2C, SPI协议, 以及RS485总线, CAN 总线定义了更为优秀的物理层和链路层, 以及种类丰富的上层协议. 与I2C、SPI有时钟信号的同步通讯方式不同, CAN通讯并不是以时钟信号来进行同步的, 它是一种异步通讯, 只有 CAN_High 和 CAN_Low 两条信号线, 共同构成一组差分信号线, 以差分信号的形式进行通讯.

CAN 物理层主要分为闭环总线及开环总线网络两种形式, 一个适合于高速通讯, 一个适合于远距离通讯.

  • CAN闭环通讯网络是一种遵循 ISO11898 标准的高速, 短距离网络, 总线最大长度为40m, 通信速度最高为1Mbps, 总线的两端各要求有一个"120欧"的电阻, 来做阻抗匹配, 以减少回波反射.
  • CAN开环总线网络是遵循 ISO11519-2 标准的低速, 远距离网络, 最大传输距离为1km, 最高通讯速率为125kbps, 两根总线是独立的, 不形成闭环, 要求每根总线上各串联有一个 2.2KR 的电阻.

关于共地

CAN 在多个收发器之间的连接, 可以不共地, 只需要 CANH 和 CANL 两线连接.

CAN 总线的通信机制

CAN总线上可以挂载多个通讯节点, 节点之间的信号经过总线传输, 实现节点间通讯. CAN通讯协议不对节点进行地址编码, 而是对数据内容进行编码, 所以网络中的节点个数理论上不受限制, 只要总线的负载足够即可, 可以通过中继器增强负载.

CAN通讯节点由一个CAN控制器及CAN收发器组成, 控制器与收发器之间通过 CAN_Tx及 CAN_Rx 信号线相连, 收发器与CAN总线之间使用 CAN_High 及 CAN_Low 信号线相连. 其中 CAN_Tx 及 CAN_Rx 使用普通的类似TTL逻辑信号, 而 CAN_High 及 CAN_Low 是一对差分信号线, 当CAN节点需要发送数据时, 控制器把要发送的二进制编码通过 CAN_Tx 线发送到收发器, 然后由收发器把这个普通的逻辑电平信号转化成差分信号, 通过差分线 CAN_High 和 CAN_Low 输出到CAN总线网络. 收发器接收总线上的数据时则是相反的过程, 收发器把总线上收到的 CAN_High 及 CAN_Low 信号转化成普通的逻辑电平信号, 再通过 CAN_Rx 输出到控制器中.

差分信号

差分信号又称差模信号, 与传统使用单根信号线电压表示逻辑的方式有区别, 使用差分信号传输时, 需要两根信号线, 这两个信号线的振幅相等, 相位相反, 通过两根信号线的电压差值来表示逻辑0和逻辑1. 相对于单信号线传输的方式, 使用差分信号传输具有如下优点

  • 抗干扰能力强, 当外界存在噪声干扰时, 几乎会同时耦合到两条信号线上, 而差分信号只关心两个信号的差值, 所以外界的共模噪声可以被抑制
  • 能有效抑制它对外部的电磁干扰, 同样的道理, 由于两根信号的极性相反, 对外辐射的电磁场可以相互抵消, 耦合的越紧密, 泄放到外界的电磁能量越少
  • 时序定位精确, 由于差分信号的开关变化是位于两个信号的交点, 而不像普通单端信号依靠高低两个阈值电压判断, 因而受工艺, 温度的影响小, 能降低时序上的误差, 同时也更适合于低幅度信号的电路
  • 由于差分信号线具有这些优点, 所以在USB协议, 485协议, 以太网协议及CAN协议的物理层中, 都使用了差分信号传输.

CAN协议中的差分信号

CAN协议中对它使用的 CAN_High 及 CAN_Low 表示的差分信号做了规定. 以高速CAN协议为例, 当表示逻辑1时(隐性电平), CAN_High 和 CAN_Low 线上的电压均为2.5V, 即它们的电压差为 0, 而表示逻辑0时(显性电平), CAN_High的电平为 3.5V, CAN_Low线的电平为1.5V, 电压差为 2V.

CAN 总线的特点

CAN 总线网络是一种多主网络, 在总线处于空闲状态时, 任何一个节点单元都可以申请成为主机, 向总线发送消息. 其原则是: 最先访问总线的节点单元可以获得总线的控制权, 多个节点单元同时尝试获取总线的控制权时, 将发生仲裁事件, 具有高优先级的节点单元将获得总线控制权.

CAN 协议中, 所有的消息都以固定的数据格式打包发送. 两个以上的节点单元同时发送信息时, 根据节点标识符(常称为 ID, 打包在固定的数据格式中)决定各自优先级关系, CAN 总线没有其他总线的地址概念, 在总线上增加节点单元时, 连接在总线的其他节点单元的软硬件都不需要改变.

CAN 总线的通信速率和总线长度有关, 在总线长度小于 40m 的场合中, 数据传输速率可以达到 1Mbps, 而即便总线长度上升至 1000m, 数据的传输速率仍可达到 50Kbps, 无论在速率还是传输距离都明显优于常见的 RS232, RS485 和 I2C 总线.

对于总线错误, CAN 总线有错误检测功能, 错误通知功能, 错误恢复功能三种应对措施, 分别应对于下面三点表述:

  1. 所有的单元节点都可以自动检测总线上的错误
  2. 检测出错误的节点单元会立刻将错误通知给其他节点单元
  3. 若正在发送消息的单元检测到当前总线发生错误, 则立刻强制取消当前发送, 并不断反复发送此消息至成功为止.

CAN 总线上的每个节点都可以通过判断得出, 当前总线上的错误是暂时错误(如瞬间的强干扰)还是持续错误(如总线断裂). 当总线上发生持续错误时, 引起故障的节点单元会自动脱离总线.

CAN 总线上的节点数量在理论上没有上限, 但在实际上收到总线上的时间延时及电气负载的限制. 降低最大通信速率可以增加节点单元的连接数, 反之减少节点单元的连接数则最大通信速率可以提高.

CAN 总线的数据通信是以数据帧的格式进行的

CAN 数据位传输时间

为了实现位同步, CAN协议把每一个数据位(bit)的时序分解成SS段, PTS段, PBS1段, PBS2段, 这四段的长度加起来即为一个CAN数据位的长度. 分解后最小的时间单位是Tq, 而一个完整的位由8~25个Tq组成.

CAN 的数据帧

了解CAN的数据帧, 对了解CAN的过滤机制有帮助

  • Identifier is the ID of the transmitting Device
  • RTR (Remote Transmission Request) Specifies if the data is Remote frame or Data frame
  • IDE specifies if we are using Standard ID or Extended ID
  • r is the Reserved bit
  • DLC specifies the data length in Bytes
  • Data Field is where we can send the data, which should be upto 8 bytes
  • Checksum and DEL are the CRC data and it’s Delimiter
  • ACK and DEL is the acknowledgment bit and it’s Delimiter

CAN 数据帧的代码实现

整体帧结构

CAN_TxHeaderTypeDef   TxHeader;
uint8_t TxData[8];
uint32_t TxMailbox;
  • TxHeader 用于存储头信息, 包含了 RTR, DLC 等, 在SPL中对应的类型为 CAN_TxHeaderTypeDef
  • TxData 用于存储传输的数据
  • TxMailbox 用于发送此消息的 mailbox

帧头的结构和帧数据

TxHeader.StdId = 0x446;
TxHeader.RTR = CAN_RTR_DATA; // Remote or data frame
TxHeader.IDE = CAN_ID_STD; // Standard or extended
// reserved bit
TxHeader.DLC = 2; // Data length in bytes
  • CAN_ID_STD 表示使用了标准ID模式(非扩展ID)
  • 0x446 就是发送的ID, 位宽最大为 11-bit
  • CAN_RTR_DATA 表示这个帧为数据帧
  • DLC 标识后面数据的字节长度, 因为发送两个字节, 所以这里是2
  • 随后在 TxData 中存储两个字节
TxData[0] = 50;
TxData[1] = 0xAA;

AIR32F103/STM32 的 CAN 外设 bxCAN

以下的描述适用于AIR32F103和STM32.

bxCAN 控制器 (Basic Extended CAN) 支持CAN协议2.0A和2.0B标准. 该CAN控制器支持最高的通讯速率为1Mbps, 可以自动地接收和发送CAN报文, 支持使用标准ID和扩展ID的报文. 外设中具有3个发送邮箱, 发送报文的优先级可以使用软件控制, 还可以记录发送的时间;具有2个3级深度的接收FIFO, 可使用过滤功能只接收或不接收某些ID号的报文; 可配置成自动重发; 不支持使用DMA进行数据收发.

CAN波特率的计算

通过配置位时序寄存器CAN_BTR的TS1[3:0]及TS2[2:0]寄存器位设定BS1及BS2段的长度后, 可以确定每个CAN数据位的时间

BS1段时间

Tbs1 =Tq x (TS1[3:0] + 1)

BS2段时间

Tbs2 = Tq x (TS2[2:0] + 1)

整个数据位的时间

Tbit = 1Tq + Tbs1 +Tbs2 = 1 + (TS1[3:0] + 1)+ (TS2[2:0] + 1)

Tq 是 CAN 通信的最小时间单元, 与 CAN 时钟总线及分频器配置有关, CAN1和CAN2外设都是挂载在APB1总线上的, 而位时序寄存器 CAN_BTR 中的 BRP[9:0] 寄存器位可以设置CAN外设时钟的分频值 , 所以

Tq = brp * Tpclk = (BRP[9:0]+1) * Tpclk

其中的PCLK指APB1时钟, 默认值为36MHz. 可以计算出 CAN 的波特率:

BaudRate = 1 / Tbit = Fpclk / ((Tbs1 + Tbs2 + 1) * brp)

CAN 波特率的设置

CAN_InitStructure.CAN_TTCM = DISABLE; // time triggered communication mode off
CAN_InitStructure.CAN_ABOM = DISABLE; // automatic bus-off management off
CAN_InitStructure.CAN_AWUM = DISABLE; // automatic wake-up mode off, wakeup by software cleaar CAN->MCR SLEEP bit
CAN_InitStructure.CAN_NART = ENABLE; // no-automatic retransmission mode on
CAN_InitStructure.CAN_RFLM = DISABLE; // rx FIFO Locked mode off
CAN_InitStructure.CAN_TXFP = DISABLE; // transmit FIFO priority off
CAN_InitStructure.CAN_Mode = mode;
// Set baud rate
CAN_InitStructure.CAN_SJW = tsjw; // synchronisation_jump_width, CAN_SJW_1tq ~ CAN_SJW_4tq
CAN_InitStructure.CAN_BS1 = tbs1; // number of time quanta in Bit Segment 1, CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_BS2 = tbs2; // number of time quanta in Bit Segment 2, CAN_BS1_1tq ~ CAN_BS1_16tq
CAN_InitStructure.CAN_Prescaler = brp; // clock prescaler, 1~1024
CAN_Init(CAN1, &CAN_InitStructure);

CAN 的ID过滤机制

CAN 是一种典型的广播式网络, 在实际应用中, 如果只希望接收到特定类型的数据, 就要借助过滤器来实现. AIR32/STM32的CAN控制器包含14个过滤器, 可以设置为 屏蔽模式列表模式 对CAN总线上的报文进行过滤. 当节点希望接收到一种报文时, 可以用屏蔽位模式进行过滤, 当节点希望接受到单一类型报文时, 应该配置为列表模式.

CAN控制器的每个过滤器都具备一个寄存器, 称为屏蔽寄存器。其中标识符寄存器的每一位都有屏蔽寄存器的每一位所对应.

AIR32/STM32 使用 CAN 外设内建的过滤器, 初始化代码为

  CAN_FilterTypeDef canfilterconfig;

  canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
canfilterconfig.FilterBank = 18; // 指定使用哪个过滤器
canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
canfilterconfig.FilterIdHigh = 0x103<<5;
canfilterconfig.FilterIdLow = 0;
canfilterconfig.FilterMaskIdHigh = 0x103<<5;
canfilterconfig.FilterMaskIdLow = 0x0000;
canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
canfilterconfig.SlaveStartFilterBank = 20; // how many filters to assign to the CAN1 (master can) HAL_CAN_ConfigFilter(&hcan1, &canfilterconfig);

FilterMode 用于设置过滤模式, 在STM32中有两种过滤模式, 这里使用的是掩码模式

  • MASK MODE, 掩码模式, 使用寄存器中设置的掩码对接收到的ID中特定的位进行比较.
  • LIST MODE, 列表模式, 对于接收到的ID, 直接使用寄存器中的ID进行比较.

FilterScale 用于指定是 1)一个32bit的过滤寄存器, 还是 2)两个16bit的过滤寄存器. 这里使用的是一个 32 Bit 寄存器.

FilterIdHigh 用于设置 ID 寄存器的高16 Bits, 这里的值会被用于与输入的ID进行比较. 这里只比较接收到的消息的标准ID, 因此将值左移5位, STD ID 从 ID HIGH Register 的第5位开始.

FilterMaskIdHigh 是掩码寄存器的高16 Bits, 在对接收到的消息的ID进行比较时, 会忽略这个寄存器中bit=0的位, 仅对会对bit=1对应的位, 与ID寄存器中对应的位进行比较.

掩码模式过滤的图例说明

上面就是 FilterIdHigh 寄存器和 FilterMaskIdHigh 寄存器, 因为不使用扩展ID, 所以低5位可以忽略

  1. 设置ID和Mask时, 都要将需要设置的ID值(0x103)左移5位, 因为低5位用于extId
  2. 与接收的ID对比时, 会提取Mask中激活的位, 其它位都会忽略. 这个例子中被激活的位是0, 1 和 8
  3. 根据激活位提取的bit, 会与ID中对应bit的值进行比较, 当这几个bit的值都一致时, ID就是匹配的

根据上面的设置

  • 如果输入的是0x102, 根据Mask设置, 第0,1,8位会用于比较, 而ID的第0位为1, 所以这个输入会被忽略
  • 如果输入的是0x107, 根据Mask设置, 第0,1,8位会用于比较, ID设置的这三位都是1和输入的一致, 所以这个输入会被接收

实例测试

硬件准备

  • TJA1050 或 MCP2551 的 CAN 收发模块 x 2
  • 带 AIR32F103 的开发板

因为 TJA1050 和 MCP2551 都是5V供电, 因此开发板上要有5V输出, 否则需要单独供电

代码

代码仓库目录 https://github.com/IOsetting/air32f103-template/tree/master/Examples/NonFreeRTOS/CAN

这个目录下包含两个模式的例子, 一个是 Loopback, 一个是 Normal, 从合宙官方仓库的例子参考(抄)的.

Loopback 模式

Loopback 是测试模式, 发送的数据不进入总线直接进入接收队列, 用于检查 CAN 收发器是否正常工作. 运行后在串口输入's', 会发送8个字节并将接收到的数据通过串口回显.

Normal 模式

正常的通信模式, 需要两套 MCU + CAN 收发器. CAN 收发器之间通过 CANH 和 CANL 连接. 代码中设置过滤器时, 使用的是相同的 ID + Mask 值, 对两个MCU编译烧录时需要将 ID_TARGET 和 ID_RECEIV 换一下. 运行后, 在一侧串口输入's', 在另一侧会通过串口显示接收到的数据.

参考

AIR32F103(九) CAN总线的通信和ID过滤机制及实例的更多相关文章

  1. 重学STM32---(九)之CAN通信(一)

    目录 1.CAN 是什么 2.CAN 特点 3.错误状态的种类 4.总线拓扑 5.CAN 协议 1.CAN 是什么   CAN 是 Controller Area Network的缩写(以下称为 CA ...

  2. Linux中总线设备驱动模型及平台设备驱动实例

    本文将简要地介绍Linux总线设备驱动模型及其实现方式,并不会过多地涉及其在内核中的具体实现,最后,本文将会以平台总线为例介绍设备和驱动程序的实现过程. 目录: 一.总线设备驱动模型总体介绍及其实现方 ...

  3. 工厂模式,根据ID创建对应的实例类

    工厂模式,根据ID创建对应的实例类 // // main.cpp // TestCPP1 // // Created by bianchx on 15/4/27. // Copyright (c) 2 ...

  4. EL语法 ${person.id} 这里面的id指的是实例对象的成员变量

    EL语法 ${person.id} 这里面的id指的是实例对象的成员变量

  5. CAN通信帧ID如何设定?

    CAN总线ID是包含在报文帧中的. 1.主要用作CAN总线的仲裁使用,所以一般来说网络上的每个节点(向总线上发送)的ID应该有所不同.ID值越低,报文优先级越高,在两组不同ID报文同时上线时候,仲裁机 ...

  6. CAN通信帧ID的含义解析? (转载)

    https://www.cnblogs.com/isAndyWu/p/10298695.html这个文章解答了我的一个id使用的疑惑,因此谢谢作者,转载. CAN总线ID是包含在报文帧中的. 1.主要 ...

  7. Docker 与 K8S学习笔记(九)—— 容器间通信

    容器之间可通过IP.Docker DNS Server或joined三种方式进行通信,今天我们来详细学习一下. 一.IP通信 IP通信很简单,前一篇中已经有所涉及了,只要容器使用相同网络,那么就可以使 ...

  8. Bluetooth篇 开发实例之九 和蓝牙模块通信

    首先,我们要去连接蓝牙模块,那么,我们只要写客户端的程序就好了,蓝牙模块就相当于服务端. 连接就需要UUID. #蓝牙串口服务SerialPortServiceClass_UUID = ‘{00001 ...

  9. .Net中Remoting通信机制简单实例

    .Net中Remoting通信机制 前言: 本程序例子实现一个简单的Remoting通信案例 本程序采用语言:c# 编译工具:vs2013工程文件 编译环境:.net 4.0 程序模块: Test测试 ...

  10. ID过滤靓号写法(PHP和Nodejs版本)

    1 前言 例如某APP的用户ID,需要按照一定规则把靓号先存取来,然后慢慢按要求释放靓号 2 代码 PHP版本如下: function genUserId(){ $id = ""; ...

随机推荐

  1. 【Impala】概念、原理、内外部shell、建库建表、分区、查询、自定义函数、存储压缩

    一.基本概念 1.介绍 对HDFS.Hbase数据的高性能.低延迟的交互式SQL查询功能 2.优缺点 优点:基于内存运算,无需写入磁盘,无需转化为MR,支持Data Locality调度(数据和计算在 ...

  2. 解决 ERROR: Could not find a version that satisfies the requirement xxx 的问题

    解决 ERROR: Could not find a version that satisfies the requirement xxx 的问题 1.解决 ERROR: Could not find ...

  3. 使用Google OR-Tools分析过去20年中国金融资产最佳配置组合

    前两天,在朋友圈里看到一张截至2022年Q2的金融资产历年收益图如下,图中列举了国内从2005年到2022年近20年主要的金融资产历年收益率,随产生想法分析和验证下面几个问题: 过去20年,基于怎样的 ...

  4. 盘点现在用的SqlServer 5种分页方式和拉姆达表达式分页,进来看看吧。

    现在基本上大家都在使用各种轮子自带的分页,大家是否还记得sql分页怎么写? 今天我们就来盘一盘怎么写和用哪种方式写. 欢迎大家评论区讨论. 1.ROW_NUMBER() OVER()方式(SQL201 ...

  5. 总结开源项目中的常见坏实践(Bad Practice)

    一些开源项目包含了各种编程的最佳实践供人参考学习和借鉴.但是也有一些开源项目虽然初衷是好的.但是包含了一些代码的坏实践.特别是对于一部分刚入行的大学生来说,可能会给到一些错误的示范.于是在此列举一些项 ...

  6. 【转载】MSSQL汉字首字母查询处理自定义函数

    -- 汉字首字母查询处理用户定义函数 CREATE FUNCTION f_GetPY(@str nvarchar(4000)) RETURNS nvarchar(4000) AS BEGIN DECL ...

  7. VS2019注册码

    Visual Studio 2019 Enterprise BF8Y8-GN2QH-T84XB-QVY3B-RC4DF Visual Studio 2019 Professional NYWVH-HT ...

  8. 一文读懂Go Http Server原理

    hello大家好呀,我是小楼,这是系列文<Go底层原理剖析>的第二篇,依旧是分析 Http 模块,话不多说,开始. 从一个 Demo 入手 俗话说万事开头难,但用 Go 实现一个 Http ...

  9. 關於ctype.h頭文件的一些函數

  10. angular在服务中调用组件的某个方法,并传参给组件,(反向调用),变量改变后,强制更新视图

    需要被调用方法的组件文件 import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; ...