CAN总线结构
闭环
CAN总线网络的结构有闭环和开环两种形式。
闭环结构的CAN总线网络,总线两端各连接一个120欧的电阻,两根信号线形成回路。这种CAN总线网络由ISO 11898标准定义,是高速、短距离的CAN网络,通信速率为125kbit/s到1Mbit/s。在1Mbit/s通讯速率时,总线长度最长达40m。

开环
开环结构的CAN总线网络,两根信号线独立,各自串联一个2.2k欧的电阻。
CAN总线网络由ISO11519-2标准定义,是低速、远距离的CAN网络,通信速率最高125kbit/s。在40kbit/s速率时,总线最长距离可达1000m。

信号
两根信号线的电压差CANH-CANL表示CAN总线的电平,与传输的逻辑信号1或0对应。对应于逻辑1的称为隐性(Recessive)电平,对应于逻辑0成为显性(Dominant)电平。

两种不同协议的逻辑1和0电压差

CAN总线网络中,CAN总线上的一个终端设备称为一个节点(Node),没有主设备和从设备的区别。一个CAN节点的硬件部分一般由CAN控制器和CAN收发器两个部分组成。CAN控制器负责CAN总线的逻辑控制,实现CAN传输协议;CAN收发器主要负责MCU/SOC逻辑电平与CAN总线电平之间的转换。

CAN总线的特点
- 实时性: CAN总线具有优越的实时性能,适用于需要及时传输数据的应用
- 多主机系统:CAN支持多主机系统,多个系欸点可以同时发送和接收数据。分布式控制可以是的系统更加灵活。没有主从之分。但是在同一时刻只能由一个节点发送数据,其他节点只能接收数据。
- 差分信号传输:CAN使用差分型号来传输信息。提供了良好的抗干扰能力
- 仲裁机制: CAN总线采用非破坏性仲裁机制,通过比较消息标识符的优先级来决定哪个节点有权继续发送数据。这种机制确保了总线上数据传输的有序性,避免了冲突。
- 广播信号:CAN总线采用广播通信方式,即发送的数据帧可以被总线上的所有节点接收。这种特性有助于信息的共享和同步,同时减少了系统的复杂性。
- 低成本:CAN总线的硬件成本相对较低,适用于大规模的系统集成。由于CAN控制器在硬件上实现了仲裁机制,无需额外的主机处理器,减小了成本和复杂性。
- 灵活性:CAN协议灵活适应不同的应用场景
- 错误检测和处理:CAN总线具有强大的错误检测和处理机制。通过CRC检查和其他错误检测手段,CAN能够识别和处理传输过程中可能发生的错误,提高可靠性
- 多种帧格式:CAN总线上的节点没有地址的概念。CAN总线上的数据是以帧为单位传输的,帧又分为数据帧、遥控帧等多种帧类型,帧包含需要传输的数据或控制信息。
- 线与逻辑:CAN总线具有“线与”的特性,也就是当由两个节点同时向总线发送信号时,一个是发送显性电平(逻辑0),另一个发送隐性电平(逻辑1),则总线呈现为显性电平。这个特性被用于总线总裁,也就是哪个节点优先占用总线进行发送操作。
- 特定标识符:每一个帧有一个标识符(Identifier,一下简称ID)。ID不是地址,它表示传输数据的类型,也可以用于总线仲裁时确定优先级。
- 滤波特性:每个CAN节点都接收数据,但是可以对接收的帧根据ID进行过滤。只有节点需要的数据才会被接收并进一步处理,不需要的数据会被自动舍弃。
- 半双工:CAN总线通信时半双工的,即总线不能同时发送和接收。在多个节点竞争总线进行发送时,通过ID的优先级进行仲裁,竞争胜出的节点继续发送,竞争失败的节点立刻转入接收状态。
- 无时钟信号:CAN总线没有用于同步的时钟信号,所以需要规定CAN总线通信的波特率,所以节点都是用同样的波特率进行通信。
CAN位时序和波特率
一个CAN网络需要规定一个通信的波特率,各节点都以相同的波特率进行数据通信。位时序指的是一个节点采集CAN总线上一个位数据的时序,位时序如图。通过位时序,CAN总线可以进行位同步,以吸收节点时钟差异产生的波特率误差,保证接收数据的准确性。

- 同步段(SYNC_SEG): 在这个时间段内,总线上一个发送一次位信号的跳变。如果节点在同步段检测到总线上的一个跳变沿,就代表节点和总线是同步的。同步段长度固定位1个tq
- tq(time quantum)被称为时间片,tq由CAN控制器的时钟频率fcan决定。在STM32F407中,两个CAN控制器在APB1总线上,CAN控制器有预分频器,APB1总线的时钟信号PCLK1经分频后得到fcan。
- 位段1(BS1):定义了采样点的位置。在BS1结束的时间对总线采样,得到的电平就是这个位的电平。BS1的初始长度是1到16个tq,但它的长度可以在再同步(resynchronization)的时候被自动加长,以补偿各节点频率差异导致的正相位漂移。
- 位段2:定义了发送点的位置。BS2的初始长度是1到8各tq,在同步时可以被自动缩短,以补充负相位漂移。
硬件电路

总线协议
OSI模型
CAN协议表行ISO规定的OSI基本参照模型中的传输层,数据链路层以及物理层
CAN 协议中关于 ISO/OSI 基本参照模型中的传输层、数据链路层及物理层,具体有哪些定义所示:

CAN各类型帧
帧的种类
CAN网络通信是通过5种类型的帧(frame)进行的
- 数据帧(Data frame)
- 标准格式 11个位的标识符
- 拓展格式 29个位的标识符
- 遥控帧(Remote frame)
- 标准模式
- 拓展格式
- 错误帧(Error frame)
- 过载帧(Overload frame)
- 帧间空间(Inter-frame space)
| 帧类型 | 帧用途 |
|---|---|
| 数据帧(Data frame) | 节点发送的包含ID和数据的帧,用于发送单元向接收单元传送数据的帧。 |
| 遥控帧(Remote frame) | 节点向网络上的其他节点发出的某个ID的数据请求,发送节点收到遥控帧后就可以发送相应ID的数据帧, |
| 错误帧(Error frame) | 节点检测出错误时,向其他节点发送的通知错误的帧 |
| 过载帧(Overload frame) | 接收单元未做好接收数据的准备时发送的帧,发送节点收到过载帧后可以暂缓发送数据帧 |
| 帧间空间(Inter-frame space) | 用于将数据帧、遥控帧与前后的帧分隔开的帧 |
数据帧
用于发送单元向接收单元传送数据的帧
由7个段构成
- 帧起始:表示数据开始的段
- 仲裁段:表示该帧优先级的段
- 控制段:表示数据的字节数及保留位的段
- 数据段:数据的内容,可以发送0~8个字节的数据
- CRC段:检查帧的传输错误的段
- 帧结束:表示数据结束的段
每一位讲解
| 顺序 | 字段 | 位数 | 作用 | 位值 |
|---|---|---|---|---|
| 1 | SOF(Start Of Frame)帧起始 | 1 bit | 标志一帧开始 | D(0) |
| 2 | Identifier(ID)标识符 | 11 bit(标准帧)/29 bit(扩展帧) | 消息ID,同时决定仲裁优先级 | ID越小优先级越高 |
| 3 | RTR(Remote Transmission Request)远程请求 | 1 bit | 区分数据帧和远程帧 | 数据帧:D(0),远程帧:R(1) |
| 4 | IDE(Identifier Extension)扩展标志 | 1 bit | 区分标准帧和扩展帧 | 标准:0,扩展:1 |
| 5 | r0(保留位) | 1 bit | 保留扩展使用 | 固定0 |
| 6 | DLC(Data Length Code)数据长度码 | 4 bit | 表示Data数据长度 | 0~8 Byte(CAN FD可到64Byte) |
| 7 | Data 数据段 | 0~64 Byte | 实际传输数据 | 应用层定义 |
| 8 | CRC Sequence 校验序列 | 15 bit | 检测传输错误 | CRC计算值 |
| 9 | CRC Delimiter 校验界定符 | 1 bit | 标志CRC结束 | R(1) |
| 10 | ACK Slot 应答位 | 1 bit | 接收节点确认数据正确 | 接收正确拉低D(0) |
| 11 | ACK Delimiter 应答界定符 | 1 bit | 标志ACK结束 | R(1) |
| 12 | EOF(End Of Frame)帧结束 | 7 bit | 标志一帧结束 | 全部R(1) |
![]() |
帧起始
SOF
标准、拓展格式相同
表示帧的开始的段。1个位的显性位


仲裁段
表示数据优先级的段。标准个是和拓展格式在此的构成有所不同

控制段
表示数据的优先级的段。 标准格式和拓展格式有所不同


数据段
数据段可包含0~8个字节的数据。从MSB(最高位)开始输出

CRC段
(标准、拓展格式相同) CRC 段是检查帧传输错误的帧。由 15 个位的 CRC 顺序1 和 1 个位的 CRC 界定符(用于分隔的位)构成。

ACK段
ACK 段(Acknowledge Bit)用来确认是否正常接收。由 ACK 槽(ACK Slot)和 ACK 界定符 2 个位构成

帧结束
EOF
帧结束是表示该该帧的结束的段。由 7 个位的隐性位构成。

遥控帧
用于接收单元向具有相同 ID 的发送单元请求数据的帧。
接收单元向发送单元请求发送数据所用的帧。遥控帧由 6 个段组成。遥控帧没有数据帧的数据段。
- 帧起始:表示帧开始的段
- 仲裁段:表示帧优先级的段
- 控制段:表示数据的字节数及保留位
- CRC段:检查帧的传输错误段
- ACK段:表示确认正常接收的段
- 帧结束:表示遥控帧结束的段

错误帧
用于当检测出错误时向其他单元通知错误的帧
用于在接收和发送消息时检测出错误通知错误的帧。错误帧由错误标志和错误界定符构成。
错误标志
错误标志包括主动错误标志和被动错误标志两种。
- 主动错误标志:6 个位的显性位。
- 被动错误标志:6 个位的隐性位。
错误界定符
错误界定符由8个位的隐性位构成

过载帧
用于接收单元通知其尚未做好接收准备的帧
过载帧是用于接收单元通知其尚未完成接收准备的帧。过载帧由过载标志和过载界定符构成。
过载标志
6 个位的显性位。过载标志的构成与主动错误标志的构成相同。
过载界定符
8 个位的隐性位。过载界定符的构成与错误界定符的构成相同。
帧间空间
帧间隔是用于分隔数据帧和遥控帧的帧。数据帧和遥控帧可通过插入帧间隔将本帧与前面的任何帧(数据帧、遥控帧、错误帧、过载帧)分开。
过载帧和错误帧前不能插入帧间隔

间隔
3 个位的隐性位。
总线空闲
隐性电平,无长度限制(0 亦可)。本状态下,可视为总线空闲,要发送的单元可开始访问总线。
延迟传送(发送暂时停止)
8 个位的隐性位。只在处于被动错误状态的单元刚发送一个消息后的帧间隔中包含的段。
CAN优先级
优先级的确定
在总空闲态,最先开始发送消息的单元获得发送权。
多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。
仲裁的过程如图所示: 前面的单元1和单元电平都一样,到后面单元1是隐性电平,单元2是显性电平,显性优先级更高,则单元2的优先级更高,获得发送权,而单元1则变为接收状态。

数据帧和遥控帧的优先级
具有相同 ID 的数据帧和遥控帧在总线上竞争时,仲裁段的最后一位(RTR)为显性位的数据帧具有优先权,可继续发送。

标准格式和拓展格式的优先级
标准格式 ID 与具有相同 ID 的遥控帧或者扩展格式的数据帧在总线上竞争时,标准格式的 RTR 位为显性位的具有优先权,可继续发送。 标准格式和扩展格式的仲裁过程如图 31 所示。

优先级法则
数据帧和遥控帧的仲裁段用于多个节点竞争总线时进行仲裁,优先级高的帧获得再总线上发送数据的权利。优先级的确认总结为以下几条法则:
- 在总线空闲时,最先开始发送消息的节点获得发送权;
- 多个节点同时开始发送时,从仲裁段的第一位开始进行仲裁,第一次出现各节点的位电平互异时,输出显性电平的节点获得发送权;
- 相同ID和格式的数据帧和遥控帧,数据帧具有更高优先级,因为数据中的RTR位时显性电平,而遥控帧的RTR位时显性电平;
- 对于11位标准ID相同的标志数据帧和扩展数据帧,标准数据帧具有更高的优先级,因为标志数据帧的IDE位时显性电平,而扩展数据帧的IDE位时隐性电平。
错误
错误共有 5 种。多种错误可能同时发生。
- 位错误
- 填充错误
- CRC 错误
- 格式错误
- ACK 错误 错误的种类、错误的内容、错误检测帧和检测单元如表

位错误
- 位错误由向总线上输出数据帧、遥控帧、错误帧、过载帧的单元和输出 ACK 的单元、输出错误的单元来检测。
- 在仲裁段输出隐性电平,但检测出显性电平时,将被视为仲裁失利,而不是位错误。
- 在仲裁段作为填充位输出隐性电平时,但检测出显性电平时,将不视为位错误,而是填充错误。
- 发送单元在 ACK 段输出隐性电平,但检测到显性电平时,将被判断为其它单元的 ACK 应答,而非位错误。
- 输出被动错误标志(6 个位隐性位)但检测出显性电平时,将遵从错误标志的结束条件,等待检测出连续相同 6 个位的值(显性或隐性),并不视为位错误。
格式错误
- 即使接收单元检测出 EOF(7 个位的隐性位)的最后一位(第 8 个位)为显性电平,也不视为格式错误。
- 即使接收单元检测出数据长度码(DLC)中 9∼15 的值时,也不视为格式错误。
错误帧的输出
检测出满足错误条件的单元输出错误标志通报错误。处于主动错误状态的单元输出的错误标志为组东错误标志;处于被动错误状态的单元输出的错误标识为被动错误标识。发送单元发送错误帧后,讲再次发送数据帧和遥控帧

位时序
由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位可分为 4 段。
- 同步段(SS)
- 传播时间段(PTS)
- 相位缓冲段 1(PBS1)
- 相位缓冲段 2(PBS2) 这些段又由可称为 Time Quantum(以下称为 Tq)的最小时间单位构成。
1 位分为 4 个段,每个段又由若干个 Tq 构成,这称为位时序。
1 位由多少个 Tq 构成、每个段又由多少个 Tq 构成等,可以任意设定位时序。通过设定位时序,多个单元可
同时采样,也可任意设定采样点。
各段的作用和 Tq 数如表7 所示。1 个位的构成如图 32 所示。


同步
CAN 协议的通信方法为 NRZ(Non-Return to Zero)方式。各个位的开头或者结尾都没有附加同步信号。发送单元以与位时序同步的方式开始发送数据。另外,接收单元根据总线上电平的变化进行同步并进行接收工作。
但是,发送单元和接收单元存在的时钟频率误差及传输路径上的(电缆、驱动器等)相位延迟会引起同步偏差。因此接收单元通过硬件同步或者再同步的方法调整时序进行接收。
硬件同步
接收单元在总线空闲状态检测出帧起始时进行的同步调整。在检测出边沿的地方不考虑 SJW 的值而认为是 SS 段。

再同步
在接收过程中检测出总线上的电平变化时进行的同步调整。 每当检测出边沿时,根据 SJW 值通过加长 PBS1 段,或缩短 PBS2 段,以调整同步。但如果发生了超出 SJW 值的误差时,最大调整量不能超过 SJW 值。

调整同步的规则
硬件同步和再同步遵从如下规则。
- 1 个位中只进行一次同步调整。
- 只有当上次采样点的总线值和边沿后的总线值不同时,该边沿才能用于调整同步。
- 在总线空闲且存在隐性电平到显性电平的边沿时,则一定要进行硬件同步。
- 在总线非空闲时检测到的隐性电平到显性电平的边沿如果满足条件(1)和(2),将进行再同步。但还要 满足下面条件。
- 发送单元观测到自身输出的显性电平有延迟时不进行再同步。
- 发送单元在帧起始到仲裁段有多个单元同时发送的情况下,对延迟边沿不进行再同步。
驱动分析
probe函数
数据结构
struct net_device *ndev; //创建网络设备
struct rockchip_can *rcan; //创建real can结构体
struct resource *res; //获取设备树硬件资源
struct __iomem *addr; //物理地址映射的虚拟地址
int err,irq; //错误处理、中断申请
中断线申请
CAN帧发送完成,CAN帧接收完成,CAN控制器发生错误时候中断
irq = platform_get_irq(pdev, 0);
num = 0;表示第一个中断,只是个编号,防止中断串了
错误处理
如果中断申请成功 irq >= 0
if(irq < 0)
{
dev_err(&pdev->dev,"could not get a valid irq\n");//给当前设备打印一条error级别的日志。
return -ENODEV;// No such device 设备不存在
}
设备树硬件资源获取
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//从当前平台设备pdev中,获取第一段寄存器地址资源
- pdev: the current platform device
- IORESOURCE_MEM:request a memoy I/O resource, ususally device devicee register
- 0: resource index 0, meaning the first one get resouce from device tree:
can@fe57000 {
reg = <0x00 0xfe570000 0x0 0x1000>;
}
just get first address res is a adress
addr = devm_ioremap_resource(&pdev->dev, res);
It maps the physical register range in res into a kernel virtual address.
- &pdev -> dev: the device that owns this mapping
- res: the MMIO resource obtained form platform_get_resource
- error handling:
if (IS_ERR(addr))// IS_ERR checks whether addr is an enconded error pointer
return -EBUSY;// resource busy
Allocate a network device
net = alloc_candev(sizeof(struct rockchip_can), 1);
function prototype:
#define alloc_candev(sizeof_priv, echo_skb_max) \
alloc_candev_mqs(sizeof_priv, echo_skb_max, 1, 1)
1:allocates one CAN echo buffer, mening one frame can be pending for transmission skb: socket buffer
- error handing: nedev success: return 0 or a valid pointer
if (!ndev) {
dev_err(&pdev->dev, "could not allocate memory for CAN device\n");
return -ENOMEM;// not enough memory
}
rcan resource allocation
ndev->netdev_ops = &rockchip_can_netdev_ops;//assigns the CAN network opration callbacks
ndev->irq = irq;
ndev->flags |= IFF_ECHO; //enable CAN frame echo support
rcan = netdev_priv(ndev);//get the driver-private data allocated by alloc_candv , points to rochip_can
rochip_can is dirver private data struct
- register
- clocks
- reset control
- CAN state
register interrupt handler
register the CAN interrupt handler
err = dev_request_irq(&pdev->dev, ndev->irq, rockchip_can_interrupt, 0, ndev->name, ndev);
- &pdev->dev: device thar owns the IRQ resouce
- ndev->irq: Linux IRQ number obtained from platform_get_irq
- rockchip_can_interrupt: function called when the interrupt occrus
- 0: no special IRQ flags
- ndev->name: interrupt name, usually shown in /proc/interrupts
- ndev: passed to the handler as dev_id dev_request_irq is devm_request_threaded_irq
- error handing:
if (err) {
dev_err(&pdev->dev, "request_irq err: %d\n", err);
return err;
}
Device Tree relationship:
interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
Flow:
Device Tree interrupts -> platform_get_irq() -> ndev->irq -> devm_requets_irq() -> rockchip_can_interrupt()
reset handler
rcan->reset = devm_reset_control_array_get(&pdev->dev, &rcan—>clks);
it gets all reset lines assigned to the CAN device.
- first false: request exclusive reset control
- sencond false: reset resource is mandatory, not optional Device Tree:
can@fe570000 {
resets = <&cru SRST_CAN0>, <&cru SRST_P_CAN0>;
reset-names = "can", "pclk";
};
Error handling:
if (IS_ERR(rcan->reset)) {
if (PTR_ERR(rcan->reset) != -EPROBE_DEFER)//EPROBE_DEFER reset controller is not ready yet; the kenrnel should retry probe() later
dev_err(&pdev->dev, "failed to get rcan reset lines\n");
return PTR_ERR(rcan->reset);//extracts the actual negative error code
}
clk handler
rcan->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rcan->clks);
&pdev->dev: current CAN device&rcan->clks: stores the clock array- Return value: number of clocks found, or a negative error code Device Tree example:
clocks = <&cru CLK_CAN0>, <&cru PCLK_CAN0>;
clock-names = "can", "pclk";
- error handle:
if (rcan->num_clks < 1) {
dev_err(&pdev->dev, "bus clock not found\n");
return -ENODEV;
}
initilize the driver-private structure
This block initializes the driver-private CAN structure after resources have been obtained.
rcan->dev = &pdev->dev; // store the device pointer for runtime power management and logging
rcan->can.clock.freq = clk_get_rate(rcan->clks[0].clk);// gets the CAN controller clock frequency.SocketCAN use it to calculate the CAN bitrate
rcan->can.bittiming_const = &rockchip_can_bittiming_const;// proveides the hardware bit-timing limits, such as BRP, TSEG1, TSEG2, and SJW ranges
rcan->can.do_set_mode = rockchip_can_set_mode;//register the callback used to start or restart the CAN controller
rcan->can.do_get_berr_counter = rockchip_can_get_berr_counter;// registr calllback used to read the
rcan->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING |
CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_3_SAMPLES;//tells SocketCAN which modes this controller supports:- Bus-error reporting - Listen-only - Loopback - Triple sampling
rcan->base = addr;//Stores the mapped register base address for later `readl()` and `writel()` operations.
connect the platform device to the CAN network device
platform_set_drvdata(pdev, ndev);
stores ndev inside pdev.
SET_NETDEV_DEV(ndev, &pdev->dev);
Sets the platform device aas the parent of CAN network device
Enable runtime power management
pm_runtime_enable(&pdev->dev);
err = pm_runtime_get_sync(&pdev->dev);
- error handling
if (err < 0) {
dev_err(&pdev->dev, "%s: pm_runtime_get failed(%d)\n",
__func__, err);
goto err_pmdisable;
}
err = register_candev(ndev);
if (err) {
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
DRV_NAME, err);
goto err_disableclks;
}
pm_runtime_put(&pdev->dev);
err_disableclks:
pm_runtime_put(&pdev->dev); //
err_pmdisable:
pm_runtime_disable(&pdev->dev);
free_candev(ndev);
return err;
ops binding
static const struct net_device_ops rockchip_can_netdev_ops = {
.ndo_open = rockchip_can_open,
.ndo_stop = rockchip_can_close,
.ndo_start_xmit = rockchip_can_start_xmit,
};
rockchip_can_open
data:
struct rockchip_can *rcan = netdev_priv(ndev);
int err;
It gets the driver-private data associated with ndev
common open
open device:
err = open_candev(ndev);
if(err)
return err;
runtime_sync
err = pm_runtime_get_sync(rcan_>dev);
if (err < 0) {
netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",__func__, err);
goto exit;
}
start:
err = rockchip_can_start(ndev);
if (err) {
netdev_err(ndev, "could not start CAN peripheral\n");
goto exit_can_start;
}
send ready:
netif_start_queue(ndev);
netdev_dbg(ndev, "%s\n", __func__);
Mark the network transmit queue as ready, allowing the networking stack to send packets to this device.
__func__: automatically expands to the current function name
rockchip_can_start
get resource
struct rockchip_can *rcan = netdev_priv(ndev);
enter the reset mode
set_reset_mode(ndev);
writel(0, rcan->base + CAN_INT_MASK);
write 0 to the interrupt-mask register. in this constroller:
mask bit = 0 → interrupt enabled
mask bit = 1 → interrupt disabled
rceiving filter accept all
writel(0, rcan->base + CAN_ID);//Sets the receive-filter reference ID to `0`.
writel(CAN_RX_FILTER_MASK, rcan->base + CAN_ID_MASK);//set all 29 CAN ID mask bits
set bittiming
rockchip_can_set_bittiming(ndev);
set normal mode
set_normal_mode(ndev);
configurate error state
rcan->can.state = CAN_STATE_ERROR_ACTIVE;
netdev_dbg(ndev, "%s\n", __func__);
rockchip_can_set_bittiming
get resource
struct rockchip_can *rcan = netdev_priv(ndev);
struct can_bittimg *bt = &rcan->can.bittiming;
u32 cfg;
packs register value CFG
cfg is the 32-bit vlalue wrtitten into the CAN bit-timing register
cfg = ((bt->sjw - 1) << BT_SJW_SHIFT) |
(((bt->brp >> 1) - 1) << BT_BRP_SHIFT) |
((bt->phase_seg2 - 1) << BT_TSEG2_SHIFT) |
((bt->prop_seg + bt->phase_seg1 - 1));
if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
cfg |= MODE_3_SAMPLES;
Filed meaning:
SJW → bits 15:14
BRP → bits 13:8
TSEG2 → bits 6:4
TSEG1 → bits 3:0
bit 16 15:14 13:8 6:4 3:0
+--------+----------+----------+----------+----------+
| SAM | SJW | BRP | TSEG2 | TSEG1 |
+--------+----------+----------+----------+----------+
bit 16:Triple sampling
MODE_3_SAMPLES
0:sample once
1:sanple three times
bit 15-14: SJW
(bt->sjw - 1) << BT_SJW_SHIFT
It defines the maximun number of Time Quanta(TQ) that CAN controller may add or remove resynchronization.
bits 13:8: BRP
((bt->brp >> 1) - 1) << BT_BRP_SHIFT
BRP divides the CAN input clock to generate one Time Quantum, TQ.
bits 6:4: TSEG2
(bt->phase_seg2 - 1) << BT_TSEG2_SHIFT
TSEG2 is the time after the sampling point:
TSEG2 = Phase_Seg2
bits 3:0: TSEG1
bt->prop_seg + bt->phase_seg1 - 1
TSEG1 is the time before the sampling point:
TSEG1 = Prop_Seg + Phase_Seg1
write register
writel(cfg, rcan->base + CAN_BTT);
netdev_dbg(ndev, "setting BITTIMING=0x%08x brp: %d bitrate:%d\n",
cfg, bt->brp, bt->bitrate);
set_nromal_mode
val = readl(rcan->base + CAN_MODE);
val |= WORK_MODE | MODE_AUTO_RETX;
writel(val, rcan->base + CAN_MODE);
read-modify-wirte operation
rockchip_can_start_xmit
get resources
struct rockchip_can *rcan = netdev_priv(ndev);
struct can_frame *cf = (struct can_frame *)skb->data;//treats the packet data inside skb as a CAN frame
canid_t id;
u8 dlc;
u32 fi;
u32 data1 = 0, data2 = 0;
check_frame init
if (can_dropped_invalid_skb(ndev, skb))
return NETDEV_TX_OK;
Gets the driver-private CAN data associated with ndev.
If invalid:
- the frame is dropped
- no hardware transmission occurs
NETDEV_TX_OKtells the network stack that the driver has handled the packet
netif_stop_queue(ndev);
Stops the transmit queue temporarily.
assgin transmit data
id = cf->can_id;
dlc = cf->can_dlc;
fi = dlc;
check Reomote Transmission Request(RTR) frame
if(id & CAN_RTR_FLAG) {
fi |= CAN_RTR;
fi &= ~CAN_DLC_MASK;
}
id & CAN_RTR_FLAG: test the RTR flag in can_idfi |= CAN_RTR:set the hardware RTR bitfi &= ~CAN_DLC_MASK: clear the DLC field because this driver sends no data bytes for an RTR frame
if (id & CAN_EFF_FLAG)
fi |= CAN_EFF;
Checks whether this is an extended CAN frame.
CAN_EFF_FLAG: flag inside Linuxcan_idCAN_EFF: corresponding hardware bit inCAN_TX_FRM_INFO
send
clears the previous transmit command:
rockchip_can_write_cmdreg(rcan, 0);
wirtes the CAN ID into the TX ID register:
if (!(id & CAN_RTR_FLAG)) { //only normal data frames contain payload. RTR frames do not write data
data1 = le32_to_cpup((__le32 *)&cf->data[0]);
data2 = le32_to_cpup((__le32 *)&cf->data[4]);//Converts the 8-byte payload into two 32-bit values
writel(data1, rcan->base + CAN_TX_DATA1);
writel(data2, rcan->base + CAN_TX_DATA2);// Writes the payload into the hardware TX data registers.
}
It reads a 32-bit little-endian value from a pointer and converts it to the CPU’s native byte order:
le32_to_cpup((__le32 *)&cf->data[0])
le32: little-endian 32-bit valueto_cpu: convert to CPU byte orderp: input is a pointer
wirte frame infomation
writel(fi, rcan->base + CAN_TX_FRM_INFO);
- DLC
- RTR flag
- extended-frame flag
Stores the transmitted skb
rockchip_can_write_cmdreg(rcan, TX_REQ);
Sets the transmit-request bit and tells the CAN controller to start sending the frame.
debug
netdev_dbg(ndev, "TX: can_id:0x%08x dlc: %d mode: 0x%08x data: 0x%08x 0x%08x\n",cf->can_id, cf->can_dlc, rcan->can.ctrlmode, data1, data2);
return NETDEV_TX_OK; 