Linux应用开发【第十四章】CAN编程应用开发
时间:2023-07-16 04:37:00
文章目录
- 14 CAN编程应用开发
-
- 14.1 CAN介绍
-
- 14.1.1 CAN是什么?
- 14.1.2 CAN的起源
- 14.1.3 CAN传输模型
- 14.1.4 CAN网络拓扑
- 14.1.5 CAN物理特性
- 14.1.6 CAN报文帧
-
- 14.1.6.1 CAN报文格式
- 14.1.6.2 CAN报文帧类型
- 14.2 CAN创建编程框架
- 14.3 STM32 CAN应用编程
-
- 14.3.1 STM32 CAN接口电路
- 14.3.2 STM32 CAN应用编程步骤
-
- 14.3.2.1准备STM32工程模版
- 14.3.2.2 编写CAN实现抽象框架的函数
- 14.3.2.3 编写CAN应用层代码
- 14.3.2.4 STM32 CAN案例测试
- 14.4 Linux socketcan基本应用编程
-
- 14.4.1 socketcan概述
- 14.4.2 socketcan基本知识点
-
- 14.4.2.1 CAN设备操作
- 14.4.2.2 什么是Socket套接口
- 14.4.2.3 Socket接口函数
- 14.4.3 socket_can简单发送实例
-
- 14.4.3.1编写抽象框架实现函数
- 14.4.3.2 编写应用层代码
- 14.4.3.3 案例测试验证
- 14.4.4 socket_can 简单接收实例
-
- 14.4.4.1 编写抽象框架的函数
- 14.4.4.编写应用层代码
- 14.4.4.3 案例测试验证
- 14.4.5 socket_can 接收和发送实例
-
- 14.4.5.1 编写抽象框架的函数
- 14.5 汽车行业CAN总线应用
-
- 14.5.1 车厂CAN总线需求
-
- 14.5.1.1 网络拓扑结构
- 14.5.1.2 CAN 报文分类
- 14.5.2 CAN 应用报文应用分析及实例
-
- 14.5.2.1 CAN 应用报文定义
- 14.5.2.2 CAN应用报文发送规则
- 14.5.2.3 汽车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 已成为国际标准 ISO和 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的 | ||
---|---|---|
序号 | 层次 | 描述 |
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标准。
序号 | 标准 | 描述 |
---|---|---|
1 | ISO11898-1 | 数据链路层和物理层信号 |
2 | ISO11898-2 | 高速接入单元 |
3 | ISO11898-3 | 低速容错接入单元 |
4 | ISO11898-4 | 时间触发通讯 |
5 | ISO11898-5 | 低功耗的接入单元 |
6 | ISO11898-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收发器有如下几种型号:
序号 | 型号 | 描述 |
---|---|---|
1 | PCA82C250 | 高速 CAN 收发器 |
2 | PCA82C251 | 高速 CAN 收发器 |
3 | PCA82C252 | 容错 CAN 收发器 |
4 | TJA1040 | 高速 CAN 收发器 |
5 | TJA1041 | 高速 CAN 收发器 |
6 | TJA1042 | 高速 CAN 收发器 |
7 | TJA1043 | 高速 CAN 收发器 |
8 | TJA1050 | 高速 CAN 收发器 |
9 | TJA1053 | 容错 CAN 收发器 |
10 | TJA1054 | 容错 CAN 收发器 |
目前实际常用的CAN控制器有如下几种型号:
序号 | 型号 | 描述 |
---|---|---|
1 | SJA1000 | 独立CAN控制器 |
2 | MCU内部控制器 | 目前市面上如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.0A | CAN2.0B |
CAN ID(标识符)长度 | 11 bits | 29 bits |
CAN ID(标识符)范围 | 0x000~0x7FF | 0x00000000~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