英创信息技术精简ISA总线Linux编程 – Part3简介

描述

精简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);
}
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分