如何快速访问ISA总线端口

描述

精简ISA总线是英创工控主板的特色之一,我们基于ISA总线推出了多串口、多网口、多CAN接口等扩展模块,我们的很多用户也基于ISA总线设计了自己的专有扩展单元并取得了成功。为了充分发挥ISA总线的性能,对于ISA总线的访问,我们除了提供最基本的设备驱动API操作函数外,还先后增加了ISA总线的块读写操作方法和ISA总线的DMA操作方法。当ISA总线以DMA方式进行数据传输时,在最大限度的利用ISA总线带宽的同时,又减少了ISA操作占用CPU的时间,所以利用DMA是进行批量数据传输时的首选方式。

在实际的应用中,除了成批量的数据访问外,也可能会存在对外设进行频繁的字或字节访问。英创主板的ISA总线周期通常在200ns左右,而应用程序调用一次设备驱动程序API(WriteFile,ReadFile)花费的时间却需要数微秒的时间,这显然大大降低了对外设单字(或单字节)的访问效率。为了解决这一问题,我们利用了WinCE的虚拟地址映射技术,在ISA驱动程序中实现了在使用ISA的应用进程地址空间内分配一段虚拟地址空间,并将其与ISA接口的物理地址空间进行了绑定。简单来讲就是实现了在WinCE应用程序中可以直接访问ISA总线的外设地址空间,从使用的角度看,我们实现了以下5个函数。ISAMmMap用于获得ISA总线的基地址,其余4个函数分别为字读/写和字节读/写操作函数。

  HANDLE       ISAMmMap(HANDLE hISA);                                    // 映射ISA总线物理地址空间
BYTE          ISARead8(HANDLE hMmMap, DWORD dwPortOffset);               // 读单字节
void           ISAWrite8(HANDLE hMmMap, DWORD dwPortOffset, BYTE ucValue);   // 写单字节
WORD       ISARead16(HANDLE hMmMap, DWORD dwPortOffset);                    // 读单字
void           ISAWrite16(HANDLE hMmMap, DWORD dwPortOffset, WORD wValue);       // 写单字


下面是上述5个函数实现的源代码,在ISAMmMap函数中调用DeviceIoControl,通过IOCTL_VIRTUAL_COPY_EX命令获取ISA总线的基地址。

  // Function: Get the base address of ISA Port
// Input:    hISA: Handle of ISA1:
// Return:   Base address of the ISA Port
HANDLE ISAMmMap(HANDLE hISA)
{
       DWORD       dwMemBase;
       if(DeviceIoControl(hISA,         //打开“ISA1:”返回的Handler
              IOCTL_VIRTUAL_COPY_EX,      // IOCTL命令码    
              NULL,0,                                // 不使用输入参数
              &dwMemBase, sizeof(DWORD),  // 得到ISA基地址   
              NULL, NULL))
              return (HANDLE)dwMemBase;
       return NULL;
}
// Function: read a byte from a port on ISA bus
// Input: hMmMap: Base address of the ISA Port
//         dwPortOffset = 0, 1, .. 255, address of port on ISA
// Return: the byte data read
BYTE ISARead8(HANDLE hMmMap, DWORD dwPortOffset)
{
       WORD   *pPortAddr;
       WORD   wValue;
       dwPortOffset &= 0xff;
       dwPortOffset <<= 1;               // D[0..7] <=> A[1..8] in AD-muxed mode
       pPortAddr = (WORD*)((DWORD)hMmMap + dwPortOffset);
       wValue = *pPortAddr;
       return (BYTE)wValue;
}
// Function: write a byte to a port on ISA bus
// Input: hMmMap: Base address of the ISA Port//
//        dwPortOffset = 0, 1, .. 255, address of port on ISA
//        ucValue = the byte data to be written
void ISAWrite8(HANDLE hMmMap, DWORD dwPortOffset, BYTE ucValue)
{
       WORD   *pPortAddr;
       dwPortOffset &= 0xff;
       dwPortOffset <<= 1;               // D[0..7] <=> A[1..8] in AD-muxed mode
       pPortAddr = (WORD*)((DWORD)hMmMap + dwPortOffset);
       *pPortAddr = (WORD)ucValue;   
}
// Function: read a word from a port on ISA bus
// Input: hMmMap: Base address of the ISA Port
//        dwPortOffset = 0, 2, 4, .. 254, address of port on ISA
// Return: the word data read
WORD ISARead16(HANDLE hMmMap, DWORD dwPortOffset)
{
       DWORD       *pPortAddr;
       DWORD       dwValue;
       dwPortOffset &= 0xFE;           // 2-byte alignment
       dwPortOffset <<= 1;               // D[0..7] <=> A[1..8] in AD-muxed mode
       pPortAddr = (DWORD*)((DWORD)hMmMap + dwPortOffset);
       dwValue = *pPortAddr;
       // the high-byte of data is at value[23..16]
       return (WORD)(((dwValue >> 8) & 0xFF00) | (dwValue & 0xFF));     
}
// Function: write a word to a port on ISA bus
// Input: hMmMap: Base address of the ISA Port
//        dwPortOffset = 0, 2, 4, .. 254, address of port on ISA
//        wValue = the word data to be written
void ISAWrite16(HANDLE hMmMap, DWORD dwPortOffset, WORD wValue)
{
       DWORD       *pPortAddr;
       DWORD       dwValue;
       dwPortOffset &= 0xFE;           // 2-byte alignment
       dwPortOffset <<= 1;               // D[0..7] <=> A[1..8] in AD-muxed mode
       pPortAddr = (DWORD*)((DWORD)hMmMap + dwPortOffset);
       dwValue = wValue;
       // dispatch high-byte of data to value[23..16]
       *pPortAddr = ((dwValue << 8) & 0x00ff0000) | (dwValue & 0x000000ff);
}


我们在ESM3352上,分别测试了利用地址映射方法直接访问ISA外设地址和调用设备驱动API函数读写ISA地址,测得的结果是当进行字(WORD)读写时,应用程序直接访问ISA外设地址比调用设备驱动API函数快10倍以上,当时行字节(BYTE)读写时,应用程序直接访问ISA外设地址比调用设备驱动API函数快15倍以上。

下面是测试程序源代码:

  int _tmain(int argc, _TCHAR* argv[])
{
       HANDLE       hISA, hMmMap;
       BYTE             ucValue;
       WORD          wValue;
       DWORD       dwStartTick, dwEndTick, i, k, cnt=2000;
       hISA = CreateFile(_T("ISA1:"),        // name of device
              GENERIC_READ|GENERIC_WRITE,         // desired access
              FILE_SHARE_READ|FILE_SHARE_WRITE,      // sharing mode
              NULL,                                               // security attributes (ignored)
              OPEN_EXISTING,                             // creation disposition
              FILE_FLAG_RANDOM_ACCESS,                    // flags/attributes
              NULL);
       hMmMap = ISAMmMap(hISA);
       if(hMmMap == NULL)
              return -1;
       printf("ISA read/write speed test\r\n");
       printf("Memory Map VS Device Driver API\r\n");
       dwStartTick = GetTickCount();
       for(i=0; i               for(k=0; k<256; k++)
              {
                     ISAWrite8(hMmMap, k, 0x55);
                     ucValue = ISARead8(hMmMap, k);      
              }
       dwEndTick = GetTickCount();
       printf("1. Memory map BYTE read/write %d KBytes, Take time:%d ms\r\n", 256 * 2 *cnt / 1024, dwEndTick - dwStartTick);
       dwStartTick = GetTickCount();
       for(i=0; i               for(k=0; k<256; k++)
              {
                     ISA_WriteUchar( hISA, k, 0x55 );
                     ISA_ReadUchar( hISA, k, &ucValue );
              }
              dwEndTick = GetTickCount();
       printf("2. Device driver API BYTE read/write %d KBytes, Take time:%d ms\r\n", 256 * 2 * cnt/ 1024, dwEndTick - dwStartTick);
       cnt /= 2;
       dwStartTick = GetTickCount();
       for(i=0; i               for(k=0; k<256; k++)
              {
                     ISAWrite16(hMmMap, k, 0x55AA);
                     wValue = ISARead16(hMmMap, k);     
              }
       dwEndTick = GetTickCount();
       printf("3. Memory map WORD read/write %d KBytes, Take time:%d ms\r\n", 256 * 4 * cnt / 1024, dwEndTick - dwStartTick);
       dwStartTick = GetTickCount();
       for(i=0; i               for(k=0; k<256; k++)
              {
                     ISA_WriteWord( hISA, k, 0x55AA );
                     ISA_ReadWord( hISA, k, &wValue );
              }
              dwEndTick = GetTickCount();
       printf("4. Device driver API WORD read/write %d KBytes, Take time:%d ms\r\n", 256 * 4 * cnt / 1024, dwEndTick - dwStartTick);
       CloseHandle(hISA);
       return 0;
}



  下面是在ESM3352上两种访问方式的测试结果:

ISA

要实现应用程序直接访问ISA外设地址空间需要更新ISA驱动程序,需要的用户可与英创联系。

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

全部0条评论

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

×
20
完善资料,
赚取积分