STM32驱动W25Q64实战:整数与小数存储全链路解析
简介这份资源面向正在学习STM32嵌入式开发、希望掌握外部Flash数据存取方法的开发者聚焦于通过SPI接口驱动W25Q64串行Flash芯片实现整数与小数两类数据的写入与读取。内容涵盖SPI四线通信时序、W25Q64指令集调用、数据类型转换与字节对齐处理并涉及擦除操作、错误检查及通信速率优化等实际开发要点适合具备一定C语言与单片机基础、想扩展存储能力或深入理解SPI协议的学习者。压缩包共77个文件约304KB以30个c源文件与32个h头文件为主体配合8个汇编启动文件、2个hex固件、Keil工程文件及批处理脚本构成可直接编译调试的完整工程。目前已有399人学习下载。借助现成的读写函数与工程结构读者可快速上手Flash数据存取实验理解底层存储原理并在此基础上完成自己的数据记录与参数保存方案。1. 从一块 W25Q64 说起为什么你的 STM32 项目需要一个外部 Flash 存储方案做过带参数保存功能的嵌入式项目的人都遇到过这个场景设备跑得好好的一断电标定参数、运行时长、校准系数全没了。STM32 自带的 Flash 虽然能存点东西但擦写次数有限、容量也紧巴巴稍微多存几组数据就得精打细算。这时候外挂一颗 W25Q64 基本是标配操作——8MB 容量、SPI 接口、擦写寿命十万次级别存小数和整数这类参数数据绰绰有余。但问题来了SPI 读写外部 Flash 这件事说简单也简单说坑也多。页编程不能跨页、写之前必须先擦除、小数在内存里是 4 字节浮点、存进去再读出来可能变成一堆乱码。这份资源围绕的就是这些实打实的问题把 W25Q64 的整数和小数存储链路完整跑通。如果你正在用 STM32 做数据记录、参数保存、掉电保护这类功能这套代码可以直接拿来改。2. 硬件连接与 SPI 初始化先把通信链路打通2.1 W25Q64 的引脚接线与 SPI 模式选择W25Q64 是标准的 SPI Flash8 个引脚里真正需要接的没几个。CS 片选接任意一个 GPIOSCK、MISO、MOSI 接 STM32 的 SPI 外设引脚VCC 和 GND 供电WP 和 HOLD 如果不做写保护和暂停功能直接拉高就行。我一般用 SPI1因为它的时钟树独立配置起来方便。STM32F103 系列上 SPI1 的引脚是 PA5SCK、PA6MISO、PA7MOSICS 随便找个 PA4 或者 PB0 都行。接线本身不复杂但有两个地方容易翻车一是 MISO 必须配置成上拉输入或者复用推挽否则读回来的数据全是 0xFF二是 CS 引脚在初始化时一定要先拉高不然上电瞬间 Flash 可能进入异常状态。W25Q64 支持 SPI 模式 0 和模式 3区别在于时钟极性CPOL和时钟相位CPHA。模式 0 是 CPOL0、CPHA0模式 3 是 CPOL1、CPHA1。两种都能用但整个工程里必须统一。我习惯用模式 0因为大部分例程和手册时序图都按这个画。2.2 SPI 初始化代码与参数说明// SPI1 初始化主机模式模式08位数据时钟分频256 void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // SCK(PA5) / MOSI(PA7) 复用推挽输出 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); // MISO(PA6) 上拉输入防止悬空读到 0xFF GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOA, GPIO_InitStructure); // CS(PA4) 普通推挽输出初始拉高 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); 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; // 模式0 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 模式0 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; // 软件控制CS SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }这段代码里几个参数值得单独说。SPI_BaudRatePrescaler_256是分频系数72MHz 主频下 SPI 时钟约 281kHz速度不快但稳。W25Q64 手册标称最高支持 80MHz实际跑 10MHz 以上就要注意 PCB 走线和杜邦线长度了我一般先用低速调通再往上提。SPI_NSS_Soft表示片选由软件控制这样可以把 CS 接到任意 GPIO 上比硬件 NSS 灵活。2.3 SPI 收发一个字节的底层函数// SPI1 读写一个字节返回读到的数据 u8 SPI1_ReadWriteByte(u8 TxData) { u16 retry 0; // 等待发送缓冲区空 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET) { retry; if (retry 2000) return 0; // 超时保护防止死循环 } SPI_I2S_SendData(SPI1, TxData); retry 0; // 等待接收缓冲区非空 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET) { retry; if (retry 2000) return 0; } return SPI_I2S_ReceiveData(SPI1); }这个函数是整个驱动的地基。SPI 是全双工发一个字节的同时也在收一个字节所以读和写用的是同一个函数。超时保护是血泪经验——如果 Flash 没接好或者 CS 没拉低没有超时的话程序就卡死在 while 里了调试的时候连串口都打印不出来。3. W25Q64 驱动层读 ID、擦除、页写、读数据3.1 读器件 ID 验证通信是否正常在写任何数据之前第一件事是读 W25Q64 的 JEDEC ID确认芯片在线。W25Q64 的 ID 是 0xEF4017厂商 ID 0xEF 是 Winbond0x4017 代表 64Mbit 容量。// 读取 W25Q64 的 JEDEC ID正常返回 0xEF4017 u32 W25Q64_ReadID(void) { u32 temp 0; W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x9F); // 发送读 ID 命令 temp | SPI1_ReadWriteByte(0xFF) 16; // 厂商 ID temp | SPI1_ReadWriteByte(0xFF) 8; // 存储器类型 temp | SPI1_ReadWriteByte(0xFF); // 容量 W25Q64_CS_HIGH(); return temp; }如果读回来是 0x000000 或者 0xFFFFFF说明通信没通。0xFFFFFF 通常是 MISO 一直高电平检查接线0x000000 可能是 CS 没拉低或者 SPI 模式不对。这一步过了后面的读写才有意义。3.2 写使能、扇区擦除与忙等待W25Q64 的存储结构是每页 256 字节每扇区 4KB16 页每块 64KB16 扇区总共 128 个块。写数据之前必须先擦除擦除的最小单位是扇区4KB。擦除后该扇区所有字节变成 0xFF。// 写使能每次写/擦除前必须调用 void W25Q64_WriteEnable(void) { W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x06); // 写使能命令 W25Q64_CS_HIGH(); } // 等待芯片空闲超时返回1表示失败 u8 W25Q64_WaitBusy(void) { u32 timeout 0; W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x05); // 读状态寄存器1 while ((SPI1_ReadWriteByte(0xFF) 0x01) 0x01) // BUSY位 { timeout; if (timeout 100000) { W25Q64_CS_HIGH(); return 1; // 超时 } } W25Q64_CS_HIGH(); return 0; } // 擦除一个 4KB 扇区addr 为扇区起始地址 void W25Q64_SectorErase(u32 addr) { W25Q64_WriteEnable(); W25Q64_WaitBusy(); W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x20); // 扇区擦除命令 SPI1_ReadWriteByte((addr 16) 0xFF); // 24位地址高字节 SPI1_ReadWriteByte((addr 8) 0xFF); SPI1_ReadWriteByte(addr 0xFF); W25Q64_CS_HIGH(); W25Q64_WaitBusy(); // 等待擦除完成约 45ms }擦除操作耗时比较长典型值 45ms最大 400ms。如果在这期间发下一条命令芯片不会响应。所以W25Q64_WaitBusy是必须的而且要有超时退出机制否则芯片异常时程序就挂死了。3.3 页写与数据读取页写命令 0x02一次最多写 256 字节且不能跨页。比如从地址 0x0000F0 开始写 32 字节写到 0x000110 就跨页了后半段会回卷到页首覆盖前面的数据。这是新手最容易踩的坑。// 页写addr 为页内起始地址len 不超过 256 且不跨页 void W25Q64_PageWrite(u32 addr, u8 *buf, u16 len) { 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 (u16 i 0; i len; i) { SPI1_ReadWriteByte(buf[i]); } W25Q64_CS_HIGH(); W25Q64_WaitBusy(); // 等待写入完成约 0.7ms } // 读取任意长度数据addr 为起始地址 void W25Q64_Read(u32 addr, u8 *buf, u32 len) { W25Q64_CS_LOW(); SPI1_ReadWriteByte(0x03); // 读数据命令 SPI1_ReadWriteByte((addr 16) 0xFF); SPI1_ReadWriteByte((addr 8) 0xFF); SPI1_ReadWriteByte(addr 0xFF); for (u32 i 0; i len; i) { buf[i] SPI1_ReadWriteByte(0xFF); } W25Q64_CS_HIGH(); }读命令 0x03 没有页限制可以从任意地址连续读任意长度地址会自动递增。写操作则受页边界约束超过 256 字节或者跨页就需要拆分成多次页写。4. 整数与小数的存储从内存字节到 Flash 数据的完整链路4.1 整数存储直接按字节写入整数存储相对直接。以int类型为例STM32 上 int 是 4 字节小端模式低字节在低地址。把整数拆成 4 个字节依次写入 Flash 即可。// 存储一个 int 到指定地址 void W25Q64_WriteInt(u32 addr, int value) { u8 buf[4]; buf[0] (value 0) 0xFF; // 低字节 buf[1] (value 8) 0xFF; buf[2] (value 16) 0xFF; buf[3] (value 24) 0xFF; // 高字节 W25Q64_PageWrite(addr, buf, 4); } // 从指定地址读取一个 int int W25Q64_ReadInt(u32 addr) { u8 buf[4]; W25Q64_Read(addr, buf, 4); return (int)((buf[3] 24) | (buf[2] 16) | (buf[1] 8) | buf[0]); }这里要注意的是写入前该地址所在扇区必须已经擦除过。如果直接往有数据的地址写结果会是新旧数据按位与读出来就是乱码。我一般会在初始化时检查一个标志位如果标志位不对就擦除整个扇区再写入默认值。4.2 小数存储浮点数的字节级处理小数在内存里是 IEEE 754 单精度浮点格式4 字节。存储思路和整数一样把 float 的 4 个字节原样搬到 Flash 里读的时候再拼回 float。// 存储一个 float 到指定地址 void W25Q64_WriteFloat(u32 addr, float value) { u8 buf[4]; // 用联合体把 float 的字节取出来避免指针类型转换的警告 union { float f; u8 b[4]; } converter; converter.f value; buf[0] converter.b[0]; buf[1] converter.b[1]; buf[2] converter.b[2]; buf[3] converter.b[3]; W25Q64_PageWrite(addr, buf, 4); } // 从指定地址读取一个 float float W25Q64_ReadFloat(u32 addr) { u8 buf[4]; union { float f; u8 b[4]; } converter; W25Q64_Read(addr, buf, 4); converter.b[0] buf[0]; converter.b[1] buf[1]; converter.b[2] buf[2]; converter.b[3] buf[3]; return converter.f; }用联合体而不是*(float*)buf这种指针强转是因为后者在部分编译器上会有对齐问题而且严格别名规则下行为不确定。联合体是标准做法编译器不会报警告代码也更好读。4.3 多参数存储的地址规划与结构体封装实际项目里不会只存一个数通常是一组参数。我一般按 4 字节对齐规划地址比如 0x000000 存整数参数 A0x000004 存浮点参数 B0x000008 存浮点参数 C以此类推。如果参数多可以定义一个结构体整体读写。// 参数结构体按 4 字节对齐 typedef struct { int run_count; // 运行次数 float kp; // 比例系数 float ki; // 积分系数 float kd; // 微分系数 float offset; // 校准偏移 } Param_t; // 保存全部参数到 Flash 起始地址 void SaveParams(u32 base_addr, Param_t *p) { W25Q64_SectorErase(base_addr); // 先擦除整个扇区 W25Q64_PageWrite(base_addr, (u8 *)p, sizeof(Param_t)); } // 从 Flash 读取全部参数 void LoadParams(u32 base_addr, Param_t *p) { W25Q64_Read(base_addr, (u8 *)p, sizeof(Param_t)); }结构体直接按字节写入有个前提结构体内部不能有指针成员且编译器不能做额外的填充优化导致大小变化。sizeof(Param_t)在这个例子里是 20 字节4 字节对齐下没有填充。如果结构体成员类型混杂建议手动确认一下sizeof的值或者用__packed关键字强制紧凑排列。5. 避坑与排查SPI 读写 W25Q64 最常见的 5 个问题5.1 读回来全是 0xFF现象调用读 ID 或者读数据返回的缓冲区全是 0xFF。原因MISO 引脚悬空或者配置成了浮空输入SPI 主机收到的永远是高电平。另一个可能是 CS 没有正确拉低芯片根本没响应。解决把 MISO 配置成上拉输入GPIO_Mode_IPU用万用表量一下 CS 引脚在通信时是否确实被拉低。如果 CS 用硬件 NSS检查 SPI_NSS 参数是否设成了硬件模式。5.2 写入成功但读出来是乱码现象写进去的数据读出来对不上或者只有部分字节正确。原因最常见的是写入前没有擦除。Flash 只能把 1 写成 0不能把 0 写成 1所以必须先擦除擦除后全变 0xFF再写。另一个原因是跨页写入超过 256 字节边界后数据回卷覆盖了页首内容。解决每次写之前确认目标扇区已擦除。如果数据超过 256 字节按页拆分循环写入每次写入前重新计算页内偏移。5.3 程序卡死在 WaitBusy 循环里现象擦除或写入后程序卡在等待 BUSY 位的 while 循环中出不来。原因芯片没有正常响应可能是供电不稳、SPI 时钟太快、或者 CS 时序不对。W25Q64 在擦除期间 BUSY 位会持续为 1但如果芯片本身没工作BUSY 位可能一直是某个固定值。解决在 WaitBusy 里加超时计数超时后返回错误码而不是死等。同时降低 SPI 时钟分频先用低速验证功能再逐步提高。5.4 浮点数读出来是 0.000000 或者极大值现象整数读写正常但浮点数存进去读出来完全不对。原因浮点数的字节序或者联合体使用有问题。如果用了指针强转*(float*)buf在某些编译器优化等级下会出错。另外如果写入时 float 变量本身没初始化存进去的就是随机值。解决统一用联合体做 float 和字节数组的转换。写入前打印一下 float 的原始值读取后再打印一次对比确认。如果用的是 double注意 STM32 上 double 是 8 字节和 float 的 4 字节处理方式不同。5.5 擦除一次后数据就再也写不进去了现象第一次擦除写入正常第二次想改数据时发现写不进去读出来还是旧值。原因写操作之前没有调用写使能命令 0x06。W25Q64 每次写或擦除之前都必须先发写使能芯片内部有一个写使能锁存位命令执行后自动清零。解决检查W25Q64_WriteEnable()是否在每次W25Q64_PageWrite和W25Q64_SectorErase之前都被调用了。我习惯把写使能直接放在页写和擦除函数的开头避免遗漏。6. 二次开发改造从能跑到好用几个实用的优化方向这套代码跑通之后下一步就是让它更适应真实项目。第一个改造方向是加一层参数管理接口把地址分配、默认值、读写校验都封装起来。比如定义一个参数表每个参数有名字、地址、类型、默认值初始化时自动检查 Flash 里的数据是否合法不合法就写入默认值。这样上层业务代码不用关心 Flash 地址直接调Param_Get(kp)和Param_Set(kp, 1.5f)就行。第二个方向是加数据校验。Flash 存储偶尔会因为电源波动或者擦写寿命问题出现位翻转存进去 3.14 读出来变成 3.15 这种事虽然概率低但在工业场景里不能接受。常见做法是在每个参数后面跟一个 CRC16 校验值读取时校验不通过就回退到默认值。如果参数是一整块结构体可以在结构体末尾加一个uint16_t crc成员保存时计算前面所有字节的 CRC 填进去读取时重新计算比对。第三个方向是磨损均衡。W25Q64 每个扇区擦写寿命约 10 万次如果每次保存参数都擦同一个扇区频繁保存的场景下几年就可能把那个扇区写坏。简单做法是准备两个扇区轮流用每次保存切换到另一个扇区读取时比较两个扇区的数据版本号取最新的。再复杂一点可以用一个扇区做日志式追加写写满后再整体擦除。第四个方向是掉电保护。保存参数的过程中如果断电可能只写了一半数据下次上电读出来就是半新半旧的混合体。解决办法是写入前先在另一个地址写一个“正在写入”标志写完后改成“写入完成”上电时检查标志位决定是否使用备份数据。或者用双备份加版本号的方式每次保存交替写两个区域读取时选版本号大且校验通过的那个。我一般会在项目初期就把参数管理这层做扎实后面加参数、改参数类型都不用动底层驱动。从那以后我每次做带 Flash 存储的项目都强制走一遍“擦除-写入-回读-校验”的完整流程确认数据真的落盘了才放心。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

UE4组件与碰撞实战:从架构设计到性能优化的完整指南

UE4组件与碰撞实战:从架构设计到性能优化的完整指南

UE4里的组件和碰撞,这两个词在项目里几乎天天能碰到,但说实话,很多人对它们的理解一直停留在“知道怎么用”却说不清“为什么要这样用”的阶段。我最近在做一个动作玩法Demo,为了让一个可拾取物品表现出“靠近提示—按键拾取—拾取…

2026/10/19 8:12:55 阅读更多 →
ASCII、Unicode与UTF-8:从字符编码到乱码排障的底层逻辑

ASCII、Unicode与UTF-8:从字符编码到乱码排障的底层逻辑

项目日志里突然刷出一排"锟斤拷",测试同事当场截图问我是不是服务器内存坏了。这是我在某个业务系统上线后遇到的第一起大规模乱码事故——不是硬件故障,也不是代码逻辑错误,问题出在字符编码上:服务端用UTF-8写日志&am…

2026/10/19 8:11:53 阅读更多 →
LoRA为何必须将B矩阵初始化为0?零初始化原理深度解析

LoRA为何必须将B矩阵初始化为0?零初始化原理深度解析

“LoRA 为什么必须把一个矩阵初始化为 0”——这个问题我最早是在一次技术评审上被问住的。当时我刚写完一版手搓 LoRA,还得意于自己把公式背得滚瓜烂熟,结果对面一句“为什么偏偏是 B 置零,而不是 A 置零?换成 1e-6 行不行&#…

2026/10/19 8:11:53 阅读更多 →

最新新闻

Git Diff驱动的LLM代码审查CLI工具设计与实践

Git Diff驱动的LLM代码审查CLI工具设计与实践

1. 项目概述:这不是又一个“AI写代码”玩具,而是一套可嵌入日常开发流的开源代码评审基础设施“open-code-review”这个名称乍看平平无奇,但拆开来看——open不是指“开源”,而是指“开放接入、开放协议、开放上下文”&#xff1b…

2026/10/19 13:56:58 阅读更多 →
用Seedance 2.0接车企商单:AI视频从零到交付级成片全流程

用Seedance 2.0接车企商单:AI视频从零到交付级成片全流程

上周收到一个外包朋友的转发需求:某车企要推一款新能源SUV,给了一条30秒的新车宣传片任务,预算不算高,但要求“有高级感、有运动感、要能看出品牌调性”。这条需求原文一共不到三句话,却成了我最近做AI视频内容以来最完…

2026/10/19 13:56:58 阅读更多 →
Web世界模型:为AI智能体生成无限网页训练环境

Web世界模型:为AI智能体生成无限网页训练环境

我拿到这个标题时的第一反应是:网页环境终于要被"世界模型"这股风刮到了。过去两年我一直在和浏览器自动化、AI agent打交道,最头疼的事情从来不是模型本身,而是环境——想让智能体学会操作网页,就必须给它足够多、足够…

2026/10/19 13:56:58 阅读更多 →
STM32入门第一步:点亮LED并理解GPIO控制

STM32入门第一步:点亮LED并理解GPIO控制

简介:零死角玩转STM32初级篇是一份面向初学者的完整入门教程包,由PDF文档与配套例程构成,覆盖从STM32选型、Cortex-M内核到GPIO、时钟、中断、定时器、串口、ADC/DAC等基础外设应用,并简要介绍RTOS。配套PDF从开发环境搭建、RCC时…

2026/10/19 13:56:58 阅读更多 →
Calcite优化:AggregateCaseToFilterRule将CASE聚合改写为FILTER提升SQL性能

Calcite优化:AggregateCaseToFilterRule将CASE聚合改写为FILTER提升SQL性能

当你在数据报表里写count(case when ... then 1 end)的时候,可能没有意识到,这行代码正在把优化器关在门外。Apache Calcite 里有一条专门针对这种场景的规则,叫AggregateCaseToFilterRule,它做的事情很纯粹:把聚合参数…

2026/10/19 13:56:58 阅读更多 →
基于YOLOv8/YOLOv5的田间杂草检测系统:从数据集构建到UI部署完整复盘

基于YOLOv8/YOLOv5的田间杂草检测系统:从数据集构建到UI部署完整复盘

田间杂草检测这种项目,很多人第一反应是找个开源模型跑一下,能出框就算完事。但真正做过一次田间实测的人都知道,训练集里mAP刷到0.95,拉到地里一测,漏检的、误检的能让你怀疑人生。这个基于YOLOv8/YOLOv7/YOLOv6/YOLO…

2026/10/19 13:55:57 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 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/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →