从LED看STM32寄存器编程:地址、位运算与外设配置
第一次写STM32寄存器代码我记得是从点亮一颗LED开始的。当时库函数封装得已经很好直接调一行GPIO_WriteBit就能亮灯可我盯着那行封装看半天硬是想不明白它凭什么能把灯点亮。后来翻到参考手册的寄存器章节把外设地址和位域定义一点点对着代码看才真正明白库函数背后那些-操作符、宏定义、位运算到底在干什么。这种感觉就像一直开着自动挡汽车突然有一天下决心去弄懂离合器和换挡逻辑。这本“STM32寄存器白话手册”不是什么权威手册的替代品而是想帮那些刚接触寄存器、被一堆英文缩写和十六进制地址劝退的朋友把寄存器这层窗户纸捅破。这篇文章适合两类人一类是刚开始学STM32只会抄库函数但想弄清底层原理的新手另一类是调库函数调到头大遇到诡异问题不知道怎么定位的开发者。我会用最直白的话解释寄存器地址、位运算、时钟使能、GPIO配置再带你把定时器和串口的寄存器配置思路走一遍。读完之后你再去翻官方参考手册会发现里面的表格其实没那么吓人。1. 寄存器到底是个啥先别背把它当成带锁的邮箱1.1 地址就是“门牌号”寄存器就是“控制面板”芯片内部的处理器和外设之间靠什么沟通靠地址。每一个可供CPU访问的存储单元都有一个地址而寄存器就是一堆挂到特定地址上的存储单元只不过它们里面存的每一个位都有明确定义读写之后会影响外设的实际行为。你可以把寄存器想成“带锁的邮箱”邮箱挂在一条叫“总线”的街上每个邮箱都有门牌号地址CPU要写信就往门牌号里扔要读信就从门牌号里取。但邮箱里的信纸不是随便写的哪一格写什么内容硬件设计者都在手册里规定了。比如某一位代表“使能这个外设的时钟”另一位代表“把引脚配成输出模式”。你把这些位拼好写进去外设就按你的意思工作。我在给新人讲的时候常打一个比方寄存器有点像家用电器面板上的旋钮和指示灯。旋钮是CPU能写的位指示灯是CPU能读的位。你不在意面板后面的电路只按标注操作就能控制空调、微波炉。STM32的寄存器也一样手册就是那块面板上的标注说明只不过标注用的是ENABLE、MODE、RESET这种词。1.2 为什么从寄存器入手反而容易理解单片机很多人会问既然库函数方便为什么还要写寄存器我的体会是寄存器不是用来替代库函数的而是用来理解单片机工作逻辑的。库函数本质上就是一组操作寄存器的代码组合。如果你不知道某项功能操作了哪个寄存器、置了哪个位遇到配置不生效、引脚没反应、中断起不来的问题时就只能靠猜。而寄存器方式让你每一步都明确这一步打开时钟那一步改模式再一步输出电平。排查问题的时候可以按地址逐个检查比黑盒调试踏实得多。另外有些功能用库函数的默认配置反而绕比如快速翻转引脚、做位带操作、低延迟控制通常都要直接访问寄存器。至少我见过的很多底层驱动程序最后还是直接写寄存器。这不是为了显得技术高深而是效率高、意图清晰。理解了寄存器再回头用库函数反而是降维操作。2. 打开手册先练“三查”基地址、偏移量、位定义2.1 从哪里开始读先看时钟树和总线图我见过太多人一上来就找某个外设的寄存器列表结果被几十个寄存器砸晕。正确的顺序应该是先看芯片的“总线图”和“时钟树”。STM32的内部外设不是全部挂在一根总线上而是分散在AHB、APB1、APB2等不同总线上。不同总线对应不同的基地址范围访问外设之前还要确认外设挂在哪条总线因为这直接决定你要使能哪个时钟寄存器。拿一个很常见的芯片型号来说GPIOA挂在APB2总线上它的基地址是0x40010800GPIOB挂在APB2基地址是0x40010C00而定时器TIM2挂在APB1上基地址是0x40000000。这些地址不用死记打开芯片参考手册的“内存映射”章节就能查到。我习惯把这页截图放在项目文件夹里写代码的时候随时翻。2.2 寄存器地址怎么算基地址加偏移刚接触寄存器的人最容易卡在“地址怎么来”这个问题上。其实很简单外设的基地址加上每个寄存器的偏移量就是这个寄存器的绝对地址。举例GPIOB的基地址是0x40010C00手册里告诉你CRH寄存器的偏移量是0x04那么GPIOB的CRH寄存器地址就是0x40010C00 0x04 0x40010C10在C语言里你通常会用一个指向整型数的指针来访问它#define GPIOB_CRH (*(volatile unsigned int *)(0x40010C10))volatile在这里千万不能丢它告诉编译器这个地址里的内容可能被硬件修改不要做优化缓存。这条后面我会专门讲因为太多人栽在这上面。2.3 手册里的“位”怎么读一列一列讲清楚打开官方参考手册随便翻到一个寄存器会发现表格分为好几列偏移地址、位序号、位名称、复位值、读/写属性、描述。我第一次看这个表也是一脸懵后来总结出一个“三看”习惯先看偏移地址确认寄存器在整个外设基地址上的位置。再看位序号和位名称确认我要操作的是哪一位。最后看复位值和描述确认上电默认是什么状态写入什么值才生效。比如GPIO的CRH寄存器里面按4个位一组对引脚8~15进行配置每组包含MODE和CNF两个字段。要配置某个引脚你不仅要找到对应的4位位域还要保证写入的模式值和配置值符合表里的编码。这种“查表填值”的过程本身就是寄存器编程的核心动作。3. 位运算就是寄存器的母语置位清零判断的底层逻辑3.1 置位用|清零用~翻转用^寄存器的一位通常只改对应的Bit其他位要保持原样。所以直接赋值reg 0x01是危险的它会把整个寄存器其他位置清零。正确做法是用位运算。把某一位置1就用“按位或”reg | (1 4); // 把reg的第4位置1把某一位置0就用“按位与 按位取反”reg ~(1 4); // 把reg的第4位清0需要多位一起操作时可以组成一个“位掩码”。比如要把CRH里面引脚9对应的MODE字段写成输出模式有两种写法一种是直接清掉再填GPIOB_CRH ~(0xF 20); // 先清这个4位位域 GPIOB_CRH | (0x1 20); // 再写入新值另一种是读出原值、改好后一并写回。第二种看起来只写了一次寄存器但实际上处理的是软件里的临时值对硬件而言没有本质差别反而更容易出错。我一般推荐先清再置逻辑清楚。翻转某一位用“按位异或”reg ^ (1 3); // 每执行一次第3位翻转一次想要LED在定时中断里来回翻转这种写法最常用比先读ODR再改ODR直观多了。3.2 读状态时别忘了屏蔽无关位读寄存器判断状态的时候新手最容易犯的错误是直接拿整个寄存器的值和某个状态比较。比如读GPIO的IDR想知道引脚5是不是高电平if (GPIOB_IDR (1 5)) // 结果为非0说明bit5为1 { // 引脚是高电平 }这里必须用“与”运算把bit5单独提取出来。如果你写成if (GPIOB_IDR (1 5))那只有引脚5为高且其他引脚全为低时条件才成立基本等于是个写错的判断。读外设状态寄存器也是这样尤其看中断标志位。正确姿势永远是“先屏蔽再判断”把关心的位单独拿出来看。3.3 位带操作给每一位起一个小名STM32的部分芯片支持“位带区”也就是把每个位映射到另一个地址空间里的一个字。你可以把一个bit当作一个完整的32位变量来读写代码会变得非常简洁#define LED_PIN_BIT ((*(volatile unsigned int *)0x42000000 (0x40010C00 0x0C) * 32 5 * 4))这段宏看起来很复杂但它的意思就是“GPIOB_ODR的第5位被映射成了一个变量”。对它赋值就是置位或清零读它就等于读该引脚电平。位带操作适合用在实时性要求比较高的场景省去了读-改-写三步变成一条编译指令。不过别处处都玩位带它让代码可读性下降很多定位问题的时候光找地址就够喝一壶的。4. 点亮一颗LED要动三个寄存器完整走一遍4.1 RCC时钟使能外设没电配置白搭STM32的外设都有独立的时钟开关不用它的时候默认是关着的。比如GPIOB你要用它的引脚第一步不是配置引脚模式而是先让GPIOB的时钟跑起来。RCC复位与时钟控制寄存器里有一个APB2ENR对应APB2总线上的外设使能。GPIOB在APB2总线上APB2ENR的第4位是它的使能位于是RCC_APB2ENR | (1 4);这一步如果不做后面写CRH、ODR全都无效因为GPIOB这个外设压根没通电。这里也解释了为什么很多库例程里第一行总是开时钟——不是为了走流程是硬件设计的根本要求。4.2 GPIO模式寄存器输入输出模式怎么填假设LED接在PB5和地之间高电平点亮。GPIOB的引脚5属于低8位引脚对应的配置寄存器是CRL偏移0x00。我习惯先定义两个干净的点用宏#define RCC_APB2ENR (*(volatile unsigned int *)(0x40021000 0x18)) #define GPIOB_CRL (*(volatile unsigned int *)(0x40010C00 0x00)) #define GPIOB_ODR (*(volatile unsigned int *)(0x40010C00 0x0C))PB5对应CRL中的位域是bit20~bit23。把它清空后写入0x01意思是“通用推挽输出输出速度10MHz”。GPIOB_CRL ~(0xF 20); GPIOB_CRL | (0x1 20);为什么是10MHz而不是50MHz普通LED用10MHz足够。如果引脚用来接I2C高速信号或者屏幕的像素时钟才需要配到50MHz。输出速度快会带来噪声和功耗不是越大越好。4.3 输出电压寄存器ODR和BSRR的区别引脚配成输出模式后控制高低电平有两组寄存器ODR和BSRR。ODR是输出数据寄存器直接整个寄存器赋值或读改写BSRR是位设置/清除寄存器按位写可以让指定位置高或拉低而且不需要读改写。用ODR点灯GPIOB_ODR | (1 5); // 点亮 GPIOB_ODR ~(1 5); // 熄灭用BSRR点灯GPIOB_BSRR (1 5); // bit0~15是置位写1则PB5输出高 GPIOB_BSRR (1 (5 16)); // bit16~31是清零写1则PB5输出低BSRR的好处是写入只对有影响的位置生效而且不需要先读再改。在多线程或中断里操作同一个引脚时ODR的读改写可能会被中途打断BSRR则没有这个问题。我写底层驱动时输出引脚优先用BSRR。4.4 从LED扩展到按键输入模式怎么配点完灯再试按键你会进一步理解模式配置。按键一端接引脚一端接GND再配上拉电阻。配置原理是把引脚模式设成输入上拉方法是在MODC字段写入0b1000输入上拉/下拉模式然后给ODR这位写1表示上拉最后从IDR里读电平#define GPIOB_IDR (*(volatile unsigned int *)(0x40010C00 0x08)) GPIOB_CRL ~(0xF 12); GPIOB_CRL | (0x8 12); // PB3配置成输入上拉/下拉 GPIOB_ODR | (1 3); // 上拉 if ((GPIOB_IDR (1 3)) 0) { // 按键按下电平被拉低 }这一步做好你对GPIO的理解基本就到位了先开时钟再选模式最后读写数据。后面再去碰串口和定时器思路是一模一样的。5. 定时器和串口的寄存器配法举一反三的关键5.1 定时器配置顺序的原则定时器比GPIO多但核心其实就几件事时钟源、预分频、自动重装、中断使能、启动计数。这些其实是一连串有先后关系的工作其中最关键的是理解时钟来源。以大家常用的定时器为例它挂在APB1总线上。如果系统时钟是72MHz而APB1分频器被配置为2分频那么APB1总线上的外设时钟是36MHz但定时器会自动加倍回到72MHz。很多手册和教程都强调过这个“×2”规则但实际做项目时还是经常有人算错定时频率原因就是忘了这一步。正确做法是先看RCC时钟树确定APB1分频系数再算定时器实际时钟最后反推PSC和ARR。顺序不能反反了所有时间都不对。5.2 一个定时中断的例子假设要用定时器产生1秒中断翻转一颗LED。系统时钟72MHzAPB1分频2定时器时钟72MHz。设置预分频器PSC为7200-1即计数频率变成10kHz自动重装寄存器ARR设为10000-1那么累加到10000次就是1秒。代码核心步骤示例#define TIM2_BASE 0x40000000 #define RCC_APB1ENR (*(volatile unsigned int *)(0x40021000 0x1C)) // 开定时器2时钟 RCC_APB1ENR | (1 0); // 配预分频和重装值 *(volatile unsigned int *)(TIM2_BASE 0x28) 7200 - 1; // PSC *(volatile unsigned int *)(TIM2_BASE 0x2C) 10000 - 1; // ARR // 清除更新标志使能更新中断 *(volatile unsigned int *)(TIM2_BASE 0x24) ~(1 0); // SR *(volatile unsigned int *)(TIM2_BASE 0x0C) | (1 0); // DIER // 启动计数 *(volatile unsigned int *)(TIM2_BASE 0x00) | (1 0); // CR1这里有一个很多人忽略的细节改PSC和ARR之前最好先把计数器停掉或者等当前周期结束否则可能出现一次意外跳变。我在调试时就遇到过改完ARR后第一次中断时间明显偏短的情况后来先清零计数器再重载配置才稳定。5.3 串口初始化从复位值开始拼寄存器串口初始化要动的寄存器更多但思路仍然是“查表填位”。首先是打开USART所在总线的时钟然后配置GPIO复用功能接下来设置波特率寄存器BRR最后使能USART发送和接收。波特率寄存器是最有意思的部分它不是直接写波特率数值而是要算一个分频值放进BRR。不过很多芯片支持用USARTDIV的小数部分从BRR的手册公式反推你可以按公式一步步算。初学者最容易犯错的是忘记BRR的最高位必须保留为0否则可能把寄存器值写成一个不存在的分频。串口初始化里还有一个小陷阱USART的CR1寄存器复位值默认是不使能的。很多人写好一堆代码后发现根本没输出回头一查原来是忘了把UE位置1。这种问题不是逻辑复杂纯粹是对复位值不够敏感。所以我的习惯是每次配置外设前先看一眼手册里的“复位值”列哪些位默认是0哪些位默认是1都心里有数再动手。6. 寄存器踩坑记录volatile、读-改-写和手册阅读习惯6.1 volatile不写编译器可能连外设都“优化”掉volatile是我见过新手写寄存器代码时最容易漏掉的东西。它的作用是告诉编译器这个地址的内容每次必须重新从内存读取不要用缓存值替代。举个例子你写一个循环等待某个状态寄存器变成1while ((flag_reg 0x01) 0);如果flag_reg没用volatile修饰编译器可能认为这个值在循环体里没被修改于是把循环优化成一个死循环或者干脆优化掉。单片机的寄存器不仅有CPU写还有外设硬件在改编译器并不知道这一点。所以在定义寄存器指针时一律用*(volatile unsigned int *)不要嫌难看。这个表达是寄存器编程的保命符。6.2 读-改-写操作与多任务竞争寄存器操作里最常见的操作方式就是读、改、写三步比如GPIOB_ODR | (1 5);这行代码编译后实际上是三条指令读ODR、修改第5位、再写回ODR。如果在中断和主程序里同时操作同一个寄存器的不同位就可能出现“丢失写入”的问题。比如主程序刚要清bit0中断里正好置bit2最后主程序把旧值写回去中断置的bit2就没了。解决这个问题的办法有三种一是尽量用BSRR这类硬件置位/清除寄存器二是中断和主程序操作同一个引脚时暂时关中断三是用关键区的互斥保护。实际项目里我基本把“能用BSRR就不碰ODR”写进了自己的驱动规范。6.3 我的小习惯按项目整理寄存器速查表官方参考手册有几百页每次找寄存器翻大厚书确实麻烦。我现在每一个项目都会建一个头文件只写上这个项目真正用到的寄存器地址和位掩码。比如用USART1就把USART1基地址、SR、DR、CR1和BRR的地址全部定义进去旁边加上注释说明哪位是干什么的。注释别写成复制粘贴要按自己的理解写。比如“bit13串口总开关必须置1”这种话在调试时比英文缩写有用得多。整理这份速查表的过程其实也是重新梳理外设逻辑的过程。很多我当时“明明会用但说不清”的功能都是在这时候想明白的。还有一定要养成“改一个寄存器就验证一次”的习惯。不要一口气写完几十行配置再上电测试那样出了问题根本不知道是哪一行惹的祸。我用串口调试或者仿真器查看寄存器实时值一步一确认时钟开了没引脚模式对没对计数器走没走这样能把问题锁定在小范围内。最后分享一个对我帮助很大的小技巧。读手册寄存器表格时我习惯拿一张纸把这个寄存器从bit0到bit31全部画成一格一格的然后把复位值、当前值、目标值写上去。这个看起来原始的办法比盯着PDF盯半天效率高得多。寄存器编程说到底是跟具体地址和每一位打交道把抽象表格变成自己手里的图很多糊涂账自然而然就清楚了。

相关新闻

LeetCode 884|两句话中的不常见单词:用哈希表统计词频的典型解法(InterviewGuide 题解)

LeetCode 884|两句话中的不常见单词:用哈希表统计词频的典型解法(InterviewGuide 题解)

文档教程知识库 【免费下载链接】InterviewGuide 🔥🔥「InterviewGuide」是阿秀从校园->职场多年计算机自学过程的记录以及学弟学妹们计算机校招&秋招经验总结文章的汇总,包括但不限于C/C 、Golang、JavaScript、Vue、操作系统、数据结…

2026/10/12 1:34:51 阅读更多 →
xdma驱动2019版本实战:编译、调优与避坑指南

xdma驱动2019版本实战:编译、调优与避坑指南

简介:这份资源为2019版XDMA驱动,面向使用Xilinx FPGA进行高速数据传输开发的工程师与学习者,可解决PCIe DMA通道搭建与驱动适配问题。XDMA驱动具备高吞吐量、低延迟特性,通常与Vivado 2019.2配合使用,也有搭配Vivado 2…

2026/10/12 1:33:50 阅读更多 →
5-Pin磁吸连接器从选型到量产:引脚定义、磁路设计与充电协议全解析

5-Pin磁吸连接器从选型到量产:引脚定义、磁路设计与充电协议全解析

1. 从一根线说起:为什么5-Pin磁吸连接器值得单独拿出来聊做智能穿戴和便携设备的人都有一个共同的痛:充电接口的可靠性,往往决定了整机的返修率。我见过太多项目,主板设计得漂漂亮亮,固件调得稳稳当当,最后…

2026/10/12 1:33:50 阅读更多 →

最新新闻

【深度学习新浪潮】Meta Muse 智能体:它是什么?有哪些特点?为什么突然火了?

【深度学习新浪潮】Meta Muse 智能体:它是什么?有哪些特点?为什么突然火了?

1. 引言 近期,Meta Muse 智能体在 AI 领域引发广泛关注,开发者、创作者与科技从业者纷纷展开讨论。许多初次接触者不禁疑惑:这是 Meta 推出的又一款大模型?抑或仅是蹭热度的 AI 玩具? 事实并非如此。Meta Muse 是 Meta 在 AI 智能体方向的一次战略性布局,它并非简单的对…

2026/10/12 2:24:22 阅读更多 →
Spring-boot-3 -注解 yaml配置 -日志

Spring-boot-3 -注解 yaml配置 -日志

4、核心技能1. 常用注解SpringBoot 摒弃 XML 配置方式,改为全注解驱动1. 组件注册Configuration 自定义配置类、SpringBootConfiguration 用来标注SpringBoot主启动类的Bean 可以在自定义配置类面创建对象交给ioc容器,组件在容器中的名字为方法名、Scope…

2026/10/12 2:24:22 阅读更多 →
Neuroimage: 动态功能连接方法的重测信度比较

Neuroimage: 动态功能连接方法的重测信度比较

本篇文献发表在Neuroimage杂志。所发布内容旨在与大家分享学术新知,促进交流学习版权归原作者或原出处所有,感谢各位学者的辛勤付出与研究成果。1.引言大脑的功能组织具有丰富的时空结构,可以使用功能连接指标进行探测。功能连接被定义为两个…

2026/10/12 2:24:22 阅读更多 →
page_alloc __rmqueue

page_alloc __rmqueue

__rmqueue() 是伙伴系统分配路径的核心调度器。它在持有 zone->lock 的前提下,按照碎片化风险从低到高的顺序,依次尝试不同的分配策略,直到成功或彻底失败。核心作用与策略链它的本质是一个多级降级策略链:先尝试最“干净”的方…

2026/10/12 2:24:22 阅读更多 →
游戏引擎中物理步进与动画采样的同步机制解析

游戏引擎中物理步进与动画采样的同步机制解析

1. 这不是教科书,是我在三个项目里拆过七次引擎后写下的物理与动画系统手记“游戏引擎架构深度解析(三):物理与动画系统”——看到这个标题,你大概率正卡在某个角色落地时穿模、布料抖动像癫痫发作、或者刚加完一个新关…

2026/10/12 2:24:22 阅读更多 →
产业与汇率全景分析深入分析多表格形成一篇文章

产业与汇率全景分析深入分析多表格形成一篇文章

产业与汇率全景深度分析:汇率是外生变量,产业是底层根基引言汇率从来不是孤立的数字,它是一国产业竞争力、贸易结构、资本流动、宏观政策、全球供需格局共同定价的结果;反过来,汇率波动又会重塑产业成本、订单、利润、…

2026/10/12 2:23:21 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练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/11 14:36:54 阅读更多 →