APP下载

基于Can总线的嵌入式网络控制节点的设计与实现

2011-05-10张永红

制造业自动化 2011年16期
关键词:缓冲区寄存器指针

张永红

(北京电子科技职业学院 自动化工程学院,北京 100176)

0 引言

近年来,基于各种总线标准的网络化控制系统已经在工业控制领域内得到广泛应用。网络化控制系统采用了完全分散化的控制节点结构,将控制的权力很大部分交给了处于控制现场的智能节点,系统内各种交互信息通过现场总线传送。

当前已实用化的总线标准有许多种,如WorldFIP、Profibus、LONWORKS、CAN 等。 其中,CAN(Controller Area Network)是Bosch公司在现代汽车应用技术中领先推出的一种串行通信网络。CAN主线采用多主站工作方式,根据优先权进行总线访问仲裁,能够检测出通信过程产生的任何错误。CAN总线还具有卓越的信号传输性能,当信号传输距离达到10km时,它仍可提供高达50Kbit/s的数据传输速率。另外,CAN协议废除了站地址编码,而采用对通信数据块编码的方式,这样使得网络内的节点个数在理论上不受限制。目前,CAN总线已经在许多行业得到了广泛的应用,尤其是工业控制领域,并常被认为是最有前途的现场总线之一。

常用的CAN总线节点一般采用的是“单片机+CAN控制器”的结构,这样由于运算能力的限制,这类节点的智能化程度较低,常是作为工控机节点的从节点。而近年来,以ARM为代表的嵌入式32位微处理器技术得到了飞速发展,无论是在功耗、便携性还是在硬件成本上,许多高性能的ARM芯片已经与单片机相差无几,因此在CAN节点设计中,使用ARM芯片取代传统的8/16位单片机已经是一个非常实用的选择。这样设计的CAN节点,不仅保留了低功耗、低成本和小体积的优点,而且性能得到了大幅提高,若辅以大容量的存储器,同时运行功能强大的嵌入式操作系统,它几乎已可以取代原先的工控机节点。本文则从软硬件两方面详细介绍了上述设计方案的具体实现过程。

1 节点的接口电路设计

本文设计的CAN总线节点是某工业控制系统的一个子模块,同时综合考虑其它相关需求和功能扩展,因此选用了AT91RM9200处理器作为系统的核心处理单元。AT91RM9200[1]是ATMEL公司生产的一款高性能的ARM9处理器,它是一款通用工业级ARM芯片,主频为180MHz/200MIPS,已经在工业控制、智能仪器仪表等领域内得到了大量的成功应用。

CAN控制器选用的是SJA1000芯片[2],它是Philips公司生产的一款独立CAN总线控制器芯片,专用于移动目标和一般工业环境中控制器局域网络(CAN)。SJA1000本质上是早期的PCA82C200的升级产品,与后者在管脚、电气特性上完全兼容,而且除具有基本CAN工作模式(BasicCAN)外,还增加了一种新的增强工作模式(PeliCAN),这种新模式支持具有许多新特性的CAN2.0B协议。

SJA1000的总线接口采用的是地址总线和数据总线复用的方式,这种方式与51类似,也采用总线复用架构的处理器,接口很方便,并在读写时序上也很好配合,但当与数据总线和地址总线分离的微处理器接口时,则需要专门的读写逻辑与之配合,并且还相对比较复杂,基于这个原因,目前许多设计都采用诸如SPI等专用接口的CAN总线控制器,但这种方式使应用受到了诸多限制,如要求微处理器必须有SPI接口,同时当系统需要多路CAN总线接口时,会受到SPI端口数的限制等。

ARM架构的数据总线和地址总线是分离的,因此,必须引入专门的控制逻辑,才能实现对SJA1000的操作。

图1和图2分别为SJA1000读/写操作时序。分析其读/写时序,可以看出,无论是读操作还是写操作,首先必须送出操作寄存器的地址,然后读/写数据。在写地址的过程中,片选信号(/CS)和读(/RD)、写(/WR)均无效(高电平),仅ALE信号有效(高电平),而在读/写数据的过程中,读/写信号有效(低电平),ALE信号无效(低电平),同时,在操作的过程中,还必须满足信号电平的持续时间。

因此,可以采用如图3所示的控制逻辑实现。在图3所示的SJA1000操作时序的实现中,左端的信号如/CS、/RD、/WR、A7分别为ARM微处理器的片选、读、写控制信号,A7为地址信号(也可以是其它的地址),右端产生的ALE_CAN、/CS_CAN、/WR_CAN、/RD_CAN 分别与SJA1000对应的信号相连接,当微处理器对SJA1000对应的地址进行读写操作时,即可产生正确的控制逻辑。

2 节点的驱动程序开发

在工业控制应用中,使用嵌入式操作系统已逐渐成为一个流行的选择。目前,市场上的嵌入式操作系统超过100种,其中嵌入式Linux是一种非常理想、经济的选择,因为它不仅具有功能强大、高性能、稳定性好等优点,还是免费并开放源代码的。同时Linux内核采用了模块化设计,具有非常良好的移植性和可定制性。现在,许多ARM生产厂商都已经将Linux系统移植到其生产的ARM芯片上,并发布了相关源代码供用户免费使用。本系统中采用的操作系统就是ATMEL公司发布的支持其AT912RM9200处理器的ARMLinux系统的版本,版本号为2.4.27。

CAN控制器SJA1000显然属于Linux系统中的字符设备类型,其驱动程序的实现架构类似于系统中字符设备的通用实现结构,关于Linux设备驱动开发的详细分析可参考文献[3]。本小节则以SJA1000的增强工作模式(PeliCAN)为例,对Linux系统下CAN设备驱动程序的主要实现部分进行了详细说明,包括主要数据结构的定义、操作函数和中断函数的实现三个部分。

2.1 CAN设备驱动的主要数据结构

为方便驱动程序的设计和编写,驱动中定义了两个数据结构体,即协议帧数据结构和缓冲区结构体,下面给出每个结构体的定义及成员变量的解释。

1)协议帧数据结构

其中,协议帧结构体是用来对CAN网络的报文数据帧进行抽象,驱动中使用该结构体来进行用户与内核空间的数据帧传递及发送/接收数据缓冲区的管理。

从缓冲区结构体的定义可以看出,CAN设备的数据缓冲区由两个独立缓冲区构成,一个用于设备读操作,另一个用于写操作。读缓冲区和写缓冲区都是一个先入先出的环形缓冲区,缓冲区的大小设为协议帧结构体的整数倍,如64倍。驱动通过缓冲区的读指针和写指针来进行缓冲区管理。如对于接收缓冲区(读缓冲区),它的读指针指向了当驱动程序从内核空间向用户空间拷贝报文数据帧时,缓冲区中第一个有效数据帧的位置;而缓冲区的写指针则代表了在控制器芯片执行接收数据时,即将数据帧从SJA1000的寄存器读到缓冲区,缓冲区的当前可写位置;这样通过读指针和写指针的相对位置及缓冲区的整体长度就可以得到读缓冲区中当前的数据帧个数。写缓冲区的管理与读缓冲区基本相同,其详细机制可参考后面给出的相关伪代码。同时缓冲区中还定义了volatile int型变量tx_in_progress来表征当前是否有实际的数据发送操作已被启动,“1”标识已启动,否则为“0”。另外,结构体定义中使用到了Linux系统的等待队列结构,关于它的详细机制可参考文献[3]。

2.2 CAN设备的操作函数

字符设备驱动的核心就是实现设备的操作函数结构,所谓的操作函数结构,本质上是定义了应用程序在设备上的所有可能操作。但对于某一具体设备,驱动中只需要实现设备工作所必须的操作。如对于CAN设备,本例中共实现了5种系统调用函数,即open、close、read、write和ioctl函数。

其中,open函数是在应用程序打开CAN设备时被调用,函数主要实现两部分功能,首先对驱动中的变量和数据结构进行初始化,并分配缓冲区空间;另一部分就是对CAN控制器SJA1000初始化,即在复位时为芯片的各个寄存器设置正确的初始值。对于SJA1000芯片,需要设置的寄存器包括:1)模式和时钟寄存器;2)输出控制寄存器;3)验收代码寄存器和验收屏蔽寄存器;4)总线定时寄存器;5)错误计数寄存器;6)中断使能寄存器。需要注意的是,SJA1000的配置寄存器只能在复位模式下可写,所以在设置寄存器之前,必须先进入复位模式,所有设置完成后必须返回正常工作模式,关于SJA1000芯片寄存器设置的更多内容可参见文献[2]。CAN设备的close调用的实现功能非常简单,即等待缓冲区中已有的数据帧被处理完,然后释放缓冲区,最后关闭设备中断。

驱动程序的write就是对应于用户写CAN设备时的系统调用。它的功能就是完成应用程序在用户空间中的报文发送,即报文数据从用户空间向内核空间的传递。下面给出了实现write函数的伪代码:

判断指定的数据长度是否满足数据帧格式,若不满足,则提示并返回;

通过写指针和读指针的相对位置及缓冲区的整体长度计算输出缓冲区中空闲空间的大小;

while 空闲空间长度 < 一个数据帧大小

使用interruptible_sleep_on_timeout()函数将写进程放入写操作等待队列睡眠,该函数既说明了睡眠可被信号中断,还可以指定进程的最长睡眠时间;

睡眠结束后,再次计算空闲空间的大小。若空闲空间长度已大于或等于一个数据帧大小,则可跳出循环,否则继续while循环;

从用户空间拷贝N字节数据到输出缓冲区,其中:N = min(计算的空闲空间大小,数据长度参数),然后更新输出缓冲区的写指针位置;

if 当前无实际的发送操作已被启动,即tx_in_progress 等于 0

置位tx_in_progress为1,同时调用发送初始化函数,即将输出缓冲区中第一个有效数据帧写入到控制器芯片相应的发送寄存器中,同时使能发送操作,完成后,结束can_write()函数并返回数值N;

需要说明的是,上述伪码中在对临界区变量进行操作和判断时,即计算空闲区长度和判断tx_in_progress变量时,必须要在关闭设备中断条件下进行,操作完成后立即重新打开中断,下面描述的读函数中与临界区变量相关的操作也必须采用同样的机制。

读函数read的功能就是响应用户对CAN设备的读操作,如果接收缓冲区中已有了数据帧,则直接从缓冲区拷贝M字节的数据返回给用户,其中M为用户要求字节数与缓冲区已有数据字节数之间的较小值,更新读指针位置然后函数返回数值M;而如果当前缓冲区中无有效数据,则需要判断设备文件的读取模式,若为非阻塞模式,则直接返回,否则将读进程放入读操作等待队列睡眠,指定进程的最大睡眠时间并设置为睡眠可被信号中断模式,当任务被唤醒后,再对唤醒方式进行判断,若因睡眠时间结束而唤醒,则函数直接返回相应标识,否则说明缓冲区中已有数据,则执行上述的拷贝动作,并更新缓冲区的读指针位置,函数结束并返回实际读取字节数,具体实现略。

设备的ioctl函数是用于设备控制的公共接口,可以根据设备的具体需求来实现相应的控制代码,在本文中共实现了三种功能,即清除读/写缓冲区、设置总线波特率、设置验收代码寄存器和验收屏蔽寄存器。

2.3 CAN设备的中断函数

如上所述,设备的读函数和写函数只是完成用户空间与设备的接收/发送缓冲区之间的数据帧传递,而真正的数据接收和发送工作,即芯片的寄存器与数据缓冲区之间的数据读取和写入,是由设备中断函数来完成。因此,实现中断函数既是驱动程序开发的核心,也是难点。下面是本例中使用的中断函数的伪代码:

读can芯片的中断状态寄存器,用来判断中断源类型;

if CAN接收中断

读芯片的RX帧信息寄存器,然后根据接收的数据帧类型,即标准帧或扩展帧,读取相应的ID寄存器和RX数据寄存器,并将各数值写入一个数据帧结构体中,再把读取的该数据帧拷贝到接收缓冲区中,缓冲区的写指针就是缓冲区的当前可写位置,拷贝完成后,更新写指针位置;

读控制器的状态寄存器,判断RXFIFO是否还有可用信息,若还有信息,则重复执行上述的数据读取操作,否则就判断读操作等待队列上是否有睡眠任务,有则唤醒它,中断函数结束;

else if CAN发送中断

判断设备的发送缓冲区是否已为空,若为空,则置tx_in_progress变量为0,并判断写操作等待队列上是否有睡眠进程,有则唤醒它,然后中断函数结束;

否则继续发送缓冲区中的数据帧,而缓冲区的读指针代表了缓冲区中当前需要发送的数据帧位置,发送完成后,更新读指针位置,并判断写操作等待队列上是否有睡眠进程,有则唤醒它,中断函数退出;

else 即为其他中断

判断错误类型,并置位相应错误标识,然后分别判断写操作等待队列和读操作等待队列上是否有睡眠进程,有则唤醒它,中断函数退出;

}

当CAN控制器发生除接收中断和发送中断外的其它类型中断时,一般是根据具体的应用需求采取相应的处理措施。本例中采用了置位相应标识,由用户程序进行处理的方式。另外,驱动程序除实现上述的操作函数和中断函数,还应在模块初始化函数中完成设备注册、申请中断并注册中断函数及其他初始化工作;在模块清除函数中需要卸载设备并释放中断资源。

3 结束语

本文从软硬件两方面对基于ARM9芯片的

CAN节点的具体设计过程进行了介绍,对硬件设计中的关键性问题和驱动模块实现结构都作了详细分析。目前,该CAN总线节点作为一个子系统已在某高速的网络化数据采集系统中得到应用,

运行结果表明设备在CAN总线2.0B协议标准(兼容2.0A)下,数据发送、接收完全正常,满足了系统工作要求。本文给出的示例具有一定的普适性,

对基于其他嵌入式设备上的CAN模块开发也有一定的参考价值。

[1] Atmel Semiconductors. Data Sheet AT91RM9200 [EB/OL]. http://www.atmel.com, 2005.

[2] 周立功单片机发展有限公司. SJA1000独立CAN控制器应用指南[EB/OL]. http://www.zlgmcu.com, 2004.

[3] 魏永明, 骆刚, 姜君, 译. Linux设备驱动程序(第二版)[M]. 北京: 北京电力出版社,2002.

[4] 邬宽明. CAN总线原理和应用系统设计[M]. 北京: 北京航空航天大学出版社, 1996.

猜你喜欢

缓冲区寄存器指针
STM32和51单片机寄存器映射原理异同分析
垂悬指针检测与防御方法*
Lite寄存器模型的设计与实现
基于ARC的闪存数据库缓冲区算法①
移位寄存器及算术运算应用
为什么表的指针都按照顺时针方向转动
一类装配支线缓冲区配置的两阶段求解方法研究
初涉缓冲区
浅析C语言指针
Lx5280模拟器移植设计及实施