精简ISA总线Linux编程 – Part3

 2019-3-22     作者:Emtronix     [nemail]    
[lablebox]

  精简ISA总线接口是一种8-bit宽度的双向并行扩展总线,其特点是地址数据分时复用8位总线,加上4条总线控制信号,即可实现对外部数据的快速读写。若再使能一条总线时钟信号(共13条信号),就可实现高达10MB/s以上的数据传输。精简ISA总线作为英创主板的特色功能之一,在ESM6802、ESM7000、ESM7100、ESM335x等多款型号中均有配置。


  关于对精简ISA总线接口的应用编程的基本方法,请参考《精简ISA总线编程– Part 1》;应用程序直接启动DMA做定长数据的传送方法,请参考《精简ISA总线编程– Part 2》。本文介绍由外部硬件触发DMA传送,应用程序通过ISA驱动(/dev/em_isa)读取采集数据的方法。


硬件DMA的基本工作原理


  下图是基于硬件DMA实现高速数据采集功能的系统框图:


精简ISA总线编程– Part3-1.png


  ● 应用程序通过常规的异步ISA读写操作,对AD采集单元进行必要配置。

  ● 通过特殊的isa_write_buf(..)操作启动硬件DMA。

  ● 当AD采集单元转换数据准备好,发出DMA请求信号(DMAREQ置高,脉冲宽度400ns – 1000ns)。

  ● DMA控制器感受到DMAREQ信号,连续产生4个同步总线周期,读取AD单元内已准备好的数据,每个同步周期读取2个字节,共读取8个字节。从DMAREQ请求开始,到DMA数据传输完毕,整个过程大约1840ns。之后DMA将等待一下一个DMAREQ脉冲信号。

  ● DMA读取的数据将自动存入驱动程序内部的环形Buffer中,当DMA读取的数据达到一定阈值(4KB)时,驱动将通过事件触发应用程序读取整块数据。


  由于AD单元中的数据是通过DMA硬件存入系统缓冲区的,由此产生的CPU开销就很低。应用程序可在数据采集的同时,完成必要的数据处理、显示、通讯等功能块。另一方面,由于AD采集单元不再需要保存转换的数据,可有效降低硬件成本。


  实现基于硬件DMA的数据采集,需要以下信号:


ESM7000信号管脚实际信号功能

ISA_AD0

ISA地址数据总线,LSB
ISA_AD1ISA地址数据总线

ISA_AD2

ISA地址数据总线
ISA_AD3ISA地址数据总线

ISA_AD4

ISA地址数据总线
ISA_AD5ISA地址数据总线

ISA_AD6

ISA地址数据总线
ISA_AD7ISA地址数据总线,MSB

ISA_CSn

ISA片选控制信号CS,低电平有效
GPIO24ISA同步总线周期时钟BCLK

ISA_ADVn

ISA地址锁存控制信号ADV,低电平有效
ISA_RDnISA数据读控制信号RD,低电平有效

ISA_WEn

ISA数据写控制信号WE,低电平有效
GPIO12DMA请求信号DMAREQ,输入,高电平有效


  注意:在使用硬件DMA数据传输时,将禁止使用挂角GPIO12和GPIO24的GPIO功能、禁止使用CAN2端口。


DMA传输总线时序说明


  图1是一次完整的DMA传输总体时序图。


dma-ext-read-bl4-sync-1.png


精简ISA总线编程– Part3.png

图1 硬件DMA传输总线时序


  从上面的时序可见,DMAREQ请求开始,到第一个总线周期,大约有640ns的延时。整个传输周期大约1840ns。按2000ns计算,采用硬件DMA传输,可实现每秒4MB字节的数据传输率。若假设4路模拟通道,每个样点16-bit量化,这样就对应每通道500ksps的采样率。这样的采样率可满足绝大部分的工控应用需求。展开图1观察,可见:


dma-ext-read-bl4-sync-2.png

图2 硬件触发DMA传输时序前半部分


dma-ext-read-bl4-sync-3.png

图3 硬件触发DMA传输时序后半部分


  从上面的时序图可见,有DMA启动的总线周期,每个周期只有6个BCLK脉冲,读取2个数据字节。这与在《精简ISA总线编程– Part 2》中介绍的CPU启动的DMA操作不同。在使用时需特别注意。DMAREQ的脉冲宽度有一定要求:DMAREQ脉冲宽度应大于240ns,才能保证可靠触发DMA,其次DMAREQ应在DMA传输周期结束前变低,否则可能误触发下一次DMA传输。


  每个总线周期详细的时序关系如下:


dma-ext-read-sync.png

图4 硬件触发DMA总线周期时序


精简ISA总线编程– Part3-2.png

图5 硬件触发DMA总线周期时序参数标注


  为了简化AD采集单元的电路设计,硬件触发DMA传输总线周期输出的地址固定在0xE0。AD采集单元的其他寄存器应避免使用0xE0 – 0xE1这两个地址。


应用程序设计要点


  应用程序启动DMA数据传输,需要使用数据结构struct isa_transfer的传递参数和数据,struct isa_transfer的结构定义如下:


structisa_transfer
{
       void              *rx_buf;                /* != NULL: buffer for bus read */
       void              *tx_buf;                /* != NULL: buffer for bus write */
       unsigned     len;                      /* buffer length in byte */
       unsigned     offset;                  /* offset,port address on isa bus */
       unsigned     inc;                      /* = 0: fixed offset, = 1: offset+1 after r/w */
};


  启动硬件触发DMA传输,需要特殊的写操作,代码如下:


structisa_transfer      t;
 
// start ext-trigger dma
memset(&t, 0, sizeof(structisa_transfer));
t.offset = 0x50E0;
t.len = 0xFFFFFFFF;           
isa_write_buf(fd, &t);


  注意在上述代码中t.rx_buf和t.tx_buf均必须为空。停止硬件触发DMA传输的代码为:


structisa_transfer      t;
 
// stopext-trigger dma
memset(&t, 0, sizeof(structisa_transfer));
t.offset = 0x50E0;
t.len = 0;
isa_write_buf(fd, &t);


  在启动DMA后,应用程序的数据接收线程需调用poll等待数据ready的消息:


structpollfdfds[1];
 
fds[0].fd = fd;
fds[0].events = POLLPRI;
 
// wait data ready with timeout 2 seconds
if(poll(fds, 1, 2) == -1) {
perror("poll failed!\n");
goto error;
    }
// data is ready….


  读取数据的代码为:


intrc, total_bytes;
structisa_transfer      t;
externunsignedchargbuf[64 * 1024];
 
total_bytes = 0;
memset(&t, 0, sizeof(structisa_transfer));
// read data
t.rx_buf = gbuf;
t.offset = offset;
t.len = sizeof(gbuf);
rc = read(fd, &t, sizeof(structisa_transfer));
if(rc< 0) {
       printf("%s dma data read failed %d\n", __func__, rc);
}
elseif(rc> 0) {
       total_bytes += rc;
       printf("%s dma data read %d\n", __func__, total_bytes);
}


  有兴趣的客户可与英创公司技术联系,索取完整的测试代码源码。技术支持邮箱:support@emtronix.com

[lablebox]