PCI9x5x驱动移植支持PCI9054在win7下使用3

接上文,本文章继续记录中泰联创的数据采集卡驱动翻新过程。

读写FPGA寄存器函数移植

将PCI8KPLX_IOCTL_BAR_RW改为PCI8KPLX_IOCTL_BAR_READ作为读FPGA寄存器命令,将PCI8KPLX_IOCTL_BAR_BULK_RW改为PCI8KPLX_IOCTL_BAR_BULK_WRITE作为写FPGA寄存器命令。
老驱动使用一个PCI8KPLX_IOCTL_BAR_RW命令作为内核通讯接口,没有利用到系统自身设计的便利,所以新驱动将读命令和写命令拆分成两个IOCTL操作。

之前的PCI8KPLX_IOCTL_OPEN_IRQ和PCI8KPLX_IOCTL_CLOSE_IRQ请求,都不需要返回给应用层数据,因此在PLxEvtIoDeviceControl函数中直接使用WdfRequestComplete函数设置请求完成即可;但是PCI8KPLX_IOCTL_BAR_READ需要返回给应用层寄存器读取结果,因此必须要设定返回字节数,可以通过WdfRequestCompleteWithInformation完成请求来设置返回字节数,但是这样就必须考虑其它不返回数据的函数,会增加编程复杂度,因此使用WdfRequestSetInformation函数在需要返回数据的函数里面设置返回字节数,不需要返回数据的函数,不调用这个函数,系统会默认返回0字节。

内核中添加代码如下:

VOID
PLxEvtIoDeviceControl(
    _In_ WDFQUEUE   Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t     OutputBufferLength,
    _In_ size_t     InputBufferLength,
    _In_ ULONG      IoControlCode
    )
    {
        ...
        case PCI8KPLX_IOCTL_BAR_READ:
			status = PCI8KPLX_IOCTL_BAR_READ_Handler(Request, devExt);
			break;
		case PCI8KPLX_IOCTL_BAR_WRITE:
			status = PCI8KPLX_IOCTL_BAR_WRITE_Handler(Request, devExt);
			break;
        ...
    }
/**
 * 功能:读回FPGA寄存器内容
 * 输入:ULONG[0],操作位宽;0对应32位,1对应16位,2对应8位
 *       ULONG[1],地址;0~255
 * 输出:ULONG[0],寄存器内容
 */
NTSTATUS
PCI8KPLX_IOCTL_BAR_READ_Handler(
	_In_ WDFREQUEST Request,
	_In_ PDEVICE_EXTENSION DevExt
)
{
	NTSTATUS status = STATUS_SUCCESS;
	PULONG pBuff = NULL; // 指向输入缓冲区
	PULONG pOutBuff = NULL; // 指向输出缓冲区
	size_t bufferSize = 0;
	// 1. 获取输入缓冲区 
	status = WdfRequestRetrieveInputBuffer(Request, PCI8KPLX_IOCTL_BAR_READ_IN_SIZE, (PVOID*)&pBuff, &bufferSize);
	if (!NT_SUCCESS(status)) {
		return status;
	}
	ULONG type = pBuff[0];
	ULONG addr = pBuff[1];
	ULONG ret = 0;
	switch (type)
	{
	case PORT_RW_32BIT:
		ret = READ_PORT_ULONG((PULONG)(DevExt->addrLocal + addr));
		break;
	case PORT_RW_16BIT:
		ret = READ_PORT_USHORT((PUSHORT)(DevExt->addrLocal + addr));
		break;
	case PORT_RW_8BIT:
		ret = READ_PORT_UCHAR((PUCHAR)(DevExt->addrLocal + addr));
		break;
	default:
		return STATUS_INVALID_PARAMETER;
	}
	
	//获取输出缓冲区
	status = WdfRequestRetrieveOutputBuffer(
		Request,
		PCI8KPLX_IOCTL_BAR_READ_OUT_SIZE,  // 要求至少能存 EVENT_COUNT 个 ULONG
		(PVOID*)&pOutBuff,
		&bufferSize
	);
	if (!NT_SUCCESS(status)) {
		return status;
	}
	pOutBuff[0] = ret;
	WdfRequestSetInformation(Request, (ULONG_PTR)PCI8KPLX_IOCTL_BAR_READ_OUT_SIZE);
	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
		"%s:status %x", __FUNCDNAME__, status);

	return status;
}
/**
 * 功能:设置FPGA寄存器
 * 输入:ULONG[0],操作位宽;0对应32位,1对应16位,2对应8位
 *       ULONG[1],地址;0~255
 *       ULONG[2],数据
 * 输出:无
 */
NTSTATUS
PCI8KPLX_IOCTL_BAR_WRITE_Handler(
	_In_ WDFREQUEST Request,
	_In_ PDEVICE_EXTENSION DevExt
)
{
	NTSTATUS status = STATUS_SUCCESS;
	PULONG pBuff = NULL; // 指向输入缓冲区
	size_t bufferSize = 0;
	// 1. 获取输入缓冲区 
	status = WdfRequestRetrieveInputBuffer(Request, PCI8KPLX_IOCTL_BAR_WRITE_IN_SIZE, (PVOID*)&pBuff, &bufferSize);
	if (!NT_SUCCESS(status)) {
		return status;
	}
	ULONG type = pBuff[0];
	ULONG addr = pBuff[1];
	ULONG data = pBuff[2];
	switch (type)
	{
	case PORT_RW_32BIT:
		WRITE_PORT_ULONG((PULONG)(DevExt->addrLocal + addr), data);
		break;
	case PORT_RW_16BIT:
		WRITE_PORT_USHORT((PUSHORT)(DevExt->addrLocal + addr), (USHORT)data);
		break;
	case PORT_RW_8BIT:
		WRITE_PORT_UCHAR((PUCHAR)(DevExt->addrLocal + addr), (UCHAR)data);
		break;
	default:
		return STATUS_INVALID_PARAMETER;
	}

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
		"%s:status %x", __FUNCDNAME__, status);

	return status;
}

应用层代码移植,打算尝试直接用 Qoder 修改,看能否成功,下达下面的指令:“zt9054/sys/dll目录下的程序原来是调用sys目录下的驱动,现在改成调用zt9054/sys/sys目录下的驱动,现在要把TK3000_WriteD调用PCI8KPLX_IOCTL_BAR_WRITE,TK3000_ReadD调用PCI8KPLX_IOCTL_BAR_READ实现,请修改zt9054/sys/dll目录下这两个函数的相关代码”

代码加上了,但是所有中文都变成了乱码。接下来为了编码问题又和Qoder斗争了半天,最后总算是搞定,和自己写时间也差不多,但是如果没有编码问题还是会节约不少时间的。
以下是Qoder修改的部分代码

long CZTCARD::WriteD(ULONG cardNO, ULONG nOffset, ULONG dataDoubleWord)
{
//函数名称:
//函数功能:以IO方式,对板卡寄存器进行32位写
//入口参数:
//          baseAddr:板卡基地址
//         nOffset:偏移地址,在硬件说明书上可以查到
//  dataDoubleWord:要写入寄存的值
//返回值:  0  表成功
//         -1  表失败,应该进一步调用 ZT511PF_GetLastErr 判断出错原因

    CHECK_CARDNO(cardNO); //检查板卡号
    CHECK_ERRNO; //若用户参数错,不继续执行

    ULONG inBuffer[3];
    inBuffer[0] = PORT_RW_32BIT;  // New protocol: specify 32-bit write
    inBuffer[1] = nOffset;
    inBuffer[2] = dataDoubleWord;

//  ULONG outBuffer[1];
    ULONG nOutput;

    if (!DeviceIoControl(g_ztpciHandle[cardNO-g_baseNO],
                         PCI8KPLX_IOCTL_BAR_WRITE,  // Use new control code
                         inBuffer,
                         sizeof(inBuffer),
                         NULL,
                         0,
                         &nOutput,
                         NULL)
       )
    {
        g_errorLevel = ERR_EXCHANGE_DATA; //与底层驱动之间交换数据出错
        return -1;
    }
    return ZT_SUCCESS;
}

修改启动和停止AD采集对应代码

主要是需要将内核寄存器操作部分移植到应用层,让Qoder仿照我之前写好的中断初始化相关代码,结果他直接把我一个文件大部分代码都删除了,幸亏有git,还得随时盯着才能不出错。
经过多次沟通调整,Qoder 生成的代码仍频繁出错,最后还是我自己修改代码,Qoder反倒是浪费了时间。
最后把通道号缓存变量转移到了dll中:

// AD通道相关全局变量
ULONG g_adEndChCache[MAX_CARD_COUNT + 1];
void SetAdEndChCache(ULONG cardNo, ULONG ulCh) {
	if (cardNo > MAX_CARD_COUNT)
		return;
	g_adEndChCache[cardNo] = ulCh;
}

ULONG GetAdEndChCache(ULONG cardNo) {
	if (cardNo > MAX_CARD_COUNT)
		return 0;
	return g_adEndChCache[cardNo];
}

两个核心函数的修改内容如下:

long CROMAD::EnableAD(ULONG cardNO, unsigned long ulCh, unsigned long ulADFreqFlag)
{
    CHECK_CARDNO(cardNO); //检查板卡号
    CHECK_ERRNO; //若用户参数错,不继续执行
	// 设置本地缓存的通道号
	SetAdEndChCache(cardNO, ulCh);
	
	//设置控制字
	//设置频率
	// 从本地缓存读取控制字
	ULONG ulCtrlWord = GetCtrlWord(cardNO); // 使用tk2000.CPP中定义的函数	
	ulCtrlWord = SET_ULONG_BITS( ulCtrlWord, TK3000_ADFREQ_MASK, TK3000_ADFREQ, ulADFreqFlag );
	//将设备数据线设成输入状态(D0~D15)
	ulCtrlWord = SET_ULONG_BITS( ulCtrlWord, TK3000_DIR_MASK, TK3000_DIR, TK3000_D_IN );
    //将控制字写到本地缓存中
	SetCtrlWord(cardNO, ulCtrlWord);
	
	//实际写AD通道和控制寄存器,AD采集开始
	WRITED(cardNO, OFF_AD_END_CH, GetAdEndChCache(cardNO)); 
	WRITED(cardNO, OFF_CTRLWORD, (USHORT)(GetCtrlWord(cardNO)));
    return (g_errorLevel != ZT_SUCCESS) ? -1 : ZT_SUCCESS; //调过别的函数,要重新判断是否出错
}


long CROMAD::DisableAD(ULONG cardNO)
{
    CHECK_CARDNO(cardNO); //检查板卡号
    CHECK_ERRNO; //若用户参数错,不继续执行

    //停止AD采集,将频率设成0即可
	// 从本地缓存读取控制字
	ULONG ulCtrlWord = GetCtrlWord(cardNO);	
	ulCtrlWord = SET_ULONG_BITS( ulCtrlWord, TK3000_ADFREQ_MASK, TK3000_ADFREQ, ADFREQ_0 );
	SetCtrlWord(cardNO, ulCtrlWord);
		
	//更新控制字中的通道状态
	ulCtrlWord = GetCtrlWord(cardNO);
	ulCtrlWord = SET_ULONG_BITS( ulCtrlWord, TK3000_ADFREQ_MASK, TK3000_ADFREQ, 0 );
	SetCtrlWord(cardNO, ulCtrlWord);
	
	WRITED(cardNO, OFF_CTRLWORD, (USHORT)(GetCtrlWord(cardNO)));
    return (g_errorLevel != ZT_SUCCESS) ? -1 : ZT_SUCCESS; //调过别的函数,要重新判断是否出错
}
posted @ 2026-01-24 14:04  自由的好好干活  阅读(0)  评论(0)    收藏  举报