引言
扫描式荧光定量检测仪由于能够获取更加精准的待测物浓度信息,为临床诊断提供更加准确可靠的评价依据,正成为各国大力发展的重要POCT检测平台。
通过使用步进电机进行荧光纸条扫描检测,可以做到扫描检测定位精准,不受各种干扰因素影响,扫描检测过程中的误差不会积累,而且控制性能十分优异,启动停止精度级别可以控制在几毫秒之内。
在扫描式荧光定量检测仪研发过程中,自主设计步进电机驱动电路,控制电机高效平稳运行,并通过光耦对电机进行精确定位,用于控制电机停止或进行检测。扫描式荧光仪搭载嵌入式Linux系统。嵌入式Linux操作系统是将流行的Linux操作系统进行裁剪,使之能在嵌入式计算机系统上运行的一种操作系统,嵌入式Linux既继承了Internet上无限的开放源代码资源,又具有嵌入式操作系统的特性。嵌入式Linux的优点是免版权费,拥有全世界自由软件者开发支持,网络性能优异,代码开放,软件移植容易[1]。
1 硬件系统设计
扫描式荧光仪以三星S3C2440微处理器为核心。S3C2440处理器是基于ARM920T的16/32位RISC嵌入式处理器。
1.1 S3C2440控制THB6128
硬件系统框图如图1所示。
图1 系统框图
S3C2440芯片控制高细分两相混合式步进电机驱动芯片THB612。S3C2440处理器通过EINT0引脚、EINT7引脚、GPB1引脚与THB6128芯片相连。步进电机通过皮带与荧光试纸条卡槽相连接,进而带动卡槽运动。卡槽下侧安装有挡板,用于遮挡光耦光线,在仪器固定位置处安装有两个光耦,当挡板遮挡光耦光线时,光耦触发光电信号,进而控制电机停止或开始一次荧光检测。
1.2 电机驱动芯片电路
步进电机驱动电路如图2所示。为节约仪器研发成本,缩小仪器空间,所以自主设计步进电机驱动电路。步进电机采用SST39C1010两相步进电机。在步进电机驱动电路中,使用高细分两相混合式步进电机驱动芯片THB6128。首先从S3C2440芯片中连接三路控制引脚与THB6128芯片连接,EINT7用于电机使能,EINT0用于电机方向控制,GPB1用于步进电机脉冲输出。这三路信号先经过TLP521_4光耦进行电气隔离,增加安全性,减小电路干扰。THB6128芯片工作电压为24 V。电路通过两路降压芯片LM2596和LM2672为电机提供细分设置电压。为增加电机运动可调性,设置可调电阻RV1、RV2,分别用于电机的细分模式控制、工作电流控制。最后输出两相电机信号OUT1A、OUT2A、OUT1B、OUT2B给步进电机,即可驱动步进电机平稳高效运行[2]。
图2 步进电机驱动电路
2 驱动程序设计
Linux内核结构体系可分为应用程序、库函数、操作系统(内核)、驱动程序。在Linux操作系统中,驱动程序是操作系统内核与硬件设备的直接接口,驱动程序屏蔽了硬件的细节,驱动程序是内核的一部分,它具有以下功能:对设备初始化和释放,比如向内核注册这个程序,这样应用程序传入文件名时,内核才能找到相应的驱动程序。对设备进行管理,包括实时参数设置以及提供对设备的操作接口。读取应用程序传送给设备文件的数据并回送给应用程序请求的数据。检测处理设备出现的错误。
应用程序通过Linux系统的调用实现与内核通信。由于Linux中将设备当做文件处理,所以对设备进行操作的调用和对文件操作的操作类似,主要包括open()、read()、write()、ioctl()、close()等接口函数。应用程序发出系统调用命令后,会从用户态转到内核态,通过内核将open()等的系统调用转换成对物理设备的操作。在Linux中通过分层实现对物理设备的调用,这样使得内核的结构清晰,提高了模块化的独立性[3]。
2.1 驱动程序结构体
file_operations结构体在头文件linux/fs.h中定义,用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址。步进电机驱动程序的file_operations结构体为:
static struct file_operations dev_fops = {
.owner=THIS_MODULE,
.ioctl=pwm_ioctl,
};
光耦驱动程序file_operations结构体为:
static struct file_operations dev_fops = {
.owner=THIS_MODULE,
.open=Optocoupler_open,
.release=Optocoupler_close,
.read=Optocoupler_read,
.poll =Optocoupler_poll,
};
在电机驱动程序中内核只需要实现ioctl接口,用于控制电机的方向、速度及运行停止。步进电机的read、write、open等驱动接口函数没有实现,则采用Linux内核默认设置。光耦驱动程序中则需要实现open、close、read、poll等操作。
2.2 步进电机驱动程序编写
步进电机驱动程序主要实现ioctl函数,用于控制电机运行、停止,及开始检测。在驱动程序ioctl函数中使用到如下函数:s3c2410_gpio_cfgpin、s3c2410_gpio_setpin、s3c2410_gpio_getpin。该系列函数定义在arch/arm/machs3c2410/gpio.c中,宏定义在include/asmarm/archs3c2410/regsgpio.h中,是用于对s3C2410 CPU引脚进行设置的函数。
s3c2410_gpio_cfgpin用于设置GPIO引脚复用功能,s3c2410_gpio_setpin用于设置GPIO引脚的输出值,s3c2410_gpio_getpin用于获取GPIO引脚对应值。
(1) 步进电机ioctl函数实现
static int pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){
s3c2410_gpio_cfgpin(S3C2410_GPF(0), S3C2410_GPIO_OUTPUT);//设置GPF0引脚为输出功能,此引脚用于方向控制
s3c2410_gpio_setpin(S3C2410_GPF(0), direction);//设置GPF0引脚输出值,direction为正代表电机正转,为负代表电机反转
s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);//设置GPB0引脚为PWM输出模式,再通过对TCON、TCFG1、TCFG0等寄存器做相关设置使GPB0输出一个合适的脉冲,用于控制电机旋转,通过设置脉冲频率可以控制电机旋转速度
……
}
S3C2440芯片中一共有5个16位定时器,其中4个定时器(定时器0~3)具有脉宽调制功能,因此用S3C2440处理器可以很容易地将GPB0复用为PWM功能。PWM是通过引脚TOUT0~TOUT3输出的,而这4个引脚是与GPB0~GPB3复用的,因此要实现PWM功能首先要把相应的引脚配置成TOUT输出[4]。
(2) 步进电机Stop函数实现
static void PWM_Stop(void) {
s3c2410_gpio_cfgpin(S3C2410_GPB(0), s3C2410_GPIO_OUTPUT); //设置GPB0为输出模式
s3c2410_gpio_setpin(S3C2410_GPB(0), 0); //输出低电平,即停止输出脉冲,则电机停止
}
2.3 光耦驱动程序设计
步进电机一次测试过程需要依靠光耦进行精确定位,光耦用于判定电机是否到位及是否应该开始检测,光耦驱动使用中断方式进行信号检测[6]。首先在file_operations的Optocoupler_open函数中进行中断注册,中断注册以后,编写中断响应函数,当注册的中断到达时,自动执行中断响应函数。
(1) 光耦驱动结构体文件
struct button_irq_desc {
int irq;//中断号
int pin;//光耦引脚
int pin_setting;//光耦引脚设置
int number;//光耦序列号
char *name;//光耦名称
};
(2) 中断注册函数
int err;
err=request_irq(button_irqs[i].irq, Optocoupler_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]);
if (err)
break;
其中,参数button_irqs[i].irq表示申请的硬件中断号, Optocoupler_interrupt为系统注册的中断处理子程序, IRQ_TYPE_EDGE_BOTH将中断方式设置为双沿触发, button_irqs[i].name为指向设备名称的字符指针,(void *)&button_irqs[i])用来标识产生中断的设备。
(3) 光耦中断响应函数
static irqreturn_t Optocoupler_interrupt(int irq, void *dev_id){
int down;
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
down=!s3c2410_gpio_getpin(button_irqs->pin);//获取当前引脚状态位
if(down!=(key_values[button_irqs->number] & 1)) {
key_values[button_irqs->number] = '0' + meet;//引脚状态位发生改变时,重新设值
Optocoupler_mett = true;//遇到光耦
wake_up_interruptible(&button_waitq);//唤醒其他等待中断
}
return IRQ_RETVAL(IRQ_HANDLED);
}
在file_operations中的Optocoupler_read接口函数中,由于内核空间与用户空间的内存不能直接互访,因此借助函数copy_to_user()将内核空间中的key_values值复制到用户空间的buff中。copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
然后应用程序便可调用read函数直接读取光耦状态位。
3 电机应用程序编写
扫描式荧光仪应用软件使用Linux环境下的QT开发,QT是一个跨平台的C++图形用户界面应用程序框架,它提供给应用程序开发者建立艺术级的图形用户界面所需的所有功能,其最大功能便是跨平台,可支持Windows、Linux、嵌入式Linux、Mac OS等多种操作系统,代码只需做很少改动就可以在多种平台上编译运行[6]。应用程序需要在电机运行过程中实时获取光耦状态位用于控制电机的停止或运行。
3.1 循环定时器设置
设计一个循环定时器用于光耦状态位的检测。
QTimer *Timer=new QTimer;
Timer->Start(5);//5 ms后定时开始
Connect(Timer,SIGNAL(TimerOut()),this,SLOT(checkStatus());//5 ms以后进行光电开关状态位的检测
3.2 光耦状态位检测函数
在checkstatus函数中,再度重新设置Timer->start(5),即定时器循环启动。然后打开光耦驱动,并读光耦驱动。根据光耦状态标志位来发出不同信号,若内部光耦状态位发生变化,则发 InnerOptocouplers()信号,若外部光耦状态位发生变化,则发 OuterOptocouplers()信号。由于checkstatus函数被循环启动,所以程序一直在检测光耦状态位。
当电机带动卡槽挡板遇到外部光耦时。程序接收到OuterOptocouplers()信号,此时电机停止。当卡槽挡板遇到内部光耦时,接收InnerOptocouplers()信号,此时电机开始荧光试剂检测过程。软件执行流程图如图3所示。
图3 软件执行流程图
结语
编写好应用程序,安装驱动模块,将应用程序交叉编译在ARM板上运行。经检测,电机运行平稳无噪音,光耦定位精准。电机可顺利实现正反旋转、加减速度、停止及一次开始检测过程,完全满足了仪器检测要求,且检测精度超过了同期类似产品。