EM9260嵌入式Linux工控板的CAN通讯方案解析

can(controller area network)即控制器局域网,由于具有高性能、高可靠性以及简单的网络结构,在工业系统中越来越受到人们的重视,并迅速成为了目前国际上应用最广泛的现场总线之一。
英利嵌入式linux工控主板em9260是一款面向工业自动化领域的高性价比工控板,板上带有标准can通讯接口。与板上其他标准通讯接口一样,em9260的can接口实现了相应的嵌入式linux驱动程序,应用程序可以通过打开文件的进行读写的标准方式实现对can总线接口的数据通讯。本文侧重于介绍can通讯方案。
硬件组成
em9260嵌入式linux工控板的can均采用了philips半导体公司的sja1000t can总线控制器,sja1000是一款独立的控制器,主要用于汽车和一般工业环境中的控制器局域网络(can)芯片,它是philips半导体pca82c200 can控制器(basiccan)的替代产品,而且它增加了一种新的工作模式(pelican),这种模式支持具有很多新特性的can 2.0b协议。
em9260的can通讯接口可提供高达1mbps的数据传输速率,当采用5kbps的的数据传输速率时其通讯距离最高可达到10km。硬件的错误检定特性也增强了can的抗电磁干扰能力,这给数据的远程可靠传输提供了有利保证。
em9260的can通讯接口根据用户的需要分为两种:一种带光电隔离,一种不带光电隔离。带光电隔离can总线通讯模块的can收发器端的所有信号和电源与其它部分完全隔离,可承受至少1kv(有效值)的电压冲击。光电隔离的功能可在em9260的应用底板上来实现,英利公司在em9260评估底板上提供了相应的参考电路。
can驱动接口函数
1、can报文的帧格式简介
在can2.0b中存在两种不同的帧格式,其主要的区别在于标识符的长度,具有11位标识符的帧称为标准帧,而包括有29位标识符的帧称为扩展帧。下面分别介绍数据帧的格式。
1、can2.0b标准帧
can标准帧信息为11个字节,包括两部分:信息和数据部分。前3个字节为信息部分,如图所示:
注:1、字节1为帧信息。d7位表示帧格式,在标准帧中,ff=0;d6位表示帧的类型,rtr=0表示为数据帧,rtr=1表示为
远程帧,在一般的数据通讯中,只使用数据帧;dlc表示数据帧实际的数据长度
2、字节2、字节3为报文识别码,11位有效
3、字节4~字节11为数据帧的实际数据,远程帧时无效
2、can2.0b扩展帧
can标准帧信息为13个字节,包括两部分:信息和数据部分。前5个字节为信息部分,如图所示:
注:1、字节1为帧信息。d7位表示帧格式,在扩展帧中,ff=1;d6位表示帧的类型,rtr=0表示为数据帧,rtr=1表示为
远程帧;dlc表示数据帧实际的数据长度
2、字节2~字节5为报文识别码,29位有效
3、字节6~字节13为数据帧的实际数据,远程帧时无效
2、can应用数据结构
英利公司提供的基于嵌入式linux下的can操作api函数,为了方便用户的使用,结合目前常用的一些方法,对于can接口接收的数据报文采用了以下结构。
struct can_frame
{
canid_t can_id; /* 用于定义can报文id以及 eff/rtr/err等标志 */
__u8 can_dlc; /* 用于定义can报文数据包长度0-8 */
__u8 data[8]; /* 用于定义can报文数据 */
};
其中的can报文id为一个32 bit大小的结构,其中各个bit位定义如下:
typedef __u32 canid_t;
bit 0-28: can 报文的id(标准帧11bit/扩展帧为29bit).
bit 29 : can报文错误帧标志(0 = data frame, 1 = error frame)
bit 30 : can报文远程帧标志( 1 = rtr frame )
bit 31 : can报文帧格式标志 (0 = 标准帧, 1 = 扩展帧 )
在进行can通讯时需要设置相关的参数,包括波特率、选取的数据滤波方式等,其中对于滤波器的设置,在滤波器的作用下,只有当接收报文中的标识位和验收滤波器预定义的位值相等时,can控制器才允许将收到的报文存入rxfifo中。为了方便使用,在英利公司的api函数中采用了一个struct accept_filter用来设置相关验收滤波器的相关定义。
struct accept_filter
{
unsigned int accept_code; /* 用于定义can报文验收代码位 32bit*/
unsigned int accept_mask; /* 用于定义can报文验收屏蔽位 32bit*/
unsigned char filter_mode; /* 用于定义can报文滤波模式 */
};
3、can通讯接口api函数
em9260的系统内核中实现了can接口的驱动,实现can接口 open( ) / close() 、read( ) / write( )等函数操作。和在linux下操作设备的方式和操作文件的方式一样,调用open( )打开设备文件,再调用read( )、write( )对can接口进行数据读写操作。另外在此驱动程序的基础上,封装了一套简单实用的api函数,以满足对于can接口一些特殊参数设置的需要。各个函数的定义在can_api.h文件下,在该头文件中对于各个api函数均有相应的中文说明。
具体在进行应用程序开发时,首先调用can接口的open( )函数打开can接口:
sprintf( portname, '/dev/em9x60_can%d', canno );
m_fd = open(portname, o_rdwr |o_nonblock );
得到有效的文件描述符m_fd后,然后可调用can_api.h文件中定义的api函数对can接口进行相应的通讯参数设置:
can_startchip( m_fd );
can_setbaudrate( m_fd, baudrate );
can_setglobalacceptancefilter( m_fd, acceptancefilter );
再调用read( ) / write( ) 实现can数据的收发操作。
4、can通讯接口的数据收发应用示例
在英利公司提供的can方案中,can通讯的数据收发均采用的中断方式,驱动程序中已自动完成了数据的收发,以及内部定义的can接收缓冲区和发送缓冲区的管理。对于用户开发应用程序来说,只需要调用英创公司提供的can通讯api函数中的收发函数即可。本小节主要介绍一个can通讯的综合应用示例程序。
app_cantest是一个支持can数据通讯的示例,该例程采用了面向对象的c++编程,把can数据通讯作为一个对象进行封装,用户调用该对象提供的接口函数即可方便地完成can数据通讯的操作。
// 定义can通讯类
class em9x60_can
{
private:
// 通讯线程标识符id
pthread_t m_thread;
// can接收线程
static int receivethreadfunc( void* lparam );
public:
em9x60_can();
virtual ~em9x60_can();
// 已打开的can文件描述符
int m_fd;
unsigned int m_canid;
can_frame rxmsg;
// 退出数据接收线程标志
int m_exitthreadflag;
// 按照指定的参数打开can接口,并创建can接口接收线程
int opencan( int canno, can_baudrate baudrate, accept_filter*acceptancefilter );
// 关闭接口并释放相关资源
int closecan( );
// 初始化设置can数据包id信息
int initcanidinfo( struct canidinfo* pcanid );
// can接口写数据
int writecan( char* buf, int len );
// can接收数据处理函数
virtual int packagepro( char* buf, int len );
};
opencan 函数用于根据输入参数打开can设备,并创建can数据接收线程。
res = pthread_create( &m_thread, &attr, (void*)&receivethreadfunc, (void*)this );
receivethreadfunc函数是can数据接收和处理的主要核心代码,在该函数中调用select( ),等待串口数据的到来。对于接收到的数据处理也是在该函数中实现,在本例程中处理为简单的数据回发,用户可结合实际的应用修改此处代码,修改packagepro( )函数即可。流程如下:
int em9x60_can::receivethreadfunc(void* lparam)
{
em9x60_can *pcan = (em9x60_can*)lparam;
int len;
// 定义读事件集合
fd_set fdread;
int ret;
struct timeval atime;
while( 1 )
{
// 收到退出事件,结束线程
if( pcan->m_exitthreadflag )
{
break;
}
fd_zero(&fdread);
fd_set(pcan->m_fd,&fdread);
atime.tv_sec = 0;
atime.tv_usec = 30000;
ret = select( pcan->m_fd+1,&fdread,null,null,&atime );
if (ret closecan( );
break;
}
if (ret >= 0)
{
// 判断是否读事件
if (fd_isset(pcan->m_fd,&fdread))
{
len = read( pcan->m_fd, (char*)&pcan->rxmsg, sizeof(can_frame) );
while( len > 0 )
{
// 对接收的数据进行处理,这里为简单的数据回发
pcan->packagepro( (char*)&pcan->rxmsg, len );
// 处理完毕
len = read( pcan->m_fd, (char*)&pcan->rxmsg, sizeof(can_frame) );
}
}
}
}
printf( 'receivethreadfunc finished\n' );
pthread_exit( null );
return 0;
}
需要注意的是,select( )函数中的时间参数在linux下,每次都需要重新赋值,否则会自动归0。

VR智能安全教育体验馆的意义以及它的存在目的
易灵思与商显客户合作推出完整的商业显示动态背光方案
广明源SafeGlo全方位紫外线物表消毒机杀菌率达99.9%
线路板厂商迎来大订单,华为Mate 60 Pro将加速生产
互联网巨头如何利用边缘计算切入5G网络基础设施建设?
EM9260嵌入式Linux工控板的CAN通讯方案解析
DC/DC变换器在电动汽车车载12V低压供电中的应用
疫情爆发为无人机迎来新的发展机遇
iPhone8什么时候上市?iPhone将取消指纹识别,这是真的吗?
HDGX地下金属管线测试仪直连法测试方法
6个国家50多场官司?高通苹果彻底决裂,导致苹果5G手机被延后?
国产操作系统的突围之路
运动型蓝牙耳机什么牌子好、最适合跑步用的耳机
动态电压缩放如何在可穿戴设备中节省电力
一种采用组件方式设计、内部采用模块化方式的嵌入式移动数据库系统的设计
麒麟990性能实测:CPU、GPU以及AI算力
爱普泰科AUDIO ANALYZER 音频分析仪 A2
直流他励电机调速方式有哪几种
区块链技术真的能提升金融效率吗
电梯无线网桥怎么装 如何避免干扰