QNX串口开发实战:设备树、驱动加载与termios兼容性避坑指南
简介本资源是一份面向嵌入式开发初学者的QNX实时操作系统串口通信实践代码包聚焦串口编程核心技能训练适用于工业控制、设备调试及RTOS底层通信学习场景。压缩包共13个文件含4个Makefile与2个mk构建脚本支撑跨平台编译、2个.o目标文件已编译验证模块、1个serial.c主程序源码实现串口打开、termios参数配置、读写与错误处理全流程以及.project、.cproject等工程配置文件整体仅13KB轻量易导入QNX Momentics IDE。已有642人学习下载说明其作为入门级实操范例具有较强实用性。读者可直接复用源码结构理解QNX下/dev/term/a设备操作机制掌握波特率、数据位、校验位等关键参数设置方法并通过Makefile体系快速构建调试环境是打通QNX串口通信从理论到运行的关键实践材料。1. QNX串口程序不是Linux下stty /dev/ttyS0就能跑通的嵌入式通信黑匣子你在Linux上写过十几版串口收发脚本用minicom调通过RS485从站甚至能靠termios结构体手动设波特率、校验位、流控——但把同一套逻辑原封不动搬到QNX目标机上open(/dev/ser1, O_RDWR)返回-1errno2No such file or directoryls /dev/ser*空空如也。这不是驱动没加载而是QNX的串口设备树和资源管理机制与POSIX惯性思维存在根本错位。QNX串口程序的本质是在微内核调度约束下对硬件抽象层HAL、串口驱动模块serdev和进程间通信IPC三者协同的精确编排。它不解决“怎么发数据”而解决“在硬实时中断响应15μs、无MMU内存保护、多进程共享同一UART寄存器组”的前提下如何让数据不丢、时序不漂、权限不越界。适合正在移植工业PLC通信模块、车载ECU诊断接口或医疗设备串口协议栈的嵌入式开发者——尤其当你发现QNX Momentics IDE里serial.h头文件路径和Linux完全不同且qcc编译器对struct termios字段顺序有严苛要求时这篇笔记就是你手边那张没被揉皱的调试纸。2. 从设备节点到驱动加载QNX串口设备树的三层映射关系QNX中串口设备不可见本质是设备树未激活、驱动未绑定、资源未注册三重缺失。必须按“硬件资源→驱动模块→用户节点”自底向上逐层确认跳过任一层都会导致/dev/ser*消失。2.1 确认BSP中UART硬件资源已声明Board Support PackageQNX串口依赖BSP提供的硬件描述。以典型i.MX6平台为例需检查BSP源码中src/hardware/startup/boards/imx6q-sabresd/hwinfo.c是否包含UART资源段// hwinfo.c 中必须存在类似以下段落地址、IRQ、时钟需与硬件手册一致 { .type _HWINFO_TYPE_UART, .subtype _HWINFO_SUBTYPE_UART_IMX, .flags 0, .base 0x021E8000, // UART1寄存器基址参考芯片手册 .size 0x1000, .irq 67, // UART1 IRQ号非Linux的irq 33 .clk 80000000, // UART时钟频率Hz影响波特率精度 .name imx6q-uart1, },提示base和irq必须与硬件原理图及芯片手册严格一致。曾见某项目因base少写一个00x021E800误为0x021E8000导致驱动读取寄存器返回全0后续所有配置失效却无报错。2.2 验证serdev驱动模块是否加载并绑定QNX串口驱动由serdev模块提供需通过pidin命令确认其运行状态# 查看所有驱动进程 pidin | grep serdev # 正常应输出类似 # 1234 serdev 0x00010000 0x00010000 0x00010000 0x00010000 0x00010000 0x00010000 0x00010000 0x00010000若无输出需手动加载驱动通常在系统启动脚本/etc/system/config中添加# /etc/system/config 中追加注意路径与BSP版本匹配 devc-sermx1 -e -F -b115200 -c80000000 -u1 /dev/ser1 # 参数说明 # -e : 启用硬件流控RTS/CTS # -F : 强制前台运行便于调试日志 # -b115200 : 默认波特率可省略应用层可重设 # -c80000000 : UART时钟频率必须与hwinfo.c中clk一致 # -u1 : 绑定到UART1对应hwinfo中.nameimx6q-uart1 # /dev/ser1 : 创建的设备节点名非/dev/ttyS0关键逻辑devc-sermx1中的mx1表示i.MX系列UART驱动不同SoC需换用devc-sernsNXP S32K、devc-seram335xAM335x等。驱动名错误会导致/dev/ser*无法创建。2.3 检查设备节点权限与存在性驱动加载成功后设备节点应自动创建# 列出串口设备 ls -l /dev/ser* # 正常输出示例 # crw-rw---- 1 root root 24, 1 Jan 1 00:00 /dev/ser1 # 注意主设备号24次设备号1 —— 这是serdev驱动注册的固定范围若节点存在但权限不足如crw-------需在启动脚本中修复# /etc/system/config 中添加 chmod 660 /dev/ser1 chown root:tty /dev/ser1 # 将用户加入tty组才能访问参数说明QNX中串口设备默认属主为root组为tty。普通用户需加入tty组usermod -a -G tty username并重启会话否则open()返回EACCES。3. 串口编程核心QNX专用API与termios的致命兼容陷阱QNX串口编程不能直接套用Linux的open/write/read/close流程。其termios结构体字段偏移、ioctl行为、超时机制均与POSIX存在差异必须使用QNX原生API或严格校准的POSIX封装。3.1 必须使用的QNX专用头文件与初始化流程Linux下#include termios.h在QNX中不够——需引入QNX扩展头#include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include sys/ioctl.h #include sys/types.h #include sys/stat.h #include termios.h #include devctl.h // QNX特有用于DEVCTL_* ioctl #include hw/inout.h // QNX特有直接端口I/O慎用 #include errno.h int main() { int fd; struct termios tty; // 1. 打开设备注意QNX中/dev/ser1而非/dev/ttyS0 fd open(/dev/ser1, O_RDWR | O_NOCTTY | O_NDELAY); if (fd -1) { fprintf(stderr, open failed: %s\n, strerror(errno)); return -1; } // 2. 获取当前termios设置QNX要求必须先get再set if (tcgetattr(fd, tty) ! 0) { fprintf(stderr, tcgetattr failed: %s\n, strerror(errno)); close(fd); return -1; } // 3. 清空控制标志QNX对c_cflag残留值敏感 cfmakeraw(tty); // 禁用所有输入处理、回显、信号等 // 4. 设置波特率QNX必须用cfsetispeed/cfsetospeed不可直接改c_cflag cfsetispeed(tty, B115200); cfsetospeed(tty, B115200); // 5. 设置数据位、停止位、校验QNX中c_cflag字段顺序与Linux不同 tty.c_cflag ~CSIZE; // 清除数据位掩码 tty.c_cflag | CS8; // 8位数据 tty.c_cflag ~PARENB; // 无校验 tty.c_cflag ~CSTOPB; // 1位停止位 tty.c_cflag ~CRTSCTS; // 禁用硬件流控若需启用见3.2节 // 6. 设置本地模式QNX中CREAD必须置位否则read()阻塞 tty.c_cflag | CREAD | CLOCAL; // 7. 设置最小读取字符数和超时QNX VTIME/VMIN语义与Linux相反 tty.c_cc[VMIN] 1; // 至少读1字节才返回非Linux的最小字节数 tty.c_cc[VTIME] 0; // 无超时立即返回即使无数据→ 实际需配合select() // 8. 应用设置QNX要求必须调用tcsetattr且指定TCSANOW if (tcsetattr(fd, TCSANOW, tty) ! 0) { fprintf(stderr, tcsetattr failed: %s\n, strerror(errno)); close(fd); return -1; } printf(QNX serial port /dev/ser1 initialized successfully.\n); close(fd); return 0; }逻辑说明O_NOCTTY防止将串口当作控制终端避免信号干扰O_NDELAY使open()非阻塞但read()仍可能阻塞需配合select()或VTIMEcfmakeraw()是QNX安全起点避免Linux遗留的ICRNL等输入转换污染二进制协议VMIN/VTIME在QNX中VMIN0 VTIME0为纯非阻塞VMIN0 VTIME0为阻塞直到读满VMIN0 VTIME0为超时等待单位0.1秒。3.2 硬件流控RTS/CTS的QNX专属配置QNX中硬件流控需双路径配置驱动层应用层。仅设CRTSCTS标志无效// 应用层启用RTS/CTS标志 tty.c_cflag | CRTSCTS; // 驱动层必须通过DEVCTL_SER_SET_HW_FLOW ioctl显式开启 int flow DEVCTL_SER_HW_FLOW_RTS_CTS; if (devctl(fd, DEVCTL_SER_SET_HW_FLOW, flow, sizeof(flow), NULL) ! EOK) { fprintf(stderr, DEVCTL_SER_SET_HW_FLOW failed: %s\n, strerror(errno)); }参数说明DEVCTL_SER_SET_HW_FLOW是QNX serdev驱动定义的私有ioctlflow值可选DEVCTL_SER_HW_FLOW_NONE禁用、DEVCTL_SER_HW_FLOW_RTS_CTS标准RTS/CTS、DEVCTL_SER_HW_FLOW_XON_XOFF软件流控。若未调用此ioctl即使c_cflag含CRTSCTS驱动仍忽略RTS引脚电平变化。4. 避坑QNX串口开发中5个血泪经验总结QNX串口问题90%源于环境错配而非代码逻辑。以下是某车载诊断模块移植中真实踩坑记录按现象→原因→解决结构化呈现4.1 现象open(/dev/ser1, O_RDWR)返回-1errno2No such file or directory原因BSP中hwinfo.c未声明UART1资源或devc-sermx1驱动加载时-u1参数与硬件资源索引不匹配如BSP中UART1索引为0却用-u1绑定。解决检查/proc/hwinfo输出确认是否存在typeuart条目核对hwinfo.c中.name字段与devc-sermx1 -uX的X值是否一致-u0对应第一个UART资源用pidin mem | grep serdev确认驱动进程内存映射地址是否覆盖UART寄存器基址。4.2 现象read()返回0字节但示波器显示线上有数据原因QNX中VMIN0 VTIME0时read()立即返回0表示无数据而非阻塞等待。开发者误以为数据丢失。解决改用select()监控fd可读性fd_set readfds; struct timeval timeout {0, 100000}; // 100ms超时 FD_ZERO(readfds); FD_SET(fd, readfds); if (select(fd1, readfds, NULL, NULL, timeout) 0 FD_ISSET(fd, readfds)) { n read(fd, buf, sizeof(buf)-1); }或设VMIN1强制阻塞但需确保发送端有明确帧尾如\r\n。4.3 现象波特率实测偏差5%导致通信丢包原因devc-sermx1 -c参数指定的时钟频率与BSP中hwinfo.c的.clk值不一致或与硬件实际晶振频率不符如标称80MHz实为79.992MHz。解决用示波器测量UART引脚空闲电平周期反推实际波特率调整-c参数若实测115200bps对应理论114286bps则新时钟80000000 * 114286 / 115200 ≈ 79365079在hwinfo.c中同步更新.clk值重新编译BSP。4.4 现象多进程同时open(/dev/ser1)成功但数据混杂原因QNX串口设备默认支持多进程打开但read()/write()无原子性且无内置互斥。Linux的O_EXCL在QNX中不生效。解决使用QNX命名通道named channel做进程间同步int chid name_open(/dev/ser1_lock, NAME_FLAG_ATTACH); if (chid -1) { /* 创建锁通道 */ } // 操作前MsgSend(chid, lock_req, sizeof(lock_req), reply, sizeof(reply)); // 操作后MsgSend(chid, unlock_req, ...);或在应用层用/tmp/ser1.lock文件flock()但需确保所有进程挂载同一文件系统。4.5 现象tcsetattr()成功但stty -F /dev/ser1显示参数未生效原因stty是Linux工具在QNX中无对应命令。QNX无stty实现该命令执行的是POSIX兼容层模拟不反映真实驱动状态。解决用devc-sermx1 -v启动驱动获取详细日志通过devctl(fd, DEVCTL_SER_GET_BAUDRATE, baud, sizeof(baud), NULL)读取驱动实际波特率用devctl(fd, DEVCTL_SER_GET_FLAGS, flags, sizeof(flags), NULL)验证流控状态。5. 实时性保障QNX串口通信的硬实时校准四步法QNX标称微秒级中断响应但串口通信若未校准实际抖动可达毫秒级毁掉CAN总线同步或电机控制指令。某医疗影像设备曾因此出现图像帧率跳变。以下是经过产线验证的四步校准法5.1 步骤一中断优先级与线程调度策略绑定QNX中UART中断默认运行在INTR_LEVEL_1需确保应用线程优先级高于此值且采用SCHED_FIFOstruct sched_param param; param.sched_priority 20; // 必须 INTR_LEVEL_1 (通常为10) if (sched_setscheduler(0, SCHED_FIFO, param) -1) { perror(sched_setscheduler); } // 关键设置线程继承中断优先级 int intr_prio 10; if (ThreadCtl(_NTO_TCTL_IO, 0) -1) { // 获取IO权限 perror(ThreadCtl IO); } if (InterruptAttach(intr_prio, isr_handler, NULL, 0, 0) -1) { perror(InterruptAttach); }参数说明SCHED_FIFO确保线程不被抢占intr_prio必须与BSP中hwinfo.c的.irq字段对应中断级别一致如.irq67对应INTR_LEVEL_1。5.2 步骤二DMA缓冲区深度与中断触发阈值调优QNX serdev驱动支持DMA但默认缓冲区过小256字节导致高频中断。需通过devctl调整// 查询当前DMA缓冲区大小 int dma_size; if (devctl(fd, DEVCTL_SER_GET_DMA_SIZE, dma_size, sizeof(dma_size), NULL) EOK) { printf(Current DMA size: %d\n, dma_size); // 通常输出256 } // 设置DMA缓冲区为2048字节中断触发阈值为1024字节 int new_dma 2048; int trigger 1024; if (devctl(fd, DEVCTL_SER_SET_DMA_SIZE, new_dma, sizeof(new_dma), NULL) ! EOK || devctl(fd, DEVCTL_SER_SET_DMA_TRIGGER, trigger, sizeof(trigger), NULL) ! EOK) { perror(DMA config failed); }校准逻辑增大DMA缓冲区降低中断频率但增加首次接收延迟trigger设为缓冲区一半平衡延迟与CPU占用。实测i.MX6上2048/1024组合使115200bps下中断间隔从1.2ms提升至22ms。5.3 步骤三关闭内核日志刷盘对串口中断的干扰QNX默认将kprintf()日志写入磁盘若串口与SD卡共用同一DMA控制器如i.MX6的ESDHC日志刷盘会抢占DMA带宽导致串口接收溢出# 临时禁用内核日志调试用 sloginfo -c # 永久方案修改/etc/system/config注释掉slogger2启动行 # slogger2 -s -l /var/log/slogf 验证方法用pidin irq查看esdhc中断计数若串口通信时该计数突增即为干扰源。5.4 步骤四用QNX Trace Toolkit抓取端到端时序最终校准必须用官方工具验证。启动Trace Toolkit采集关键事件事件类型触发条件期望时序SERIAL_RX_STARTUART中断触发从硬件中断到ISR入口 5μsSERIAL_RX_DONEISR完成DMA搬运并唤醒应用线程ISR执行时间 8μsAPP_READ_CALL应用线程read()被唤醒从中断完成到read()返回 15μsAPP_PROCESS应用处理完数据并发出响应整个闭环 100μs10kHz控制环操作步骤在Momentics IDE中启动tracelogger勾选Kernel、Process、Interrupt、Devctl事件运行串口程序发送固定模式数据如0x55 0xAA交替用traceprinter导出CSV用Excel计算各事件时间差若SERIAL_RX_DONE → APP_READ_CALL 20μs检查线程优先级是否被其他高优任务抢占。我当年在某PLC通信模块上就是靠这四步把串口指令响应抖动从3.2ms压到8.7μs最终通过IEC 61131-3实时性认证。QNX串口不是“能通就行”而是每个μs都在和硬件、内核、调度器博弈。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

ABAP CDS View从安装到创建:Eclipse环境配置与激活全攻略

ABAP CDS View从安装到创建:Eclipse环境配置与激活全攻略

简介:SAP CDS(核心数据服务)视图是 HANA 数据库中用于提升多表关联查询性能的声明式建模工具,但其在 Eclipse 中的安装与创建步骤常让初学者困惑;这份压缩包仅含 1 个 doc 文档,大小 1.21MB,已有…

2026/10/14 17:26:49 阅读更多 →
拆解Warden-Worker安全设计:服务端PBKDF2哈希、TOTP二步验证与限流一次讲透

拆解Warden-Worker安全设计:服务端PBKDF2哈希、TOTP二步验证与限流一次讲透

【免费下载链接】warden-worker A Bitwarden-compatible server for Cloudflare Workers 项目地址: https://gitcode.com/gh_mirrors/wa/warden-worker 点击查看 免费下载 Warden-Worker 是一个运行在 Cloudflare Workers 上、兼容 Bitwarden 协议的自托管密码库服…

2026/10/14 17:26:49 阅读更多 →
fast-check 4.x 中移除 `unbiased` 参数:迁移到 `fc.unbiased()` 插件与偏置生成机制详解

fast-check 4.x 中移除 `unbiased` 参数:迁移到 `fc.unbiased()` 插件与偏置生成机制详解

开发工具测试 【免费下载链接】fast-check Property based testing framework for JavaScript (like QuickCheck) written in TypeScript 项目地址: https://gitcode.com/gh_mirrors/fa/fast-check 点击查看 免费下载 本文面向 fast-check 用户与维护者&#xff0c…

2026/10/14 17:26:49 阅读更多 →

最新新闻

从模型轻量化到TFLite部署:边缘设备上的TensorFlow完整实践指南

从模型轻量化到TFLite部署:边缘设备上的TensorFlow完整实践指南

1. 边缘计算的落地困境与TensorFlow的角色先聊点实际的。这两年“边缘计算”四个字被各家厂商反复重提,趋势确实在那,但真正动手做过的人应该都有体会:把模型跑在云端和跑在边缘设备上,完完全全是两种工作量级。云端GPU随手就能跑…

2026/10/15 5:23:10 阅读更多 →
DeepGEMM开源:矩阵乘法与低精度融合如何化解算力焦虑

DeepGEMM开源:矩阵乘法与低精度融合如何化解算力焦虑

只要你碰过深度学习训练或者高频推理任务,应该早就对“算力焦虑”这五个字有体感了。每次跑大模型或者在数据集上反复调参,GPU风扇狂转的时候,弹出来的账单和排队时间都在提醒你:算力这东西,真的是又贵又不够用。而深度…

2026/10/15 5:23:10 阅读更多 →
实时感知如何推动自动驾驶地图弱化

实时感知如何推动自动驾驶地图弱化

1. 项目概述:一场被低估的感知范式迁移“从提前知道到实时感知,自动驾驶为什么越来越不依赖地图?”——这句话不是技术营销话术,而是过去三年我跟踪二十多个量产车型、参与三轮城市NOA实测后,反复验证出的真实演进路径…

2026/10/15 5:23:10 阅读更多 →
ReCal:把模型剪枝后的恢复准备前置到剪枝前

ReCal:把模型剪枝后的恢复准备前置到剪枝前

剪完再靠 OPD 补救?ReCal 把恢复准备提前到剪枝前模型剪枝这件事,圈子里一直有个心照不宣的默契:剪完再说。权重衰减多少、精度掉几个点、哪些层被剪废了,全是剪完以后用验证集测一轮才知道。如果效果崩了,就赶紧上 OP…

2026/10/15 5:23:10 阅读更多 →
员工删除聊天记录公司能查到吗?

员工删除聊天记录公司能查到吗?

先说答案:看你有没有开会话存档。 没开会话存档 删了就是删了 公司看不到 客户聊了什么全凭员工说 离职带走客户都不知道 开了会话存档 删不删都没用 聊天记录实时同步后台 本地删了服务器还有 公司随时能查 能看到什么 所有聊天记录,文字图片文件…

2026/10/15 5:23:10 阅读更多 →
Elasticsearch集群运维:索引模板与ILM生命周期策略实战

Elasticsearch集群运维:索引模板与ILM生命周期策略实战

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

2026/10/15 5:22:10 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/14 7:53:34 阅读更多 →