tag 标签: w25q64

相关博文
  • 热度 24
    2016-6-23 20:35
    3298 次阅读|
    0 个评论
    百度文库 http://wenku.baidu.com/view/7bfd82fd5901020206409c1b
  • 热度 18
    2013-3-16 21:26
    3890 次阅读|
    3 个评论
    第二十八章 SPI 实验 本章我们将向大家介绍STM32的SPI功能。在本章中,我们将使用STM32自带的SPI来实现对外部FLASH(W25Q64)的读写,并将结果显示在TFTLCD模块上。本章分为如下几个部分: 28.1 SPI 简介 28.2 硬件设计 28.3 软件设计 28.4 下载验证 28.1 SPI 简介 SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32也有SPI接口。 SPI接口一般使用4条线通信: MISO 主设备数据输入,从设备数据输出。 MOSI 主设备数据输出,从设备数据输入。 SCLK时钟信号,由主设备产生。 CS从设备片选信号,由主设备控制。 SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。 SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。 不同时钟相位下的总线数据传输时序如图28.1.1所示:   图28.1.1 不同时钟相位下的总线传输时序(CPHA=0/1) STM32的SPI功能很强大,SPI时钟最多可以到18Mhz,支持DMA,可以配置为SPI协议或者I2S协议(仅大容量型号支持,战舰STM32开发板是支持的)。 本章,我们将使用STM32的SPI来读取外部SPI FLASH芯片(W25Q64),实现类似上节的功能。这里对SPI我们只简单介绍一下SPI的使用,STM32的SPI详细介绍请参考《STM32参考手册》第457页,23节。然后我们再介绍下SPI FLASH芯片。 这节,我们使用STM32的SPI2的主模式,下面就来看看SPI2部分的设置步骤吧,STM32的主模式配置步骤如下: 1 )配置相关引脚的复用功能,使能SPI2 时钟。 我们要用SPI2,第一步就要使能SPI2的时钟,SPI2的时钟通过APB1ENR的第14位来设置。其次要设置SPI2的相关引脚为复用输出,这样才会连接到SPI2上否则这些IO口还是默认的状态,也就是标准输入输出口。这里我们使用的是PB13、14、15这3个(SCK.、MISO、MOSI,CS使用软件管理方式),所以设置这三个为复用IO。 2 )设置SPI2 工作模式。 这一步全部是通过SPI2_CR1来设置,我们设置SPI2为主机模式,设置数据格式为8位,然后通过CPOL和CPHA位来设置SCK时钟极性及采样方式。并设置SPI2的时钟频率(最大18Mhz),以及数据的格式(MSB在前还是LSB在前)。 3 )使能SPI2 。 这一步通过SPI2_CR1的bit6来设置,以启动SPI2,在启动之后,我们就可以开始SPI通讯了。 SPI2的使用就介绍到这里,接下来介绍一下W25Q64。W25Q64是华邦公司推出的大容量SPI FLASH产品,W25Q64的容量为64Mb,该系列还有W25Q80/16/32等。ALIENTEK所选择的W25Q64容量为64Mb,也就是8M字节。 W25Q64将8M的容量分为128个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q64的最少擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q64开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。 W25Q64的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q64支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q64的介绍,请参考W25Q64的DATASHEET。 28.2 硬件设计 本章实验功能简介:开机的时候先检测W25Q64是否存在,然后在主循环里面检测两个按键,其中1个按键(WK_UP)用来执行写入W25Q64的操作,另外一个按键(KEY1)用来执行读出操作,在TFTLCD模块上显示相关信息。同时用DS0提示程序正在运行。 所要用到的硬件资源如下: 1)  指示灯DS0 2)  WK_UP和KEY1按键 3) TFTLCD模块 4) SPI 5) W25Q64 这里只介绍W25Q64与STM32的连接,板上的W25Q64是直接连在STM32的SPI2上的,连接关系如图28.2.1所示:   图28.2.1 STM32与W25Q64连接电路图 28.3 软件设计 打开上一章的工程,首先在HARDWARE文件夹下新建一个FLASH的文件夹和SPI的文件夹。然后新建一个flash.c和flash.h的文件保存在FLASH文件夹下,新建spi.c和spi.h的文件,保存在SPI文件夹下,并将这两个文件夹加入头文件包含路径。 打开spi.c文件,输入如下代码: #include "spi.h" //SPI口初始化 //这里针是对SPI2的初始化 void SPI2_Init(void) {            RCC-APB2ENR|=13;           //PORTB时钟使能                RCC-APB1ENR|=114;        //SPI2时钟使能        //这里只针对SPI口初始化        GPIOB-CRH=0X000FFFFF;        GPIOB-CRH|=0XBBB00000;     //PB13/14/15复用                    GPIOB-ODR|=0X713;         //PB13/14/15上拉        SPI2-CR1|=010;                    //全双工模式        SPI2-CR1|=19;                     //软件nss管理        SPI2-CR1|=18;           SPI2-CR1|=12;                     //SPI主机        SPI2-CR1|=011;                    //8bit数据格式             SPI2-CR1|=11;                     //空闲模式下SCK为1 CPOL=1        SPI2-CR1|=10;                     //数据采样从第二个时间边沿开始,CPHA=1         //对SPI2属于APB1的外设.时钟频率最大为36M.        SPI2-CR1|=33;                     //Fsck=Fpclk1/256        SPI2-CR1|=07;                     //MSBfirst          SPI2-CR1|=16;                     //SPI设备使能        SPI2_ReadWriteByte(0xff);//启动传输        }   //SPI2速度设置函数 //SpeedSet:0~7 //SPI速度=fAPB1/2^(SpeedSet+1) //APB1时钟一般为36Mhz void SPI2_SetSpeed(u8 SpeedSet) {        SpeedSet=0X07;                //限制范围        SPI2-CR1=0XFFC7;        SPI2-CR1|=SpeedSet3;   //设置SPI2速度        SPI2-CR1|=16;              //SPI设备使能        } //SPI2 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 u8 SPI2_ReadWriteByte(u8 TxData) {                   u16 retry=0;                               while((SPI2-SR11)==0)             //等待发送区空            {               retry++;               if(retry=0XFFFE)return 0;        //超时退出        }                            SPI2-DR=TxData;                         //发送一个byte        retry=0;        while((SPI2-SR10)==0)            //等待接收完一个byte         {               retry++;               if(retry=0XFFFE)return 0;  //超时退出        }                                                          return SPI2-DR;                       //返回收到的数据                           } 此部分代码主要初始化SPI,这里我们选择的是SPI2,所以在SPI2_Init函数里面,其相关的操作都是针对SPI2的,其初始化步骤和我们上面介绍的一样。在初始化之后,我们就可以开始使用SPI2了,在SPI2_Init函数里面,把SPI2的频率设置成了最低(36Mhz,256分频)。在外部函数里面,我们通过SPI2_SetSpeed来设置SPI2的速度,而我们的数据发送和接收则是通过SPI2_ReadWriteByte函数来实现的。 保存spi.c,并把该文件加入HARDWARE组下面,然后我们打开spi.h在里面输入如下代码: #ifndef __SPI_H #define __SPI_H #include "sys.h" // SPI总线速度设置 #define SPI_SPEED_2              0 #define SPI_SPEED_4              1 #define SPI_SPEED_8              2 #define SPI_SPEED_16             3 #define SPI_SPEED_32              4 #define SPI_SPEED_64              5 #define SPI_SPEED_128            6 #define SPI_SPEED_256            7 void SPI2_Init(void);                       //初始化SPI2口 void SPI2_SetSpeed(u8 SpeedSet);     //设置SPI2速度   u8 SPI2_ReadWriteByte(u8 TxData);   //SPI2总线读写一个字节 #endif 此部分代码我们就不多介绍了,保存spi.h,然后我们打开flash.c,在里面编写与W25Q64操作相关的代码,由于篇幅所限,详细代码,这里就不贴出了。我们仅介绍几个重要的函数,首先是SPI_Flash_Read函数,该函数用于从W25Q64的指定地址读出指定长度的数据。其代码如下:            //读取SPI FLASH  //在指定地址开始读取指定长度的数据 //pBuffer:数据存储区 //ReadAddr:开始读取的地址(24bit) //NumByteToRead:要读取的字节数(最大65535) void SPI_Flash_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        {         pBuffer =SPI2_ReadWriteByte(0XFF);   //循环读数     }        SPI_FLASH_CS=1;                                            } 由于W25Q64支持以任意地址(但是不能超过W25Q64的地址范围)开始读取数据,所以,这个代码相对来说就比较简单了,在发送24位地址之后,程序就可以开始循环读数据了,其地址会自动增加的,不过要注意,不能读的数据超过了W25Q64的地址范围哦!否则读出来的数据,就不是你想要的数据了。 有读的函数,当然就有写的函数了,接下来,我们介绍SPI_Flash_Write这个函数,该函数的作用与SPI_Flash_Read的作用类似,不过是用来写数据到W25Q64里面的,其代码如下: //写SPI FLASH  //在指定地址开始写入指定长度的数据 //该函数带擦除操作! //pBuffer:数据存储区 //WriteAddr:开始写入的地址(24bit)                                       //NumByteToWrite:要写入的字节数(最大65535)   u8 SPI_FLASH_BUFFER ;        void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   {        u32 secpos;        u16 secoff;        u16 secremain;                u16 i;           u8 * SPI_FLASH_BUF;              SPI_FLASH_BUF=SPI_FLASH_BUFFER;            secpos=WriteAddr/4096;//扇区地址 0~511 for w25x16        secoff=WriteAddr%4096;//在扇区内的偏移        secremain=4096-secoff;//扇区剩余空间大小         //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用       if(NumByteToWrite=secremain)secremain=NumByteToWrite;//不大于4096个字节        while(1)        {                   SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容               for(i=0;i               {                      if(SPI_FLASH_BUF !=0XFF)break;//需要擦除                         }               if(i               {                      SPI_Flash_Erase_Sector(secpos);//擦除这个扇区                      for(i=0;i                      {                             SPI_FLASH_BUF =pBuffer ;                            }                      SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096); //写入整个扇区               }else SPI_Flash_Write_NoCheck(pBuffer,WriteAddr,secremain); //写已经擦除了的,直接写入扇区剩余区间.                 if(NumByteToWrite==secremain)break;//写入结束了               else//写入未结束               {                      secpos++;//扇区地址增1                      secoff=0;//偏移位置为0                    pBuffer+=secremain;  //指针偏移                      WriteAddr+=secremain;//写地址偏移                         NumByteToWrite-=secremain;                           //字节数递减                      if(NumByteToWrite4096)secremain=4096;        //下一个扇区还是写不完                      else secremain=NumByteToWrite;                      //下一个扇区可以写完了               }            };    } 该函数可以在W25Q64的任意地址开始写入任意长度(必须不超过W25Q64的容量)的数据。我们这里简单介绍一下思路:先获得首地址(WriteAddr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是否要擦除,如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。 其他的代码就比较简单了,我们这里不介绍了。保存falsh.c,然后加入到HARDWARE组下面,再打开flahs.h,在该文件里面输入如下代码: #ifndef __FLASH_H #define __FLASH_H                       #include "sys.h" ......//省略部分代码 #endif 这里面就定义了一些与W25Q64操作相关的命令(部分省略了),这些命令在W25Q64的数据手册上都有详细的介绍,感兴趣的读者可以参考该数据手册,其他的就没啥好说的了。保存此部分代码。最后,我们在test.c里面,修改main函数如下: 由于编辑器的问题,下面的内容省略,请大家下载附件阅读。 ;i++)       ) ;i++) ;i++)
相关资源
  • 所需E币: 1
    时间: 2023-7-29 17:10
    大小: 922.12KB
    上传者: Argent
    W25Q64
  • 所需E币: 0
    时间: 2021-9-27 16:52
    大小: 912.91KB
    上传者: Argent
    电子产品日新月异,不管是硬件工程师还是软件工程师,基本的模电、数电、微机原理、信号处理等知识是必备的条件,从二极管到三极管,从单片机到多核MCU,3G网络到5G产品的普及,不管电子产品的集成度怎么高,其产品还是少不了电阻电容电感,每个元器件在电路中必然有其作用,有兴趣了解的网友,下载学习学习吧。