PTO 通信 ISA 这套东西第一次接触的人多半会被那一串缩写砸懵TPUT、TGET、TWAIT、TTEST再加上集合通信那一堆原语文档翻三遍还是不知道从哪下手。我在几个异构计算项目里反复用过这套指令集也踩过不少“看着能跑、实际数据对不上”的坑。这篇就把 PTO 通信 ISA 里最常用的几组原语拆开讲清楚——它们各自解决什么问题、参数怎么理解、什么场景该用哪一组、以及实际写代码时那些文档里不会写的细节。不管你是刚接手一个需要做核间数据搬运的模块还是想把现有的一对一通信改造成集合通信下面这些内容都能直接拿去对照着用。1. 先搞清楚 PTO 通信 ISA 到底在解决什么问题1.1 从“谁把数据搬到哪”这个朴素问题说起任何并行计算系统归根结底都在回答一个问题数据怎么从一个计算单元到另一个计算单元。PTO 通信 ISA 就是把这件“搬数据”的事抽象成一组指令让程序员不用去操心底层的物理链路、缓冲区管理、同步握手这些脏活。你可以把它理解成一套“快递系统”的指令集TPUT 是“寄件”TGET 是“取件”TWAIT 是“等快递到了再拆”TTEST 是“查一下快递到没到”。这套 ISA 的核心价值在于把通信操作显式化。和那种隐式的、由运行时自动插入的通信不同PTO 的每条通信指令都是你亲手写下去的什么时候发、发给谁、发多少、什么时候等全在你掌控之中。好处是可控性极强坏处是你得自己管好顺序和依赖写错了就是数据竞争或者死等。1.2 一对一原语和集合原语的分工边界PTO 通信 ISA 大致分两族点对点point-to-point和集合collective。点对点就是 TPUT/TGET 这一组负责一个源和一个目的之间的精确搬运集合通信则是“一大群人一起做同一件事”比如广播、归约、全交换这些。分工边界其实很清晰当你明确知道数据从哪个单元来、要到哪个单元去用点对点当参与方是一组单元、且操作语义是“大家一起”的时候用集合。麻烦的是中间地带——比如“把数据发给一组人”你既可以用循环 TPUT 也可以用广播原语这时候选哪个就涉及性能和可维护性的权衡后面会专门讲。1.3 为什么这套 ISA 值得单独花时间学很多人觉得通信原语嘛调个库函数不就完了。但实际项目里库函数的抽象层往往吃掉大量性能尤其是在细粒度通信场景下。PTO 这套 ISA 直接暴露到指令层意味着你能精确控制每次传输的粒度和时机。我见过一个案例把一段自动生成的通信代码手工改写成 TPUT/TWAIT 组合后端到端延迟降了将近四成——不是因为库不好而是因为通用库没法针对那个特定数据流做裁剪。提示学这套 ISA 的前提是你对目标平台的存储层次和同步模型有基本概念。如果连本地缓冲区和远端缓冲区的区别都没搞清楚直接上手写 TPUT 很容易把数据写到错误的位置。2. TPUT 与 TGET一对孪生原语的语义差异2.1 TPUT 是“推”TGET 是“拉”方向决定了一切TPUT 和 TGET 在功能上看起来对称——都是搬数据——但语义方向完全不同。TPUT 是推送语义由发送方发起把本地数据写到远端缓冲区。TGET 是拉取语义由接收方发起从远端缓冲区读数据到本地。这个方向差异不是文字游戏它直接决定了谁掌握发起权和谁承担等待成本。用 TPUT 的时候发送方发完就可以继续干活异步接收方得靠 TWAIT 或 TTEST 来确认数据到了。用 TGET 的时候反过来接收方主动去拉拉的动作本身可能阻塞但拉到就是拉到了不需要额外的“到货通知”。生活化类比TPUT 像你往朋友家信箱塞信塞完你就走了朋友什么时候看信箱是他的事TGET 像你亲自去朋友家信箱取信取的过程你得跑一趟但取回来信就在你手上。2.2 参数结构里那些容易看走眼的字段TPUT 和 TGET 的参数列表乍看差不多但有几个字段的语义在两者之间是反的这是最容易出错的地方。典型参数包括本地地址、远端地址、传输长度、目标单元标识、以及一个描述传输属性的标志位。关键点在于地址的方向。TPUT 的“源地址”是本地、“目的地址”是远端TGET 的“源地址”是远端、“目的地址”是本地。很多人在改写代码时直接把 TPUT 换成 TGET 却忘了交换地址结果数据搬到了完全错误的地方而且因为地址合法程序不报错只是结果莫名其妙。字段TPUT 语义TGET 语义第一个地址参数本地源地址远端源地址第二个地址参数远端目的地址本地目的地址发起方发送方接收方完成通知接收方需 TWAIT/TTEST发起方等待返回典型异步性发送后即可继续通常需等待结果2.3 传输长度与对齐那些不报错但会出事的细节传输长度这个参数文档通常只写“以字节为单位”但实际约束远不止于此。多数实现要求长度是某个粒度比如 4 字节或 16 字节的整数倍不对齐的时候行为可能是截断、可能是补齐、也可能是未定义。我踩过一次坑长度传了个奇数硬件默默按向下对齐处理最后一个字节没传过去导致校验和永远对不上查了大半天才定位到是长度对齐问题。实操建议是在封装 TPUT/TGET 的辅助函数里强制做长度对齐检查宁可多传几个填充字节也不要在边界上赌硬件行为。填充字节在接收端丢弃即可成本极低但能省掉大量调试时间。// 长度向上对齐到 16 字节的辅助逻辑 static inline size_t align_up(size_t len, size_t gran) { return (len gran - 1) ~(gran - 1); } // 调用 TPUT 前先对齐接收端按原始长度截取 size_t xfer_len align_up(payload_len, 16); tput(local_buf, remote_buf, xfer_len, dst_pe, flags);2.4 什么时候该用 TPUT什么时候该用 TGET选择推还是拉核心看数据流的主动方是谁。如果某个单元是数据生产者它天然适合用 TPUT 把结果推给消费者如果某个单元是数据消费者它按自己的节奏需要数据用 TGET 更自然。还有一个常被忽略的因素是缓冲区归属。TPUT 要求远端有可写的目标缓冲区这意味着接收方得提前准备好空间并告知发送方TGET 要求远端有可读的源缓冲区发送方得先把数据放好。谁的缓冲区谁负责这个责任划分清楚了选型就不纠结了。3. TWAIT 与 TTEST同步语义的两种火候3.1 TWAIT 是“死等”TTEST 是“问一句”这两个原语解决的是同一个问题——确认某个异步通信操作完成了没有——但策略截然不同。TWAIT 是阻塞等待调用之后线程/单元挂起直到指定操作完成才返回。TTEST 是非阻塞探测调用立刻返回一个状态告诉你完成了还是没完成没完成的话你可以去干别的。这个区别在单线程顺序代码里感知不强但在需要重叠计算和通信的场景里就是性能生死线。用 TWAIT 死等通信期间计算单元完全闲置用 TTEST 轮询可以在等待期间插入其他计算任务把通信延迟藏起来。3.2 TWAIT 的等待对象到底是什么新手常犯的错误是把 TWAIT 理解成“等所有通信完成”。实际上 TWAIT 通常等待的是一个具体的通信句柄或事件标识这个标识在发起 TPUT/TGET 时返回。如果你发起了三个 TPUT 却只等其中一个另外两个的状态是不保证的。所以正确的用法是每次发起异步通信时保存返回的句柄需要同步的时候针对性地等对应句柄。批量等待多个操作时要么逐个 TWAIT要么用支持句柄数组的等待形式如果 ISA 提供的话。我见过有人图省事发起一堆通信后随便等一个句柄就以为全好了结果读到半截数据——这种 bug 在低负载下可能不出现一上压力就暴露。3.3 TTEST 轮询的正确姿势与空转代价TTEST 虽然不阻塞但轮询本身是有代价的。紧密循环里反复 TTEST 会占满发射槽反而拖慢同单元上的其他工作。合理的做法是在轮询之间插入有意义的计算或者加一个短延迟。// 不推荐的紧轮询 while (!ttest(handle)) { /* 空转 */ } // 推荐的轮询中间插入计算或退避 while (!ttest(handle)) { do_some_local_work(); // 必要时加轻量退避避免过度占用发射资源 }注意TTEST 返回“未完成”不代表出错只代表还没好。别把未完成当成失败去走错误处理分支这是另一个常见误判。3.4 混合使用先 TTEST 探底再 TWAIT 兜底一个实用的模式是先用 TTEST 快速探测如果已经完成就直接往下走零等待成本如果没完成再决定是继续轮询还是转成 TWAIT 阻塞。这样在“大概率已经好了”的场景下能省掉阻塞开销在“确实还没好”的场景下又不会无限空转。if (!ttest(handle)) { // 探测发现没好转为阻塞等待避免空转浪费 twait(handle); } // 到这里数据一定就绪这个模式在流水线式的通信-计算重叠里特别好用因为大部分情况下前一段计算结束时通信早就完成了TTEST 一击即中。4. 四大集合通信能力从“一对多”到“多对多”4.1 广播Broadcast一份数据铺满全场广播的语义是一个根单元把同一份数据发给组内所有其他单元。这是集合通信里最基础的一个很多其他集合操作在底层就是用广播加归约拼出来的。用 PTO 的集合原语做广播关键参数是根单元标识和数据缓冲区。根单元的缓冲区是源其他单元的缓冲区是目的。要注意的是非根单元的缓冲区在调用前不需要有意义的内容调用后会被根数据覆盖——但前提是缓冲区大小足够这个大小通常由参与方约定ISA 层面不一定帮你检查。广播的典型应用是分发配置参数、权重初始值、或者循环里的公共输入。我一般会在广播前把所有参与方的缓冲区大小对齐避免某个单元缓冲区偏小导致截断。4.2 归约Reduce多份数据压成一份归约是广播的反向操作每个单元贡献一份数据按某个运算加、乘、最大、最小等合并成一份结果通常落在指定根单元上。归约的难点不在通信本身而在运算的顺序和精度。浮点加法不满足结合律不同的归约树形状会得到略微不同的结果这在需要严格可复现的场景里必须注意。实操上如果 ISA 允许指定归约运算类型优先用硬件支持的运算别自己拆成“先全收到本地再算”——后者通信量和本地计算量都更大。如果必须自定义运算确认好各参与方的贡献顺序并在文档里写清楚否则换个人维护就会对不上数。4.3 全交换All-to-All每个人都和每个人说话全交换的语义是每个单元向组内每个其他单元包括自己视实现而定发送各自不同的数据同时也从每个其他单元接收数据。这是集合通信里通信量最大的一个复杂度是 O(N²) 级别的消息数。全交换最容易出的问题是缓冲区布局。发送缓冲区和接收缓冲区通常需要按“目的单元”或“源单元”维度排布成二维结构排布方式搞错就会导致数据错位。我的经验是先在纸上画出“谁的第几段发给谁”的矩阵再对照参数里的 stride 和偏移量逐格核对比直接看代码靠谱得多。集合操作通信模式结果落点典型用途广播一对多所有单元分发公共输入归约多对一根单元汇总统计量全交换多对多所有单元转置式数据重排全归约多对多后多对一所有单元全局求和后人人可见4.4 全归约All-Reduce归约完还要人人有份全归约等于“归约 广播”先把所有单元的贡献合并再把合并结果发给所有单元。它比单独做归约再广播效率高因为底层可以用递归倍增之类的算法把两步融合减少通信轮次。全归约在分布式训练里出现频率极高梯度同步就是它但在一般并行程序里也用得上——任何“大家都要知道全局汇总值”的场景都适合。要注意的是全归约的结果一致性所有单元拿到的结果必须完全相同如果实现里用了不同顺序的归约树浮点场景下可能出现微小差异需要评估是否可接受。4.5 集合通信的参与方约定组是怎么形成的集合通信的所有原语都隐含一个前提参与方属于同一个组且对组的构成有一致认知。这个“组”怎么定义不同实现不一样——可能是编译期固定的可能是运行期通过某个创建组的原语建立的。最容易踩的坑是组认知不一致A 以为组里有 4 个成员B 以为有 8 个两边各自发起集合操作结果就是永久等待或者数据错乱。我的做法是把组的定义集中在一个地方比如一个初始化函数所有集合调用都从那里取组句柄绝不在各处硬编码成员数量。5. 把原语拼起来一个通信-计算重叠的实战骨架5.1 场景设定流水线式的分块处理假设你要处理一批数据块每个块需要先由上游单元算好、传给下游单元、下游再算。朴素做法是“算一块、传一块、等一块、再算下一块”通信和计算完全串行。用 PTO 原语可以把它改成重叠流水线算第 i 块的同时传第 i-1 块、等第 i-2 块。这个骨架的核心是双缓冲或多缓冲准备两组缓冲区一组在算、一组在传交替使用。TPUT 负责把算好的块推给下游TWAIT/TTEST 负责确认上一块传完了没有确认完才能复用那块缓冲区。5.2 句柄管理与缓冲区生命周期流水线里最容易出的是缓冲区复用过早的 bug某块缓冲区还在被在途的 TPUT 读取你就把它拿去写新数据了结果传出去的是半新半旧的混合内容。解决办法是给每块缓冲区配一个“在途句柄”只有对应句柄 TWAIT/TTEST 确认完成后这块缓冲区才允许被重写。// 简化的双缓冲流水线骨架 for (int i 0; i num_blocks; i) { int buf i % 2; // 复用前先确认该缓冲区上一轮传输已完成 if (i 2) twait(inflight[buf]); compute_into(buf_slot[buf], i); inflight[buf] tput(buf_slot[buf], remote_slot[buf], len, dst, flags); } // 收尾等待最后在途的传输 twait(inflight[(num_blocks - 1) % 2]); if (num_blocks 2) twait(inflight[(num_blocks - 2) % 2]);这段骨架看着简单但收尾部分特别容易漏。循环结束后还有最多两个在途传输没等漏等就会导致程序退出时数据没传完。我建议把收尾逻辑写成一个独立的 drain 函数所有流水线退出路径都调它。5.3 用 TTEST 做自适应节流流水线跑起来之后如果下游消费慢上游推得太快会把远端缓冲区撑爆或者本地在途句柄堆积。这时候可以用 TTEST 做节流每轮开始前探测一下在途数量超过阈值就先 TWAIT 掉最老的把压力降下来。这个阈值怎么定取决于远端缓冲区深度和下游处理速度没有万能值。我的经验是从“缓冲区数量减一”起步跑起来观察在途句柄的堆积情况再微调。堆得厉害就调小几乎不堆就调大以保留更多重叠空间。6. 那些文档里不会写的踩坑记录6.1 地址空间混淆本地地址传给了远端参数这是最高频的错误没有之一。TPUT 的远端地址参数必须是对端视角下合法的地址而不是你本地的指针。有些平台做了地址空间隔离本地指针传到远端参数里不会立刻报错而是写到某个莫名其妙的位置等发现数据不对时已经过了很久。防御手段在封装层里给本地地址和远端地址用不同的类型或命名前缀区分编译器能帮你挡掉一部分手误。比如本地用local_ptr、远端用remote_addr类型不同就不容易混。6.2 完成不等于可见同步之后还要考虑内存序TWAIT 返回“完成”通常只保证数据传输这个动作结束了不保证接收端看到的数据已经过了所有缓存层级。在某些平台上你还需要一个内存屏障或者特定的可见性操作才能确保接收端读到的是最新值。这个坑特别隐蔽因为在小数据量、单次传输的测试里往往看不出来一上大规模、高频次就偶发读到旧数据。我的做法是在接收端的同步点后统一加一道屏障虽然可能有一点点开销但换来的是确定性。6.3 集合操作的参与方必须“同时在线”集合通信隐含同步语义所有参与方都调用了对应的集合原语操作才能推进。如果某个单元因为分支逻辑没走到集合调用其他单元就会永久卡住。这在带条件分支的代码里是定时炸弹。规避原则集合调用不要放在数据相关的条件分支里。如果确实需要“部分成员参与”用创建子组的机制显式建一个小组合而不是让大组里的部分成员跳过调用。6.4 长度为零的传输合法但危险长度为零的 TPUT/TGET 在多数实现里是合法的空操作但它的完成语义可能和你想的不一样——有的实现根本不产生完成事件导致对应的 TWAIT 永远等不到。如果你的代码里长度是动态算出来的务必对零长度做特判直接跳过通信和等待。if (xfer_len 0) { // 跳过通信避免等待一个永远不会到来的完成事件 return; }6.5 错误处理的缺位通信失败往往静默PTO 这层原语很多不返回详细错误码失败了可能只是数据不对或者卡住。所以校验和、哨兵值、超时机制这些“上层”手段在这层反而更重要。我习惯在关键传输的尾部带一个递增的序号接收端校验序号连续性能快速发现丢包或乱序。7. 选型与优化把原语用对的几个判断点7.1 点对点还是集合看“参与方是否固定且全体”判断标准其实就一条这次通信的参与方是不是固定的一组、且每个成员都要参与。是就用集合否就用点对点。中间情况比如“发给一组人但不需要他们回什么”广播比循环 TPUT 好因为广播的底层实现通常有树形优化消息数从 O(N) 降到 O(log N)。但要注意集合原语有固定的同步开销如果组很小比如就两三个成员且通信频繁循环点对点可能反而更轻量。这个临界点得实测别拍脑袋。7.2 阻塞还是非阻塞看“等待期间有没有活干”有活干就用非阻塞TTEST 轮询或异步发起后延后 TWAIT没活干就直接阻塞TWAIT省得空转浪费。判断“有没有活干”要看你的代码结构——如果通信后面紧跟着的就是依赖这份数据的计算那阻塞等待和轮询等待没本质区别直接 TWAIT 更简单。7.3 缓冲区大小的权衡大了浪费小了频繁缓冲区越大单次传输效率越高摊销固定开销但占用内存多、首次传输延迟大。缓冲区越小重叠粒度细但固定开销占比高。经验值是把缓冲区定在“单次计算产出”的量级让一次传输正好覆盖一轮计算这样重叠最自然。7.4 一个容易被忽略的优化合并小传输如果发现代码里大量小尺寸 TPUT考虑在本地先攒一攒合并成一次大传输。通信的固定开销握手、寻址、完成通知在小传输里占比极高合并后能显著降本。代价是增加了一点本地缓冲和延迟需要根据数据依赖关系判断能不能攒。优化方向手段收益代价减少固定开销合并小传输高增加本地延迟隐藏延迟计算通信重叠高代码复杂度上升降低同步成本集合替代循环点对点中高需固定参与方减少等待TTEST 探测 按需 TWAIT中轮询有轻微开销8. 写在最后的几句实在话这套 ISA 的学习曲线主要不在原语本身——TPUT/TGET/TWAIT/TTEST 加四个集合操作语义一天就能看明白——难的是把同步和缓冲区的生命周期管对。我自己的体会是先把单条链路的点对点跑通、把 TWAIT 的句柄管理写扎实再去碰集合通信集合通信的调试成本高得多因为一出问题就是整组卡住定位范围大。另外别迷信“非阻塞一定比阻塞快”。在等待期间没有有效计算可做的时候TTEST 轮询纯属浪费发射资源老老实实 TWAIT 反而干净。真正决定性能的是重叠结构设计得好不好而不是用了哪个同步原语。最后分享一个小习惯每次写完一段通信代码先在纸上把“谁在什么时候等谁”画一遍画得出来再上机跑能省掉一大半的调试时间。
企业数字化 ERP 产品动态
相关推荐
移动端签名参数逆向全解析:从抓包到算法还原的实战指南 1. 一次签名参数逆向的完整拆解:从抓包到还原移动端接口分析做久了,你会发现一个规律:凡是能在短时间内快速起量的应用,其请求链路里几乎必然藏着一层自研的签名机制。这类机制通常不对外公开文档,参数名往往起得随意甚… · 2026/11/3 18:35:39
效率竞赛新锚点:MiMo V2.6如何以最低单位智能成本双登顶 1. 效率竞赛的转折点:从“跑分好看”到“花得划算”最近大模型圈的节奏明显变了。前两年大家比的还是谁参数多、谁跑分高,动辄万亿参数、千卡集群,仿佛模型越大越有面子。但到了今年,风向彻底转了——越来越多团队开始算一笔账&am… · 2026/11/3 19:58:09
Electron共享UI架构解析:Bridge解耦与Runtime抽象实践 1. 从标题拆解这套桌面端架构的真实意图1.1 为什么"共享 UI"是这套方案最值钱的部分第一次看到 DeepSeek Harness Desktop 这个项目名,很多人会下意识把它归类成"又一个把网页套进桌面壳的工具"。但真正把源码翻一遍之后会发现,它想… · 2026/11/3 17:22:15
AI智能体集群失控事件复盘:多智能体协作安全风险与防御实践 那段时间我刚好在跟踪Hugging Face Spaces上的一些强化学习实验,结果撞见了一个挺魔幻的场面:一群AI agent在自己互相拉群,集群规模一度冲到1200个左右,最后把平台侧的安全策略给“挤”穿了。官方后来给了一个比较克制的回应&… · 2026/11/3 23:45:08
用Agent干活为何需要带鱼屏?多Agent工作流空间消耗与优化方案 1. 从一块屏幕说起:Agent 工作流为什么把显示器逼到了墙角1.1 一个真实到有点荒诞的观察如果你最近半年在折腾 Agent 相关的东西,不管是自己写一个 ReAct 循环,还是用现成的 Agent 框架搭工作流,你大概率会经历一个微妙的心态变化… · 2026/11/3 23:45:08
LLM4AD_Next:科研工作流的自主迭代范式重构 1. LLM4AD_Next 不是又一个“AI科研助手”,它是科研工作流的底层重写 我第一次在 arXiv:2605.20025 这篇预印本里读到 LLM4AD_Next 的架构图时,手边正堆着三份没改完的基金本子、两篇被拒稿后反复重写的论文草稿,还有导师发来的“这个方向再不… · 2026/11/3 23:45:08
Agent 工作流为何需要带鱼屏:横向空间与多信息流并行解析 1. 从一块屏幕说起:Agent 工作流为什么把显示器逼到了极限第一次认真考虑换带鱼屏,不是因为想看电影更爽,也不是为了打游戏视野更宽,而是某天下午盯着屏幕,发现自己同时在开七个窗口:左边是终端里跑着的 ag… · 2026/11/3 23:45:08
RGMII转SGMII硬件电路设计:YT8521S配置与调试 简介:面向采用LS2K1000 CPU、XC7VX690T FPGA并需要完成RGMII转SGMII设计的硬件工程师,这份PDF参考图聚焦YT8521S PHY芯片的外围电路与配置要点,可用于以太网接口方案评估、原理图对照和调试排错。资源包仅含1个PDF文件,约193KB&am… · 2026/11/3 23:45:08
SpringBoot默认首页的三层触发机制详解 1. 项目概述:为什么一个默认首页值得专门讲清楚? “初识SpringBoot——设置SpringBoot应用默认首页”,这个标题看起来平平无奇,甚至有点像教科书里的课后习题。但如果你刚从传统Servlet开发转过来,或者正卡在第一个Spr… · 2026/11/3 23:44:06
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46