Linux应用开发第十四章CAN编程应用开发

Posted 韦东山

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux应用开发第十四章CAN编程应用开发相关的知识,希望对你有一定的参考价值。

14 CAN编程应用开发

14.1 CAN介绍

14.1.1 CAN是什么?

​ CAN,全称为“Controller Area Network”,即控制器局域网,是国际上应用最广泛的现场总线之一。

最初,CAN 被设计作为汽车环境中的微控制器通讯,在车载各电子控制装置 ECU 之间交换信息,形成汽车

电子控制网络。比如:发动机管理系统、变速箱控制器、仪表装备、电子主干系统中,均嵌入 CAN 控制装

置。

​ 一个由 CAN 总线构成的单一网络中,理论上可以挂接无数个节点。实际应用中,节点数目受网络硬件

的电气特性所限制。例如,当使用 Philips P82C250 作为 CAN 收发器时,同一网络中允许挂接 110 个节点。

CAN 可提供高达 1Mbit/s 的数据传输速率,这使实时控制变得非常容易。另外,硬件的错误检定特性也增

强了 CAN 的抗电磁干扰能力。

14.1.2 CAN的起源

​ CAN 最初出现在 80 年代末的汽车工业中,由德国 Bosch 公司最先提出。当时,由于消费者对于汽车功

能的要求越来越多,而这些功能的实现大多是基于电子操作的,这就使得电子装置之间的通讯越来越复杂,

同时意味着需要更多的连接信号线。提出 CAN 总线的最初动机就是为了解决现代汽车中庞大的电子控制装

置之间的通讯,减少不断增加的信号线。于是,他们设计了一个单一的网络总线,所有的外围器件可以被

挂接在该总线上。1993 年,CAN 已成为国际标准 ISO11898(高速应用)和 ISO11519(低速应用)。

CAN 是一种多主方式的串行通讯总线,基本设计规范要求有高的位速率,高抗电磁干扰性,而且能够检

测出产生的任何错误。当信号传输距离达到 10Km 时,CAN 仍可提供高达 50Kbit/s 的数据传输速率。

由于 CAN 总线具有很高的实时性能,因此,CAN 已经在汽车工业、航空工业、工业控制、安全防护等领

域中得到了广泛应用。

14.1.3 CAN传输模型

​ CAN 通讯协议主要描述设备之间的信息传递方式。CAN 层的定义与开放系统互连模型(OSI)一致。每

一层与另一设备上相同的那一层通讯。实际的通讯发生在每一设备上相邻的两层,而设备只通过模型物理

层的物理介质互连。CAN 的规范定义了模型的最下面两层:数据链路层和物理层。下表中展示了 OSI 开放

式互连模型的各层。应用层协议可以由 CAN 用户定义成适合特别工业领域的任何方案。已在工业控制和制

造业领域得到广泛应用的标准是 DeviceNet,这是为 PLC 和智能传感器设计的。在汽车工业,许多制造商

都应用他们自己的标准。

表格 OSI开发系统互联模型
序号层次描述
7应用层最高层。用户、软件、网络终端等之间用来进行信息交换。
6表示层将两个应用不同数据格式的系统信息转化为能共同理解的格式
5会话层依靠低层的通信功能来进行数据的有效传递。
4传输层两通讯节点之间数据传输控制。操作如:数据重发,数据错误修复
3网络层规定了网络连接的建立、维持和拆除的协议。如:路由和寻址
2数据链路层规定了在介质上传输的数据位的排列和组织。如:数据校验和帧结构
1物理层规定通讯介质的物理特性。如:电气特性和信号交换的解释

​ 虽然CAN传输协议参考了OSI 七层模型,但是实际上CAN协议只定义了两层“物理层”和“数据链路层”,因此出现了各种不同的“应用层”协议,比如用在自动化技术的现场总线标准DeviceNet,用于工业控制的CanOpen,用于乘用车的诊断协议OBD、UDS(统一诊断服务,ISO14229),用于商用车的CAN总线协议SAEJ1939.

表格 CAN的
序号层次描述
7应用层主要定义CAN应用层。
2数据链路层数据链路层分为逻辑链接控制子层LLC和介质访问控制子层MAC。
MAC 子层是 CAN 协议的核心。它把接收到的报文提供给 LLC 子
层,并接收来自 LLC 子层的报文。 MAC 子层负责报文分帧、仲
裁、应答、错误检测和标定。MAC 子层也被称作故障界定的管理
实体监管 LLC 子层涉及报文滤波、过载通知、以及恢复管理。
LLC = Logical Link Control MAC = Medium Access Control
1物理层物理层,为物理编码子层PCS. 该层定义信号是如何实际地传输
的,因此涉及到位时间、位编码、同步。

14.1.4 CAN网络拓扑

​ CAN总线是一种分布式的控制总线。

​ CAN总线作为一种控制器局域网,和普通以太网一样,它的网络很多CAN节点构成。

其网络拓扑结构如下图所示:

​ CAN网络的每个节点非常简单,均由一个MCU(微控制器)、一个CAN控制器和一个CAN收发器构成,然后使用双绞线连接到CAN网络中。

14.1.5 CAN物理特性

​ CAN总线遵循国际标准ISO11898,如ISO11898-1,ISO11898-2,ISO11898-3,ISO11898-4标准。

序号标准描述
1ISO11898-1数据链路层和物理层信号
2ISO11898-2高速接入单元
3ISO11898-3低速容错接入单元
4ISO11898-4时间触发通讯
5ISO11898-5低功耗的接入单元
6ISO11898-6选择性唤醒的高速接入单元

CAN 能够使用多种物理介质,例如双绞线、光纤等。最常用的就是双绞线。

信号使用差分电压传送,两条信号线被称为“CAN_H”和“CAN_L”。

静态时CAN_H和CAN_L均是 2.5V 左右,此时状态表示为逻辑“1”,也可以叫做 “隐性”。

用 CAN_H 比 CAN_L 高表示逻辑“0”,称为“显形”,此时,通常电压值为:CAN_H = 3.5V 和 CAN_L

= 1.5V 。

目前实际常用的CAN收发器有如下几种型号:

序号型号描述
1PCA82C250高速 CAN 收发器
2PCA82C251高速 CAN 收发器
3PCA82C252容错 CAN 收发器
4TJA1040高速 CAN 收发器
5TJA1041高速 CAN 收发器
6TJA1042高速 CAN 收发器
7TJA1043高速 CAN 收发器
8TJA1050高速 CAN 收发器
9TJA1053容错 CAN 收发器
10TJA1054容错 CAN 收发器

目前实际常用的CAN控制器有如下几种型号:

序号型号描述
1SJA1000独立CAN控制器
2MCU内部控制器目前市面上如STM32系列,S32K系列,IMX6系列等等很多单片机均内部集成了CAN控制。

14.1.6 CAN报文帧

14.1.6.1 CAN报文格式

标准 CAN 的标志符长度是 11 位,而扩展格式 CAN 的标志符长度可达 29 位。

CAN 协议的 2.0A 版本 规定 CAN 控制器必须有一个 11 位的标志符。

同时,在 2.0B 版本中规定,CAN 控制器的标志符长度可以是 11 位或 29 位。

遵循 CAN2.0B 协议的 CAN 控制器可以发送和接收 11 位标识符的标准格式报文或 29 位标识符的扩展格式报文。

标准帧&扩展帧对比
帧格式标准帧扩展帧
规范CAN2.0ACAN2.0B
CAN ID(标识符)长度11 bits29 bits
CAN ID(标识符)范围0x000~0x7FF0x00000000~0x1FFFFFFF

14.1.6.2 CAN报文帧类型

CAN报文类型又分如5种帧类型:

数据帧:主要用于发送方向接收方传输数据的帧;

遥控帧:主要用于接收方向具有相同ID的发送方请求数据的帧;

错误帧:主要用于当检测出错误时向其他节点通知错误的帧。

过载帧:主要用于接收方通知其他尚未做好接收准备的帧。

间隔帧:主要用于将数据帧及遥控帧与前一帧分隔开来的帧。

其中数据帧是使用最多的帧类型,这里重点介绍以下数据帧。

数据帧如下图所示:

由上图所示,数据帧包括:

(1)帧起始。表示数据帧开始的段。

(2)仲裁段。表示该帧优先级的段。

(3)控制段。表示数据的字节数及保留位的段。

(4)数据段。数据的内容,一帧可发送0~8个字节的数据。

(5)CRC段。检查帧的传输错误的段。

(6)ACK段。表示确认正常接收的段。

(7)帧结束。表示数据帧结束的段。

具体介绍可以查看”CAN2.0A”、”CAN2.0B”详细介绍。

我们主要关注我们编程所需要关注的几个段:

ID: CAN报文ID;

IDE: 为0是标准帧,为1是扩展帧;

RTR: 为0是数据帧,为1是远程帧;

DLC: CAN报文数据长度,范围0~8字节;

Data:数据,0~8个字节;

14.2 CAN编程框架创建

当前我们所学习的是应用编程,为了以后CAN编程框架的通用性和可移植性,我们创建一个抽象的CAN应用编程框架,此框架可以适用于单片机应用编程,也可以适用于linux应用编程。

因此,根据CAN总线编程的通用属性,我们抽象出如下属性:

属性属性描述说明
CAN端口号描述CAN端口,如CAN1,CAN2,CAN3,与具体硬件外设有关。
CAN收发器配置描述CAN收发器模式设置,收发器模式有Normal,Stanby,
Sleep,ListenOnly等模式; 本章节所使用的收发器是硬件默
认配置,因此不需要配置。
CAN控制器配置描述CAN收发器配置,如CAN波特率配置,采样率设置,过
滤器设置等;
CAN中断配置描述CAN中断接收函数配置
读取CAN报文描述CAN读取报文实现
发送CAN报文描述CAN发送报文实现

根据上面表格所描述的属性,创建CAN应用编程框架如下:

typedef struct _CAN_COMM_STRUCT

    /* CAN硬件名称 */
    char name[10];
    /* CAN端口号,裸机里为端口号;linux应用里作为socket套接口 */
    int  can_port;                                
    /* CAN控制器配置函数,返回端口号赋值给can_port */
    int  (*can_set_controller)( void );                  
    /* CAN接口中断创建,在linux中对应创建接收线程 */
    void (*can_set_interrput)( int can_port , pCanInterrupt callback );             
    /* CAN读取报文接口 */
    void (*can_read)( int can_port , CanRxMsg* recv_msg);   
    /* CAN发送报文接口*/
    void (*can_write)( int can_port , CanTxMsg send_msg);   
CAN_COMM_STRUCT, *pCAN_COMM_STRUCT;

此框架可以用类比套用在单片机上,也可以使用在linux socketcan应用编程上。

14.3 STM32 CAN应用编程

本节主要使用14.2中的应用编程框架,在单片机上试验框架的可行性,以一个基本的接收和发送的案例来做讲解;

14.3.1 STM32 CAN接口电路

如下图所示,为本章STM32例程所使用的开发板STM32最小系统和CAN收发器接口电路。

图14.3.1-1 STM32F407最小系统

图14.3.1-1 TJA1050 CAN收发器接口电路

14.3.2 STM32 CAN应用编程步骤

下面我们按照CAN通信的编程框架来一步一步实现基于STM32的CAN应用编程。

STM32 CAN应用编程,步骤如下:

14.3.2.1准备STM32工程模版

请参见第14章节代码“01_stm32f407_can”例程;

所使用的开发环境为:MDK 5.24.

打开MDK工程后,如下图所示:

上图中目录CMSIS, STM32F407_LIB,main均为STM32运行的基础框架。

目录app_can为CAN应用编程所需要的文件。

14.3.2.2 编写CAN抽象框架的实现函数

(1)定义CAN端口号

见第14章节代码“01_stm32f407_can_addline”中“can_controller.h”文件。

主要根据STM32硬件的CAN有多路,依次定义为CAN_PORTCAN1, CAN_PORT_CAN2等,从“14.3.1 STM32 CAN接口电路”可知道,当前使用的CAN1.

25 /* CAN端口号定义*/
26 enum
27 
28     CAN_PORT_NONE = 0,
29     CAN_PORT_CAN1,
30     CAN_PORT_CAN2,
31     CAN_PORT_MAX
32 ;

(2)配置CAN控制器

配置CAN控制器有3个部分:GPIO(CAN_TX,CAN_RX管脚)配置,CAN波特率配置,CAN过滤器配置。

见第14章节代码“01_stm32f407_can_addline”中“can_controller.c”文件int CAN_Set_Controller( void )函数。

A.GPIO(CAN_TX,CAN_RX管脚)配置

配置GPIO代码如下:

96     /*************************************************************/
97     /*CAN相关GPIO配置,此处为:CAN_TX, CAN_RX*/
98
99     /*使能GPIO时钟*/
100     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
101     /*初始化管脚配置*/
102     GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_0 ;
103     GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_AF;
104     GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
105     GPIO_InitStructure.GPIO_OType   = GPIO_OType_PP;
106     GPIO_InitStructure.GPIO_PuPd    = GPIO_PuPd_UP;
107     GPIO_Init(GPIOD, &GPIO_InitStructure);
108
109     GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_1;
110     GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_AF;
111     GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
112     GPIO_InitStructure.GPIO_OType   = GPIO_OType_PP;
113     GPIO_InitStructure.GPIO_PuPd    = GPIO_PuPd_UP;
114     GPIO_Init(GPIOD, &GPIO_InitStructure);
115     /*将GPIO设置为CAN复用模式*/
116     GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_CAN1);
117     GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_CAN1);

B.配置波特率,工作模式

按照如下代码,使能CAN外设,设置CAN工作模式为Normal,设置波特率为500kbps。

119     /*************************************************************/
120     /*CAN控制器相关配置,此处为波特率,采样率等*/
121
122     /* 使能CAN时钟 */
123     RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
124
125     /* 初始化CAN控制器工作模式*/
126     CAN_DeInit(CAN1);
127     CAN_StructInit(&CAN_InitStructure);
128     CAN_InitStructure.CAN_TTCM = DISABLE;
129     CAN_InitStructure.CAN_ABOM = DISABLE;
130     CAN_InitStructure.CAN_AWUM = DISABLE;
131     CAN_InitStructure.CAN_NART = DISABLE;
132     CAN_InitStructure.CAN_RFLM = DISABLE;
133     CAN_InitStructure.CAN_TXFP = DISABLE;
134     CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;//CAN工作模式
135
136     /* 初始化CAN波特率 */
137     CAN_Baud_Process(500,&CAN_InitStructure);
138     CAN_Init(CAN1, &CAN_InitStructure);

其中配置波特率的函数是一个自定义函数,这里可以不了解,只需要知道是配置波特率即可,如果需要使用本章代码,可以查看具体的源码工程。

C. 配置CAN过滤器

如下代码为配置过滤器:

141     /*************************************************************/
142     /* 初始化CAN过滤器 */
143     CAN_FilterInitStructure.CAN_FilterNumber = 0;                       /* CAN1滤波器号从0到13 */
144     CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;     /* 滤波屏蔽模式 */
145     CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
146     CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
147     CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
148     CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;      /* 不屏蔽任何ID */
149     CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;           /* 不屏蔽任何ID */
150     CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
151
152     CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
153     CAN_FilterInit(&CAN_FilterInitStructure);
154
155     /*************************************************************/
156     /* 设置完CAN后,返回当前设置的CAN的端口号,此处主要类比linux socketcan中的套接口 */

此处我们设置过滤器不屏蔽任何报文ID,这里只是了解单片机下的一些过程。

(3)配置CAN接收中断

CAN总线支持发送中断和接收中断,此处仅仅使用了接收中断。

见第14章节代码“01_stm32f407_can_addline”中“can_controller.c”文件void CAN_Set_Interrupt(int can_port, pCanInterrupt callback)函数。

CAN中断配置代码如下:

163 /**********************************************************************
164 * 函数名称: void CAN_Set_Interrupt(int can_port,  pCanInterrupt callback)
165 * 功能描述: 使能CAN中断处理,并传入应用的的回调函数,回调函数主要处理应用层的功能
166 * 输入参数: can_port,端口号
167 *            callback: 中断具体处理应用功能的回调函数
168 * 输出参数: 无
169 * 返 回 值: 无
170 * 修改日期             版本号        修改人           修改内容
171 * -----------------------------------------------
172 * 2020/05/13         V1.0             bert            创建
173 ***********************************************************************/
174 void CAN_Set_Interrupt(int can_port,  pCanInterrupt callback)
175 
176     NVIC_InitTypeDef NVIC_InitStructure;
177
178     /* 根据CAN端口号配置中断 */
179     switch( can_port )
180     
181         case CAN_PORT_CAN1:
182         
183             /* 初始化回调接口函数 */
184             if ( NULL != callback )
185             
186                 g_pCanInterrupt = callback;
187             
188
189             /* 使用CAN0_RX中断,在linux socket can中类似创建接收线程 */
190             NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
191             NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
192             NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
193             NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
194             NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
195             NVIC_Init(&NVIC_InitStructure);
196             CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
197         
198         break;
199
200         default:
201             break;
202
203     
204     return ;
205 

CAN接收中断函数如下:

275 /**********************************************************************
276 * 函数名称: void CAN1_RX0_IRQHandler(void)
277 * 功能描述: CAN接收中断函数
278 * 输入参数: 无
279 * 输出参数: 无
280 * 返 回 值: 无
281 * 修改日期             版本号        修改人           修改内容
282 * -----------------------------------------------
283 * 2020/05/13         V1.0             bert            创建
284 ***********************************************************************/
285 void CAN1_RX0_IRQHandler(void)
286 
287     /* 如果回调函数存在,则执行回调函数 */
288     if( g_pCanInterrupt != NULL)
289     
290         g_pCanInterrupt();
291     
292
293     /* 清除挂起中断 */
294     CAN_ClearITPendingBit(CAN1,CAN_IT_FMP0);
295 

此处CAN中断通过回调函数g_pCanInterrupt()函数将应用层需要的代码分层到应用层,此处为驱动部分通用接口。

(4)CAN报文读取函数

当CAN接收中断产生,通过CAN报文读取函数从FIFO中读取已经接收到的CAN报文。

见第14章节代码“01_stm32f407_can_addline”中“can_controller.c”文件void CAN_Read(int can_port, CanRxMsg* recv_msg)函数。

CAN报文读取函数如下:

208 /**********************************************************************
209 * 函数名称: void CAN_Read(int can_port, CanRxMsg* recv_msg)
210 * 功能描述: CAN读取接收寄存器,取出接收到的报文
211 * 输入参数: can_port,端口号
212 * 输出参数: recv_msg:接收报文
213 * 返 回 值: 无
214 * 修改日期             版本号        修改人           修改内容
215 * -----------------------------------------------
216 * 2020/05/13         V1.0             bert            创建
217 ***********************************************************************/
218 void CAN_Read(int can_port, CanRxMsg* recv_msg)
219 
220     switch( can_port )
221     
222         case CAN_PORT_CAN1:
223         
224             /* 从FIFO中读取CAN报文 */
225             CAN_Receive(CAN1,CAN_FIFO0, recv_msg);
226         
227         break;
228
229         default:
230             break;
231     
232     return ;
233 

(5)CAN报文发送函数

当需要发送CAN报文时,通过向CAN发送邮箱填充数据,启动发送报文。

见第14章节代码“01_stm32f407_can_addline”中“can_controller.c”文件void CAN_Write(int can_port, CanTxMsg send_msg)函数。

CAN报文读取函数如下:

235 /**********************************************************************
236 * 函数名称: void CAN_Write(int can_port, CanTxMsg send_msg)
237 * 功能描述: CAN报文发送接口,调用发送寄存器发送报文
238 * 输入参数: can_port,端口号
239 * 输出参数: send_msg:发送报文
240 * 返 回 值: 无
241 * 修改日期             版本号        修改人           修改内容
242 * -----------------------------------------------
243 * 2020/05/13         V1.0             bert            创建
244 ***********************************************************************/
245 void CAN_Write(int can_port, CanTxMsg send_msg)
246 
247     unsigned char i;
248     uint8_t transmit_mailbox = 0;
249     CanTxMsg TxMessage;
250
251     switch( can_port )
252     
253         case CAN_PORT_CAN1:
254         
255             TxMessage.StdId = send_msg.StdId;     // 标准标识符为0x000~0x7FF
256             TxMessage.ExtId = 0x0000;             // 扩展标识符0x0000
257             TxMessage.IDE   = CAN_ID_STD;         // 使用标准标识符
258             TxMessage.RTR   = CAN_RTR_DATA;       // 设置为数据帧
259             TxMessage.DLC   = send_msg.DLC;       // 数据长度, can报文规定最大的数据长度为8字节
260
261             for(i=0; i<TxMessage.DLC; i++)
262             
263                 TxMessage.Data[i] = send_msg.Data[i];
264             
265             transmit_mailbox = CAN_Transmit(CAN1,&TxMessage);  /* 返回这个信息请求发送的邮箱号0,1,2或没有邮箱申请发送no_box */
266         
267         break;
268
269         default:
270             break;
271     
272     return ;
273 

(6)CAN抽象结构体框架初始化

定义一个can1通信结构实例CAN_COMM_STRUCT can1_controller;

使用(1)~(5)步骤实现的函数,初始化can1_controller,构成与应用层关联的一个连接点。

298 /**********************************************************************
299 * 名称:     can1_controller
300 * 功能描述: CAN1结构体初始化
301 * 修改日期             版本号        修改人           修改内容
302 * -----------------------------------------------
303 * 2020/05/13         V1.0             bert            创建
304 ***********************************************************************/
305 CAN_COMM_STRUCT can1_controller = 
306     .name                   = "can0",
307     .can_port               = CAN_PORT_CAN1,
308     .can_set_controller     = CAN_Set_Controller,
309     .can_set_interrput      = CAN_Set_Interrupt,
310     .can_read               = CAN_Read,
311     .can_write              = CAN_Write,
312 ;

14.3.2.3 编写CAN应用层代码

根据14.3.2.2 已经将具体的CAN硬件操作已经实现,并且已经抽象实例化了CAN编程框架。

但是我们现在还没关联到应用层,应用层并不知道调用哪个接口。

(1)CAN应用层注册实例

在应用层编写一个通用的实例化注册函数。

见第14章节代码“01_stm32f407_can_addline”中“app_can.c”文件int register_can_controller(const pCAN_COMM_STRUCT p_can_controller)函数。

代码实现如下:

62 /**********************************************************************
63 * 函数名称: int register_can_controller(const pCAN_COMM_STRUCT p_can_controller)
64 * 功能描述: 应用层进行CAN1结构体注册
65 * 输入参数: p_can_controller,CAN控制器抽象结构体
66 * 输出参数: 无
67 * 返 回 值: 无
68 * 修改日期             版本号        修改人           修改内容
69 * -----------------------------------------------
70 * 2020/05/13         V1.0             bert            创建
71 ***********************************************************************/
72 int register_can_controller(const pCAN_COMM_STRUCT p_can_controller)
73 
74     /* 判断传入的p_can_controller为非空,目的是确认这个结构体是实体*/
75     if( p_can_controller != NULL )
76     
77         /* 将传入的参数p_can_controller赋值给应用层结构体gCAN_COMM_STRUCT */
78
79         /*端口号,类比socketcan套接口*/
80         gCAN_COMM_STRUCT.can_port              = p_can_controller->can_port;
81         /*CAN控制器配置函数*/
82         gCAN_COMM_STRUCT.can_set_controller    = p_can_controller->can_set_controller;
83         /*CAN中断配置*/
84         gCAN_COMM_STRUCT.can_set_interrput     = p_can_controller->can_set_interrput;
85         /*CAN报文读函数*/
86         gCAN_COMM_STRUCT.can_read              = p_can_controller->can_read;
87         /*CAN报文发送函数*/
88         gCAN_COMM_STRUCT.can_write             = p_can_controller->can_write;
89         return 1;
90     
91      return 0;
92 

然后通过调用register_can_controller( &can1_controller );将实例can1_controller注册给应用的4 static CAN_COMM_STRUCT gCAN_COMM_STRUCT;

之后应用层只需要调用应用层自己的gCAN_COMM_STRUCT实例即可操作CAN通信功能。

315 /**********************************************************************
316 * 函数名称: void CAN1_contoller_add(void)
317 * 功能描述: CAN结构体注册接口,应用层在使用can1_controller前调用
318 * 输入参数: 无
319 * 输出参数: 无
320 * 返 回 值: 无
321 * 修改日期             版本号        修改人           修改内容
322 * -----------------------------------------------
323 * 2020/05/13         V1.0             bert            创建
324 ***********************************************************************/
325 void CAN1_contoller_add(void)
326 
327     /*将can1_controller传递给应用层*/
328     register_can_controller( &can1_controller );
329 

(2)CAN应用层初始化

CAN应用层初始化代码如下;

94 /**********************************************************************
95 * 函数名称: void app_can_init(void)
96 * 功能描述: CAN应用层初始化
97 * 输入参数: 无
98 * 输出参数: 无
99 * 返 回 值: 无
100 * 修改日期             版本号        修改人           修改内容
101 * -----------------------------------------------
102 * 2020/05/13         V1.0             bert            创建
103 ***********************************************************************/
104 void app_can_init(void)
105 
106     /**
107     * 应用层进行CAN1结构体注册
108     */
109     CAN1_contoller_add();
110
111     /*
112     *调用can_set_controller进行CAN控制器配置,
113     *返回can_port,类比linux socketcan中的套接口,单片机例程中作为自定义CAN通道
114     */
115     gCAN_COMM_STRUCT.can_port = gCAN_COMM_STRUCT.can_set_controller();
116     /**
117     * 调用can_set_interrput配置CAN接收中断,类比socketcan中的接收线程
118     */
119     gCAN_COMM_STRUCT.can_set_interrput( gCAN_COMM_STRUCT.can_port, CAN_RX_IRQHandler_Callback );
120 

(3)设计一个简单的周期发送报文功能

CAN周期发送报文的功能代码实现如下:

123 /**********************************************************************
124 * 函数名称: void app_can

以上是关于Linux应用开发第十四章CAN编程应用开发的主要内容,如果未能解决你的问题,请参考以下文章

#父与子的编程之旅#第十四章

C Primer Plus(第六版)第十四章 编程练习答案

Python 3标准库 第十四章 应用构建模块

《OpenCL异构并行编程实战》补充笔记散点,第十二至十四章

第十四章:Python の Web开发基础

第十四章 网络编程