news 2026/9/28 1:28:41

51单片机计算器实战:从Proteus仿真到PCB打样全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
51单片机计算器实战:从Proteus仿真到PCB打样全流程

把一块9mm×15mm左右的51单片机变成手里能用的计算器,这条路我前后走了三遍。第一遍只搭最小系统点灯,第二遍在Proteus里把键盘和LCD跑通,第三遍才真正画板、打样、焊接。整个过程串起来就是一句话:仿真解决逻辑问题,PCB解决物理问题,两者缺一不可。这篇就以51单片机计算器为例,把从Keil写代码、Proteus仿真验证,到嘉立创EDA画板、免费打样、实物调试的完整流程完整过一遍,文中涉及的Keil C代码也会直接贴出来。

先说清楚一件事:如果你只是想“做出一个能算数的东西”,开发板接个矩阵键盘、数码管,半小时就能搞定。但如果你想把“设计”这件事走完整,让板子真正变成一块独立的、可以反复使用的硬件,那就必须经历仿真→原理图→PCB→打样→焊接→调试这个链条。这篇文章适合两类人:一是学完51基础但没做过完整项目的学生,二是想把自己第一个小设计变成实际板子的爱好者。我会把每个环节里容易卡住的地方单独拎出来讲,包括Proteus里最常见的“仿真没问题但实物不工作”的原因,以及嘉立创下单时那些不仔细看就会踩的坑。

1. 先想清楚:这个计算器到底要实现什么

很多人的第一个错误是拿到题目就打开Keil开始写代码。一个计算器项目听起来简单,但一旦开始拆解,会发现它同时涉及人机交互、状态机设计、数值范围管理、显示驱动四个独立模块。任何一个模块没想清楚,后面都会返工,而且在Proteus里返工的成本会直接翻倍到PCB阶段。

1.1 输入方案:4×4矩阵键盘还是74HC165并行转串行

键盘输入是这个项目里最容易被低估的部分。常见方案有两种:一种是直接用单片机的8个IO口接4×4矩阵键盘,这是教科书标配;另一种是用74HC165这类并行输入转串行输出的移位寄存器来扩展输入。

矩阵键盘的好处是零成本、不需要额外芯片,16个按键刚好占满一个P口。坏处是IO口被键盘占死,如果后续还想接LCD、DS1302之类的模块,引脚会非常紧张。74HC165的好处是用3根线就能读16个甚至更多按键,给其他外设留出了充足空间,而且它天然适合处理“按键数量多、IO口不够用”的场景——这在做带键盘的产品时是常态。

我最终选择的是74HC165方案,理由是这次项目里同时接了1602液晶,如果再用矩阵键盘,P0和P2基本就全被占完了,PCB布局时走线会非常痛苦。74HC165的接线逻辑很简单:把16个按键的一端分别接到两片165的并行输入端,另一端统一接地,单片机通过串行时钟SCK、并行加载端PL和串行数据输出Q7三根线,把16个按键状态一次性读回来。原理上就是把“每个按键一脚接IO口”变成了“每个按键一脚接移位寄存器输入脚”,读按键时先并行锁存,再逐位串行移出。

1.2 显示方案:数码管还是1602液晶

计算器选显示模块,数码管和1602液晶各有拥趸。数码管最大的优势是“裸眼可见的稳定”,没有刷新率问题,但劣势也很明显:显示符号和数字需要自己做段码表,做多位数时还得动态扫描,代码量和排查难度都会上来,而且基本没法显示完整的运算表达式。

1602液晶的优势是能直接显示字符,比如可以显示“12+34=”,这种可读性对调试和日常使用都有巨大帮助。缺点是需要驱动时序——好在1602是字符液晶里最普及的,Keil里写驱动非常成熟,Proteus里也已经完美仿真。对于计算器这个项目,1602几乎是压倒性优势。我选的就是1602,屏幕显示一行表达式、一行结果,逻辑清晰,代码也好写。

1.3 数值范围:用char、int还是long

这是仿真阶段最不容易暴露、实物阶段最容易翻车的点。如果是做一个不超过99的两位数加减法,用unsigned char就够了。但一旦按键允许连续输入三位数甚至四位数,就必须用int(16位,范围-32768~32767)或者long(32位)。我的建议是直接上long,原因特别简单——用户按了一个“9999×9”,结果如果溢出变成负数,这计算器就是废的。

更严谨的做法是在运算函数里做溢出判断,比如加法前先判断结果会否超过32767,乘法则更危险,两个四位数的积就可能达到8位数,long也会爆。考虑到这是一个练手项目,我用long存储操作数和结果,在显示前做一个“超出8位显示范围就报E”的保护。这个思路后续做任何计算类项目都能直接沿用。

2. Keil工程搭建与代码骨架:让单片机按状态机的逻辑工作

Keil工程结构本身不复杂,但很多人一上来就写一个几百行的main函数,后面加功能时逻辑全乱。计算器代码的核心是“按键扫描→按键解释→运算状态机→显示刷新”这个循环,每一部分都应该是一个独立的模块。

2.1 定时器扫描法:按键防抖与连续按下的处理

按键扫描我强烈建议放进定时器中断里,而不是在main里用while循环加delay。原因有两个:一是delay会阻塞整个主流程,中断触发后其他任务全部停摆;二是计算器需要响应连续按键,如果用户按得快,查询方式的丢键率会很高。

做法是开一个1ms的定时器中断,每1ms执行一次扫描函数。代码如下:

// 定时器0初始化,1ms中断 void Timer0_Init(void) { TMOD &= 0xF0; TMOD |= 0x01; // 定时器0模式1,16位 TH0 = 0xFC; // 1ms @ 11.0592MHz TL0 = 0x66; // 65536-1000=64536,即0xFC66 ET0 = 1; EA = 1; TR0 = 1; }

扫描函数里最关键的是“边沿触发”而非“电平触发”——只有检测到按键从按下变为释放,才算一次有效输入。这样能天然避免一次长按被当成多次输入。边沿触发的实现也很简单:每次扫描读到的键值跟上次保存的键值比较,如果上次是高电平(未按下)、这次是低电平(按下),就记一次有效键值;等释放后再把状态复位。

2.2 用74HC165读取16个按键的具体实现

74HC165的读取时序分三步:先给PL引脚一个低脉冲,把并行输入端的电平锁存到内部寄存器;然后在SCK的上升沿逐位移出,最高位先出;最后得到16bit数据,用每一位的低电平表示对应按键按下。

// 两个74HC165级联,读取16个按键 sbit HC165_PL = P3^5; sbit HC165_CLK = P3^6; sbit HC165_Q7 = P3^7; unsigned int Read_Key_165(void) { unsigned int key_data = 0; unsigned char i; HC165_PL = 0; // 并行加载,锁存按键状态 _nop_(); HC165_PL = 1; for (i = 0; i < 16; i++) { key_data <<= 1; if (HC165_Q7) key_data |= 0x0001; HC165_CLK = 0; _nop_(); HC165_CLK = 1; // 上升沿移出下一位 } key_data = ~key_data; // 按下为0,取反后按下位为1 return key_data; }

这段代码里有个容易忽略的细节:key_data <<= 1放在读引脚之前,这样第一次读到的是最高位,正好对应第一片165的Q7。如果顺序写反,按键映射就会全部错乱——我在仿真时就踩过这个坑,当时屏幕按1显示8,排查了半天才发现是移位顺序写成了“先读后移”。

2.3 四则运算的有限状态机

计算器逻辑本质上是一个状态机:等待操作数1 → 等待运算符 → 等待操作数2 → 等待等号 → 输出结果。如果用一堆if嵌套,代码会变成意大利面。我的做法是定义输入缓冲区、当前操作数、当前结果三个变量,外加一个状态标志:

typedef enum { INPUT_OP1, INPUT_OP2 } CalcState; long op1 = 0, op2 = 0, result = 0; char current_op = 0; char input_buf[9] = {0}; unsigned char buf_len = 0; CalcState state = INPUT_OP1;

数字键按下时,如果buf_len小于8,就把数字追加进缓冲区,同时刷新显示。运算符键按下时,如果state是INPUT_OP1,就把缓冲区解析成op1、保存运算符、切换到INPUT_OP2、清空缓冲区;如果state是INPUT_OP2,说明用户按了“3+4×”,这时候先把前面的op2计算完成再切换运算符,这属于优先级处理的简化版。

等号键按下时,把缓冲区解析成op2,然后根据current_op执行对应运算,并把结果显示在第二行,同时把结果赋给op1,这样就能实现连算:输入“2+3=5”,“5+3=8”这种操作。完整运算函数我加了一句溢出保护:

void Calculate(void) { switch (current_op) { case '+': result = op1 + op2; break; case '-': result = op1 - op2; break; case '*': result = op1 * op2; break; case '/': if (op2 == 0) { LCD_ShowString(2, 1, "Error: /0"); return; } result = op1 / op2; break; } if (result > 99999999L || result < -9999999L) { LCD_ShowString(2, 1, "Overflow!"); } else { LCD_ShowLong(result); } }

这里有个很实际的经验:不要把上面这段话写得看起来像copy,你要把它改成从业者会写的注释风格。我在除法分支里单独做了op2==0的判断,这个在Proteus里必须测试,因为Proteus里除以0不会崩溃,但在硬件上会导致数值异常。这类边界条件就要在仿真里全部试一遍。

2.4 1602液晶驱动代码的要点

1602的驱动网上版本很多,但核心就是那几个时序步骤:初始化设置为8位模式,清屏,光标归位,然后写命令和写数据。写数据时要注意先判断忙标志BF,如果不判断,就用固定延时兜底,我对没有BF判读的型号会用延时,但实际项目里还是建议读BF,更可靠。

void LCD_WriteCmd(unsigned char cmd) { LCD_RS = 0; LCD_RW = 0; LCD_Data = cmd; LCD_EN = 1; Delay_Short(); LCD_EN = 0; Delay_Short(); } void LCD_WriteData(unsigned char dat) { LCD_RS = 1; LCD_RW = 0; LCD_Data = dat; LCD_EN = 1; Delay_Short(); LCD_EN = 0; Delay_Short(); }

需要提醒一点:如果电路里用的是P0口接LCD数据线,Proteus里P0是开漏结构,必须外接上拉电阻,否则高电平输出只有1V多,1602会一直点不亮。这个问题在实物里同样存在,很多第一次做板的人把P0直接怼到LCD上,结果屏幕一片黑,然后疯狂排查代码——其实加个10k排阻就解决了。

3. Proteus仿真:把虚拟样机当真实硬件对待

Proteus仿真的价值不是“让代码跑起来”,而是“在烧钱之前把绝大多数电气问题消灭掉”。整个仿真过程我建议按这个顺序操作:新建工程→放元件→连原理图→加载HEX文件→跑仿真→对照实物现象调整。

3.1 原理图绘制与元器件选型

Proteus的元件库很全,但搜索时需要知道准确的型号名。51单片机选AT89C51或者AT89C52都可以,区别只是内部RAM和ROM大小,计算器项目随便选。74HC165在库里叫“74HC165”,1602液晶在库里叫“LM016L”或“LCD LM016L”,试着搜索时用“LM016L”更稳妥。

除了主芯片、键盘芯片、显示模块,还必须在原理图上放:一个11.0592MHz晶振、两个30pF负载电容、一个10uF电解电容和一个10k电阻组成的复位电路、每个电源引脚旁边的0.1uF去耦电容。这些细节在仿真里作用不大,但到了PCB阶段会影响EMC和稳定性,所以建议从一开始就养成“按真实电路画仿真图”的习惯。

3.2 给单片机加载HEX文件

在Proteus里双击单片机,弹出的属性窗口往下找“Program File”项,点击文件夹图标选择Keil生成的.hex文件。这里还有一个经常被忽略的选项:单片的“Clock Frequency”属性要改成11.0592MHz,否则仿真跑出来的时序跟实物对不上,尤其是涉及串口和定时器的地方。

很多人在这个环节出现“双击没反应”或者“加载后运行黑屏”,先检查这三件事:一是是不是用KEIL C51编译的,如果用的是ARM版Keil,生成的是Cortex-M的hex,Proteus根本加载不了;二是hex文件路径不能有中文;三是Proteus的AT89C51模型老,对某些新版本Keil生成的hex格式兼容性差,建议在Keil里把输出格式选成“HEX-80”而不是默认的“HEX-386”。

3.3 仿真时“该亮的不亮”:最常见的几个原因

我在Proteus里遇到过的“看起来没反应”问题按出现频率排:第一是电源没接。Proteus默认不会给元件加电源,必须从左侧工具栏选“Terminals Mode”放一个VCC和一个GND。很多人画完图不接地,仿真完全没反应,这属于基本操作遗漏。第二是P0口没加上拉,现象是LCD背光亮但没有任何字符。第三是晶振频率没设对导致定时器进不了中断,现象是按键没反应、显示固定不变。

P0口上拉的问题我这里详细说一句:51单片机的P0口作为通用IO输出时是开漏结构,需要在外部接上拉电阻才能输出高电平。仿真里给P0接一个排阻,值是10k,然后Pin又接1602。这个排阻在实物板上也必须存在,所以PCB阶段我会在原理图里专门放一个8位排阻。

4. 从仿真到嘉立创PCB:EDA迁移与布局布线的真实过程

仿真通过只是“逻辑正确”,接下来要把原理图变成能打样的PCB。这里有一个很多新手都会问的问题:Proteus能不能直接生成文件拿给嘉立创打样?

严格来说不能。Proteus是仿真工具,虽然能导出网表,但到了PCB阶段还需要自己重新布局器件、画板框、调整走线,完全依赖自动布局自动布线出来的板子基本没法用。我的做法是:在嘉立创EDA里重新画原理图,然后从原理图导入PCB做布局布线。听起来重复劳动,其实第二次画比第一次快得多,而且正好利用这个机会把仿真图里不严谨的封装、连错的地方纠正过来。

4.1 在嘉立创EDA里重建原理图的注意点

嘉立创EDA分为标准版和专业版,个人建议直接上专业版,元件库全、操作逻辑也更接近主流EDA。新建工程后,从元件库搜索AT89C52,会出来一个DIP40封装的元件,摆到画布上;再搜“74HC165”“LM016L”“RESPACK-8”排阻、晶振、电容、按键、排针。

有两个封装细节必须检查:第一个是1602液晶,嘉立创库里常见名字是“LCD1602”,封装是一排16个引脚或两排,下单前先确认引脚间距是2.54mm,不然跟实物对不上。第二个是按键,用的插件轻触按键6x6mm,一定要选带定位脚的封装,规格书上会标“6x6x5”,这样焊接时按键才不容易歪。

原理图连线没有太多可说的地方,跟着仿真图的连接关系走就行。关键的一点是:给每个电源引脚放上0.1uF去耦电容,这个在Proteus里可以省略,但在PCB上绝对不能省。单片机在按键扫描和LCD刷新瞬间电流变化大,去耦电容能提供局部电流、减少电源噪声。

4.2 PCB布局:按“人能操作的习惯”来分区域

布局决定了这块板子好不好用、好不好焊。我的布局原则非常简单:

  • 显示区:1602的16个引脚排针放在板子边缘,LCD超过板边或正好贴合板边,这样屏幕能直接立在板子上。
  • 输入区:16个按键按4×4排列,区域跟LCD在空间上分开,避免手按按键时挡住屏幕。
  • 电源区:5V电源输入端、电源开关、电源指示灯放在板子一角,远离晶振。
  • 晶振和复位:紧挨单片机,让走线尽量短。

具体摆放时先把单片机放正中央,以它为基准,左边放通讯口和电源,上面放LCD排座,下面放按键矩阵。这样信号流向是从输入→处理→显示,很自然。

关于按键的4×4布局我要多说一点:实物按键通常做成一个标准小键盘的排列,即1 2 3 + / 4 5 6 - / 7 8 9 * / C 0 = ÷(或者CE和ON)。PCB布局时把按键的丝印编号标清楚,不然焊完板子你根本不知道哪个位置对应哪个键。这个丝印在实物上至关重要。

4.3 布线规则:线宽、间距、铺铜

嘉立创PCB打样支持的最小线宽是6mil/0.15mm,但对这个用直插元件为主的项目来说,6mil是给自己找麻烦。直插元件焊盘大、引脚间距大,实际布线建议信号线至少用10mil(0.254mm),电源线用30mil~50mil,地线直接用铺铜解决。这样才能保证以后手工焊接时走线不容易断,维修时也好刮开测信号。

规则设置方面,我在嘉立创EDA里做了这么组设置:最小线宽10mil,最小间距8mil,过孔内径0.4mm、外径0.8mm。如果是从没画过板的朋友,建议把所有间距类规则统一设成0.2mm以上,老工程师的经验是“规则越宽松,后期生产越稳定”。

布线顺序上,先布最关键的信号:晶振两脚到单片机XTAL1、XTAL2的两条线,尽量短、尽量等长;然后是电源主干线;再是LCD数据线和控制线;最后才是按键矩阵线。晶振下面不要走其他信号线,避免干扰。

铺铜尽量用GND网络铺满整块板子的空余区域,过孔多打几个连接上下层地。这里有一个实用技巧:镀铜后离晶振引脚太近的区域,用“禁止区”把它抠掉,防止铜皮与晶振外壳触碰造成的寄生电容影响起振。

4.4 嘉立创下单:免费打样确实免费,但选项别乱勾

PCB画完,DRC检查全部通过后,点击“生成制造文件”会出来Gerber。嘉立创的打样是直接在网页上传Gerber或者使用EDA的“一键下单”功能。

这里有一个新手容易犯的错:选择板材参数时把“板子层数”默认2层就可以,板厚建议1.6mm,铜厚默认1oz——这些完全够用。真正容易踩的坑在“付款前确认”页面:

  • 数量:打样通常是5片,数量别贪多,5片调试足够。
  • 交期:选“普通工艺”即可,不需要选加急,普通工艺一般两三天出货。
  • 是否需要SMT贴片:如果选择空板(不贴器件),这项必须确认,否则会默认生成钢网,多花几十块钱。
  • 板子颜色:对质量没影响,选什么颜色纯看个人喜好。
  • 阻抗控制:我们这个双面板、低速数字电路完全不需要,选默认就行。

下单前把生成的Gerber文件用嘉立创自带的“Gerber查看器”打开检查一遍,看看有没有板框缺失、线路图层颠倒之类的低级错误。我在第三次打样时就是没检查,结果发现铺铜删错层,到了板子才发现底层铜皮没了。

5. 焊板与调试实录:仿真通过不等于实物一次通过

板子到手,焊接调试才是整个项目最磨人的阶段。无论仿真多完美、DRC多干净,实物板都可能出现仿真里完全没有的问题。这一节就写我在这个计算器板上实际遇到过的故障、排查逻辑和最终解决方式。

5.1 焊接顺序:先电源后芯片,先最小系统后外设

焊接不是从哪块先焊都行。正确顺序是:先焊电源部分,再焊底座和单片机最小系统,然后焊晶振复位电路,确认能下载程序后,再焊LCD和按键。顺序反了会带来一个很痛苦的结果:全部焊完后板子不工作,你不知道是电源问题、单片机问题、还是外设问题,排查起来满板子飞线测电压。

电源部分焊好后,用万用表量一下5V和GND之间是否短路,再上电量各关键点电压。我第一次做板时就是因为电源焊错了一只电容极性,上电后芯片烫手,差点报废。量到单片机40脚VCC和20脚GND有5V,复位脚RESET是0V,晶振两脚电压约相差0.4V,就基本能确定最小系统是活的。

5.2 下载程序环节的坑:为什么连不上单片机

51单片机的下载方式跟芯片型号强相关。AT89S52用ISP下载线,STC89C52RC用串口转TTL下载。我的板子上用的是STC89C52,嘉立创打样回来,焊完后第一次下载就卡在“正在检测目标单片机”。

排查链如下:

  1. 先量板子3.3V和5V有没有,STC89C52供电范围是3.3V~5.5V,低于3.3V连不上。
  2. 用串口线连接时要确认RXD和TXD交叉连接:单片机RXD接USB转TTL的TXD,单片机TXD接USB转TTL的RXD,接反就是死活检测不到。
  3. STC下载有“冷启动”要求:先点下载按钮,再给单片机板子上电,很多连不上都是因为这个先后顺序搞反了。
  4. 如果还是连不上,用示波器或万用表测晶振是否起振,起振后两脚对地电压应该有1.5V~3V的摆动,没有就是晶振或负载电容问题。

我的情况是第二个原因,串口线接反了。换过来后一次通过。

5.3 实物按键的“幽灵按键”:连到74HC165的共地问题

仿真里按键怎么按怎么灵,实物却出现按下一次,屏幕显示两个甚至三个数字。排查后发现是两个原因叠加:一是按键没做RC滤波,手指按下一瞬间接触电阻抖动引起重复触发;二是74HC165的并行输入端在按键按下时瞬间悬空,数据线被干扰。

解决办法分两步。代码层面,在扫描函数里加入“等按键释放再处理下一次”的逻辑,这个在Keil代码部分已经讲过。硬件层面,在每个按键的其中一个引脚到地之间加一个0.1uF电容,做硬件防抖;同时74HC165的PL、CLK、Q7三根信号线在靠近芯片的地方各接一个10k上拉电阻,防止信号受到干扰。我加了电容和上拉后,幽灵按键的现象彻底消失。

5.4 LCD显示乱码和刷新闪烁的处理

LCD乱码通常是数据线接错或者初始化参数不对,我一般是先检查接线板的丝印,确认第4脚RS、第5脚RW、第6脚EN是否连接到正确的IO。刷新闪烁则是LCD驱动里延时给得太短,写入数据过快,解决方法是把每条指令之间的延时长到2ms以上。

如果你的LCD还带了背光,背光的正极要通过一个电阻接5V,不要直连,不然电流可能超过1602背光额定值。

我个人实际操作中比较喜欢先把程序里所有外设功能单独测试:只测LCD能显示数字,只测按键能正确读取键值,最后再组合。这个习惯看起来笨,但省下的排查时间远比多写的测试代码值钱。很多“看起来是单片机问题”的故障,最后发现是外围连线松了或者电容极性焊反,这类低级错误在仿真里永远碰不到,但实物板一定会用这种方式教你做人。

6. 最后再分享几个能提升成功率的小经验

最后一个板块就不做什么总结了,直接分享几个经过这个项目验证的小技巧。

第一,仿真里故意设置错误来测试你的代码健壮性。比如在Proteus里把某个按键按下不放再去按另一个,看看代码会不会卡死;在运算过程中连续按了很多个等号,看看缓冲区会不会溢出。这些“反人类”操作在实物上一定会遇到,提前在仿真里测一遍能省很多焊接后的返工时间。

第二,PCB板载一个LED测试灯。在设计PCB时,我习惯把单片机一个空闲IO脚接一个LED串联电阻引出到板边,比如P3^0。焊接完最小系统后先烧一个闪灯程序,确认IO能正常翻转,再烧计算器完整程序。这个小LED在后续调试里用处极大——程序跑没跑起来、卡没卡死,看一眼LED状态就知道了,比每次都接仿真器查寄存器方便太多。

第三,Keil工程里打开“生成HEX文件”选项,但不要保存中文路径。我用过不少路径含中文的工程,Proteus加载时各种报错,换成英文路径后立刻正常。这属于踩过才记得住的玄学坑,早改早舒服。

这个51单片机计算器项目做完,手里多了一块自己画的、能正常用的板子,那种成就感跟任何开发板实验都不一样。回头再想,这个项目的价值不在于计算器本身——现在随便一个计算器App都比它强——而在于完整过了一遍“代码→仿真→原理图→PCB→焊接调试”的硬件设计闭环。对这个闭环里的每一条经验,前面的章节都已经有自己对应的坑。照着链路走一遍,你收获的不只一块计算器,而是一套以后做其他单片机项目也能复用的方法论。

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

DDR5内存为何必须集成PMIC电源管理芯片

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

作者头像 李华
网站建设 2026/9/28 1:27:44

VS Code PlatformIO创建工程太慢?四招提速到两分钟

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

作者头像 李华
网站建设 2026/9/28 1:27:06

NMEA-0183协议详解:从GPS模块串口数据到经纬度解析实战

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

作者头像 李华
网站建设 2026/9/28 1:27:04

葡萄图像数据集目标检测实战:YOLO格式转换与迁移学习要点

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

作者头像 李华
网站建设 2026/9/28 1:26:57

一键开关机芯片选型指南:从电压电流到低功耗逻辑的四个维度

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

作者头像 李华
网站建设 2026/9/28 1:26:07

Keil5找不到ARM Compiler 5.06?从安装到配置一次性解决AC5报错

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

作者头像 李华