首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux环境下实现基于ARM9的CAN总线通信

Linux环境下实现基于ARM9的CAN总线通信

1.  引言
        CAN(Controller Area Network)总线最早是由德国BOSCH公司提出,实现汽车环境中的微控制器通讯,在车载各电子控制装置ECU之间交换信息,形成汽车电子控制网络。由于其具有成本低,实时性好,容错性高,设计灵活等特点,目前已被广泛的应用于各种工业领域,被公认为是最有前途的现成总线之一。与此同时,随着ARM(Advanced RISC Machines)芯片及嵌入式Linux操作系统的成熟与完善,使得CAN通信的开发更为便利,应用更为广泛。本文就将从硬件,软件两方面介绍一种在Linux环境下实现基于EP9315的CAN总线通信方式。
        2.  硬件介绍及其接口实现
          本设计选用的是Cirrus Logic公司推出的EP9315处理器及PHILIPS公司推出的SJA1000独立CAN控制器。下面先对两款芯片作简要介绍,再对CAN接口硬件电路设计作详细说明。
        2.1  EP9315及SJA1000芯片简介
        EP9315是EP93XX系列微处理器的旗舰产品。内嵌先进的运行于200MHz(工业条件下推荐运行184MHz)的ARM920T微处理器核,以及支持Linux,Windows CE等操作系统的存储器管理单元MMU,16KB指令高速缓存和16KB数据高速缓存可为现有的程序和数据提供零等待时间,或者以锁存的方式确保对关键指令和数据的无延迟存取。EP9315内部集成了MaverickCrunch 数学协处理器和MaverickKey 硬件可编程ID,前者显著提高了ARM920T的浮点,整形运算与信号处理能力,同时它还具有丰富的集成外设接口,包括1/10/100Mbps以太网MAC,3通道USB2.0全速主口,SPI、I S和AC’97串行接口,PCMCIA接口,Raster/LCD接口,图像加速器,带12位A/D转换器的触摸屏接口,键盘接口,UART接口,丰富的GPIO,支持4组32位SDRAM的无缝连接等。
        SJA1000是一款高性能的CAN控制器,支持BasicCAN和PeliCANl两种工作模式,提供INTEL和Motorola两种寻址方式,地址线和数据线分时复用,SJA1000基于寄存器编址,可以通过读写寄存器来操作它。
        2.2  硬件接口电路设计
        EP9315带有16个增强型GPIO,可以通过配置PADR、PADDR寄存器使得EGPIO2控制SJ1000的ALE址锁存信号,EGPIO3接收SJA1000的中断信号,并且可以配置中断类型。由于SJA1000的数据线与地址线分时复用, 当在送地址时,RD、WR、CS信号必须无效,送数据或读数据时,RD、WR、CS信号才有效,因此结合CPLD(EPM7032芯片)或是一些逻辑门来实现该逻辑,并片选两个BANK来分别用于地址操作(nCS5_PHYBASE 0x50000000)和数据操作(nCS3_PHYBASE 0x30000000)。当读SJA1000时,首先由EP9315通过CS选通SJA1000,通过EGPIO2使得ALE有效,锁存地址,并由CS5经CPLD选通FLASH的BANK5,将地址存入映射后的BANK5区域,然后使得ALE为0取消地址锁存,最后置低EP9315的RD信号,经74LV32后使得SJA1000的读信号有效,完成读操作;写操作时,同理选通SJA1000,使能ALE,发送地址并将其锁存在BANK5,然后取消ALE,置低EP9315的WR信号,通过74LV32使能SJA1000的WR信号,接着发送数据置映射后的BANK3,将数据写入SJA1000,完成写操作。
        3.  软件分析及实现
        本文采用Linux系统,内核版本为2.4.21-rmk1,搭建ARM-LINUX交叉编译环境同时利用动态模块加载法来进行驱动开发。模块化的优点在于能将内核映像尺寸保持在最小,并且具有最大的灵活性,便于检验新的内核代码而不需重新编译,引导内核。Shell用户应用程序的开发主要采用了GDB远程调试技术,该调试环境由宿主机GDB和目标机调试stub共同构成,两者通过串口或TCP连接。驱动程序与应用程序的整体流程图如图2所示。
       

               
                                          图2 驱动程序与应用程序的整体流程

                        3.1  Linux下CAN驱动的实现
                CAN设备属于字符型设备,是以字节为单位逐个进行I/O操作的设备,在对它发出读写请求时,实际的硬件I/O紧接着就发生了。字符型设备的缓存是可有可无的,而且也不支持随机访问。应用程序可以通过标准系统调用像访问字节流(类似文件)一样来打开,读,写,关闭字符型设备。
                驱动程序主要由一下4个关键环节组成:
                ⑴模块的初始化 module_init(Mysja1000_init)
                模块的初始化函数负责注册模块所提供的任何设施。module_init的使用是强制性的,这个宏会在模块的目标代码中增加一个特殊的段,用于说明内核初始化函数所在的位置。没有这个定义,初始化函数永远不会调用。当模块正常初始化时,在初始化函数中完成了对CAN设备以及设备中断的注册,对SJA1000芯片中相关寄存器的配置,对EP9315中的EGPIO口的初始化设置,并且利用函数void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 将IO地址空间映射到内核的虚拟地址空间上去,这样就可以像读写RAM那样读写IO内存资源了。
                        ⑵服务于I/O请求的子程序
                这部分程序又被称为是驱动程序的上半部分。调用这部分程序是由于系统调用的结果。当该部分程序执行时,系统仍认为是与进行调用的进程属于同一个进程,只是由用户态变成了核心态。由于此部分程序是驱动程序与应用程序的接口,所以必须通过Linux下的一个关键的数据结构file_operations来实现该文件操作接口。file_operations结构中的成员几乎全是函数指针,实质上是一个函数跳转表。例如can_open入口点用来打开CAN设备准备进行I/O操作;can_read入口点用来实现接收CAN数据帧;can_write入口点用来实现发送CAN数据帧;can_ioctl入口点提供了一种执行CAN设备特定操作的方法,通过ioctl来实现对SJA1000寄存器的读写操作,使用户根据需要方便的配置SJA1000状态等等。
                ⑶中断服务子程序
                这部分程序又被称为是驱动程序的下半部分。Linux系统负责接收硬件中断,再由系统调用中断服务子程序,而不是直接从ARM的中断向量表中调用这部分程序。在模块初始化时已经利用request_irq( )函数注册了设备中断,所以当IRQ(中断请求)产生时,ISR(中断服务例程)运行。在中断服务程序中,首先要读取SJA1000的中断寄存器IR的值,识别中断源,比如当接收中断位RI为1则说明是接收中断,进而调用接收函数来接收数据。当CPU读取这个只读存储器时,除了RI位外的所有位都被复位。
                ⑷缓冲区操作
                SJA1000内部设有发送缓冲器TXB(13个字节),接收缓冲器RXB(13个字节)和RXFIFO(64个字节)。其中TXB是CPU和BSP(位流处理器)之间的接口,存储发送到CAN网络上的完整报文。接收缓冲器是接收过滤器和CPU之间的接口,用来接收和存储CAN总线上的报文,RXB是RXFIFO的一个窗口,可被CPU访问。为了提高收发效率,在驱动中开辟软件缓冲区,大小可以根据需要设置,将大量数据经过软件缓冲区后再进行处理。在操作软件缓冲区时,采用生产者/消费者模型,分别设定数据存入与读出的指针,并且利用memcopy函数完成对帧数据的操作。比如读取数据时,首先在操作模式下将SJA1000中起始地址为16的RXB数据读入一个数组,经过一些必要判断,再将此数组中的数据由memcopy函数传入指定的软件缓冲区。硬件缓冲与软件缓冲间一次传递一帧数据,帧有标准帧与扩展帧之分。
                3.2  Shell应用程序的实现
                    Shell应用程序主要是为用户提供一系列方便直观的接口,使得用户不必了解硬件工作的细节,不必研究驱动的具体实现,而仅仅根据自身的需要,通过一些简单明了的命令来完成所要求的任务。本文就将在Shell应用程序中实现以下命令功能:
                init初始化寄存器,rsja读取寄存器值,wsja写寄存器,gfrm收数据帧,sfrm发数据帧,conf配置滤波器,help帮助,exit退出。
                在主函数main中,通过int open(const char * filename, int mode)函数打开CAN设备,得到一个文件句柄fd,将此句柄传递给shell函数,这样就可以在各个命令函数中对CAN设备进行操作,比如可以在函数int write_sja1000(int fd,unsigned char addr, unsigned char value)中使用ioctl(fd,SJA_WRITE,®_data)来实现对SJA1000寄存器的写操作。
                在shell应用程序中,可以设定一个字符数组char cmd[5],通过fgets(cmd,5,stdin)来接收用户键入的命令,再经过strcmp函数辨认出用户的命令类型,执行相关操作。为避免输入缓冲区出现垃圾导致命令识别出现错误,需要及时清空输入缓冲,而当gcc中fflush函数无效的情况下,则可以通过while((c=getchar())!='n'&&c!=EOF){ }来清空输入缓冲。
                完成了所有驱动以及应用程序的编写后,用Makfile文件制定编译规则,然后根据规则进行编译,调试成功后可将二进制代码通过超级终端下载到板子运行,至此结束所有工作。
                4. 结语
                本文提供了一种EP9315与CAN控制器SJA1000的接口方案,详细分析了Linux操作系统下驱动与应用程序的原理及开发流程,实现了CAN设备通信,并在实际应用和测试中证明了该设计的正确性和可靠性。由于嵌入式Linux的众多优势以及CAN的实时性,易用性,可靠性等优点,它们将在工业控制及生活的各个领域具有更广泛的应用前景。
                本文作者创新点:利用EP9315与SJA1000实现CAN总线通信,完成了由硬件连接,底层驱动以致上层应用软件的所有工作,利用软件方便稳定的控制CAN通信。
继承事业,薪火相传
返回列表