1 绪论现场总线,就是应用于工业现场,采用总线方式连接多个设备,用于传输工业现场各种数据的一类通信系统[1]。CAN(Controller Area Network)总线是现场总线的一个分支,因其具有很高的可靠性和性能价格比,已经成为国际标准,在工业过程监控设备的互连方面得到广泛应用,受到工业界的广泛重视,并已被公认为几种最有前途的现场总线之一。
1.1 研究背景随着计算机硬件、软件技术及集成电路技术的迅速发展,工业控制系统已成为计算机技术应用领域中最具活力的一个分支,并取得了巨大进步。由于对系统可靠性和灵活性的高要求,工业控制系统的发展主要表现为:控制多元化,系统面向分散化,即负载分散、功能分散、危险分散和地域分散。分散式工业控制系统就是为适应这种需要而发展起来的。这类系统是以微型机为核心,将5C技术——Computer(计算机技术)、Control(自动控制技术)、Communication(通信技术)、CRT(显示技术)和Change(转换技术)紧密结合的产物。它在适应范围、可扩展性、可维护性以及抗故障能力等方面,较之分散型仪表控制系统和集中型计算机控制系统都具有明显的优越性。典型的分散式控制系统有现场设备、接口与计算设备以及通信设备组成,现场总线(Field bus)就是在这种背景下产生的[2]。
1.2 研究目的和意义从19世纪发明汽车以来,人们就一直在乘坐的舒适性、安全性和操控性方面不停地对其进行改革和创新,车上的电子设备也越来越多。这些电子设备大多是需要协同工作的,这就要求各部件之间能互相通信[1]。
为了解决汽车通信问题,CAN—bus应运而生,凭借可靠、实时、经济和灵活的特点,CAN总线很快在其他行业得到广泛应用,特别是在工业控制领域更是如鱼得水。现在CAN—bus总线已经成为全球范围内最重要的现场总线之一,甚至引领着现场总线的发展。
工业控制系统涉及众多软、硬件模块,给程序的设计和调试带来一定难度。尤其作为上、下位机间联系纽带的CAN总线通信部分,一旦在整个系统运行期间发生问题,若没有良好的人机界面和测试手段,将很难及时准确地找到并排除故障。同样,在控制系统的研制过程中,为了尽可能地减少故障和缩小故障范围,也应设计相应的测试软件来具体负责CAN总线通信及接口部分的调试、运行任务。故此,本课题就如何利用VC设计CAN总线测试软件进行介绍。
1.3 国内外发展现状自从Bosch与Intel公司于1986年正式发布CAN—bus通信方式,宝马(BMW)公司很快于1989年推出第一款使用CAN—bus通信的汽车,从此 CAN—bus开始了其辉煌的历程:(1)1990年,奔驰公司发布了第一辆使用CAN—bus的轿车,现在几乎每一辆新生产的汽车均装配有CAN—bus网络;(2)1993年,CAN—bus总线被制定成为国际标准ISO11898(高速应用)和ISO11519(低速应用);(3)1994年,欧洲成立了CiA厂商协会,美洲成立了ODVA厂商协会,专门支持CAN—bus总线的两大应用层协议——CANopen协议与DeviceNet协议[3]。
在CiA的努力推广下,CAN技术在汽车电子控制系统、电梯控制系统、安全监控系统、医疗仪器、纺织机械、船舶运输等方面均得到了广泛的应用。现已有400多家公司加入了CiA,CiA已成为全球应用CAN技术的权威。
国内在CAN总线方面的研究和应用于国外相比还存在明显的差距,体现在两个方面:(1)国内在自主研究和开发汽车电子CAN网络方面尚处于试验和起步阶段,国内绝大部分的汽车还没有采用汽车总线设计;(2)国内汽车合资企业不少已采用CAN总线技术,但核心技术掌握在外商手中。为顺应世界汽车工业发展的趋势,我国也相应加强了对CAN总线的研究,并开发具有自主知识产权的CAN总线产品。
CAN技术已应用于家用电器和智能楼宇以及小区建设中。随着无线技术的完善和将无线技术应用到CAN总线系统中研究的不断深入,可以乐观地预计,未来CAN总线技术的应用将无处不在,虚拟的CAN总线即将诞生[5]。
1.4 论文结构安排本文第一章介绍了CAN总线的研究背景和国内外发展现状,并介绍本课题研究的目的和意义。第二章简单介绍了CAN总线通信规范和SJA1000控制器。第三章简单介绍了开发环境和CAN接口卡。第四章详细介绍了软件的设计过程,包括驱动安装、接口卡函数库说明、界面设计、功能分析与设计。第五章介绍了软件的测试及程序的发布。
2 CAN总线协议分析2.1 CAN-bus 规范V2.0 版本CAN 规范技术规范由两部分组成:
• A 部分:CAN 的报文格式说明(按CAN1.2 规范定义)。
• B 部分:标准格式和扩展格式的说明。
2.1.1 CAN的分层结构在CAN V2.0A里,CAN被细分为三个层次:对象层、传输层、物理层。
而在PartB中,CAN被细分为两个层次:数据链路层(逻辑链路控制子层LLC、媒体访问控制子层MAC)、物理层。
2.1.2 报文传输(1)帧类型
报文传输由5种类型的帧所表示和控制,它们分别是数据帧、远程帧、错误帧、过载帧和帧间隔,其用途如表2.1所列。
表2.1 帧的类型及用途
| |
| 用于发送节点向接受节点传送数据,是使用最多的帧类型 |
| |
| 用于检测出通信错误(如校验错误)时向其他节点发送通知 |
| 用以在先行的和后续的数据帧(或远程帧)之间提供一附加的延时 |
| |
1) 数据帧
数据帧由7 个不同的位场组成:帧起始、仲裁场、控制场、数据场、CRC 场、应答场、帧结尾。数据帧各段的功能如表2.2所列。
表2.2 数据帧各段的功能
| | |
| | 表示数据帧开始,由单个显性位构成,在总线空闲时才允许发送 |
| | | 表示该帧的优先级,由11位ID码和1位远程帧标志位(RTR)组成 |
| | 表示该帧的优先级,由29位ID码、1位替代远程帧请求位(SRR)、1位标志位扩展位(IDE)和1位远程帧标志位(RTR)组成 |
| | |
| | |
| | 检查帧的传输错误,范围包括从帧起始到数据段的所有内容(不包括填充位) |
| | |
| | |
2) 远程帧
远程帧由6 个不同的位场组成:帧起始、仲裁场、控制场、CRC 场、应答场、帧结尾。
3) 错误帧
错误帧由两个不同的场组成。第一个场用作为不同站提供的错误标志的叠加。第二个场是错误界定符。错误标志有两种形式,主动错误标志和被动错误标志。错误界定符包括8 个“隐性”的位。
4) 过载帧
过载帧包括两个位场:过载标志和过载界定符。
5) 帧间空间
数据帧(或远程帧)与其前面帧的隔离是通过帧间空间实现的,无论其前面的帧为何类型(数据帧、远程帧、错误帧、过载帧)。所不同的是,过载帧与错误帧之前没有帧间空间,多个过载帧之间也不是由帧间空间隔离的。
(2)发送器/接收器的定义
发送器:产生报文的单元被称之为报文的“发送器”。此单元保持作为报文发送器直到总线出现空闲或此单元失去仲裁为止。
接收器:如果有一单元不作为报文的发送器并且总线也不空闲,则这一单元就被称之为报文的“接收器”。
2.1.3 报文检验校验报文是否有效的时间点,对于发送器与接收器是各不相同的。
对于发送器:如果直到帧的末尾位均没有错误,则此报文对于发送器有效。如果报文破损,则报文会根据优先权自动重发。为了能够和其他信息竞争总线,重新传输必须在总线空闲时启动。
对于接收器:如果直到一最后的位(除了帧末尾位)均没有错误,则报文对于接收器有效。
2.1.4 编码位流编码:帧的部分,诸如帧起始、仲裁场、控制场、数据场以及CRC序列,均通过位填充的方法编码。
数据帧或远程帧(CRC界定符、应答场和帧末尾)的剩余位场形式相同,不填充。错误帧和过载帧的形式也相同,但并不通过位填充的方法进行编码。
报文里的位流采用不归零编码(NRZ),这就是说,在整个位时间里,位电平要么为“显性”,要么为“隐性”。
2.1.5 错误处理错误检测:有5种不同的错误类型(这5种错误不会相互排斥):位错误、填充错误、CRC错误、形式错误、应答错误
错误标志:检测到错误条件的站通过发送错误标志指示错误。
2.1.6 故障界定至于故障界定,单元的状态可能为以下三种之一:
错误主动:可以正常地参与总线通讯并在错误被检测到时发出主动错误标志。
错误被动:不允许发送主动错误标志。
总线关闭:不允许在总线上有任何的影响(比如,关闭输出驱动器)。
2.1.7 位定时要求标称位速率:标称位速率为一理想的发送器在没有重新同步的情况下每秒发送的位数量。
标称位时间:标称位时间=1/标称位速率
可以把标称位时间划分成几个不重叠的时间片段,它们是:同步段、传播时间段、相位缓冲段1、相位缓冲段2。
2.1.8 报文滤波在CAN2.0B中,还增加了有关报文滤波的定义。
报文滤波取决于整个识别符。允许在报文滤波中将任何的识别符位设置为“不考虑”的可选屏蔽寄存器,可以选择多组的识别符,使之被映射到隶属的接收缓冲器里。
如果使用屏蔽寄存器,它的每一个位必须是可编程的,即,他们能够被允许或禁止报文滤波。屏蔽寄存器的长度可以包含整个识别符,也可以包含部分的识别符。
2.1.9 振荡器容差由于给定的最大的振荡器,其容差为1.58%,因此凭经验可将陶瓷谐振器使用在传输率高达125kbit/s的应用罩。
为了满足CAN协议的整个总线速度范围,需要使用晶振。具有最高振荡准确度要求的芯片,决定了其他节点的振荡准确度。
2.2 CAN控制器SJA1000下位机的CAN总线网络接口使用Philips公司的SJA1000芯片,SJA1000是一个独立的CAN控制器,具有一系列先进的功能,适合于多种应用,特别在系统优化、诊断和维护方面非常重要。
SJA1000具有完成CAN总线通信协议所要求的全部特性,它与独立CAN总线控制的PCA82C200完全兼容,并有支持CAN2.0B协议、扩展接收缓冲器、增强错误处理能力和增强验收滤波功能等新增功能。
SJA1000可以直接进行CAN总线互联,而PC机作为上位机,是通过USB电缆连接到CAN接口卡上的,这里我们使用的是普创电子的CANUSB—Ⅱ工业级双路智能接口卡。该接口卡中的CAN总线数据收发也是由SJA1000CAN控制器和82C250CAN收发器完成的,主机通过USB电缆来访问CAN控制器,从而实现数据通信。
2.3 本章小结本章主要介绍了CAN总线通信系统上位机通信软件的设计所涉及的基本知识,包括CAN—bus规范和CAN控制器SJA1000,有了这些知识,才能保证软件设计得以顺利开展。
3 开发环境介绍3.1 开发环境CAN总线通信系统上位机通信软件的设计应具有直观的窗口外观,丰富、人性化的友好界面,便于操作和维护。而Visual C++6.0编译器提供了强大的辅助工具集,利用这些工具可以很方便的设计出本课题所要求的应用程序。
利用Visual C++6.0开发应用程序时,主要有两种方法,一种是利用Windows本身提供的API函数编程,另一种是直接使用Miscrosoft提供的MFC类库编程。本课题使用的是MFC类库编程。
MFC类库是由Microsoft公司提供的用来编写Windows应用程序的C++类集合,在该类集合封装了Windows大部分编程对象和与它们相关的操作。MFC为用户提供了一个Windows环境下的应用程序框架和创建应用程序的组件,使用这个应用程序框架和组件,可以轻松地编写出各种不同的应用程序。
在Visual C++6.0中,可以利用MFC AppWizard应用程序向导快速地创建一个标准的Windows应用程序框架,只需在此基础上添加实现特定功能的程序代码就能编写出相应的Windows应用程序。该应用程序框架类型中包含了三种最基本、最常用的应用程序类型:单文档、多文档和基于对话框的应用程序。基于对话框应用程序功能简单、结构紧凑,执行速度快,程序源代码少,开发调试容易,符合本课题需求,故本课题采用基于对话框应用程序。
3.2 CANUSB—Ⅰ/Ⅱ智能CAN接口卡3.2.1 产品概述CANUSB—Ⅰ/Ⅱ智能CAN接口卡兼容USB1.1和USB2.0总线,带有1路/2路CAN接口的工业级智能型CAN数据接口卡。采用CANUSB—Ⅰ/Ⅱ智能CAN接口卡,PC可以通过USB总线连接至CAN网络,构成实验室、工业控制、智能小区等CAN网络领域中数据处理、数据采集。
CANUSB—Ⅰ/Ⅱ智能CAN接口卡是CAN产品开发、CAN数据分析的强大工具;同时,具有体积小、即插即用等特点,也是便携式系统用户的最佳选择。
3.2.2 智能CAN接口卡硬件接口描述CANUSB—Ⅰ/Ⅱ智能CAN接口卡集成2路CAN通道,每一路通道都是独立的,可以用于连接一个CAN—bus网络或者CAN—bus接口的设备。CANUSB—Ⅰ/Ⅱ智能CAN接口卡布局如下:

图3.1 CANUSB—Ⅰ/Ⅱ智能CAN接口卡外围端子
2路CAN—bus通道由1个10Pin接线端子引出,接线端子的引脚详细定义如下表所示:
表3.1 CANUSB—Ⅰ/Ⅱ接口卡的CAN—bus信号分配
3.3 本章小结本章介绍了CAN总线通信系统上位机通信软件的开发环境和CANUSB—Ⅰ/Ⅱ智能CAN接口卡。为了获得直观的窗口外观,丰富、人性化的友好界面,本课题利用Visual C++6.0下的MFC类库开发程序。
4 CAN通信软件设计4.1 驱动程序安装CANUSB—Ⅰ/Ⅱ智能CAN接口卡使用USB直接供电并提供智能驱动安装包,安装步骤如下:
点击产品光盘的“\CANUSB\Drivers”目录下的安装包安装驱动;
将CANUSB—Ⅰ/Ⅱ智能CAN接口卡通过USB电缆连接到计算机,提示发现新硬件,选择自动安装软件即可。
4.2 CAN接口卡函数库说明4.2.1 函数库数据结构定义(1)初始化CAN数据类型
typedef struct _INIT_CONFIG
{ DWORD AccCode;//验收码
DWORD AccMask;//屏蔽码
DWORD Reserved;//保留
UCHAR Filter;//滤波方式
UCHAR Baudrate;//波特率
UCHAR Mode;//模式
} VCI_INIT_CONFIG,*PVCI_INIT_CONFIG;
(2)CAN信息帧的数据类型
typedef struct _VCI_CAN_OBJ
{ BYTE CANIndex;//接受的数据帧来自哪个通道 =0时CAN0通道 =1时CAN1通道
DWORD ID;//报文ID
BYTE SendType;//发送帧类型,=0时为正常发送,=1时为自发自收,只有在此帧为发送帧时有意义。
BYTE ExternFlag;//是否是扩展帧
BYTE RemoteFlag;//是否是远程帧
BYTE DataLen; //数据长度(<=8,即Data的长度
BYTE Data[8]; //报文的数据
}VCI_CAN_OBJ,*PVCI_CAN_OBJ;
4.2.2 接口函数说明①BOOL __stdcall VCI_OpenDevice(DWORD DevIndex);//打开设备
②BOOL __stdcall VCI_CloseDevice(DWORD DevIndex);//关闭设备
③BOOL __stdcall VCI_InitCAN(DWORD DevIndex,DWORD CANIndex,PVCI_INIT_CONFIG InitConfig);//初始化CAN
④BOOL __stdcall VCI_StartCAN(DWORD DevIndex ,DWORD CANIndex);//启动CAN设备
⑤BOOL __stdcall VCI_ResetCAN(DWORD DevIndex ,DWORD CANIndex);//复位CAN设备
⑥BOOL __stdcall VCI_Transmit(DWORD DevIndex ,DWORD CANIndex, VCI_CAN_OBJ *SendData );//发送一帧数据
⑦DWORD __stdcall VCI_Receive(DWORD DevIndex ,PVCI_CAN_OBJ pReceive , DWORD Len , DWORD WaitTime);//接收数据
⑧BOOL __stdcall VCI_ReadDevSn(DWORD DevIndex, PCHAR DevSn);//读取序列号
其中:
DevIndex 设备索引号,有一个设备时索引号为0,有两个可以为0或1;
CANIndex 第几路CAN;
InitConfig 初始化参数结构;
SendData指向信息帧结构体;
pReceive用来接收的数据帧结构体数组的首指针;
Len 读取多少帧的数据;
WaitTime =0时为无限等待;>0时等待超时时间,以毫秒为单位;
DevSn 序列号;
返回值 为1表示操作成功,0表示操作失败;
4.2.3 接口函数库使用方法首先,把库函数文件都放在工作目录下。总共有四个文件CAN_TO_USB.h,CAN_TO_USB.lib,SiUSBXp.dll,CAN_TO_USB.dll。
VC调用动态库的方法:
(1)在.cpp中包含CAN_TO_USB.h头文件;
(2)在工程文件中加入CAN_TO_USB.lib文件。
4.2.4 接口函数库使用流程
图4.1 接口函数库使用流程
4.3 界面设计CAN总线通信系统上位机通信软件的设计目标是对CAN总线的运行状态和通信能力进行有效的测试,要求能正确识别CAN设备并打开CAN通道,可封装CAN报文进行发送,可接收CAN数据帧,并能对数据帧进行解析,在数据列表中显示报文的相关参数信息(如:帧ID、帧格式、帧类型、DLC值以及帧数据等参数),并具有过滤功能。具体有以下几个功能模块:设备连接、设备启动、设备复位、帧封装与发送、帧接收与解析以及清除显示。
4.3.1 界面布局设计打开MFC App Wizard(exe)创建一个基于对话框的应用程序,项目名为Test。打开对话框,按照软件功能要求用控件编辑器添加相应控件,设置控件属性,打开类向导,为界面上各控件添加对应的成员变量。设计完成后的界面如图3所示。表4.1列出了CAN0通道各个控件属性及成员变量的设置。
图4.2 CAN总线通信系统上位机通信软件界面设计
表4.1 CAN0通道控件属性及成员变量的设置
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| IDC_COMBO_SENDFRAMEFORMAT0 | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
CAN1通道各个控件属性及成员变量设置同CAN0通道,就不再赘述。
4.3.2 对话框初始化在CTestDlg类的OnInitDialog()函数中添加代码, 在对话框初始化的时候被调用,响应WM_INITDIALOG消息,用于完成除基本的创建之外的额外的初始化工作。将额外的初始化代码在这个函数中实现。
(1)组合框初始化
组合框的设置以控件IDC_COMBO_FILTERTYPE0为例。要求控件IDC_COMBO_FILTERTYPE0能下拉显示两种滤波方式:单滤波和双滤波,默认显示单滤波。故在OnInitDialog()函数中添加以下代码:
//滤波方式的添加
m_ComboFilterType0.AddString("单滤波");
m_ComboFilterType0.AddString("双滤波");
//设置第0行内容为显示的内容,即滤波方式默认为单滤波
m_ComboFilterType0.SetCurSel(0);
其他组合框以相同方式按要求设置。
(2)编辑框初始化
编辑框的设置以控件IDC_EDIT_MASK0为例。控件IDC_EDIT_MASK0显示的是屏蔽码的源节点,默认显示“ff”。故在OnInitDialog()函数中添加以下代码:
m_EditMask0="ff";
其他编辑框以相同方式按要求设置。
(3)要求启动时CAN默认未连接,未启动,故在OnInitDialog()函数中添加以下代码:
bConnectCAN=FALSE; //启动时CAN默认未连接
bStartCAN=FALSE; //启动时CAN默认关闭
(4)要求设备未连接时不能启动设备,默认禁用启动通道按钮,故在OnInitDialog()函数中添加以下代码:
GetDlgItem(IDC_BUTTON_CONNECT1)->EnableWindow(FALSE);
(5)更新对话框内容,把变量中的数据输出到控件,故在OnInitDialog()函数中添加以下代码:
UpdateData(false);
4.4 软件功能实现4.4.1 设备连接要求点击“连接”按钮,上位机通信软件与CANUSB—Ⅰ/Ⅱ智能CAN接口卡连接,并且显示连接成功与否。若连接失败,弹出警告框“打开设备失败!”,若连接成功,分别在列表框IDC_LIST_INFO0和IDC_LIST_INFO1显示“CAN0连接成功”“CAN1连接成功”。
在TestDlg.h头文件里添加变量:
public:
int m_devnum; //设备序号
int m_cannum; //can通道编号
void CAN0ShowInfo(CString str, int code);//CAN0接收显示控制函数
双击“连接”按钮,系统自动添加消息响应函数OnButtonConnect0(),添加代码,实现用户单击“连接”按钮,完成设备的连接功能。
定义初始化CAN的数据类型的结构体:VCI_INIT_CONFIG init_config;
如果CAN已经打开,调用VCI_CloseDevice(m_devnum)函数关闭设备。调用GetCurSel()函数获取设备序号:devnum=m_ComboDevindex.GetCurSel();
如果(VCI_OpenDevice(devnum))==0,则设备打开失败,调用MessageBox函数弹出消息框,警告“打开设备失败!”,否则CAN0和CAN1连接成功,分别在列表框IDC_LIST_INFO0和IDC_LIST_INFO1显示。
此时,按钮IDC_BUTTON_CONNECT0的标题“连接”变为“断开”,“启动通道”按钮变为有效,故添加以下代码:
GetDlgItem(IDC_BUTTON_CONNECT0)->SetWindowText("断开");
GetDlgItem(IDC_BUTTON_CONNECT1)->EnableWindow(TRUE);

图4.3 设备连接流程图
4.4.2 设备启动要求点击“启动通道”按钮,CAN0通道和CAN1通道同时启动,并显示启动成功与否。若启动通道失败,弹出消息框,发出警告,若启动通道成功,分别在列表框IDC_LIST_INFO0和IDC_LIST_INFO1显示“CAN0启动成功”“CAN1启动成功”。
双击“启动通道”按钮,系统自动添加消息响应函数OnButtonConnect1(),添加代码,实现用户单击“启动通道”按钮,完成通道CAN0和CAN1的启动功能。
如果VCI_InitCAN(m_devnum,0,&init_config)!=true,则通道CAN0初始化失败,如果VCI_InitCAN(m_devnum,1,&init_config)!=true,则通道CAN1初始化失败,并立即调用VCI_CloseDevice(m_devnum)函数,关闭设备
如果VCI_StartCAN(m_devnum,0)!=true,则通道CAN0启动失败,如果VCI_StartCAN(m_devnum,1)!=true,则通道CAN1启动失败,并立即调用VCI_CloseDevice(m_devnum)函数,关闭设备。如果通道CAN0和CAN1启动成功,则禁用启动通道按钮:
GetDlgItem(IDC_BUTTON_CONNECT1)->EnableWindow(FALSE);

图4.4 设备启动流程图
4.4.3 设备复位要求点击“复位CAN0”按钮,复位通道CAN0,点击“复位CAN1”按钮,复位通道CAN1,成功则分别在列表框IDC_LIST_INFO0和IDC_LIST_INFO1显示“复位成功”,失败则列表框IDC_LIST_INFO0和IDC_LIST_INFO1显示“复位失败”。
CAN0通道:
双击“复位CAN0”按钮,系统自动添加消息响应函数OnButtonResetcan0(),添加代码,实现用户单击“复位CAN0”按钮,完成通道CAN0的复位功能。
如果VCI_ResetCAN(m_devnum,0)==1,则复位成功,否则,复位失败,将结果显示在列表框IDC_LIST_INFO0内。
CAN1通道的复位功能设置如CAN0通道,在此就不再赘述。

图4.5 CAN0通道复位流程图
4.4.4 帧封装与发送(1)帧封装
报文标识符指定了数据通讯中的源节点 MACID 和目标节点MACID,并指示了报文的功能以及所要访问的资源节点。报文标识符被分为SrcMACID (源节点地址)、DestMACID(目标节点地址)、ACK 位、FuncID(功能码)和Source ID(资源节点地址)5 个部分,如表4.2示。
表4.2 iCAN标识符分配
SrcMACID (源节点地址):发送iCAN 报文的节点地址,占用标识符位ID28~ID21,SrcMACID 的高2 位固定为0,数值范围为0x00-0x3F。
DestMACID (目标节点地址):接收iCAN 报文节点地址,占用标识符位ID20~ID13,DestMACID 的高2 位固定为0,数值范围为0x00-0x3F。当DestMACID 的值为0xFF时,表示本次发送的帧是广播帧。
ACK(相应标识符):分配1 位,占用标识符位ID12。该位用于区分帧类型为命令帧还是响应帧,并说明是否需要应答本帧。
表4.3相应标志位
| |
| 用于命令帧,本帧需要应答,但对于广播帧,此值无意义 |
| 用于响应帧,本帧不需要应答;或不需要应答的命令帧(如广播帧) |
FunctionID(功能码):分配4 位,占用标识符位ID11~ID8,功能码用于指示iCAN 报文需要实现的功能,接收报文的节点根据报文中的功能码进行相应的处理。
Source ID(资源节点地址):用于指示所要访问的从站内部资源的起始地址,分配8 位,占用标识符位ID7~ID0。
分别输入源节点、目的节点、响应标示符、功能码、资源节点地址,要求对报文进行封装后发送。
以源节点为例,占用标识符位ID28~ID21,故处理程序如下:
memcpy(&szFrameID[0],(LPCTSTR)m_EditSendFrmID0,m_EditSendFrmID0.GetLength());//拷贝m_EditSendFrmID0所指内存内容到&szFrameID[0]所指的内存地址上
CAN0strtodata((unsigned char*)szFrameID,&FrameID[0],1,0);//字符串转换为数据串
ExtendID |= (FrameID[0]&0x3f)<<21;//左移21位
其他以相同方式处理,报文就封装在FrameID[0]中。
(2)发送
要求点击“发送”按钮,发送数据,结果分别显示在列表框IDC_LIST_INFO0和IDC_LIST_INFO1内。
CAN0通道:
双击“发送”按钮,系统自动添加消息响应函数OnButtonSend0(),添加代码,实现用户单击“发送”按钮,完成发送通道CAN0数据的功能。
如果bStartCAN==FALSE,则通道未启动,不能发送数据,弹出消息框,警告“请启动设备!”。
调用VCI_Transmit(m_devnum,0,&frameinfo)函数判断发送状态,如果为真,则发送成功,在列表框IDC_LIST_INFO0内显示封装好的帧ID和“设备0 通道CAN0 发送成功”,否则显示“设备0 通道CAN0 发送失败”。
CAN1通道的发送功能设置同CAN0通道,在此就不再赘述。

图4.6 CAN0通道发送流程
4.4.5 帧接收与解析(1)接收
要求CAN0和CAN1通道能接收到对方通道或自己发出的数据。
CAN0通道:
在TestDlg.h头文件里定义CAN0接收线程执行函数:
static UINT CAN0ReceiveThread(void *param);
在TestDlg.cpp内添加CAN0接收线程执行函数
UINT CTestDlg::CAN0ReceiveThread(void *param){}并在其中添加代码,完成数据接收功能。
定义CAN信息帧数据类型的结构体:VCI_CAN_OBJ frameinfo[50];
获取序列号:bSn=VCI_ReadDevSn(dlg->m_devnum,DevSn);
获取数据长度:Len=VCI_Receive(dlg->m_devnum,frameinfo,50,200);
如果Len<=0,则没有接收到数据,否则,接收到数据。
如果frameinfo.CANIndex==0,则CAN0通道接收数据,否则,CAN1通道接收数据。如果frameinfo.ExternFlag==0,则帧类型为标准帧,否则,为扩展帧。如果frameinfo.RemoteFlag==0,则帧格式为数据帧,否则,为远程帧。

图4.7 CAN0接收数据流程图
(2)帧解析
- 源节点:封装后的帧ID右移21位输出的即为源节点,程序如下:
tmpstr.Format("源节点:%02x ",(frameinfo.ID>>21)&0x3f);
- 目的节点:封装后的帧ID右移13位输出的即为目的节点,程序如下:
tmpstr.Format(" 目的节点:%02x ",(frameinfo.ID>>13)&0x3f);
- 响应标示符:封装后的帧ID右移12位输出的即为响应标示符,如果为1则为响应帧,为0则为命令帧,程序如下:
if((frameinfo.ID>>12)&0x01)
str+=" 响应帧";
else
str+=" 命令帧";
- 功能码:封装后的帧ID右移8位输出的即为功能码,程序如下:
tmpstr.Format(" function ID:%02x ",(frameinfo.ID>>8)&0x0f);
- 资源节点编号:封装后的帧ID最后8位即为功能码,程序如下:
tmpstr.Format(" source ID:%02x ",frameinfo.ID&0xff);
图4.8 帧解析效果图
4.4.6 清除显示要求点击“清除显示”按钮,列表框IDC_LIST_INFO0和IDC_LIST_INFO1内的内容全部清除。
CAN0通道:
双击“清除显示”按钮,系统自动添加消息响应函数OnButton Clear0(),添加代码,实现用户单击“清除显示”按钮,完成列表框IDC_LIST_INFO0内的内容全部清除功能。
添加以下代码,清除CAN0列表显示项:
m_ListInfo0.ResetContent();
4.5 本章小结本章具体介绍了CAN通信软件的设计,第一部分介绍了CANUSB—Ⅰ/Ⅱ智能CAN接口卡的驱动安装方法;第二部分介绍了CAN接口卡函数库;第三部分介绍了软件的界面设计,包括界面的布局、控件属性及成员变量的设置,和对话框的初始化;第四部分详细介绍了软件功能的实现,包括设备连接、启动、复位、帧封装与发送、帧接收与解析和清除显示六个部分功能的实现方法。
5 测试及发布5.1 软件功能测试将CANUSB—Ⅰ/Ⅱ智能CAN接口卡连至PC的USB接口槽,运行上位机软件,测试功能。
5.1.1 基本功能测试执行程序,如图输入数据,测试各项功能是否能正常运行。

图5.1 程序软件功能测试
经测试,各项功能运行正常。
5.1.2 非法输入限制对于编辑框,若输入非法字符,则弹出消息框“非法字符,请重新输入!”的警告,如下图所示:

图5.2 非法输入警告
5.2 程序发布一个程序除了.exe以外,还需要用到很多其他外部资源,这样程序使用起来很不方便,程序打包就是将这些所有资源放到一个安装程序中,这样只要下载并运行这个安装程序,就可以让安装程序释放目标程序及所有这些资源,方便使用。
Inno Setup是一个免费的安装制作软件,小巧、简便、精美是其最大的特点。用Inno Setup对已经制作好的工程进行打包步骤如下:
- 将已经调试完成的工程,生成发行版(release);
- 安装好Inno Setup Compiler汉化版,启动软件,使用脚本设计向导创建一个新的脚本文件,完成安装程序的制作,过程如下图所示:

图5.3 应用程序信息

图5.4 应用程序文件夹

图5.5 应用程序文件

图5.6 应用程序图标

图5.7 应用程序文档

图5.8 安装语言

图5.9 编译设置

图5.10 Inno Setup 预处理器

图5.11 完成向导

图5.12 编译脚本
- 双击Setup图标,进行尝试安装,安装成功后,在开始菜单栏出现运行和卸载快捷方式,可以运行软件和卸载软件。

图5.13 安装成功效果图
5.3 本章小结本章对已经编写完成的程序进行功能测试,主要测试连接设备、启动通道、复位、发送和接收功能,并能实现帧的封装和解析,测试无误后进行程序发布,方便使用。
结论
毕业设计是本科学习阶段一次非常难得的理论与实际相结合的机会,通过这次CAN总线通信系统上位机通信软件的设计,我从对CAN总线相关技术一无所知到有了一点了解,巩固了曾经学过的知识,锻炼了理论与实际结合解决实际问题的能力,同时也提高了我查阅文献资料的能力,使自己有了全方位的提高,丰富了自己的经验。
本文主要完成了以下的研究:
1、对CAN总线的研究背景、发展现状及本课题的研究目的和意义进行了论述。
2、简单分析了CAN2.0协议和CAN控制器SJA1000。
3、介绍了本课题的开发环境和CANUSBⅠ/Ⅱ只能CAN接口卡。
4、基于VC++设计CAN通信软件,并实现了以下功能:
①正确识别CAN设备并打开CAN通道;
②可封装CAN报文进行发送;
③可接收CAN数据帧,并能对接收的数据帧进行解析:在数据列表中显示报文的相关参数信息(如:帧ID、帧格式、帧类型、DLC值以及帧数据等参数);
④具有过滤功能,可不显示指定的协议帧。
5、对软件功能进行测试,并发布软件。
由于自身水平所限,毕业设计必定还有很多不足和欠缺考虑的地方,也感到自身知识的贫乏,希望在日后的努力中能够更加完善。
致谢
本人的毕业设计一直是在李艳老师的悉心指导下进行的。李老师治学严谨,要求严格,学识渊博,为人亲切。从课题的选定、方案的确定、实际的设计到论文的写作,李老师都给予了无微不至的关怀。在整个毕业设计过程中,李艳老师时时以高标准要求,严格安排时间,并为我指明大方向,使我少走很多弯路。在遇到问题时,李艳老师也耐心指导,循循善诱,让我能够独立思考,顺利地完成我的毕业设计。在此表示诚挚的感谢和由衷的敬意。
此外,我还要感谢许多同学在整个过程中的帮助和配合。