SPI FLASH(W25Q128BV) 包含SPI工作原理

目录

 

一、SPI简介

        1、全双工与半双工

         2、同步与异步

        3、SPI通信方式

二、SPI工作模式

三、W25Q128BV

        1、读ID Read Manufacturer/Device ID(90h)         

         2、读ID代码实现(硬件SPI)

         3、IO口模拟SPI时序图实现 (软件SPI)  模式3


一、SPI简介

        SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI是一种高速的(10Mbps),全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议,如NRF24L01、VS1053、SD卡等。

        1、全双工与半双工

                a:半双工常见于485总线,即下图所示,使用的是差分信号,这样子就决定了是半双工,即任何一个传输时刻,只有一个传输方向,要么是发送,要么是接收。优点在于传输距离远,理论上达到1.2km。

                 d:全双工常见SPI通信,即下图所示,两边可以同时收发数据,优点是速度快,延迟小。

         2、同步与异步

                 a:同步的典型特征:有时钟线,可以控制通信的速度。

                 b:异步的典型特征:约束好通信的波特率,数据帧格式.

        3、SPI通信方式

根据上图我们可以看到,由于SPI是全双工,同步的通信总线,SCLK,MOSI,MISO这三个引脚可以共用于从机,根据片选引脚(/SS)来区别来区别从机(输出低电平的从机有效工作),注意:硬件连接看起来是一对多,一个主机对应多个从机,但是实际上通信只能一对一通信,当一个硬件处于通信状态(输出低电平),其他硬件处于等待状态(输出高电平),不然容易出现数据的冲突

SCLK(Serial Clock):串行时钟线

MOSI(Master Output Slave Input):主机输出数据,从机接收数据  

MISO(Master Input Slave Output):从机输出数据,主机接收数据

/SS(slave select):从机选择,该引脚输出低电平,该引脚有效工作

二、SPI工作模式

SPI总线有四种工作模式,其中使用最为广泛的是模式0和模式3方式。

CPOL(Clock Polarity):时钟极性选择,为0时SPI总线空闲时,时钟线为低电平 ; 为1时SPI总线空闲时,时钟线为高电平。

CPHA(Clock Phase):时钟相位选择,为0时在SCLK第一个跳变沿,主机对MISO引脚电平采样;为1时在SCLK第二个跳变沿,主机对MISO引脚电平采样

通过上面两张图我们可以发现,模式0是从机先发送第一个数据,模式3是主机先发送第一个数据,它们之间有什么区别呢,其实没有什么区别,作用就是做好通信前的准备,约定好数据的采集时机

三、W25Q128BV

        W25Q128BV:内存128MBit/16MType,每个可编程页256字节,支持SPI模式0和模式3。具体可以看相关手册。由于手册繁多且复杂,这边就不多做描述。

关于SPI SLASH具体描述可参考博主:(15条消息) W25Q128数据手册阅读总结_百里之外的博客-CSDN博客https://blog.csdn.net/qq_40993639/article/details/122053404

        1、读ID Read Manufacturer/Device ID(90h)         

 本文我们讲一讲常用的指令中的一种,读ID Read Manufacturer/Device ID(90h),下图是完整的时序图,数据手册是分两个图来看,我这里合成一个图看着更方便一点。关于这个时序图的工作方式在手册有总结,是一段英文解释,我这边用自己的话解释这个时序图的工作原理。

在这个指令传输之前,我们要使/cs(/ss)引脚拉低移位传输指令码90h(1001 0000)对应时钟线(CLK)0-7,每一个时钟周期对应一个Bit(比特),紧接着我们要传输24-BIt(比特)地址(A23-A0),其对应时钟线(CLK)8-31,也就是3个字节地址,完成上诉工作之后,厂商ID和设备ID就会发给我们主机,通信结束后,使/cs(/ss)引脚拉高。

通过读ID往往能够检测到SPI通信是否正常,也能够知道从机设备是否在线。

         2、读ID代码实现(硬件SPI)

编程环境:keil5 STM32f407

 首先初始化w25q128。

GPIO_InitTypeDef  GPIO_InitStructure;
SPI_InitTypeDef  SPI_InitStructure;

void w25q128_init(void)
{

	//使能端口B的硬件时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
	//使能SPI1的硬件时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	
	
	//配置PB3~PB5为复用功能模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;	//指定3、4、5号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//配置为复用功能模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
	
	//将PB3~PB5连接到SPI1的硬件
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);		
	
	
	//配置PB14为输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;	//指定14号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为推挽功能模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
	//PB14初始电平状态为高电平,因为总线空闲的时候,SS引脚(片选引脚)为高电平
	PBout(14)=1;
	
	
	//SPI1参数的配置        //硬件SPI设置  模拟SPI不需要这个则删除掉
	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_High;//根据从机的手册来配置,模式3
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//根据从机的手册来配置,模式3,第二跳变沿

    //由软件代码控制片选引脚     SPI_NSS_Hard表示由硬件控制引脚
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    
    //波特率  根据从机的手册来配置,SPI的硬件时钟=84MHz/16=5.25MHz
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;

    //根据从机的手册来配置 MSB表示最高有效位传输
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_Init(SPI1, &SPI_InitStructure);
	
	
	//使能SPI1硬件工作
	SPI_Cmd(SPI1,ENABLE);

}

读ID

//官方代码,
uint8_t SPI1_SendByte(uint8_t byte)
{
  /*!< Loop while DR register in not emplty */
  //检测是否发送完毕
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);    

  /*!< Send byte through the SPI1 peripheral */
  //如果发送完,就把数据发送出去
  SPI_I2S_SendData(SPI1, byte);

  /*!< Wait to receive a byte */
  
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

  /*!< Return the byte read from the SPI bus */
   //对方返回一个字节回来
  return SPI_I2S_ReceiveData(SPI1);
}

void w25q128_read_id(uint8_t *m_id,uint8_t *d_id)
{
	W25Q128_SS=0;

	SPI1_SendByte(0x90);

	SPI1_SendByte(0x00);
	SPI1_SendByte(0x00);
	SPI1_SendByte(0x00);

	//这里参数随便填,从机会忽略引脚电平状态
	*m_id=SPI1_SendByte(0xFF);
	*d_id=SPI1_SendByte(0xFF);

	W25Q128_SS=1;	//拉高结束
}

获取厂商ID和设备ID并打印 

int main()
{
    uint8_t m_id,d_id;
    
    //串口初始化
    usart1_init(115200);
       
    w25q128_init();
    
    w25q128_read_id(m_id,d_id);

    printf("m_id=%X,d_id=%x\r\n",m_id,d_id);

    while(1)
    {
        
    }
}

打印结果 

  从0地址连续读64字节的数据实现        Read Data(03H)

 基本上不变,指令变为03H,后面就是传输连续字节,接收完一个字节,其指向会指向新的地址,每个地址都指向有效数据(会自动偏移)。

//通用写法
void w25q128_read(uint32_t addr,uint8_t *buf,uint32_t len)
{
	uint8_t *p = buf;
	
    //拉低开始
	W25Q128_SS=0;

	SPI1_SendByte(0x03);

	//假如有个地址为0x123456.
	SPI1_SendByte(addr>>16);    //0x12
	SPI1_SendByte(addr>>8);	    //0x34
	SPI1_SendByte(addr);	    //0x56
	
	while(len--)
	{
		*p=SPI1_SendByte(0xFF);
		p++;
	}


    //拉高结束
	W25Q128_SS=1;	
}	

 打印

         3、IO口模拟SPI时序图实现 (软件SPI)  模式3

 初始化模拟SPI w25q128

GPIO_InitTypeDef  GPIO_InitStructure;
SPI_InitTypeDef  SPI_InitStructure;
#define W25Q128_SS		PBout(14)
#define W25Q128_SCLK   	PBout(3)
#define W25Q128_MOSI   	PBout(5)
#define W25Q128_MISO	PBin(4)

void w25q128_init(void)
{

	//使能端口B的硬件时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);	
	
	
	//配置PB3 PB5 PB14为输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_5|GPIO_Pin_14;	//指定3、5、14号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	

	
	//配置PB4为输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;	//指定4号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//配置为输入模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
	//PB14初始电平状态为高电平,因为总线空闲的时候,SS引脚(片选引脚)为高电平
	W25Q128_SS=1;
	
	//CPOL=1,SCLK引脚在SPI总线空闲的时候为高电平
	W25Q128_SCLK=1;
	
	//MOSI引脚随意电平    可高可低
	W25Q128_MOSI=1;


}

读ID,只不过发送需要自己写

uint8_t SPI1_SendByte(uint8_t byte)
{
	int32_t i=0;
	
	uint8_t d=0;
	
    //一个周期的变化
	for(i=7; i>=0; i--)
	{
	
		if(byte & (1<<i))
			W25Q128_MOSI=1;
		else
			W25Q128_MOSI=0;
	
		
        
		W25Q128_SCLK=0;    //模式3单个周期  SCLK拉低
		delay_us(1);       //延时1微秒
		
		W25Q128_SCLK=1;    //模式3单个周期  SCLK拉高
		delay_us(1);       //延时1微秒
        
        //判断MISO是否是高电平
		if(W25Q128_MISO)
			d|=1<<i;
	
	}
	
	return d;
}


void w25q128_read_id(uint8_t *m_id,uint8_t *d_id)
{
	W25Q128_SS=0;

	SPI1_SendByte(0x90);

	SPI1_SendByte(0x00);
	SPI1_SendByte(0x00);
	SPI1_SendByte(0x00);

	
	*m_id=SPI1_SendByte(0xFF);
	*d_id=SPI1_SendByte(0xFF);

	W25Q128_SS=1;	
}
	

成功打印数据,与硬件SPI打印是一样的 

 本次就分享到这里,如看到我理解错的地方也请指正我,感谢各位的观看❤

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: W25Q128BV是一款闪存存储器,常用于存储设备中。该存储器容量为128MB,可提供可靠稳定的数据存储和快速读写速度。W25Q128BV中文手册中详细介绍了产品的特性和使用方法。 手册中首先介绍了W25Q128BV的主要特点,包括SPI接口、4KB授权、全面擦除和程序和数据区分离等。接下来,手册详细介绍了如何使用W25Q128BV进行数据读写和擦除。同时,手册还为用户提供了命令代码和示例,以方便用户在实际应用中使用。 此外,手册中还介绍了W25Q128BV的各项参数和规格,包括存储密度、工作电压、温度范围、封装类型等。用户可以根据这些参数选择合适的W25Q128BV存储器。 总之,W25Q128BV中文手册为用户提供了详尽的产品使用指南和技术参数,使用户在使用该产品时更加方便快捷。 ### 回答2: w25q128bv是一款容量为128Mb的串行闪存芯片,在很多嵌入式系统中被广泛使用。用户手册提供了丰富的且详细的文档资料,让用户可以更好地了解和使用这款芯片。 手册主要分为六个部分。第一部分是介绍,包括了芯片的特性、引脚输入输出、尺寸等基本信息。第二部分是功能描述,详细介绍了芯片的各项功能和操作命令。第三部分是命令表,给出了操作命令的格式、指令码、时序和参数信息。第四部分是寄存器参数表,给出了芯片内部寄存器的详细信息。第五部分是电气特性,讲述了芯片的电流、工作温度、电压等各项特性参数。最后是应用建议,提供了在工程实践中建议遵循的几个原则,方便用户更好地使用该芯片。 该手册内容详实、易于理解,能帮助用户学习和使用w25q128bv芯片。对于熟悉嵌入式开发的人员,手册已足够详细。而对于初学者而言,手册可以帮助他们尽快掌握该芯片的使用方法,能够独立完成相应的工作。 ### 回答3: w25q128bv是一种闪存存储器,其中文手册是一份详细的说明书,描述了其规格、功能以及使用方法。该手册包括如下内容: 1. 概述:介绍了w25q128bv的特点、主要功能和适用范围。同时也介绍了它的架构、引脚定义和电气特性等重要内容。 2. 访问模式:详细描述了w25q128bv所支持的读、写和擦除等访问模式,包括读取操作的时序图和写入/擦除操作的电气特性等。 3. 功能特性:列出了w25q128bv所支持的各种特性,包括软件保护、硬件保护、写保护和高速读取等。 4. 应用案例:给出w25q128bv的典型应用案例,介绍了它在存储器、嵌入式系统以及数据日志等方面的广泛应用场景。 5. 注意事项:提供了使用w25q128bv时需要注意的一些事项,包括电气特性、端口输入和输出等内容。 综上所述,w25q128bv中文手册是一份非常详尽的说明书,涵盖了硬件设计、软件开发以及其他相关方面信息,为用户提供了详细的使用指南和技术支持,可帮助用户更好地使用和维护该产品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值