Chinaunix首页 | 论坛 | 博客
  • 博客访问: 620095
  • 博文数量: 112
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 1406
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-25 18:46
文章分类
文章存档

2011年(1)

2010年(5)

2009年(25)

2008年(81)

我的朋友

分类: LINUX

2008-04-26 09:49:12

0 引言

在铁路系统中,为了保证列车的安全运行,需要对铁轨及周围状况进行实时检测。目前采用的方法是在铁路沿线安装多个检测设备,用于检测洪水、大风、泥石流等自然灾害及轨温等参数。这些设备一般采用的通信方式是RS-232RS485CAN,并通过专线连接至监控中心的各个监控设备。这种方式极大浪费了线路资源,也不易于设备的统一管理。因此,需要一种安装在铁路沿线的设备,它将附近的检测设备发送的信息统一收集并通过一条专线直接送往监控中心。为了和多个检测设备通信,我们的设备必须同时具有多个RS-232RS-485CAN接口。本文即是出于这种应用需要,提出了扩展多个CAN总线接口的方法。

1  系统结构

1.1  芯片介绍

本系统采用ATMEL公司的AT91RM9200(以下简称9200)作为MCU,该处理器基于ARM920T内核,主频为180MHz时,性能可达到200MIPS,最高主频为209MHz。同时该处理器还具有丰富的外设资源,非常适合工业控制领域的应用[1]。采用的操作系统是Arm-Linux,内核版本为2.4.19

目前主流的CAN协议控制器一般采用I/O总线(SJA1000等)或SPI接口(MCP2515等)与MCU进行通信。由于我们采用PC/104总线扩展卡的方式来扩展多个RS-232RS-485接口,没有多余的I/O片选线可用,因此我们最终选用9200SPI接口与MCP2515进行多路CAN总线接口的扩展。

MCP2515是具有SPI接口的独立CAN控制器,它完全支持CAN V2.0B技术规范,通讯速率最高可达1Mb/s,内含3个发送缓冲器、2个接收缓冲器、629位验收滤波寄存器和229位验收屏蔽寄存器[2]。它的SPI接口时钟频率最高可达10MHz,可满足一个SPI主机接口扩展多路CAN总线接口的需要。

1.2   系统硬件接口

系统硬件接口的原理框图如图12所示。图19200MCP2515的接口框图,通过9200SPI接口,连接了5MCP2515。由于9200SPI从设备片选线数量有限,故采用片选译码方式,NPCS0可作为普通的外部中断线使用(NPCS0IRQ5复用引脚)。由于9200的外部中断线资源有限,我们采用中断线共享的方式,即分别有两个MCP2515共享同一中断线,最后一个MCP2515独占一条中断线,以满足不同通信速率下数据处理的需要。

 

1  AT91RM9200MCP2515接口框图

2 MCP2515的外围CAN总线接口框图,图中省略了MCP25159200的接口部分。由于设备需要安装在铁路沿线,必须具有防雷击的能力。因此MCP2515 CAN总线收发器(TJA1050)之间采用高速光耦进行完全的电气隔离,并且光耦两端电路的电源也必须用电源隔离模块隔离开,这样才能真正起到隔离的作用。在TJA1050CANHCANL引脚与地之间连接两个30pF的电容可以过滤CAN总线上的高频干扰,两个二极管可以在总线电压发生瞬变干扰时起保护作用。光耦正常工作时输入电流为10mA左右,内部发光二极管的正向电压降为1.7V左右,因此要特别注意输入端串联电阻的阻值选择。

 

2  MCP2515 CAN总线接口电路

2  SPI主机的工作方式

       9200通过SPI接口与五个MCP2515进行通信,9200SPI控制器工作在主机模式,MCP2515工作在从机模

式。MCP2515支持多个指令(如复位指令、读指令、写指令等),以便于9200通过SPI接口对MCP2515的内部寄存器进行读写操作。9200 SPI控制器作为主机时工作模式流程如图3所示[1]

 

3  9200 SPI控制器主机模式流程图

需要注意的是,SPI使能后,只有在SPI_TDR(发送数据寄存器)中有数据时,才会根据片选配置(固定外设或可变外设)使能相应片选,而SPI_TDR中无数据时,则片选自动禁用。因此,9200MCP2515连续发送多个字节时,要保证在前一个字节传输完毕前,后一个字节就被写入到SPI_TDR中,以避免片选被自动禁用。同时,在传输完每一个字节后,还要读取SPI_RDR(接收数据寄存器)。

MCP2515的读指令为例,如图4所示为驱动程序完成一次读指令操作(只读一个字节数据)的过程,图中假设9200 SPI采用固定外设的片选配置方式。其他指令的软件实现流程与读指令类似。

 

4  SPI读指令操作软件流程图

3  驱动程序设计

       驱动程序是应用程序与硬件之间的中间软件层,它完全隐蔽了设备工作的细节。用户通过一组标准化的系统调用来完成相关的硬件操作,而驱动程序就是把这些标准化的系统调用映射到具体的设备操作上。嵌入式linuxPC平台下的linux设备驱动程序的设计方法基本一样。Linux操作系统根据设备中信息传送方式的不同,将设备分成三种类型:字符设备、块设备和网络设备[3]9200MCP2515的通信都是通过SPI接口以字节为单位进行的,因此MCP2515属于字符设备。由于5MCP2515共享9200的一个SPI接口,因此采用一个驱动程序来管理所有的MCP2515,这样做有利于对所有设备进行统一管理。

3.1   驱动程序中定义的主要数据结构

       CAN总线通信是基于报文帧的,在驱动程序中,无论发送数据还是接收数据都是基于报文帧的操作[4],因此需要设计合适的数据结构以满足数据操作的需要。

3.1.1  接收与发送CAN报文帧结构体

typedef struct {

       unsigned char node_num;

       unsigned int  id;

       unsigned char dlc;

       unsigned char data[8];

       int          ext_flag;

       int          rtr_flag;

} CanFrame;

       其中,node_numMCP2515的节点号(04),idCAN报文帧的标识符,dlc为数据长度(08),dataCAN报文帧的数据缓冲区,ext_flag用于标识CAN报文帧是否为扩展帧,rtr_flag用于标识CAN报文帧是否为远程帧。

3.1.2  设备配置结构体

1)波特率和报文滤波配置结构体

typedef struct{

unsigned char  node_num;

CanBaudRate  baudrate;

CanFilter      filter;

int                   br_flag;

int                    fi_flag;

} CanDevConfig;

       其中,node_numMCP2515的节点号(04),baudrateCAN总线通信速率,filter为报文滤波配置结构,br_flag用于标识波特率配置是否有效,fi_flag用于标识报文滤波配置是否有效。 baudratefilter的数据结构结构类型定义如下所示:

typedef enum {

_125kbps,

_250kbps,

_500kbps,

_1Mbps

} CanBaudRate;

 

typedef struct{

       unsigned int mask_0;

       unsigned int mask_1;

       unsigned int filter_0;

       unsigned int filter_1;

       unsigned int filter_2;

       unsigned int filter_3;

       unsigned int filter_4;

       unsigned int filter_5;

}CanFilter;

2)工作模式配置结构体

typedef struct{

       unsigned char node_num;

       unsigned char oper_mode;

} CanDevMode;    

其中,node_num意义同上,oper_mode表示该节点的工作模式,MCP2515共有五种工作模式分别是:配置模式、休眠模式、仅监听模式、回环模式和正常模式,一般设备都工作在正常模式。

3.1.3 环形数据接收缓冲区结构体

typedef struct {

       CanFrame can_recv_buf[RECV_BUF_SIZE];

       int       recv_pos;

       int       read_pos;

       wait_queue_head_t  wq;

} CanDev;

其中,can_recv_buf为接收CAN报文帧环形数据缓冲区,recv_posread_pos分别表示数据存入和读出缓冲区的位置。wq定义的是一个等待队列,用于实现阻塞型read操作。

3.2  驱动程序接口

       驱动程序的接口主要分为3个部分:(1)初始化与退出函数接口,完成设备安装和卸载等操作;(2)文件系统接口,由file_operations数据结构来完成;(3)与设备的接口,完成对设备的读写等操作。

3.2.1  初始化与退出函数

       在安装驱动程序时,操作系统会调用初始化函数进行设备注册、设备初始化以及安装中断处理例程等操作。参考文献[3]详细论述了设备注册的方法,而在这个部分我们主要讨论设备初始化时的配置方法。在本驱动程序中设备初始化分两步,一是对9200SPI控制器初始化,二是对5MCP2515初始化。

9200 SPI控制器的初始化配置方式是:

将相应的I/O口线配置给SPI控制器,使能SPI控制器时钟;

配置SPI模式寄存器(SPI_MR),使SPI控制器工作在主机模式,固定外设选择,片选译码使能,SPI时钟为MCK,片选延迟为6MCK周期;

配置SPI片选寄存器(SPI_CSR0SPI_CSR3),配置方式为:SPI 0,0模式(CPOL=0NCPHA=1)或1,1模式(CPOL1NCPHA=0),传输位为8位,SPCK波特率为10MHzSCBR=3),SPCK前延迟为5MCK周期,连续传输间延迟为0

配置SPI控制寄存器(SPI_CR,使能SPI

MCK是系统主时钟,它是9200处理器时钟的3倍分频,MCK60MHz

MCP2515的初始化配置方式是:

MCP2515发送复位指令;

配置默认的CAN总线波特率;

配置默认的报文滤波方式为无报文滤波并接收所有的报文;

使MCP2515进入回环模式。

5MCP2515在初始化函数中都要进行初始化配置。

       接下来是在初始化函数中安装中断处理例程。根据硬件设计,一共需要安装三个中断处理例程以对应三个外部中断IRQ0IRQ1IRQ2。需要注意的是,申请的中断号就是外部中断的外设ID。我们在3.2.2节将对这三个中断处理例程进行分析。

       在卸载设备驱动程序时会调用退出函数,退出函数主要完成设备的注销和中断释放。

       参考文献[3]详细论述了中断处理例程的安装、设备注销和中断释放的方法,在此不再详述。

3.2.2  中断接收服务例程

MCP2515收到CAN报文帧后,产生中断并将 引脚置低。9200响应外部中断,并调用和外部中断相对应的中断处理例程。我们一共有三个中断处理例程,at91_mcp2515_irq_handler_0响应IRQ0的中断,at91_mcp2515_irq_handler_1_2响应IRQ1的中断,at91_mcp2515_irq_handler_3_4响应IRQ2的中断。其中IRQ0只和一个MCP2515相连,而IRQ1IRQ2分别被两个MCP2515所共享。IRQ0的中断处理流程如图5所示。IRQ1的中断处理流程如图6所示。IRQ2IRQ1的中断处理流程相同。

 

5 IRQ0的中断处理流程

需要注意的是在图5的处理流程中并没有清中断操作,这是因为我们采用RX读缓冲区指令读取MCP2515 RX缓冲区中的数据,该指令操作结束后,MCP2515会自动清除相应的接收中断标志位。

 

6 IRQ1的中断处理流程

3.2.3  文件系统接口定义

       文件系统接口struct file_operations的成员全都是函数指针,这些指针指出了设备驱动程序所提供的入口点位置,对于程序员来说,编写驱动程序的任务之一就是实现file_operations中函数指针所指向的函数。本驱动程序所定义的file_operations为:

static struct file_operations at91_mcp2515_fops = {

       owner:    THIS_MODULE,

       write:      at91_mcp2515_write,

       read:            at91_mcp2515_read,

       ioctl:       at91_mcp2515_ioctl,

       open:       at91_mcp2515_open,

       release:   at91_mcp2515_release,

};

       其中的每一项都对应着一个我们在驱动程序中实现的的函数。


3.2.4  openrelease函数


上层的应用程序要对设备进行操作之前,必须先调用open函数打开设备。为了避免资源竞争,驱动程序只允许一个进程调用open函数打开设备并对设备进行操作。在open函数中完成的操作是:初始化环形数据缓冲区,初始化等待队列。

       由于同时只能有一个进程打开设备,因此当该进程调用close函数关闭设备后,内核就会调用release函数关闭设备。release函数完成的操作是:禁用所有处于活动状态的节点的内部中断,将节点工作模式设置为回环模式,同时将活动节点对应的外部中断禁用,最后将活动节点的状态设置为非活动状态。需要说明的是,在驱动程序中用一个全局静态数组node_status[5]来维护各个节点的状态(ACTIVEINACTIVE),状态为ACTIVE的活动节点表示该节点已经接入了CAN总线,可正常工作。

3.2.5  ioctl函数

ioctl函数用于对设备进行配置。我们在ioctl函数中实现了两个命令,IOCTRL_CONFIG_CAN_DEV用于配置节点的CAN总线波特率和报文滤波,IOCTRL_SET_OPER_MODE用于配置节点的工作模式。这两种配置命令所对应的配置参数都是指向应用层相应数据结构的指针,两个配置结构在3.1.2节已经介绍过了。

IOCTRL_CONFIG_CAN_DEV命令配置波特率和报文滤波时,在配置完成后,如果节点处于INACTIVE状态,则需要使能节点内部的接收中断,使能节点所对应的外部中断,并将节点状态设置为ACTIVE。在通常情况下,通过ioctl函数对需要配置的节点执行完IOCTRL_CONFIG_CAN_DEV命令后,还要再对配置过的节点执行IOCTRL_SET_OPER_MODE命令使节点处于正常的工作模式。


3.2.6  read函数

上层的应用程序通过调用read函数来读取存储在内核空间环形缓冲区中的CAN报文帧数据。通过比较recv_posread_pos的值来判断接收缓冲区中是否有数据。当在接收缓冲区中没有数据时,如果进程是以普通读写的方式打开该设备,则要调用interruptible_sleep_on函数使进程休眠,直到中断发生后再唤醒进程继续进行读缓冲区操作,这样便实现了阻塞型读操作。如果在打开设备时加入了O_NONBLOCK标志,则在无数据可读时直接返回。当接收缓冲区中有数据时,则通过copy_to_user函数将缓冲区中的数据从内核空间拷贝到用户空间,并更新read_pos

3.2.7   write函数


上层的应用程序通过调用write函数向设备写入数据,即通过某个MCP2515节点发送CAN报文帧。在驱动程序中并没有实现发送缓冲区,MCP2515本身具有三个发送缓冲器,我们可以利用它自身的发送缓冲器来构造环形缓冲区,用全局静态数组TXB_curr_num[5]来维护每个节点的当前发送缓冲器编号(TXB0TXB1TXB2)。通过copy_from_user函数将要发送的报文帧数据从用户空间拷贝到内核空间,然后再根据发送节点的当前发送缓冲器编号将数据写入节点的发送缓冲器中,并使能节点开始发送数据。发送完毕后要对该节点的当前发送缓冲器编号进行更新。

3.2.8   关于竞争问题

我们的系统是单CPU系统,并采用Linux 2.4.19内核,它是非抢占的。同时,我们设计的驱动程序也只允许一个进程打开并操作该设备。在这种情况下驱动程序中所涉及到的竞争问题主要就是中断处理程序内核代码和其他设备操作的内核代码之间的资源竞争。在我们上文中所提到的所有设备操作中,都要通过9200SPI接口与MCP2515进行通信。9200MCP2515进行通信都是以命令字节开始的,并且在一个命令操作过程中(一般会连续传输多个字节),片选和时钟是不能被禁用的,否则操作就会失败。因此,MCP2515的一个完整的命令操作就是一个临界区域,在对MCP2515进行一个命令操作的过程中必须禁用所有的中断,以保证命令操作的正常执行。在驱动程序中,我们采用local_irq_savelocal_irq_restore函数对中断禁用和恢复,在这两个函数调用之间,就是我们对MCP2515执行一个命令操作的代码。

4   结语

本文针对特有的应用需求提出的多路CAN总线接口和驱动程序设计,经过测试可以稳定正常的运行。关于驱动程序的编译和运行方法,参考文献[3]有很好的说明。上层的测试程序编写也比较简单,但要注意数据结构的定义和底层驱动程序的一致性。本文侧重介绍设计的基本方法,实现基本的功能。MCP2515本身提供了许多的功能,在实现基本功能的基础上,我们也可以根据自己的应用需要再进行功能扩展。

 

参考文献

[1] Atmel corporation. AT91RM9200 data sheet, Rev.2006.

[2] Microchip Technology Inc. MCP2515 data sheet,2005.

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

[4] 杜尚丰,曹晓钟,徐津.CAN总线测控技术及其应用[M].北京:电子工业出版社,2007.

阅读(2725) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~