QNX下PEX8311多路实时数据采集的驱动设计

时间:2022-05-08 03:28:57

QNX下PEX8311多路实时数据采集的驱动设计

【摘要】实时操作系统QNX在工业领域中的应用越来越广泛,在高速数据采集方面QNX能最大限度地发挥实时操作系统的优势。本文介绍了QNX下PEX8311多路实时数据采集驱动的开发流程。详细阐述了驱动程序和应用程序之间利用共享内存方式完成进程通信的具体实现方法。最后,应用程序采用无锁环形缓存进行多路数据分发的实现。该方法也为其他操作系统实现多路实时数据采集驱动程序提供了一种全新的方法。

【关键词】QNX;PCI;共享内存;PEX8311;环形缓存

1.QNX多路实时采集卡

多路实时采集卡由基于PCI接口PEX8311和FPGA的连接组成。FPGA可以同时采集16路数据,每路数据采样率达到200k且每路可以单独设置采用率,因此最高采用率可以达到3.2M。PEX8311采用DMA工作模式中的demand方式将采集到的数据传输给运行在CPU上的QNX。驱动程序利用QNX实时性高的特点把采集到的数据实时存入到用户程序和驱动程序都可以访问的共享存储空间中。驱动进程和应用进程利用共享内存方式交换采集数据,避免了不必要的中间环节,节省了系统开销,增强了系统的实时性。

2.采集卡驱动设计

(1)资源管理器交互建立

资源管理器是QNX操作系统实现与用户程序交互数据的通道,应用程序中使用到的open(),close(),read(),write()等函数通过资源管理器接口映射到底层函数对硬件相应的操作。实现流程如下:

dispatch_create();函数实现用户程序与资源管理器交互所需要的通道

iofunc_func_init();该函数初始化应用程序功能函数层接口

iofunc_attr_init ();该函数初始化设备属性接口

resmgr_attach ();该函数向进程管理器注册,并使设备在命名空间中产生相应的名称。

(2)硬件资源管理和分配

和其他操作系统类似,QNX提供了丰富的PCI接口函数,可

以方便地注册、管理、使用PCI设备。具体流程如下:

ThreadCtl(); 设置线程对IO端口进行操作的权限

pci_attach(); 连接到QNX提供的PCI服务上

pci_attach_device();

devinit.port=inf.CpuBaseAddress[0]+(j*0x200);

devinit.intr=inf.Irq;

探测PCI设备并取得相应设备的资源,如设备首地址、中断等。

port_regbase=mmap_device_memory();

设备首地址映射到内存地址,访问设备寄存器用port_regbase+offset即可。

(3)中断注册

QNX提供了两种连接中断的接口:

Int InterruptAttachEvent(int intr,const struct sigevent *event,unsigned flags);

Int InterruptAttach(int intr,const struct sigevent*(*handler)(void*, int ), const void *area, int size, unsigned flags);

其中intr代表了中断向量号,在读取设备信息时已经被初始化在了info变量中。两个函数分别有自己的优缺点,在具体设计中应视具体情况而定。InterruptAttachEvent()函数用法简单,运行在用户空间,可以启动单独的线程去处理特定任务,优点是其应用接近linux操作系统可以方便程序移植,缺点是当中断发生时会引起上下文切换,从而使降低效率。对于InterruptAttach()函数而言,中断处理函数首先将原线程打断,然后判断中断是否需要建立新的线程处理此任务还是原线程处理此任务,所以对于不是自己要处理的中断,可以减少上下文切换的开支。本论文中采样InterruptAttach()函数处理中断,具体操作流程如图1所示。

图1 InterruptAttach处理过程

为了减少系统调度开销,采用图1的调度策略,即中断返回并没有创建新的线程来处理中断后的大量数据交换,而是在原线程中处理这些数据。

图2 共享内存示意图

(4)共享内存

驱动进程和应用进程采用共享内存的方式传递采集到的数据,PEX8311采用DMA方式,传输数据块中包含16路的采集数据,其每次传输的数据块长度为D_size,其中,由于每路的采集率可以设置为不同的采集率,所以传输数据块中包含各路的信息量可以是不同的,例如:第一路采样率为200k,第十路采样率为100k,那么数据块中包含第一路和第十路的数据量为2:1,图2表示共享内存的示意图,共享内存的大小为Shm_size=Shm_endShm_start,写指针W_ptr和读指针R_ptr沿着内存增加的方向移动,当到达共享内存底部时环回到顶端,读写指针在共享内存初始化时赋值为顶端地址,写指针W_ptr只能被中断程序修改,读指针只能被应用程序修改。所以在共享内存使用上不需要信号量,只需保证写指针W_ptr不超过读指针R_ptr即可。

当PEX8311用DMA每传输完一个数据块后产生一个中断,在中断服务程序中,除了做一些中断保护外,对共享内存的处理描述代码如下:

bufFlag = 1;

W_ptr += D_size;

if(W_ptr >= Shm_start + Shm_size)

W_ptr = Shm_start;

当中断发生后,代码D_size的数据已经传输完毕,写指针W_ptr需要被重新赋值,指针移到下一位置准备接收,当其到达共享内存的的低端时,需要重新赋值Shm_start,然后开始采集下一帧数据。然后通过返回一个信号量,退出中断,为了系统的实时性,中断处理程序尽量短,在中断中通过返回return(event)信号量办法告诉QNX操作系统中断处理函数执行完毕,需要进行调用InterruptWait()函数中断后的后续操作,中断后续操作的工作就是通知应用进程一帧数据已经采集完毕并存储在共享内存中,可以进行读操作了。函数sem_post()用来两个进程间的通信,其为信号量加1,在应用进程里函数sem_wait()实现信号量减1,函数sem_post()和函数sem_wait()均为原子操作,这样应用进程就能和驱动进程完成一个读一个写的操作。变量bufFlag为操作标志,在驱动进程里可以完成某种判读的处理。

3.应用进程接口

应用进程把得到的采集数据放到16个环形缓存(buffer)里,每个环形缓存用一个结构体表示:

struct kbuf{

unsigned char *buffer;

unsigned int size;

unsigned int in;

unsigned int out;

};

环形缓存是一个实现无锁且提供同时读写操作的特殊缓存,其容量大小size定义必须为2的幂。in为写入指针,out为读出指针。下面列出了环形缓存的写入操作代码:

len=min(len,k_buf->size-k_buf->in+k_buf->out);

l=min(len,k_buf->size-(k_buf->in&(k_buf->size-1)));

memcpy(k_buf->buffer+(k_buf->in &(k_buf->size-1)),data,l);

memcpy(k_buf->buffer,data+l,len-l);

k_buf->in+=len;

return len;

k_buf为环形缓存数据结构,data为要存入数据的地址指针,len为要写入数据的长度,该操作返回实际写入的数据长度。

4.结束语

本文介绍了PEX8311多路高速采集卡在QNX操作系统下的驱动程序设计。应用共享内存方式完成驱动和应用之间的进程通信,并利用环形缓存实现各路数据的存取。在实际应用中取得良好的效果。

上一篇:基于multisim的多路输出直流稳压电源设计 下一篇:基于STC12C5A60S2单片机综合体质测量仪的设计