简介
CAN通信帧共分为数据帧、远程帧、错误帧、过载帧和帧间隔,本文这里以数据帧为例。
注:显性电平对应逻辑0,CAN_H和CAN_L之差为2.5V左右。而隐性电平对应逻辑1,CAN_H和CAN_L之差为0V
, 数据帧有标准帧和扩张帧两种格式,一个11位,一个29位
标准帧和扩张帧两种格式区别:
- 扩展帧的仲裁域有29位,可以出现2^29中报文,且在数据链路上是有间隙的(对操作者透明),帧ID的范围是0000 0000-1FFF FFFF。(PS:目的就是构造29位的CAN ID,可以实现更加庞大的ID群)
- 标准帧的仲裁域是连续的11位,可以出现2^11种报文,也就是帧ID的范围是000-7FF;
- 标准帧和扩张帧的控制帧中的DLC(数据长度)完全相同,但保留位不同,标准帧为IDE、R0,扩展帧为R1、R0,必须以显性电平发送(由数据链路层操作)
注意:这里的帧ID 并不是表示发送的目的地址,而是表示访问总线的消息的优先级(帧ID值越小,优先级越高,最小是0x00000000)
CAN协议特点:
1、多主控制。
2、系统柔软性。
3、通讯速度快,通讯距离远。
4、具有错误检测、错误通知、错误恢复功能。
5、故障封闭功能。
6、连接节点多。
CAN总线具有自动仲裁功能,这样就提高了总线的利用率。
CAN总线没有被发送出去的隐性信号,会由CAN控制器后续发送出去。这里牵涉到CAN总线优先级的问题,后续进一步讲述。
当然,CAN相比485具有明显优势,主要原因还是在于CAN控制器。
CAN直接通过TX连接对方的RX引脚,单向传输可以,双向传输就不行,因为CAN控制器会实时监测发送出去的信号是否正确。也就是说TX要与RX信号一致才行,否则CAN控制器认为你发送失败。
CAN总线收发,中断方式接收配置
平台:STM32F103RB
STM32CUBEMX V5.3
配置CAN
CAN的波特率最大为1Mbps。
波特率计算方法:时钟主频 / 分频 / (tq1 + tq2 + swj)
以500K的波特率配置为例:
stm32f103的CAN的时钟主频是36M,分9频就是4M,在除以(5 + 2 + 1)得到500K的波特率。
注意:stm32cubemx生成的CAN代码是不带过滤器的,需要自己手动添加。
CAN之数据帧格式
代码部分
CAN初始化
CAN_HandleTypeDef hcan1;
CAN_FilterTypeDef sFilterConfig;
//CAN初始化:
/* CAN init function */
void MX_CAN_Init(void)
{
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 9;
hcan1.Init.Mode = CAN_MODE_LOOPBACK; // 回环模式,测试用。
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_5TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan1.Init.TimeTriggeredMode = DISABLE; // 时间触发通信模式禁能
hcan1.Init.AutoBusOff = DISABLE; // 软件自动离线管理禁能
hcan1.Init.AutoWakeUp = DISABLE; // 睡眠模式自动唤醒禁能
hcan1.Init.AutoRetransmission = DISABLE; // 报文自动重传禁能
hcan1.Init.ReceiveFifoLocked = DISABLE; // 报文锁定禁能,新报文覆盖旧报文
hcan1.Init.TransmitFifoPriority = ENABLE; // 使能优先级由报文标识符决定
if (HAL_CAN_Init(&hcan1) != HAL_OK)
{
Error_Handler();
}
sFilterConfig.FilterBank = 0; // 使用过滤器0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽
sFilterConfig.FilterIdHigh = 0x0000; // ID高十六位
sFilterConfig.FilterIdLow = 0x0000; // ID低十六位
sFilterConfig.FilterMaskIdHigh = 0x0000; // ID掩码高十六位
sFilterConfig.FilterMaskIdLow = 0x0000; // ID掩码低十六位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器0关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活过滤器0
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_CAN_Start(&hcan1) != HAL_OK)
{
Error_Handler();
}
if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) // 使能FIFO0数据中断接收
{
Error_Handler();
}
}
覆写接收中断回调函数
(注意:上面的配置我们使用的式FIFO0,所以要覆写FIFO0的中断回调函数):
void can_msg_info(CAN_RxHeaderTypeDef* hdr, const void* data)
{
printf("IDE : %s\r\n",
hdr->IDE == CAN_ID_STD ? "CAN_ID_STD" : "CAN_ID_EXT");
printf("RTR : %s\r\n",
hdr->RTR == CAN_RTR_DATA ? "CAN_RTR_DATA" : "CAN_RTR_REMOTE");
printf("DLC : %ld\r\n", hdr->DLC);
printf("StdId : 0x%X\r\n", hdr->StdId);
printf("ExtId : 0x%X\r\n", hdr->ExtId);
printf("Data : %s\r\n", (char *) data);
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
if (hcan->Instance == hcan1.Instance)
{
uint8_t buf[8];
CAN_RxHeaderTypeDef CAN_RX_HDR;
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_RX_HDR, buf) // 获得接收到的数据头和数据
== HAL_OK)
{
can_msg_info(&CAN_RX_HDR, buf);
HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中断
}
}
}
CA发送函数:
uint8_t CAN_Transmit(const void* buf, uint32_t len)
{
uint32_t txmailbox = 0;
uint32_t offset = 0;
CAN_TxHeaderTypeDef hdr;
hdr.IDE = CAN_ID_STD; // 帧ID:标准帧
hdr.RTR = CAN_RTR_DATA; // 帧类型:数据帧
hdr.StdId = 0x12; // 标准帧ID,最大11位,也就是0x7FF
hdr.ExtId = 0x12; // 扩展帧ID,最大29位,也就是0x1FFF
hdr.TransmitGlobalTime = DISABLE;
while(len != 0)
{
hdr.DLC = len > 8 ? 8 : len; // 数据长度
if(HAL_CAN_AddTxMessage(&hcan1, &hdr, ((uint8_t *)buf) + offset, &txmailbox) != HAL_OK)
return 1;
offset += hdr.DLC;
len -= hdr.DLC;
}
return 0;
}
附注:stm32cubemx can配置参数详解:
配置参数
参数 | 意思 |
---|---|
Prescaler | 预分频,即位时序提到的APB1 peripheral clocks继续分一次频 |
Time Quantum | 最小时间单位Tq,自动计算出来的,不需要填写 |
Time Quanta in Bit Segment 1 | PBS1段长度 |
Time Quanta in Bit Segment 2 | PBS2段长度 |
ReSynchronization Jump Width | 重同步跳跃宽度,即位时序提到的SJW |
Time Triggered Communication Mode | 是否使能时间触发 |
Automatic Bus-Off Management | 是否使能自动离线管理 |
Automatic Wake-Up Modet | 是否使能自动唤醒 |
Qutomatic Retransmission | 是否使能自动重传 |
Receive Fifo Locked Mode | 是否使能锁定FIFO |
Transmit Fifo Priority | 配置报文优先级的判断方法 |
Oprating Mode | 操作模式 |
- 这些参数也可以在can.c中自行修改
- 中断(NVIC)设置,根据需要设置,一般勾上CAN1 RX0 Interrupt
8.1.2 协议层
位时序
意义:为了实现正确的总线电平采样,确保通讯正常。最小单位是Tq(Time Quantum),一个完整位由8~25个Tq组成
组成:SS段、PTS 段、PBS1段、PBS2段
段名 | 意义 | 作用 |
---|---|---|
SS(1Tq) | 同步段 | 补偿物理延时,是传播时间、收发器延时之和的两倍 |
PTS(1~8Tq) | 传播时间段 | 补偿变压阶段误差 |
PBS1(1~8Tq) | 相位缓冲段1 | 使总线各节点同步 |
PBS2(2~8Tq) | 相位缓冲段2 | 补偿边沿阶段误差 |
SJW(1~4Tq) | 再同步补偿宽度 | 补偿时钟频率偏差、传输延迟等 |
例如
通讯波特率的计算:CAN使用的时钟线是APB1 peripheral clocks(假设是APC),即一般是SYSCLK的四分频。而CAN通讯还要对此进行预分频(假设是Prescaler),则一个Tq为 Prescaler / APC(单位s)。而一个数据位占(SS+PTS+PBS1+PBS2+SJW)个Tq。则一秒可以传输的位数就是1 T q ∗ 一 个 位 得 T q 数 \frac{1}{Tq一个位得Tq数}Tq∗一个位得Tq数1
例如:在时钟树查得APB1 peripheral clocks是45MHz,预分频为5,则一个Tq为 4 45 M H z = 111.1111 … ( n s ) \frac{4}{45MHz}=111.1111\dots(ns)45MHz4=111.1111…(ns) ,上图中有19个Tq,则此时波特率为1 19 ∗ 111.11 ∗ 1 0 − 9 ≈ 473689 ( b p s ) \frac{1}{19111.11*10^{-9}}\approx473689(bps)19∗111.11∗10−91≈473689(bps)