尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32火灾报警装置Proteus仿真设计与避坑指南
简介基于STM32的火灾报警装置Proteus仿真资源主要面向嵌入式初学者与电子设计人员可用于学习烟雾浓度、环境温度检测到声光报警联动的完整设计流程解决开发中实物调试依赖硬件的问题。资源包共276个文件压缩后8.46MB主要内容包括C语言源程序与头文件、Proteus仿真工程pdsprj、编译生成的hex文件和项目文档其中C代码涵盖ADC采样、阈值比较与报警逻辑仿真工程用于虚拟搭建电路hex文件可直接烧录到开发板验证。方案从传感器信号采集到报警输出逐层拆解支持在Proteus中实时修改参数观察现象免去重复焊板成本适合课程设计、毕业设计或项目入门。目前已有343人学习目录按功能模块组织定位代码与电路非常高效且带详细注释便于二次开发。1. 火灾报警装置仿真的真正价值在硬件到手前先做完逻辑验证做嵌入式开发最怕的不是代码写不出来而是硬件焊好了、板子通电了结果蜂鸣器不响、传感器读数飘或者逻辑一团乱——这时候你分不清是代码问题还是电路问题。STM32火灾报警装置Proteus仿真的核心价值就是把「硬件调试验证」这一步提前到「买元件、画PCB之前」用软件把整条信号链路跑通DS18B20测温、烟雾传感器模拟量采集、阈值判断、声光报警、排风扇联动。它适合三类人正在备赛电子设计竞赛想快速出原型的学生、准备做毕业设计的本科生以及工作中需要先给甲方看功能演示的工程师。别把仿真当玩具一个能在Proteus里稳定跑完的报警流程移植到真实板子上可能只需要改两三处引脚定义。2. 系统架构与关键器件选型为什么这套组合能跑通2.1 信号链设计从传感器到执行器的完整路径整个装置的核心信号流是单向的传感器采集物理量 → 经ADC或单总线协议转换为数字值 → STM32做阈值判断 → 输出控制信号驱动蜂鸣器、LED和直流电机模拟排风扇。这个架构决定了代码的组织方式——不需要实时操作系统一个while(1)主循环加定时器中断就足够。仿真的优势在于你不需要像真实硬件那样考虑传感器上电稳定时间、信号毛刺滤波但恰恰因为Proteus是理想模型它反而能把「程序逻辑」和「硬件特性」两个层面的问题分开。比如温度读取失败时真实硬件可能是线路接触不良而仿真里几乎一定是代码时序问题——这反而让你更容易定位是驱动层写错了还是业务逻辑错了。下表列出我在这套方案里采用的器件组合和选型理由器件型号/参数在系统中的角色选型理由主控STM32F103C8T6核心处理单元72MHz主频、20KB RAM、3路ADC、主流资料最多温度传感器DS18B20环境温度采集单总线协议、数字输出、无需额外ADC烟雾传感器MQ-2模块模拟输出烟雾浓度采集模拟电压输出、阈值可调、Proteus中可用电位器模拟显示LCD1602I2C或并行实时显示温度与浓度16x2字符足够展示关键信息声光报警蜂鸣器 红绿LED报警输出GPIO高低电平控制、逻辑简单执行机构5V直流电机模拟排烟/通风用ULN2003驱动顺便验证PWM调速2.2 STM32资源分配引脚规划与片上外设的使用边界STM32F103C8T6有48引脚功能上足够富裕但仿真时引脚分配直接影响Proteus原理图的可读性和接线效率。我的习惯是遵循「功能分区」原则PA0-PA3留给按键输入测试模式、阈值调整、消音/复位PA4-PA7SPI或普通GPIO接LCD如果用I2C版本则改用PB6/PB7PA9/PA10USART1仿真时用来输出调试信息这个后面会展开讲PB0-PB3接LED指示灯红色报警、绿色正常PB5蜂鸣器PWM输出复用TIM3通道2PB8/PB9DS18B20数据线如果走I2C LCD就换成PB6/PB7PA0MQ-2烟雾传感器模拟输出进ADC1通道0这样的分配在Proteus里画图时也比较顺手——传感器靠左侧显示在中间执行器在右侧信号流从左到右不交叉。注意一个容易忽略的点STM32F103的ADC输入阻抗不是无限大的在Proteus仿真里这个问题不明显但如果你用的是真实MQ-2模块内部有运放输出PA0引脚配置成模拟输入是必须的否则读数会乱跳。从仿真角度看模拟输入和浮空输入在Proteus里表现几乎一样但你按真实硬件标准去配置后面移植才不会翻车。时钟树方面外部晶振在Proteus里仿真容易出幺蛾子后面避坑章节细说所以我建议直接用内部HSI 8MHz倍频到64MHz——对这套应用完全够用而且省去外部晶振起振等待的时间。设置方法是在CubeMX的RCC配置里选择「Crystal/Ceramic Resonator」或「Bypass Clock」之前先想清楚如果用的是Proteus 8.10以上版本直接支持外部晶振仿真但启动速度会慢两秒内部时钟就没这个问题。3. 工程搭建与核心代码实现从CubeMX到Keil的完整流程3.1 CubeMX初始化配置引脚映射与定时器参数的设定先强调一个实操习惯无论你在Proteus仿真还是真实硬件上跑都必须用STM32CubeMX生成初始化代码不要手写寄存器。原因不是懒而是CubeMX生成的时钟树配置和外设初始化有官方保障初学阶段手写RCC配置代码简直是给自己挖坑。在CubeMX里按2.2的引脚分配逐一配置后有几个参数需要特别注意ADC1选择PA0为IN0采样时间设55.5周期不要选最快因为MQ-2模块的输出阻抗较高长采样时间能减少误差TIM3Channel2输出PWM控制蜂鸣器频率设4kHz蜂鸣器谐振频率附近响度大占空比默认50%Channel3用于电机PWM频率20kHz避开人耳可听范围USART1115200-8-N-1仿真调试输出用PB8配置为开漏输出模式因为DS18B20的总线是开漏协议需要外部上拉Proteus里放一个4.7k电阻这个必须放生成代码后main.c里要做的事是先初始化传感器再进入主循环。此时CubeMX生成的while(1)循环体是空的所有业务逻辑要在指定用户代码区里写。3.2 传感器驱动与数据采集DS18B20时序和注意点DS18B20是单总线器件所有读写操作都靠时序。Proteus仿真对时序的容忍度比真实硬件高不少但代码写得不规范很容易得到55℃这种离谱读数这是DS18B20上电复位的默认值。我的驱动代码框架如下uint8_t DS18B20_Reset(void) { uint8_t presence 0; GPIOB-BSRR GPIO_BSRR_BR8; // 拉低总线 delay_us(480); // 480us复位脉冲 GPIOB-BSRR GPIO_BSRR_BS8; // 释放总线 delay_us(60); presence (GPIOB-IDR GPIO_IDR_IDR8) ? 1 : 0; // 读存在脉冲 delay_us(420); return presence; // 返回0表示有设备应答 }这段代码的逻辑是主机拉低总线480us以上制造复位脉冲然后释放总线等待从设备应答。DS18B20收到复位脉冲后60us内会把总线拉低产生一个60~240us的存在脉冲。presence读取在释放总线后60us进行——这个时间窗口很关键读早了读到的是高电平误判为无设备读晚了也可能错过低电平窗口。参数方面delay_us(480)和delay_us(420)是DS18B20数据手册里给出的最小/最大时序要求不能随意改短。Proteus仿真的虚拟示波器可以看到这些时序的波形如果看到复位脉冲只有200us那传感器大概率不响应。温度读取的完整流程通常是复位 → 写ROM命令0xCC跳过ROM匹配 → 写功能命令0x44启动温度转换 → 等待750ms → 复位 → 写0xCC → 写0xBE读暂存器 → 连续读9字节。其中750ms转换等待用HAL_Delay(750)即可但在实际项目里我会用定时器做非阻塞等待——仿真时可以先用阻塞式跑通逻辑再说。3.3 报警判断与联动控制逻辑阈值怎么定才不误报报警逻辑是整个装置的核心。常见做法是单阈值判断即温度超过50℃或烟雾浓度超过某阈值就报警但这样误报率很高——比如有人在旁边抽烟都会触发。更合理的策略是「双条件与带触发延时」。我在这套仿真里实现的逻辑是这样的温度阈值设为55℃烟雾浓度阈值设为ADC采样值的60%大约1.5V对应满量程3.3V的45%左右两个条件都满足时才触发声光报警。另外加了一个软件滤波连续5次采集每次间隔200ms都超过阈值才确认报警避免瞬时尖峰误触发。typedef struct { uint8_t temp_exceed; // 温度超限标志 uint8_t smoke_exceed; // 烟雾超限标志 uint8_t confirm_count; // 连续确认计数 } AlarmState; void Alarm_Task(AlarmState *state, float temp, uint16_t adc_val) { state-temp_exceed (temp 55.0f) ? 1 : 0; state-smoke_exceed (adc_val 2048) ? 1 : 0; // 12位ADC2048≈1.65V if (state-temp_exceed state-smoke_exceed) { state-confirm_count; if (state-confirm_count 5) { HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_SET); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2); // 蜂鸣器响 HAL_GPIO_WritePin(FAN_GPIO_Port, FAN_Pin, GPIO_PIN_SET); // 排风扇转 } else { state-confirm_count 0; // 任一条件不满足就清零 HAL_TIM_PWM_Stop(htim3, TIM_CHANNEL_2); HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_SET); } } }这里有个容易忽视的设计细节confirm_count在没有同时满足条件时必须清零而不是只在满足条件时加一。否则可能出现累积计数——早上飘过一次烟雾、下午温度升高后直接触发报警这就是典型的逻辑漏洞。从仿真调试的角度你可以在这个函数里加一条串口打印每次判断后输出当前状态方便肉眼验证。adc_val 2048这个阈值对应的是12位ADC满量程4096的一半也就是1.65V。Proteus里通常用电位器分压模拟MQ-2输出你旋转电位器观察红绿灯变化时阈值点对应的角度就是2048这个值。你可以用这个方法来标定自己的阈值参数。4. Proteus仿真环境配置从原理图到跑通的关键步骤4.1 原理图搭建要点与元件库选择Proteus里搭STM32仿真原理图第一步是确认版本——低于8.9的版本不支持STM32F103系列仿真这是很多人下载了个老版本然后折腾半天跑不起来的直接原因。我用的是8.10以上版本Proteus 8.13 SP0这类之所以强调这一点是因为旧版要么元件库里找不到STM32F103C8T6要么找得到但仿真模型有Bug。元件清单如下STM32F103C8T6Library里搜STM32F103C8注意不要选成STM32F103R6这类引脚数不同的型号DS18B20DS18B20可直接搜到MQ-2模块Proteus里没有现成MQ-2模型常见做法是「电位器电池电阻网络」搭一个等效电路。具体来说用一个10K电位器分压模拟传感器输出电压范围0.5V~3.3V再串联一个1K电阻限流后送ADC引脚LCD1602选LM016L这是Proteus里最常见的1602模型I2C版本选PCF8574LCD组合蜂鸣器BUZZER或SOUNDER注意选有源蜂鸣器无源的要用PWM驱动两者在Proteus里都能用但接线不同5V直流电机MOTOR-DCULN2003驱动电机搜ULN20034.7K上拉电阻DS18B20数据线、10K下拉电阻复位引脚搭原理图时有一个布局技巧把整个系统按「电源→传感器→STM32→执行器」四列分布放置导线尽量短而直。Proteus的自动布线有时候会绕远手动布还是主要的。对于电源网络用VCC标签符号代替实际连线可以大幅减少导线交叉。4.2 烧录EL文件与仿真参数运行Proteus与Keil的协作仿真运行的核心是让Proteus加载Keil编译出的目标文件。坑点在于Proteus的STM32模型只支持加载两种格式——AXFARM的ELF格式或HEX。这不是普通51单片机的HEX格式而是需要Keil特殊配置后再生成的Intel HEX。操作步骤是这样的Keil里点魔术棒Options for Target→ Debug → 选择右侧的「Use Simulator」软件仿真调试器而不是ST-Link那类硬件调试器在Settings里勾选「Run to main()」——这决定加载程序后是否自动跳到main入口编译工程不要点Download只点Build回到Proteus双击STM32F103C8T6元件在Program File一栏里选择刚才编译生成的.hex文件Clock Frequency设成8MHz与CubeMX里内部HSI设置一致你若用外部晶振则设成8MHz但必须在CubeMX里也同步配置点Play键开始仿真如果你的程序里用了HAL_Delay()这种依赖SysTick的函数Proteus仿真时系统会调用ARM CMSIS模型来模拟Systick中断这个过程比真实硬件慢得多——这是正常现象不是因为代码死循环。需要特别提醒的是Keil优化等级对仿真影响很大如果你开了-O3优化部分局部变量会被优化掉Proteus仿真时你无法观察这些变量的值建议个人调试时用-O0逻辑验证完再开优化。4.3 仿真运行中的观测手段虚拟示波器、串口与内存查看Proteus仿真不像真实硬件可以用逻辑分析仪到处点但有三个内置观测手段足够排查绝大多数问题虚拟示波器在左侧工具栏点图表按钮添加DIGITAL图表或ANALOGUE图表。把DS18B20数据线引脚PB8拖进图表运行仿真后就能看到单总线的波形。这里有一个常规检查项DS18B20的写时序中每个时隙应该在60~120us之间如果大于120us可能超过传感器容限虽然Proteus模型很宽容但真实硬件肯定不行。如果你看到500us的低电平那多半是delay_us函数写错了——HAL库的HAL_Delay是毫秒级函数微秒延时要用DWT-CYCCNT或定时器实现。虚拟串口终端Proteus里放置一个VIRTUAL TERMINAL把RXD接PA9USART1_TX运行后就能看到串口打印的调试信息。这个特别适合验证报警逻辑变迁——在代码的关键路径打印状态切换信息比盯着LCD屏幕猜强得多。存储器观察窗Proteus运行时点「Debug」→「Memory Contents」能看到STM32内部Flash和RAM的内容。当你的程序意外跑飞时观察PC指针停在0xFFFFFFFF或某个异常地址就可以判断是堆栈溢出了还是中断向量表配置错误。记住一个核心原则仿真的观测手段比真实硬件更抽象像示波器这种工具其实是辅助你验证代码逻辑不该花大量时间适配Proteus的波形显示参数。重点是确认代码里的「状态」按照你设想的在切换波形只是佐证。5. 避坑记录Proteus STM32仿真最常见的六个坑5.1 仿真开始后程序不运行卡在启动文件里现象点击Play键后STM32元件没有反应点「Debug」→「Start/Restart Debugging」发现程序指针卡在SystemInit或Reset_Handler里一动不动。原因90%的情况是Proteus的STM32模型无法正确初始化F103系列的外部晶振或者你的CubeMX里选了外部晶振但Proteus原理图上没有接8MHz晶振电路。另一个低频原因是Keil编译时选了Use MicroLIB但CubeMX生成的启动文件不兼容——这属于较罕见的版本冲突。解决在CubeMX里改RCC配置为HSI内部高速时钟生成代码后重新编译加载。如果你坚持用外部晶振必须在Proteus原理图上给STM32加OSC_IN/OSC_OUT引脚接8MHz无源晶振两个20pF负载电容仿真才会启动。内部时钟豁免一切推荐优先使用。5.2 DS18B20读到的温度恒定是25℃或85℃现象LCD显示温度永远25℃或85℃不管环境怎么变都不动。Proteus模型里DS18B20的温度通常由「Temperature Sensor」器件提供模拟输入但更常见的是根本没接。原因25℃是DS18B20上电复位后的默认寄存器值85℃是芯片内部的ROM测试值。两者都说明你根本没有成功执行「温度转换」命令或者读到的数据全是FF。追根究底大概率是DS18B20_Reset()里时序不对比如480us延时变成480ms导致复位脉冲时长不合法。解决先在Proteus里加虚拟示波器看单总线波形。如果复位脉冲宽度在480us左右、存在脉冲60~240us就检查代码里写0xCC跳过ROM的时序如果波形完全不对用系统滴答定时器实现微秒级延时函数delay_us()。5.3 ADC读数固定跳变两个值烟雾浓度显示像在抽风现象仿真的烟雾浓度读数要么是4095要么是0中间值一闪而过LCD上数字上下跳变没有稳定值。原因这种情况通常是你用了电位器模拟MQ-2输出但电位器中心抽头引脚没接对或者ADC初始化时通道号错了。Proteus的电位器有明确的三个引脚开路的那个引脚悬空状态下在仿真环境会表现为随机电平。解决检查电位器抽头到PA0的连线是否接触良好确认ADC1初始化里Channel设为ADC_CHANNEL_0而不是ADC_CHANNEL_1。另外在CubeMX里把采样周期设长一点55.5周期以上避免因为采样电容未充满导致读数不稳。5.4 蜂鸣器不响但LED指示正常现象报警逻辑触发了红灯明显亮起但蜂鸣器无声。原因多半是你用的蜂鸣器元件是无源蜂鸣器需要PWM波形驱动才发声但TIM3定时器没有正确启动PWM输出或者通道配错了。另一个可能是蜂鸣器正负极接反——这在真实硬件上不至于一点声音都没有但仿真里反接可能直接不工作。解决确认原理图中蜂鸣器正极接PB5TIM3_CH2负极接地检查初始化里用的通道是TIM_CHANNEL_2手动在main()里强制把PB5拉高GPIO_PIN_SET测试蜂鸣器是否响——如果不响则换一个有源蜂鸣器元件SOUNDER类型用高低电平就能驱动。5.5 仿真运行速度极慢刷新一次要好几秒现象整个仿真跑起来后LCD上数字刷新按秒算虚拟示波器波形出来一次要转半天。原因Proteus的ARM仿真模型本身就比51单片机慢一个量级加上LCD1602这种字符型显示器的刷新机制在图形模式渲染下开销大两者叠加起来就特别卡。解决三个技巧配合一是把仿真速度调到最大在Proteus底部状态栏的CPU使用率旁边把模拟速度设为「Maximum」二是在程序里降低LCD刷新频率比如从200ms刷新一次改成500ms三是分割仿真——先把温度显示模块注释掉单独跑跑通后再加显示逐模块排除了就不会同时卡在多个元件上。5.6 编译生成的HEX文件在Proteus里加载后报错现象在Keil里编译成功也选了Output标签页的Create HEX File但Proteus加载后弹窗提示「The selected file is not a valid HEX or AXF file」。原因Keil的Output选项里虽然勾了HEX但如果你的AC6编译器版本较新生成的HEX文件头可能包含扩展地址记录HEX386Proteus的旧版加载器不支持。解决改输出AXF格式在Keil → Output标签 → Select Folder for Objects里生成.axf文件加载到Proteus或者干脆用旧版AC5编译器。作为替代方案用objcopy命令把AXF转成纯Intel HEXarm-none-eabi-objcopy -O ihex firmware.axf firmware.hex这样Proteus就能顺利加载。6. 进阶验证用「逻辑分层验证法」从仿真平滑过渡到真实硬件仿真代替不了真实硬件但仿真能帮你把「代码逻辑错误」和「硬件连接错误」彻底隔离开。我从这套项目里总结出一个行之有效的过渡方法——逻辑分层验证法分四步第一步在仿真里跑通全部功能然后把Proteus工程里的传感器和执行器全部替换为简单的虚拟仪表用LED指示报警状态即可只保留STM32和电源电路。第二步把这段精简代码烧写到真实板子上如果LED指示正确说明你的GPIO配置、时钟树、报警逻辑在硬件层面没有错。第三步在真实板上逐次接回原先的外设——先接LCD显示是否正常再接蜂鸣器听声音最后接DS18B20和MQ-2传感器。第四步每接一个外设就对照仿真中该外设的表现做比较哪里表现不一致哪里就是你真正需要排查的硬件问题。这个方法的精妙之处在于仿真和真实硬件的差异集中在「外设的电气特性」上而恰好STM32内部的代码逻辑经过前三步验证后是可以直接复用的。我做过一次记录——如果代码在仿真里稳定跑通且按键消抖、阈值判断这些逻辑都正常移植到真实板子上改动量基本控制在CubeMX的引脚重映射和延时函数适配两个层面。对了还有一个重要的收获是这套仿真项目里把阈值逻辑做成了「可配置参数」在代码开头用一个结构体集中管理温度阈值、烟雾阈值、确认计数次数三个参数。在仿真阶段你可以用虚拟电位器快速旋转来模拟不同浓度然后观察这些参数是怎么影响报警响应的。真实硬件阶段你只需要把这段配置代码替换成Flash读写保存用户参数的操作即可。从那以后我再也没有一上来就焊板子——每次拿到新的传感器模块或MCU型号都强制自己先在Proteus里把整个逻辑链路跑一遍这样反复调试的成本几乎为零而且连面包板都不用插。这套个习惯让我省下的时间远远超过学Proteus本身花掉的时间。希望这份从仿真到真实硬件的避坑笔记能帮你在做STM32火灾报警装置时少走几段弯路。本文还有配套的精品资源点击获取
RELATED

相关推荐

通过top、vmstat、free、df、iostat组合定位Linux性能瓶颈

通过top、vmstat、free、df、iostat组合定位Linux性能瓶颈

1. 资源查看不是“跑几个命令”那么简单Linux系统资源查看,top、vmstat、free、df、iostat这五个命令几乎是每本入门书里的标配。我见过很多人把每个命令背得滚瓜烂熟,但一遇到服务器告警,还是不知道从哪下手。原因是他们把这些命令当成了“查…

📅 2026/10/20 18:55:02
基于MATLAB的分布式发电配电网网损与电压支撑仿真分析

基于MATLAB的分布式发电配电网网损与电压支撑仿真分析

搞配电网的人对“分布式发电”这个词肯定不陌生,但真正在MATLAB里把它的影响算清楚、讲明白,却不是看一眼潮流结果就能交差的事。我在做这个关于分布式发电对配电网影响的综合研究时,最深的感受是:网损和电压支撑这两个指标&#…

📅 2026/10/20 18:50:01
别再用new ObjectMapper()!Jackson实例复用与性能优化之道

别再用new ObjectMapper()!Jackson实例复用与性能优化之道

第 N 次看到new ObjectMapper()出现在业务代码里,我实在忍不住了。今天想把这件事一次说透:ObjectMapper 不能当成普通工具对象随手 new,它是个典型的重量级组件,每次 new 都在烧 CPU、堆内存和 GC 停顿。文章的后半部分会给出线程…

📅 2026/10/20 18:50:01
MORE NEWS

更多资讯

📰

智能化小区网络规划全解析:从拓扑选型到落地避坑指南

简介:面向智能化小区网络规划毕业设计选题的论文资源,适合网络工程、智能建筑方向的学生及需要完成课程设计者参考。内容以小区智能化系统为背景,依次覆盖宽带网络分类、OSI七层参考模型,并逐一分析中继器、网桥、路由器、网关、集…

📰

上下文模式实战:多端多状态系统的动态行为调度策略

1. 从“上下文模式”说起:一个被低估的工程概念第一次看到“context-mode”这个词,很多人会下意识觉得它是个抽象到没法落地的概念。但如果你写过稍微复杂一点的系统,或者维护过一个跑了半年以上的项目,就会明白:上下文…

📰

微表情识别中的自适应关键帧提取方法

简介:本资源是一套面向人工智能与计算机视觉方向研究者、高校学生及微表情分析初学者的优质项目实践方案,聚焦视频中持续时间短、强度弱的微表情自动识别难题。项目提出基于自适应关键帧筛选的检测方法,通过光流变化度量与动态阈值机制精准定…

📰

产品EMC设计与风险评估:从原理图到量产的全流程实战

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

📰

基于深度学习与PyQt的湿滑路面自适应制动决策系统设计与实现

1. 这个毕设为什么选"湿滑路面自适应制动":项目价值与技术边界第一次看到这个题目,很多同学的第一反应是"这不就是做个ABS仿真吗"——我当初也是这么想的,但真正动手之后才发现,这个项目的核心价值并不在制动…

📰

WSL下STM32固件烧录实战:从编译到烧录的完整指南

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬