锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

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

时间:2023-07-16 04:37:00 can输出型传感器electronic光纤传感器抗干扰接近传感器ifl15

文章目录

  • 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 物理层 规定通信介质的物理特性。例如:解释电气特性和信号交换
p>​ 虽然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标准。

序号 标准 描述
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 

相关文章