7.5.10 CAN 使用指南
基本概述
- 最大可使用 CAN controller 数量:10。
- CAN 最高传输速率:8M。(受限于 transceiver 的波特率限制,目前实验室只测试 验证到5M 波特率。)
- 一个 controller 的 Ram 内划分的 Block 个数:
- CAN0~CAN3:4 Block (可变 payload);
- CAN4~CAN9:4 Block (可变 payload)+ 4 Block(固定 payload)。
- 一个 controller 支持的最大 Mailbox 个数为128。
- 一个 controller 支持一路 RxFIFO,FIFO 深度为:
- CAN0~CAN3:8 * 64 bytes;
- CAN4~CAN9:32 * 64 bytes。
- 不支持 TTController,即不支持 TTCAN(一种基于 CAN 总线的高层协议)。
- CAN 支持多包合并传输,并且可以配置合包的数量和超时时间,默认合包数量为1,超时时间为1000us。
- 最大可使用 CAN controller 数量:16。
- CAN 最高传输速率:8M。(受限于 transceiver 的波特率限制,目前实验室只测试验证到5M 波特率。)
- 一个 controller 的 Ram 内划分的 Block 个数:
- CAN0~CAN3:4 Block (可变 payload);
- CAN4~CAN11:4 Block (可变 payload)+ 4 Block(固定 payload)。
- CAN12~CAN15:4 Block (可变 payload);
- 一个 controller 支持的最大 Mailbox 个数为128。
- 一个 controller 支持一路 RxFIFO,FIFO 深度为:
- CAN0~CAN3:8 * 64 bytes;
- CAN4~CAN11:32 * 64 bytes;
- CAN12~CAN15:8 * 64 bytes;
- 考虑到系统层面的唤醒功能,软件驱动不支持 controller 的 PretendedNetwork 功能。
- 不支持 TTController,即不支持 TTCAN(一种基于 CAN 总线的高层协议)。
- CAN 支持多核使用,可将不同的 CAN 控制器绑定在不同的核心上,但不支持多个核心同时使用同一个 CAN 控制器。
软件架构
S100芯片的 CAN 控制器位于 MCU 域,负责 CAN 数据收发。由于感知等应用位于 Acore,因此部分 CAN 数据需要通过 IPC 核间通信机制转发到 Acore。架构保证传输可靠性,转发机制实现数据正确性检测、丢包检测和传输超时检测等机制。此外,还需要规避 MCU 侧高频转发小数据块导致 CPU 占用率过高,造成 MCU 实时性降低等性能问题。
S100 CAN 转发方案的核心流程如下:
- 首先通过 MCU 侧 CAN2IPC 模块将 CAN 通道映射到对应 IPC 通道,然后通过 Acore 侧 CANHAL 模块将 IPC 通道反映射为虚拟 CAN 设备通道。最后用户通过 CANHAL 提供的 API 接口获取虚拟 CAN 设备中的数据。其中,CAN2IPC 模块为 MCU 侧服务,CANHAL 模块为 Acore 侧提供给应用程序的动态库。
- CAN 采用中断的方式接收数据,当接收到数据之后调用 CAN2IPC 模块,CAN2IPC 模块将 MCU 侧 CAN 数据,按照指定传输协议进行打包,然后通过 IPC 核间通信转发到 Acore。Ipc instance 0和 Ipc instance 4分配给 can 使用,默认使能 can5-can9, can5-can9与 IPC 对应关系如下表:
| Ipc_ShmCfgInstances | channel | |
|---|---|---|
| can0 | 0 | - |
| can1 | 0 | - |
| can2 | 0 | - |
| can3 | 0 | - |
| can4 | 0 | - |
| can5 | 0 | 4 |
| can6 | 0 | 6 |
| can7 | 4 | 7 |
| can8 | 4 | 2 |
| can9 | 0 | 3 |
- CANHAL 模块获取来自 MCU 侧的 IPC 数据,按照指定的传输协议解析数据,并支持业务软件通过 API 获取原始 CAN 帧。
S600芯片的 CAN 控制器位于 MCU 域,负责 CAN 数据收发。由于感知等应用位于 Acore,因此部分 CAN 数据需要通过 IPC 核间通信机制转发到 Acore。架构保证传输可靠性,转发机制实现数据正确性检测、丢包检测和传输超时检测等机制。此外,还需要规避 MCU 侧高频转发小数据块导致 CPU 占用率过高,造成 MCU 实时性降低等性能问题。
S600 CAN 转发方案的核心流程如下:
- 首先通过 MCU 侧 CAN2IPC 模块将 CAN 通道映射到对应 IPC 通道,然后通过 Acore 侧 CANHAL 模块将 IPC 通道反映射为虚拟 CAN 设备通道。最后用户通过 CANHAL 提供的 API 接口获取虚拟 CAN 设备中的数据。其中,CAN2IPC 模块为 MCU 侧服务,CANHAL 模块为 Acore 侧提供给应用程序的动态库。
- CAN 采用中断的方式接收数据,当接收到数据之后调用 CAN2IPC 模块,CAN2IPC 模块将 MCU 侧 CAN 数据,按照指定传输协议进行打包,然后通过 IPC 核间通信转发到 Acore。Ipc instance 0和 Ipc instance 4分配给 can 使用,默认使能 can1-can10, can1-can10与 IPC 对应关系如下表:
| Ipc_ShmCfgInstances | channel | |
|---|---|---|
| can0 | 0 | - |
| can1 | 0 | 1 |
| can2 | 0 | 2 |
| can3 | 0 | 3 |
| can4 | 4 | 4 |
| can5 | 0 | 4 |
| can6 | 4 | 0 |
| can7 | 4 | 1 |
| can8 | 4 | 2 |
| can9 | 4 | 3 |
| can10 | 4 | 5 |
- CANHAL 模块获取来自 MCU 侧的 IPC 数据,按照指定的传输协议解析数 据,并支持业务软件通过 API 获取原始 CAN 帧。
数据流如上图所示:
- 外设数据通过 CAN 的 PHY 和控制器器件被 MCU 域 CAN 驱动接收后,CAN 驱动将数据上报并缓存在 hobot CANIF 模块。
- 满足合包个数或超时时间,调用 CAN2IPC 模块,按照可靠传输协议进行打包,然后通过 IPC 核间通信机制转发给 Acore。
- CANHAL 模块获取来自 MCU 侧的 IPC 数据,按照指定的传输协议解析数据,Acore 应用程序通过 CANHAL Lib 库提供的 API 获取 CAN 帧。
方案特性说明:
- 支持数据透传正确性校验。
- 支持数据透传丢包检测。
- 支持传输超时检测。MCU 侧 CAN2IPC 转发数据时将数据包打上 MCU 侧的时间戳,Acore CANHAL 接收到数据后会读取 Acore 的时间戳,如果传输超时会报警。注意,需要提前启动时间同步完成 MCU RTC 时间和 Acore 网卡 phc0的时间同步。
- 支持多个 CAN 通道并行传输。MCU 侧多个 CAN 控制器的数据可同时被转发给 Acore,Acore 应用程序通过 CANHAL 从不同通道号读出 CAN 数据。
- 由于 CANHAL 底层通过 ipc 核间通信进行传输,而 ipc 目前不支持多个进程或者线程读写同一个通道,因此 CANHAL 也不支持该特性。
硬件连接说明
-
CAN 物理层的形式主要分为闭环总线及开环总线网络两种,一个适合于 高速通讯,一个适合于远距离通讯;S100的 sample 默认采用闭环总线网络架构。
-
CAN 总线的引脚位于 S100的 MCU 扩展板上,引出了5路 CAN 接口,连接器分别对应了5个绿色的螺丝式的3 PIN 连接器。1 PIN(三角标志)为 GND,中间 PIN 为 CAN_L,剩下的为 CAN_H。

- MCU 小板通过2pin 跳帽的形式来选择是否在 CAN_H 和 CAN_L 之间接入120欧姆电阻;当插入跳帽时,接入电阻,适用于闭环网络所需的终端匹配阻抗;移除跳帽则断开终端电阻,适用于开环网络或中继节点场景。

CAN 闭环网络使用两个120欧姆电阻是 CAN 总线的标准配置,以下以 S100举例,如何正确接入电阻:
整体而言,开环网络配置不需要接入120欧姆电阻,而闭环网络配置总共需要插入两个120欧姆电阻;
- 在使用开环网络时,确保 CAN_H 与 CAN_L 线路正确连接,所用到的 CAN 不要插入跳线帽(在网络中不接入120欧姆电阻);
- 若将 S100的 CAN5和 CAN6连接组成双节点内部闭环网络,确保 CAN_H 与 CAN_L 线路 正确连接,还需要在 CAN5和 CAN6接线端子后面的插针插入跳帽(在网络中插入两个120欧姆电阻);
- 若将 S100的 CAN5~CAN9连接组成多节点内部闭环网络,确保 CAN_H 与 CAN_L 线路正确连接,还需要插入两个跳线帽,任意选择两个,严禁插入超过2个跳线帽,以免出现不可预测的问题;
- 若将 S100的 CAN5~CAN9中的任意一个控制器和其它 CAN 设备组成外部闭环网络,确保 CAN_H 与 CAN_L 线路正确连接外,还需要在 RDK 的 CAN 控制器的接线端子后面的插针插入跳帽,并在网络中其它设备端接入一个120Ω电阻;
-
CAN 物理层的形式主要分为闭环总线及开环总线网络两种,一个适合于高速通讯,一个适合于远距离通讯;S600的 sample 默认采用闭环总线网络架构。
-
CAN 总线的引脚 S600共引出10路 can,其中在 MCU 扩展板,引出了5路 CAN 接口,连接器分别对应了5个绿色的螺丝式的3 PIN 连接器。每个 pin 脚的作用可以查看 mcu 子板的背面。在底板上,也引出了5路 CAN 接口,使用 BP 连接器引出。
-
MCU 小板通过拨动拨码开关,来选择是否在 CAN_H 和 CAN_L 之间接入120欧姆电阻;当拨码开关波动到 ON 端,表示接入电阻,适用于闭环网络所需的终端匹配阻抗;当拨码开关波动到数字编码端,表示断开电阻,适用于开环网络或中继节点场景。
-
MCU 扩展版上的 CAN 与拨码开关:

- CAN 与拨码开关的对应关系:
| DPI num | DPI num | ||
|---|---|---|---|
| can1 | 1 | can4 | 4 |
| can2 | 2 | can10 | 5 |
| can3 | 3 |
- S600底板上的 CAN 与拨码开关:

- 底板上 J16为 can 在的位置, 信号名 J16从上到下如下:
| Signal name |
|---|
| GND |
| CAN5_H |
| CAN5_L |
| CAN6_H |
| CAN6_L |
| GND |
| CAN7_H |
| CAN7_L |
| CAN8_H |
| CAN8_L |
| CAN9_H |
| CAN9_L |
- 底板拨码开关在底板背面

- CAN 与拨码开关的对应关系:
| DPI num | DPI num | ||
|---|---|---|---|
| can5 | 1 | can8 | 4 |
| can6 | 2 | can9 | 5 |
| can7 | 3 |
CAN 闭环网络使用两个120欧姆电阻是 CAN 总线的标准配置,以下以 S600举例,如何正确接入电阻:
- 在使用开环网络时,确保 CAN_H 与 CAN_L 线路正确连接,所用到的 CAN(在网络中不接入120欧姆电阻);
- 若将 S600的 CAN1和 CAN2连接组成双节点内部闭环网络,确保 CAN_H 与 CAN_L 线路正确连接,还需要将 CAN1和 CAN2对应拨码开关波动到 ON 端(在网络中插入两个120欧姆电阻);
- 若将 S600的 CAN1~CAN10中的任意一个控制器和其它 CAN 设备组成外部闭环网 络,确保 CAN_H 与 CAN_L 线路正确连接外,还需要将 RDK 的 CAN 控制器对应的莫玛开源波动到 ON 端,并在网络中其它设备端接入一个120Ω电阻;
CAN Filter 配置
标准帧的 filter 最多可配置128个,扩展帧的 filter 最多可配置64个,可选择的 filter 类型如下:
- ONE_ID_FILTER:指定 ID 并可配置 MASK 来忽略 ID 中的哪些 bit 进行过滤,
- RANGE_ID_FILTER:按照 ID 范围进行过滤,
- TWO_ID_FILTER:指定两个 ID 进行过滤。
过滤器的识别
过滤器类型通过检查 u32HwFilterCode 的最高2位来确定:
- 0b00: ONE_ID_FILTER
- 0b01: RANGE_ID_FILTER
- 0b10: TWO_ID_FILTER
/**
* @struct Can_HwFilterType
* @brief Can Hardware Filter
* @NO{S01E03C01}
*/
typedef struct Can_HwFilterType
{
const uint32 u32HwFilterCode; /**< @brief Specifies (together with the filter mask) the identifiers range that passes the hardware filter. */
const uint32 u32HwFilterMask; /**< @brief Describes a mask for hardware-based filtering of CAN identifiers. */
}Can_HwFilterType;
- 配置举例:
- 这是 CAN 7 的过滤器配置,拥有两个过滤器
- 过滤器0的第一个元素的高2位为01,属于范围过滤方式
- 扩展帧和标准帧的过滤相互独立,互不影响
- 标准帧的所有过滤器,如下面的例子过滤器0和过滤1为"或"关系,即如果至少有一个过滤元件满足匹配标准,则 CAN 消息内容将被传输到增强型 RX FIFO 存储器
- 同理,扩展帧的所有过滤器,如下面的例子过滤器2和过滤3为"或"关系,即如果至少有一个过滤元件满足匹配标准,则 CAN 消息内容将被传输到增强型 RX FIFO 存储器
// Config/McalCdd/gen_s100_sip_B_mcu1/Can/src/Can_PBcfg.c
static const Can_HwFilterType Can_aHwFilter_Object7[4U]=
{
{ /* Standard frame configuration */
(uint32)0x400007ffU, // 标准帧配置:接收id为0x0~0x7ff的消息
(uint32)0x00000000U
},
{ /* Standard frame configuration */
(uint32)0x400007ffU, // 标准帧配置:接收id为0x600~0x7ff的消息
(uint32)0x00000600U
},
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x0~0x1fffffff的消息
(uint32)0x00000000U
},
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x600~0x1fffffff的消息
(uint32)0x00000600U
}
};
ONE_ID_FILTER(单 ID 过滤方式)
这是最常见的过滤器类型,使用过滤器代码和掩码进行过滤,伪代码如下:
if ((Received_ID & Filter_Mask) == (Filter_Code & Filter_Mask))
// 接收该消息
else
// 丢弃该消
以标准帧过滤器0配置为例,代码如下:
// Config/McalCdd/gen_s100_sip_B_mcu1/Can/src/Can_PBcfg.c
static const Can_HwFilterType Can_aHwFilter_Object7[4U]=
{
{ /* Standard frame configuration */
(uint32)0x00000400U, // 只接收id = 0x400&0x7ff = 0x400 消息
(uint32)0x000007ffU
},
{ /* Standard frame configuration */
(uint32)0x400007ffU, // 范围过滤方式,支持混用
(uint32)0x00000600U
}
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x0~0x1fffffff的消息
(uint32)0x00000000U
},
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x600~0x1fffffff的消息
(uint32)0x00000600U
}
};
RANGE_ID_FILTER(范围过滤方式)
在这种模式下,使用范围过滤逻辑:
if (id1 <= Received_ID <= id2)
// 接收该消息
else
// 丢弃该消息
这也是 S100 MCU 默认的过滤方式,也是最常用的过滤方式;举例代码如下:
// Config/McalCdd/gen_s100_sip_B_mcu1/Can/src/Can_PBcfg.c
static const Can_HwFilterType Can_aHwFilter_Object7[4U]=
{
{ /* Standard frame configuration */
(uint32)0x00000400U, // 只接收id = 0x400&0x7ff = 0x400 消息
(uint32)0x000007ffU
},
{ /* Standard frame configuration */
(uint32)0x400007ffU, // 范围过滤方式,支持混用
(uint32)0x00000600U
}
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x0~0x1fffffff的消息
(uint32)0x00000000U
},
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x600~0x1fffffff的消息
(uint32)0x00000600U
}
};
TWO_ID_FILTER(双 ID 过滤方式)
这种类型允许指定两个独立的 ID 进行匹配:
- id1: 第一个匹配 ID
- id2: 第二个匹配 ID
if (Received_ID == id1 || Received_ID == id2)
// 接收该消息
else
// 丢弃该消息
以标准帧过滤器0配置为例,代码如下:
// Config/McalCdd/gen_s100_sip_B_mcu1/Can/src/Can_PBcfg.c
static const Can_HwFilterType Can_aHwFilter_Object7[4U]=
{
{ /* Standard frame configuration */
(uint32)0x80000404U,// 只接收id为404的消息
(uint32)0x00000303U // 只接收id为303的消息
},
{ /* Standard frame configuration */
(uint32)0x400007ffU, // 范围过滤方式,支持混用
(uint32)0x00000600U
}
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x0~0x1fffffff的消息
(uint32)0x00000000U
},
{ /* Extended frame configuration */
(uint32)0x5fffffffU, // 扩展帧配置:接收id为0x600~0x1fffffff的消息
(uint32)0x00000600U
}
};
- RDK S100软硬件支持收发扩展帧和标准帧,而不需要修改配置
- RDK S100软硬件支持对扩展帧和标准帧分别过滤
- 注意 id 的长度配置,超出规定长度将发生截断,扩展帧的 id 长度最高为29位,即最大为0x1FFFFFFF,标准帧的 id 长度最高为11位,即最大为0x7FF
- RDK S600软件目前还不支持扩展帧
波特率配置
CAN 的标称位时(Nominal bit timing)可以分为四个段:
- 同步段(sync_seg):用于节点间的时钟同步,所有节点在此段内检测信号边沿。其长度固定为1个时间单位(TQ)
- 传播段(prop_seg):补偿信号在物理线路上的传播延迟。其长度可调整,用于确保信号在物理介质上的传输时间
- 相位缓冲段1(phase_seg1):用于调整相位误差,确保采样点的准确性,可以扩展重同步
- 相位缓冲段2(phase_seg2):同样用于调整相位误差,但可以缩短 这些段的总和决定了 CAN 的总位时间,通过调整这些段的长度,可以配置不同的波特率。 此外还有以下几个重要概念:
- 同步跳转宽度(SJW,synchronization jump width):CAN 总线同步机制中允许调整相位缓冲段的最大时间量,在 硬同步 和 重同步 过程中补偿节点间的时钟偏差,确保采样点对齐。
- 延迟补偿偏移量(Transceiver Delay Compensation Offset):仅 CAN FD 支持,用于解决数据段高速传输时的物理层时序偏移用于补偿 CAN FD 模式下 收发器环路延迟 和 信号传播时间 的固定修正值
- 采样点:CAN 控制器在位时间内对总线电平进行采样的精确时刻,用于判定位的逻辑值(显性0或隐性1)
取值范围和公式计算
- 采样点计算:(sync_seg + prop_seg + phase_seg1)/(sync_seg + prop_seg + phase_seg1 + phase_seg2)×100%
- 同步段固定一个 tq
- prop_seg + phase_seg1>phase_seg2
- SJW ≤ min(Phase_Seg1, Phase_Seg2)
- 当配置5M 及以上波特率时,需配置补偿参数,补偿参数计算公式如下:
- TDC offset = (PropSeg + Seg1 + 1) * Fd Prescaler
配置仲裁段1M 数据段5M 实例说明
1. 基础参数确认
- CAN 时钟频率(CAN_CLK): 40 MHz
- 目标波特率(Bit Rate): 5 Mbps
- 预分频值(Prescaler): 1(不分频)
- 单 Bit 时间内的 TQ 总数: TQ = CAN_CLK/(Bit Rate×Prescaler)=40MHz/5Mbps×1=8TQ
- 时间量子:Tq time = 1 / (40M / prescaler) = 1/40M = 25ns
2. 时间量子(TQ)分配
Sync_Seg(固定段): 1 TQ(同步段不可修改)
剩余 TQ 分配: Prop_Seg+Phase_Seg1+Phase_Seg2=8−1=7TQ
此处取 Prop_Seg=1,Phase_Seg1=4,Phase_Seg2=2,则采样点= (Sync_Seg+Prop_Seg+Phase_Seg1)/Total TQ x 100% = (1+4+1)/8 x 100%= 75%
3. 延迟补偿偏移量(Transceiver Delay Compensation Offset)
Offset=(Prop_Seg+Phase_Seg1+1)×Prescaler = (1+4+1)×1=6TQ
SJW(Synchronization Jump Width)设置:
SJW 必须满足: SJW≤min(Phase_Seg1,Phase_Seg2)=min(4,2)=2
因此,配置 SJW = 2 TQ。
4. 终配置参数
根据上述的方式同理可以计算到1M 情况下的参数,由于部分寄存器获取到的值会自动加一,所以实际写入的值要减一,具体可看下表
- 5M 75%数据段配置
| 参数名 | 值(TQ 或时间) | 需写入寄存器的值 |
|---|---|---|
| Sync_Seg | 1 TQ | 无需写入,固定为1 |
| Prop_Seg | 1 TQ | 1 |
| Phase_Seg1 | 4 TQ | 3 |
| Phase_Seg2 | 2 TQ | 1 |
| Prescaler | 1 | 0 |
| SJW | 2 TQ | 1 |
| 延迟补偿偏移量 | 6 TQ | 6 |
- 1M 80%仲裁段配置
| 参数名 | 值(TQ 或时间) | 需写入寄存器的值 |
|---|---|---|
| Sync_Seg | 1 TQ | 无需写入,固定为1 |
| Prop_Seg | 7 TQ | 6 |
| Phase_Seg1 | 8 TQ | 7 |
| Phase_Seg2 | 4 TQ | 3 |
| Prescaler | 2 | 1 |
| SJW | 2 TQ | 1 |
5. 8M 的配置相对于5M 较为特殊,使用60%的采样
| 参数名 | 值(TQ 或时间) | 需写入寄存器的值 |
|---|---|---|
| Sync_Seg | 1 TQ | 无需写入,固定为1 |
| Prop_Seg | 1 TQ | 1 |
| Phase_Seg1 | 1 TQ | 0 |
| Phase_Seg2 | 2 TQ | 1 |
| Prescaler | 1 | 0 |
| SJW | 1 TQ | 0 |
| 延迟补偿偏移量 | 3 TQ | 3 |
6. 将结果更新到配置文件中
配置文件路径:
${mcu_sdk}/Config/McalCdd/gen_s100_sip_B_mcu1/Can/src/Can_PBcfg.c
配置文件中存在两个波特率相关的重要结构体,下面以 CAN5为例分 别说明:
- Can_aControllerConfig:用于配置 CAN 控制器。每个控制器都有一个对应的配置项
static const Can_ControllerConfigType Can_aControllerConfig[CAN_CONTROLLER_CONFIG_COUNT]=
{
...
{
/* Controller ID configured */
(uint8)5U,
/* Hw controller Offset */
(uint8)5U,
/* Base Address */
FLEXCAN_5_BASE,
/* Activation or not */
(boolean)TRUE,
/* Bus Off uses polling or not */
(boolean)TRUE,
/* Global mask of Legacy FIFO (not used) */
(uint32)0xFFFFFFFFU,
/* Acceptance Mode of Legacy FIFO (not used)*/
CAN_LEGACY_FIFO_FORMAT_A,
/* Legacy FIFO Warning Notification */
NULL_PTR,
/* Legacy FIFO Overflow Notification */
NULL_PTR,
/* Enhanced FIFO Overflow Notification */
NULL_PTR,
/* Error interrupt enable or not */
(boolean)TRUE,
/* Can Error Notification */
Can_ErrorNotif,
/* CanFd Error Notification */
CanFd_ErrorNotif,
/* Default Baudrate ID, 4--1M+5M 5--1M+8M */
(uint16)4U,
/* Baudrate config Count*/
(uint16)6U,
/* Pointer to baudrate config Structure */
Can_aBaudrateConfig_Ctrl5,
/* Pointer to LLD structure to IP config */
&Flexcan_aCtrlConfigPB[5U],
/* HwObject count */
(uint8)9U,
/* Point to group of HwObject that referenced to the current Controller */
Can_apHwObject_Ctrl5
},
...
}
- Can_aBaudrateConfig_Ctrl5:用于定义特定控制器的波特率配置,这是一个大数组,上述步骤中生成的参数均写到这个数组中
static const Can_BaudrateConfigType Can_aBaudrateConfig_Ctrl5[6U]=
{
{
/*Enhance CBT support */
(boolean)TRUE,
/* Tx Bit Rate Switch or not */
(boolean)TRUE,
/* CanFd support */
(boolean)TRUE,
/* Nominal bit rate */ //仲裁段配置
{
(uint8)6U, // 传播段(prop_seg)
(uint8)7U, // 相位缓冲段1(phase_seg1)
(uint8)3U, // 相位缓冲段2(phase_seg2)
(uint16)3U, // 预分频值(Prescaler)
(uint8)1U //同步跳转宽度(SJW)
},
/* Data bit rate */ //数据段配置
{
(uint8)3U,
(uint8)3U,
(uint8)1U,
(uint16)3U,
(uint8)1U
},
/* Tx Arbitration Start delay */
(uint8)12U, // 延迟补偿偏移量(Transceiver Delay Compensation Offset)
/* Tranceiver Delay Disable */
(boolean)FALSE,
(uint8)0U
},
...
RDK S100默认配置了6组参数,用户可以通过修改 Can_aControllerConfig 中的 u16DefaultBaudrateID 成员值来选择波特率,下表为索引对应的波特率参数:
| u16DefaultBaudrateID | 仲裁段频率 | 数据段频率 |
|---|---|---|
| 0 | 500K | 1M |
| 1 | 500K | 2M |
| 2 | 1M | 2M |
| 3 | 1M | 5M(短距离:小于50m) |
| 4 | 1M | 5M(长距离:大于50m) |
| 5 | 1M | 8M |
多包合并配置
CAN 驱动支持接收多包合并数据传输功能,当 CAN 驱动接收到一定阈值的数据包时,会将它们合并,通过 IPC 从 MCU 传输到 Acore。目的是为了减少 IPC 传输频率,提高传输效率。在此基础上提供了合包数量和超时时间两个可配参数。
- 合包数量:用于设置 CAN 驱动接收到多少包之后,进行一次 IPC 传输, 默认为1。
- 超时时 间:用于当一定时间内还没有收到所设阈值的数据包时,进行一次强制传输,默认为1000us。
配置方式
进入 MCU 串口配置方式如下:
D-Robotics:/$ Can
Can_Set_Merge_Time CMD -------- Set the CAN packet merge timeout in ...
Can_Set_Merge_Num CMD -------- Set the number of CAN packages to me...
D-Robotics:/$ Can_Set_Merge_Num 8 // 设置合包数量为8
D-Robotics:/$ Can_Set_Merge_Time 100 // 设置超时时间为100us
acore 侧也支持多包合并传输功能,下面只简单介绍相关数据结构,具体可以使用应用sample中提供的测试用例进行修改验证。
#define CAN_FRAME_NUM (1) // Acore侧表示合并传输数据包的数量
struct pack_info pack = { 0 };
pack.data_num = CAN_FRAME_NUM;
应用 sample
使用指南
执行 sample 之前需要先开启 MCU1,MCU1 开启的流程参考开启 MCU。
Acore canhal 使用可参考 sample 源码目录:source/hobot-io-samples/debian/app/Can,可以在 S100的/app/Can 目录下直接 make 编译使用。
以多路透传为例,目录结构如下:
$ tree /app/Can/can_multi_ch
.
├── Makefile // 主编译脚本
├── config // 配置文件目录
│ ├── channels.json // 通道映射配置文件
│ ├── ipcf_channel.json // IPCF通道映射配置文件
│ └── nodes.json // Can虚拟设备映射配置文件
├── main.cpp // 主程序
├── readme.md // 说明文件
├── can_multich_log.h // 日志头文件
└── run.sh // 运行脚本
json 文件配置主要包括3个 json 配置文件:node.json、ipcf_channel.json、channels.json。目前为了支持多进程,各个进程都会去当前路径下的 config 目录下寻找这3个配置文件。
node.json 负责创建虚拟 CAN 设备节点给 CANHAL API 访问。关键配置选项包括:
- channel_id 字段指定该虚拟 CAN 设备从 ipc 配置文件 ipcf_channel.json 中哪一个节点获取数据。
- target 字段表示该虚拟 CAN 设备节点的名称,CANHAL API 通过该名称访问指定的节点。
- enable 字段表示该节点是否使能。
{
"nodes" : [
{
"id" : 0,
"enable" : true,
"mode_comment" : "value_table: R, W, RW",
"mode" : "RW",
"target" : "can6_ins0ch6",
"clk_source" : "/dev/hrtc0",
"io_channel" : {
"device_type_comment" : "value_table: can, eth, ipcf, spi",
"device_type" : "ipcf",
"channel_id" : 0
},
"raw_protocol" : "built_1.0"
},
.....
{
"id" : 4,
"enable" : true,
"mode_comment" : "value_table: R, W, RW",
"mode" : "RW",
"target" : "can5_ins0ch4",
"clk_source" : "/dev/hrtc0",
"io_channel" : {
"device_type_comment" : "value_table: can, eth, ipcf, spi",
"device_type" : "ipcf",
"channel_id" : 4
},
"raw_protocol" : "built_1.0"
}
]
}
ipcf_channel.json 将 node.json 中用到的 ipc 节点映射到具体的 instance 和 channel。
{
"enable" : true,
"libipcf_path" : "/usr/hobot/lib/libhbipcfhal.so.1",
"channels" : [
{
"id" : 0,
"channel" : {
"name" : "can6_ins0ch6",
"instance": 0,
"channel": 6,
"fifo_size": 64000,
"fifo_type": 0,
"pkg_size_max": 4096,
"dev_path":"/dev/ipcdrv",
"dev_name":"ipcdrv",
"recv_timeout" : 4000
}
},
......
{
"id" : 4,
"channel" : {
"name" : "can5_ins0ch4",
"instance": 0,
"channel": 4,
"fifo_size": 64000,
"fifo_type": 0,
"pkg_size_max": 4096,
"dev_path":"/dev/ipcdrv",
"dev_name":"ipcdrv",
"recv_timeout" : 4000
}
}
]
}
channels.json 指定 ipc 配置文件,用户一般不需要更改。
{
"io_channels" : {
"ipcf" : "./config/ipcf_channel.json"
}
}
Acore 无法直接操作 CAN 外设,需要通过借助 Ipc 模块来中转数据,与外设通道的映射关系可以查阅 MCU IPC使用指南 中的 IPC 使用情况章节。
Acore 应用程序通过 CANHAL 获取 MCU 侧 CAN 帧的流程伪代码如下:
void send_frame_data(void *arg)
{
... do
{
ret = canSendMsgFrame(test_params->target, &frame[0], &pack);
if (ret > 0) { /* 发送成功 */
send_count++;
PRINT_DEBUG("Send length %d\n", ret);
break;
} else if (ret == -CAN_TRY_AGAIN && /* 由于IPC或者其他资源不足,重试 */
retry++ < MAX_RETRY) {
usleep(2);
continue;
} else { /* 发送失败 */
PRINT_ERR("Send failed after retries, exiting...\n");
break;
}
}
while (1)
;
}
void *recv_frame_data(void *arg)
{
while (!exit_flag) {
ret = canRecvMsgFrame(target, frame, &pack); // non blocking
if (ret < 0) {
if (ret == -CAN_TRY_AGAIN) { /* 由于IPC或者其他资源不足,重试 */
PRINT_DEBUG("canRecvMsgFrame try again\n");
continue;
} else {
PRINT_ERR("canRecvMsgFrame failed ret: %d\n", ret);
gettimeofday(¤t_time, NULL);
double elapsed =
difftime(current_time.tv_sec, last_recv_time.tv_sec);
if (elapsed > RECV_TIMEOUT) { /* 超时退出 */
PRINT_INFO(
"No data received for %d seconds. Exiting thread.\n",
RECV_TIMEOUT);
goto can_recv_exit;
}
continue;
}
}
}
}
int main(int argc, char *argv[])
{
ret = canInit();
pthread_create(&send_thread, NULL, send_frame_data, &tx_params);
pthread_create(&rx_threads[i], NULL, recv_frame_data, &rx_params[i])
pthread_join(send_thread, NULL);
pthread_join(rx_threads[i], NULL);
canDeInit();
}
- 首先执行 canInit()完成初始化,然后创建发送线程和接收线程
- 发送线程调用 canSendMsgFrame()发送数据包,接收线程调用 canRecvMsgFrame()接收数据包,其中 target 参数为 json 文件中配置好的通道。
- pack 信息包含这一包数据的信息,包括 can 帧数量、mcu 侧的时间戳以及 acore 侧的 monotic 时间戳等信息。
- canhal 会从这一包 ipc 数据中解析出 can 帧,用户通过 frame 指针读取出所有 can 帧。
- 最后执行 canDeInit()释放资源。
can 的接收和发送函数依赖 IPC 的资源,当传输速率过快时会出现资源耗尽的情况,此时可以进行降速和重传。
ACORE 侧实例说明
简单的 can 收发 sample
目录介绍
// /app/Can/can_send
.
├── Makefile // 主编译脚本
├── canhal_send.c // 发送一帧标准帧数据
└── config // 配置文件目录
├── channels.json // 通道映射配置文件
├── ipcf_channel.json // 通道映射配置文件
└── nodes.json // 通道映射配置文件
// /app/Can/can_get
.
├── Makefile // 主编译脚本
├── canhal_get.c // while 1循环,接收数据
└── config // 配置文件目录
├── channels.json // 通道映射配置文件
├── ipcf_channel.json // 通道映射配置文件
└── nodes.json // 通道映射配置文件
使用前提
这里仅给出一个简单的 sample,实际应用中需要根据实际需求进行修改。
使用前提:
- MCU1正常运行
- 硬件连接:使用 CAN 闭环总线网络,CAN5连接 CAN6,RDKS100上 CAN5和 CAN6需要在接线柱后面的2 PIN 引脚使用跳线帽或杜邦线短接, 以确保 CAN_H 和 CAN_L 之间接入120欧姆电阻, RDKS600上需要将 can5,can6对应的拨码开关,波到 ON 端,以确保 CAN_H 和 CAN_L 之间接入120欧姆电阻。
- 由于 can_send 和 can_get 都使用的 instance channel4(默认映射来自 CAN5的数据),由于 Ipc 单个 channel 只能被一个线程使用,因此在互联测试的时候需要依据 软件架构中 can 与 instance channel 的对应关系来设置配置文件。例如针对 can6, S100上 intance 为0, channel 为6, S600上 instance 为4, channel 为0, 对应设置文件如下:
{
"enable" : true,
"libipcf_path" : "/usr/hobot/lib/libhbipcfhal.so.1",
"channels" : [
{
"id" : 0,
"channel" : {
"name" : "bypass",
"instance": 0, // S100 为0 S600 为4
"channel": 6, // S100 为6 S600 为0
"fifo_size": 64000,
"fifo_type": 0,
"pkg_size_max": 4096,
"dev_path":"/dev/ipcdrv",
"dev_name":"ipcdrv",
"recv_timeout" : 4000
}
}
]
}