精简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读写操作,对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_AD1 | ISA地址数据总线 |
ISA_AD2 | ISA地址数据总线 |
ISA_AD3 | ISA地址数据总线 |
ISA_AD4 | ISA地址数据总线 |
ISA_AD5 | ISA地址数据总线 |
ISA_AD6 | ISA地址数据总线 |
ISA_AD7 | ISA地址数据总线,MSB |
ISA_CSn | ISA片选控制信号CS,低电平有效 |
GPIO24 | ISA同步总线周期时钟BCLK |
ISA_ADVn | ISA地址锁存控制信号ADV,低电平有效 |
ISA_RDn | ISA数据读控制信号RD,低电平有效 |
ISA_WEn | ISA数据写控制信号WE,低电平有效 |
GPIO12 | DMA请求信号DMAREQ,输入,高电平有效 |
注意:在使用硬件DMA数据传输时,将禁止使用挂角GPIO12和GPIO24的GPIO功能、禁止使用CAN2端口。
DMA传输总线时序说明
图1是一次完整的DMA传输总体时序图。
图1 硬件DMA传输总线时序
从上面的时序可见,DMAREQ请求开始,到第一个总线周期,大约有640ns的延时。整个传输周期大约1840ns。按2000ns计算,采用硬件DMA传输,可实现每秒4MB字节的数据传输率。若假设4路模拟通道,每个样点16-bit量化,这样就对应每通道500ksps的采样率。这样的采样率可满足绝大部分的工控应用需求。展开图1观察,可见:
图2 硬件触发DMA传输时序前半部分
图3 硬件触发DMA传输时序后半部分
从上面的时序图可见,有DMA启动的总线周期,每个周期只有6个BCLK脉冲,读取2个数据字节。这与在《精简ISA总线编程– Part 2》中介绍的CPU启动的DMA操作不同。在使用时需特别注意。DMAREQ的脉冲宽度有一定要求:DMAREQ脉冲宽度应大于240ns,才能保证可靠触发DMA,其次DMAREQ应在DMA传输周期结束前变低,否则可能误触发下一次DMA传输。
每个总线周期详细的时序关系如下:
图4 硬件触发DMA总线周期时序
图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); } |
全部0条评论
快来发表一下你的评论吧 !