找回密码
 立即注册
查看: 72|回复: 36

25.SPI读写W25X40CL - 按k1

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 3 天前 | 显示全部楼层 |阅读模式
各位大侠:

   这几天跟着冲哥学习STC32G12K128 其中实验 《25.SPI读写W25X40CL 》

如果写入和读出的地址是0X000000 开头,就基本正常

但我把地址改成:


#define     W25X40CL_ADDRESS  0x000100  //保存的W25X40CL起始页地址,我们改用第1页,0X000100--0X00001ff


好像就不行了

只有按K1  马上就假死机  好像CDC串口就出问题了

还有一个问题:我发现保存是数据似乎不准确,保存10个温度,再读回来,好像数据不对

难道我理解错了?

我是这样理解的:

W25X40C 是一款 4M-bit(512KB)的串行闪存芯片,采用分页结构管理存储空间。其地址分配如下:
关键参数

  • 容量4M-bit512KB),地址范围为 0x000000 0x07FFFF(十六进制)。
  • 页大小256字节(标准SPI闪存页大小)。
  • 总页数512KB ÷ 256B = 2048
页地址计算
每一页的起始地址可通过以下公式计算:
页地址 = 页号 × 2560x100
其中,页号范围为 0 到 2047(十进制)。
示例:

  • 00x000000
  • 10x000100
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 串口)

实在没办法差到底是哪里出问题了,恳请帮忙!!!!

Demo.uvproj

14.04 KB, 下载次数: 0

回复

使用道具 举报 送花

3

主题

1860

回帖

568

积分

等待验证会员

积分
568
发表于 3 天前 | 显示全部楼层
各位大侠好:

关于在使用 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):
  
  1. c
  2.    W25X40CLEraseSector(W25X40CLADDRESS & 0xFFFFF000); // 对齐到扇区边界
复制代码

2. 写入前等待擦除完成(轮询 BUSY 位);
3. 写入数据:
  
  1. c
  2.    W25X40CLPageProgram(W25X40CLADDRESS, buffer, length);
复制代码
4. 写入后等待写入完成;
5. 读取验证:
  
  1. c
  2.    W25X40CLReadData(W25X40CLADDRESS, readbuffer, length);
复制代码

三、推荐调试方法

1. 使用调试串口输出关键信息(如地址、写入数据、读取数据等);
2. 用 LED 指示程序运行状态(如写入开始、写入完成、读取开始等);
3. 使用逻辑分析仪分析 SPI 通信过程;
4. 逐步调试:先测试 Page 0 是否正常,再测试 Page 1;
5. 测试单个写入与读取操作是否正确,再测试连续多个页的读写。

四、结论与建议

综合分析:
W25X40CL 的地址设置为 0x000100 是合法的,只要确保擦除和写入流程正确;
死机问题大概率与 SPI 通信异常或资源冲突有关,建议检查中断处理与 SPI 状态;
数据不一致问题多由未擦除或通信错误导致,应确保擦除写入流程完整;
建议严格按照 Flash 写入流程进行操作:擦除 → 写入 → 等待完成 → 读取验证。

如能提供更详细的代码片段或现象截图(如串口输出内容、逻辑分析仪波形),可进一步帮助定位问题。

祝调试顺利!

国芯人工智能 敬上

[本文内容由国芯人工智能辅助生成,仅供参考]
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 12:49 | 显示全部楼层
想了一下,还是要从基础干起:


先复习以下内容:

我们借用STC的程序:

32-硬件SPI访问FLASH-PM25LV040-串口2监控(成功)

一步一步的复习以下串口最基本的操作:

1 UART2 初始化:

  1. //========================================================================
  2. // 函数: void UART2_config(u8 brt)
  3. // 描述: UART2初始化函数。
  4. // 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
  5. // 返回: none.
  6. // 版本: VER1.0
  7. // 日期: 2014-11-28
  8. // 备注:
  9. //========================================================================
  10. void UART2_config(u8 brt)    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
  11. {
  12.     if(brt == 2)
  13.     {
  14.         SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));
  15.         S2CFG |= 0x01;     //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
  16.         S2CON = (S2CON & 0x3f) | 0x40;    //UART2模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
  17.         ES2   = 1;         //允许中断
  18.         S2REN = 1;         //允许接收
  19.         S2_S  = 1;         //UART2 switch to: 0: P1.0 P1.1,  1: P4.6 P4.7
  20.         B_TX2_Busy = 0;   //发送忙标志清零
  21.         TX2_Cnt = 0;          //发送计数清零
  22.         RX2_Cnt = 0;         //接收计数清零
  23.        
  24.     }
  25. }
复制代码


32-硬件SPI访问FLASH-PM25LV040-串口2监控(成功).zip

481.48 KB, 下载次数: 0

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 12:51 | 显示全部楼层
串口2配置寄存器(S2CFG)



S2CFG |= 0x01;     //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误

这句就好懂
截图202510201251101547.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 12:55 | 显示全部楼层
SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));


这句就是定时器2作为时钟脉冲的设置:

是怎么来的?
18.4.5串口2 模式1,模式1波特率计算公式
当软件设置S2CON的S2SMO、S2SM1为"01”时,串行口2则以模式1进行工作。此模式为8位1
UART格式,一帧信息为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"。


看看要求的脉冲时序图:


截图202510201255148168.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 12:56 | 显示全部楼层
串口 2 的波特率是可变的,其波特率固定由定时器 2 产生。当定时器采用 1T 模式时(12 倍速),相应的波特率的速度也会相应提高 12 倍。


截图202510201256064053.jpg
截图202510201256201543.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 12:59 | 显示全部楼层
#define    Baudrate2           115200L
我们采用的波特率为Baudrate2时的定时器2重载值:65536UL - (MAIN_Fosc / 4) / Baudrate2
很明显我们是1T模式。

一会儿我们在复习定时器2 的设置:


回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:00 | 显示全部楼层
接下来看看串口2脚位设置:
串口1就有4组脚位切换,串口2就只有2组脚位:我们程序选1
S2_S = 1;         //UART2 switch to: 0:P1.0 P1.1,  1: P4.6 P4.7
所以引脚硬件连线,我们要找P4.6 –RxD2  P4.7 T xD2

截图202510201300028130.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:04 | 显示全部楼层
到最重要的:

S2CON = (S2CON& 0x3f) | 0x40;    //UART2模式, 0x00: 同步移位输出, 0x40:8位
//数据,可变波特率, 0x80:9位数据,固定波特率, 0xc0: 9位数据,可变波特率
//S2CON最终选模式1:0x40: 8位数据,可变波特率



先看下图:


截图202510201304353582.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:06 | 显示全部楼层
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



回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-10-22 06:54 , Processed in 0.139825 second(s), 97 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表