• STM32的SPI


    泉水

    SPI介绍

    • SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。

    • SPI,是一种高速的,全双工,同步的通信总线。

    • SPI 主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

    应用

    在这里插入图片描述

    SPI内部结构图

    在这里插入图片描述

    详细的内部图:
    在这里插入图片描述在这里插入图片描述

    仔细看右下角有两个关键寄存器,CR1和CR2。
    SPI 接口一般使用 4 条线通信:
    MISO 主设备数据输入,从设备数据输出。Master input slaver output,主如从出。

    • 寄存器通过 MOSI 信号线将字节传送给从机,从机也将自己的移位寄存器中的内容通过 MISO 信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
    • 外设的写操作和读操作是同步完成的。

    • MOSI 主设备数据输出,从设备数据输入。
    • SCLK 时钟信号,由主设备产生。
    • CS(NSS) 从设备片选信号,由主设备控制,从设备也需要进行相应的设置。可让 SPI主器件与从器件进行单独通信,从而并避免数据线上的竞争。

    • 从器件的 NSS 输入可由主器件上的标准 IO 端口驱动,这是当外部NSS处于高电平时的玩法。
    • NSS 引脚在使能(SSOE 位)时还可用作输出,可在SPI 处于主模式配置时驱动为低电平
      • 只要器件配置成 NSS 硬件管理模式,所有连接到该主器件 NSS 引脚的其它器件 NSS 引脚都将呈现低电平,并因此而作为从器件。
      • 当配置为主模式,且 NSS 配置为输入(MSTR=1 且 SSOE=0)时,如果NSS 拉至低电平,SPI 将进入主模式故障状态:MSTR 位自动清零,并且器件配置为从模式
        load_url2

    SPI介绍

    参考 中文参考手册 721
    SPI 接口提供两个主要功能,支持 SPI 协议或 I 2 S 音频协议。 默认情况下,选择的是 SPI 功能。可通过软件将接口从 SPI 切换到 I 2 S。

    SPI相关的寄存器

    SPI 控制寄存器 1(SPI_CR1)

    在这里插入图片描述

    9位SSM,8位SSI

    • SSM:软件从设备管理 (Software slave management)SSM置位时,NSS输入引脚的电平将被SSI的值代替。

    • SSI:内部从设备选择 (Internal slave select)

    • SSOE:SS输出使能 (SS output enable)

    • MSTR:主设备选择 (Master selection)

    在这里插入图片描述

    • 主设备和从设备在进行SPI通信的时候,从设备都有个CS片选信号,低电平有效,我们通常都要用这个NSS连到从设备的CS上。
    • 控制内部NSS引脚与SSI(一个寄存器)相连(软件模式),还是与外部NSS引脚(硬件引脚)相连(硬件模式)。
      在这里插入图片描述
      在这里插入图片描述

    所谓输入,就是NSS的电平信号给自己,所谓输出,就是将NSS的电平信号发送出去,给从机。
    在这里插入图片描述

    456位

    设置SPI2的速度
    在这里插入图片描述

    2位 MSTR

    • 当SPI工作在从模式(MSTR=0)

    • 当SPI工作在主模式配置(MSTR=1)

      • 当一个SPI设备需要发送广播数据,它必须拉低NSS信号,以通知所有其它的设备它是主设备;如果它不能拉低NSS,这意味着总线上有另外一个主设备在通信,这时将产生一个硬件失败错误(Hard Fault)。

    SPI数据寄存器2(SPI_CR2)

    在这里插入图片描述

    2位SSOE

    SS 输出使能 (SS output enable)
    0:在主模式下禁止 SS 输出,可在多主模式配置下工作
    1:在主模式下使能 SS 输出,不能在多主模式环境下工作
    注意: 不适用于 I 2 S 模式和 SPI TI 模式

    1 位TXDMAEN

    发送缓冲区 DMA 使能 (Tx buffer DMA enable)
    当此位置 1 时,每当 TXE 标志置 1 时,即产生 DMA 请求。
    0:关闭发送缓冲区 DMA
    1:使能发送缓冲区 DMA

    0位 RXDMAEN

    接收缓冲区 DMA 使能 (Rx buffer DMA enable)
    当此位置 1 时,每当 RXNE 标志置 1 时,即产生 DMA 请求。
    0:关闭接收缓冲区 DMA
    1:使能接收缓冲区 DMA

    SPI 数据寄存器(SPI_DR)

    在这里插入图片描述

    SPI工作模式

    SPI总线有四种工作方式。主要输出串行同步时钟极性和相位可以进行配置。
    重点:每个flash手册中会注明使用的工作模式,若没直接注明,需要根据flash命令的时序自行判断。

    • 时钟极性
      如果CPOL=0,串行同步时钟的空闲状态为低电平;
      如果 CPOL=1,串行同步时钟的空闲状态为高电平。
    • 时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。
      如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;
      如果 CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。
    • SPI 主模块和与之通信的外设备时钟相位和极性应该一致。

    不同时钟相位下的总线数据传输时序如图
    在这里插入图片描述

    STM32F4 的 SPI 功能很强大,SPI 时钟最高可以到 37.5Mhz,支持 DMA,可以配置为 SPI协议或者 I2S 协议(支持全双工 I2S)。

    SPI中断

    在这里插入图片描述

    SPI使用步骤

    我们将利用 STM32 的 SPI 来读取外部 SPI FLASH 芯片(W25Q128),实现类似IIC的功能。

    使能 SPI2 的时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );//PORTB 时钟使能
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE );//SPI2 时钟使能
    
    • 1
    • 2

    配置相关引脚的复用功能

    这里使用 PB13、14、15 这 3 个(SCK.、MISO、MOSI,CS 使用软件管理方式),所以设置这三个为复用 IO。

    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 GPIOB
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    SPI2的引脚在PB上,可以参考W25Q128硬件连接图。
    在这里插入图片描述

    初始化 SPI2, 设置 SPI2 工作模式

    这在库函数中是通过 SPI_Init 函数来实现的。

    void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
    • 1

    SPI_InitTypeDef 的定义:

    typedef struct
    {
    uint16_t SPI_Direction;
    uint16_t SPI_Mode;
    uint16_t SPI_DataSize;
    uint16_t SPI_CPOL;
    uint16_t SPI_CPHA;
    uint16_t SPI_NSS;
    uint16_t SPI_BaudRatePrescaler;
    uint16_t SPI_FirstBit;
    uint16_t SPI_CRCPolynomial;
    }SPI_InitTypeDef;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里挑取几个重要的成员变量讲解一下:
    • SPI_Direction 用来设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式

    SPI_Direction_2Lines_FullDuplex //全双工
    
    • 1

    • SPI_Mode 用来设置 SPI 的主从模式

    SPI_Mode_Master  主机模式
    SPI_Mode_Slave //从机模式
    
    • 1
    • 2

    • SPI_DataSize为 8 位还是 16 位帧格式选择项

    SPI_DataSize_8b
    
    • 1

    • SPI_CPOL 用来设置时钟极性

    SPI_CPOL_High 串行同步时钟的空闲状态为高电平
    
    • 1

    • SPI_CPHA 用来设置时钟相位,就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集

     SPI_CPHA_2Edge,或者1Edge
    
    • 1

    • SPI_NSS 设置NSS 信号由硬件(NSS 管脚)还是软件控制

     SPI_NSS_Soft //软件
    
    • 1

    • SPI_BaudRatePrescaler设置 SPI 波特率预分频值决定 SPI 的时钟的参数,从不分频道 256 分频 8 个可选值

    SPI_BaudRatePrescaler_256 //256 分频值
    
    • 1

    传输速度为 36M/256=140.625KHz。2-156,凡是2的几次方都可以。
    • SPI_FirstBit ,设置数据传输顺序是 MSB 位在前还是 LSB 位在前

    SPI_FirstBit_MSB 高位在前
    
    • 1

    • SPI_CRCPolynomial 来设置 CRC 校验多项式,提高通信可靠性,大于 1 即可。

    初始化代码:

    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
    SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
    SPI_Init(SPI2, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使能SPI2

    使能 SPI2 的方法是:

    SPI_Cmd(SPI2, ENABLE); //使能 SPI 外设
    SPI2_ReadWriteByte(0xff); //④启动传输,主机发一个字节,进行一次传输,可以启动传输
    
    • 1
    • 2

    SPI传输数据

    发送数据函数

    void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

    接收数据函数

    uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;

    查看 SPI 传输状态函数

    判断数据是否传输完成,发送区是否为空
    判断接收是否完成,接收区是否空

    接收
    SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)
    • 1
    发送
    SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) 
    
    • 1

    设置SPI2速度函数

    单独的设置分频系数的函数

     
    //SPI 速度设置函数
    //SpeedSet://SPI_BaudRatePrescaler_256 256 分频 (SPI 281.25K@sys 72M)
    void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
    {
    	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
    	SPI2->CR1&=0XFFC7;
    	SPI2->CR1|=SPI_BaudRatePrescaler; //设置 SPI2 速度
    	SPI_Cmd(SPI2,ENABLE);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 参考SPI 控制寄存器 1(SPI_CR1)

    读写一个字节

    u8 SPI2_ReadWriteByte(u8 TxData)
    {
    	u8 retry=0;
    	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //等待发送区空
    	{
    		retry++;//重试
    		if(retry>200)return 0;
    	} //读取两百次还没有值,说明无效,返回
    	SPI_I2S_SendData(SPI2, TxData); //通过外设 SPIx 发送一个数据
    	retry=0;
    	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //等待接收完一个 byte
    	{
    		retry++;
    		if(retry>200)return 0;
    	}
    	return SPI_I2S_ReceiveData(SPI2); //返回通过 SPIx 最近接收的数据
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    W25Q128

    • W25Q128 是华邦公司推出的大容量 SPI FLASH 产品,W25Q128 的容量为 128Mb,该系列还有 W25Q80/16/32/64等。

    容量

    在这里插入图片描述
    24位地址

    • 16M ,2的24次方。
    • 256 个块(Block)。2的8次方
    • 每个块大小为 64K 字节。
    • 每个块又分为16 个扇区(Sector)2的4次方
    • 每个扇区 4K 个字节。2的12次方
    • 每页256字节
      共有2的24次方
      块地址8位+扇区地址4位+偏移地址12位。

    擦除

    W25Q128 的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。
    这样要求芯片必须有 4K 以上 SRAM 才能很好的操作。

    • W25Q128 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,
    • W25Q128 支持标准的 SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出时相当于 160Mhz,四输出时相当于 320M)。

    在这里插入图片描述

    写之前,必须擦除对应扇区内容,也就是确保其中的值是0xFFF。但是擦除的最小单位是扇区,也就是4K。所以在擦除之前我们先将这个扇区的数据读取出来,保存在缓存区。在缓存中将对应的地址更新之后,一次性将数据写到对应的sector之中。

    W25QXX驱动解读

    w25qxx.c,里面编写的是与 W25Q128 操作相关的代码

    W25QXX.h

    在这里插入图片描述

    W25QXX_CS片选,值0选定,1取消

    初始化SPI

    在这里插入图片描述
    先不选中
    速度设置最高,因为操作SPI flash读写数据速度越快越好
    读取ID的函数

    读取状态寄存器

    在这里插入图片描述

    片选和取消片选
    第一句写得是个指令
    第二句才是读取,写了一个空

    写状态寄存器

    在这里插入图片描述
    写了两次,同样一个指令和一个字节

    擦除一个扇区

    在这里插入图片描述
    先写使能
    等待不繁忙
    片选
    写指令
    写地址开始
    写24位,取消片选,flash擦除100多毫秒的一个扇区。

    读取 SPI FLASH

    在这里插入图片描述

    //在指定地址开始读取指定长度的数据
    //pBuffer:数据存储区
    //ReadAddr:开始读取的地址(24bit)
    //NumByteToRead:要读取的字节数(最大 65535)
    void W25QXX_Read (u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
    {
    u16 i;
    SPI_FLASH_CS=0; //使能器件
    SPI2_ReadWriteByte(W25X_ReadData); //发送读取命令
    SPI2_ReadWriteByte((u8)((ReadAddr)>>16)); //发送 24bit 地址
    SPI2_ReadWriteByte((u8)((ReadAddr)>>8));
    SPI2_ReadWriteByte((u8)ReadAddr);
    for(i=0;i<NumByteToRead;i++)
    {
    pBuffer[i]=SPI2_ReadWriteByte(0XFF); //循环读数
    }
    SPI_FLASH_CS=1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    由于 W25Q128 支持以任意地址开始读取数据,在发送 24 位地址之后,程序就可以开始循环读数据了,其地址会自动增加的,不过要注意,不能读的数据超过了 W25Q128 的地址范围哦!

    无检查写函数

    //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    //pBuffer:数据存储区
    //WriteAddr:开始写入的地址(24bit)
    //NumByteToWrite:要写入的字节数(最大65535)
    //CHECK OK
    void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    { 			 		 
    	u16 pageremain;	   
    	pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
    	if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节,这也是结束标识
    	while(1)
    	{	   
    		W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    		if(NumByteToWrite==pageremain)break;//写入结束了
    	 	else //NumByteToWrite>pageremain
    		{
    			pBuffer+=pageremain;
    			WriteAddr+=pageremain;	
    
    			NumByteToWrite-=pageremain;			  //减去已经写入了的字节数
    			if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    			else pageremain=NumByteToWrite; 	  //不够256个字节了
    		}
    		//按照页剩余写一次,然后256个字节的写,然后写最后一页多出来的。
    	};	    
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    NoCheck是说可以跨扇区的写
    下方表示写了一个扇区
    在这里插入图片描述

    在这里插入图片描述

    W25QXX_Write函数

    在这里插入图片描述

    作用与 W25QXX_Flash_Read 的作用类似,不过是用来写数据到 W25Q128 里面的,其代码如下:

    u8 W25QXX_BUFFER[4096];
    void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    {
    	u32 secpos;
    	u16 secoff;
    	u16 secremain;
    	u16 i;
    	u8 * W25QXX_BUF;
    	W25QXX_BUF=W25QXX_BUFFER;
    	secpos=WriteAddr/4096;//扇区地址,每个扇区是4096,所以除以4096得到的整数就是扇区的地址标号
    	secoff=WriteAddr%4096;//在扇区内的偏移
    	secremain=4096-secoff;//扇区剩余空间大小
    	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    	if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于 4096 个字节
    	while(1)
    	{
    		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    		//secpos*4096是该扇区的起始地址
    		for(i=0;i<secremain;i++)//校验数据
    		{
    			if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除,偏移地址内有数据
    //擦除后的默认值是0xFFF
    		}
    		if(i<secremain)//需要擦除
    		{
    			W25QXX_Erase_Sector(secpos); //擦除这个扇区
    			for(i=0;i<secremain;i++) //复制
    			{
    				W25QXX_BUF[i+secoff]=pBuffer[i];
    			}
    			W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区
    		}
    		else 
    			W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);
    		//如果扇区剩余空间足够,直接写入扇区剩余区间.
    		//是否需要写入下一个扇区
    		if(NumByteToWrite==secremain)break;//写入结束了
    		else//写入未结束
    		{
    			secpos++;//扇区地址增 1
    			secoff=0;//偏移位置为 0
    			pBuffer+=secremain; //指针偏移
    			WriteAddr+=secremain; //写地址偏移
    			NumByteToWrite-=secremain; //字节数递减
    			if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
    			else secremain=NumByteToWrite; //下一个扇区可以写完了
    		}
    	};
    }
    //跟无检查页写入的逻辑一致。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 该函数可以在 W25Q128 的任意地址开始写入任意长度(必须不超过 W25Q128 的容量)的数据。
    • 先获得首地址(WriteAddr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是否要擦除,如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。
    • 擦除的最小单位是扇区,也就是4K。所以在擦除之前我们先将这个扇区的数据读取出来,保存在缓存区。在缓存中将对应的地址更新之后,一次性将数据写到对应的sector之中。
    • 当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。

    W25N01G

    介绍

    在这里插入图片描述

    引脚及支持

    NAND FLASH W25N01GVZEIG支持Standard/DUAL/QUAD SPI
    在这里插入图片描述

    在这里插入图片描述
    区别就是在于注释的内容:

    • IO0和IO2被用于标准和双SPI命令。
    • IO0-IO3被用于四元SPI指令(四元代表从四个IO口获取数据)
    • 写保护和保持函数只有在标准模式和双SPI模式才可用。

    引脚分配的说明:

    /CS

    (Clip Select) :片选线
    在这里插入图片描述当CS被拉低,装置将会被选择,能量消耗将会增加到激活水平,命令可以被写或者数据可以被读。上电后,CS必须从高到低的转变,在一个命令被传来之前。CS输入必须在上下电的时候跟随VCC供能水平。如果需要,一个上拉电阻可以帮助完成这个

    DO

    (Serial Data Output(Data Input Output 1)) :串行数据输出线

    /WP

    (Write Protect Input (Data Input Output 2)) :写数据保护线
    在这里插入图片描述写保护引脚可以被用来保护状态寄存器。和状态寄存器的BP位、SRP位结合使用。一部分内存如256K到一整块的内存可以被硬件保护。WP-E位控制WP引脚的功能。
    在这里插入图片描述
    当WP-E为0时,装置软件保护,只有SR1被保护。WP引脚变作一个为四元SPI操作的数据引脚。
    当WP-E为1时,装置硬件保护,WP变成一个激活的低输入引脚,作为整个装置的写保护。如果WP接地,装置变成一个只读引脚。

    4、GND :地线
    5、DI(Serial Data Input(Data Input Output 0)) :串行数据输入线
    6、CLK(Serial Clock) :时钟线

    /Hold

    (Hold Input (Data Input Output 3)) :数据输入保持线
    在这里插入图片描述
    在标准和双SPI操作器件,hold引脚被选择时允许装置暂停。当hold被拉低,CS同时也拉低,DO引脚将会高阻态同时DI和CLK引脚上的信号将会被忽略。当hold是高,装置恢复。hold函数可以在多个装置被共享相同的SPI信号时使用。hold引脚低时激活。
    8、VCC :电源输入

    SPI指令

    标准SPI命令

    在这里插入图片描述
    W25N01可以通过SPI兼容总线访问,由clk,cs,DI和DO组成。标准SPI命令使用:

    • 使用DI输入引脚将指令、地址或数据串行地写入CLK上升沿上的设备。
    • DO输出引脚用于从CLK下降沿上的设备读取数据或状态。
    • 模式0和模式3的主要区别在于CLK的电平,当SPI总线主设备处于待机且数据没有被传输时,对于模式0,CLK信号通常在/CS的下降和上升边缘处是低电平状态的;对于模式3,CLK信号通常在/CS的下降和上升边缘处是高电平状态的。

    双SPI


    当使用3B命令和BBH命令时候,w25n01支持双spi操作。这些命令允许数据可以从装置在两到三个时间周期内传递。双SPI命令对于快速下载到RAM上等非常理想。

    四元SPI命令

    在这里插入图片描述
    当使用命令如6BH、EBH、32H等命令时,w25n01支持四元SPI操作。这些命令允许数据从装置以4-6个周期传输。四元读命令。。。

    写保护

    在这里插入图片描述
    需要注意的是:片选引脚必须跟随VCC供电水平,从上电直到最小电压的VCC供电水平和Tvsl延时时间到达。并且该引脚必须跟随VCC供电水平在下电时阻止指令序列颠倒。如果需要上拉电阻可以帮助完成这玩意。
    在这里插入图片描述
    上电之后装置自动处于写失能状态,WEL位0。一个写使能命令必须在一个页程序、扇区擦除、块擦除命令执行被接收之前发出。在执行完一个程序、擦朱或者写命令之后,WEL位会被自动清除到0。
    在这里插入图片描述
    软件控制写保护是容易的,通过使用写状态寄存器命令并且设置状态寄存器的SRP0、SRP1和块波阿虎TB、BP3-0位。这样可以设置一个部分或者完全的内存序列被配置为只读。与WP引脚结合,在软件控制下改变状态寄存器就可以使能或者失能。看写保护寄存器了解更多信息。
    在这里插入图片描述
    写访问状态寄存器由非只读的状态寄存器保护位SRP0,SRP1、写使能命令、和当WP-E为1的时候的WP引脚控制。

    地址

    在这里插入图片描述W25N01G的寻址方式是页地址+列地址(Page Add + Column Add)。
    在这里插入图片描述

    • 列地址大小为2112( 2 11 + 2 6 2^{11}+2^6 211+26)Byte,由2048Byte的数据缓冲区和64Byte的备用区域(Spare Area)组成。(没有完全用完地址)
    • 页地址大小1024 x 64 x 2KB,即该flash共有1024个块(Block),每个块包含64页(Page),每页大小为2KB。(对应flash的大小,1024x64x2/1024=128M)
    • 由于NAND Flash在设定上存在坏块,所以就有Spare Area,它被用于标记坏块(bad block)和保存对MainArray中main区的ECC码(Error Checking and Correcting,纠错码)。

    PA与PC

    在这里插入图片描述

    • PA是页地址请求16位,PA[15:6]是一个128K的块的地址(共有1024个)。PA[0-5]是一个2K的页的地址(共有64个)。

    最后一个扇区

    1024*(4096)*N每个扇区大小4K,但实际可用的只有2K。
    1024代表快,N代表第几个页。

    OTP

    OTP锁定的值。
    One Time Program (OTP) write protection

    寄存器1

    在这里插入图片描述

    块保护

    S7-S2为块保护位,主要提供写保护控制和状态。块保护位上电后的默认值是1,以保护整个flash。如果配置寄存器(SR-2)中的SR1-L位被设置为1,那么其默认值将是OTP锁定的值。

    清除块保护指令*

    Protection Register / Status Register-1 (Volatile Writable, OTP lockable) 此寄存器上电后的默认值是 0b01111100,也就是说 Flash 处于写保护状态。我们需要用 Write Status Register (1Fh/01h) 这条指令清除写保护。 否则擦写指令不起作用。
    {

    }

    WP-E

    当WP-E = 0(默认值)时,设备处于软件保护模式,当WP-E = 1时,设备处于硬件保护模式。
    在这里插入图片描述
    写保护位用来使能硬件保护。当该位1时,WP引脚被拉低,堵塞任何写、执行和擦除命令。装置变成只读的。四元SPI操作也不能在WP-E为1的时候操作。

    寄存器2

    状态寄存器2可以改变BUF和ECC-E的状态。

    寄存器3

    在这里插入图片描述
    地址:0xC0即可。

    BUSY

    擦写、执行程序忙。

    P-FAIL

    程序执行失败

    E-FAIL

    擦写失败

    ECC位

    累计的ECC状态
    在这里插入图片描述
    ECC函数用在NAND闪存中纠正在读操作器件有限内存的错误。
    ECC状态位应该在完成读操作后检查一下证明数据的完整性。
    ECC状态位不关心0.
    这些位将会在重新上电或者执行复位命令后清零。
    在这里插入图片描述
    解读上图。ECC1和0都是1的情况只在BUF=0的时候应用。
    0 0 整个数据输出成功,没有任何ECC纠正
    0 1 整个数据输出成功,有1-4位的ECC纠正
    1 0 仅一个单页里不能被ECC修复,整个数据输出包含超过4位的错误。
    1 1 在多个页中整个数据输出包含超过4位错误。在连续读模式,额外的命令只能提供最后失败的PA地址,用户不能获得其它失败页的PA地址。数据不适合使用。

    装置ID

    在这里插入图片描述

    指令解读

    写状态寄存器

    在这里插入图片描述
    写状态寄存器指令让我们可以写状态寄存器。可以写的状态位包括…,所有其它位是只读的,不受该命令影响。

    在这里插入图片描述
    未来写状态寄存器位,命令通过拉低CS进入。发送命令1F,呆着寄存器地址,然后写状态寄存器即可。
    在这里插入图片描述
    参照7.1.3的描述,上拉之后,默认BP,TB,ECC-E位都是1,其它位是0.
    在这里插入图片描述
    写状态寄存器命令。
    impedance/阻抗/

    注意内容

    上拉

    1、SPI 的信号线最好加 1k 上拉电阻, 实测发现 W25N01GV 这颗芯片的驱动能力比较弱。在调试的时候这可能发生误导,因为 BUSY = 0 时表明命令执行完毕,实际上是读回来的虚假低电平。(待验证)。

    BUF

    3、要注意两个型号的区别
    W25N01GVxxIG, 默认 BUF = 1, 处于 Buffer Read Mode。
    W25N01GVxxIT, 默认 BUF = 0,处于 Continous Read Mode。
    两种模式可以用过命令切换。

    写数据

    4、写入数据分两步:1. 先把数据传入芯片内部的 2048 字节 RAM 缓冲区。比如通过 Quad Program Data Load (32h) 指令装载数据。 2 执行 Program Execute (10h)。如果只执行第一步,不执行第二步,数据还可以从缓冲区读出来,但是掉电后数据就没有了。

    读数据

    5.读数据也分为两步,第一步先将指定页数据读取到缓冲区(0x13H ),第二步将数据从缓冲区读出来(0x03H)。

    写使能

    6、在执行 Block Erase,Program Execute等命令前要先执行Write Enable(06h)指令,否则指令不起作用。

    极性

    7、 非常重要的一点是:SPI的 频率1M合适 ,SPI的的极性一定要为高。也就是空闲时要为高电平。(吃亏了,血泪教训)

  • 相关阅读:
    【C++】:继承
    Docker (二): Docker安装及配置镜像加速
    Gstreamer应用开发实战指南(五)
    .NET Core剪裁器Zack.DotNetTrimmer升级瘦身引擎,并支持剪裁计划的录制和回放
    AWS KMS加密和解密
    【数据结构与算法】时间复杂度和空间复杂度
    Java性能优化的过程方法与求职面经总结
    OData WebAPI实践-OData与EDM
    Linux系统调优详解(九)——sar查看系统整体运行状态
    【计算机网络】——应用层
  • 原文地址:https://blog.csdn.net/qq_45578181/article/details/126533683