news 2026/6/23 13:55:35

windows用户态到内核态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
windows用户态到内核态

以下是一个驱动层 + 用户态的交互示例,覆盖“超时设置+设备状态查询+数据读写”全流程,包含驱动代码、用户态头文件、用户态调用代码。

一、整体架构说明

层级核心文件/功能
驱动层实现IRP_MJ_DEVICE_CONTROL处理逻辑,响应自定义IOCTL(设置超时、查状态、读写数据)
接口层ioapist.h:封装IOCTL、结构体、用户态API声明
用户态层调用ioapist.h封装的API,完成设备打开、参数下发、数据交互、设备关闭

二、步骤1:定义公共接口头文件ioapist.h

ioapist.h 并非 Windows 系统原生公开的头文件(不属于 MSDN/Windows SDK 标准头文件),而是行业 / 厂商自定义的接口头文件,核心用于封装设备 I/O 操作的 API 集合(尤其是通过DeviceIoControl实现的设备控制、数据交互逻辑),常见于硬件驱动开发、工业控制、嵌入式 Windows 设备通信等场景。
该文件是驱动和用户态的“协议约定”,需保证两端结构体、IOCTL完全一致。

#ifndefIOAPIST_H#defineIOAPIST_H#ifdef__cplusplusextern"C"{#endif/************************ 1. 基础常量定义 ************************/// 自定义设备类型(避开Windows系统预留值,范围0x8000-0xFFFF)#defineFILE_DEVICE_CUSTOM_DEV0x8000// 缓冲区方式(驱动与用户态数据交互方式,这里用缓冲式,最通用)#defineMETHOD_CUSTOMMETHOD_BUFFERED// 访问权限#defineFILE_CUSTOM_ACCESSFILE_ANY_ACCESS// 封装CTL_CODE宏,生成唯一IOCTL控制码#defineCUSTOM_CTL_CODE(code)\CTL_CODE(FILE_DEVICE_CUSTOM_DEV,code,METHOD_CUSTOM,FILE_CUSTOM_ACCESS)/************************ 2. IOCTL控制码定义 ************************/#defineIOCTL_SET_TIMEOUTCUSTOM_CTL_CODE(0x0001)// 设置超时#defineIOCTL_GET_DEVICE_STATUSCUSTOM_CTL_CODE(0x0002)// 获取设备状态#defineIOCTL_WRITE_DATACUSTOM_CTL_CODE(0x0003)// 写数据到设备#defineIOCTL_READ_DATACUSTOM_CTL_CODE(0x0004)// 从设备读数据/************************ 3. 数据交互结构体(驱动/用户态一致) ************************/// 超时设置结构体(对应IOCTL_SET_TIMEOUT)typedefstruct_DEV_TIMEOUT{DWORD dwReadTimeout;// 读超时(毫秒)DWORD dwWriteTimeout;// 写超时(毫秒)DWORD dwCtrlTimeout;// 控制指令超时(毫秒)}DEV_TIMEOUT,*PDEV_TIMEOUT;// 设备状态结构体(对应IOCTL_GET_DEVICE_STATUS)typedefstruct_DEV_STATUS{BOOL bOnline;// 设备是否在线(TRUE/FALSE)DWORD dwErrorCount;// 累计错误数DWORD dwBufferSize;// 设备缓存区大小DWORD dwReserved;// 预留字段(保证结构体对齐)}DEV_STATUS,*PDEV_STATUS;// 数据读写结构体(对应IOCTL_READ/WRITE_DATA)typedefstruct_DEV_DATA{BYTE szBuffer[1024];// 数据缓冲区DWORD dwDataLen;// 有效数据长度(输入/输出)DWORD dwErrorCode;// 操作错误码(输出)}DEV_DATA,*PDEV_DATA;/************************ 4. 辅助常量/枚举 ************************/// 默认设备路径(用户态打开设备用)#defineDEFAULT_DEVICE_PATHL"\\\\.\\CustomDevice"// 超时默认值#defineDEFAULT_READ_TIMEOUT5000// 5秒#defineDEFAULT_WRITE_TIMEOUT3000// 3秒#defineDEFAULT_CTRL_TIMEOUT2000// 2秒// 设备状态枚举(辅助用户态判断)typedefenum{DEV_STATUS_OFFLINE=0,DEV_STATUS_ONLINE=1,DEV_STATUS_ERROR=2}DEV_STATUS_ENUM;/************************ 5. 用户态封装API声明 ************************/// 打开设备(封装CreateFileW)HANDLE __stdcallDevOpen(LPCWSTR lpDevicePath,// 设备路径(如DEFAULT_DEVICE_PATH)DWORD dwDesiredAccess// 访问权限(GENERIC_READ | GENERIC_WRITE));// 关闭设备(封装CloseHandle)VOID __stdcallDevClose(HANDLE hDevice);// 设置设备超时(封装DeviceIoControl + IOCTL_SET_TIMEOUT)BOOL __stdcallDevSetTimeout(HANDLE hDevice,PDEV_TIMEOUT pTimeout);// 获取设备状态(封装DeviceIoControl + IOCTL_GET_DEVICE_STATUS)BOOL __stdcallDevGetStatus(HANDLE hDevice,PDEV_STATUS pStatus);// 写数据到设备(封装DeviceIoControl + IOCTL_WRITE_DATA)BOOL __stdcallDevWriteData(HANDLE hDevice,PDEV_DATA pData);// 从设备读数据(封装DeviceIoControl + IOCTL_READ_DATA)BOOL __stdcallDevReadData(HANDLE hDevice,PDEV_DATA pData);#ifdef__cplusplus}#endif#endif// IOAPIST_H

三、步骤2:驱动层实现(内核态,C语言)

驱动基于WDM框架开发,核心实现IRP_MJ_DEVICE_CONTROL回调,处理用户态下发的IOCTL指令。

1. 驱动头文件(CustomDev.h
#ifndefCUSTOM_DEV_H#defineCUSTOM_DEV_H#include<wdm.h>#include"ioapist.h"// 引入公共接口头文件// 设备扩展结构体(保存设备状态、超时配置等)typedefstruct_DEVICE_EXTENSION{PDEVICE_OBJECT pDeviceObj;// 设备对象指针UNICODE_STRING ustrDeviceName;// 设备名UNICODE_STRING ustrSymLinkName;// 符号链接名// 设备运行状态DEV_TIMEOUT stTimeout;// 超时配置(用户态下发)DEV_STATUS stStatus;// 设备状态BYTE szDevBuffer[1024];// 设备内部缓存区DWORD dwDevBufferLen;// 缓存区有效数据长度}DEVICE_EXTENSION,*PDEVICE_EXTENSION;// 函数声明NTSTATUSDriverUnload(PDRIVER_OBJECT pDriverObj);NTSTATUSDispatchCreateClose(PDEVICE_OBJECT pDeviceObj,PIRP pIrp);NTSTATUSDispatchDeviceControl(PDEVICE_OBJECT pDeviceObj,PIRP pIrp);#endif// CUSTOM_DEV_H
2. 驱动实现文件(CustomDev.c
#include"CustomDev.h"/************************ 1. 驱动入口函数 ************************/NTSTATUSDriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegistryPath){NTSTATUS status;PDEVICE_OBJECT pDeviceObj=NULL;PDEVICE_EXTENSION pDevExt=NULL;UNICODE_STRING ustrDeviceName;UNICODE_STRING ustrSymLinkName;// 1. 设置驱动卸载例程pDriverObj->DriverUnload=DriverUnload;// 2. 设置分发例程(处理Create/Close/DeviceControl)pDriverObj->MajorFunction[IRP_MJ_CREATE]=DispatchCreateClose;pDriverObj->MajorFunction[IRP_MJ_CLOSE]=DispatchCreateClose;pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL]=DispatchDeviceControl;// 3. 创建设备对象RtlInitUnicodeString(&ustrDeviceName,L"\\Device\\CustomDevice");status=IoCreateDevice(pDriverObj,// 驱动对象sizeof(DEVICE_EXTENSION),// 设备扩展大小&ustrDeviceName,// 设备名FILE_DEVICE_CUSTOM_DEV,// 设备类型(与ioapist.h一致)0,// 设备特性FALSE,// 非独占&pDeviceObj// 输出设备对象);if(!NT_SUCCESS(status)){DbgPrint("IoCreateDevice failed, status: 0x%X\n",status);returnstatus;}// 4. 初始化设备扩展pDevExt=(PDEVICE_EXTENSION)pDeviceObj->DeviceExtension;pDevExt->pDeviceObj=pDeviceObj;pDevExt->ustrDeviceName=ustrDeviceName;// 初始化默认超时pDevExt->stTimeout.dwReadTimeout=DEFAULT_READ_TIMEOUT;pDevExt->stTimeout.dwWriteTimeout=DEFAULT_WRITE_TIMEOUT;pDevExt->stTimeout.dwCtrlTimeout=DEFAULT_CTRL_TIMEOUT;// 初始化设备状态pDevExt->stStatus.bOnline=TRUE;// 默认在线pDevExt->stStatus.dwErrorCount=0;// 无错误pDevExt->stStatus.dwBufferSize=1024;// 缓存区大小1024pDevExt->dwDevBufferLen=0;// 初始无数据// 5. 创建符号链接(用户态可通过\\.\CustomDevice访问)RtlInitUnicodeString(&ustrSymLinkName,L"\\DosDevices\\CustomDevice");pDevExt->ustrSymLinkName=ustrSymLinkName;status=IoCreateSymbolicLink(&ustrSymLinkName,&ustrDeviceName);if(!NT_SUCCESS(status)){DbgPrint("IoCreateSymbolicLink failed, status: 0x%X\n",status);IoDeleteDevice(pDeviceObj);returnstatus;}DbgPrint("CustomDev Driver Load Success!\n");returnSTATUS_SUCCESS;}/************************ 2. 驱动卸载例程 ************************/NTSTATUSDriverUnload(PDRIVER_OBJECT pDriverObj){PDEVICE_OBJECT pDeviceObj=pDriverObj->DeviceObject;PDEVICE_EXTENSION pDevExt=(PDEVICE_EXTENSION)pDeviceObj->DeviceExtension;// 删除符号链接IoDeleteSymbolicLink(&pDevExt->ustrSymLinkName);// 删除设备对象IoDeleteDevice(pDeviceObj);DbgPrint("CustomDev Driver Unload Success!\n");returnSTATUS_SUCCESS;}/************************ 3. Create/Close分发例程 ************************/NTSTATUSDispatchCreateClose(PDEVICE_OBJECT pDeviceObj,PIRP pIrp){PDEVICE_EXTENSION pDevExt=(PDEVICE_EXTENSION)pDeviceObj->DeviceExtension;NTSTATUS status=STATUS_SUCCESS;// 模拟:打开设备时标记在线状态if(pIrp->CurrentLocation==1&&pIrp->MajorFunction==IRP_MJ_CREATE){pDevExt->stStatus.bOnline=TRUE;DbgPrint("Device Opened!\n");}// 关闭设备时标记离线elseif(pIrp->CurrentLocation==1&&pIrp->MajorFunction==IRP_MJ_CLOSE){pDevExt->stStatus.bOnline=FALSE;DbgPrint("Device Closed!\n");}// 完成IRPpIrp->IoStatus.Status=status;pIrp->IoStatus.Information=0;IoCompleteRequest(pIrp,IO_NO_INCREMENT);returnstatus;}/************************ 4. DeviceControl分发例程(核心) ************************/NTSTATUSDispatchDeviceControl(PDEVICE_OBJECT pDeviceObj,PIRP pIrp){NTSTATUS status=STATUS_SUCCESS;PIO_STACK_LOCATION pIoStack=IoGetCurrentIrpStackLocation(pIrp);PDEVICE_EXTENSION pDevExt=(PDEVICE_EXTENSION)pDeviceObj->DeviceExtension;ULONG ulIoControlCode=pIoStack->Parameters.DeviceIoControl.IoControlCode;ULONG ulInputLen=pIoStack->Parameters.DeviceIoControl.InputBufferLength;ULONG ulOutputLen=pIoStack->Parameters.DeviceIoControl.OutputBufferLength;DbgPrint("DispatchDeviceControl: IOCTL=0x%X\n",ulIoControlCode);/************************ 处理IOCTL_SET_TIMEOUT ************************/if(ulIoControlCode==IOCTL_SET_TIMEOUT){// 校验输入缓冲区大小if(ulInputLen!=sizeof(DEV_TIMEOUT)){status=STATUS_INVALID_PARAMETER;DbgPrint("IOCTL_SET_TIMEOUT: Input buffer size error!\n");gotoEnd;}// 读取用户态下发的超时参数PDEV_TIMEOUT pTimeout=(PDEV_TIMEOUT)pIrp->AssociatedIrp.SystemBuffer;// 保存到设备扩展pDevExt->stTimeout=*pTimeout;DbgPrint("Set Timeout: Read=%dms, Write=%dms, Ctrl=%dms\n",pTimeout->dwReadTimeout,pTimeout->dwWriteTimeout,pTimeout->dwCtrlTimeout);}/************************ 处理IOCTL_GET_DEVICE_STATUS ************************/elseif(ulIoControlCode==IOCTL_GET_DEVICE_STATUS){// 校验输出缓冲区大小if(ulOutputLen!=sizeof(DEV_STATUS)){status=STATUS_INVALID_PARAMETER;DbgPrint("IOCTL_GET_DEVICE_STATUS: Output buffer size error!\n");gotoEnd;}// 将设备状态写入输出缓冲区(返回给用户态)PDEV_STATUS pStatus=(PDEV_STATUS)pIrp->AssociatedIrp.SystemBuffer;*pStatus=pDevExt->stStatus;// 设置返回数据长度pIrp->IoStatus.Information=sizeof(DEV_STATUS);DbgPrint("Get Status: Online=%d, ErrorCount=%d\n",pStatus->bOnline,pStatus->dwErrorCount);}/************************ 处理IOCTL_WRITE_DATA ************************/elseif(ulIoControlCode==IOCTL_WRITE_DATA){// 校验输入缓冲区大小if(ulInputLen!=sizeof(DEV_DATA)){status=STATUS_INVALID_PARAMETER;DbgPrint("IOCTL_WRITE_DATA: Input buffer size error!\n");gotoEnd;}// 读取用户态下发的数据PDEV_DATA pData=(PDEV_DATA)pIrp->AssociatedIrp.SystemBuffer;if(pData->dwDataLen>1024){// 校验数据长度status=STATUS_BUFFER_OVERFLOW;pData->dwErrorCode=STATUS_BUFFER_OVERFLOW;DbgPrint("IOCTL_WRITE_DATA: Data too long!\n");gotoEnd;}// 将数据拷贝到设备内部缓存RtlCopyMemory(pDevExt->szDevBuffer,pData->szBuffer,pData->dwDataLen);pDevExt->dwDevBufferLen=pData->dwDataLen;pData->dwErrorCode=STATUS_SUCCESS;// 写入成功pIrp->IoStatus.Information=sizeof(DEV_DATA);DbgPrint("Write Data: Len=%d, Data=%s\n",pData->dwDataLen,pData->szBuffer);}/************************ 处理IOCTL_READ_DATA ************************/elseif(ulIoControlCode==IOCTL_READ_DATA){// 校验输出缓冲区大小if(ulOutputLen!=sizeof(DEV_DATA)){status=STATUS_INVALID_PARAMETER;DbgPrint("IOCTL_READ_DATA: Output buffer size error!\n");gotoEnd;}// 读取设备内部缓存数据,返回给用户态PDEV_DATA pData=(PDEV_DATA)pIrp->AssociatedIrp.SystemBuffer;RtlCopyMemory(pData->szBuffer,pDevExt->szDevBuffer,pDevExt->dwDevBufferLen);pData->dwDataLen=pDevExt->dwDevBufferLen;pData->dwErrorCode=STATUS_SUCCESS;pIrp->IoStatus.Information=sizeof(DEV_DATA);DbgPrint("Read Data: Len=%d, Data=%s\n",pData->dwDataLen,pData->szBuffer);}/************************ 未知IOCTL ************************/else{status=STATUS_NOT_SUPPORTED;DbgPrint("Unsupported IOCTL: 0x%X\n",ulIoControlCode);gotoEnd;}End:// 完成IRPpIrp->IoStatus.Status=status;IoCompleteRequest(pIrp,IO_NO_INCREMENT);returnstatus;}

四、步骤3:用户态实现(ioapist.c+ 测试程序)

1. 封装API实现(ioapist.c
#include<windows.h>#include<stdio.h>#include"ioapist.h"/************************ 1. 打开设备 ************************/HANDLE __stdcallDevOpen(LPCWSTR lpDevicePath,DWORD dwDesiredAccess){if(lpDevicePath==NULL){lpDevicePath=DEFAULT_DEVICE_PATH;}// 打开设备(需管理员权限)HANDLE hDevice=CreateFileW(lpDevicePath,dwDesiredAccess,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(hDevice==INVALID_HANDLE_VALUE){DWORD dwErr=GetLastError();printf("DevOpen failed! ErrCode=0x%X\n",dwErr);returnNULL;}returnhDevice;}/************************ 2. 关闭设备 ************************/VOID __stdcallDevClose(HANDLE hDevice){if(hDevice!=NULL&&hDevice!=INVALID_HANDLE_VALUE){CloseHandle(hDevice);printf("DevClose success!\n");}}/************************ 3. 设置超时 ************************/BOOL __stdcallDevSetTimeout(HANDLE hDevice,PDEV_TIMEOUT pTimeout){if(hDevice==NULL||pTimeout==NULL){SetLastError(ERROR_INVALID_PARAMETER);returnFALSE;}DWORD dwBytesReturned=0;BOOL bResult=DeviceIoControl(hDevice,IOCTL_SET_TIMEOUT,pTimeout,sizeof(DEV_TIMEOUT),NULL,0,&dwBytesReturned,NULL);if(!bResult){DWORD dwErr=GetLastError();printf("DevSetTimeout failed! ErrCode=0x%X\n",dwErr);}else{printf("DevSetTimeout success! Read=%dms, Write=%dms, Ctrl=%dms\n",pTimeout->dwReadTimeout,pTimeout->dwWriteTimeout,pTimeout->dwCtrlTimeout);}returnbResult;}/************************ 4. 获取设备状态 ************************/BOOL __stdcallDevGetStatus(HANDLE hDevice,PDEV_STATUS pStatus){if(hDevice==NULL||pStatus==NULL){SetLastError(ERROR_INVALID_PARAMETER);returnFALSE;}DWORD dwBytesReturned=0;BOOL bResult=DeviceIoControl(hDevice,IOCTL_GET_DEVICE_STATUS,NULL,0,pStatus,sizeof(DEV_STATUS),&dwBytesReturned,NULL);if(!bResult){DWORD dwErr=GetLastError();printf("DevGetStatus failed! ErrCode=0x%X\n",dwErr);}else{printf("DevGetStatus success! Online=%d, ErrorCount=%d, BufferSize=%d\n",pStatus->bOnline,pStatus->dwErrorCount,pStatus->dwBufferSize);}returnbResult;}/************************ 5. 写数据 ************************/BOOL __stdcallDevWriteData(HANDLE hDevice,PDEV_DATA pData){if(hDevice==NULL||pData==NULL){SetLastError(ERROR_INVALID_PARAMETER);returnFALSE;}DWORD dwBytesReturned=0;BOOL bResult=DeviceIoControl(hDevice,IOCTL_WRITE_DATA,pData,sizeof(DEV_DATA),pData,// 输出错误码到同一个结构体sizeof(DEV_DATA),&dwBytesReturned,NULL);if(!bResult){DWORD dwErr=GetLastError();printf("DevWriteData failed! ErrCode=0x%X\n",dwErr);}else{printf("DevWriteData success! Len=%d, Data=%s, ErrCode=0x%X\n",pData->dwDataLen,pData->szBuffer,pData->dwErrorCode);}returnbResult;}/************************ 6. 读数据 ************************/BOOL __stdcallDevReadData(HANDLE hDevice,PDEV_DATA pData){if(hDevice==NULL||pData==NULL){SetLastError(ERROR_INVALID_PARAMETER);returnFALSE;}DWORD dwBytesReturned=0;BOOL bResult=DeviceIoControl(hDevice,IOCTL_READ_DATA,NULL,0,pData,sizeof(DEV_DATA),&dwBytesReturned,NULL);if(!bResult){DWORD dwErr=GetLastError();printf("DevReadData failed! ErrCode=0x%X\n",dwErr);}else{printf("DevReadData success! Len=%d, Data=%s, ErrCode=0x%X\n",pData->dwDataLen,pData->szBuffer,pData->dwErrorCode);}returnbResult;}
2. 用户态测试程序(TestApp.c
#include<windows.h>#include<stdio.h>#include"ioapist.h"intmain(){HANDLE hDevice=NULL;DEV_TIMEOUT stTimeout={0};DEV_STATUS stStatus={0};DEV_DATA stWriteData={0};DEV_DATA stReadData={0};/************************ 1. 打开设备 ************************/hDevice=DevOpen(DEFAULT_DEVICE_PATH,GENERIC_READ|GENERIC_WRITE);if(hDevice==NULL){printf("Open Device Failed!\n");return-1;}printf("Open Device Success!\n");/************************ 2. 设置超时 ************************/stTimeout.dwReadTimeout=6000;// 6秒stTimeout.dwWriteTimeout=4000;// 4秒stTimeout.dwCtrlTimeout=3000;// 3秒if(!DevSetTimeout(hDevice,&stTimeout)){gotoCleanup;}/************************ 3. 获取设备状态 ************************/if(!DevGetStatus(hDevice,&stStatus)){gotoCleanup;}/************************ 4. 写数据到设备 ************************/charszWriteBuf[]="Hello Custom Device!";RtlCopyMemory(stWriteData.szBuffer,szWriteBuf,strlen(szWriteBuf));stWriteData.dwDataLen=strlen(szWriteBuf);if(!DevWriteData(hDevice,&stWriteData)){gotoCleanup;}/************************ 5. 从设备读数据 ************************/RtlZeroMemory(&stReadData,sizeof(DEV_DATA));if(!DevReadData(hDevice,&stReadData)){gotoCleanup;}/************************ 6. 清理资源 ************************/Cleanup:DevClose(hDevice);printf("Test App Exit!\n");return0;}

五、编译与运行说明

1. 驱动编译(WDK环境)
  • 依赖:Windows Driver Kit(WDK 10+);
  • 配置:驱动项目属性中,选择“Kernel Mode Driver”,平台选择x64/x86;
  • 输出:生成CustomDev.sys驱动文件。
2. 用户态编译(VS环境)
  • 依赖:Visual Studio 2019+,Windows SDK;
  • 配置:控制台项目,将ioapist.hioapist.cTestApp.c加入项目;
  • 编译选项:启用Unicode,平台选择与驱动一致(x64/x86);
  • 输出:生成TestApp.exe可执行文件。
3. 运行步骤
  1. 安装驱动:通过sc create CustomDev type= kernel binPath= "C:\CustomDev.sys"创建服务,sc start CustomDev启动驱动;
  2. 以管理员权限运行TestApp.exe
  3. 查看输出:控制台会打印各步骤结果,驱动侧可通过DebugView查看DbgPrint日志;
  4. 卸载驱动:sc stop CustomDev+sc delete CustomDev

六、关键细节与注意事项

  1. 权限要求:用户态程序需管理员权限(否则CreateFile打开设备失败);
  2. 结构体对齐:驱动和用户态的结构体必须保证内存对齐(避免数据解析错误);
  3. 错误处理
    • 驱动侧:通过DbgPrint打印调试信息,用NT_SUCCESS判断状态;
    • 用户态:通过GetLastError获取错误码,常见错误:
      • ERROR_INVALID_HANDLE:设备句柄无效;
      • ERROR_NOT_SUPPORTED:驱动不支持该IOCTL;
      • ERROR_ACCESS_DENIED:无管理员权限;
  4. 缓冲区方式:示例中用METHOD_BUFFERED(最通用),驱动侧通过pIrp->AssociatedIrp.SystemBuffer访问数据;
  5. 符号链接:驱动创建的符号链接\\DosDevices\\CustomDevice对应用户态的\\\\.\\CustomDevice,需保证名称一致。

七、运行预期输出(用户态控制台)

Open Device Success! DevSetTimeout success! Read=6000ms, Write=4000ms, Ctrl=3000ms DevGetStatus success! Online=1, ErrorCount=0, BufferSize=1024 DevWriteData success! Len=19, Data=Hello Custom Device!, ErrCode=0x0 DevReadData success! Len=19, Data=Hello Custom Device!, ErrCode=0x0 DevClose success! Test App Exit!

该示例完整覆盖了“驱动初始化→用户态打开设备→下发参数→数据交互→关闭设备→驱动卸载”全流程,可直接基于此扩展更多功能(如设备错误处理、超时触发逻辑等)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 5:32:17

嵌入式系统(基于FreeRTOS)串口命令行调试工具

一、整体结构说明 嵌入式系统&#xff08;基于FreeRTOS&#xff09;串口命令行调试工具&#xff0c;采用模块化设计&#xff0c;核心结构分层如下&#xff1a;模块层级功能说明1. 配置与宏定义调试开关、缓冲区大小、密码/超时配置、硬件适配宏&#xff08;UART/FreeRTOS&#…

作者头像 李华
网站建设 2026/6/23 4:12:15

Qwen3-VL-8B中文多模态实测:懂语境更懂中国用户

Qwen3-VL-8B中文多模态实测&#xff1a;懂语境更懂中国用户 在电商客服收到一张模糊的衣物照片&#xff0c;用户问&#xff1a;“这油渍能洗掉吗&#xff1f;” 如果系统只能回答“图片包含深色斑点”&#xff0c;那毫无意义。 但若它能结合布料纹理、污渍形态和生活常识说&…

作者头像 李华
网站建设 2026/6/22 23:32:44

Axios网络请求优化(缓存)

合理使用缓存&#xff0c;避免重复请求// 通过缓存机制&#xff0c;存储已经发出的请求结果&#xff0c;如果同样的请求再次发起&#xff0c; // 直接从缓存中获取数据&#xff0c;而不是重新发请求。import axios from "axios";// 缓存对象 const cache new Map<…

作者头像 李华
网站建设 2026/6/23 18:07:47

通过短时倒谱(Cepstrogram)计算进行时-倒频分析研究附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2026/6/23 0:26:12

无人机启用的无线传感器网络中的节能数据收集附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2026/6/23 10:37:39

[特殊字符]️ 羽毛球检测数据集介绍-1686张图片 运动赛事分析 智能健身设备 自动裁判系统 体育视频内容分析 机器人运动训练

&#x1f4e6;点击查看-已发布目标检测数据集合集&#xff08;持续更新&#xff09; 数据集名称图像数量应用方向博客链接&#x1f50c; 电网巡检检测数据集1600 张电力设备目标检测点击查看&#x1f525; 火焰 / 烟雾 / 人检测数据集10000张安防监控&#xff0c;多目标检测点…

作者头像 李华