STM32片内Flash读写测试:从物理规则到HAL库实战

发布时间:2026/9/8 2:22:36

STM32片内Flash读写测试:从物理规则到HAL库实战
简介STM32片内Flash读写测试工程面向嵌入式开发者和单片机存储应用学习者重点解决设备掉电后数据保存与Flash编程难题。资源为完整Keil工程包共140个文件以h/c源码文件为主涵盖系统时钟初始化、Flash驱动、串口输出、定时器等模块同时附带uvprojx工程配置、hex/axf烧录文件、map映射文件及相关调试辅助文件压缩包总大小2.96MB导入即可编译运行。已有1031人学习下载。代码封装了flash_write与flash_read等常用接口并围绕Flash初始化、扇区擦写、编程状态检查、备份存储等关键流程展开配合详细注释和读写自检逻辑帮助读者理解STM32内部Flash分扇区管理、有限擦写寿命及编程保护机制。通过实际读写循环与异常处理可快速掌握非易失性存储开发技巧提升嵌入式系统的数据可靠性与稳定性设计能力。1. 为什么要自己动手做片内Flash读写测试最近做一个小项目需要在STM32上保存一批设备校准参数。最开始我特别天真直接把参数定义成const数组想着烧录的时候写死在Flash里结果调试阶段每次改参数都要重新下载程序麻烦不说还差点把Bootloader区域冲掉。后来我索性把片内Flash的读写测试认认真真做了一遍把擦除、写入、读取、校验整条链路彻底摸清了。这篇内容就是我当时完整踩坑过程的总结适合正在做参数掉电存储、OTA升级标志位或简单日志记录的人看。我会先讲清楚Flash的物理规则和不同型号的差异再给出一套可以直接抄的HAL库读写代码最后是几个高频率出现的报错排查思路——比如很多人一上来就遇到的Flash Download Failed和No STM32 Target Found其实99%都是环境问题不是代码问题。片内Flash这个东西说难不难但和RAM完全不是一个用法。你可以把Flash想象成一块只能从白色画成黑色的白板画笔只能涂黑也就是把1写成0如果想把黑色变回白色只能把整块板子擦干净重来。这就是Flash最核心的物理特性不理解这一点后面你会踩无数坑。我现在每次焊完板子都会先花十分钟做一次Flash读写测试确认存储区域安全、读写逻辑正确再开始写业务代码这个习惯帮我省掉了大量排查时间。1.1 片内Flash到底能做什么片内Flash本质上是非易失存储掉电不丢数据。很多人一听到Flash第一反应是“这不是放程序代码的地方吗”没错但它也是芯片上唯一一块用户可写、掉电不丢、访问速度又快的存储区域。片内除了Flash还有OTP区、备份寄存器和外部E2PROM各有各的用途但论通用性Flash绝对是最常用的。我在实际项目中用Flash做过几类事情一是保存传感器校准系数比如温度补偿表、零偏值二是保存设备序列号、MAC地址、配网信息这样批量生产时每台设备可以有独立参数三是保存Bootloader和App之间的跳转标志位上电后先判断Flash里的某个字节是不是约定值决定要不要进入固件升级流程四是存简单的运行日志掉电重启后还能翻看上次运行状态。这些场景的共同特点是数据量不大、不需要频繁写入但要求掉电保存——Flash正好合适。有同学会问为什么不用片外的E2PROM或SPI Flash因为片内Flash免费啊而且读取不需要额外驱动直接指针解引用就能访问。它的缺点是不能按字节改、有擦写寿命限制但作为参数存储完全够用。如果你需要频繁记录日志比如每秒写一次那我劝你还是用外部SPI Flash或者干脆加片E2PROM片内Flash的擦写寿命一般是1万次左右扛不住这么频繁的写入。1.2 不同型号的Flash结构差异很大这是我踩过最深的一个坑。STM32不同系列的Flash内部结构完全不一样连“最小擦除单位”这个概念都有好几种叫法。以最常见的F103系列举例中容量型号比如C8T6Flash总共64KB按1KB一页划分大容量型号比如ZET6Flash有512KB前4页是16KB后面每页2KB。F1系列用“页”作为擦除单位所以FLASH_ErasePage传的是页地址。但到了F4系列就不一样了。F4的Flash按“扇区”管理同样是1MB容量分成了4个16KB扇区、1个64KB扇区、7个128KB扇区。你在F103上习惯了一页1KB、擦除特别精细换到F4直接给你来一个128KB的扇区一个不小心就会把不需要擦除的代码区带进去。H7系列更复杂按Bank分成两个区操作前还要确认自己在哪个Bank。所以动笔写代码之前请务必打开对应型号的《参考手册》翻到Flash章节把“页/扇区大小”和“起始地址”抄下来记到笔记本上。我个人的习惯是把常用型号的Flash参数整理成一张表写测试代码时直接查芯片系列典型型号Flash容量擦除单位擦除单位大小F103中容量STM32F103C8T664KB页1KBF103大容量STM32F103ZET6512KB页2KB前4页16KBF407STM32F407VET6512KB扇区16KB/64KB/128KBF429STM32F429ZIT62MB扇区多个Bank混合换芯片型号后复制以前代码的时候务必确认这些参数不要想当然。1.3 写Flash前必须搞懂的4条物理规则第一Flash写入只能用1写0不能把0写1。想恢复成1只能整体擦除。所以“读-改-写”在Flash上行不通你必须先擦掉整个页/扇区再写入新数据。如果页里还有别的数据不想丢只能先读到RAM里擦除完再一起写回去。第二写入的最小单位不是1字节而是半字16位部分系列支持32位字写入或64位双字写入。F103用HAL库操作时FLASH_TYPEPROGRAM_HALFWORD一次写16位FLASH_TYPEPROGRAM_WORD一次写32位。F4/H7还支持FLASH_TYPEPROGRAM_DOUBLEWORD一次写64位。写入地址必须对齐半字对齐2字节、字对齐4字节、双字对齐8字节不对齐直接HardFault或返回错误。第三Flash有锁定机制。芯片复位后Flash默认处于锁定状态要编程必须先调用HAL_FLASH_Unlock()解锁。这个锁由Flash控制寄存器里的LOCK位控制目的是防止程序跑飞时把Flash内容写坏。写完后记得调用HAL_FLASH_Lock()重新上锁我见过不少代码只解锁不上锁虽然平时没事但对于有安全要求的项目来说是一个隐患。第四擦除和编程操作期间Flash控制器会占用内部总线这时候如果CPU试图从Flash取指就会产生总线冲突。最直接的后果是擦除过程中来了一个中断中断服务函数在Flash里一看取不到指令直接HardFault死机。后面我会专门讲这个问题的解决方案。2. 读写测试的整体设计思路2.1 测试环境和工具选择我用的测试平台是一块非常常见的STM32F103C8T6最小系统板开发环境是Keil 5加STM32CubeMX生成的HAL库工程调试器是ST-Link V2。为什么推荐HAL库因为现在大部分新项目都是用CubeMX初始化外设HAL库函数对Flash操作的封装比较统一换芯片型号时改动最小。如果你还在用标准库也不用担心Flash操作的核心思路是一样的无非是FLASH_Unlock、FLASH_ErasePage、FLASH_ProgramWord、FLASH_Lock这几个函数。后面我会做一个对照你按自己的环境选择即可。我这里还要提一下调试器的选择。ST-Link V2便宜够用但有一个缺点连接稳定性一般。尤其是杜邦线插得松或者下载速度调太高经常出现“flash download failed”之类的报错。如果你手头有J-Link协议支持更好稳定性也会好不少。不过我这次测试用的是ST-Link后面会给出排查这类报错的具体方法即便你用J-Link遇到类似问题思路也是通用的。2.2 规划一个绝对安全的测试地址这是整个测试中最需要小心的一步。STM32F103C8T6的Flash从0x08000000开始总容量64KB最后1KB的地址范围是0x0800FC00到0x0800FFFF。大多数情况下我们的测试程序编译出来也就10KB到30KB直接烧录后会从0x08000000开始存放正常不会占用到最后1KB。所以我把用户数据区选在0x0800FC00也就是最后一页。为什么不往前选因为越靠前越接近代码区程序一旦变大就可能和你的数据区重叠。Flash不同于外部存储芯片地址重叠不会有任何提示只会悄悄把你的程序代码覆盖掉导致随机死机、跑飞排查起来非常折磨人。测试的时候我故意采用了“从后往前规划”的思路这也是量产项目里比较规范的做法程序代码从前放用户数据从后放中间留出一段安全间距。如果芯片容量小、程序又大可以适当减小间距但绝不能完全不留。对于C8T6这类64KB芯片我的建议是用户数据区固定放在倒数第2页到倒数第4页之间具体看你的数据量。2.3 测试方案拆解解锁、擦除、写入、校验我的完整测试流程分四步。第一步调用HAL_FLASH_Unlock()解锁Flash原理我前面讲过不解锁就写硬件会直接拒绝。第二步擦除目标页。调用HAL_FLASHEx_Erase()把刚才规划的0x0800FC00这一页擦干净擦除后整页数据都变成0xFF。这里要注意HAL库的擦除接口需要传一个FLASH_EraseInitTypeDef结构体里面填写擦除类型、起始地址和擦除页数。还有一个非常隐蔽的坑必须注意检查第二个参数PageError如果擦除失败PageError会返回出错的页地址。第三步调用HAL_FLASH_Program()把要写入的数据按字写入到页内偏移地址。一次写32位循环多次写多个数据。写入完成后调用HAL_FLASH_Lock()重新锁定。第四步校验。直接用指针读取Flash对应地址的内容与写入时使用的原始数据逐一对比。注意一定不要忽略了掉电测试写入成功后断电再重新上电执行读取程序确认数据仍然一致。只有掉电再上电验证过才能证明数据真正保存住了。这四步看着简单但每一步都有对应的坑。接下来我直接给出完整的测试代码然后逐个环节解释注意事项。3. 代码级实操HAL库实现Flash写入与读取3.1 按页擦除的完整写法先看擦除代码#define FLASH_USER_START_ADDR 0x0800FC00 // 最后一页起始地址 #define FLASH_USER_END_ADDR 0x0800FFFF // 最后一页结束地址 FLASH_EraseInitTypeDef eraseInit; uint32_t pageError 0; HAL_FLASH_Unlock(); eraseInit.TypeErase FLASH_TYPEERASE_PAGES; // 按页擦除 eraseInit.PageAddress FLASH_USER_START_ADDR; eraseInit.NbPages 1; // 擦除1页 if (HAL_FLASHEx_Erase(eraseInit, pageError) ! HAL_OK) { // 擦除失败pageError会指向出错的页地址 // 建议这里加一个明显提示比如串口打印或LED快闪 }这段代码里最容易被忽略的就是pageError参数。官方库的HAL_FLASHEx_Erase在擦除出错时返回HAL_ERROR但很多人的代码根本不检查返回值。我见过不少项目Flash擦除失败后继续写入导致数据全乱。我的建议是擦除和写入后至少要做一次失败检查哪怕是暂时打印一条串口日志也能帮你省下排查时间。擦除期间CPU会有一到几十毫秒的停顿F103擦一页典型耗时在20ms到40ms量级。对于大部分应用来说这个时间可以接受但如果你的系统里有实时性要求高的控制任务比如PWM输出、编码器计数这几十毫秒的停顿可能会造成可见的影响。后面我会讲怎么用关中断的方式规避风险。3.2 数据写入与对齐问题擦除完成后就可以写入数据了。这里给出一次写入4个32位数据的例子uint32_t testData[4] {0x12345678, 0xA5A5A5A5, 0x0000FFFF, 0x5A5A1234}; uint32_t addr FLASH_USER_START_ADDR; for (int i 0; i 4; i) { if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr i * 4, testData[i]) ! HAL_OK) { // 写入失败处理 } } HAL_FLASH_Lock();这里有一个我自己踩过很多次的坑地址对齐。HAL_FLASH_Program的写入地址必须按4字节对齐。如果写成addr i而不是addr i * 4那么第2次写入时地址就变成0x0800FC01直接不对齐。轻则写入失败重则触发HardFault程序当场死给你看。为什么要用0x12345678和0xA5A5A5A5这种测试值因为Flash擦除后的默认值是0xFFFFFFFF如果你测试数据也用0xFFFFFFFF写完和没写看起来一模一样校验完全无效。应该选择一些0和1混合得比较均匀的数值这样写完后能明显看出差异。我还会特意在数据里加一个0x0000FFFF用来测试高位低位是否写反了。还有一个经验不同系列的编程粒度不同。F103支持按字写F4/H7还支持双字写。优先使用当前系列支持的最大编程粒度比如F4用FLASH_TYPEPROGRAM_DOUBLEWORD一次写64位这样可以减少Flash编程操作的次数缩短整体写入时间。不过双字写入要求8字节对齐注意调整地址偏移。3.3 读回校验和掉电测试读取Flash内容比写入简单得多因为Flash在STM32里是内存映射的直接用指针访问即可uint32_t readback[4]; bool match true; for (int i 0; i 4; i) { readback[i] *(__IO uint32_t *)(FLASH_USER_START_ADDR i * 4); if (readback[i] ! testData[i]) { match false; } }这个for循环会在内存里跳4次把对应的值读出来和原始值对比。不要忘了加volatile类型的强转——__IO就是HAL库里的volatile宏防止编译器把读操作优化掉。我见过有人不用指针而用memcpy效果是一样的但要确认memcpy的长度没有越界别把Flash后面的区域也读了。真正关键的步骤是掉电测试。写入完成后把板子断电等几秒钟再上电执行同样的读取代码。如果读出来的数据依然是刚才写入的值说明Flash保存功能正常。如果读出来是0xFF说明数据其实没写进去——大概率是擦除失败或地址写错。我还建议做一次“重复擦写”测试比如连续写入、断电、读取20次中途不要修改任何代码。这样能验证地址区域是否稳定同时观察芯片是否出现偶发问题。Flash擦写是有寿命的这种测试数据最好用RAM里的变量自增生成这样可以避免反复修改源代码。3.4 标准库和HAL库函数对照很多老项目还在用标准库我这里给一个快速对照表方便你切换操作标准库HAL库解锁FLASH_Unlock()HAL_FLASH_Unlock()擦除页FLASH_ErasePage(addr)HAL_FLASHEx_Erase()写入16位FLASH_ProgramHalfWord(addr, data)HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr, data)写入32位FLASH_ProgramWord(addr, data)HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data)锁定FLASH_Lock()HAL_FLASH_Lock()标准库的FLASH_ProgramWord只能写32位没有64位双字操作。如果你的项目从标准库迁移到HAL库最需要注意的是FLASHEx_Erase的结构体参数这个接口设计得比较绕但用习惯后发现填参数的过程相当于强制你去确认地址和页数能减少因疏忽导致的错误。标准库最大的问题是擦除接口没有返回值检查这一点HAL库做得更好所以我建议新项目尽量用HAL库。4. 测试过程中的高频问题与排查实录4.1 Flash Download Failed程序都下载不进去很多人在做Flash测试时遇到的第一个问题不是读写失败而是程序压根下载不进去。Keil弹出“Flash Download Failed - Target DLL has been cancelled”这个问题光我在论坛上就看到过无数次。排查顺序我建议这样来首先确认ST-Link的SWDIO和SWCLK杜邦线有没有接反或松动这是最大嫌疑。用万用表蜂鸣档量一下调试器到目标板的连通性很多“玄学问题”其实就是接触不良。其次打开Keil的Options for Target - Debug - Settings把下载速度从4MHz降到1MHz。高速模式下SWD抗干扰能力差尤其是杜邦线较长或者面包板连接的时候4MHz极易出错。降到1MHz后绝大多数连接不稳定的问题都能解决。我以前觉得降低下载速度显得不专业后来发现这是最省时间的做法。最后检查目标板供电。ST-Link V2的3.3V输出能力有限如果板子上还有其他外设比如OLED屏、蜂鸣器、ESP8266模块电流超了之后电压跌落下载必然失败。这种情况下换外部供电保持调试器和目标板共地。如果上述都不行用ST-Link Utility做一次整片擦除很多时候芯片内部有错误的Flash配置擦掉后就能恢复正常。4.2 No STM32 Target Found调试器连不上芯片“No STM32 Target Found”这个报错比Flash Download Failed更严重它意味着调试器压根没有发现芯片。常见原因有三个芯片被设置了读保护、程序把SWD引脚重新配置了、芯片进入低功耗模式。先说读保护。如果之前调试时不小心设置了RDP级别芯片会拒绝调试器连接。解决方法是按住目标板复位键不放让调试器在“连接时复位”的模式下抓住芯片。ST-Link Utility里面有“Connect under reset”选项勾选后再尝试连接。注意解除读保护会自动触发整片擦除所以别拿有重要代码的芯片乱试。再说程序把SWD引脚配置成普通GPIO。PA13和PA14默认是SWDIO和SWCLK但如果你在代码里把它们复用成别的功能下载一次后调试口就没了。遇到这种情况同样是按住复位键抢连接然后把程序擦掉。所以我建议测试Flash期间代码里不要碰PA13和PA14的复用配置。4.3 写进去的数据读出来全是0xFF如果Flash写入后读回的数据全是0xFF第一个要怀疑的就是没有擦除。Flash擦除后的状态是0xFF写入操作只能把某些位置0如果你写入前没有成功擦除写入时硬件会发现对应位已经是0了无法再改成0最终编程失败。排查方法很简单在写入前先单独调用一次擦除然后用调试器或ST-Link Utility读一下Flash目标地址确认确实变成0xFFFFFFFF。如果是再进行写入测试。另外选测试数据时别用0xFFFFFFFF原因前面说过写完和没写一样无法判断是否真正写入成功。还有一种情况是地址算错了。比如你想写最后一页0x0800FC00但实际写到了0x0800F800而0x0800F800那个区域没有擦除写进去自然失败或读出乱码。建议先计算好地址打印出来确认再执行写入。4.4 擦除时中断触发导致HardFault这个问题比较隐蔽一旦遇到会让人排查到怀疑人生。前面说过Flash擦除期间Flash控制器占用总线CPU此时不能从Flash取指。如果这时候来了一个中断中断服务函数在Flash里CPU无法从Flash读取指令直接触发HardFault。我遇到过一次特别典型的场景擦除Flash时蜂鸣器“滴”了一声系统立刻死机。排查到最后发现是定时器中断在擦除期间被触发而中断处理函数恰好放在Flash区。解决办法有两个方向。一是擦除前关中断擦完再恢复__disable_irq(); HAL_FLASHEx_Erase(eraseInit, pageError); __enable_irq();这个方法简单粗暴适合擦除时间短、系统能容忍短暂中断延迟的场景。二是把中断处理函数放到RAM里执行。在链接脚本里增加RAM执行段或者用__attribute__((section(.ramfunc)))标记关键函数。这个方法复杂但更优雅适合对实时性要求较高的系统。我的建议是刚开始测试Flash时直接采用关中断的方案先保证功能正确再考虑优化。5. 从测试到实战常见工程化扩展5.1 用Flash做参数存储永远不要只存一份测试通过之后就要考虑正式应用了。如果你的目的是用片内Flash保存参数我强烈建议做双备份方案。双备份的经典做法是参数区准备两个页页A存当前有效参数页B存上一次写入的参数。写入时先写页B校验通过后再写页A读取时先读页A如果校验失败就回退到页B。每份参数都带CRC校验确保数据没有被篡改或丢失。为什么这么设计因为产品在用户手里随时可能断电你无法保证断电的瞬间不是正在写Flash。如果只存一份写入到一半断电Flash里残留的数据就是半新半旧的垃圾重启后系统无法判断是否有效。双备份加CRC校验可以最大程度避免这种风险。我做第一个量产项目时没考虑这个问题结果有客户反馈“设置经常丢失”后来改成双备份才彻底解决。5.2 用ST-Link Utility/J-Flash直接查看Flash内容调试Flash内容时不要只依赖串口打印。ST-Link Utility可以直接读取芯片整个Flash并保存成bin文件还可以对比两个bin文件的差异。我用这个功能排查过好几次“为什么程序运行逻辑不对”的问题——最后发现是Flash里残留了上次的测试数据程序启动时把那块当有效参数读进来导致行为异常。看到真面目后问题一下就清晰了。J-Flash也提供类似功能而且批量操作速度更快。如果你要批量读取多片芯片的Flash内容做比对J-Flash配合脚本会非常方便。我在量产阶段用J-Flash导出每台设备的Flash镜像存档备案一旦后续出了质量问题可以直接翻出来比对当时的存储状态。这个习惯帮我快速定位过两次“出厂参数异常”的客诉。5.3 把Flash操作封装成独立模块我现在写项目已经不再把Flash操作散落在业务代码里了而是单独封装成flash_if.c和flash_if.h模块对外只暴露三个函数Flash_WriteParams、Flash_ReadParams、Flash_EraseParams。读函数直接从Flash映射地址memcpy到RAM结构体写函数内部按照“关中断 - 解锁 - 擦页 - 写入 - 校验 - 锁定 - 恢复中断”的顺序执行。所有Flash地址、页大小、数据结构体定义都放在头文件里换芯片型号时只需要改宏定义和结构体不用动任何业务逻辑。这个封装方式还有一个好处我可以随时给测试代码加几个串口命令比如通过串口发送“W”就执行写操作发送“R”就执行读操作发送“E”就执行擦除。调试的时候效率提升非常明显不用反复改代码、烧录、复位。你也可以在封装模块内部加一个状态机把每次写入的结果记录到一个全局变量里方便调试器实时观察。最后再分享一个我自己的体会STM32片内Flash读写测试这件事看着简单但值得当成一个正式的小项目来做。很多问题的根子不在于“不会调用库函数”而在于对Flash的物理规则不够敬畏——不擦除就写、地址重叠、中断不关这些都是会让你排查到怀疑人生的点。建议你拿到板子后先用整片擦除和全片读取把芯片的“底细”摸清楚再去做精细的读写测试。等你把这片Flash彻底摸透了后面再做参数存储、OTA、日志记录基本就是水到渠成的事情。本文还有配套的精品资源点击获取

相关新闻

FastAPI生产环境部署实战:从Uvicorn热更新到Docker与Nginx

FastAPI生产环境部署实战:从Uvicorn热更新到Docker与Nginx

2026/9/8 2:22:36

简介:面向需要将 FastAPI 应用投入实际运行的 Python Web 开发者,这份资料围绕 FastAPI 部署全流程展开,从环境准备、应用创建、路由编写,到如何通过 OpenAPI 文档与 JavaScript 前端进行数据交互,均有清晰说明。资源包…

BERT微调实战:从零复现提取式摘要模型全流程

BERT微调实战:从零复现提取式摘要模型全流程

2026/9/8 2:22:36

简介:面向自然语言处理开发者与学术研究者,这一项目完整实现了基于BERT的抽取式文本摘要微调流程,从数据预处理、模型搭建到训练评估均有对应实现,可复现论文中的摘要提取实验。压缩包共36个文件,以20个Python脚本为核…

AI编程的下一站:从Vibe Coding到Spec Coding,让代码值得信任

AI编程的下一站:从Vibe Coding到Spec Coding,让代码值得信任

2026/9/8 2:12:36

最近有一条消息让我停下来想了很久:SpaceX 收购了 AI 编程初创公司 Cursor。说实话,这几年 AI 编程工具层出不穷,但这条消息的分量不太一样。倒不是因为我第一时间就相信它——公开信息还非常有限,交易细节、团队归属都还没有完整…

A2A与MCP:AI智能体通信机制的技术解析与实践应用

A2A与MCP:AI智能体通信机制的技术解析与实践应用

2026/9/8 3:22:39

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

ESP32智能插座调试软件功能测试全流程与问题排查实践

ESP32智能插座调试软件功能测试全流程与问题排查实践

2026/9/8 3:22:39

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

GMA T.33 VP10:高转V12与手动挡的纯粹驾驶机器解析

GMA T.33 VP10:高转V12与手动挡的纯粹驾驶机器解析

2026/9/8 3:22:39

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

OpenCV图像处理实战:从算法原理到项目集成完整指南

OpenCV图像处理实战:从算法原理到项目集成完整指南

2026/9/8 3:22:39

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

PLC与组态软件协作的智能停车场收费系统设计与实现

PLC与组态软件协作的智能停车场收费系统设计与实现

2026/9/8 3:22:39

做停车场收费系统,很多人第一反应是“这不就是一台收费电脑加两台车牌识别相机的事吗?”真到了现场调试,你会发现哪怕是再简单的出入口,只要涉及道闸升降、地感检测、防砸车这几个基本动作,就离不开一套可靠的电气控制…

儿童电话手表安全配置指南:从权限管理到信息链路

儿童电话手表安全配置指南:从权限管理到信息链路

2026/9/8 3:12:38

“重生六岁,我靠电话手表反杀恶魔家教。”——这个小说标题本身就带着很强的叙事张力。一个成年人带着前世记忆回到童年,身边唯一能动用的现代工具,恰是戴在手腕上的那只儿童电话手表。但作为一个长年关注智能硬件和青少年数字生活的人&#…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/7 20:21:46

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/7 3:44:24

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/7 8:03:37

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

芯片良率波动可视化:动画拆解工艺因果,重建客户信任

芯片良率波动可视化:动画拆解工艺因果,重建客户信任

2026/9/8 0:02:30

芯片这个行业有个不太被人摆到台面上、但几乎每天都在发生的场景:客户拿着一条良率曲线截图问你,这批货的良率怎么掉了三个点,是不是工艺出问题了,产生的不良会不会流到他们产线上去。你解释了半天,客户似懂非懂&#…

PyTorch DataLoader参数冲突:sampler与shuffle互斥的根源与正确写法

PyTorch DataLoader参数冲突:sampler与shuffle互斥的根源与正确写法

2026/9/8 0:02:30

ValueError: sampler option is mutually exclusive with shuffle,这个报错我在 PyTorch 的 DataLoader 上至少见过几十次了,而且很有意思的是,它经常不是新手专属——很多写了好几年模型的老手,在从单机改成自定义采样器&#xf…

中国车企再破谣言,GAC吉利零跑获欧盟安全五星

中国车企再破谣言,GAC吉利零跑获欧盟安全五星

2026/9/8 0:02:30

有人可能在网上开着皮卡拍视频,声称中国电动车不仅性能不如美国大排量车型,安全性也堪忧。然而事实恰恰相反,GAC、吉利和零跑最新推出的电动车型在极为严苛的欧盟新车安全评鉴(Euro NCAP)测试中全部斩获满分。就在特斯…

远程协作的工作台整理

远程协作的工作台整理

2026/9/7 3:38:07

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/8 3:19:39

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/6 23:21:51

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…