基于ARM+Linux的直流伺服控制系统设计

基于ARM+Linux的直流伺服控制系统设计

张海波1,2,陈 涛1,王建立1,李洪文1,邓永停1,2 时间:2012年10月09日 来源:电子技术应用2012年第8期
字 体:  大  中  小
关键词: ARM9 嵌入式Linux CPLD Linux设备驱动 伺服控制
        Altera Samsung公司

摘  要: 目前大多伺服控制系统依赖计算机实现,系统体积功耗大、成本高。针对此问题,以ARM9 S3C2440芯片和CPLD EPM570T144芯片为硬件平台,在嵌入式Linux操作系统下设计了直流伺服控制系统。ARM实现控制算法,得出控制量给CPLD ,CPLD主要用作控制接口扩展和信号处理。详细介绍了Linux设备驱动设计和伺服控制应用程序设计。实验结果证明,系统能够实现等速跟踪、位置跟踪和正弦跟踪等控制功能,并能实现复杂控制算法,以满足控制系统实时性和高速性要求。
关键词: ARM9;嵌入式Linux;CPLD;Linux设备驱动;伺服控制

    随着数字信息技术的不断发展,人们对伺服控制系统的实时性、稳定性和复杂性的要求越来越高,单靠顺序结构的软件设计已经不容易满足上述要求。目前很多伺服控制系统的控制器采用PC/104结构或依赖上位计算机,根据实际的控制系统需要扩展相应的控制电路,使得系统体积大、成本高、可靠性不易保证,且用户交互性不好。嵌入式Linux操作系统由于具有代码开源、可移植性、软硬件可裁剪性、资源丰富及支持多种硬件平台和接口等特点,并且从2.6版本以后的Linux实时性有了很大的提高,正被越来越多地应用于伺服控制系统中。通过嵌入式Linux操作系统对控制系统的软硬件资源进行分配、调度、控制和协调,能够充分发挥控制系统的性能。ARM处理器以其体积小、低功耗、低成本、高性能、文档丰富及嵌入式软件多等优点而得到广泛的应用。因此,本文以ARM9和CPLD为硬件平台,在嵌入式Linux操作系统下设计了直流伺服控制系统。
1 硬件平台
    系统原理框图[1]如图1所示。系统以ARM作为主控芯片,主要负责运行操作系统并实现控制算法、人机交互和多机通信等。CPLD EPM570T144主要负责从ARM接收数据,产生相应的PWM波;接收编码器输出信号,并对其进行处理,得到编码器的值,将其送给ARM,从而实现电机的闭环控制。CPLD和ARM之间通过地址总线(13根)、数据总线(16根)、控制总线(片选、读写使能信号等)与GPIO口(作为外部中断使用)连接,即CPLD类似于ARM的一个外部存储器(CPLD挂接在ARM的bank1存储空间上,地址空间为0x08000000~0x10000000),ARM和CPLD的数据交换类似于对存储器的读写操作。这种总线方式扩展,使得系统数据交换快速、操作简单。控制板通过JTAG、UART、USB和网口与上位机连接,在目标板和上位机之间建立交叉开发环境,可在控制板和上位机之间实现程序下载调试、文件传输和通信等,便于系统软件开发和调试。

基于ARM+Linux的直流伺服控制系统设计_第1张图片

2 CPLD程序设计
    CPLD程序分为电机辨向、四倍频、编码器脉冲计数、PWM波生成和总线数据读写5个模块,如图2所示。采用VHDL语言,依据自底向上设计的方法,以便于程序开发和移植。

    采用增量式编码器,需对编码器输出的ABZ码进行处理[2],经过辨向、倍频、计数后得到编码器值。ARM与CPLD之间通过双向总线交换数据,CPLD读取ARM写入数据总线的数据,产生对应的PWM波。当CPLD中的编码器值可读后,CPLD采用中断方式通知ARM,然后将编码器值写到数据总线上供ARM读取。由于CPLD与ARM的其他外设共用数据总线,所以在CPLD对总线进行操作时要特别注意,除了CPLD往总线上写数据外,其他时刻都应该将总线置为高阻态,以让出总线的使用权,否则其他外设(如网口、ADC接口等)会因CPLD一直占用总线而不能正常工作。
    CPLD应用计数法产生PWM波[3],CPLD时钟频率为100 MHz,设置PWM总计数值为8 000。CPLD根据ARM给定的0~8 000的计数值对时钟计数,产生两路反相的PWM波。为防止功率放大器的H桥同一侧上下同时导通,一般设置有3~5 μs的死区,本设计中设置为5 μs的死区。
3 设备驱动设计
3.1 设备驱动简介

    设备驱动是连接应用程序与硬件设备的桥梁,驱动程序为应用程序提供了接口函数,用户在应用程序中调用相应的接口函数便可实现对硬件设备的操作,因此,驱动程序的开发是嵌入式系统开发的关键环节。Linux设备驱动分为字符设备驱动、块设备驱动和网络设备驱动[4]。本文中控制板上移植了Linux2.6操作系统,该操作系统下需设计ARM读写CPLD的数据及对CPLD产生的中断信号响应的驱动,这一要求采用字符设备驱动来实现。应用程序通过系统调用对设备文件进行诸如read、write等操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取设备驱动程序中初始化的file_operations结构体,获取相应操作(read/write等)对应的函数指针,接着把控制权交给该函数。因此,编写设备驱动程序的主要工作就是编写这些文件操作的接口函数,并填充file_operations的各个域。
3.2 设备驱动程序设计
    为便于开发和调试,设备驱动使用模块的方式动态加载到内核中去。加载模块的方式与以往的应用程序开发有很大的不同。以往在开发应用程序时都有一个main()函数作为程序的入口点,而在驱动开发时却没有main()函数,模块在调用insmod命令时被加载,此时的入口点为module_init()函数,在该函数中完成设备的注册、设备文件的创建和相关内存及寄存器的地址映射。同样,模块在调用rmmod命令时被卸载,此时的入口点为module_exit()函数,在该函数中将不用的资源返还给操作系统,把注册的设备、创建的设备文件及IO内存映射等注销掉。在设备完成注册和加载之后,用户的应用程序就可以对该设备进行一定的操作,如read、write等。而驱动就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作,module_init()入口点函数则不需要完成其他如read、wirte之类的功能。驱动程序需要定义和实现open、read、write等函数,并填充到file_operations结构中,file_operations结构把应用程序中的系统调用与驱动中对应的函数联系在一起。file_operations结构体如下所示:
static struct  file_operations    cpld_drv_fops = {
    .owner    = THIS_MODULE,
    .write    = cpld_drv_write,
    .read    = cpld_drv_read,
    .open    = cpld_drv_open,
    .release    = cpld_drv_close,
    .fasync    = cpld_drv_fasync,
};
其中,write()函数实现向CPLD中写入数据,read()函数实现ARM从CPLD读取数据。设备驱动运行在内核空间,而应用程序运行在用户空间,设备驱动程序不能直接访问用户空间的地址,在read()和write()函数中分别调用内核函数copy_to_user()和copy_from_user()实现数据的转移。read函数实现读取CPLD中的编码器值,write函数实现将产生PWM波的计数值写入CPLD中,这两个函数实现了内核空间与用户空间的数据交换。从驱动程序结构看,驱动程序由三部分组成:结构体struct file_operations及其成员函数的实现、设备初始化module_init()和设备注销module_exit()。
    读写CPLD需要对内存进行读写操作[5]。CPLD产生的读中断信号连接到ARM的GPF1口,CPLD的使能信号由ARM的GPF0产生,因此需要配置相应的寄存器。驱动程序中需要对内存和寄存器进行操作,本操作系统下不能直接对内存和寄存器的物理地址进行操作,需先将相应的内存和寄存器的物理地址映射到内核的虚拟地址空间,通过对映射后的虚拟地址进行操作实现对寄存器和内存的操作。
    ARM对CPLD的读操作采用异步通知和内核中断方式[5]实现,这样可减少系统开支。首先在驱动的open()函数中调用request_irq()函数注册内核中断,并在内核中实现中断处理函数,在内核中断处理函数中调用kill_fasyn()函数给指定的应用程序发送信号,通知应用程序CPLD中的编码器值可读。当CPLD无可读中断产生时,将read()函数放入等待队列,主程序一直处于睡眠状态,而不是应用程序主动去调用read()函数来等待中断的产生,即采用异步通知方式,调用内核中的fasync_helper()函数来实现。当CPLD有可读中断产生时,在中断处理函数中通过kill_fasync()函数,向进程发送信号SIGIO,触发应用程序中signal声明的异步触发函数,使用POLL_IN表明有数值可以读取。另外,要注意,在进入中断服务程序后,首先通过中断自旋锁spin_lock_irq()关闭所有中断,以防止其他中断源中断kill_fasync的工作,在中断服务程序结束时,再通过spin_unlock_irq()打开中断。中断处理函数部分代码如下:
spinlock_t lock;
static irqreturn_t eint1_irq(int irq, void *dev_id)
//中断服务程序
{
    spin_lock_irq(&lock);//关闭中断
    kill_fasync (&eint1_async, SIGIO, POLL_IN);
//产生中断后,驱动向应用程序发送数据可读信号
    spin_unlock_irq(&lock);//开中断
    return IRQ_RETVAL(IRQ_HANDLED);
}
4 应用程序设计

转自:http://www.chinaaet.com/article/199909

你可能感兴趣的:(基于ARM+Linux的直流伺服控制系统设计)