1. CAN

CAN - Controller Area Network 即控制器局域网协议

1.1 差分信号与电平逻辑

CAN 依赖专门的收发器芯片来与单片机进行通讯。普通的电平信号经过其转化变成差分信号,具体的逻辑根据两条线的压差来决定,而差分信号也可以通过该芯片转化为普通的电平信号。

  • Recessive - 隐形电平,表示逻辑1
  • Dominamt - 显性电平,表示逻辑0

差分信号由于受到干扰双绞线共同受到干扰,其压差变化幅度基本与未受干扰前相差不大,因此差分信号抗干扰能力强,这也使得 CAN 的通讯距离较长。

1.2 数据帧结构

  • SOF 显性电平
  • 仲裁段包括 ID(Identifier)RTR:
    • ID11 字节,由于区分总线上挂载的设备
    • RTR1 字节,表示帧是数据帧 (0) 还是遥控帧 (1)
  • 控制段包括 IDE(Identifier Extension)r0, DLC(Data Length Code)
    • IDE 用于区分这一帧是拓展帧还是标准帧,0 为标准。
    • r0 为保留位
    • DLC 表示数据部分的长度,4 字节,表示数据的长度 (0-8Bits)
  • 数据段根据 DLC 的具体数值决定
  • CRC 校验段包括 CRCCRC Delimiter 即定界符
    • CRC 为循环冗余校验码 15Bits
    • 定界符 1Bit
  • ACK 段包括 ACK 槽及其定界符,均为 1Bit,发送时为隐性电平,如果确认收到则拉高
  • EOF 隐性电平

遥控帧对比数据帧的区别是,他没有数据段,是接收单元发给发送节点请求数据帧的。

拓展帧与普通帧的区别在于:

  • IDRTR 去除,换为显性的 SRR 占位
  • IDE 后加入 18 位拓展 ID
  • 拓展 ID 后跟随 RTR 以及两个保留位 RO/R1

1.3 CAN 仲裁

  1. 在总线空闲时,最先开始发送 的节点获得发送权,一旦开始发送,不会被其他节点抢占。

  2. 多个节点同时开始发送时,各发送节点从仲裁段的第一位开始进行仲裁。连续输出 显性电平 最多的节点可继续发送。

  3. 具有相同ID的数据帧和遥控帧在总线上竞争时,数据帧具有优先权可继续发送。

  4. ​标准格式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}}$$
STM32F103CAN 有两个发送邮箱 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
2
3
4
5
6
7
8
void CAN_Filter_Config()
{
CAN_Filter can_filter{0};
can_filter.FilterNumber = ...
can_filter.FilterMode = ...
...
HAL_CAN_ConfigFilter(&hcan,&can_filter);
}

创建过滤器结构体,配置过滤器参数并调用 HAL 的API。

1
2
HAL_CAN_Start(&hcan);
HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);

启动 CAN 并且开启 FIFO0 接收中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
uint8_t CANx_SendStdData(CAN_HandleTypeDef* hcan,uint16_t ID,uint8_t *pData,uint16_t Len)
{
static CAN_TxHeaderTypeDef Tx_Header;

Tx_Header.StdId=ID;
Tx_Header.ExtId=0;
Tx_Header.IDE=0;
Tx_Header.RTR=0;
Tx_Header.DLC=Len;

uint32_t TX_Mailbox = ...

if(HAL_CAN_AddTxMessage(&hcan, &Tx_Header, pData,&TX_Mailbox)!= HAL_OK)
{
if(HAL_CAN_AddTxMessage(hcan, &Tx_Header, pData, (uint32_t*)CAN_TX_MAILBOX1) != HAL_OK)
{
HAL_CAN_AddTxMessage(hcan, &Tx_Header, pData, (uint32_t*)CAN_TX_MAILBOX2);
}
}
}

编写 CAN 发送函数,创建报文结构体以及填写信息,再调用 HALAPI(这里使用哪个邮箱也可以自己写)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
uint8_t rxData1[8]; 
uint8_t rxData2[8];
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
CAN_RxHeaderTypeDef RxHeader;
if(hcan->Instance ==CAN1)
{
HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, rxData1);
}
else if(hcan->Instance == CAN2)
{
HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader,rxData2);
}
}

编写接收回调。