说实话用Proteus仿真DHT11温湿度传感器是我这几年在嵌入式教学和项目预研里遇到过最容易被“细节”打败的活儿之一。明明就是两根线、一个单总线协议仿真图看着也简单可一旦到了Keil里写完代码下载进Proteus要不就是LCD1602上显示湿度88%、温度30℃要不就是怎么读都是0xFF甚至干脆界面卡死。这篇文章把我实际踩过的、帮学生排查过的、以及群里反复被问到的5个大坑全部整理出来每个坑都给出解决思路和可直接用的代码。无论你是刚学51单片机的新手还是正在用Proteus做课程设计的老手照着这篇文章排查一遍基本能把DHT11的仿真问题清干净。1. 仿真前必须弄懂的事DHT11到底是怎么和单片机说话的1.1 单总线协议和40位数据结构DHT11不是什么高速传感器它用的是单总线1-Wire通信一根数据线既做输入又做输出数据和时钟混在一起传输。主机单片机通过拉低这根线、再释放给传感器一个启动信号传感器随后返回一串80微秒左右的低电平响应接着连续吐40位数据。这40位里包含8位湿度整数部分、8位湿度小数部分、8位温度整数部分、8位温度小数部分以及8位校验和。很多人在仿真里出错不是因为看不懂协议而是因为没意识到单总线的时序窗口非常窄。DHT11判断一位是0还是1靠的是高电平持续的时间短高电平26到28微秒代表0长高电平70微秒左右代表1。问题来了Proteus里的DHT11模型尤其是从官网或第三方库导入的版本会对这些“时间窗”做严格校验。一旦你的延时程序不够精确时序飘了模型就根本不给数据返回的全是0或255。1.2 Proteus里DHT11模型的真实行为这里要特别说清楚一个容易误会的点Proteus自带的DHT11模型在早期版本里甚至根本不存在很多人的做法是从网上单独下载.DLL或.LIB文件放进Proteus的LIBRARY目录。不同来源的模型行为差异很大。有些模型对时序很宽容你用普通的软件延时也能跑有些模型则严格按照数据手册的时序窗口来判定稍微差一点就满脸写着“我不配合”。所以我建议在仿真层面先不要把“读不到数据”完全归咎于代码元件模型的差异本身就是最大的变量之一。我的做法是先用一个已知能跑的例程后面会贴在Proteus里确认模型正常再改自己的代码逻辑。这样能把“模型问题”和“代码问题”隔离开排查速度会快很多。2. 坑一元件库找不到DHT11或者拖出来却不工作2.1 排查是库缺失还是模型版本太老这是新手最常见的第一个拦路虎。你在Proteus的元件搜索框里输入DHT11结果什么都没有或者搜出来一个叫“DHT11”但元件符号很奇怪的模块放上去仿真完全没有反应。先说结论Proteus 8.6以前的版本默认库里基本都没有DHT11。如果你想省事直接用Proteus 8.9以上的版本自带模型要完善很多。如果你只能用老版本那就必须手动添加第三方库。这里要注意网上流传的DHT11库文件五花八门有一些其实是给“Proteus 7”用的放到Proteus 8下面会直接报错或者不显示。我自己试过最稳妥的做法是去Proteus官网的元件库页面搜索“DHT11”下载对应你版本的库文件。下载后把文件解压里面通常有LIBRARY和MODELS两个文件夹分别把里面的内容复制到你的Proteus安装目录下的同名文件夹。然后重启Proteus搜索DHT11应该能看到一个三脚或四脚的传感器模型。2.2 拖进去没有数据的三个快查点如果你已经能看到DHT11元件但仿真时怎么都不出数据先别急着改代码检查三件事第一DHT11的电源脚有没有接5V正极地脚有没有接GND。很多自带模型的DHT11是四脚封装VCC、DATA、GND、NCNC脚悬空就行千万别接错位置。第二数据线上有没有加上拉电阻。虽然不少Proteus模型内部已经集成了上拉但如果你用的是纯模型版本数据线默认是弱上拉通信极不稳定。我在仿真图上习惯在DATA引脚和VCC之间加一个4.7kΩ或10kΩ电阻这个动作成本很低却能省掉很多莫名其妙的问题。第三单片机的晶振频率是不是12MHz。DHT11的时序计算很多示例代码是围绕12MHz晶振来写的如果你改用了11.0592MHz软件延时的时间参数全要跟着调整。在仿真阶段我建议老老实实用12MHz先把数据跑通再说。3. 坑二读出来的温湿度永远是0或者停在一个固定值3.1 为什么Proteus对延时这么敏感这个坑我见得太多了。代码逻辑看着完全没问题拉低18毫秒释放等响应然后循环读40位。结果LCD上湿度永远显示0%温度永远显示0℃数据纹丝不动。原因基本都出在延时函数上。DHT11的启动时序要求主机拉低总线的时间必须大于18毫秒很多人的延时函数是用嵌套for循环写的在12MHz晶振下这个“毫秒延时”可能实际只有几毫秒甚至错到几十毫秒。仿真模型对启动信号长度有严格判断如果拉低时间不够传感器根本不会响应。我在自己项目里验证过单纯靠for循环嵌套做延时在Keil C51编译优化等级不同的情况下实际延时时间能差出2到3倍。同样的代码O0优化和O2优化出来的时序完全不同这是很隐蔽的一个变量。3.2 精确延时的两种主流写法要解决延时不准第一步是关掉编译器优化或者把延时写成稳定可预期的形式。这里给出两种我实测稳定的写法。第一种利用_nop_()函数。在Keil C51里包含intrins.h头文件_nop_()对应一条NOP指令在12MHz晶振下正好1微秒。写一个精确的微秒级延时函数可以通过NOP的个数来凑时间。但这种写法只适合微秒级修正不推荐用来做毫秒级延时。第二种用定时器做基准延时。这种更踏实下面这个函数是我比较常用的void Delay_ms(unsigned int ms) { unsigned int i; for (i 0; i ms; i) { TMOD 0xF0; TMOD | 0x01; TH0 0xFC; TL0 0x18; // 12MHz下定时器定时1ms TR0 1; while (!TF0); TR0 0; TF0 0; } }这个函数用定时器0的16位模式每次定时1毫秒。12MHz晶振对应机器周期1微秒初值设成65536-100064536也就是0xFC18这样每次溢出正好1毫秒。用它来拉低总线18毫秒就是调用Delay_ms(18)非常稳定不会因为编译器优化而漂移。3.3 上拉电阻和接线的隐藏坑如果你数据线确实接了上拉电阻但输出一直是0还有一个容易被忽略的点DHT11和LCD1602的数据线有没有“打架”。很多人在Proteus里图省事把DHT11的数据脚接到了P0口。P0口是开漏输出内部没有上拉如果图上一时没加上拉整个P0口都会被拉死别说DHT11LCD1602也会跟着抽风。DHT11建议接P2口或P3口这些端口内部有上拉电阻仿真时更省心。我个人的习惯是DHT11接P2.0这样既能避开P0口的上拉问题又方便布线。4. 坑三时序明明对着数据手册写的数据还是乱飘4.1 仿真速度对DHT11时序的影响有一种情况特别容易让人心态爆炸代码和时序看起来完全按照数据手册写的第一次运行是正常的温湿度但第二次运行或者点击暂停再继续数据就乱了一会儿显示正常一会儿显示0xFF。这个坑和Proteus的仿真速度有关。Proteus仿真时动画更新速度是可以调的默认情况下如果你的单片机程序里同时跑着LCD1602刷新、DHT11读取、延时函数大量的时间消耗会导致整个仿真实时性变差。DHT11对时序的容忍度本来就低一旦仿真速度被界面渲染拖慢数据线上的波形就会变形读回来的自然就是错的。我的经验是做DHT11仿真时把Proteus右下角的仿真帧率调低一点或者尽量少放一些高频闪烁的显示元件比如有时钟源或跑马灯用得太多了就会拖慢。另外不要打开太多的调试窗口尤其是虚拟终端和波形监视器这些工具对仿真速度的影响比你想象中大得多。4.2 读位时用“先判断低电平再判断高电平”的技巧再来说一个很多人忽略的细节。DHT11传输每一位时先是50微秒低电平再是高电平。高电平维持的时间决定这一位是0还是1。很多人的代码是这样写的while(!DHT11_PIN); // 等待高电平 delay_us(40); // 延时40us后再判断电平 if(DHT11_PIN) bit 1;这个思路本身没有错但问题在于延时40微秒之后如果高电平已经结束读到的是低电平这一位就被误判成0。如果时序偏快或偏慢这种写法很容易出错。我更推荐这样写先等待低电平结束再用一个短延时落在高电平中间然后直接读取引脚状态。// 等待50us低电平结束 while(!DHT11_PIN); // 高电平刚开始延时30us Delay_us(30); // 此时如果还是高电平说明是1否则是0 if(DHT11_PIN) { bit_value 1; // 等待剩余高电平结束保证下一位的低电平能被检测到 while(DHT11_PIN); } else { bit_value 0; }这样能让采样点落在一个相对安全的位置即使高电平宽度有一些波动也不容易误判。这个技巧算是我在反复试错后觉得最有效的一个调整。4.3 用虚拟示波器看波形别靠猜建议大家在Proteus里接一个虚拟示波器Virtual Oscilloscope直接观察DHT11数据线上的波形。这不是必须的步骤但在排查时序问题时非常高效。你只要把探头接到DHT11的DATA引脚上就能看到启动信号是否满足18毫秒低电平、响应信号是否存在、40位数据波形的高电平宽度是否符合0和1的特征。我见过很多人靠猜测反复修改延时参数改了一下午都没找到问题接上示波器其实一眼就能定位。5. 坑四LCD1602一直不显示或者显示乱码5.1 LCD不显示的三个快查点DHT11读到了数据结果LCD1602上什么也没有或者显示了几个乱码这个坑同样高频。首先明确一个事情DHT11仿真问题很多时候是被LCD1602的显示问题放大的。传感器明明返回了正常数据但LCD不配合你误以为是DHT11坏了。第一个快查点LCD1602的V0脚对比度。Proteus里如果不接电位器V0脚直接接地部分模型会显示得非常淡甚至看不见字。我一般直接接一个10k电位器把中间抽头接到V0脚调节对比度这是最稳妥的。第二个快查点RS、RW、EN这三根控制脚的连接。RS和EN接错是常态一旦接反LCD完全没反应。RW脚如果不需要读操作可以直接接地省一个IO。第三个快查点LCD1602的初始化延时。上电后要等一会儿通常是15毫秒以上再发功能设置指令。如果初始化太快LCD内部状态机还没准备好后面的指令全部无效。5.2 数据拼接时的类型坑LCD能正常显示了但显示出来的湿度是“-1”或者温度显示个“85”这通常是数据类型拼接的问题。DHT11返回的湿度整数、湿度小数、温度整数、温度小数每个都是unsigned char类型。如果你把它们直接用整型运算或printf格式化很容易出负数或者乱码。我的建议是先把四个字节读出来存好再统一拼接unsigned char humi_int buf[0]; unsigned char humi_dec buf[1]; unsigned char temp_int buf[2]; unsigned char temp_dec buf[3]; unsigned char check buf[4];然后单独做校验把前四个字节加起来看和第五个字节是否相等。校验通过再去拼接显示。如果不做校验数据在传输中错了一位你看到的可能就是温度35℃这种离谱的值还不好查。另外LCD1602显示的时候要手动把数字转换成字符。比如显示“T:25C”不能直接把整数25赋给LCD的数据口得先算出十位和个位分别加上0x30再送显示。6. 坑五仿真一切正常烧到实物板子上却不工作6.1 Proteus模型理想化和实物时序差异这个坑是很多做完仿真想做成实物的同学最容易栽的。Proteus里一切正常温湿度显示准确LCD1602清晰一拿到面包板或者打好的PCB上完蛋什么都读不出来。核心原因在于Proteus里的DHT11模型在时序校验上通常比实物要宽松它对噪声、毛刺、电平的斜率这些真实世界的不确定因素完全免疫。实物的DHT11对时序要求更苛刻上电后还需要一段稳定时间通常1秒以上如果上电后立刻去读传感器根本还没准备好。解决办法也很直接在代码里每次读DHT11之前先加一个1秒以上的延时让传感器稳定读完之后两次读取间隔也不要小于1秒否则DHT11会认为你还在给它发启动信号直接罢工。这个习惯在仿真里看不出来但在实物上是决定成败的。6.2 硬件侧容易被忽略的三个问题如果你的实物失败但仿真正常优先检查硬件这三个点第一供电电压。DHT11标称工作电压是3.3V到5.5V但很多模块在5V下最稳定。如果你的单片机是3.3V系统DHT11的数据引脚电平就很可能不满足高电平判断阈值读回全0。这时候需要给数据线上加电平转换或者换5V系统。第二上拉电阻到底有没有接到VCC。很多人面包板上接了一个10k电阻但电阻的另一端悬空等于没接。数据线上靠内部弱上拉抗干扰能力不够线一长就完蛋。第三杜邦线太长。DHT11的数据线上有毛刺的话高电平宽度会变短代码里如果用的是40微秒延时判断法很容易把1误判成0。我实测下来数据线超过20厘米就要非常小心尽量用短的杜邦线。6.3 固件里必须有超时保护最后一个建议也是很多人代码里没有的读DHT11时一定要加超时保护。DHT11是单总线设备它不回数据时数据线会一直保持高电平。如果你的代码里写了while(!DHT11_PIN);而DHT11又恰好没响应这个循环会直接卡死整个程序冻住。我的做法是每次等待电平变化时都加一个计数上限unsigned char wait_timeout 0; while(!DHT11_PIN) { wait_timeout; if(wait_timeout 200) return 0; // 超时退出 Delay_us(1); }这样即使DHT11没接好或者损坏程序也不会死等最多就是读到个错误值系统还能继续跑。这对实物调试尤其重要可以救回很多看起来像是“烧坏了”的单片机。7. 完整例程51单片机DHT11LCD1602可复现工程7.1 工程文件组织方式下面这套代码我验证过在Proteus 8.9以上版本、Keil C51、12MHz晶振、STC89C52或AT89C51环境里可以实现DHT11温湿度数据读取并在LCD1602上显示。工程里我习惯建三个源文件加两个头文件项目根目录下建立一个文件夹命名为DHT11_Project里面包含main.c主函数负责延时、调用DHT11读取、控制LCD显示lcd1602.cLCD1602驱动源码lcd1602.hLCD1602引脚定义和函数声明dht11.cDHT11读取源码dht11.hDHT11引脚定义和函数声明这样做的好处是以后换项目直接复制LCD和DHT11的驱动文件不用重写。7.2 DHT11驱动源码dht11.h和dht11.c头文件部分定义引脚和函数声明#ifndef DHT11_H #define DHT11_H #include reg52.h sbit DHT11_PIN P2^0; void DHT11_Start(void); unsigned char DHT11_Read_Byte(void); unsigned char DHT11_Read_Data(unsigned char *humi_int, unsigned char *humi_dec, unsigned char *temp_int, unsigned char *temp_dec); void Delay_us(unsigned int us); void Delay_ms(unsigned int ms); #endif源文件部分具体实现#include dht11.h // 微秒级延时12MHz晶振下一个NOP约1us void Delay_us(unsigned int us) { while(us--) { _nop_(); _nop_(); _nop_(); } } // 毫秒级延时用定时器0实现比较精确 void Delay_ms(unsigned int ms) { unsigned int i; for (i 0; i ms; i) { TMOD 0xF0; TMOD | 0x01; TH0 0xFC; TL0 0x18; TR0 1; while(!TF0); TR0 0; TF0 0; } } // 主机发送启动信号拉低至少18ms再释放 void DHT11_Start(void) { DHT11_PIN 1; Delay_us(2); DHT11_PIN 0; Delay_ms(20); DHT11_PIN 1; Delay_us(30); } // 读取一个字节从低位开始 unsigned char DHT11_Read_Byte(void) { unsigned char i, value 0; for (i 0; i 8; i) { unsigned char timeout 0; // 等待50us低电平结束 while(!DHT11_PIN) { timeout; if(timeout 250) return 0; Delay_us(1); } // 低电平结束高电平刚到来延时30us后采样 Delay_us(30); if(DHT11_PIN) { value | (0x80 i); // 高位先出还是低位先出需要结合模型决定 timeout 0; while(DHT11_PIN) { timeout; if(timeout 250) break; Delay_us(1); } } } return value; } // 读取完整温湿度数据返回1表示成功0表示失败 unsigned char DHT11_Read_Data(unsigned char *humi_int, unsigned char *humi_dec, unsigned char *temp_int, unsigned char *temp_dec) { unsigned char buf[5]; unsigned char i; unsigned char timeout 0; DHT11_Start(); // 等待从机响应低电平 timeout 0; while(DHT11_PIN) { timeout; if(timeout 250) return 0; Delay_us(1); } // 等待响应低电平结束 timeout 0; while(!DHT11_PIN) { timeout; if(timeout 250) return 0; Delay_us(1); } // 连续读取5个字节 for (i 0; i 5; i) { buf[i] DHT11_Read_Byte(); } // 校验 if ((buf[0] buf[1] buf[2] buf[3]) ! buf[4]) { return 0; } *humi_int buf[0]; *humi_dec buf[1]; *temp_int buf[2]; *temp_dec buf[3]; return 1; }注意这里Delay_us里的_nop_()需要包含intrins.h头文件你可以在源文件顶部加#include intrins.h。这个延时函数我做了三次NOP循环一次大概是4到5微秒用来等待30微秒就调用6到7次循环实际效果够用。如果你希望更精确可以用定时器来做微秒延时但对DHT11来说5微秒的误差是能接受的。7.3 LCD1602驱动和主函数main.cLCD1602驱动我在这里只给出精简版能完成光标初始化、清屏、显示字符串和数字即可。你可以直接复制到lcd1602.c中#include lcd1602.h #include string.h sbit RS P1^0; sbit RW P1^1; sbit EN P1^2; void LCD_Delay(unsigned int t) { while(t--); } void LCD_WriteCmd(unsigned char cmd) { RS 0; RW 0; P0 cmd; LCD_Delay(100); EN 1; LCD_Delay(100); EN 0; } void LCD_WriteData(unsigned char dat) { RS 1; RW 0; P0 dat; LCD_Delay(100); EN 1; LCD_Delay(100); EN 0; } void LCD_Init(void) { LCD_Delay(15000); LCD_WriteCmd(0x38); LCD_Delay(5000); LCD_WriteCmd(0x38); LCD_Delay(5000); LCD_WriteCmd(0x38); LCD_Delay(5000); LCD_WriteCmd(0x0C); LCD_Delay(5000); LCD_WriteCmd(0x01); LCD_Delay(5000); LCD_WriteCmd(0x06); } void LCD_SetCursor(unsigned char row, unsigned char col) { unsigned char addr; if(row 0) addr 0x80 col; else addr 0xC0 col; LCD_WriteCmd(addr); } void LCD_ShowString(unsigned char row, unsigned char col, char *str) { LCD_SetCursor(row, col); while(*str) { LCD_WriteData(*str); } } void LCD_ShowNum(unsigned char row, unsigned char col, unsigned int num, unsigned char len) { char temp[6]; unsigned char i; temp[len] \0; for(i len; i 0; i--) { temp[i-1] 0 num % 10; num / 10; } LCD_ShowString(row, col, temp); }主函数main.c每2秒读取一次并显示#include reg52.h #include dht11.h #include lcd1602.h void main(void) { unsigned char humi_int, humi_dec, temp_int, temp_dec; unsigned char result; LCD_Init(); LCD_ShowString(0, 0, DHT11 Test); Delay_ms(1000); while(1) { result DHT11_Read_Data(humi_int, humi_dec, temp_int, temp_dec); if(result) { LCD_ShowString(0, 0, Hum:); LCD_ShowNum(0, 4, humi_int, 2); LCD_WriteData(.); LCD_ShowNum(0, 7, humi_dec, 2); LCD_WriteData(%); LCD_ShowString(1, 0, Tmp:); LCD_ShowNum(1, 4, temp_int, 2); LCD_WriteData(.); LCD_ShowNum(1, 7, temp_dec, 2); LCD_WriteData(C); } else { LCD_ShowString(0, 0, Read Error!); } Delay_ms(2000); } }7.4 把代码变成hex文件并加载进ProteusKeil工程创建时记得在Options for Target里选择芯片型号比如AT89C51或STC89C52。重点注意Output选项卡里必须勾选“Create HEX File”否则Proteus找不到可加载的程序文件。在Proteus仿真图上双击单片机芯片在Program File一栏选择生成的.hex文件同时确认晶振频率设置为12MHz。然后点击运行LCD1602应该显示当前环境温湿度。注意Proteus本身不模拟真实温度变化DHT11模型默认输出的可能是固定值你可以在元件属性里修改湿度或温度值来模拟不同环境这是仿真和硬件的另一个区别。8. 我踩了无数次坑之后总结的排查顺序最后再分享一个我实际使用的排查顺序照着做能省不少时间。DHT11仿真出问题时不要急着打开代码反复调。第一步看模型。确认Proteus版本和DHT11元件来源模型有问题后面全白搭。如果你用的是老版本建议直接换成新版本自带的模型这是最省心的做法。第二步看连线。电源、地、上拉电阻、数据脚接哪个端口这四样检查完基本能排除硬件侧的低级错误。第三步看程序能不能跑起来。先点运行随便写个P1口电平翻转程序确认单片机本身在Proteus里能工作。如果单片机都跑不起来DHT11的代码自然也不会有效果。第四步看时序。接上虚拟示波器看启动信号有没有18毫秒低电平看响应信号是否存在看数据位高电平宽度是30还是70微秒。这一步能精准定位代码里的延时问题。第五步看显示。确认LCD1602初始化成功、对比度合适、显示函数正确。很多时候DHT11数据早就读出来了只是显示环节把结果吞了。我自己的体会是Proteus仿真DHT11这件事说难不难说简单也绝不简单。它考察的不是你能不能背下时序代码而是你有没有一套稳定的调试路径。按这个顺序走下来绝大多数问题都能在半小时内定位而不是靠瞎猜参数浪费时间。希望这篇文章能帮你少踩几个坑顺利把仿真跑通。
企业数字化 ERP 产品动态
相关推荐
光学畸变与TV畸变:定义、测量方法与换算误区全解析 /* 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:32:45
AM32电调遥测功能实现:DMA+USART与CRC8校验 /* 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:32:45
我的世界Java版网页地址真相:浏览器运行原理与服务器联机指南 /* 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:32:45
如何给老 Mac 装上新版 macOS:OpenCore Legacy Patcher(OCLP)实操指南 如何给老 Mac 装上新版 macOS:OpenCore Legacy Patcher(OCLP)实操指南 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher
这篇… · 2026/9/25 2:14:22
SOIL-master 编译与集成指南:OpenGL 纹理加载库跨 IDE 实战 简介:SOIL(Simple and Fast Multimedia Library)是一套轻量高效的OpenGL图像加载库,面向需要在OpenGL环境中处理纹理的图形开发者与C/C学习者。它支持BMP、GIF、JPEG、PNG、TGA、DDS等多种格式,可帮助开发者快速将图片… · 2026/9/25 2:14:16
React 360 环境背景管理完全指南:Environment 模块的全景图、背景视频与屏幕渲染实战 前端3D渲染 【免费下载链接】react-360 Create amazing 360 and VR content using React 项目地址: https://gitcode.com/gh_mirrors/re/react-360 点击查看 免费下载 本指南围绕 docs/environment.md 展开,系统讲解 React 360 中 Environment 模块的全… · 2026/9/25 2:14:16
NEXDAP:ESP32-C3变身RP2040调试管家,一条USB线搞定下载与日志 /* 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 2:14:16
创维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 /* 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