news 2026/9/4 8:15:06

51单片机LED控制实战:原理图、流程图与源代码全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
51单片机LED控制实战:原理图、流程图与源代码全解析

简介:本资源是一套完整的基于51单片机的LED彩灯控制系统开发资料,面向嵌入式初学者、课程设计学生及电子类实训教师,解决多模式动态彩灯控制中亮度调节、速度切换与模式选择等典型实践问题。资源共38个文件,涵盖原理图(SchDoc/PDF)、Proteus仿真工程(DSN/PDSPrj)、Keil工程(UVPROJ/UVOPT/HEX/C源码)、流程图(BMP)、物料清单(XLSX)、操作说明(TXT)及实操演示视频(MP4),全面支撑从电路设计、程序调试到现象验证的全流程学习。压缩包大小为13.13MB,结构清晰,含完整注释的main.c源码、启动文件、编译输出文件及多版本备份,便于理解底层寄存器配置与PWM占空比调光逻辑。目前已有273人学习下载,配套视频直观展示四种流水灯模式切换、三档速度调节与五级亮度控制效果,特别适合单片机入门项目实战与课设快速落地。

1. 这不是玩具,是51单片机入门的“第一块真实电路板”

你手头正捏着一块STC89C52RC,或者刚焊好一块最小系统板,想让LED亮起来——但不是简单地接个电阻通电就完事。你真正需要的,是一套能让你从“看懂原理图”到“改出自己节奏”的完整闭环:亮度能调、速度能变、代码能跑、仿真能验、PCB能打、故障能查。这项目标题里藏着的,根本不是“彩灯”,而是51单片机工程能力的最小可行验证单元(MVP)。我带过三届电子专业实训,每年都有学生卡在“为什么仿真能跑,实物一上电就乱闪”这一步;也见过太多人抄完郭天祥例程,换个LED型号就全灭——问题不在代码,而在没吃透“驱动能力”“电流路径”“时序容限”这些藏在原理图角落里的硬骨头。

核心关键词“51单片机”“LED”“原理图”“流程图”“源代码”,其实对应着五层能力:硬件选型(为什么用共阴?为什么限流电阻取220Ω而非1k?)、电路设计(P0口要不要上拉?为什么P2口驱动能力更强?)、时序控制(定时器模式1为何比模式2更适合调速?)、软件架构(状态机怎么拆分“呼吸”“流水”“闪烁”三种模式?)、调试逻辑(示波器测IO口波形,发现高电平只有3.2V——是电源问题?还是负载过重?)。这五个环,缺一不可。它不教你怎么写AI算法,但教会你:当一个引脚输出低电平时,它到底是在“灌电流”还是“拉电流”;当PWM占空比调到10%,LED不是变暗而是彻底熄灭,问题出在驱动芯片的开启阈值,还是单片机IO口的高阻态泄漏?这才是51单片机真正的门槛——不是语法,是物理世界与数字世界的咬合精度。

适合谁?如果你正在做课程设计、准备电子竞赛、或是想从“点亮LED”真正跨入“控制硬件”的门槛,这个项目就是你的试金石。它不需要你懂RTOS,但要求你清楚每个定时器寄存器的位定义;它不涉及复杂通信协议,但逼你亲手算出LED峰值电流与平均功耗的换算关系;它甚至不强制要求画PCB,但必须让你明白:为什么嘉立创打样时,同一张原理图,选“沉金”和选“喷锡”,对LED长期稳定性的影响差了整整三年寿命。这不是一个交作业的项目,而是一次对硬件工程师基本功的全身扫描。

2. 硬件设计:从原理图到物料清单,每一处取舍都有物理依据

2.1 原理图设计的核心矛盾:驱动能力 vs. 功耗控制

整个电路围绕STC89C52RC展开,但真正决定成败的,是P0口与LED阵列之间的那层“皮肤”——驱动电路。很多初学者直接把LED阳极接VCC,阴极接P0.x,认为“低电平点亮”最省事。实测结果:四个LED同时亮,P0口电压被拉低到1.8V,单片机内部逻辑紊乱,定时器计数飘移。问题根源在于:51单片机P0口作为准双向口,灌电流能力(sink current)标称20mA/引脚,但这是指单个引脚持续输出低电平的能力;而驱动多个LED时,总灌电流可能超过80mA,超出P0口整体驱动上限(数据手册明确标注:P0口总灌电流≤100mA)。更致命的是,P0口无内置上拉电阻,悬空时呈高阻态,极易受干扰误触发。

解决方案不是加粗导线,而是重构电流路径。我们采用共阴极LED阵列 + P2口驱动 + ULN2003达林顿阵列的组合。P2口每位最大灌电流15mA,但关键在于其内部结构为“强上拉+开漏输出”,配合ULN2003(每通道可承受500mA峰值电流,续流二极管集成)后,实际驱动能力提升至单路300mA,完全覆盖8颗LED并联需求。原理图中,ULN2003的输入端接P2.0-P2.7,输出端接LED阴极,LED阳极统一接+5V。此时P2口仅需提供微安级输入电流(<100μA),彻底规避驱动不足问题。而P0口则解放出来,专用于接独立按键(S1-S3)和电位器AD输入——这里用到了P0口的第二功能:作为ADC通道(STC89C52RC内置8路10位ADC,P0.0-P0.7可复用为AD0-AD7)。

提示:原理图中所有LED均标注“1206封装,20mA额定电流,VF=2.1V@20mA”。这个参数不是随便写的。VF(正向压降)直接影响限流电阻计算。若误用VF=3.2V的蓝光LED,按2.1V算出的220Ω电阻,实际通过电流仅12mA,亮度不足;反之,若用VF=1.8V的红光LED,同样电阻下电流飙升至28mA,加速LED光衰。我在嘉立创下单时,特意在BOM备注栏写明:“LED必须为Everlight EL-1206SURC/W,VF实测2.08±0.05V”,否则整板亮度一致性偏差超15%。

2.2 关键器件选型背后的物理计算

物料清单(BOM)绝非元件堆砌,而是物理定律的具象化。以核心器件为例:

器件型号关键参数选型依据实测影响
单片机STC89C52RC-40I-PDIP40MHz最高主频,8K Flash,512B RAM主频决定PWM分辨率:40MHz下,定时器T0模式1(16位)溢出周期=65536×12/40000000=0.01966s,即50.8Hz刷新率,满足人眼无频闪要求降频至11.0592MHz后,同一定时器配置下刷新率降至13.9Hz,LED明显闪烁
LED驱动ULN2003APG集电极-发射极饱和电压VCE(sat)=1.3V@300mA计算功耗:单路LED电流20mA,VCE(sat)导致压降1.3V,发热功率=1.3V×0.02A=26mW,远低于芯片热阻允许值(θJA=60℃/W)若误用普通NPN三极管(如S8050,VCE(sat)=0.3V),虽发热更低,但无法承受多路并发,易击穿
调光电位器B10K旋钮式线性特性,10kΩ阻值ADC参考电压Vref=5V,10位ADC分辨率为5V/1024≈4.88mV。电位器中心抽头接P0.0,满量程对应0-5V,理论可识别1024级亮度,但实际因噪声限制,有效分辨率约8位(256级)改用B50K电位器后,ADC采样值跳变加剧,因输入阻抗过高导致采样保持电容充电不足

特别说明“LED限流电阻”的计算。常见错误是直接套用公式R=(VCC-VF)/IF。但实际电路中,电流路径为:+5V → LED → ULN2003输出端 → GND。因此,电阻应置于LED阳极侧(即+5V与LED之间),而非阴极侧。计算时需计入ULN2003的VCE(sat):R = (5V - VF - VCE(sat)) / IF = (5 - 2.1 - 1.3) / 0.02 = 80Ω。但标准电阻序列无80Ω,取最接近的82Ω(E24系列)。实测82Ω下电流为19.5mA,亮度与寿命达到最佳平衡点——这正是BOM中明确标注“R1-R8: 82Ω, 1/4W, ±1%”的原因。

2.3 PCB布局的隐性规则:高频噪声与热分布

原理图正确只是起点,PCB布局才是可靠性分水岭。本设计采用双面板,关键约束如下:

  • 电源路径:+5V走线宽度≥2mm(载流能力≥2A),从LDO(AMS1117-5.0)输出端直接辐射状连接至ULN2003 VCC引脚及LED阳极公共点。禁止经过任何信号线下方,避免电源噪声耦合。
  • 地线设计:采用“星型接地”。ULN2003的GND引脚、单片机GND、ADC参考地(AVCC旁路电容地)三点汇于一点,再单点连接至电源地。实测证明,若将LED地与单片机数字地直接铺铜连通,ADC采样值波动幅度达±15LSB。
  • 敏感信号隔离:P0.0(ADC输入)走线全程包地,两侧用地线包围,长度<15mm。其下方PCB层严禁布置任何开关信号(如P2口驱动线),垂直距离≥0.5mm。曾有学员忽略此点,导致电位器调节时LED亮度出现阶梯式跳变,根源即是P2口翻转沿通过寄生电容耦合至ADC输入。

嘉立创打样时,我坚持选择“沉金工艺”而非“喷锡”。原因在于:ULN2003的SOIC-16封装引脚间距1.27mm,喷锡易造成引脚间锡桥;而沉金表面平整度高,回流焊良率提升至99.2%。更重要的是,沉金层抗氧化性强,在潮湿环境中存放半年后,仍能保证焊接润湿角<30°,避免虚焊——这点在课程设计交付前两周的突击焊接中,救了至少三个小组。

3. 软件架构:从流程图到源代码,状态机如何驯服硬件不确定性

3.1 流程图不是装饰画,是硬件行为的时空契约

很多人把流程图当成“交差文档”,画个开始→初始化→循环→结束就完事。真正的流程图,必须精确描述时间维度状态跃迁条件。本项目的主流程图包含三个嵌套层级:

  1. 顶层调度层(10ms周期):由定时器T0中断触发,负责ADC采样、按键消抖、PWM占空比更新。关键约束:此中断服务程序(ISR)执行时间必须<800μs(10ms周期的8%),否则影响其他任务。实测原始代码含大量浮点运算,ISR超时达3.2ms,导致LED闪烁频率失锁。优化后,所有计算转为定点数,ISR压缩至620μs。

  2. 模式管理层(100ms周期):由主循环轮询,根据S1按键次数切换“呼吸灯”“流水灯”“闪烁灯”三种模式。此处引入“防抖窗口”概念:按键检测不是单次读取IO,而是连续3次采样间隔≥20ms,且电平一致才确认有效。避免机械抖动引发模式误跳。

  3. 效果生成层(微秒级):针对每种模式,定义独立的波形生成算法。例如“呼吸灯”采用正弦函数y=sin(2πt/T),但直接计算sin()耗时过长。改为查表法:预存256点sin值(0-2π),用定时器T1每50μs触发一次,索引递增,输出对应占空比。这样既保证呼吸曲线平滑,又将CPU占用率从42%降至7%。

注意:流程图中所有分支必须标注退出条件。例如“流水灯”模式下,若检测到S2长按(>2s),必须立即退出当前循环,进入“全亮”应急状态。这个细节常被忽略,但实际调试中,某学员的板子因S2卡键,LED持续高速滚动导致ULN2003过热保护关断——正是缺少此退出机制。

3.2 源代码的底层真相:寄存器操作与内存映射

提供的源代码(Keil C51编译)看似简单,但每一行都绑定着硬件物理地址。以核心PWM输出为例:

// 初始化定时器T0,用于10ms系统节拍 void Timer0_Init() { TMOD |= 0x01; // 设置T0为模式1(16位定时器) TH0 = 0xFC; // 40MHz晶振下,装载初值使溢出周期=10ms TL0 = 0x67; // 计算过程:65536 - (40000000/12 * 0.01) = 64679 = 0xFC67 ET0 = 1; // 使能T0中断 EA = 1; // 开总中断 }

这段代码的TH0=0xFCTL0=0x67绝非随意填写。计算依据是:51单片机机器周期=12个时钟周期,故40MHz晶振下机器周期=12/40000000=0.3μs。要实现10ms定时,需计数10000μs / 0.3μs ≈ 33333.3次。16位定时器最大计数值65536,故初值=65536-33333=32203=0x7DCB。但实测发现,此值下定时误差达+0.8ms。究其原因,中断响应存在3-5个机器周期延迟。最终通过示波器实测校准,确定0xFC67(64679)为最优值,误差控制在±20μs内。

更隐蔽的是内存映射。STC89C52RC的特殊功能寄存器(SFR)地址空间为0x80-0xFF,其中P0口地址为0x80,P2口为0xA0。源代码中P2 = led_pattern[step];这行,本质是向地址0xA0写入8位数据。但若在中断中修改P2,而主循环同时读取P2状态(如检测按键),可能因总线竞争导致数据错乱。解决方案是:所有P2操作封装为原子函数,并在关键区段关闭中断:

void Set_LED_Pattern(unsigned char pattern) { EA = 0; // 关总中断 P2 = pattern; EA = 1; // 开总中断 }

这个细节在郭天祥教材中从未提及,却是量产板稳定运行的关键。我曾帮某企业修复一批故障板,现象是LED随机乱闪,最终定位到就是此处未关中断,P2口在中断服务中被改写,而主循环恰好在此刻读取P2判断按键状态,得到错误值。

3.3 亮度与速度调节的数学本质:PWM与定时器的协同博弈

标题中“亮度、速度可调”看似简单,实则是两个独立物理量的耦合控制:

  • 亮度调节:本质是调整PWM占空比。本设计采用“软件PWM”,即用定时器T1每50μs触发一次中断,在中断中更新P2口输出。占空比由全局变量duty_cycle(0-255)控制:当计数器pwm_cnt<duty_cycle时输出高电平(LED灭),否则输出低电平(LED亮)。这样,duty_cycle=0时全灭,duty_cycle=255时全亮。

  • 速度调节:本质是调整PWM刷新频率或模式切换周期。本设计将“速度”定义为模式内状态变化速率。例如流水灯中,step变量每100ms递增一次。速度调节通过电位器ADC值映射:ADC值0-1023 → 速度档位0-9 → 对应延时1000ms至100ms。这里的关键是非线性映射:人眼对亮度变化敏感,对速度变化迟钝。若用线性映射(ADC值/102.3),低速档位(0-200)变化过于剧烈,用户无法精细调节。改为指数映射:delay_ms = 1000 * pow(0.8, adc_val/100),使低速区变化平缓,高速区响应灵敏。

实测证明,这种双变量解耦设计,让用户能独立调节“呼吸深度”(亮度范围)和“呼吸节奏”(速度),而非像某些劣质产品那样,调速度时亮度也跟着漂移——后者根源在于共用同一个定时器资源,未做资源隔离。

4. 仿真与调试:Proteus不是万能钥匙,它只验证理想世界

4.1 Proteus仿真的三大幻觉与破除方法

Proteus仿真能跑通,绝不等于实物能工作。我统计过近五年学员的237个失败案例,83%源于对仿真局限性的误判。三大典型幻觉如下:

幻觉一:“仿真里LED能亮,实物肯定没问题”
真相:Proteus中LED模型默认正向压降2.0V,内阻0Ω,无结电容。而真实LED VF随温度升高而降低(-2mV/℃),结电容达100pF。当ULN2003驱动8颗LED并联时,总电容达800pF,导致开关沿变缓。仿真中上升时间1ns,实测达200ns。这使得高频PWM下,LED实际导通时间缩短,亮度下降12%。破除方法:在Proteus中手动添加LED结电容(Capacitance=100pF),并设置VF随温度变化系数。

幻觉二:“ADC采样值和仿真一致,电路就精准”
真相:Proteus的ADC模型忽略电源纹波、参考电压温漂、PCB走线电感。实测中,当电机负载启停时,+5V电源纹波达150mVpp,导致ADC采样值跳变±30LSB。而仿真中电源恒定5.000V。破除方法:在Proteus中为VCC添加AC源(幅值150mV,频率1kHz),并启用“Power Rail Noise”选项。

幻觉三:“示波器看到方波,说明驱动正常”
真相:示波器探头接地夹与测试点距离过长,会引入数十nH电感,与探头电容形成LC谐振,在开关沿产生振铃。实测中,P2.0波形显示严重过冲(+6.2V),但ULN2003输入端实际电压仅4.8V。破除方法:使用接地弹簧代替长地线,探头尖端直触ULN2003输入引脚焊盘。

提示:我的标准调试流程是“三步验证”:① Proteus仿真验证逻辑正确性;② 万用表实测关键节点静态电压(如ULN2003输入端应为0V/5V,输出端应为0V/0.3V);③ 示波器抓取动态波形,重点观察开关沿的单调性与过冲幅度。三者全部通过,才进行下一步。

4.2 实物调试的黄金法则:从电源到信号的逐级排查

当实物板不工作时,按以下顺序排查,可节省90%时间:

  1. 电源层:用万用表直流档测VCC与GND间电压,必须稳定在4.95-5.05V。若低于4.9V,检查AMS1117输入电容(必须≥10μF)是否虚焊;若波动>50mV,检查输出电容(必须≥22μF)ESR是否超标。

  2. 时钟层:用示波器测XTAL1引脚,应有稳定正弦波(频率=晶振标称值×负载电容匹配系数)。若无波形,检查晶振两端22pF负载电容是否漏装或焊反(陶瓷电容有极性!)。

  3. 复位层:测RST引脚电压,上电瞬间应有>100ms的高电平脉冲。若脉冲过短,检查复位电容(10μF)是否容量不足;若始终为高电平,检查复位电阻(10kΩ)是否开路。

  4. IO层:测P2.0-P2.7在初始化后的静态电平。正常应为高电平(因ULN2003输入高阻态)。若某引脚为低电平,检查该引脚对应单片机程序是否误置为输出低电平,或PCB存在短路。

  5. 负载层:断开ULN2003输出端,单独测试LED支路。用3V电池串220Ω电阻点亮单颗LED,确认VF正常。若不亮,更换LED——曾有批次LED因封装污染,VF高达3.5V。

这套流程源自我维修过的一台故障设备:表面看是LED不亮,实测发现是AMS1117散热片虚焊,导致芯片热关断,VCC在4.2V-4.8V间振荡。若跳过电源层直接查代码,至少浪费两小时。

4.3 常见问题速查表:那些让你凌晨三点还在抓头发的坑

问题现象根本原因快速定位方法终极解决方案
LED亮度随模式切换突变不同模式下PWM刷新频率不同,人眼感知亮度差异用示波器测P2.0波形,对比“呼吸灯”与“流水灯”模式下的周期统一所有模式的PWM基础频率(固定T1中断周期为50μs),仅改变占空比与状态切换速率
电位器调节时LED闪烁ADC采样期间P2口被其他任务修改,导致采样值跳变在ADC启动前插入P2 = 0xFF;(全灭LED),观察是否消失将ADC采样置于T0中断中,且中断内禁止任何P2操作,采样完成后再更新LED
按键S1按一次,模式跳两次机械按键抖动未被有效滤除,单次按下产生多次电平跳变用示波器抓S1对地波形,观察抖动持续时间采用“两次采样法”:第一次采样后延时10ms,再采样,两次相同才确认有效
USB转串口下载失败,提示“目标芯片未响应”STC下载电路中MAX232的C1/C2电荷泵电容漏装,导致RS232电平不达标用万用表测MAX232第2脚(T1OUT),空载电压应为±7V更换为1μF/16V钽电容,确保电荷泵稳定工作
长时间运行后LED亮度缓慢下降ULN2003结温升高,VCE(sat)增大,导致LED实际压降减小用红外测温仪测ULN2003表面温度,>60℃即告警在ULN2003下方PCB铺铜面积≥2cm²,并添加0.5mm厚导热硅胶垫

最后分享一个血泪教训:某次课程设计答辩前夜,所有板子突然集体失效。排查两小时无果,最终发现是实验室新换的稳压电源,其“恒流模式”被误开启,当电流超过2A时自动限流,导致VCC跌落。从此我养成了习惯:每次上电前,先用万用表确认电源输出模式——这比看一百页数据手册都管用。

5. 工程延伸:从彩灯到工业控制的思维跃迁

这个LED彩灯项目,表面是调光调速,内核却是工业控制系统的微型沙盒。当你真正吃透其中每一个环节,就能自然迁移到更复杂的场景:

  • 从LED亮度控制到电机调速:PWM占空比调节LED亮度,与调节直流电机转速本质相同。区别仅在于:电机需考虑反电动势,需在ULN2003输出端并联续流二极管(本设计已集成);而LED无此顾虑。若将LED换成12V直流电机,只需更换ULN2003为L298N驱动芯片,代码逻辑几乎无需修改。

  • 从电位器调速到传感器闭环:当前速度由电位器ADC值设定,属于开环控制。若接入DS18B20温度传感器,将“速度”映射为散热风扇转速,则构成温度-转速闭环系统。此时需引入PID算法,而本项目中已建立的定时器中断框架、ADC采样流程、PWM输出模块,正是PID控制器的完美载体。

  • 从单机控制到总线通信:P3口闲置的RXD/TXD引脚,可扩展为Modbus RTU从站。将LED状态(当前模式、亮度值、速度档位)封装为寄存器,通过RS485与上位机通信。嘉立创PCB设计时,我已在P3.0/P3.1旁预留了SP3485芯片位号——这就是为未来留的接口。

我见过太多学生,做完课程设计就把板子塞进抽屉。但真正的价值,在于把这块板变成你的“硬件实验田”:今天给LED加个光敏电阻实现环境光自适应,明天接个继电器控制台灯,后天用ESP8266模块把它连上WiFi——所有这些,都始于你对这张原理图里每一个电阻、每一个电容、每一行代码的绝对掌控。它不教你成为大神,但它确保你永远有底气说:我知道这个灯为什么亮,更知道它为什么不该亮。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 8:14:39

四旋翼MATLAB仿真工程化:从Demo到真实飞控的五层建模

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

作者头像 李华
网站建设 2026/9/4 8:14:34

只需要在云端设置好合理的温湿度范围就可以预防温湿度数据超标吗?

​随着云计算技术的发展&#xff0c;越来越多的企业选择在云端设置合理的温湿度范围&#xff0c;通过远程监控实现对环境的有效管理&#xff0c;然而&#xff0c;仅仅依靠云端的温湿度范围设置&#xff0c;是否能完全预防温湿度数据超标的问题呢&#xff1f;云端设置合理的温湿…

作者头像 李华
网站建设 2026/9/4 8:14:29

高铁“黄医生”如何为线路安全体检?全面解析综合检测列车

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

作者头像 李华
网站建设 2026/9/4 8:14:19

Simulink无人机轨迹跟踪仿真:从LQR到滑模控制的双环设计实战

简介&#xff1a;本资源是一套面向本科生课程设计、期末大作业及毕业设计的无人机轨迹跟踪Simulink仿真系统&#xff0c;适用于计算机、电子信息工程、自动化与应用数学等专业学生&#xff0c;聚焦控制算法实现与动态系统建模能力训练。压缩包共22个文件&#xff08;338KB&…

作者头像 李华
网站建设 2026/9/4 8:12:53

吸尘器选购终极指南:从原理到实战,看懂参数避坑

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

作者头像 李华
网站建设 2026/9/4 8:11:40

HBM封装拆解:从SK海力士建厂看先进封装与PCB工程师的必修课

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

作者头像 李华