【STC8A8K64S4A12开发板】—片外存储器FLASH讲解
版权声明:本文为博主原创文章,转载请附上原文出处链接。
文章目录
- 前言
- 一、硬件设计
- 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)。
■ 使用注意事项
- 由于FLASH的物理特性,决定了FLASH每一位的操作只能从1变为0(写操作)。
- 大多数FLASH芯片或单片机内未使用FLASH存储空间每一位出厂默认都是1。
- 对FLASH写操作之前必须将待操作FLASH空间数据都置为1。
- 对FLASH的擦除操作即是把待操作的空间的每一位都置为1。
- 所以FLASH写操作前需有擦除操作。
- 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讲解相关推荐
- STC8A8K64S4A12开发板介绍
版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.STC8A8K64S4A12系列单片机介绍 二.STC8A8K64S4A12开发板概述 三.STC8A8K64S4A12开发 ...
- 【STC8A8K64S4A12开发板】—4x4矩阵按键检测
版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件电路设计 1.矩阵按键检测介绍 2.矩阵按键检测原理介绍 二.软件设计 1.矩阵按键扫描实验 – 指示灯闪烁 1.1.工 ...
- 【STC8A8K64S4A12开发板】—RS485总线通信
版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件设计 1.开发板串口硬件电路 2.RS485电气性能 3.RS485通信协议 4.RS485电路设计 二.软件设计 1. ...
- FPGA基础入门【8】开发板外部存储器SPI flash访问
前两篇教程利用数码管project介绍了chipscope和各种烧写开发板的方式,这篇开始继续探索开发板,这次关注外置存储器的控制,外置指的是芯片外部,不是开发板外部.板子上的外置存储器有DDR2和S ...
- 【STC8A8K64S4A12开发板】—小白做GPIO按键实验
版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件电路设计 1.开发板用户按键硬件电路 2.按键检测接法 3.按键检测电路考虑因素 二.软件设计 1.寄存器解析 1.1. ...
- 学习 ARM 系列 -- FS2410 开发板上 Nand Flash 到内存的代码搬移
一.目的 通过将 Nand Flash 前 4K 代码搬移到 SDRAM 中,了解如何初始化并使用 ARM 的内存, 为编写 ARM bootloader 和搬移内核到内存作准备. 二.代 ...
- 【STC8A8K64S4A12开发板】—开始做 定时器/计数器 实验啦
版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件设计 1.TIMER概念介绍 2.STC8A8K64S4A12系列单片机定时器/计数器介绍 3.定时器/计数器工作模式 ...
- 【原创】用J-LINK烧写ARM开发板的Nor Flash
以往烧写开发板上的nor flash,大多以JTAG接电脑并口进行烧写,但是目前带并口的电脑几乎很少,USB口则常见.虽然也有带USB的HJTAG,然而价格对于个人而言相对偏高,淘宝上几十元的J-LI ...
- 国产软硬件龙芯系列迅为2K1000开发板配置 nand flash
开发板使用手册 让研发速度快上加快 更新包含了开发环境搭建.系统编译与烧写和pmon基础知识.启动流程分析.网络加载系统.烧写系统到nand以及新增pmon 移植教程.新增pmon下操作GPIO章节和 ...
最新文章
- C 语言判断大端小端
- Android 视频播放器,在线播放
- 安全态势感知产品对比_设计中的对比和人的感知
- 用sort()方法随机打乱数组
- 舍不得删的12个优质公号
- (延迟两秒,跳转相应页面)(返回到上一个页面并刷新)
- 【蓝桥杯 - 真题】六角幻方(dfs+剪枝)
- 腾讯优图提出LCVR-MQVI算法,勇夺NTIRE 2021双赛道冠亚军
- CSS样式的使用(CSS选择器、CSS语法、CSS常见样式):
- WiFi穿透能力甩对手两堵墙 荣耀9X新特性令人侧目
- 蓝桥杯2019年第十届C/C++省赛C组第二题-矩形切割
- 简单总结下8.25技术大会感受
- 人事管理系统:若依框架集成activiti工作流自动审批
- 5款高效软件推荐,每一款都是良心之作
- FTP连接时出现“227 Entering Passive Mode” 的解决方法
- 用Disk Genius检测和修复硬盘坏道
- latex算法框三线加粗
- USB OTG原理简述
- JavaWeb开发与代码的编写(二十四)
- C++之类和对象1:望尽天涯路
热门文章
- off文件格式(Object File Format)
- Object-C之文件操作
- 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败,原因是出现以下错误: 8000401a 因为配置标识不正确...
- 初入科研,如何正确做科研总结
- wget下载需登录的网页中的文件
- VGG16神经网络模型复现
- swift能干什么,不能干什么及相关概念
- 图像处理:推导五种滤波算法(均值、中值、高斯、双边、引导)
- 张量网络算法基础(一、张量和线性代数基础)
- springboot农产品销售网站管理系统java