版权声明:本文为博主原创文章,转载请附上原文出处链接。

文章目录

  • 前言
  • 一、硬件设计
    • 1.FRAM铁电存储器介绍
    • 2.W25Q128JV存储芯片介绍
      • 2.1.芯片引脚定义
      • 2.2.芯片介绍及使用注意事项
  • 二、软件设计
    • 1.外接FLASH存储器读写单字节实验(模拟SPI)
      • 1.1.工程需要用到的c文件
      • 1.2.头文件引用和路径设置
      • 1.3.编写代码
      • 1.4.硬件连接
    • 2.外接FLASH存储器读写多字节实验(模拟SPI)
      • 2.1.工程需要用到的c文件
      • 2.2.编写代码
      • 2.3.硬件连接
    • 3.外接FLASH存储器读写单字节实验(硬件SPI)
      • 3.1.工程需要用到的c文件
      • 3.2.头文件引用和路径设置
      • 3.3.编写代码
      • 3.4.硬件连接
    • 4.外接FLASH存储器读写多字节实验(硬件SPI)
      • 4.1.工程需要用到的c文件
      • 4.2.编写代码
      • 4.3.硬件连接
  • 总结

前言

今天介绍下STC8A8K64S4A12系列单片机外部FLASH存储器W25Q128引脚定义和硬件电路设计,掌握通过STC8A8K64S4A12系列单片机SPI外设对W25Q128的程序设计。


一、硬件设计

1.FRAM铁电存储器介绍

FLASH存储器属内存器件的一种,是一种非易失性( Non-Volatile )内存。该存储器结合了ROM和RAM的长处,不仅具备电可擦除可编程(EEPROM)的性能,还不会断电丢失数据,同时可以快速读取数据,所以现如今大多数单片机片内都集成了这种存储器。

接下来,我们主要介绍的是外部的FLASH存储器芯片,外部的FLASH存储器芯片种类和生产厂家都比较多。其中WINBOND(华邦)公司生产的W25Q系列存储器应用很广泛,是我们介绍的重点。W25Q系列存储器相比于普通的串行FLASH器件提供了更好的灵活性和性能,该系列存储器可在2.7V到3.6V的供电电压下工作(注意芯片后缀JV),工作时电流消耗最低4mA,在睡眠模式下消耗仅1uA。另外,所有的W25Q系列存储器芯片都提供节省空间的封装。

WINBOND(华邦)公司生产的W25Q系列存储器支持标准SPI接口,操作寄存器指令兼容,但不同存储空间的芯片有不同的ID,具体如下表。

表1:W25Q系列存储器介绍

序号 芯片型号 存储空间 供电电压 芯片ID 工作温度 备注
1 W25Q16JV 16M bit 2.7V~3.6V 0xEF14 -40℃~85℃
2 W25Q32JV 32M bit 2.7V~3.6V 0xEF15 -40℃~85℃
3 W25Q64JV 64M bit 2.7V~3.6V 0xEF16 -40℃~85℃
4 W25Q128JV 128M bit 2.7V~3.6V 0xEF17 -40℃~85℃
5 W25Q256JV 256M bit 2.7V~3.6V 0xEF18 -40℃~85℃
6 W25Q512JV 512M bit 2.7V~3.6V 0xEF19 -40℃~85℃

☆注:芯片ID中的高8位0xEF代表的Winbond Serial Flash系列。厂家还有1G bit和2G bit的存储器芯片W25Q01JV和W25Q02JV,有需要更大存储空间可以考虑。

2.W25Q128JV存储芯片介绍

2.1.芯片引脚定义

W25Q128JV支持标准的SPI接口,双线/四线IO模式SPI:串行时钟,数据选择,串行数据IO0(DI),IO1(DO),IO2(/WP),IO3(/HOLD)。

SPI时钟频率最高可达133MHz,因此在双IO SPI模式下等效于266MHz,在四IO SPI/QPI模式下等效于532MHz。这些传输速率比标准的异步8位或者16位并行FLASH存储器表现得更好。

图1:W25Q128JV芯片引脚

STC8A8K64S4A12开发板上设计了可供用户焊接使用的W25Q系列存储器芯片的接口,其中原理图部分及硬件实物部分如下。

图2:开发板外扩FLASH模块

2.2.芯片介绍及使用注意事项

W25Q128JV存储芯片是由65536可编程的页组成的,每页有256个字节。一次最多可以写256个字节。可以一次擦除16页(4K字节)、128页(32K字节)、256页(64K字节)或者一整片。W25Q128JV有4096个可擦除的扇区,256个可擦除的块。4K字节的扇区对于数据和参数存储有更高的灵活性。

■ W25Q128JV内部框图

图3:W25Q128JV芯片内部框图

☆注:W25Q128JV存储器实际的存储空间会略大于128M bit的,因为比如4096字节都按照4K来计算的。其他W25Q系列存储器的页、扇区、块计算都和上面W25Q128JV内部框图一样,不一样的是不同的存储器块数不一样。比如,W25Q64JV存储器只有128个块,也就是8M字节存储空间(即64M bit)。

■ 使用注意事项

  1. 由于FLASH的物理特性,决定了FLASH每一位的操作只能从1变为0(写操作)。
  2. 大多数FLASH芯片或单片机内未使用FLASH存储空间每一位出厂默认都是1。
  3. 对FLASH写操作之前必须将待操作FLASH空间数据都置为1。
  4. 对FLASH的擦除操作即是把待操作的空间的每一位都置为1。
  5. 所以FLASH写操作前需有擦除操作。
  6. W25Q128JV存储芯片的擦除比较灵活,可以按扇区、块甚至是整片擦除。(擦除是需要时间的,比如整片擦除约用时几十秒)

☆注:有些用户说我没有擦除直接写的,第一次没有问题,之后怎么出错了?想一想什么原因。(往往第一次是出厂的默认的每个空间位都是1,所以写操作没有问题)。

二、软件设计

关于STC8A8K64S4A12系列SPI外设原理及相关寄存器的介绍请参考外接FRAM存储器实验部分,对外接FLASH存储器的SPI配置是完全一样的。

1.外接FLASH存储器读写单字节实验(模拟SPI)

1.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表2:实验需要用到的c文件

序号 文件名 后缀 功能描述
1 uart .c 包含与用户uart有关的用户自定义函数。
2 W25q128 .c SPI通信及操作FLASH有关的用户自定义函数。
3 delay .c 包含用户自定义延时函数。

1.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h"
#include "uart.h"
#include " w25q128.h"

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表3:头文件包含路径

序号 路径 描述
1 …\ Source uart.h、w25q128.h和delay.h头文件在该路径,所以要包含。
2 …\Use STC8.h头文件在该路径,所以要包含。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

图4:添加头文件包含路径

1.3.编写代码

首先,在w25q128.c文件中编写模拟SPI方式的读写字节函数,这里的模拟SPI读字节函数与写字节函数是分开的,代码如下:

程序清单:模拟SPI写字节函数

/***************************************************************************** * 描  述 : 模拟SPI写入一个字节 * 入  参 : uint8 date * 返回值 : 无 ****************************************************************************/
void SPI_WriteByte(uint8 date)
{  uint8 temp,i;  temp = date;    for (i = 0; i < 8; i++)   {  SPI_SCK_0;  delay_us(10);  if((temp&0x80)==0x80)  { SPI_MOSI_1; }  else   { SPI_MOSI_0; }  SPI_SCK_1 ;  delay_us(10);  temp <<= 1;  }  SPI_MOSI_0;
}

程序清单:模拟SPI读字节函数

/*********************************************************************** * 描  述 : 模拟SPI读取一个字节 * 入  参 : 无 * 返回值 : 读取uint8数据 ***********************************************************************/
uint8 SPI_ReadByte(void)
{  uint8 temp=0;  uint8 i;  for(i = 0; i < 8; i++)  {  temp <<= 1;  SPI_SCK_0 ;          delay_us(10);  if(E_SPI_MISO)   {temp++; }  SPI_SCK_1 ;  delay_us(10);  }  return(temp);
}

然后,编写对FLASH存储器的基本操作函数,如下表所示。

表4:FLASH相关用户函数汇集

关于每个操作外部FLASH相关用户函数,下面给出详细代码。

程序清单:FLASH芯片写使能函数

/*************************************************************************** * 描  述 : 写禁止(将WEL清0) * 入  参 : 无 * 返回值 : 无 *************************************************************************/
void WriteDisable (void)
{  SPI_CS_0;  SPI_WriteByte(W25X_WriteDisable);    SPI_CS_1;
}

程序清单:读FLASH芯片ID函数

/************************************************************************* * 描  述 : 读取存储器芯片ID * 入  参 : 无 * 返回值 : uint16 ID
备注:W25Q16的ID:0XEF14  W25Q32的ID:0XEF15  W25Q64的ID:0XEF16  W25Q128的ID:0XEF17
**************************************************************************/
uint16 W25Q_ReadID(void)
{  uint16 Temp = 0;  uint8 Temp1 = 0;  uint8   Temp2 = 0;  SPI_CS_0;                     SPI_WriteByte(0x90);        //发送读取ID命令        SPI_WriteByte(0x00);          SPI_WriteByte(0x00);          SPI_WriteByte(0x00);                     Temp1|=SPI_ReadByte();    Temp2|=SPI_ReadByte();    Temp = Temp1*256+Temp2;  SPI_CS_1;                 return Temp;
}

程序清单:读FLASH状态寄存器函数

/************************************************************************ * 描  述 : 读取存储器芯片的状态 * 入  参 : 无 * 返回值 : uint8 状态寄存器数据字节
备注:芯片内部状态寄存器第0位=0表示空闲,0位=1表示忙
**************************************************************************/
uint8 W25Q_ReadStatus(void)
{  uint8 status=0;  SPI_CS_0;  SPI_WriteByte(W25X_ReadStatus);   // 0x05读取状态的命令字  status=SPI_ReadByte();            // 读取状态字节  SPI_CS_1;                         return status;
}

程序清单:写FLASH状态寄存器函数

/*********************************************************************** * 描  述 : 写芯片的状态寄存器 * 入  参 : uint8 Status * 返回值 : 无
备注:只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
***********************************************************************/
void W25Q_WriteStatus(uint8 Status)
{  SPI_CS_0;  SPI_WriteByte(W25X_WriteStatus);  // 0x01读取状态的命令字  SPI_WriteByte(Status);            // 写入一个字节  SPI_CS_1;
}

程序清单:在一页内写入多字节数据函数

/************************************************************************ * 描  述 : 在一页(0~65535)内写入少于256个字节的数据(在指定地址开始写入最大256字节的数据) * 入  参 : pbuf:数据存储区  WriteAddr:开始写入的地址(24bit)  Len:要写入的字节数(最大256)  * 返回值 : 无
备注:Len:要写入的字节数,该数不应该超过该页的剩余字节数!!!
************************************************************************/
void W25Q_Write_Page(uint8* pbuf,uint32 WriteAddr,uint16 Len)
{  uint16 i;  while(W25Q_ReadStatus()&0x01);        //判断是否忙  WriteEnable();                          //写使能  SPI_CS_0;                               //使能器件     SPI_WriteByte(W25X_Writepage);          //发送写页命令  SPI_WriteByte((uint8)((WriteAddr)>>16));   //发送24bit地址     SPI_WriteByte((uint8)((WriteAddr)>>8));     SPI_WriteByte((uint8)WriteAddr);    for(i=0;i<Len;i++)                      //循环写数  {  SPI_WriteByte(*pbuf++);        }  SPI_CS_1;                               //取消片选  while(W25Q_ReadStatus()&0x01);        //等待写入结束
}

程序清单:向FLASH指定地址存入多字节数据函数

/*************************************************************************  * 描  述 : 在指定地址开始写入指定长度的数据 * 入  参 : pbuf:数据存储区  WriteAddr:开始写入的地址(24bit)  Len:要写入的字节数(最大65535) * 返回值 : 无
备注:必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
*************************************************************************/
void W25Q_Write_N(uint8 * pbuf,uint32 WriteAddr,uint16 Len)
{  uint16 PageLen;                  // 页内写入字节长度  PageLen=256-WriteAddr%256;       // 单页剩余的字节数 (单页剩余空间)  if(Len<=PageLen) PageLen=Len;    // 不大于256 个字节  while(1)  {  W25Q_Write_Page(pbuf,WriteAddr,PageLen);  if(PageLen==Len)break;         // 写入结束了  else  {  pbuf+=PageLen;  WriteAddr+=PageLen;  Len-=PageLen;              //  减去已经写入了的字节数  if(Len>256)PageLen=256;    // 一次可以写入256 个字节  else PageLen=Len;          // 不够256 个字节了  }  }
}

程序清单:从FLASH指定地址读取多字节数据函数

/************************************************************************  * 描  述 : 在指定地址开始写入指定长度的数据 * 入  参 : pbuf:数据存储区  WriteAddr:开始写入的地址(24bit)  Len:要写入的字节数(最大65535) * 返回值 : 无
备注:必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
**************************************************************************/
void W25Q_Write_N(uint8 * pbuf,uint32 WriteAddr,uint16 Len)
{  uint16 PageLen;                  // 页内写入字节长度  PageLen=256-WriteAddr%256;       // 单页剩余的字节数 (单页剩余空间)  if(Len<=PageLen) PageLen=Len;    // 不大于256 个字节  while(1)  {  W25Q_Write_Page(pbuf,WriteAddr,PageLen);  if(PageLen==Len)break;         // 写入结束了  else  {  pbuf+=PageLen;  WriteAddr+=PageLen;  Len-=PageLen;              //  减去已经写入了的字节数  if(Len>256)PageLen=256;    // 一次可以写入256 个字节  else PageLen=Len;          // 不够256 个字节了  }  }
}

程序清单:按扇区擦除数据函数

/********************************************************************* * 描  述 : 擦除一个扇区( 4K扇擦除) * 入  参 : uint32 Addr24  扇区地址 * 返回值 : 无
备注:擦除一个扇区的最少时间:150ms
*************************************************************************/
void W25Q_SectorErase(uint32 Addr24) //擦除资料图示的4KB空间
{  unsigned char Addr1;       // 最低地址字节  unsigned char Addr2;       // 中间地址字节  unsigned char Addr3;       // 最高地址字节    Addr1=Addr24;  Addr24=Addr24>>8;  Addr2=Addr24;  Addr24=Addr24>>8;  Addr3=Addr24;                      // 把地址拆开来    while(W25Q_ReadStatus()&0x01);     // 判断是否忙     WriteEnable();                     // 写允许  SPI_CS_0;  SPI_WriteByte(W25X_S_Erase);       // 整扇擦除命令  SPI_WriteByte(Addr3);  SPI_WriteByte(Addr2);  SPI_WriteByte(Addr1);  SPI_CS_1;  while(W25Q_ReadStatus()&0x01);   // 等待擦除完成
}

程序清单:按块擦除数据函数

/*********************************************************************** * 描  述 : 擦除一块擦除( 64K) * 入  参 : uint32 Addr24  扇区地址 * 返回值 : 无
*************************************************************************/
void W25Q_BlockErase(uint32 Addr24)  //擦除资料图示的64KB空间
{  uint8 Addr1;       // 最低地址字节  uint8 Addr2;       // 中间地址字节  uint8 Addr3;       // 最高地址字节    Addr1=Addr24;  Addr24=Addr24>>8;  Addr2=Addr24;  Addr24=Addr24>>8;  Addr3=Addr24;                      // 把地址拆开来    while(W25Q_ReadStatus()&0x01);     // 判断是否忙     WriteEnable();                     // 写允许  SPI_CS_0;  SPI_WriteByte(W25X_B_Erase);       // 整扇擦除命令  SPI_WriteByte(Addr3);  SPI_WriteByte(Addr2);  SPI_WriteByte(Addr1);  SPI_CS_1;  while(W25Q_ReadStatus()&0x01);   // 等待擦除完成
}

程序清单:擦除整片芯片函数

/************************************************************************* * 描  述 : 擦除整片芯片 * 入  参 : 无 * 返回值 : 无
备注:不同型号的芯片时间不一样
**************************************************************************/
void W25Q_ChipErase(void)
{  while(W25Q_ReadStatus()&0x01);     // 判断是否忙     WriteEnable();                     // 写允许  SPI_CS_0;  SPI_WriteByte(W25X_C_Erase);       // 整片擦除命令  SPI_CS_1;                          // 从CS=1时开始执行擦除  while(W25Q_ReadStatus()&0x01);     // 等待擦除完成
}

最后,在主函数中对串口1进行初始化,并通过串口1发送不同的命令实现对单片机片外FLASH的单字节读、写及擦除等操作。

代码清单:主函数

int main()
{  uint16  Temp;  uint8   Temp1,Temp2;  P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  SPI_CS_1;                                     //SPI使能引脚初始化  SPI_SCK_0;                                    //SPI时钟引脚初始化  Uart1_Init();                                 //串口1初始化    EA = 1;                                       //使能总中断  delay_ms(10);                                 //初始化后延时  Temp=W25Q_ReadID();                           //读取外扩存储器芯片ID  Temp1=(uint8)(Temp/256);                      //将读取的存储器芯片ID高字节存放到Temp1中  Temp2=(uint8)Temp;                            //将读取的存储器芯片ID低字节存放到Temp2中  while (1)  {  if(W_ID)                           //读存储器芯片ID模式  {  W_ID=0;                            //ID标志变量清零,发送一次  SendDataByUart1(Temp1);            //串口1发送芯片ID高字节  SendDataByUart1(Temp2);            //串口1发送芯片ID低字节  }  if(WriteFLAG)                     //写模式  {  WriteFLAG=0;                      //写标志变量清零,发送一次  W25Q_Write_N(scan,0x00000010,1);  //在0x00000010地址开始写入1字节scan中的数据  SendDataByUart1(0x33);            //串口1发送数据0x33  }  if(ReadFLAG)                      //读模式  {  ReadFLAG=0;                         //读标志变量清零,发送一次  W25Q_Read_N(buffer,0x00000010,1);   //读0x00000010地址开始的1字节数据到buffer中  SendStringByUart1_n(buffer,1);      //串口1发送buffer中存的数据  }    if(ClearFLAG)                     //扇区擦除模式  {  ClearFLAG=0;                        //清除标志变量清零,发送一次  W25Q_SectorErase(0x00000010);       //对0x00000010地址所在的扇区进行扇区擦除  SendDataByUart1(0x00);              //串口1发送数据0x00  }     }
}

1.4.硬件连接

图5:开发板连接图

2.外接FLASH存储器读写多字节实验(模拟SPI)

2.1.工程需要用到的c文件

本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-16-1:外接FLASH存储器读写单字节实验(模拟SPI)”部分。

2.2.编写代码

首先,在w25q128.c文件中编写模拟SPI方式的读写字节函数和对FLASH存储器的基本操作函数。请参考“实验2-16-1:外接FLASH存储器读写单字节实验(模拟SPI)”部分。

然后,在主函数中对串口1进行初始化,并通过串口1发送不同的命令实现对单片机片外FLASH的多字节读、写及擦除等操作。

代码清单:主函数

int main()
{  uint16  Temp;  uint8   Temp1,Temp2;  P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  SPI_CS_1;                                     //SPI使能引脚初始化  SPI_SCK_0;                                    //SPI时钟引脚初始化  Uart1_Init();                                 //串口1初始化    EA = 1;                                       //使能总中断  delay_ms(10);                                 //初始化后延时  Temp=W25Q_ReadID();                           //读取外扩存储器芯片ID  Temp1=(uint8)(Temp/256);                      //将读取的存储器芯片ID高字节存放到Temp1中  Temp2=(uint8)Temp;                            //将读取的存储器芯片ID低字节存放到Temp2中  while (1)  {  if(W_ID)                           //读存储器芯片ID模式  {  W_ID=0;                            //ID标志变量清零,发送一次  SendDataByUart1(Temp1);            //串口1发送芯片ID高字节  SendDataByUart1(Temp2);            //串口1发送芯片ID低字节  }  if(WriteFLAG)                     //写模式  {  WriteFLAG=0;                      //写标志变量清零,发送一次  W25Q_SectorErase(0x00000000);       //擦除一个扇区(0x00000000在的扇区)  W25Q_Write_N(scan,0x00000000,10); //向FLASH地址0x00000000中写入scan数组中10个字节数据  SendDataByUart1(0x33);            //串口1发送数据0x33表示写操作完成  }  if(ReadFLAG)                      //读模式  {  ReadFLAG=0;                         //读标志变量清零,发送一次  W25Q_Read_N(buffer,0x00000000,10);      //从FLASH地址0x00000000开始读取10字节数据并存入到buffer数组中  SendStringByUart1_n(buffer,10);      //串口1发送数组buffer中的值(即读取的多字节数据)  }    if(ClearFLAG)                     //扇区擦除模式  {  ClearFLAG=0;                        //清除标志变量清零,发送一次  W25Q_SectorErase(0x00000000);       //擦除一个扇区(0x00000000在的扇区)  SendDataByUart1(0x00);              //串口1发送数据0x00表示擦除完成  }     }
}

2.3.硬件连接

图6:开发板连接图

3.外接FLASH存储器读写单字节实验(硬件SPI)

3.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表5:实验需要用到的c文件

序号 文件名 后缀 功能描述
1 uart .c 包含与用户uart有关的用户自定义函数。
2 w25q128 .c SPI通信及操作FLASH有关的用户自定义函数。
3 delay .c 包含用户自定义延时函数。

3.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h"
#include "uart.h"
#include " w25q128.h"

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表6:头文件包含路径

序号 路径 描述
1 …\ Source uart.h、w25q128.h和delay.h头文件在该路径,所以要包含。
2 …\Use STC8.h头文件在该路径,所以要包含。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

图7:添加头文件包含路径

3.3.编写代码

首先,在w25q128.c文件中对硬件SPI进行初始化,并编写硬件SPI的读写字节函数,代码如下:

程序清单:硬件SPI初始化函数

/************************************************************************************** * 描  述 : 硬件SPI初始化 * 入  参 : 无 * 返回值 : 无 **************************************************************************************/
void Init_SPI(void)
{  P_SW1 |=0X08;                         //将 SPI 调整到 P7.4 P7.5 P7.6  P7.7   P_SW1 &=0XFB;                         //将 SPI 调整到 P7.4 P7.5 P7.6  P7.7   SPDAT = 0;  SPSTAT = SPIF | WCOL;               //清除SPI状态位  SPCTL = SPEN | MSTR | SSIG;         //主机模式
}

程序清单:硬件SPI读写字节函数

/*********************************************************************** * 描  述 : 硬件SPI写入一个字节,并返回一个值 * 入  参 : uint8 date * 返回值 : 无 ***********************************************************************/
uint8 SPI_SendByte(uint8 SPI_SendData)
{  SPDAT = SPI_SendData;        //触发SPI发送数据  while (!(SPSTAT & SPIF));    //等待发送完成  SPSTAT = SPIF | WCOL;        //清除SPI状态位  return SPDAT;                //返回SPI数据
}

然后,编写对FLASH存储器的基本操作函数,如下表所示。

表7:FLASH相关用户函数汇集

关于每个操作外部FLASH相关用户函数,下面给出详细代码。

程序清单:FLASH芯片写使能函数

/*********************************************************************** * 描  述 : 写使能(将WEL置位) * 入  参 : 无 * 返回值 : 无 **************************************************************************/
void WriteEnable (void)
{  SPI_CS_0;  SPI_SendByte(W25X_WriteEnable);    SPI_CS_1;
}

程序清单:FLASH芯片写禁止函数

/************************************************************************** * 描  述 : 写禁止(将WEL清0) * 入  参 : 无 * 返回值 : 无 ***********************************************************************/
void WriteDisable (void)
{  SPI_CS_0;  SPI_SendByte(W25X_WriteDisable);    SPI_CS_1;
}

程序清单:读FLASH芯片ID函数

/********************************************************************** * 描  述 : 读取存储器芯片ID * 入  参 : 无 * 返回值 : uint16 ID
备注:W25Q16的ID:0XEF14  W25Q32的ID:0XEF15  W25Q64的ID:0XEF16  W25Q128的ID:0XEF17
************************************************************************/
uint16 W25Q_ReadID(void)
{  uint16 Temp = 0;  uint8 Temp1 = 0;  uint8   Temp2 = 0;  SPI_CS_0;                     SPI_SendByte(0x90);        //发送读取ID命令         SPI_SendByte(0x00);           SPI_SendByte(0x00);           SPI_SendByte(0x00);                      Temp1|=SPI_SendByte(0xFF);    Temp2|=SPI_SendByte(0xFF);    Temp = Temp1*256+Temp2;  SPI_CS_1;                 return Temp;
}

程序清单:读FLASH状态寄存器函数

/********************************************************************** * 描  述 : 读取存储器芯片的状态 * 入  参 : 无 * 返回值 : uint8 状态寄存器数据字节
备注:芯片内部状态寄存器第0位=0表示空闲,0位=1表示忙
***********************************************************************/
uint8 W25Q_ReadStatus(void)
{  uint8 status=0;  SPI_CS_0;  SPI_SendByte(W25X_ReadStatus);   // 0x05读取状态的命令字  status=SPI_SendByte(0xFF);            // 读取状态字节  SPI_CS_1;                         return status;
}

程序清单:写FLASH状态寄存器函数

/************************************************************************* * 描  述 : 写芯片的状态寄存器 * 入  参 : uint8 Status * 返回值 : 无
备注:只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
************************************************************************/
void W25Q_WriteStatus(uint8 Status)
{  SPI_CS_0;  SPI_SendByte(W25X_WriteStatus);  // 0x01读取状态的命令字  SPI_SendByte(Status);            // 写入一个字节  SPI_CS_1;
}

程序清单:在一页内写入多字节数据函数

/*********************************************************************** * 描  述 : 在一页(0~65535)内写入少于256个字节的数据(在指定地址开始写入最大256字节的数据) * 入  参 : pbuf:数据存储区  WriteAddr:开始写入的地址(24bit)  Len:要写入的字节数(最大256)  * 返回值 : 无
备注:Len:要写入的字节数,该数不应该超过该页的剩余字节数!!!
**********************************************************************/
void W25Q_Write_Page(uint8* pbuf,uint32 WriteAddr,uint16 Len)
{  uint16 i;  while(W25Q_ReadStatus()&0x01);        //判断是否忙  WriteEnable();                          //写使能  SPI_CS_0;                               //使能器件     SPI_SendByte(W25X_Writepage);          //发送写页命令  SPI_SendByte((uint8)((WriteAddr)>>16));   //发送24bit地址     SPI_SendByte((uint8)((WriteAddr)>>8));     SPI_SendByte((uint8)WriteAddr);    for(i=0;i<Len;i++)                      //循环写数  {  SPI_SendByte(*pbuf++);        }  SPI_CS_1;                               //取消片选  while(W25Q_ReadStatus()&0x01);        //等待写入结束
}

程序清单:向FLASH指定地址存入多字节数据函数

/************************************************************************  * 描  述 : 在指定地址开始写入指定长度的数据 * 入  参 : pbuf:数据存储区  WriteAddr:开始写入的地址(24bit)  Len:要写入的字节数(最大65535) * 返回值 : 无
备注:必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
************************************************************************/
void W25Q_Write_N(uint8 * pbuf,uint32 WriteAddr,uint16 Len)
{  uint16 PageLen;                  // 页内写入字节长度  PageLen=256-WriteAddr%256;       // 单页剩余的字节数 (单页剩余空间)  if(Len<=PageLen) PageLen=Len;    // 不大于256 个字节  while(1)  {  W25Q_Write_Page(pbuf,WriteAddr,PageLen);  if(PageLen==Len)break;         // 写入结束了  else  {  pbuf+=PageLen;  WriteAddr+=PageLen;  Len-=PageLen;              //  减去已经写入了的字节数  if(Len>256)PageLen=256;    // 一次可以写入256 个字节  else PageLen=Len;          // 不够256 个字节了  }  }
}

程序清单:从FLASH指定地址读取多字节数据函数

/*********************************************************************** * 描  述 : 在指定地址开始读取指定长度的数据 * 入  参 : pbuf:数据存储区  ReadAddr:开始读取的地址(24bit)  Len:要读取的字节数(最大65535) * 返回值 : 无
************************************************************************/
void W25Q_Read_N(uint8 * pbuf,uint32 ReadAddr,uint16 Len)
{  uint16 i;    while(W25Q_ReadStatus()&0x01);        // 判断是否忙 SPI_CS_0;                             // 使能器件     SPI_SendByte(W25X_ReadDATA8);        // 发送读取命令     SPI_SendByte((uint8)((ReadAddr)>>16));  // 发送24bit地址     SPI_SendByte((uint8)((ReadAddr)>>8));     SPI_SendByte((uint8)ReadAddr);    for(i=0;i<Len;i++)  {  *pbuf++=SPI_SendByte(0xFF);            // 读一个字节     }  SPI_CS_1;                             // 取消片选
}

程序清单:按扇区擦除数据函数

/********************************************************************** * 描  述 : 擦除一个扇区( 4K扇擦除) * 入  参 : uint32 Addr24  扇区地址 * 返回值 : 无
备注:擦除一个扇区的最少时间:150ms
*************************************************************************/
void W25Q_SectorErase(uint32 Addr24) //擦除资料图示的4KB空间
{  unsigned char Addr1;       // 最低地址字节  unsigned char Addr2;       // 中间地址字节  unsigned char Addr3;       // 最高地址字节    Addr1=Addr24;  Addr24=Addr24>>8;  Addr2=Addr24;  Addr24=Addr24>>8;  Addr3=Addr24;                      // 把地址拆开来    while(W25Q_ReadStatus()&0x01);     // 判断是否忙     WriteEnable();                     // 写允许  SPI_CS_0;  SPI_SendByte(W25X_S_Erase);       // 整扇擦除命令  SPI_SendByte(Addr3);  SPI_SendByte(Addr2);  SPI_SendByte(Addr1);  SPI_CS_1;  while(W25Q_ReadStatus()&0x01);   // 等待擦除完成
}

程序清单:按块擦除数据函数

/************************************************************************* * 描  述 : 擦除一块擦除( 64K) * 入  参 : uint32 Addr24  扇区地址 * 返回值 : 无
**************************************************************************/
void W25Q_BlockErase(uint32 Addr24)  //擦除资料图示的64KB空间
{  uint8 Addr1;       // 最低地址字节  uint8 Addr2;       // 中间地址字节  uint8 Addr3;       // 最高地址字节    Addr1=Addr24;  Addr24=Addr24>>8;  Addr2=Addr24;  Addr24=Addr24>>8;  Addr3=Addr24;                      // 把地址拆开来    while(W25Q_ReadStatus()&0x01);     // 判断是否忙     WriteEnable();                     // 写允许  SPI_CS_0;  SPI_SendByte(W25X_B_Erase);       // 整扇擦除命令  SPI_SendByte(Addr3);  SPI_SendByte(Addr2);  SPI_SendByte(Addr1);  SPI_CS_1;  while(W25Q_ReadStatus()&0x01);   // 等待擦除完成
}

程序清单:擦除整片芯片函数

/*********************************************************************** * 描  述 : 擦除整片芯片 * 入  参 : 无 * 返回值 : 无
备注:不同型号的芯片时间不一样
*************************************************************************/
void W25Q_ChipErase(void)
{  while(W25Q_ReadStatus()&0x01);     // 判断是否忙     WriteEnable();                     // 写允许  SPI_CS_0;  SPI_SendByte(W25X_C_Erase);       // 整片擦除命令  SPI_CS_1;                          // 从CS=1时开始执行擦除  while(W25Q_ReadStatus()&0x01);     // 等待擦除完成
}

最后,在主函数中对串口1、SPI外设进行初始化,并通过串口1发送不同的命令实现对单片机片外FLASH的单字节读、写及擦除等操作。

代码清单:主函数

int main()
{  uint16  Temp;  uint8   Temp1,Temp2;  P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  SPI_CS_1;                                     //SPI使能引脚初始化  Init_SPI();                                     //初始化SPI  Uart1_Init();                                 //串口1初始化    EA = 1;                                       //使能总中断  delay_ms(10);                                 //初始化后延时  Temp=W25Q_ReadID();                           //读取外扩存储器芯片ID  Temp1=(uint8)(Temp/256);                      //将读取的存储器芯片ID高字节存放到Temp1中  Temp2=(uint8)Temp;                            //将读取的存储器芯片ID低字节存放到Temp2中  while (1)  {  if(W_ID)                           //读存储器芯片ID模式  {  W_ID=0;                            //ID标志变量清零,发送一次  SendDataByUart1(Temp1);            //串口1发送芯片ID高字节  SendDataByUart1(Temp2);            //串口1发送芯片ID低字节  }  if(WriteFLAG)                     //写模式  {  WriteFLAG=0;                      //写标志变量清零,发送一次  W25Q_Write_N(scan,0x00000010,1);  //在0x00000010地址开始写入1字节scan中的数据  SendDataByUart1(0x33);            //串口1发送数据0x33  }  if(ReadFLAG)                      //读模式  {  ReadFLAG=0;                         //读标志变量清零,发送一次  W25Q_Read_N(buffer,0x00000010,1);   //读0x00000010地址开始的1字节数据到buffer中  SendStringByUart1_n(buffer,1);      //串口1发送buffer中存的数据  }    if(ClearFLAG)                     //扇区擦除模式  {  ClearFLAG=0;                        //清除标志变量清零,发送一次  W25Q_SectorErase(0x00000010);       //对0x00000010地址所在的扇区进行扇区擦除  SendDataByUart1(0x00);              //串口1发送数据0x00  }     }
}

3.4.硬件连接

图8:开发板连接图

4.外接FLASH存储器读写多字节实验(硬件SPI)

4.1.工程需要用到的c文件

本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-16-3:外接FLASH存储器读写单字节实验(硬件SPI)”部分。

4.2.编写代码

首先,在w25q128.c文件中编写硬件SPI方式的读写字节函数和对FLASH存储器的基本操作函数。请参考“实验2-16-3:外接FLASH存储器读写单字节实验(硬件SPI)”部分。

然后,在主函数中对串口1和SPI进行初始化,并通过串口1发送不同的命令实现对单片机片外FLASH的多字节读、写及擦除等操作。

代码清单:主函数

int main()
{  uint16  Temp;  uint8   Temp1,Temp2;  P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  SPI_CS_1;                                     //SPI使能引脚初始化  Init_SPI();                                     //初始化SPI  Uart1_Init();                                 //串口1初始化    EA = 1;                                       //使能总中断  delay_ms(10);                                 //初始化后延时  Temp=W25Q_ReadID();                           //读取外扩存储器芯片ID  Temp1=(uint8)(Temp/256);                      //将读取的存储器芯片ID高字节存放到Temp1中  Temp2=(uint8)Temp;                            //将读取的存储器芯片ID低字节存放到Temp2中  while (1)  {  if(W_ID)                           //读存储器芯片ID模式  {  W_ID=0;                            //ID标志变量清零,发送一次  SendDataByUart1(Temp1);            //串口1发送芯片ID高字节  SendDataByUart1(Temp2);            //串口1发送芯片ID低字节  }  if(WriteFLAG)                     //写模式  {  WriteFLAG=0;                      //写标志变量清零,发送一次  W25Q_SectorErase(0x00000000);       //擦除一个扇区(0x00000000在的扇区)  W25Q_Write_N(scan,0x00000000,10); //向FLASH地址0x00000000中写入scan数组中10个字节数据  SendDataByUart1(0x33);            //串口1发送数据0x33表示写操作完成  }  if(ReadFLAG)                      //读模式  {  ReadFLAG=0;                         //读标志变量清零,发送一次  W25Q_Read_N(buffer,0x00000000,10);      //从FLASH地址0x00000000开始读取10字节数据并存入到buffer数组中  SendStringByUart1_n(buffer,10);      //串口1发送数组buffer中的值(即读取的多字节数据)  }    if(ClearFLAG)                     //扇区擦除模式  {  ClearFLAG=0;                        //清除标志变量清零,发送一次  W25Q_SectorErase(0x00000000);       //擦除一个扇区(0x00000000在的扇区)  SendDataByUart1(0x00);              //串口1发送数据0x00表示擦除完成  }     }
}

4.3.硬件连接

图9:开发板连接图

总结

以上是今天要讲的内容,希望对大家有帮助,如果有啥不明白的,欢迎讨论哦!。

【STC8A8K64S4A12开发板】—片外存储器FLASH讲解相关推荐

  1. STC8A8K64S4A12开发板介绍

    版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.STC8A8K64S4A12系列单片机介绍 二.STC8A8K64S4A12开发板概述 三.STC8A8K64S4A12开发 ...

  2. 【STC8A8K64S4A12开发板】—4x4矩阵按键检测

    版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件电路设计 1.矩阵按键检测介绍 2.矩阵按键检测原理介绍 二.软件设计 1.矩阵按键扫描实验 – 指示灯闪烁 1.1.工 ...

  3. 【STC8A8K64S4A12开发板】—RS485总线通信

    版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件设计 1.开发板串口硬件电路 2.RS485电气性能 3.RS485通信协议 4.RS485电路设计 二.软件设计 1. ...

  4. FPGA基础入门【8】开发板外部存储器SPI flash访问

    前两篇教程利用数码管project介绍了chipscope和各种烧写开发板的方式,这篇开始继续探索开发板,这次关注外置存储器的控制,外置指的是芯片外部,不是开发板外部.板子上的外置存储器有DDR2和S ...

  5. 【STC8A8K64S4A12开发板】—小白做GPIO按键实验

    版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件电路设计 1.开发板用户按键硬件电路 2.按键检测接法 3.按键检测电路考虑因素 二.软件设计 1.寄存器解析 1.1. ...

  6. 学习 ARM 系列 -- FS2410 开发板上 Nand Flash 到内存的代码搬移

    一.目的    通过将 Nand Flash 前 4K 代码搬移到 SDRAM 中,了解如何初始化并使用 ARM 的内存,    为编写 ARM bootloader 和搬移内核到内存作准备. 二.代 ...

  7. 【STC8A8K64S4A12开发板】—开始做 定时器/计数器 实验啦

    版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件设计 1.TIMER概念介绍 2.STC8A8K64S4A12系列单片机定时器/计数器介绍 3.定时器/计数器工作模式 ...

  8. 【原创】用J-LINK烧写ARM开发板的Nor Flash

    以往烧写开发板上的nor flash,大多以JTAG接电脑并口进行烧写,但是目前带并口的电脑几乎很少,USB口则常见.虽然也有带USB的HJTAG,然而价格对于个人而言相对偏高,淘宝上几十元的J-LI ...

  9. 国产软硬件龙芯系列迅为2K1000开发板配置 nand flash

    开发板使用手册 让研发速度快上加快 更新包含了开发环境搭建.系统编译与烧写和pmon基础知识.启动流程分析.网络加载系统.烧写系统到nand以及新增pmon 移植教程.新增pmon下操作GPIO章节和 ...

最新文章

  1. C 语言判断大端小端
  2. Android 视频播放器,在线播放
  3. 安全态势感知产品对比_设计中的对比和人的感知
  4. 用sort()方法随机打乱数组
  5. 舍不得删的12个优质公号
  6. (延迟两秒,跳转相应页面)(返回到上一个页面并刷新)
  7. 【蓝桥杯 - 真题】六角幻方(dfs+剪枝)
  8. 腾讯优图提出LCVR-MQVI算法,勇夺NTIRE 2021双赛道冠亚军
  9. CSS样式的使用(CSS选择器、CSS语法、CSS常见样式):
  10. WiFi穿透能力甩对手两堵墙 荣耀9X新特性令人侧目
  11. 蓝桥杯2019年第十届C/C++省赛C组第二题-矩形切割
  12. 简单总结下8.25技术大会感受
  13. 人事管理系统:若依框架集成activiti工作流自动审批
  14. 5款高效软件推荐,每一款都是良心之作
  15. FTP连接时出现“227 Entering Passive Mode” 的解决方法
  16. 用Disk Genius检测和修复硬盘坏道
  17. latex算法框三线加粗
  18. USB OTG原理简述
  19. JavaWeb开发与代码的编写(二十四)
  20. C++之类和对象1:望尽天涯路

热门文章

  1. off文件格式(Object File Format)
  2. Object-C之文件操作
  3. 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败,原因是出现以下错误: 8000401a 因为配置标识不正确...
  4. 初入科研,如何正确做科研总结
  5. wget下载需登录的网页中的文件
  6. VGG16神经网络模型复现
  7. swift能干什么,不能干什么及相关概念
  8. 图像处理:推导五种滤波算法(均值、中值、高斯、双边、引导)
  9. 张量网络算法基础(一、张量和线性代数基础)
  10. springboot农产品销售网站管理系统java