前面在使用CH582M和STCAi8051U读写W25Q64的处理中,感觉有不一样的地方,所以本次单独为W25Q64的读写时序开一篇学习帖。
根据W25Q64的数据手册,对于读取JEDEC ID指令,有如下说明:
出于兼容性考虑,W25Q64JV 提供了多条用于电子识别器件身份的指令。读取 JEDEC ID 指令兼容于 2003 年采用的 JEDEC 标准(适用于 SPI 兼容串行存储器)。该指令通过将 /CS 引脚拉低并移位输入指令码“9Fh”来启动。随后,JEDEC 为华邦(Winbond)分配的制造商 ID 字节(EFh)以及两个器件 ID 字节——存储器类型(ID15-ID8)和容量(ID7-ID0)——将在 CLK 的下降沿以最高有效位(MSB)在先的方式移出,如图 43 所示。有关存储器类型和容量值,请参阅制造商与器件标识表。
注意,这里“JEDEC 为华邦(Winbond)分配的制造商 ID 字节(EFh)以及两个器件 ID 字节——存储器类型(ID15-ID8)和容量(ID7-ID0)——将在 CLK 的下降沿以最高有效位(MSB)在先的方式移出”,明确说明是在时钟信号的下降沿按照MSB → LSB的方式输出的,这一点,在STCAi8051U中是已经被证明了的。本次实验中,仍旧使用CH582M单片机,以GPIO口软件模拟方式输出CS、SCK、MOSI,输入MISO数据。实际操作上,以许可范围内的SCK时钟,按照固定时间间隔从MOSI引脚输出0x9F指令,然后利用逻辑分析仪捕捉CS、SCK、MOSI、MISO信号,确认JEDEC ID数据在SCK中所处位置。
代码如下:
/*********************************************************************
* @fn void SendByte(uint8_t d)
*
* @brief 发送单字节
*
* @param d - 发送字节
*
* @return none
*/
void SendByte(uint8_t d) {
uint8_t i= 0;
W25Q64_SCK_L(); // SCK = 0
while (i < 8) {
W25Q64_SCK_L(); // SCK = 0
DelayUs(1); // 延迟1微秒
if (d & 0x80) {
W25Q64_MOSI_H();
} else {
W25Q64_MOSI_L();
}
W25Q64_SCK_H(); // SCK = 1
DelayUs(1); // 延迟1微秒
// 左移
d<<=1;
i++;
}
W25Q64_SCK_L(); // SCK = 0
}
int main(void) {
uint8_t tx_buf[256];
uint8_t rx_buf[256];
uint32_t flash_id;
uint16_t i;
uint8_t err = 0;
/* 系统时钟初始化 */
SetSysClock(CLK_SOURCE_PLL_60MHz);
/* 调试串口初始化 */
DebugInit();
PRINT("CH582M:软件模拟 + W25Q64:读取 JEDEC ID\r\n");
DelayMs(100);
GPIOA_SetBits(GPIO_Pin_12);
/* PA12(CS)、PA13(SCK)、PA14(MOSI) 推挽输出 */
GPIOA_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
/* PA15(MISO) 上拉输入 */
GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);
while (1) {
/* 发送《读取 JEDEC ID》的指令0x9F */
W25Q64_CS_L(); // CS = 0
SendByte(W25X_JEDEC_ID);
SendByte(0xFF);
SendByte(0xFF);
SendByte(0xFF);
W25Q64_CS_H(); // CS = 1
DelayUs(100);
}
}逻辑分析仪获得的信号: 
为了方便分析,截取数据0x17部分的逻辑信号: 
放大后:

由信号可以看到,MISO信号的确是在SCK的下降沿位置处开始发生变化的,并持续到下一次SCK的下降沿来到之前的整段时间内。这也就能解释得通,为什么在CH582M的SPI0外设操作中,使用模式0下能在SCK的上升沿处获得正确的数据了。因为数据在下一次SCK的上升沿位置处依旧保持着正确的数据。
我要赚赏金
