惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
腾讯CDC
D
Docker
G
Google Developers Blog
D
DataBreaches.Net
雷峰网
雷峰网
Blog — PlanetScale
Blog — PlanetScale
S
SegmentFault 最新的问题
The Cloudflare Blog
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
量子位
美团技术团队
aimingoo的专栏
aimingoo的专栏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Engineering at Meta
Engineering at Meta
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - AI产品观察

公式截图怎么转LaTeX代码?这个免费工具一键搞定 免费白嫖Google顶尖算力?深度揭秘这个满血版Gemini 3 Pro体验入口 深度解析 VoGen:2026 年最强免费 AI 语音合成与情感克隆技术方案 分享一个网站,Crealens.ai - 可以免费体验 AI图像/视频 生成-编辑-创作 WriteRightly 这个AI 可以像教授一样批改论文 免费、快速、可靠:揭秘IsGPT如何精准检测AI内容 Crealens.ai 免费体验GPT-4o 生图+吉卜力风格化 目标检测-基于Pytorch实现Yolov3(1)- 搭建模型 01布尔模型&倒排索引 openpose模型在AI challenge人体骨骼关键点检测的表现 隐马尔可夫模型(HMM) 概率生成模型 卷积神经网络经验-CS231n笔记 双显卡笔记本安装CUDA+theano、tensorflow环境 Exynos 4412 Uboot源码解析 隐马尔可夫模型 进程详解(1)——可能是最深入浅出的进程学习笔记 贪吃蛇游戏C语言源代码学习 经典功率谱估计及Matlab仿真 CAN总线(一)
基于WDF的PCI/PCIe接口卡Windows驱动程序(5)-如何为硬件移...
AI产品观察 · 2016-12-06 · via 博客园 - AI产品观察

原文地址:http://www.cnblogs.com/jacklu/p/6139347.html

如果你觉得这篇博客对你的项目有用,请引用以下论文:

Meng Shengwei, Lu Jianjie. Design of a PCIe Interface Card Control Software Based on WDF. Fifth International Conference on Instrumentation and Measurement, Computer, Communication and Control. IEEE, 2016:767-770.

正如前几篇博客所说,使用WDF开发PCIe驱动程序是我本科毕业设计的主要工作。在读研的两年,我也分别为所在课题组移植了自己编写的驱动程序,在Windows 32位和64位平台下的PXI、PXIe、PCI、PCIe板卡分别得到了验证。

这篇文章根据自己最新编写的驱动代码(源代码请找博主索取),主要讲述如何为自己的硬件板卡移植驱动程序,并简单讲述如何使用Altera系列FPGA配置PCI IP核,然后对INF文件作简要描述,最后描述如何使用Qt编写上位机软件调用底层驱动。

准备去读博了,这一篇将作为这个系列的完结,之后将把更多精力放到机器学习上来。 

1概述

所编写的驱动代码程序包括7个源代码文件,分别是Device.h, Driver.h, Public.h, Trace.h, Device.c,Driver.c, Queue.c。其中Device.h 定义了与硬件相关的地址偏移;Public.h定义了DeviceIoControl 用到的用户自定义命令字, 此头文件由上层应用程序和驱动程序共同使用;Queue.c定义了I/O回调例程,分别使用了read、write和I/O Control 三个队列。 除了这三个文件外, 不建议更改其他4个文件的代码。三个源文件函数列表分别如图 1-1、1-2、1-3所示:

image

2驱动程序移植说明

2.1 Public.h说明

  • 代码中定义了GUID值,开发者可以使用 VS2013 下的工具 GUIDGen.exe 生成 GUID值,该GUID标识驱动程序,应用程序根据这个GUID值来找到对应的驱动程序。
  • 代码中定义了CTL_CODE, I/O 处理例程 DeviceIoControl 的第二个参数dwIoControlCode 就是由 CTL_CODE 宏定义的。 CTL_CODE 是一个用于创建一个唯一的32 位系统I/O控制代码的宏,这个控制代码包括4部分组成:DeviceType(设备类型,高16位(16-31 位)),Access(访问限制,14-15 位),Function(功能2-13位),Method(I/O访问内存使用方式)。CTL_CODE 定义中有一个Method域,该2域定义了驱动程序中获取应用程序数据缓冲区的地址方式。 已经定义的 CTL_CODE 命令字说明如表 2-1 所示, 用户可以根据格式自定义 CTL_CODE, 实现不同功能。

image

什么是 CRA 寄存器组? 打开 quartus 的 sopc builder,可以看到sopc架构如图 2-1 所示,在PCI IP核一栏中有 Control Register Access 寄存器组,地址范围0x00000000-0x00003fff。 里面关键的寄存器地址如图 2-2 所示。 通过读黄色标识的寄存器,可以通过驱动程序调试验证 PCI 核。 关于 CRA 寄存器组的配置说明会在 2.3 节详细说明。

image

image

2.2 Device.h说明

  • 代码对 FPGA 上硬件资源的偏移地址进行宏定义,在 Altera 系列的 FPGA 里,这些偏移地址也叫 Avalon 地址, 在 sopc builder 可以自定义分配, 如图 2-1 中的 Base和End 所示, “ 小锁头” 标志表示地址锁定, 点击该标志解锁后可以自定义便宜地址。这些地址必须与驱动程序中所用的地址一一对应;
  • 代码定义了设备对象结构体, 对几个重要的成员变量注释如下:

image

  • 代码对一些事件回调例程进行了说明, 一般不需要用户进行二次修改;

2.3 Queue.c说明

  • 代码是用户需要针对功能开发的代码。 以从应用程序向驱动程序写入偏移地址为例,即原代码第 xx-xx 行 , 首先在 Public.h 文件里定义 IoControlCode 为qd41_IOCTL_WRITE_OFFSETADDRESS, 第 48-53 行为获取应用程序输入缓存数据的指针( 代码中为 inBuffer) , 通过赋值语句将 inBuffer 指向的数据内容赋给设备对象的成员变量 OffseAddressFromApp, 即完成了本功能;
  • 代码定义了写队列功能, 对 DMA 的写功能配置在此函数中完成。 Altera的 DMA IP 核共有 5 个寄存器, 如图 2-3 所示。

image

  • 在配置 DMA 前需要配置 PCI CRA 寄存器, 使能 PCI 中断, 配置 Avalon-PCI 地址转换表, 如代码所示;
  • 为什么需要配置 Avalon-PCI 地址转换表? 因为 PCI IP核一端是 PCI 总线,一端是Avalon总线,地址转换过程图 2-4 所示,类似MMU地址翻译原理,不在赘述,此时需要把PC机获得的DMA传输缓存物理地址的高16位地址写入地址转换表;

image

  • 配置好 PCI 后就可以配置 DMA 控制寄存器了, 首先将状态寄存器和控制寄存器清零,如代码所示;
  • 然后将读写地址分别写入读写寄存器, 注意PC机上的内存地址为低16位,而高16位要配置在 Avalon-PCI 地址转换表中, 如代码所示, 其中 0x20000 为 PCI IP核的 Avalon 地址, 如图 2-1 所示;
  • 向长度寄存器写入 DMA 传输长度(单位:字节) , 如代码所示;
  • 向控制寄存器写入 DMA 控制字 0x8c, 即长度寄存器降低为0时传输完成, 使能DMA,32 位字传输, 如代码所示;

3应用程序如何调用驱动程序

3.1 GUID说明

GUID( Globally Unique Identifier) 是微软推出的全局唯一标识符, 通过使用某个特定的算法( 比如根据时间或地点等信息) 生成一组128 位二进制数,来标识某一个实体,比如硬盘上的一张图片。 GUID 广泛应用于微软的产品中, 用于识别接口、文件等对象。开发者可以使用 VS2013 下的工具 GUIDGen.exe 生成 GUID 值, 该 GUID 标识驱动程序, 应用程序根据这个 GUID 值来找到对应的驱动程序。

应用程序总体流程设计为: 首先通过 Win32API 函数 CreateFile 打开设备, 然后调用DeviceIoControl 函数与驱动程序通信,即读写数据,当应用程序退出时,调用CloseHandle函数关闭设备。

应用程序根据底层设置的 GUID 获取设备路径, 从而与指定设备建立连接: 首先调用SetupDiGetClassDevs 函数获得符合传入参数 GUID 的设备信息集合 hDevInfo; 然后根据设备信息集合 hDevInfo 和 GUID 调用 SetupDiEnumDeviceInterfaces 函数枚举设备信息集合中的设备,并输出设备接口数据信息 DeviceInterfaceData ; 再 根 据 hDevInfo和 DeviceInterfaceData 调用 SetupDiGetDeviceInterfaceDetail函数得到保存设备接口详细信息DeviceInterfaceDetailData 的缓冲区大小 ,接着为其开辟空间 , 最后再调用一次SetupDiGetDeviceInterfaceDetail 函数获得设备接口详细信息 pDeviceInterfaceDetailData; 最后根据设备接口详细信息的设备接口路径 pDeviceInterfaceDetailData->DevicePath 调用CreateFile 函数来创建设备句柄。

3.2 应用程序如何打开设备

在测试程序 Source.c 的代码第 62-70 行,完成打开设备句柄功能, 不需要用户改动。

3.3 应用程序如何读设备

成功打开设备后, 调用 DeviceIoControl 即可通过 IOControl 队列与驱动程序通信, 以读32bit 数据为例, 首先向驱动程序写入需要读的存储单元地址, 如代码第 257-272 行所示,然后向驱动程序传入读数据缓存 outBuffer, 如代码第 274-289 所示, 即可获得FPGA 上对应偏移地址的数据。

3.4 应用程序如何写设备

成功打开设备后, 调用 DeviceIoControl 即可通过 IOControl 队列与驱动程序通信, 以写32bit 数据为例, 首先向驱动程序写入需要写的存储单元地址, 如代码第 303-317 行所示,然后向驱动程序传入缩写数据缓存 inBuffer, 如代码第 322-336 所示, 即可将数据写入 FPGA上对应偏移地址的内存单元。

3.5 应用程序如何对设备进行DMA传输

通过Win32API 函数ReadFile和WriteFile对设备句柄进行文件读写操作, 在内核驱动中会调用驱动程序的 qd41EvtIoRead和qd41EvtIoWrite,实际的DMA配置在这两个驱动回调例程中实现, ReadFile和WriteFile 只是完成了数据从用户层到内核层的搬运。详细代码在第 340-378 行,不再赘述。

4 INF文件与如何调用Qt编写上位机软件调用底层驱动

image

image

image

image

我把C语言编写的应用程序每一个独立的控制命令编译成一个可执行文件,这样Qt可以在新的进程中调用 C 程序编写的命令字读写程序。使用Qt 封装好的进程类 QProcess, 使用其成员函数 start 即可调用一个外部程序。

 广告时间~本人博士赚外快,如需要完整的驱动程序源代码请联系合作email: 346457821@qq.com