STM32 SPI驱动W25Q64 Flash实战:从时序到跨页写入的避坑指南

1. 为什么我劝你先搞懂W25Q64的脾气再动手写代码

市面上讲STM32驱动W25Q64的文章一抓一大把,但大部分都是丢一段代码出来,告诉你“烧进去就能跑”。我刚开始接触这颗Flash芯片的时候也是这么想的,结果踩了一连串坑:读出来的ID是0xFFFFFF,写进去的数据读回来全变成0xFF,擦除一个扇区等了半天以为程序卡死了。后来才明白,问题根本不在代码本身,而在于我没搞清楚W25Q64的工作逻辑和STM32 SPI外设之间的配合关系。

W25Q64是一颗8MB容量的SPI NOR Flash,64Mbit,分成128个块(Block),每块64KB,每个块又分成16个扇区(Sector),每个扇区4KB。这个层级结构不是随便分的,它直接决定了你的擦除粒度和写入策略。NOR Flash有个物理特性:写入只能把bit从1变成0,不能从0变成1。所以每次写入之前,对应区域必须处于擦除状态(全0xFF)。擦除的最小单位是扇区(4KB),不是字节,也不是页。这个特性如果你不提前理解,写代码的时候就会遇到“为什么我写了新数据,读出来是旧数据和新数据按位与的结果”这种让人抓狂的问题。

这篇文章我打算把STM32F103通过SPI驱动W25Q64的完整流程拆开讲,从硬件连接到SPI配置,从命令时序到读写擦除的实操代码,再到我实际调试中遇到的几个典型坑。不管你是刚接触SPI和Flash的新手,还是已经能跑通但想搞清楚底层细节的开发者,应该都能从里面找到有用的东西。我用的芯片是STM32F103C8T6,SPI1,引脚是PA5(SCK)、PA6(MISO)、PA7(MOSI),片选CS用PA4软件控制。这套配置很经典,换成其他F103型号或者SPI2也大同小异。

2. 硬件连接与SPI模式选择:别小看这两根线的接法

2.1 引脚连接与电平匹配的注意事项

先说过硬件这一关。W25Q64的引脚不多,VCC、GND、CS、CLK、DI(MOSI)、DO(MISO)、WP、HOLD。其中WP是写保护,HOLD是暂停通信。如果你只是做基本的读写测试,WP接VCC(禁用写保护),HOLD接VCC(禁用暂停功能)就行。但这里有个细节:WP和HOLD不能悬空,悬空的话芯片内部状态不确定,可能出现写不进去或者通信异常的情况。我一开始就是HOLD没接,读ID能读到,但写数据死活写不进去,查了半天才发现是HOLD悬空导致芯片间歇性进入暂停状态。

CS片选我用的是PA4,软件控制。为什么不用硬件NSS?因为STM32的硬件NSS在多主机或者某些配置下容易出问题,而且W25Q64的CS需要在整个命令序列期间保持低电平,软件控制更直观可控。接线的时候注意CS走线尽量短,如果杜邦线太长,高速SPI下可能出现片选信号边沿抖动,导致命令解析错误。

电源方面,W25Q64的工作电压是2.7V到3.6V,STM32F103的IO也是3.3V,直接连没问题。但如果你用的是5V的单片机,中间必须加电平转换,否则Flash会被打坏。这个坑虽然基础,但每年都有不少人踩。

2.2 SPI模式0还是模式3:时序图里藏着答案

W25Q64支持SPI模式0(CPOL=0,CPHA=0)和模式3(CPOL=1,CPHA=1)。这两种模式的区别在于时钟极性和相位。模式0下,时钟空闲为低电平,数据在第一个边沿(上升沿)采样;模式3下,时钟空闲为高电平,数据在第二个边沿(下降沿)采样。

我一般用模式0,因为STM32的SPI配置成模式0最直观,而且大部分例程也是这么做的。但你要知道,模式0和模式3在W25Q64上都能正常工作,关键是主机和从机必须匹配。如果你发现读出来的数据总是移位或者错位,先检查SPI模式是否和Flash要求的一致。

SPI时钟频率方面,W25Q64支持最高80MHz(快速读模式下),但STM32F103的SPI1最高只能到72MHz(APB2时钟72MHz,分频系数最小2)。我一般设置在18MHz到36MHz之间,也就是分频系数4或2。为什么不用最高速?因为杜邦线飞线的情况下,高速SPI信号完整性很难保证,容易出现误码。如果你画了PCB,走线阻抗控制得好,可以往上提。但调试阶段建议先用低速(比如4.5MHz,分频系数16),确保通信稳定后再提速。

// SPI1初始化配置,模式0,主机,8位数据,分频16(4.5MHz) void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // PA5 SCK, PA7 MOSI 复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // PA6 MISO 浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // PA4 CS 推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS拉高,空闲状态 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }

这段代码里有个点值得说:SPI_FirstBit_MSB。W25Q64的命令、地址、数据都是MSB先行,这个必须设对,否则整个通信全乱。我见过有人设成LSB,结果读ID读出来是反的,查了一下午。

3. 命令时序拆解:从读ID到页写入的完整链路

3.1 读ID命令:验证通信是否正常的第一个里程碑

拿到一块新Flash,第一步不是急着写数据,而是读ID。W25Q64的制造商ID是0xEF,设备ID是0x4017(对于W25Q64)或者0x4018(W25Q128)。读ID有两个命令:0x90(读制造商/设备ID)和0x9F(读JEDEC ID)。我一般用0x9F,因为它返回三个字节:制造商ID、内存类型、容量。正常返回应该是0xEF、0x40、0x17。

读ID的时序很简单:CS拉低,发送0x9F,然后连续读三个字节,CS拉高。但这里有个细节:在发送命令和读取数据之间,不需要额外的等待周期。W25Q64在0x9F命令下,时钟连续输出数据,你只要继续给时钟就能读到。

// 读取JEDEC ID uint32_t W25Q64_ReadID(void) { uint32_t id = 0; W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x9F); id |= SPI1_ReadWriteByte(0xFF) << 16; id |= SPI1_ReadWriteByte(0xFF) << 8; id |= SPI1_ReadWriteByte(0xFF); W25Q64_CS_HIGH(); return id; }

如果读出来是0x000000或者0xFFFFFF,先检查硬件连接,再检查SPI模式。0xFFFFFF通常意味着MISO一直高电平,可能是Flash没供电或者CS没拉低。0x000000则是MISO一直低,可能是MISO接错或者Flash损坏。

3.2 写使能:每次写入和擦除前的必修课

W25Q64有个状态寄存器(Status Register),里面的WEL位(Write Enable Latch)决定了芯片是否允许写入。每次执行页写入、扇区擦除、块擦除之前,都必须先发送0x06命令(写使能)。写使能之后,WEL位自动置1,执行完写入或擦除操作后,WEL位自动清零。如果你忘了发写使能,写入命令会被芯片忽略,数据写不进去,而且芯片不会给你任何错误提示。

我刚开始调试的时候,擦除扇区一直不成功,读回来还是旧数据。后来用逻辑分析仪抓波形才发现,我发了擦除命令但没发写使能。这个坑很隐蔽,因为代码逻辑看起来没问题,但芯片就是不执行。

// 写使能 void W25Q64_WriteEnable(void) { W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x06); W25Q64_CS_HIGH(); }

3.3 等待忙状态:别在Flash干活的时候打扰它

擦除和写入操作需要时间。扇区擦除典型时间45ms,最大400ms;页写入典型时间0.7ms,最大3ms。在这段时间内,Flash处于忙状态,除了读状态寄存器命令(0x05),其他命令都不响应。所以每次擦除或写入之后,必须轮询状态寄存器的BUSY位,直到它变为0。

// 等待Flash空闲 void W25Q64_WaitBusy(void) { uint8_t status = 0; do { W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x05); status = SPI1_ReadWriteByte(0xFF); W25Q64_CS_HIGH(); } while (status & 0x01); // BUSY位是bit0 }

这里有个经验:轮询之间加一点延时,比如几微秒,不要连续疯狂读。虽然SPI读状态寄存器很快,但连续高频轮询会增加功耗,而且对调试没什么帮助。我一般加个delay_us(10),实际影响可以忽略。

3.4 扇区擦除与页写入:理解4KB和256字节的边界

扇区擦除命令是0x20,后面跟24位地址。W25Q64的地址是24位的,因为8MB容量需要23位地址(2^23 = 8M),但命令格式是3个字节,所以高8位补0。擦除的最小单位是4KB,你给的地址会被对齐到扇区起始地址。比如你给0x000010,实际擦除的是0x000000到0x000FFF。

页写入命令是0x02,后面跟24位地址,然后是要写入的数据。页的大小是256字节。如果你写入的数据超过256字节,地址会自动回卷到页起始地址,覆盖之前写入的数据。比如你在页内偏移250的位置写10个字节,前6个字节写到偏移250到255,后4个字节会回卷写到偏移0到3,覆盖掉原来的数据。这个特性很容易被忽略,导致数据莫名其妙被覆盖。

// 扇区擦除(4KB对齐) void W25Q64_SectorErase(uint32_t addr) { W25Q64_WriteEnable(); W25Q64_WaitBusy(); W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x20); SPI1_ReadWriteByte((addr >> 16) & 0xFF); SPI1_ReadWriteByte((addr >> 8) & 0xFF); SPI1_ReadWriteByte(addr & 0xFF); W25Q64_CS_HIGH(); W25Q64_WaitBusy(); } // 页写入(不超过256字节) void W25Q64_PageWrite(uint32_t addr, uint8_t *data, uint16_t len) { if (len > 256) len = 256; // 安全限制 W25Q64_WriteEnable(); W25Q64_WaitBusy(); W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x02); SPI1_ReadWriteByte((addr >> 16) & 0xFF); SPI1_ReadWriteByte((addr >> 8) & 0xFF); SPI1_ReadWriteByte(addr & 0xFF); for (uint16_t i = 0; i < len; i++) { SPI1_ReadWriteByte(data[i]); } W25Q64_CS_HIGH(); W25Q64_WaitBusy(); }

3.5 数据读取:0x03命令的边界与高速读的区别

读数据命令是0x03,后面跟24位地址,然后连续输出数据。地址会自动递增,可以一直读到Flash末尾。这个命令支持的最高时钟频率是50MHz(对于W25Q64),在STM32F103的SPI1上跑36MHz没问题。

但如果你需要更高速度,可以用快速读命令0x0B,它需要一个额外的dummy字节(空周期),然后才能输出数据。快速读支持80MHz。不过对于大多数应用,0x03够用了,代码也更简单。

// 读取数据 void W25Q64_ReadData(uint32_t addr, uint8_t *buf, uint32_t len) { W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x03); SPI1_ReadWriteByte((addr >> 16) & 0xFF); SPI1_ReadWriteByte((addr >> 8) & 0xFF); SPI1_ReadWriteByte(addr & 0xFF); for (uint32_t i = 0; i < len; i++) { buf[i] = SPI1_ReadWriteByte(0xFF); } W25Q64_CS_HIGH(); }

4. 跨页写入与地址对齐:实际项目里最容易翻车的地方

4.1 为什么不能直接写超过256字节的数据

前面说了页大小是256字节,但实际项目中你要写入的数据往往超过256字节,比如写一个结构体、一帧图像数据、一段日志。这时候不能直接调一次页写入就完事,必须把数据拆分成多个页写入,而且每个页写入的起始地址必须对齐到页边界。

举个例子,你要从地址0x000100开始写300字节。0x000100是页起始地址(256的倍数),前256字节写到0x000100到0x0001FF,剩下44字节写到0x000200到0x00022B。这没问题。但如果你从0x000080开始写300字节,0x000080不是页起始地址,前128字节写到0x000080到0x0000FF,然后地址回卷到0x000000,后128字节会覆盖掉0x000000到0x00007F的数据。这就是跨页写入的坑。

正确的做法是写一个跨页写入函数,自动处理拆分和地址对齐:

// 跨页写入,自动处理页边界 void W25Q64_WriteNoCheck(uint32_t addr, uint8_t *data, uint32_t len) { uint32_t pageRemain = 256 - (addr % 256); if (len <= pageRemain) { W25Q64_PageWrite(addr, data, len); } else { W25Q64_PageWrite(addr, data, pageRemain); W25Q64_WriteNoCheck(addr + pageRemain, data + pageRemain, len - pageRemain); } }

这个递归写法很简洁,但要注意栈深度。如果写入数据特别大,递归层数会很多。实际项目中我一般用循环实现,避免栈溢出风险。

4.2 擦除与写入的配合:先擦后写,顺序不能反

NOR Flash的物理特性决定了写入之前必须擦除。但擦除是4KB起步的,如果你只想改一个字节,也得擦掉整个扇区,然后重新写入整个扇区的数据。这个“读-改-写”的流程在实际项目中很常见。

我一般的做法是:先把整个扇区的数据读到RAM缓冲区(4KB),在缓冲区里修改需要改的字节,然后擦除扇区,再把缓冲区写回去。这个过程听起来简单,但有几个细节要注意:缓冲区大小至少4KB,对于STM32F103C8T6这种只有20KB RAM的芯片,4KB缓冲区不算小,要合理规划内存。另外擦除和写入期间不能断电,否则数据会丢失。如果对数据可靠性要求高,可以考虑加掉电检测或者用文件系统(比如LittleFS、FatFS)来管理。

4.3 状态寄存器保护位:WP引脚和SRP位的关系

W25Q64的状态寄存器有两个写保护相关的位:SRP0、SRP1、BP0-BP2、WPS。这些位配合WP引脚可以实现硬件写保护。如果你把WP引脚拉低,并且设置了相应的保护位,那么对应的区域就无法擦除和写入。这个功能在防止误操作或者保护关键数据时很有用,但调试阶段建议把WP接高电平,禁用写保护,否则你会遇到“为什么写不进去”的问题。

我遇到过一种情况:Flash之前被设置过写保护,换到新板子上之后,写保护位还在,导致无法写入。解决办法是发送0x50命令(写状态寄存器使能),然后写状态寄存器清零保护位。但要注意,有些保护位一旦设置,需要特定的时序才能清除,具体参考数据手册。

5. 调试实录:读出来全是0xFF和写入不生效的排查过程

5.1 读ID正常但读写数据异常:问题出在CS时序

有一次我调一块新板子,读ID正常,返回0xEF4017,说明SPI通信没问题。但写数据之后读回来全是0xFF,擦除也不生效。用逻辑分析仪抓波形,发现CS信号在命令序列中间有毛刺,偶尔会跳高一下。原因是CS走线太长,而且和SCK线平行走了一段,产生了耦合。SCK跳变的时候,CS被耦合出了毛刺,Flash误以为命令结束,导致后续数据被忽略。

解决办法很简单:CS走线尽量短,远离SCK,必要时加一个上拉电阻(10K)到VCC,确保空闲时CS稳定高电平。改线之后问题消失。

5.2 写入成功但读出来是旧数据:忘了等待忙状态

还有一次,写数据之后立即读,读出来是旧数据。但延时一段时间再读,新数据就出来了。原因是写入之后没有等待忙状态,Flash还在内部编程,读命令被忽略了。加上W25Q64_WaitBusy()之后问题解决。

这个坑的隐蔽性在于,如果你用调试器单步执行,每一步都有延时,反而不会出问题。但全速运行的时候,Flash还没写完你就去读,读到的就是旧数据或者不确定的数据。

5.3 擦除时间过长导致看门狗复位

扇区擦除最大400ms,如果你开了看门狗,喂狗间隔小于400ms,擦除期间看门狗就会复位。我一开始用独立看门狗,超时设了200ms,结果擦除的时候系统不断复位。后来把看门狗超时改成1s,并且在擦除和写入期间增加喂狗操作,问题解决。

提示:擦除和写入期间,如果系统有实时性要求,建议把擦除操作放在低优先级任务或者空闲时执行,避免阻塞主循环。

5.4 地址计算错误导致数据覆盖

有一次我写一个日志系统,每条日志固定128字节,写到Flash里。日志索引从0开始,地址是index * 128。写到第33条的时候,发现第1条的数据被覆盖了。原因是第33条的地址是33*128=4224,而扇区大小是4096,4224已经跨到了下一个扇区。但我在擦除的时候只擦了第一个扇区,第二个扇区没擦,所以写入的时候地址回卷到了第一个扇区的开头,覆盖了第1条日志。

这个问题的根本原因是我没有按扇区管理日志,而是按条管理。后来改成按扇区擦除,每个扇区存32条日志(4096/128=32),写满一个扇区再擦下一个,问题解决。

6. 性能优化与可靠性提升的几个实操技巧

6.1 用DMA驱动SPI读写,解放CPU

STM32F103的SPI支持DMA。对于大块数据读写,用DMA可以显著降低CPU占用。配置SPI1_TX和SPI1_RX的DMA通道,发送和接收都走DMA,CPU只需要在DMA传输完成中断里处理后续逻辑。

但要注意,W25Q64的命令阶段(发送命令和地址)数据量很小,用DMA反而增加开销。我一般只在数据阶段用DMA,命令阶段还是用轮询。另外DMA传输期间CS必须保持低电平,所以CS的控制要放在DMA启动之前和完成中断之后。

6.2 加入CRC校验,确保数据完整性

SPI通信在高速或者长走线的情况下可能出错。W25Q64本身不提供CRC校验,但你可以自己在数据后面附加CRC。写入的时候计算CRC并存储,读取的时候重新计算并比对。如果CRC不匹配,说明数据损坏,可以触发重读或者报错。

我一般用CRC-16/CCITT,计算速度快,检错能力够用。对于关键数据,还可以加备份扇区,主扇区损坏时从备份恢复。

6.3 扇区磨损均衡:延长Flash寿命

W25Q64的擦写寿命是10万次(每个扇区)。如果你频繁写同一个扇区,比如每秒写一次日志,不到两天那个扇区就报废了。解决办法是磨损均衡:把写操作分散到不同扇区,轮流使用。

简单的实现方式是维护一个写指针,每次写新数据就移动到下一个扇区,写满一圈再回到第一个扇区擦除重写。这样每个扇区的擦写次数就均匀了。如果数据量不大,还可以用多个扇区做环形缓冲区,进一步提高寿命。

6.4 低功耗场景下的注意事项

如果你的设备是电池供电,Flash的功耗也要考虑。W25Q64在待机模式下的电流大约是10uA,读写时几mA。不用的时候可以把CS拉高,Flash自动进入待机。如果对功耗要求极高,还可以发送0xB9命令进入深度掉电模式,电流降到1uA以下,但唤醒需要额外的时间。

注意:深度掉电模式下,除了释放深度掉电命令(0xAB),其他命令都不响应。唤醒之后需要等待一段时间(tRES1,约3us)才能正常操作。

7. 从裸机驱动到文件系统的过渡思路

裸机读写Flash适合数据量小、结构简单的场景。但如果你的项目需要管理大量文件、支持目录结构、处理断电恢复,裸机驱动就不够用了。这时候可以考虑移植一个轻量级文件系统,比如LittleFS或者FatFS。

LittleFS是专门为嵌入式Flash设计的,支持掉电恢复和磨损均衡,代码量也不大,很适合STM32F103这种资源有限的芯片。移植的时候需要提供底层的读、写、擦除接口,也就是我们前面写的那些函数。FatFS更通用,但需要你自己实现磨损均衡和掉电保护,复杂度更高。

我个人的经验是:如果只是存一些配置参数或者少量日志,裸机驱动加简单的扇区管理就够了。如果要做数据记录仪、文件存储这类应用,直接上LittleFS,省心很多。移植LittleFS的时候,注意块设备接口的erase函数要按扇区擦除,prog函数要处理跨页写入,read函数直接调0x03命令就行。

最后说一个实际调试中的小技巧:在Flash里留一个专门的区域存调试信息,比如擦除次数、写入次数、错误计数。每次上电的时候读出来打印到串口,可以帮你快速判断Flash的健康状态。这个区域不要频繁擦写,可以放在最后一个扇区,只在必要时更新。

相关新闻

芯片测试为何必须用SMU?四象限与开尔文四线测量原理解析

芯片测试为何必须用SMU?四象限与开尔文四线测量原理解析

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

2026/10/21 1:01:46 阅读更多 →
3D SiC MOSFET功率模块选型指南:四款新品对比与实测

3D SiC MOSFET功率模块选型指南:四款新品对比与实测

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

2026/10/21 1:01:46 阅读更多 →
STM32嵌入式开发入门指南:从选型到实战避坑

STM32嵌入式开发入门指南:从选型到实战避坑

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

2026/10/21 1:01:46 阅读更多 →

最新新闻

Wi-Fi MAC Layer: Scanning, Authentication, Association, and Data Transmission Processes

Wi-Fi MAC Layer: Scanning, Authentication, Association, and Data Transmission Processes

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

2026/10/21 2:25:53 阅读更多 →
ROS2笔记1

ROS2笔记1

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

2026/10/21 2:25:53 阅读更多 →
26 精武杯手机部分复盘

26 精武杯手机部分复盘

【简要案情】 近期&#xff0c;某市反诈部门接到多名群众报案&#xff0c;均遭遇“投资理财”类电信网络诈骗&#xff0c;指向的犯罪团伙涉案资金巨大、作案链条隐蔽。办案单位迅速成立专案组开展电子数据取证工作。经初查&#xff0c;该犯罪团伙搭建虚假投资平台&#xff0c;通…

2026/10/21 2:25:53 阅读更多 →
PHP安全漏洞修复指南:防SQL注入与XSS

PHP安全漏洞修复指南:防SQL注入与XSS

一、为什么 PHP 项目总被盯上做 PHP 开发这些年&#xff0c;我见过太多项目上线后被黑的情况。说句实话&#xff0c;大部分漏洞并不是什么高深的技术&#xff0c;翻来覆去就是 SQL 注入和 XSS 这两类。很多新手写代码的时候图省事&#xff0c;直接把用户输入拼进 SQL 语句&…

2026/10/21 2:25:53 阅读更多 →
中小企业建数据中台预算不多怎么办?先把这 5 笔成本拆开,别只问报价

中小企业建数据中台预算不多怎么办?先把这 5 笔成本拆开,别只问报价

报价单拿到手&#xff0c;最常见的第一反应是"怎么比我想的贵这么多"。而这个"我想的"&#xff0c;通常是拿一套软件的价钱估出来的。 这篇为谁写&#xff1a;中小企业里兼管数据的 IT、被老板一句"我们是不是该上中台&#xff0c;先报个价"问到…

2026/10/21 2:25:53 阅读更多 →
C语言基础笔记 04 指针

C语言基础笔记 04 指针

00 包 目前CLion的最新版本还不支持中文&#xff0c;需要导入#include <stdlib.h> &#xff0c;再在main函数内写system("chcp 65001");才能在代码中用中文stdlib 还可以提供动态内存分布——malloc函数01 使用原因对于函数&#xff0c;在函数外定义的变量&…

2026/10/21 2:24:52 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →