01
概述
W25Q128是一种NOR Flash芯片,掉电后数据不丢失的特点。
W25Q128FV阵列被组织成65,536个可编程页面,每个页面256字节。每次最多可编程256字节。可以以16页为一组(即一个Sector)、128页为一组(8个Sector)、256页为一组(16个Sector)或整个芯片(芯片擦除)进行擦除。W25Q128FV分别有4,096个可擦除扇区和256个可擦除块。较小的4KB扇区为需要数据和参数存储的应用程序提供了更大的灵活性。
标准SPI通信支持时钟频率高达104MHz,Dual SPI通信支持时钟频率高达208MHz,QSPI通信支持时钟频率高达416MHz。
注意:W25Q128一共为128M bits(16M Byte),又分为256个块(每个块512K bit(64K Byte)),每个块又分为16个扇区(每个扇区32K bit(4 KByte)),每个扇区又分为16页(每个页2K bit(256 Byte))
02
物理特性
可以将 1 写成 0,但是不能将 0 写成 1,要想将 0 写成 1,必须进行擦除操作。如果要改变数据,就需要先擦除后写数据。
如果想要修改小于扇区大小的数据,需要将整个扇区的数据,在内存中进行备份,然后修改内存中的数据,再将数据写回到原扇区位置。因此,驱动要达到支持自动完成这个过程,用户可以使用驱动修改任意位置的数据。
03
存储结构
W25Q128可以存储16777216字节,存储一个字节占用一个地址,所以寻址范围是0-(16777216-1),对应的16进制为0-0xFFFFFF(所以寄存器地址是24位的)
04
命令总览
05
组件的使用
1 Gitee链接地址
Demo位于amaziot_bloom_os_sdk\sample\3rd\2.1_W25Q128 Gitee源码地址:https://gitee.com/ning./hongdou Github源码地址:https://github.com/ayumid/hongdou 编译指令:.\build.bat -l .\amaziot_bloom_os_sdk\sample\3rd\2.1_W25Q128 |
2 组件功能介绍
实现软件模拟SPI,驱动W25Q128芯片,实现数据存储。
3 代码讲解
1 drv_w25q128_delay_us
功能:该函数用于,延时。
参数:
返回值:无
示例:
C //初始化i2c总线 ret = drv_xl9535_i2c_init(); |
2 drv_w25q128_gpio_set
功能:该函数用于,模拟SPI设置IO输出电平。
参数:
返回值:0 成功,-1 失败
示例:
C drv_w25q128_gpio_set(DRV_w25q128_SPI_CS, DRV_w25q128_GPIO_LOW); |
3 drv_w25q128_byte_wr
功能:该函数用于,SPI写读一个字节 mode3。
参数:
返回值:flash返回数据
示例:
C drv_w25q128_byte_wr(DRV_w25q128_DUMMY_BYTE); |
4 drv_w25q128_byte_rd
功能:该函数用于,SPI只读一个字节。
参数:无
返回值:flash返回数据
示例:
C drv_w25q128_byte_rd(DRV_w25q128_DUMMY_BYTE); |
5 drv_w25q128_busy_wait
功能:该函数用于,W25Q128 忙等待。
参数:无
返回值:无
示例:
C while(drv_w25q128_read_reg1() & BIT_BUSY); |
6 drv_w25q128_read_reg
功能:该函数用于,读reg。
参数:无
返回值:无
示例:
C while(drv_w25q128_read_reg() & BIT_BUSY); |
7 drv_w25q128_read_jedecid
功能:该函数用于,读 W25Q128 JEDEC_ID(制造商、类型、容量)。
参数:无
返回值:无
示例:
C sample_w25q128_uart_printf("identification is 0x%X, Device id is 0x%X, Manufacturer Device ID is 0x%X", drv_w25q128_read_identification(), drv_w25q128_read_device_id(), drv_w25q128_read_manufacturer_id()); |
8 drv_w25q128_read_manufacturer_id
功能:该函数用于,读 W25Q128 制造商 ID。
参数:无
返回值:无
示例:
C sample_w25q128_uart_printf("identification is 0x%X, Device id is 0x%X, Manufacturer Device ID is 0x%X", drv_w25q128_read_identification(), drv_w25q128_read_device_id(), drv_w25q128_read_manufacturer_id()); |
9 drv_w25q128_read_device_id
功能:该函数用于,读 W25Q128 设备 ID。
参数:无
返回值:无
示例:
C sample_w25q128_uart_printf("identification is 0x%X, Device id is 0x%X, Manufacturer Device ID is 0x%X", drv_w25q128_read_identification(), drv_w25q128_read_device_id(), drv_w25q128_read_manufacturer_id()); |
10 drv_w25q128_write_enable
功能:该函数用于,写使能。
参数:无
返回值:无
示例:
C sample_w25q128_uart_printf("identification is 0x%X, Device id is 0x%X, Manufacturer Device ID is 0x%X", drv_w25q128_read_identification(), drv_w25q128_read_device_id(), drv_w25q128_read_manufacturer_id()); |
11 drv_w25q128_write_disable
功能:该函数用于,写失能。
参数:无
返回值:无
示例:
12 drv_w25q128_write_page
功能:该函数用于,页编程(调用本函数写入数据前需要先擦除扇区)。
参数:
返回值:无
示例:
C drv_w25q128_write_page(pbuf, addr, pageremain); |
13 drv_w25q128_read
功能:该函数用于,读闪存数据。
参数:
返回值:无
示例:
C drv_w25q128_read((UINT8*)rx_buff1, 8181, strlen(tx_buff1)); |
14 drv_w25q128_sector_erase
功能:该函数用于,扇区擦除。
参数:
返回值:无
示例:
C drv_w25q128_sector_erase(secpos * DRV_w25q128_SOCTOR_SIZE); |
15 drv_w25q128_chip_rease
功能:该函数用于,FLASH整片擦除(为了安全起见,若要调用,请先调用 drv_w25q128_write_enable 函数)。
参数:无
返回值:无
示例:
16 drv_w25q128_powr_down
功能:该函数用于,掉电。
参数:无
返回值:无
示例:
17 drv_w25q128_release_powr_down
功能:该函数用于,读闪存数据。
参数:
返回值:无
示例:
C drv_w25q128_read((UINT8*)rx_buff1, 8181, strlen(tx_buff1)); |
18 drv_w25q128_write_nocheck
功能:该函数用于,写数据。
参数:
返回值:无
示例:
C drv_w25q128_write_nocheck(w25q128_buffer, secpos * DRV_w25q128_SOCTOR_SIZE, DRV_w25q128_SOCTOR_SIZE); |
19 drv_w25q128_write
功能:该函数用于,写闪存数据,可以使任意地址。
参数:
返回值:无
示例:
C drv_w25q128_write((UINT8*)tx_buff1, 8181, strlen(tx_buff1)); |
20 drv_w25q128_init
功能:该函数用于,写数据。
参数:无
返回值:无
示例:
4 Demo实战
4.1 创建一个Demo
复制20.1_file_xtu示例工程,到同一个文件夹下,修改文件名为3.1_SSD1315,如图:
4.2 修改makefile
增加文件组件所在目录头文件路径,和源文件路径,如图:
4.3 增加头文件
使用代码编辑器,将新建的工程文件加入代码编辑器中,打开main.c,修改main.c,加入am.h等头文件,如图:
4.4 修改代码
在Phase2Inits_exit 创建一个任务,如图:
4.1 概述
上电后,按下按键,串口会打印出按下了哪一个按键
4.2 测试
测试步骤:
- 参考编译教程,和文档开头的编译指令,进行编译
- 按照编译教程选择对应的选项
- 烧录
4.3 宏定义介绍
- sample_w25q128_uart_printf
输出日志到DEBUG 串口,日志比较少,可以输出到这个串口,如果日志比较多,需要输出到usb口,以免不必要的问题出现
- sample_w25q128_catstudio_printf
输出日志到USB 串口,使用catstudio查看,catstudio查看日志需要更新对应版本mdb.txt文件,软件打开filtter过滤日志,只查看用户输出的日志
- SAMPLE_W25Q128_STACK_SIZE
栈空间宏定义
4.4 全局变量介绍
任务指针
4.5 函数介绍
底层初始化,本例空
底层初始化,本例空
底层初始化,本例空
创建主任务,初始化INT 引脚
代码片段:
C void Phase2Inits_exit(void) { int ret;
sample_w25q128_task_stack = malloc(SAMPLE_W25Q128_STACK_SIZE);
ret = OSATaskCreate(&sample_w25q128_task_ref, sample_w25q128_task_stack, SAMPLE_W25Q128_STACK_SIZE, 88, "sample_w25q128_task", sample_w25q128_task, NULL); ASSERT(ret == OS_SUCCESS); } |
主任务,代码发分为两部分,一部分是发送不定长数据;另一部分是上电后等待其它模块发送的数据,收到后打印到串口。
代码片段:
C static void sample_w25q128_task(void *ptr) { int ret = 0; uint32_t identification = 0; // unsigned char writeBuf[30] = {0}; // unsigned char readBuf[30] = {0};
// ret = ql_spi_init(QL_SPI_PORT0, QL_SPI_MODE3, QL_SPI_CLK_812_5KHZ); // sample_w25q128_catstudio_printf("ql_spi_init ret %d", ret);
drv_w25q128_init();
identification = drv_w25q128_read_jedecid(); sample_w25q128_uart_printf("identification is 0x%X, Device id is 0x%X, Manufacturer Device ID is 0x%X", drv_w25q128_read_jedecid(), drv_w25q128_read_device_id(), drv_w25q128_read_manufacturer_id()); // while(1) // { // drv_w25q128_gpio_set(DRV_w25q128_SPI_CS, 0); // sample_w25q128_uart_printf("low"); // OSATaskSleep(5*200); // drv_w25q128_gpio_set(DRV_w25q128_SPI_CS, 1); // sample_w25q128_uart_printf("high"); // OSATaskSleep(5*200); // } if(identification != JEDECID) { /* 读取错误处理 */ sample_w25q128_uart_printf("SPI read-write Error, please check the connection between MCU and SPI Flash\n"); } else { //读取成功处理 char tx_buff1[64] = "abcdefghigklmnopqrstuvwxyz0123456789"; char rx_buff1[64] = {0}; char tx_buff2[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210"; char rx_buff2[64] = {0}; int i = 0; //测试跨sector写,并且读出数据,写两次,第二次保留第一次部分数据,证明数据擦除,写入正常 drv_w25q128_write((UINT8*)tx_buff1, 8181, strlen(tx_buff1));//从8181地址开始写数据,需要写第二和第三个扇区 drv_w25q128_read((UINT8*)rx_buff1, 8181, strlen(tx_buff1)); sample_w25q128_uart_printf("read flash:%s", rx_buff1);
if(!strncmp(tx_buff1, rx_buff1, strlen(tx_buff1))) { sample_w25q128_uart_printf("SPI read-write succeed 1"); } //验证驱动擦除扇区时,可以保留之前有效内容 drv_w25q128_write((UINT8*)tx_buff2, 8186, strlen(tx_buff2));//从8186地址开始写数据,需要写第二和第三个扇区,同时不能擦掉8181 - 8186的5字节数据 drv_w25q128_read((UINT8*)rx_buff2, 8181, strlen(tx_buff2) + 5); sample_w25q128_uart_printf("read flash:%s", rx_buff2);
if(!strncmp(rx_buff2, "abcdeABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210", strlen("abcdeABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210"))) { sample_w25q128_uart_printf("SPI read-write succeed 2"); } } // memset(writeBuf, 0x00, sizeof(writeBuf)); // memset(readBuf, 0x00, sizeof(readBuf));
// writeBuf[0] = 0x9F; while (1) { // ret = ql_spi_write_read(QL_SPI_PORT0, readBuf, writeBuf, 1); // sample_w25q128_catstudio_printf("ql_spi_write_read ret %d, readBuf %02X,%02X,%02X\n", ret, readBuf[0], readBuf[1], readBuf[2]); // ret = ql_spi_write(QL_SPI_PORT0, writeBuf, 1); // sample_w25q128_catstudio_printf("ql_spi_write_read ret %d, readBuf %02X\n", ret, writeBuf[0]); // ret = ql_spi_read(QL_SPI_PORT0, readBuf, 3); // sample_w25q128_catstudio_printf("ql_spi_write_read ret %d, readBuf %02X,%02X,%02X\n", ret, readBuf[0], readBuf[1], readBuf[2]); OSATaskSleep(5 * 200); } } |
4.6 固件
点击下载 Lora Demo固件
5 生态组件链接
SPI NOR FLASH
注:本文部分内容来源于网络,如有侵权,请及时联系我们。
本文章源自奇迹物联开源的物联网应用知识库Cellular IoT Wiki,更多技术干货欢迎关注收藏Wiki:Cellular IoT Wiki 知识库(https://rckrv97mzx.feishu.cn/wiki/wikcnBvAC9WOkEYG5CLqGwm6PHf)
文章评论(0条评论)
登录后参与讨论