25.SPI读写W25X40CL - 按k1(20251026已经解决)
各位大侠:这几天跟着冲哥学习STC32G12K128 其中实验 《25.SPI读写W25X40CL 》
如果写入和读出的地址是0X000000 开头,就基本正常
但我把地址改成:
#define W25X40CL_ADDRESS0x000100//保存的W25X40CL起始页地址,我们改用第1页,0X000100--0X00001ff
好像就不行了
只有按K1马上就假死机好像CDC串口就出问题了
还有一个问题:我发现保存是数据似乎不准确,保存10个温度,再读回来,好像数据不对
难道我理解错了?
我是这样理解的:
W25X40C 是一款 4M-bit(512KB)的串行闪存芯片,采用分页结构管理存储空间。其地址分配如下:关键参数
[*]容量:4M-bit(512KB),地址范围为 0x000000 至 0x07FFFF(十六进制)。
[*]页大小:256字节(标准SPI闪存页大小)。
[*]总页数:512KB ÷ 256B = 2048页。
页地址计算每一页的起始地址可通过以下公式计算:
页地址 = 页号 × 256(0x100)
其中,页号范围为 0 到 2047(十进制)。示例:
[*]第0页:0x000000
[*]第1页:0x000100
第2页:0x000200第3页:0x000300第4页:0x000400第5页:0x000500第6页:0x000600
[*]...
[*]第2047页(最后一页):0x07FF00
注意事项1. 地址格式:所有地址为24位十六进制,高位补零(如 0x000100)。2. 跨页操作:写入或读取时若超出当前页地址(如从 0x0000FF 连续写入到 0x000100),需处理页边界限制。3. 实际应用:通常通过页号(0~2047)间接管理地址,而非直接使用绝对地址。如需完整的页地址列表(2048项),建议通过脚本或工具生成,或按需分段查询。
实验的运行环境就是试验箱9.6 也不需要配置串口()直接用STC USB CDC 串口)
实在没办法差到底是哪里出问题了,恳请帮忙!!!!
各位大侠好:
关于在使用 STC32G12K128 控制器通过 25SPI 接口读写 W25X40CL Flash 芯片时遇到的问题,结合您描述的现象(按键 K1 按下后死机、数据写入与读取不一致),我将从以下几个方面进行分析与建议:
一、W25X40CL 存储结构与地址理解
W25X40CL 是 Winbond 推出的一款 4M-bit(512KB)的 SPI NOR Flash 存储芯片,其地址结构如下:
总容量:512KB(即 0x000000 ~ 0x7FFFFF)
页面大小:每页 256 字节(Page Size = 256B)
扇区大小:4KB(Sector Size = 4KB = 0x1000)
块大小:64KB 或 32KB(视具体型号)
因此,地址 0x000100 属于第 1 页(Page 1),而 Page 0 的地址范围是 0x000000 ~ 0x0000FF。
您的理解是正确的,W25X40CL 的地址可以自由指定,只要不超过芯片容量即可。
二、问题分析
1. 按下 K1 后死机(假死)
这个现象可能与以下几个方面有关:
(1)SPI 通信异常导致系统阻塞
当前代码可能在读写 Flash 时没有做超时处理或错误检测;
如果 SPI 通信失败(如 CS 没有释放、时序错误),可能导致程序卡死在等待状态;
特别是在调用 CDC 串口输出时,如果 SPI 通信与串口共用中断或资源,容易引发冲突。
(2)Flash 写入未完成就进行下一次操作
Flash 写入是一个耗时操作(通常需要几毫秒),必须等待写入完成标志(BUSY)为 0;
如果未等待写入完成就进行下一次操作(如读取或写入),可能造成 Flash 错误响应,进而导致 SPI 通信异常。
(3)K1 按键触发中断处理不当
如果 K1 是中断触发方式,需确保中断服务函数(ISR)中不要进行复杂的操作(如 SPI 读写);
建议在 ISR 中仅设置标志位,由主循环处理实际操作。
建议排查步骤:
1. 检查 SPI 初始化配置是否正确(模式、频率、极性等);
2. 检查 W25X40CL 的 BUSY 位是否被正确轮询;
3. 检查 K1 按键是否引起 SPI 或串口资源冲突;
4. 添加调试输出,确认死机位置(例如使用 LED 或串口输出);
5. 使用逻辑分析仪检查 SPI 通信时序是否正确。
2. 数据写入与读取不一致
这个问题可能由以下原因造成:
(1)未擦除就写入
Flash 存储器写入前必须确保目标地址所在扇区已被擦除;
Flash 只能从 1 写为 0,不能从 0 写为 1,因此必须先擦除才能写入新数据;
如果直接写入一个未擦除的地址,写入数据会与原有数据进行“与”操作,导致数据错误。
(2)地址偏移错误或缓冲区越界
检查写入与读取时使用的地址是否一致;
检查写入/读取长度是否匹配;
确保缓冲区大小足够,避免越界访问。
(3)SPI 通信错误导致数据错误
SPI 通信速率过高可能导致数据传输错误;
检查 SPI 的时钟频率是否在 W25X40CL 支持范围内(一般建议不超过 80MHz);
使用逻辑分析仪抓取 SPI 数据线,确认发送与接收的数据是否一致。
建议操作流程:
1. 擦除目标扇区(4KB):
c
W25X40CLEraseSector(W25X40CLADDRESS & 0xFFFFF000); // 对齐到扇区边界
2. 写入前等待擦除完成(轮询 BUSY 位);
3. 写入数据:
c
W25X40CLPageProgram(W25X40CLADDRESS, buffer, length);
4. 写入后等待写入完成;
5. 读取验证:
c
W25X40CLReadData(W25X40CLADDRESS, readbuffer, length);
三、推荐调试方法
1. 使用调试串口输出关键信息(如地址、写入数据、读取数据等);
2. 用 LED 指示程序运行状态(如写入开始、写入完成、读取开始等);
3. 使用逻辑分析仪分析 SPI 通信过程;
4. 逐步调试:先测试 Page 0 是否正常,再测试 Page 1;
5. 测试单个写入与读取操作是否正确,再测试连续多个页的读写。
四、结论与建议
综合分析:
W25X40CL 的地址设置为 0x000100 是合法的,只要确保擦除和写入流程正确;
死机问题大概率与 SPI 通信异常或资源冲突有关,建议检查中断处理与 SPI 状态;
数据不一致问题多由未擦除或通信错误导致,应确保擦除写入流程完整;
建议严格按照 Flash 写入流程进行操作:擦除 → 写入 → 等待完成 → 读取验证。
如能提供更详细的代码片段或现象截图(如串口输出内容、逻辑分析仪波形),可进一步帮助定位问题。
祝调试顺利!
国芯人工智能 敬上
[本文内容由国芯人工智能辅助生成,仅供参考] 想了一下,还是要从基础干起:
先复习以下内容:
我们借用STC的程序:
32-硬件SPI访问FLASH-PM25LV040-串口2监控(成功)
一步一步的复习以下串口最基本的操作:
1 UART2 初始化:
//========================================================================
// 函数: void UART2_config(u8 brt)
// 描述: UART2初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART2_config(u8 brt) // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
if(brt == 2)
{
SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));
S2CFG |= 0x01; //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
S2CON = (S2CON & 0x3f) | 0x40; //UART2模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
ES2 = 1; //允许中断
S2REN = 1; //允许接收
S2_S= 1; //UART2 switch to: 0: P1.0 P1.1,1: P4.6 P4.7
B_TX2_Busy = 0; //发送忙标志清零
TX2_Cnt = 0; //发送计数清零
RX2_Cnt = 0; //接收计数清零
}
}
串口2配置寄存器(S2CFG)
S2CFG |= 0x01; //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
这句就好懂
SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));
这句就是定时器2作为时钟脉冲的设置:
是怎么来的?18.4.5串口2 模式1,模式1波特率计算公式当软件设置S2CON的S2SMO、S2SM1为"01”时,串行口2则以模式1进行工作。此模式为8位1UART格式,一帧信息为10位:1位起始位,8位数据位(低位在先)和1位停止位。波特率可变,即可根据需要进行设置波特率。TxD2为数据发送口,RxD2为数据接收口,串行口全双工接受/发送。模式1 的发送过程:串行通信模式发送时,数据由串行发送端 TxD2输出。当主机执行一条写S2BUF的指令就启动串行通信的发送,写“S2BUF”信号还把“1”装入发送移位寄存器的第9位,并通知TX控制单元开始发送。移位寄存器将数据不断右移送TxD端口发送,在数据的左边不断移入"0”作补充。当数据的最高位移到移位寄存器的输出位置,紧跟其后的是第9位“1”,在它的左边各位全为“0”,这个状态条件,使TX控制单元作最后一次移位输出,然后使允许发送信号“SEND”失效,完成一帧信息的发送,并置位中断请求位S2TI,即S2TI-1,向主机请求中断处理。模式1的接收过程:当软件置位接收允许标志位S2REN,即S2REN=1时,接收器便对RxD2端口的信号进行检测,当检测到RxD2端口发送从“1”-"0”的下降沿跳变时就启动接收器准备接收数据,并立即复位波特率发生器的接收计数器,将1FFH装入移位寄存器。接收的数据从接收移位寄存器的右边移入,已装入的 1FFH 向左边移出,当起始位"0"移到移位寄存器的最左边时,使 RX 控制器作最后一次移位,完成一帧的接收。若同时满足以下两个条件:S2RI=0;S2SM2=0 或接收到的停止位为 1。则接收到的数据有效,实现装载入S2BUF,停止位进入S2RB8,S2RI标志位被置1,向主机请求中断,若上述两条件不能同时满足,则接收到的数据作废并丢失,无论条件满足与否,接收器重又检测RxD2端口上的"1"-"0"的跳变,继续下一帧的接收。接收有效,在响应中断后,S2RI标志位必须由软件清0。通常情况下,串行通信工作于模式 1 时,S2SM2 设置为"0"。
看看要求的脉冲时序图:
串口 2 的波特率是可变的,其波特率固定由定时器 2 产生。当定时器采用 1T 模式时(12 倍速),相应的波特率的速度也会相应提高 12 倍。
#define Baudrate2 115200L我们采用的波特率为Baudrate2时的定时器2重载值:65536UL - (MAIN_Fosc / 4) / Baudrate2很明显我们是1T模式。
一会儿我们在复习定时器2 的设置:
接下来看看串口2脚位设置:串口1就有4组脚位切换,串口2就只有2组脚位:我们程序选1S2_S = 1; //UART2 switch to: 0:P1.0 P1.1,1: P4.6 P4.7 所以引脚硬件连线,我们要找P4.6 –RxD2P4.7 T xD2
到最重要的:
S2CON = (S2CON& 0x3f) | 0x40; //UART2模式, 0x00: 同步移位输出, 0x40:8位//数据,可变波特率, 0x80:9位数据,固定波特率, 0xc0: 9位数据,可变波特率//S2CON最终选模式1:0x40: 8位数据,可变波特率
先看下图:
B7 和B6都是0,指定串口2工作在模式0 ---同步移位串行方式此时第三位B5要设置为0 。现在是S2CON= (S2CON & 0x3f) | 0x40; 0x3f =0011 1111,&的意思是前2位取0,后6位不变0接着按位或0x40=0000000
那么:S2CON = (S2CON & 0x3f) | 0x40;的意思是:
位操作:s2con & 0x3f: 这一步是对 S2CON 寄存器的某些位进行清零操作。0x3f 的二进制表示是 00111111,与 S2CON 进行按位与操作会保留 S2CON 的低 6 位,并将高 2 位清零。
| 0x40: 这一步是设置 S2CON 的第 6 位(因为 0x40 的二进制表示是 01000000)。是把第6为置1