尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于pyserial与自定义帧协议的LCD1602串口上位机开发实战
1. 为什么我要自己写一个串口上位机而不是用现成工具市面上串口调试助手一抓一大把随便搜一下就能找到十几种功能从基础的收发到协议解析、波形绘制应有尽有。那我为什么还要花时间自己写一个原因其实很朴素通用工具解决的是通用问题而我要解决的是一个非常具体的、带交互逻辑的显示控制问题。LCD1602这块屏搞过单片机的人基本都接触过。它本身很简单两行十六个字符基于HD44780控制器支持并行和四线模式。但当你把它接到单片机或者开发板上想动态地往屏幕上推内容的时候如果每次都要改代码、重新编译、烧录这个循环就太长了。尤其是调试阶段你可能只是想看看某个变量当前的值或者临时改一下显示的文字结果每次都要走一遍完整的编译烧录流程效率极低。所以我想要的是一套从PC端直接控制LCD1602显示内容的方案。PC端跑一个自己写的上位机通过串口把要显示的内容发给下位机下位机收到之后直接刷到LCD上。这样我就可以在PC上实时地改显示内容不用碰下位机的代码。那为什么不用现成的串口助手因为现成的工具只能发原始数据它不知道我要发的是一条“显示指令”还是别的什么。我需要的是一个带协议封装的上位机——我输入要显示的文字它自动打包成下位机能识别的帧格式发出去。这个逻辑用通用串口助手也能做但你每次都要手动拼帧、算校验太麻烦了。自己写一个上位机把这些逻辑固化进去用起来才顺手。另外还有一个原因我想把整个链路搞清楚。从PC端的串口编程到数据帧的设计到下位机的解析和LCD驱动这一整条链路走通一遍对串口通信和LCD控制的理解会深很多。这比单纯调用一个库函数要有价值得多。这篇文章适合哪些人看如果你正在学单片机、想了解串口通信的完整链路、或者手头正好有LCD1602想玩点不一样的东西那这篇内容应该能给你一些参考。如果你已经是串口通信的老手也可以看看我在帧协议设计和异常处理上的一些取舍或许有可以借鉴的地方。2. 串口通信链路的整体设计思路2.1 从上位机到下位机的数据流向整条链路其实不复杂但每个环节都有需要想清楚的地方。先看数据是怎么流的PC端上位机Python pyserial→ USB转串口模块 → 下位机串口接收 → 帧解析 → LCD1602显示上位机这边我用Python来写核心库就是pyserial。选Python的原因很简单开发快、调试方便、跨平台。pyserial这个库封装了底层的串口操作打开串口、配置波特率、读写数据都是几行代码的事。而且Python的字符串处理能力很强做协议封装和解析很顺手。下位机这边我用的是常见的8位单片机串口接收用中断方式收到完整帧之后解析然后驱动LCD1602。LCD1602我用的是4位数据模式这样只需要6根IO口RS、RW、E、D4-D7就能驱动省下来的IO口可以干别的。这里有一个关键的设计决策上位机和下位机之间传什么有两种方案方案A上位机直接发ASCII字符下位机收到什么就显示什么方案B上位机发结构化的数据帧包含命令字、地址、数据、校验方案A最简单但灵活性差。比如我想控制光标位置、清屏、开关显示这些操作没法用纯ASCII表达。方案B更复杂但扩展性强后面想加什么功能都方便。我选的是方案B的简化版自定义一个轻量级的帧协议既能传显示内容也能传控制命令。2.2 为什么选pyserial而不是其他方案Python下面操作串口其实不止pyserial一个选择。但pyserial是事实上的标准库社区活跃、文档齐全、跨平台支持好。我在Windows和Linux上都跑过行为一致没有遇到什么兼容性问题。安装很简单pip install pyserial就这一条命令。装完之后import serial就能用了。有一个细节需要注意pyserial的版本。如果你用的是比较老的版本比如2.xAPI和3.x有一些差异。现在默认装的都是3.x用serial.Serial()创建对象参数名是port、baudrate这些。老版本有些参数名不一样网上搜到的代码如果跑不起来先检查一下版本。另外在Windows上串口号是COM1、COM3这种格式在Linux上通常是/dev/ttyUSB0或者/dev/ttyACM0。写代码的时候最好把串口号做成可配置的不要写死。2.3 下位机串口接收的中断处理逻辑下位机这边串口接收我用的是中断方式。为什么不轮询因为轮询会占用主循环的时间而且如果波特率稍微高一点轮询很容易丢数据。中断方式下每收到一个字节就进一次中断把数据存到缓冲区里主循环再慢慢处理。中断服务函数里做的事情要尽量少只负责把接收到的字节存入环形缓冲区然后更新缓冲区指针。帧的解析放到主循环里做这样不会因为解析逻辑耗时太长而影响后续数据的接收。环形缓冲区的大小要算一下。假设波特率是9600一帧数据最长20个字节那么缓冲区至少要有两帧的容量也就是40个字节以上。我一般会开到64字节或者128字节留足余量。如果波特率更高比如115200那缓冲区还要再大一些因为同样时间内收到的数据量更大。还有一个容易忽略的点帧与帧之间的间隔。如果上位机连续快速发送多帧下位机的中断可能会在很短时间内收到大量数据。如果主循环处理不过来缓冲区就会溢出。解决办法有两个一是加大缓冲区二是上位机发送时加适当的延时。我一般两个都用缓冲区开到128字节上位机每帧之间加10ms延时实测下来很稳。3. 自定义帧协议的设计与取舍3.1 帧格式的字段定义帧协议的设计是整个项目的核心。设计得好后面扩展方便设计得不好用起来处处别扭。我最终定的帧格式是这样的字段长度说明帧头2字节固定为0xAA 0x55用于帧同步命令字1字节0x01显示字符串0x02清屏0x03设置光标0x04开关显示数据长度1字节后续数据段的字节数数据段N字节具体的数据内容校验和1字节从命令字到数据段所有字节的累加和取低8位帧头用两个字节而不是一个字节是为了降低误同步的概率。如果只用一个0xAA做帧头数据段里出现0xAA就会误判。用两个字节0xAA 0x55数据段里连续出现这两个字节的概率就低很多了。命令字目前定义了四个显示字符串、清屏、设置光标位置、开关显示。后面如果需要还可以扩展比如控制背光、自定义字符等。数据长度字段告诉下位机后面有多少个字节的数据。这样下位机就知道要收多少个字节才算一帧完整的数据。校验和用的是最简单的累加和。为什么不用CRC因为在这个场景下传输距离短、数据量小、环境干扰有限累加和足够用了。CRC计算量大在下位机上跑有点浪费。当然如果你对可靠性要求特别高换成CRC16也不是不行但代码会复杂一些。3.2 帧头选择的考量为什么是0xAA 0x550xAA和0x55这两个值在二进制下是交替的0xAA是101010100x55是01010101。这种交替模式在串口通信里有个好处方便接收方做位同步。虽然串口本身是异步通信每个字节有起始位和停止位来做同步但在数据流中频繁出现交替模式对接收方的时钟恢复也有帮助。另外0xAA和0x55在ASCII里不是可打印字符正常显示内容里不会出现这两个字节的连续组合。所以用它们做帧头误判的概率很低。还有一个细节帧头之前可以有任意垃圾数据。下位机的解析逻辑是先在缓冲区里找0xAA找到之后再检查下一个字节是不是0x55。如果是就从这里开始解析一帧如果不是就继续往后找。这样即使前面有一些杂散数据也不会影响帧的解析。3.3 校验和的计算方式与容错策略校验和的计算很简单就是把命令字、数据长度、数据段的所有字节加起来取低8位。上位机发送前算好下位机收到后重新算一遍对比是否一致。def calc_checksum(data): return sum(data) 0xFF下位机那边如果校验和不匹配直接丢弃这一帧不做任何处理。同时可以做一个错误计数如果连续多帧校验失败说明通信质量有问题可以考虑让下位机通过某种方式反馈比如点亮一个LED。不过在这个项目里我没有做反馈机制因为LCD本身就能显示状态如果显示不正常一眼就能看出来。有一个容易踩的坑数据段里如果包含0xAA 0x55怎么办比如我要显示的文字里恰好有这两个字节。在ASCII文本里0xAA和0x55不是可打印字符所以正常显示内容不会包含它们。但如果你要传二进制数据就需要做转义处理。我的做法是在数据段中如果遇到0xAA就转义成0xAA 0x00如果遇到0x55就转义成0x55 0x00。接收方解析时遇到0xAA后面跟0x00就还原成0xAA遇到0x55后面跟0x00就还原成0x55。这样虽然增加了数据量但保证了帧的完整性。不过在这个项目里我传的都是ASCII文本所以转义逻辑实际上没有用到。但设计的时候要考虑进去后面万一要传二进制数据就不用改协议了。4. 上位机pyserial代码的完整实现4.1 串口初始化与参数配置上位机的核心就是pyserial的使用。先看串口初始化的代码import serial import serial.tools.list_ports import time class LCDController: def __init__(self, portNone, baudrate9600, timeout1): if port is None: port self.auto_detect_port() self.ser serial.Serial( portport, baudratebaudrate, bytesizeserial.EIGHTBITS, parityserial.PARITY_NONE, stopbitsserial.STOPBITS_ONE, timeouttimeout ) time.sleep(2) # 等待串口稳定 def auto_detect_port(self): ports serial.tools.list_ports.comports() for p in ports: if USB in p.description or CH340 in p.description: return p.device if ports: return ports[0].device raise Exception(未找到可用串口)几个关键参数说明一下baudrate波特率我用的9600。这个要和下位机一致不一致的话收到的就是乱码。9600在短距离传输下很稳定没必要追求高速。bytesize数据位8位。这是最常用的配置。parity校验位无校验。因为我在帧协议里已经做了校验串口本身的校验位就不需要了。stopbits停止位1位。标准配置。timeout读超时1秒。这个参数影响read()的行为设成1秒意味着如果1秒内没有收到数据read()就会返回。time.sleep(2)这一行很重要。串口打开之后下位机那边可能还在复位或者初始化如果立刻发数据可能会丢失。等2秒让下位机稳定下来实测下来这个延时是必要的。自动检测串口的逻辑用到了serial.tools.list_ports这个模块可以列出当前系统上所有可用的串口。我根据描述里是否包含“USB”或“CH340”来判断哪个是目标串口。CH340是最常见的USB转串口芯片之一很多开发板都用它。4.2 帧封装函数的编写帧封装就是把要发送的数据打包成前面定义的格式def build_frame(self, cmd, datab): frame bytearray() frame.append(0xAA) frame.append(0x55) frame.append(cmd) frame.append(len(data)) frame.extend(data) checksum sum(frame[2:]) 0xFF frame.append(checksum) return bytes(frame)这个函数接收命令字和数据返回完整的帧字节串。注意校验和是从命令字开始算的不包括帧头。因为帧头是固定的不需要校验。发送显示字符串的调用方式def display_string(self, text, row0, col0): # 先设置光标位置 self.send_command(0x03, bytes([row, col])) # 再发送显示内容 data text.encode(ascii) self.send_command(0x01, data)这里分两步先设置光标再显示内容。也可以合并成一帧但分开更灵活后面如果只想改光标位置不显示内容也可以单独调用。4.3 发送逻辑与异常处理发送数据的核心方法def send_command(self, cmd, datab): frame self.build_frame(cmd, data) try: self.ser.write(frame) self.ser.flush() except serial.SerialException as e: print(f发送失败: {e}) self.reconnect()flush()的作用是确保数据全部写出去了再返回。不加flush()的话数据可能还在缓冲区里程序就继续往下走了。虽然大多数情况下不加也没问题但加上更保险。异常处理主要是捕获SerialException比如串口被拔掉了、被其他程序占用了都会抛这个异常。捕获到之后我尝试重新连接。重连的逻辑就是关掉当前的串口对象重新打开def reconnect(self): try: self.ser.close() except: pass time.sleep(1) self.ser serial.Serial( portself.ser.port, baudrateself.ser.baudrate, timeout1 ) time.sleep(2)重连之后同样要等2秒让下位机稳定。还有一个实际使用中遇到的问题如果上位机发送速度太快下位机处理不过来。我在send_command里加了一个小延时time.sleep(0.01) # 10ms延时10ms听起来不多但足以让下位机的中断服务函数把数据收完主循环开始处理。如果不加这个延时连续发送多帧的时候下位机的缓冲区很容易溢出。5. 下位机LCD1602驱动与帧解析的配合5.1 LCD1602的4位模式初始化时序LCD1602的4位模式初始化是有固定时序的这个时序不能乱。HD44780的数据手册里写得很清楚但第一次搞的人很容易在这里翻车。初始化流程大概是这样的上电后等待15ms以上发送0x038位模式等待4.1ms再发送0x03等待100us再发送0x03等待100us发送0x02切换到4位模式发送0x28设置4位模式、2行显示、5x8点阵发送0x0C开显示、关光标、不闪烁发送0x06设置输入模式光标右移、屏幕不移动发送0x01清屏等待1.52ms以上这里面有几个时间参数是硬性要求不能缩短。特别是最后清屏的1.52ms如果不等够清屏命令可能没执行完就发下一条命令屏幕就会显示异常。我在代码里把这些延时都写死了void lcd_init(void) { delay_ms(15); lcd_write_cmd(0x03); delay_ms(5); lcd_write_cmd(0x03); delay_us(150); lcd_write_cmd(0x03); delay_us(150); lcd_write_cmd(0x02); delay_us(150); lcd_write_cmd(0x28); delay_us(150); lcd_write_cmd(0x0C); delay_us(150); lcd_write_cmd(0x06); delay_us(150); lcd_write_cmd(0x01); delay_ms(2); }注意这里的lcd_write_cmd在4位模式下要分两次写先写高4位再写低4位。每次写之前要确保LCD不忙检查忙标志位或者用足够的延时来等待。我用的方法是延时等待而不是查忙标志位。查忙标志位需要把数据口设为输入模式读完之后再设回输出模式比较麻烦。用延时的话只要延时足够长就不会有问题。对于1602来说大多数命令的执行时间在40us左右清屏和归位命令需要1.52ms。我统一用150us的延时清屏用2ms实测下来很稳。5.2 串口中断接收与主循环解析的分工下位机的串口接收和帧解析是分开的。中断服务函数只做一件事把收到的字节存入环形缓冲区。volatile uint8_t rx_buf[128]; volatile uint8_t rx_head 0; volatile uint8_t rx_tail 0; void uart_isr(void) { uint8_t data UART_DATA_REG; uint8_t next (rx_head 1) % sizeof(rx_buf); if (next ! rx_tail) { rx_buf[rx_head] data; rx_head next; } // 如果缓冲区满了丢弃这个字节 }主循环里做帧解析void process_frame(void) { while (rx_tail ! rx_head) { uint8_t byte rx_buf[rx_tail]; rx_tail (rx_tail 1) % sizeof(rx_buf); // 状态机解析 parse_byte(byte); } }解析用一个状态机来实现状态包括等待帧头1、等待帧头2、等待命令字、等待长度、接收数据、等待校验和。每个状态收到不符合预期的字节就回到初始状态重新找帧头。这种状态机的好处是不会因为某一帧出错而卡死。即使中间丢了几个字节状态机也能自动恢复到找帧头的状态继续解析后面的帧。5.3 显示命令的解析与执行解析完一帧之后根据命令字执行对应的操作void execute_command(uint8_t cmd, uint8_t *data, uint8_t len) { switch (cmd) { case 0x01: // 显示字符串 for (uint8_t i 0; i len; i) { lcd_write_data(data[i]); } break; case 0x02: // 清屏 lcd_write_cmd(0x01); delay_ms(2); break; case 0x03: // 设置光标 if (len 2) { uint8_t row data[0]; uint8_t col data[1]; uint8_t addr (row 0) ? (0x80 col) : (0xC0 col); lcd_write_cmd(addr); } break; case 0x04: // 开关显示 if (len 1) { lcd_write_cmd(data[0] ? 0x0C : 0x08); } break; } }设置光标位置的时候LCD1602的地址计算要注意第一行起始地址是0x80第二行起始地址是0xC0。所以第一行第col列的地址是0x80col第二行是0xC0col。col的范围是0到15。显示字符串的时候如果字符串长度超过一行剩余的空间LCD会自动换行吗不会。LCD1602的显示RAM是连续的第一行0x80-0x8F第二行0xC0-0xCF。如果你在第一行第15列写了一个字符再写下一个字符它会写到第二行第0列因为0x8F的下一个地址是0xC0。但这个行为不是“自动换行”而是地址递增的结果。如果你不想让它跨行就要自己控制长度。6. 联调过程中踩过的坑与排查方法6.1 串口打开失败与权限问题在Linux上跑上位机的时候第一次打开串口经常会遇到权限问题Permission denied: /dev/ttyUSB0这是因为普通用户没有权限访问串口设备。解决办法有两个一是用sudo运行但不推荐因为这样会把整个程序提权二是把当前用户加到dialout组里sudo usermod -a -G dialout $USER然后重新登录或者重启权限就生效了。这个坑我踩过好几次每次换新机器都要重新设置一遍。在Windows上串口被其他程序占用是常见问题。比如你开着一个串口助手又跑上位机就会报“拒绝访问”。解决办法就是关掉其他占用串口的程序。有时候串口助手关了但进程还在后台需要在任务管理器里结束进程。6.2 乱码问题的三种可能原因串口通信出现乱码原因通常有三种第一种波特率不匹配。这是最常见的。上位机设的9600下位机也必须是9600。如果下位机用的是内部RC振荡器时钟精度不够波特率可能会有偏差。偏差太大的话收到的数据就是乱码。解决办法是改用外部晶振或者把波特率调低一些比如4800降低对时钟精度的要求。第二种数据位、停止位、校验位配置不一致。上位机设的是8N1下位机也必须是8N1。这个一般不会错但如果你用的是某个现成的例程它可能默认是7位数据位或者有校验位那就对不上了。第三种地线没接。串口通信需要共地。如果上位机和下位机的地线没有连在一起信号电平就没有参考点收到的数据也是乱的。这个问题在只用USB供电的时候不容易发现因为USB线里已经包含了地线。但如果你用的是独立的电源给下位机供电就一定要把地线连起来。排查的时候我一般先用一个简单的回环测试把上位机的TX和RX短接发什么就应该收到什么。如果回环测试正常说明上位机和串口线没问题问题在下位机那边。如果回环测试都不正常那就是上位机或者串口线的问题。6.3 LCD显示异常花屏、缺划、偏移LCD1602显示异常有几种典型表现花屏屏幕上出现一堆乱码字符。原因通常是初始化时序不对或者数据线接触不良。先检查初始化代码里的延时够不够再检查接线有没有松动。缺划某个字符的某一段不显示。这通常是数据线的问题比如D4-D7中某根线没接好。因为4位模式下一个字节分两次传如果某根数据线有问题高4位或低4位就会出错。偏移显示的内容位置不对比如应该在第一行显示的跑到了第二行。这通常是光标地址设置错了。检查地址计算第一行是0x80col第二行是0xC0col。col从0开始不要搞错。还有一个我遇到过的问题显示内容闪烁。原因是清屏太频繁。每次显示新内容之前都清屏屏幕就会闪。解决办法是不要每次都清屏而是用空格覆盖原来的内容或者只更新变化的部分。6.4 数据丢帧的定位过程联调的时候遇到过一个烦人的问题连续发送多帧下位机偶尔会丢帧。表现是LCD上显示的内容偶尔会缺几个字符或者光标位置不对。排查过程是这样的第一步确认上位机有没有发出去。我在send_command里加了打印每次发送都输出帧的十六进制内容。确认上位机确实发了。第二步确认下位机有没有收到。我在下位机的中断服务函数里加了一个计数器每收到一个字节就加一。通过某种方式比如用另一个串口输出或者用LED闪烁把计数器读出来。发现收到的字节数比发送的少说明确实丢了。第三步定位丢在哪里。我加大了环形缓冲区从64字节加到128字节丢帧概率降低了但没有完全消失。后来发现是主循环处理太慢中断频繁触发的时候缓冲区还是会被填满。最终的解决办法是上位机加延时。每帧之间加10ms给下位机足够的时间处理。加上延时之后丢帧问题彻底消失了。这个延时看起来简单但确实有效。因为下位机的主循环里除了处理串口数据还要刷LCD刷LCD本身就需要时间每个字符大概几十微秒如果数据来得太快主循环根本来不及处理。7. 实测效果与可扩展的方向7.1 实际运行效果与性能数据整套系统跑起来之后实测效果是这样的波特率9600下发送一条16字符的显示命令从PC发出到LCD显示出来延迟大约在20-30ms左右。这个延迟人眼基本感觉不到实时性够用。连续发送100帧每帧间隔10ms下位机接收并显示没有出现丢帧或显示错误。长时间运行连续几个小时系统稳定没有出现死机或显示异常。性能瓶颈主要在下位机的LCD刷新速度。LCD1602的写操作需要一定的时间尤其是清屏命令需要1.52ms。如果频繁清屏整体刷新率就会下降。所以我在设计上尽量避免清屏只更新变化的部分。7.2 可以继续扩展的功能这套基础框架搭好之后后面想加功能就很方便了。几个我考虑过的扩展方向自定义字符LCD1602支持8个自定义字符每个字符是5x8的点阵。可以通过扩展命令字让上位机把自定义字符的点阵数据发下去下位机写入CGRAM。这样就能显示一些特殊符号比如箭头、图标等。背光控制如果LCD模块有背光控制引脚可以加一个命令字来控制背光开关。不过这个需要硬件支持普通的1602模块背光一般是直接接电源的。多行滚动通过定时发送设置光标命令让显示内容滚动起来。这个可以在上位机做定时改变光标位置下位机不需要改。协议升级如果以后要传二进制数据可以把校验和换成CRC16增加转义机制。不过对于文本显示来说现在的协议已经够用了。上位机界面现在上位机是命令行调用的可以加一个简单的GUI用tkinter或者PyQt做成一个带输入框和发送按钮的小工具。这样用起来更直观不用每次都改代码。7.3 一些个人经验总结这个项目做下来有几个体会比较深协议设计要留余量。我一开始只定义了显示字符串和清屏两个命令后来想设置光标位置发现没有对应的命令字只能临时加。如果一开始就把命令字设计得宽裕一些后面扩展就不用改协议版本了。延时不是坏事。在PC端编程的时候我们习惯追求速度能不加延时就不加。但在嵌入式联调的时候适当的延时反而是保证稳定性的关键。上位机发太快下位机处理不过来加个10ms延时问题就解决了。状态机是解析不定长帧的最好方式。用状态机解析帧代码清晰容错性好。即使中间丢了字节状态机也能自动恢复。比用固定偏移去取字节要可靠得多。调试信息要能输出。下位机没有屏幕的时候调试信息只能靠LED或者另一个串口输出。我在调试阶段用了一个IO口接LED每收到一帧就翻转一次通过LED的闪烁频率就能判断有没有收到数据。这个方法虽然原始但很有效。先跑通再优化。一开始不要追求完美的代码结构先把链路跑通看到LCD上显示出正确的字符再回头优化代码。如果一开始就想着把代码写得多么优雅可能半天都看不到效果容易失去信心。这套方案的核心思路其实可以迁移到很多类似的场景只要是通过串口控制某种外设都可以用“自定义帧协议上位机封装下位机状态机解析”这个模式。把LCD1602换成其他模块比如OLED、步进电机、继电器板整体框架不用大改只需要改命令字和执行逻辑就行了。
RELATED

相关推荐

AI编程三大方法论:契约型、增强型与范式型工程实践

AI编程三大方法论:契约型、增强型与范式型工程实践

1. 这不是工具对比,而是一场关于“AI如何真正理解人”的方法论分野最近在三个团队的代码评审会上,我连续看到三份完全不同的PR描述:一份用SpecKit生成的接口契约文档里,字段约束写得比Swagger还细,但业务逻辑注释全是空…

📅 2026/10/16 4:51:38
free-claude-code全解析:免费使用Claude编程的实战指南

free-claude-code全解析:免费使用Claude编程的实战指南

被标题吸引进来的人,可能和我当时一样:第一反应是“真有人能把付费的AI编程工具做成完全免费版?”;第二反应是“这东西靠不靠谱,拿过去写代码会不会悄悄夹带点什么”。我用了一整个周末,把 “free-claude-c…

📅 2026/10/16 4:46:38
SLAM轨迹精度评估实战:EVO量化VINS-Mono在EuRoC上的表现

SLAM轨迹精度评估实战:EVO量化VINS-Mono在EuRoC上的表现

上个月我把一个自己小幅修改过的VINS-Mono分支在EuRoC MAV数据集上完整测了一轮,用EVO工具把精度和性能都量化成了报表和图表。标题里的"SLAM 3"可以理解成SLAM系列方案的第三轮评估,VINS-Mono正是这轮的主角。这篇文章就是那次评估的完整记录…

📅 2026/10/16 4:46:38
MORE NEWS

更多资讯

📰

Windows笔记本Pytorch CPU版安装指南:从零到跑通

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

📰

双节锂电池主动均衡方案:MP2642GR-Z与PIC18LF47K40协同设计

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

📰

Elasticsearch 指标聚合详解:cardinality、stats、percentiles 与 string_stats

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

📰

MP2642GR-Z与ATmega6450协同实现高可靠电池均衡

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

📰

Zabbix告警收不到?用Python脚本实现稳定SMTP邮件告警配置指南

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

📰

A*寻路算法从原理到实战:启发函数、性能调优与避坑指南

/* 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

本月热门

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

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

📞 💬