协议知识的补充与拓展
1. CAN
CAN - Controller Area Network 即控制器局域网协议
1.1 差分信号与电平逻辑
CAN 依赖专门的收发器芯片来与单片机进行通讯。普通的电平信号经过其转化变成差分信号,具体的逻辑根据两条线的压差来决定,而差分信号也可以通过该芯片转化为普通的电平信号。
Recessive- 隐形电平,表示逻辑1Dominamt- 显性电平,表示逻辑0
差分信号由于受到干扰双绞线共同受到干扰,其压差变化幅度基本与未受干扰前相差不大,因此差分信号抗干扰能力强,这也使得 CAN 的通讯距离较长。
1.2 数据帧结构
SOF显性电平- 仲裁段包括
ID(Identifier)与RTR:ID为11字节,由于区分总线上挂载的设备RTR为1字节,表示帧是数据帧 (0) 还是遥控帧 (1)
- 控制段包括
IDE(Identifier Extension)与r0,DLC(Data Length Code)IDE用于区分这一帧是拓展帧还是标准帧,0为标准。r0为保留位DLC表示数据部分的长度,4字节,表示数据的长度 (0-8Bits)
- 数据段根据
DLC的具体数值决定 CRC校验段包括CRC与CRC Delimiter即定界符CRC为循环冗余校验码15Bits- 定界符
1Bit
ACK段包括ACK槽及其定界符,均为1Bit,发送时为隐性电平,如果确认收到则拉高EOF隐性电平
遥控帧对比数据帧的区别是,他没有数据段,是接收单元发给发送节点请求数据帧的。
拓展帧与普通帧的区别在于:
ID后RTR去除,换为显性的SRR占位IDE后加入18位拓展ID- 拓展
ID后跟随RTR以及两个保留位RO/R1
1.3 CAN 仲裁
在总线空闲时,最先开始发送 的节点获得发送权,一旦开始发送,不会被其他节点抢占。
多个节点同时开始发送时,各发送节点从仲裁段的第一位开始进行仲裁。连续输出 显性电平 最多的节点可继续发送。
具有相同ID的数据帧和遥控帧在总线上竞争时,数据帧具有优先权可继续发送。
标准格式ID与具有相同ID的遥控帧或者扩展格式的数据帧在总线上竞争时,标准格式的RTR 位为显性位的具有优先权可继续发送。
1.4 CAN 波特率与位同步(略)
由于缺失时钟线,所以 CAN 是异步通信。
每个数据位都由若干个时间单元 Time Quanta 所组成,而每个数据位又可以分成不同时间单元长度的几个段:
| SYNC_SEG | BS1 | BS2 |
|---|---|---|
| 同步段 | Prop+Phase1 | Phase2 |
| 而波特率可以这样得到: |
$$BaudRate = \frac{1}{BitTime}=\frac{1}{Tq\times(SS+BS1+BS2)}$$
由于同步段常为 1 个时间单位,所以一般可以认为:
$$ BaudRate = \frac{f_{CAN}}{PSC\times(1+BS1+BS2)}=\frac{f_{APB}}{…} $$
$$Tq = \frac{PSC}{f_{APB}}$$
位同步机制则通过实现边缘检测实现。
1.5 CAN 过滤
CAN 依靠过滤机制决定某个节点是否接受来自某个 ID 的报文。
总线上任何一帧报文所有节点都会物理上收到,但并不是每个节点都关心所有 ID,如果全部进中断,CPU 负担会很重。
1.6 CubeMX & HAL
Prescaler- 决定时间单元大小BS1- 设置时间单元数BS2- 设置时间单元数SJY- 设置时间单元数(SJY就是Synchronization Jump Width,检测到相位误差时,允许对当前比特时间进行的“最大调整幅度)
$$f_{CAN}=Tq\times(BS1+BS2+SJY) = \frac{PSC}{f_{APB}}$$STM32F103 的 CAN 有两个发送邮箱 FIFO0/1 和三个接受邮箱 (启用接收中断以及时处理报文)。
节点在接受报文时,有以下几个过滤模式:
- 列表模式:列表存储期望通过的
ID,比对完全一致就接受 - 掩码模式:只比较
ID中的特征点 (验证码与屏蔽码),不全部检测
| 工作模式 | 描述 |
|---|---|
| 32位列表 | CAN_FxR1寄存器和CAN_FxR2寄存器都用来存储某个期望通过的CAN ID,这样就可以存入2个期望通过的CAN ID(标准CAN ID和扩展CAN ID均可)。 |
| 16位列表 | 两寄存器定义一样,且各自拆成两个,则总共可以写入4个标准CAN ID。 |
| 32位掩码 | CAN_FxR1用做32位宽的验证码,而CAN_FxR2则用作32位宽的屏蔽码。 |
| 16位掩码 | 可以当做2对验证码+屏蔽码组合来用,但它只能对标准CAN ID进行过滤。 |
下面来看一些 API: |
1 | void CAN_Filter_Config() |
创建过滤器结构体,配置过滤器参数并调用 HAL 的API。
1 | HAL_CAN_Start(&hcan); |
启动 CAN 并且开启 FIFO0 接收中断。
1 | uint8_t CANx_SendStdData(CAN_HandleTypeDef* hcan,uint16_t ID,uint8_t *pData,uint16_t Len) |
编写 CAN 发送函数,创建报文结构体以及填写信息,再调用 HAL 的 API(这里使用哪个邮箱也可以自己写)。
1 | uint8_t rxData1[8]; |
编写接收回调。
