简介《基于STM32G4电机控制基础篇》是一份面向电机控制初学者与嵌入式开发者的PDF培训讲义围绕STM32G4系列展开帮助读者建立从ST电机控制生态到FOC工程落地的整体认识。压缩包内仅含1个PDF文件大小约3.64MB内容以课件形式组织便于按章节查阅和对照实践。讲义先梳理ST-MC-SUITE、Motor Profiler、X-CUBE-MCSDK等工具链关系再演示用MC SDK Workbench创建工程、选择Demo板与电机、生成STM32G4 FOC代码并说明通过Dashboard或mc_api.c运行电机的两种方式。针对Workbench芯片型号有限的问题还给出结合CubeMX移植电机控制代码的思路包括Customer Board创建、Timer与控制脚、串口、编码器/Hall信号以及电流、电压、温度等模拟端口配置。已有900人学习适合希望理解官方工具链并迁移到自有板卡的读者。1. 从一块板子和一台电机说起桌上摆着一块 STM32G4 的 Nucleo 板插上功率扩展板再接一台无刷电机通电之前大部分人都会卡在同一个问题上这玩意儿到底该从哪儿下手。STM32G4 电机控制基础篇这份资料的价值不在于讲了多少公式而在于它把一条路径摊开了——先用 Motor Control Workbench 把板卡、功率板、电机参数点选出来让 MCSDK 生成一套可编译的 FOC 代码在串口面板里把电机点转起来再回头去啃电流环和速度环。适合两类人刚接触 FOC、手里有官方套件想尽快看到电机转动的初级工程师以及手上是自研板、芯片型号压根不在 Workbench 下拉列表里想借 CubeMX 把电机库移植过去的老手。第一类人读完能跑通第二类人读完能改。2. 生态与选型先弄清你要控制的是哪一类电机2.1 从电机类型倒推控制方案资料开头列的电机谱系图信息密度很高但很多人扫一眼就过去了。这张图真正的用法是倒推你先确定电机类型控制方案和外设占用基本就定了后面在 Workbench 里点选的每一项都能对应回来。电机类型典型驱动拓扑换相 / 控制方式G4 上主要占用的外设直流有刷单 H 桥PWM 调压 单电流环1 路 TIM 互补 PWM、1 路 ADC步进双 H 桥细分查表开环或堵转检测闭环2 路 TIM、片内比较器BLDC三相逆变桥六步换相Hall 或无感反电势TIM 六路 PWM、3 路输入捕获PMSM三相逆变桥FOC双电阻 / 三电阻 / 单电阻采样TIM 中心对齐 PWM 注入 ADC 编码器接口选型上有个容易忽略的点G4 系列带数学协处理器和片上比较器做 FOC 的克拉克、帕克变换和反帕克变换时三角函数和坐标旋转能压到很少的周期这也是这份资料敢在一颗主流 M4 上跑双环的原因。如果你的项目是低速大扭矩场合比如云台或者泵类负载PMSM FOC 是首选如果是风机、电动工具这类对转矩脉动不敏感的场合BLDC 六步换相的实现成本低得多代码量可能只有 FOC 的三分之一。我在实际项目里踩过的坑是明明选的是 PMSM却在 Workbench 里把功率板按 BLDC 的方式配了生成出来的代码能编译但电流波形完全不对。判断标准很简单看反电势是不是梯形波梯形波就老老实实走六步。2.2 工具链的四层结构这套生态不是一堆散装工具而是四层从上到下依次是Motor Control Workbench图形化配置选芯片、选功率板、选电机、配引脚和参数本质是一个参数编辑器加代码生成器X-CUBE-MCSDK算法本体包含 FOC 的双环控制、状态机、故障管理、各种观测器STM32CubeMX时钟树、引脚复用、中间件初始化MCSDK 的工程最终还是要靠它生成底层IDEKeil MDK、IAR、Atollic TrueSTUDIO 三选一Workbench 支持直接导出理解这四层的关键在于分清谁生成谁。Workbench 是参数 模板的角色它生成的工程里既有 MCSDK 的库文件也有 CubeMX 风格的初始化代码。当你需要改芯片、改引脚、改时钟正确的入口是 CubeMX 而不是直接去改生成出来的 main.c否则下一次重新生成你的修改全部作废这个血泪经验后面还会再提。2.3 环境安装中文路径这件事别不当回事资料在安装环节专门加了一句CubeMX 请不要安装在中文路径下。这句话不是客套是我见过翻车最多的地方。CubeMX 生成工程时会写一堆中间文件和绝对路径引用路径里带中文或者空格轻则生成的工程缺文件重则编译时报找不到目标文件而且报错信息完全不指向真正的原因。装完之后花十秒确认一下你的工作目录# 检查当前工程路径是否含非 ASCII 字符 # 含中文、空格、全角符号的路径会让 CubeMX 生成的中间文件引用失效 pwd | grep -P [^\x00-\x7F] echo 路径含非 ASCII 字符请换到 D:/mcsdk_ws 这类纯英文目录grep -P用 PCRE 匹配非 ASCII 字节命中就说明路径有风险。命令不输出任何东西才是正常状态。工具版本上CubeMX 建议 v5.6 及以上配套对应固件库MCSDK 用 5.x 系列编译器三个任选Keil MDK v5.x 和 IAR v8.x 都验证过下载器用 ST-LINK 即可。3. 用 Workbench 生成第一份 FOC 工程3.1 新建工程板卡、功率板、电机三段式选型打开 Workbench 新建工程的流程是分段的先选 MCU 或 Demo 板再选功率板最后选电机。以官方那套电机控制套件为例选中的是 NUCLEO 主板加一块三相逆变扩展板电机是一台小型云台无刷电机。三段都选完之后Workbench 会自动把电压等级、电流采样电阻、运放增益、极对数这些参数填进去。这一步的意义在于它把硬件参数从你脑子里的记忆变成了工程里的配置。后面电流环的标幺化、过流保护的阈值、转速换算全都依赖这几个值填错一个电机要么不转要么抖得像筛糠。如果用的是自己画的板子就从 Customer Board 入口进功率板和电机仍然可以复用现有型号的模板改一下采样电阻和运放倍数就行。这是我推荐的做法比从零填参数省事得多也少了很多这个值到底该填多少的犹豫。3.2 数字端口与模拟端口两类引脚别混着配Workbench 的引脚配置分两块这个划分方式值得单独讲因为它对应的正是 FOC 的两条命脉。数字端口那一块主要是 TIM 的六路互补 PWM 输出、使能脚、故障脚、串口。有传感器方案还要加上编码器的 A/B/Z 或者三路 Hall 输入。这里有个默认约定TIM1 的 CH1/CH2/CH3 加三个互补通道驱动上桥和下桥死区时间在 Workbench 里配别在 CubeMX 里重复配两处都配会以其中一处为准你改的那处可能根本没生效。模拟端口那一块是三路信号相电流、母线电压、温度。电流采样是最关键的双电阻、三电阻、单电阻三种拓扑对 ADC 触发时序的要求完全不同。三电阻采样最宽松可以在 PWM 中心点触发注入转换单电阻采样要靠移相来重构三相电流对时序最苛刻。资料里用的是套件默认配置你在自己的板子上必须按实际拓扑选。我一般会在这里停下来核对一遍原理图确认采样电阻阻值、运放倍数、有没有加偏置电路。偏置这一项最容易漏双向电流采样需要一个中点偏置一般是 VDD/2没有偏置的话负半周电流直接读不出来表现出来就是电机能转但一加载就失步。3.3 生成代码两种运行方式配置完成后点生成Workbench 会产出一套完整工程。运行电机有两种方式资料里列的这两条基本覆盖了从调试到量产的路径。第一种是串口 Dashboard。用 Workbench 自带的监控界面连上串口能看到通信链路状态、当前状态机状态、各环的给定和反馈界面上还有一排控制按钮点一下电机就转。这套东西在第一次调试时非常好用因为它把电机不转这个模糊现象拆成了状态机卡在哪一步。第二种是 API 函数控制入口在mc_api.c。真正写产品代码时用这一套#include mc_api.h #include mc_interface.h int main(void) { HAL_Init(); SystemClock_Config(); /* CubeMX 生成的时钟配置FOC 的双环频率依赖它 */ MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM1_Init(); MC_Init(); /* MCSDK 生成的总初始化入口内部会拉起状态机 */ MC_StartMotor1(); /* 使能功率级状态机由 IDLE 进入 RUN */ /* 目标速度 1500单位是 0.1Hz 电频率即 150Hz 斜坡时间 1000ms避免阶跃给定把电流环顶到限幅 */ MC_ProgramSpeedRampMotor1(1500, 1000); for (;;) { /* 主循环只做慢速任务故障确认、状态上报、通信 */ if (MC_GetOccurredFaultsMotor1() ! MC_NO_FAULTS) { MC_AcknowledgeFaultMotor1(); /* 清故障注意先排查原因再清 */ } HAL_Delay(10); } }这段代码里有三个参数要理解清楚。MC_StartMotor1()只是使能不代表立刻转状态机需要经过对齐、启动、运行几个阶段MC_ProgramSpeedRampMotor1的第一个参数单位是 0.1Hz 的电频率不是机械转速如果你的电机极对数是 71500 对应 150Hz 电频率折算机械转速约 150×60/7 ≈ 1286rpm这个换算是新手最容易搞错的地方第二个参数是斜坡时间单位毫秒给得太小会触发过流给得太大响应又迟钝1000ms 是个稳妥的起点。主循环里千万别塞控制逻辑。FOC 的电流环跑在 ADC 注入转换完成中断里速度环跑在另一个低频中断里你在 main 的 while 里改给定、改参数随时可能和中断打架。HAL_Delay(10)这种阻塞式延时在调试阶段无所谓产品代码里应该换成你自己的调度器。4. 自研板移植芯片不在列表里怎么办4.1 Customer Board 与找相似芯片做参考Workbench 支持的芯片型号是有限的G4 系列里也不是每一颗都在下拉列表里。资料给的解法是创建 Customer Board 工程芯片选一颗类似的做参考再手工改。这里的关键是类似怎么定义。我的判断顺序是先看 Flash 和 RAM 够不够MCSDK 的库加上你的应用代码64KB Flash 起步比较稳再看 TIM1 是不是带互补输出和死区这是三相逆变的硬性要求然后看 ADC 的注入通道数量和是否支持与 TIM 联动触发最后看有没有运放或者比较器有的话能省外部电路。外设资源对上了型号不同只是引脚位置和封装差异改起来是体力活。创建工程时功率板和电机仍然选现有型号因为这两个是参数模板跟芯片型号没关系。选完之后 Workbench 生成的工程里芯片相关的部分会按你选的参考型号来剩下的就是手工替换。4.2 CubeMX 与 Workbench 联合建工程移植的实际操作顺序我习惯是反过来的先在 CubeMX 里把目标芯片的工程框架搭好时钟树、调试接口、串口这些先跑通能点灯能打印再去 Workbench 里生成一份参考工程然后把 MCSDK 相关的文件搬过来。搬的东西分三类MCSDK 的库文件Middlewares 目录下、mc_config.c/h和mc_api.c/h这几个配置文件、以及中断服务函数里的调用。中断这块要特别注意MCSDK 的电流环中断入口通常挂在 ADC 注入转换完成回调上你在 CubeMX 里生成的stm32g4xx_it.c可能已经有同名函数两边合并而不是覆盖。搬完之后最容易出问题的是时钟。Workbench 生成的工程里PWM 频率、ADC 采样频率、控制环频率是相互咬合的比如 20kHz 的 PWM 对应 20kHz 的电流环速度环再分频。你在 CubeMX 里重配时钟树只要主频变了这些频率全部要跟着重算。稳妥做法是把 Workbench 生成工程里的时钟配置抄过来不要自己重新点。4.3 移植后的核对清单搬完代码能编译不代表能转我一般按下面这张表逐项过一遍五分钟能省掉一晚上的瞎猜。核对项怎么查不通过的表现TIM1 六路 PWM 引脚对照原理图CubeMX 里看复用功能电机嗡嗡响不转或只朝一个方向抖死区时间Workbench 与 CubeMX 只留一处配置上下桥直通一上电就过流ADC 注入通道顺序与电流采样拓扑对应电流波形畸变Id/Iq 有明显直流量编码器 / Hall 引脚手动转动电机看计数值增减转速闭环飞车或原地振荡母线电压采样分压比万用表实测对比 ADC 读数过压 / 欠压误报中断优先级电流环最高速度环次之通信最低高负载下偶发失控最后一项最容易忽略。G4 的中断优先级分组如果配错串口接收中断可能抢占电流环表现出来就是电机运行中偶发抽一下负载越重越明显查起来非常玄学。5. 避坑与排查几个能让人查一晚上的问题5.1 上电即报过流先怀疑采样链路别怀疑算法现象电机还没开始转状态机直接跳 FAULT_OVER_CURRENTDashboard 上电流显示一个很大的固定值。原因九成是电流采样链路的问题不是 FOC 算法。常见的有三种——运放倍数配错、偏置电压没加或加错、ADC 通道和实际采样电阻对不上。空载时如果读到的电流是一个稳定的大数而不是接近零的噪声基本可以确定是偏置问题。解决先断开通电用万用表量采样电阻两端的静态电压应该是 VDD/2 附近再用调试器看 ADC 注入通道的原始值同样应该在量程中点。两个数对不上问题在模拟前端对得上但换算出来的电流不对问题在mc_config.c里的标幺化系数。5.2 电机能转但一加载就失步现象空载运行很顺手一捏或者加个小负载转速掉下来甚至直接停转。原因电流环的限幅给得太小或者速度环的积分项没有正确初始化。MCSDK 里电流限幅是个配置项默认值按参考电机给的你的电机额定电流如果比它大一加载就顶到限幅。解决按电机铭牌的额定电流重算限幅留 1.5 倍左右的余量给动态过程。速度环积分初值这个坑更隐蔽从 IDLE 直接切到 RUN 时如果积分项是零前几百毫秒的给定完全靠比例项撑表现就是启动瞬间软绵绵。5.3 转速方向反了改线还是改参数现象给定正转电机实际反转。原因相序接反或者编码器计数方向与相序不匹配。这两个要分开看只反一个会出现能转但转速反馈是负的两个都反反而是对的。解决优先改参数不改线。Workbench 里有相序方向和编码器方向的配置项改一下重新生成就行。硬件上倒线看着简单但下次换板子又得重新倒一遍参数化更省心。改完记得跑一次正反转验证别只测一个方向。5.4 重新生成代码手写的修改全没了现象在 CubeMX 或 Workbench 里改了个引脚重新生成之后发现自己加的调试打印、自定义的启动逻辑、改过的中断服务函数全部回到初始状态。原因CubeMX 只保护USER CODE BEGIN和USER CODE END之间的内容其余部分每次生成都会覆盖。很多人第一次用的时候不知道这个约定直接在生成区里写代码。解决所有手写逻辑放进 USER CODE 段或者干脆单独建文件通过mc_api.c暴露的接口调用。我现在的习惯是生成出来的文件只读不写自己的业务代码全部另起文件这样重新生成多少次都不心疼。5.5 编译通过下载后进 HardFault现象工程编译零警告烧进去一运行就挂在 HardFault调试器看到的调用栈完全对不上。原因多半是堆栈不够或者中断向量表偏移没配对。MCSDK 的库加上浮点运算栈开销比裸机点灯大不少另一个常见原因是 Bootloader 场景下忘了设VECT_TAB_OFFSET。解决先把栈从默认的 0x400 加到 0x1000 试试能过就说明是栈的问题还不行就查启动文件里的向量表偏移和链接脚本里的 Flash 起始地址是否一致。这类问题排查有个笨办法但很有效在 HardFault_Handler 里把压栈的 PC 值读出来反查反汇编能直接定位到出错的那条指令。6. 二次开发把自动生成的工程接进自己的产品框架生成出来的工程是个能转的 Demo要变成产品还得往里塞业务。我的做法是把 MCSDK 当成一个电机驱动外设用一个周期任务把它包起来上层业务只跟自己的接口打交道不直接调MC_*系列函数。/* motor_task.c —— 把 MCSDK 状态机接到自己的调度器里1ms 调一次 */ #include mc_api.h #define POLE_PAIRS 7 typedef enum { MOT_IDLE, MOT_RUN, MOT_FAULT } mot_state_t; static mot_state_t s_state MOT_IDLE; void MotorTask_1ms(void) { switch (s_state) { case MOT_IDLE: if (MotorCmd_IsStartRequested()) /* 上层业务下发的启动请求 */ { MC_StartMotor1(); s_state MOT_RUN; } break; case MOT_RUN: { /* 上层给的是机械 rpm这里换算成 SDK 要求的 0.1Hz 电频率 f(0.1Hz) rpm * 极对数 / 60 * 10 rpm * 极对数 / 6 */ int16_t rpm MotorCmd_GetTargetRpm(); int16_t f (int16_t)(rpm * POLE_PAIRS / 6); MC_ProgramSpeedRampMotor1(f, 500); if (MC_GetOccurredFaultsMotor1() ! MC_NO_FAULTS) s_state MOT_FAULT; break; } case MOT_FAULT: MotorCmd_ReportFault(MC_GetOccurredFaultsMotor1()); /* 上报给上层做保护 */ MC_AcknowledgeFaultMotor1(); s_state MOT_IDLE; break; } }换算那行是整段的核心。POLE_PAIRS必须和电机实际极对数一致写错了转速全错而且错得很隐蔽——电机照样转、电流照样正常只是实际转速和目标值差一个比例。我见过有项目因为极对数填成极数转速差了一倍现场调试调了半天才发现。验证方法我通常按三步走。第一步空载给定几档转速用示波器测 Hall 或者编码器信号算实际转速和给定值对比误差应该在 5% 以内第二步突加突卸负载看恢复时间速度环带宽不够的话会明显拖沓第三步长时间跑至少半小时观察母线电压采样和温度采样有没有漂移温漂导致的采样偏差是最容易被忽略的慢性问题。还有个小技巧值得说调试阶段把MC_GetSTMStateMotor1()的返回值通过串口打出来比在 IDE 里打断点好用得多因为打断点会暂停 PWM电机瞬间失控。状态机的几个状态值记住就行——IDLE、ALIGN、RUN、FAULT卡在 ALIGN 说明位置检测没过卡在 IDLE 说明启动条件不满足。从那以后我每次改完mc_config.c里的参数都会强制走一遍空载低速正转 → 反转 → 加载这三步哪怕只改了一个限幅值。电机控制这东西配置项之间是耦合的改 A 影响 B 的事情太常见跑一遍比看十遍代码都踏实。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
TeamAI-CLI:团队级AI能力中间层与TypeScript实践 1. 项目概述:不是又一个 CLI 工具,而是团队 AI 能力的“水电煤”TeamAI-CLI 这个名字乍看平平无奇——CLI 工具满天飞,带“Team”前缀的也不少。但当你把它和“腾讯开源”、“团队级 AI Agent 中间层”、“让每个人的 AI 能力变成团队共享能力… · 2026/11/3 17:27:39
Linux下IgH EtherCAT主站:从网卡绑定到CiA402伺服实时控制 简介:面向工业机器人与运动控制方向的开发者、学生及工程技术人员,这份资源围绕 Linux 平台下的 EtherCAT 开源主站 IgH 展开,结合汇川 SV630N 伺服的实际调试场景,讲解主站程序的运行流程与主从通信要点,并对六自由度… · 2026/11/3 17:27:39
帝国CMS 7.5中Word粘贴功能优化实战 1. 帝国CMS 7.5前端开发中的Word粘贴功能优化实践在企业官网内容管理系统的开发中,Word文档的粘贴功能优化是一个常见但极具挑战性的需求。作为一名长期从事CMS开发的工程师,我最近完成了一个帝国CMS 7.5的项目,其中重点解决了Word内容粘贴的… · 2026/11/3 17:27:39
uniapp海外游戏分发工程:PG电子集成与Switch卡带区域适配 简介:这是一套面向海外游戏业务开发者的全栈前端解决方案,基于UniApp框架实现跨平台兼容,专为构建海外游戏交易平台、手游下载站及Switch游戏卡带销售系统设计。资源包含完整可二次开发的前端源码(非编译后产物)&#… · 2026/11/4 0:00:17
Qoder NEXT:智能代码补全工具的技术解析与应用 1. 项目概述Qoder NEXT作为新一代智能编程辅助工具,其核心突破在于通过深度优化代码补全算法,将AI生成代码的开发者采纳率提升了65%。这个数字背后代表着编程效率的实质性飞跃——开发者不再需要花费大量时间在重复性代码编写和机械性调试上,… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
双极步进电机控制实战: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
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
双极步进电机控制实战: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