首页/新闻资讯/正文详情

Windows Universal 平台自定义串口设备访问:基于 Windows.Devices.SerialCommunication 的 UWP 串口通信完整实战

发布时间:2026/9/25 12:59:07 来源:云帆数科 栏目:资讯中心
Windows Universal 平台自定义串口设备访问:基于 Windows.Devices.SerialCommunication 的 UWP 串口通信完整实战
示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载本文以 Windows-universal-samples 仓库中 CustomSerialDeviceAccess 示例位于archived/CustomSerialDeviceAccess目录JavaScript 版本实现为主体系统讲解 UWP 应用如何通过Windows.Devices.SerialCommunication命名空间完成串口设备的枚举、连接、参数配置、双向数据读写与事件监听。读完本文你将掌握 SerialDevice 从打开到关闭的完整生命周期管理包括设备选择器AQS的构造、读写超时与流式 IO 的使用、Pin Changed / Error Received 事件注册以及应用挂起恢复与设备热插拔场景下的稳健处理方案。示例概览四个可切换的串口操作场景该示例是一个 UWP JavaScript 应用启动后允许用户针对一台串口设备执行四类操作对应 sample-configuration.js 中注册的四个导航场景场景页面对应脚本核心能力Connect/Disconnectscenario1_connectDisconnect.htmlscenario1_connectDisconnect.js用 DeviceWatcher 枚举可用串口设备连接/断开选中的设备Configure the Serial devicescenario2_configureDevice.htmlscenario2_configureDevice.js查询/修改波特率、校验位、停止位、握手协议、数据位等属性Communicate with the Serial devicescenario3_readWrite.htmlscenario3_readWrite.js通过输入/输出流读写数据支持超时设置与任务取消Register for Eventsscenario4_events.htmlscenario4_events.js注册 Pin Changed 与 Error Received 事件通知应用启动时连接场景会自动展示一份符合搜索条件的可用串口设备列表并提供连接/断开选项其余三个场景则在设备已连接的前提下操作。整个示例围绕SerialDevice单例句柄展开跨场景复用同一个已打开的设备对象这一设计贯穿全部源码。前置准备声明串口设备能力在调用任何串口 API 之前必须在应用清单中声明serialcommunication设备能力否则系统会拒绝访问对应DeviceAccessStatus.deniedBySystem。示例的 Package.appxmanifest 给出了标准写法Capabilities DeviceCapability Nameserialcommunication Device Idany Function Typename:serialPort / /Device /DeviceCapability /Capabilities其中Device Idany表示允许访问系统上任意串口设备如果需要限定具体设备可将Id替换为设备实例 ID。该清单还声明了目标设备系列Windows.Universal、最低版本10.0.10240.0见 Package.appxmanifest即该示例适用于 Windows 10 及之后的通用 Windows 平台。场景一设备的枚举、连接与断开用 DeviceWatcher 动态发现串口设备连接场景的核心是用设备监视器DeviceWatcher实时跟踪设备的热插拔。在 scenario1_connectDisconnect.js 中示例通过SerialDevice.getDeviceSelector()获取默认的串口设备选择器AQS 字符串再交给DeviceInformation.createWatcher创建监视器var serialDeviceSelector Windows.Devices.SerialCommunication.SerialDevice.getDeviceSelector(); var serialDeviceWatcher Windows.Devices.Enumeration.DeviceInformation.createWatcher(serialDeviceSelector, []);代码注释特别强调JavaScript 下createWatcher()只有在提供两个参数时才接受字符串因此第二个参数必须显式传入空数组[]否则重载解析会失败。除了默认选择器示例还提供了另外两种构造 AQS 的方式在 scenario1_connectDisconnect.js 的注释中列出并有对应的被注释掉的初始化函数按接口类 GUIDUsbDevice.getDeviceSelector(vid, pid, interfaceClassGuid)如 SuperMUTT 设备使用的{875D47FC-D331-4663-B339-624001A2DC5E}定义于 constants.js按厂商 ID / 产品 IDUsbDevice.getDeviceSelector(vid, pid)例如 OSRFX2 设备 VID0x0547、PID0x1002见 constants.js。监视器注册了added、removed、enumerationcompleted三个事件见 scenario1_connectDisconnect.js设备插入时加入列表、拔出时从 UI 列表移除、枚举完成时根据连接状态刷新按钮与列表的可用性。设备条目由 deviceListEntry.js 中的deviceListEntry类封装它持有DeviceInformation与创建该条目的deviceSelector并暴露instanceId取自System.Devices.DeviceInstanceId属性供 UI 绑定。打开设备SerialDevice.fromIdAsync连接按钮的处理逻辑scenario1_connectDisconnect.js先调用eventHandlerForDevice.createNewEventHandlerForDevice()创建事件处理器再调用其openDeviceAsync打开选中的设备。打开动作最终落到 eventHandlerForDevice.js 的openDeviceAsyncreturn Windows.Devices.SerialCommunication.SerialDevice.fromIdAsync(deviceInfo.id).then(function (serialDevice) { var successfullyOpenedDevice false; if (serialDevice) { successfullyOpenedDevice true; EventHandlerForDeviceClass.current._device serialDevice; // 注册应用事件、设备访问状态事件、设备监视器事件…… } else { // 根据 DeviceAccessInformation.createFromId(deviceInfo.id).currentStatus 分类报错 } return successfullyOpenedDevice; });当fromIdAsync返回空值时示例通过DeviceAccessInformation.currentStatus区分三种失败原因deniedByUser用户在系统设置中封锁了该设备deniedBySystem通常由应用权限不足引起最常见的就是未在清单中声明设备能力其他未知错误极可能是设备正被其他应用占用。设备句柄的关闭与自动重连closeDevice()eventHandlerForDevice.js负责完整清理关闭当前设备句柄、停止并注销设备监视器、注销访问状态与应用事件、清空所有回调与设备引用。底层关闭动作在_closeCurrentlyConnectedDevice同文件 #L237-L250它先通知onDeviceCloseCallback再调用SerialDevice.close()。值得注意的是关闭语义SerialDevice关闭时会取消所有未完成的 IO 操作但不会等待 IO 完成回调执行完毕——挂起的 IO 之后仍会以任务取消或操作完成的形式回调因此示例在 UI 层对读写按钮做了状态互斥保护。自动重连由isEnabledAutoReconnect属性控制eventHandlerForDevice.js当设备被拔出后再插回added事件或访问权限从拒绝恢复为允许accesschanged事件时示例会重新调用openDeviceAsync尝试恢复连接若重连失败则把isEnabledAutoReconnect置为 false避免反复重试见同文件 #L375-L387。应用挂起与恢复的生命周期处理EventHandlerForDevice类还集中演示了 UWP 应用生命周期与串口句柄的正确配合方式eventHandlerForDevice.js挂起suspending必须停止 DeviceWatcher否则挂起期间仍持续触发事件、消耗电量并显式调用_closeCurrentlyConnectedDevice()。因为 API 在应用挂起时会自动关闭设备句柄但恢复时不会自动重开所以示例坚持每次 open 都有对应 close的原则恢复resuming重新启动 DeviceWatcher设备会重新被枚举在自动重连开启的情况下会自动恢复句柄。连接场景自身也有一份对应用事件的监听startHandlingAppEvents/stopHandlingAppEvents见 scenario1_connectDisconnect.js用于在挂起/恢复时同步启停设备监视器避免列表内容过期。场景二串口参数查询与配置配置场景scenario2_configureDevice.js演示了SerialDevice上一系列 Get/Set 属性 API 的完整用法。页面进入时若设备未连接直接隐藏内容区并提示同文件 #L270-L301。只读状态属性两类信号状态只允许读取示例用On/Off文本展示同文件 #L7-L23属性含义carrierDetectState载波检测CD信号状态true 表示载波存在dataSetReadyState数据集就绪DSR信号状态可写开关属性三个布尔开关属性通过 ToggleSwitch 控件双向绑定同文件 #L25-L89breakSignalStateBreak 信号状态置 true 时向设备发送 BreakisDataTerminalReadyEnabled数据终端就绪DTR使能isReadyToSendEnabled请求发送RTS使能。对应的 UI 控件定义在 scenario2_configureDevice.html每个开关都包含一个状态标签与一个WinJS.UI.ToggleSwitch。波特率波特率通过数字输入框 SET 按钮设置scenario2_configureDevice.jsfunction baudRateButtonClicked() { var input document.getElementById(BaudRateInput); var baudRate parseInt(input.value, 10); SdkSample.CustomSerialDeviceAccess.eventHandlerForDevice.current.device.baudRate baudRate; updateBaudRateView(); }baudRate属性单位是 bit/sbps常见的取值如 9600、115200 等。UI 上的输入控件为input typenumber见 scenario2_configureDevice.html。校验位、停止位、握手协议与数据位这四个参数均以下拉框形式呈现代码用枚举比较映射选中项与设备属性见 scenario2_configureDevice.jsParity校验位SerialParity.none/odd/even/mark/space五个选项Stop Bits停止位SerialStopBitCount.one/onePointFive/twoHandshake握手协议SerialHandshake.none无流控/requestToSendRTS/CTS 硬件流控/xonXOff软件流控/requestToSendXOnXOff两者同时启用Data Bits数据位08 的整数实际串口应用常用 7 或 8示例下拉框完整列出 08 以便观察属性的读写行为。这些下拉框的选项定义同样可在 scenario2_configureDevice.html 中找到。修改属性后立即通过对应的updateXxxView()刷新标签文本保证 UI 与设备实际状态一致。场景三通过输入/输出流读写串口数据通信场景scenario3_readWrite.js演示了串口全双工通信的标准姿势读走inputStream写走outputStream。读取数据DataReader loadAsync读取时每次最多读取 1024 字节同文件 #L61-L80function readAsync() { var bytesToRead 1024; var stream SdkSample.CustomSerialDeviceAccess.eventHandlerForDevice.current.device.inputStream; var reader new Windows.Storage.Streams.DataReader(stream); serialIo.readingPromise reader.loadAsync(bytesToRead).then(function (bytesRead) { serialIo.totalBytesRead bytesRead; if (bytesRead 0) { document.getElementById(ReadBytesTextArea).value reader.readString(bytesRead) \n; } // 显式释放 DataReader 资源 reader.detachStream(); reader.close(); }); return serialIo.readingPromise; }关键实践点loadAsync(bytesToRead)返回实际读到的字节数bytesRead随后用readString(bytesRead)按该字节数解析字符串每次读完显式调用detachStream()与close()释放资源——因为读操作可能被循环执行不能依赖垃圾回收。写入数据DataWriter storeAsync写入侧同文件 #L84-L112从输入框取字符串经DataWriter.writeString写入outputStream再调用storeAsync()真正把数据冲刷到设备var stream SdkSample.CustomSerialDeviceAccess.eventHandlerForDevice.current.device.outputStream; var writer new Windows.Storage.Streams.DataWriter(stream); writer.writeString(arrayBuffer); serialIo.writingPromise writer.storeAsync().then(function (bytesWritten) { serialIo.totalBytesWritten bytesWritten; writer.detachStream(); writer.close(); });storeAsync()的返回值才是实际写入设备的字节数示例据此累计totalBytesWritten并刷新计数标签。读写超时与任务取消场景页面提供独立的读写超时设置writeTimeout与readTimeout单位毫秒通过数字输入框修改同文件 #L149-L169输入非法值isNaN时清空输入框而不写入设备。针对可能长时间挂起的 IO 操作示例实现了完善的取消机制同文件 #L24-L48cancelRead()/cancelWrite()分别对readingPromise/writingPromise调用.cancel()cancelAllIoTasks()在读或写进行中时同时取消两者取消后 Promise 以error.name Canceled的形式回调UI 据此恢复按钮状态见 scenario3_readWrite.js。读/写按钮在操作进行期间被禁用updateButtonStates同文件 #L305-L321防止并发 IOCancel 按钮则仅在操作进行时可用。应用挂起时通过注册在EventHandlerForDevice上的onAppSuspendCallback调用cancelAllIoTasks()确保设备关闭前所有挂起 IO 被妥善取消。场景四Pin Changed 与 Error Received 事件事件场景scenario4_events.js演示了Windows.Devices.SerialCommunication提供的两类设备事件的通知式用法。注册 Pin Changed 事件串口引脚状态变化如 CTS/CD 翻转会触发pinchanged事件。注册逻辑同文件 #L44-L56registerForPinChangedEvents: function () { if (!this.isRegisteredForPinChangedEvents) { this.pinChangedEventHandler this.onPinChangedEvent; this._registeredDevice SdkSample.CustomSerialDeviceAccess.eventHandlerForDevice.current.device; this._registeredDevice.addEventListener(pinchanged, this.pinChangedEventHandler, false); this.isRegisteredForPinChangedEvents true; } }注册 Error Received 事件串口通信出错如帧错误、奇偶校验错误等会触发errorreceived事件注册方式与 Pin Changed 完全对称同文件 #L75-L87。注销与设备重建的安全处理注销逻辑同文件 #L60-L71有一个重要的防御性判断只有当当前连接的设备对象与当初注册事件时的设备对象是同一个实例时才调用removeEventListener。这是因为设备可能被拔出再重连重连后的SerialDevice是全新对象其上并不包含旧的事件处理器贸然注销反而会误操作。示例同时在_onDeviceClosing同文件 #L104-L107中统一注销两类事件并利用navigatedAway标志避免页面离开后事件输出串入其他场景页面。两个事件开关的 UI 状态同步由updatePinChangedView/updateErrorReceivedView同文件 #L133-L167完成与连接状态联动设备未连接时切换开关只会弹出提示notifyDeviceNotConnected见 utilities.js。设备单例与后台任务扩展EventHandlerForDevice 的设计要点贯穿四个场景的 eventHandlerForDevice.js 是一个单例类current静态属性见同文件 #L408-L442它承担了设备句柄之外的全部周边管理职责打开设备后自动注册应用挂起/恢复事件、设备访问权限变化事件与设备监视器事件提供两个工厂方法createNewEventHandlerForDevice()前台应用注册应用事件、开启自动重连与createNewEventHandlerForDeviceForBackgroundTasks()后台任务不注册应用事件因为后台任务不涉及 UI 生命周期设计上只允许同时连接一台设备若要支持多设备注释明确建议将单例改为多实例每个实例各自监视一台设备。这种句柄 事件管理的封装模式是 UWP 外设类示例中可复用的典型骨架直接适用于 USB、HID 等其他设备访问场景。系统要求与构建运行原文档明确了该示例的运行环境客户端为 Windows 10服务器端为 Windows Server 2016 Technical Preview见 README.md。构建与运行步骤同文档 #L51-L60若通过 ZIP 包下载示例务必解压整个压缩包而不是只解压单个示例文件夹——所有示例共享SharedContent目录中的公共依赖启动 Microsoft Visual Studio 2017选择FileOpenProject/Solution进入解压目录后定位到本示例的archived/CustomSerialDeviceAccess/js子目录双击解决方案文件 CustomSerialDeviceAccess.sln按 CtrlShiftB或BuildBuild Solution构建构建完成后按 F5启用调试运行或 CtrlF5不启用调试运行启动应用。实战要点总结综合文档与源码UWP 串口通信的完整链路可归纳为五个步骤每一步都能在示例源码中找到对应实现声明能力在 Package.appxmanifest 中声明serialcommunication设备能力枚举设备通过SerialDevice.getDeviceSelector()或 VID/PID/接口 GUID 构造 AQS用DeviceInformation.createWatcher跟踪设备热插拔打开设备用SerialDevice.fromIdAsync(id)获取句柄并对返回空值的情况按DeviceAccessStatus分类处理配置与通信读写baudRate、parity、stopBits、handshake、dataBits等属性通过inputStream/outputStream配DataReader/DataWriter完成数据收发生命周期管理处理pinchanged/errorreceived事件并在应用挂起时取消 IO、关闭句柄恢复时重新枚举与自动重连。这套流程覆盖了串口开发中绝大多数真实场景——从设备接入检测、参数协商到数据收发与异常恢复是编写可靠 UWP 串口应用时可对照参考的完整范本。赞分享示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载相关推荐Windows 通用串口设备访问实战CustomSerialDeviceAccess 示例深度解析Windows 通用串口设备访问实战CustomSerialDeviceAccess 示例深度解析 导读 本文以 Windows universal samp示例工程Windows UWP 自定义 HID 设备访问全流程实战解读 Windows-universal-samples 的 CustomHidDeviceAccess 样例Windows UWP 自定义 HID 设备访问全流程实战解读 Windows universal samples 的 CustomHidDeviceAcce示例工程LTX-2两阶段生成管道TI2VidTwoStagesPipeline的生产级质量优化指南LTX 2两阶段生成管道TI2VidTwoStagesPipeline的生产级质量优化指南 LTX 2是首个基于DiT架构的音频 视频生成基础模型而 TI2人工智能大模型媒体生成视频音频微调模型量化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

1602LCD显示扩展lcd-1602-display完全介绍:3个积木点亮你的第一块字符屏
1602LCD显示扩展lcd-1602-display完全介绍:3个积木点亮你的第一块字符屏

1602LCD显示扩展lcd-1602-display完全介绍:3个积木点亮你的第一块字符屏 【免费下载链接】lcd-1602-display 源师兄扩展项目: 1602LCD | 由源师兄组织创建 项目地址: https://gitcode.com/yuanshixiong/lcd-1602-display lcd-1602-display 是源师兄组织出品的… · 2026/9/25 12:59:01

5 分钟完成接入!OpenClaw 搭配 DeepSeek V4,模型切换一步到位(TaoToken 统一 Key 配置)
5 分钟完成接入!OpenClaw 搭配 DeepSeek V4,模型切换一步到位(TaoToken 统一 Key 配置)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 12:58:54

从视频生成到三维高斯重建:minimaxH3多视角采集实战
从视频生成到三维高斯重建:minimaxH3多视角采集实战

前阵子我在做一个小场景的三维重建项目,数据采集环节卡了很久——拍摄设备不够、光线来回变、物体表面又是反光材质,怎么拍都不理想。后来偶然试了一下用minimaxH3生成360度定格旋转视频,再把关键帧提取出来做多视角数据采集,配合… · 2026/9/25 12:58:36

YooAsset资源管理框架深度解析:分层架构、引用计数与热更新实践
YooAsset资源管理框架深度解析:分层架构、引用计数与热更新实践

1. 为什么资源管理是Unity项目绕不开的一道坎做Unity项目超过两年的朋友,大概率都经历过这样的场景:项目初期资源随便放,Resources.Load一把梭,跑得挺欢;等到版本迭代到第三四个大版本,包体膨胀到两三百兆&… · 2026/9/25 13:24:10

命令模式实战:将请求封装为对象,实现撤销、队列与宏命令
命令模式实战:将请求封装为对象,实现撤销、队列与宏命令

写代码的人可能都有过这种经历:一堆按钮的点击事件里塞满了业务逻辑,每个按钮背后new一个处理器;等业务方说要增加“撤销上一步”功能时,你发现根本无从下手,因为操作的历史记录压根没存;再后来产品又提了“… · 2026/9/25 13:23:57

第057篇 一文讲透拼多多工程化:ES Module 与 CommonJS 的区别,模块打包原理
第057篇 一文讲透拼多多工程化:ES Module 与 CommonJS 的区别,模块打包原理

摘要:本篇复盘 拼多多 前端开发岗位在 工程化 方向的真实问法,重点拆 8 道题:Babel、SWC 与 esbuild 如何取舍、ES Module 与 CommonJS 的区别,模块打包原理、MVC、MVP 与 MVVM 的差异与取舍。每题按「考察点 → 参考答案 → 代码/实操 → 易错点 → 面试官追问」五段式展… · 2026/9/25 13:23:51

WorkBuddy Enterprise:从超级个体到超级团队的Agent平台化实践
WorkBuddy Enterprise:从超级个体到超级团队的Agent平台化实践

1. 从「超级个体」到「超级团队」:这个平台到底在解决什么问题第一次看到「WorkBuddy Enterprise」这个名字,我脑子里蹦出来的第一个念头是:腾讯云终于把 CodeBuddy 那套东西往企业级方向推了。CodeBuddy 我用过挺长一段时间,单兵… · 2026/9/25 13:23:45

企业级 AI 网关全景图:TaoToken 统一 Key 与 API 通道的配置骨架
企业级 AI 网关全景图:TaoToken 统一 Key 与 API 通道的配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 13:23:39

Atlas 300V 24G 昇腾推理卡实战:YOLO 部署与性能调优全攻略
Atlas 300V 24G 昇腾推理卡实战:YOLO 部署与性能调优全攻略

1. 从一张加速卡说起:我为什么盯上了 Atlas如果你最近在折腾深度学习推理、YOLO 系列模型部署,或者搞边缘计算,那你大概率绕不开一个名字——Atlas。我先说结论:Atlas 是华为昇腾生态里的 AI 加速卡产品线,而“Atlas 3… · 2026/9/25 13:23:32

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码