中山网站建设工具猫,建筑网址大全网站,中国商务网官网,软件开发外包合同在学习过程中记录。
学习背景
最近在做的项目需要设计电路包含外扩FLASH#xff08;W25Q128#xff09;与SRAM(VTI7064)#xff0c;二者都用到了SPI通信协议#xff0c;之前没学过#xff0c;学习记录一下。
顺便说一下这次学习中发现的好用工具WPS AI。可以对文档进行…在学习过程中记录。
学习背景
最近在做的项目需要设计电路包含外扩FLASHW25Q128与SRAM(VTI7064)二者都用到了SPI通信协议之前没学过学习记录一下。
顺便说一下这次学习中发现的好用工具WPS AI。可以对文档进行总结归纳你可以对他提问相关的内容回答十分令人满意也是一次巧合我在更新WPS后本想翻译VTI7064的芯片手册全是英文属实难懂看到了WPS AI。之前充值的会员Pro他让我免费体验3个月时间过后估计要收费不过体验很好。 SPI通信协议是一种高速、全双工、同步的串行通信协议主要用于短距离通信。它使用主从模式进行通信其中主设备负责发起通信从设备负责响应通信。SPI通信协议使用四根信号线进行通信分别是MOSI、MISO、SCLK和CS。其中MOSI和MISO分别用于主设备向从设备发送数据和从设备向主设备发送数据SCLK用于同步数据传输CS用于选择要通信的从设备。SPI通信协议支持多种数据传输模式包括标准模式、双线模式、四线模式和QPI模式等。 相关原文: 5页 引脚 通常 SPI 通过 4 个引脚与外部器件相连 ● MISO 主设备输入 / 从设备输出引脚。该引脚在从模式下发送数据在主模式下接收数据。 ● MOSI 主设备输出 / 从设备输入引脚。该引脚在主模式下发送数据在从模式下接收数据。 ● SCK 串口时钟作为主设备的输出从设备的输入 ● NSS 从设备选择。这是一个可选的引脚用来选择主 / 从设备。它的功能是用来作为“片选引脚”让主设备可以单独地与特定从设备通讯避免数据线上的冲突。从设备的NSS引脚可以由主设备的一个标准I/O 引脚来驱动。一旦被使能 (SSOE 位 ) NSS 引脚也可以作为输出引脚并在SPI 处于主模式时拉低此时所有的 SPI 设备如果它们的 NSS 引脚连接到主设备的NSS 引脚则会检测到低电平如果它们被设置为 NSS 硬件模式就会自动进入从设备状态。当配置为主设备、NSS 配置为输入引脚 (MSTR1 SSOE0) 时如果 NSS被拉低则这个SPI 设备进入主模式失败状态即 MSTR 位被自动清除此设备进入从模式 在有些地方MISOSO、MOSISI、NSSCS、NSS、SS、SCKSCLK
只是称呼不同但意义相同。
模式介绍
刚开始学的时候对SPI的四种模式很是糊涂不过在回过头看数据手册的时序图就没那么复杂了下面我说一下我的理解。
SPI有四种模式模式0、模式1、模式2、模式3.这四种模式取决于SPI_CR1寄存器的位0CPHA与位1CPOL CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平此位对主模式和从模式下的设备都有效。如果CPOL被 清’0’SCK引脚在空闲状态保持低电平如果CPOL被置’1’SCK引脚在空闲状态保持高电平。 如果CPHA(时钟相位)位被置’1’SCK时钟的第二个边沿(CPOL位为0时就是下降沿CPOL位 为’1’时就是上升沿)进行数据位的采样数据在第二个时钟边沿被锁存。如果CPHA位被清’0’ SCK时钟的第一边沿(CPOL位为’0’时就是上升沿CPOL位为’1’时就是下降沿)进行数据位采 样数据在第一个时钟边沿被锁存。 CPOL 时钟极性和 CPHA 时钟相位的组合选择数据捕捉的时钟边沿。 图 212 显示了 SPI 传输的 4 种 CPHA 和 CPOL 位组合。此图可以解释为主设备和从设备的 SCK 脚、 MISO 脚、 MOSI 脚直接连接的主或从时序图。 先分析CPHA1、CPOL1根据上面介绍可以了解到对应的是SCK引脚在空闲状态保持高电平。
SCK时钟的第二个边沿也就是上升沿进行数据位的采样数据在第二个时钟边沿也是上升沿被锁存。
看一下数据时钟的时序图 在时钟的上升沿MISO信号线采集来自从机的信号同时MOSI信号线输出的数据被从设备锁存。
从图上看时钟信号线的两端都是高电平对应了出CPOL(时钟极性)位1. 模式0
CPOL0空闲状态时SCK保持低电平
CPHA0数据采样从第一个时钟边沿开始时钟上升沿进行数据采样。 模式1 CPOL0空闲状态时SCK保持低电平 CPHA1数据采样从第二个时钟边沿开始时钟下降沿进行数据采样。 模式2 CPOL1空闲状态时SCK保持高电平 CPHA0数据采样从第一个时钟边沿开始时钟下降沿进行数据采样。 模式3 CPOL1空闲状态时SCK保持高电平 CPHA1数据采样从第一个时钟边沿开始时钟下降沿进行数据采样。 CPOL与CPHA组成四种不同模式常用的是模式0.
SPI硬件框图
对于SPI的配置有软件模拟SPI与硬件SPI。软件的话可以是任意的IO口更加灵活但是对于软件编写比较麻烦。而硬件SPI是STM32内部自带的硬件电路功能相对软件会少很多但是操作简单。
SPI作为一种串行通信协议是如何进行通信呢
所谓通信实际是数据的交换数据的发送与接收将所要发送的数据转化为电平信号TTL发送出去而SPI的数据发送数据接收是依靠硬件上的移位寄存器以及发送缓冲区接收缓冲区实现的。
看一下SPI框图 下图是一个单主和单从设备互连的例子 数据发送过程 在写操作中数据字被并行地写入发送缓冲器。 当从设备收到时钟信号并且在 MOSI 引脚上出现第一个数据位时发送过程开始 ( 译注此时第一个位被发送出去) 。余下的位 ( 对于 8 位数据帧格式还有 7 位对于 16 位数据帧格式还有15位 ) 被装进移位寄存器。当发送缓冲器中的数据传输到移位寄存器时 SPI_SP 寄存器的 TXE标志被设置如果设置了SPI_CR2 寄存器的 TXEIE 位将会产生中断。 在时钟沿到来后主机的移位寄存器会在时钟沿将数据移出到MISO线进入从机同时主机的MOSI接收来自从机的数据到移位寄存器。数据的 接收过程同理。
初始状态 8个时钟周期过后 由此完成了一个字节数据的交换。
另外在通信十还会有一个开始与结束的状态这取决于NSS W25Q64
上面介绍了如何发送一个字节但是在实际应用中以W25Q128为例他需要先发送一个字节的指令码指令码后根据实际情况可以加其他字节比如用SPI对W25Q128写数据时先发送写数据的指令码随后发送地址就是要写在哪里最后是写的内容。
w25Q64写指令的代码是0x02随后写入24位的地址码最后加上指定的数据。
这里解释一下为什么是24位的地址码。W25QXX后面的两位是存储空间。
以W25Q64为存储空间为64M-bit8M字节、8MB2^23.一共23位
所以寻址时采用3字节24位足够查找全部的地址。
同理W25Q128是128M-bit16M字节16M2^24.需要24位的寻址空间。3字节24位刚好足够。 注意
写入数据操作时有几点注意事项
写入操作时 写入操作前必须先进行写使能 每个数据位只能由1改写为0不能由0改写为1 写入数据前必须先擦除擦除后所有数据位变为1擦除必须按最小擦除单元进行连续写入多字节时最多写入一页的数据超过页尾位置的数据会回到页首覆盖写入 写入操作结束后芯片进入忙状态不响应新的读写操作 读取操作时: 直接调用读取时序无需使能无需额外操作没有页的限制读取操作结束后不会进入忙状态但不能在忙状态时读取
芯片寄存器
W25Q64有两个状态寄存器 比较重要的的是BUSY位忙状态位和WEL写使能锁存位 11.1.1BUSY BUSY是状态寄存器S0中的只读位当设备执行页程序、扇区擦除、块擦除、芯片擦除或写入状态寄存器指令时BUSY位置1。在此期间时设备将忽略除读取状态寄存器和擦除挂起之外的其他指令指令参见AC特性中的tW、tPP、tSE、tBE和tCE。当编程时擦除或写入状态寄存器指令完成BUSY位将被清除为0状态表示设备准备就绪以获得进一步的指示。 11.1.2Write Enable LatchWEL 写使能锁存器WEL是状态寄存器S1中的只读位在执行写使能指令后该位被置1。当设备被禁止写时WEL状态位被清除为0。写禁用状态发生在上电时或以下任何指令之后写入禁用页面编程、扇区擦除、块擦除、芯片擦除和写入状态寄存器。
所以当要写入数据时都要调用一下该指令但是当写完后不需要调用写失能会默认发生写失能 指令集
看一下指令集表 根据这个表的内容就可以实现具体的操作了比如读取厂商的ID对W25Q64写入数据读出数据。都可以按照上面的格式进行操作。
1、Write Enable06h写使能
写使能指令图4将状态寄存器中的写使能锁位WEL位设置为1. WEL位必须在每个页面程序、扇区擦除、块擦除、芯片擦除和写入状态寄存器指令之前设置。写入使能指令是通过 /CS低移动指令代码06h进入CLK上升沿上的数据输入DI引脚然后驱动 /CS。 2、Read Status Register-1 (05h)读状态寄存器1
读状态寄存器指令允许读取8位状态寄存器。通过驱动/CS低电平并将状态寄存器-1的指令码“05h”和状态寄存器-2的指令码“35h”移到CLK上升沿的D引脚上进入指令。然后状态寄存器位在CLK下降沿的DO引脚上首先以最高有效位(MSB)移出如图6所示。状态寄存器位如图3a和3b所示包括BUSY,WELBP2-BPO.TB.SEC.SRPO.SRP1和QE位(参见本数据表前面对状态寄存器的描述) 读状态寄存器指令可以在任何时候使用即使在程序、擦除或写状态寄存器周期进行时也是如此。这允许检查BUSY状态位以确定周期何时完成以及设备是否可以接受另一条指令。可以连续读取状态寄存器如图6所示。指令由驱动/CS高完成。
使用该指时首先发送05H指令码告诉W25Q64让他执行对应的操作随后交换一个字节得到的数据就是状态寄存器的值了。
3、Page Program (02h) 页编程
Page Program指令允许在先前擦除(FFh)内存位置对1到256字节(一页)的数据进行编程。在设备接受页程序指令(状态寄存器位WEL 1)之前必须执行写使能指令。该指令是通过驱动/CS引脚低然后将指令代码“02h”后面跟着一个24位地址(A23-A0)和至少一个数据字节移动到DI脚来启动的。当数据被发送到设备时/CS引脚必须在指令的整个长度内保持低电平。Page Program指令序列如图15所示。 如果要对整个256字节的页面进行编程则最后一个地址字节(8个最低有效地址位)应设置为0如果最后一个地址字节不为零并且时钟的数量超过了剩余的页面长度则寻址将封装到页面的开头。在某些情况下可以编程少于256字节(部分页)而不会对同一页中的其他字节立生任何影响。执行部分页程序的一个条件是时钟的数量不能超过剩余的页长度。如果发送到设备的字节超过256个寻址将封装到页的开头并覆盖以前发送的数据。 与写和擦除指令一样在最后一个字节的第8位锁存之后/CS引脚必须被驱动到高位。如果不这样做页面程序指令将不会被执行。当/CS被调高后自定时的页面程序指令将在tpp的时间段内开始(参见AC特性)。当页程序周期正在进行时读取状态寄存器指令仍然可以被访问以检查BUSY位的状态。BUSY位在页程序周期中为1当周期结束并且设备准备好再次接受其他指令时变为0。在页面程序周期完成后状态寄存器中的写使能锁存(WEL)位被清除为0.如果寻址页被块保护(BP2,BP1和BPO)位保护则页面程序指令将不会被执行。 页编程顾名思义是对W25Q64存储器的其中一页进行编程的那么什么是页
W25Q64存储空间有8M在内部将8M的存储空间进行划分分为块、扇区、页。
8M分为128块每块64k。
每块分为16扇区每个扇区4k。
每扇区分为16页每页256位。
看内部框图 在进行页编程的时候是对页操作将数据写入页。
先输入指令码02h随后输入页地址建议输入时页地址的首位随后输入数据最大可以输入256位
4、Sector Erase (20h)扇区擦除
扇区擦除指令将指定扇区内的所有内存(4k字节)设置为所有15 (FFh)的擦除状态。在设备接受扇区擦除指令(状态寄存器位WEL必须等于1)之前必须执行Write Enable指令。该指令通过将/CS引脚驱动为低电平并将指令码“20h”移到24位扇区地址 在最后一个字节的第8位锁存之后/CS引脚必须被驱动到高位。如果没有这样做扇区擦除指令将不会被执行。在/CS被驱动到高电平后自定时扇区擦除指令将开始tSE的持续时间(参见AC特性)。当扇区擦除周期正在进行时读取状态寄存器指令仍然可以被访问以检查BUSY位的状态。在扇区擦除周期中BUSY位为1当周期结束设备准备好再次接受其他指令时BUSY位变为O。在扇区擦除周期结束后状态寄存器中的写使能锁存(WEL)位被清除为0。如果寻址页被块保护(SEC、TB、BP2、BP1和BPO)位保护(参见状态寄存器内存保护表)则扇区擦除指令将不会执行。 扇区擦除先输入指令码随后输入地址码即可擦除4kb的扇区擦除后数据默认为0xFF4k扇区内全部是FF。
5、Read JEDEC ID (9Fh)读设备号
出于兼容性原因W25Q64BV提供了几个指令以电子方式确定设备的身份。读取JEDEC ID指令与2003年采用的SPI兼容串行存储器的JEDEC标准兼容。指令通过驱动/CS引脚低电平并移动指令码“9Fh”来启动。JEDEC为Winbond分配的制造商ID字节(EFh)和两人设备ID字节内存类型(D15-ID8)和容量(ID7-IDO)然后在CLK的下隆沿上以最高有效位(MSB)先移出如图29所示。有关内存类型和容量值请参阅制造商和设备标识表。 首先输入指令码随后交换三个字节。读出的三个字节就是厂商ID和设备ID 6、Read Data (03h)读数据
读取数据指令允许从内存中依次读取一个数据字节。该指令是通过驱动/CS引脚低然后将指令代码“03h”和24位地址(A23-AO)转移到DI引脚来启动的。代码和地址位锁存在CLK引脚的上升沿上。接收到地址后寻址内存位置的数据字节将首先以最高有效位(MSB)在CLK下降沿的DO引脚上移出。在每个字节的数据被移出后地址自动增加到下个更高的地址从而允许连续的数据流。这意味着只要时钟继续就可以用一条指令访问整个内存。指令由驱动/CS高完成。 Read Data指令序列如图8所示。如果在擦除、编程或写周期(BUSY1)进行中发出读数据指令则该指令将被忽略并且不会对当前周期产生任何影响。读取数据指令允许时钟速率从直流到最大fR(见交流电气特性)。 发送指令码发送24位地址交换三个字节从W25Q64的DO线上输出所在地址的数据内容。
电气特性 软件模拟SPI
W25Q64是通过操作指令集来实现各种功能的如写使能读数据等。
我们必须需要用软件来模拟出他发送字节的时序这样就能实现发送字节的时序。
写使能指令只有一个字节发送字节06h进行写使能再来看写使能的时序图
使用SPI模式0
CPOL0空闲状态时SCK保持低电平
CPHA0数据采样从第一个时钟边沿开始时钟上升沿进行数据采样。 由时序图可以看到发送一个字节时
1、单片机的MOSI输出一个低电平发送1位信号0
2、将时钟信号线从低电平拉高到电平产生一个上升沿。W25Q64从第一个时钟边沿进行数据采样也就是上升沿
3、W25Q64读取到移位数据的同时会在DO引脚移除1位数据单片机的MISO接收这位数据并判断是否为1
4、拉低时钟信号线产生一个时钟周期。
这是发送1位数据的步骤发送8位时循环即可。
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive 0x00;for (i 0; i 8; i ){MySPI_W_MOSI(ByteSend (0x80 i)); //单片机发送数据W25Q64读入MySPI_W_SCK(1); //产生上升沿if (MySPI_R_MISO() 1){ByteReceive | (0x80 i);} //单片机读入数据W25Q64发送MySPI_W_SCK(0); //产生下降沿}return ByteReceive;
}
上面代码是江科大的源码
他在教程上对时序是另一种解释在SCK第一个边沿移入数据第二个边沿移出数据 在循环里实现在SCK第一个边沿上升沿移入数据第二个边沿下降沿移出数据循环8次后就完成了一个字节的发送。
在发送字节的基础上可以实现指令集的所用功能。
main.c
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include W25Q64.h
#include usart.huint8_t MID;
uint16_t DID;uint8_t ArrayWrite[] {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];int main(void)
{OLED_Init();W25Q64_Init();uart_init(9600);printf(数字1\r\n);OLED_ShowString(1, 1, MID: DID:);OLED_ShowString(2, 1, W:);OLED_ShowString(3, 1, R:);W25Q64_ReadID(MID, DID);OLED_ShowHexNum(1, 5, MID, 2);OLED_ShowHexNum(1, 12, DID, 4);printf(MID:%x,DID:%x,MID,DID);W25Q64_SectorErase(0x000000);W25Q64_PageProgram(0x000000, ArrayWrite, 4);W25Q64_ReadData(0x000000, ArrayRead, 4);OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}myspi.c
#include stm32f10x.h // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);MySPI_W_SS(1); //模式0 片选信号为高电平不选中MySPI_W_SCK(0); //模式0 时钟信号空闲状态为低电平
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive 0x00;for (i 0; i 8; i ){MySPI_W_MOSI(ByteSend (0x80 i)); //单片机发送数据W25Q64读入MySPI_W_SCK(1); //产生上升沿if (MySPI_R_MISO() 1){ByteReceive | (0x80 i);} //单片机读入数据W25Q64发送MySPI_W_SCK(0); //产生下降沿}return ByteReceive;
}myspi.h
#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endifw25q64.c
#include stm32f10x.h // Device header
#include MySPI.h
#include W25Q64_Ins.hvoid W25Q64_Init(void)
{MySPI_Init();
}void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(W25Q64_JEDEC_ID);*MID MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID 8;*DID | MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);Timeout 100000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) 0x01) 0x01){Timeout --;if (Timeout 0){break;}}MySPI_Stop();
}void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address 16);MySPI_SwapByte(Address 8);MySPI_SwapByte(Address);for (i 0; i Count; i ){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address 16);MySPI_SwapByte(Address 8);MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address 16);MySPI_SwapByte(Address 8);MySPI_SwapByte(Address);for (i 0; i Count; i ){DataArray[i] MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}w25q64.h
#ifndef __W25Q64_H
#define __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);#endifw25q64_ins.h
#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3#define W25Q64_DUMMY_BYTE 0xFF#endif硬件实现SPI
硬件实现利用了STM32F103C8T6内部的SPI电路通过初始化配置对应的IO口完成SPI操作。
硬件SPI将字节发送字节上更加简单易懂。所以只需要修改一下SPI内的函数就可以实现。实现了字节的交换其他指令就不在话下了。
myspi.c
#include stm32f10x.h // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Mode SPI_Mode_Master; //设置为主SPISPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; //SPI 设置为双线双向全双工SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; //SPI 发送接收 8 位帧结构SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; //数据传输从 MSB 位开始SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_128;//波特率预分频值为 128SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; //时钟悬空低SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; //数据捕获于第一个时钟沿SPI_InitStructure.SPI_NSS SPI_NSS_Soft; //内部 NSS 信号有 SSI 位控制SPI_InitStructure.SPI_CRCPolynomial 7; //SPI_CRCPolynomial 定义了用于 CRC 值计算的多项式。(不使用SPI_Init(SPI1, SPI_InitStructure);SPI_Cmd(SPI1, ENABLE);MySPI_W_SS(1);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) ! SET);SPI_I2S_SendData(SPI1, ByteSend);while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) ! SET);return SPI_I2S_ReceiveData(SPI1);
}到此对W25Q64的学习到一段落。
VTI7604
VTI7604是一款串行SRAM。支持1.8V和3.0V SPI/QPI接口的64Mbit串行SRAM器件。该器件可以配置1位输入/输出接口或4位I/O通用接口。所有必要的刷新操作由器件本身处理。
8M*8bit8Mbyte。对8M字节进行寻址需要2^23共23位所以寻址时用A[22:0]。 这次我将学习VTI7064的串行SPI操作
接口电路 读芯片手册
从重置设备初始化的命令序列时序图可以分出VTI7064采用SPI模式0 接口说明 4.4介绍到任何操作指令前需要将CE拉高。这里我是有点不理解的CE#是片选信号低电平有效为什么在操作前要拉高我觉得应该是拉低选中VTI7604。 4.6命令终止 所有所有的读取和写都必须由CE#低到高完成。这个CK#上升边可以终止读写激活的字行并将设备设置为待机状态。不这样做将阻止内部刷新权限直到设备看到读/写字行终止。命令终止操作不仅需要读写操作和任何命令操作如输入四元模式命令和重置命令。 起始条件和终止条件同W25Q64是一样的后面的时序图可以更好的看出来。 指令集 设备上电后默认是SPI模式但是也可以切换到QPI模式
5.1 SPI Read Operations读指令 0x03: Serial CMD, Serial IO, slow frequency 读指令有三种模式介绍0x03指令。
1、拉低CE#
2、发送指令0x03
3、发送地址
4、接收数据在时钟下降沿接收数据 快速读0x0B相对于0x03多了8个等待周期但是最大频率由33M上升到104M
0xEB是四线SPI不做讨论
5.2 SPI Write Operations写指令 5.4 SPI Read ID Operation读设备ID 7.Reset Operation 上电后默认操作
8.Input / Output Timing 10.Code Information: VTI7604相对W25Q64来说指令少了很多很多。
程序代码
目前pcb没有到手电路没焊接代码没写。写完测试好后会发布。