Flash-W25Q64
-- 这一章的重点是重点是知道标准SPI通信,地址会算,FLASH时序要会看
目录
Flash-W25Q64
回顾
FLASH
SPI简介
单片机(32)上的SPI
W25Q64
模块之间应该怎么通信呢?-- 看时序图
代码编写
更改代码的错误
应用函数
应用:将图片生成的模写入flash中
回顾
-- LCD的核心主要是应用:带背景色的显示函数,动图等
-- LVGL:界面设计的工具
-- 低功耗模式:
尤其是电池供电的设备
设备进入低功耗模式:各个模块都进入低功耗模式
例如dht11,su03t,kqm6600,光敏电阻,mq2,lcd,芯片
芯片:睡眠》停机》待机(功耗从大到小)
-
睡眠模式:任一个中断就可唤醒,唤醒之后,代码接着往下运行
睡眠模式只把1.8v供电关闭 -
停机模式:外部中断可以唤醒 ,唤醒之后,代码接着往下运行(唤醒之后系统默认使用内部高速时钟,所以要进行时钟重新初始化),类似于暂停
停机模式是把VDDA,VDD,1.8v关闭了,电压调节器未关闭 -
待机模式:需要特殊的唤醒方式(比如唤醒引脚,闹钟中断),唤醒之后,代码从头开始运行(重新),相当于复位
待机模式是把VDDA,VDD,1.8v,电压调节器都关闭了
供电区域: 主要给什么供电
VDDA :ADC
VDD : IO,电压调节器
1.8V :内核
后备区域:RTC
spi与FALSH
FLASH
-- 今天讲FLASH芯片:型号是w25Q64:8M的空间储存器(这个flash芯片与32芯片的一样)
-- 为什么引入这个呢?
-- 上一章学的LCD显示,汉字,图片,字符,都要先取模(取得模,都是数组,占的空间很大),咱们的芯片只有512kb,程序运行的时候,会报错,出现空间不够的情况。
-- 所以,我们引入一个外部的储存器,把取模的数据,放到外部的储存器中,这样,我们的芯片就可以正常工作了。
-- 今天我们讲FLASH芯片,主要的作用:保存字模,图片模
-- (FLASH芯片在银色旁边)
SPI简介
-- USART,SPI,IIC三大串行通信方式
-- SPI:是通信总线
-- 什么叫总线类型的通信?(SPI和IIC都叫做总线通信)
- 多设备通信,一对多,多对一(也支持一对一) (多个设备可以挂载到总线上,通过总线进行通信)
-- 如何判断设备是否支持串口通信?有RX和TX
-- 如何判断设备是否支持SPI通信? 有以下四根线
- SCK:同步时钟
- CS:片选线(类似于通信上的使能)
- MISO:主机输入,从机输出
- MOSI:主机输出,从机输如
-- 串口是异步通信,SPI是同步通信(有时钟线)
-- SPI是怎么实现一对多通信的?SPI物理层
连接的个数跟芯片有关,也跟从机有关
为什么主机有两个cs,从机只有一个cs?每个从机都有一个cs,主机通过cs来控制从机,有几个从机,主机就有几个cs。
-- 为什么SPI只适合短距离通信?
-- SPI的通信核心是数据交换原则
-- 什么是数据交换原则?
-- 那么为什么要在上升沿的时候发送数据,下降沿的时候接收数据呢?
-- SPI通信有四个工作模式:SPI0,SPI1,SPI2,SPI3
这四种工作模式规定了主机在哪个边沿(上升沿,下降沿)进行数据发送
-- 四种工作模式是如何形成的?
CPOL:时钟极性 (有两个取值0或者1),规定了空闲状态下,时钟的电平情况(0:低电平 1:高电平) CPHA:时钟相位(也有两个取值0/1),决定了主机在第几个边沿进行数据发送(当时钟相位=0时,是第一个边沿,=1时是第二个边沿)
单片机(32)上的SPI
-- 查看参考手册上的SPI框图
-- 接着看引脚
-- 在32单片机上,比较特殊的一点,也是与标准SPI不同的一点就是他的其中一个引脚不是cs,而是NSS。还有就是32单片机上的SPI可以支持多主机(而在其他单片机的正常情况下,都是一主多从)。
每个STM32芯片都可以做主机,如果说某一个设备SSOE被使能,输出低电平,其他的设备都会变成从机
W25Q64
-- 下面来看该型号的官方文档
-- 打开官方文档,看该型号的一些信息,其中最重要的一点:
- 作为一个储存器,他是有自己的储存结构的(最小的是以字节为单位,然后从小到大一次是:字节-》页-》扇区-》块)
- 单位的换算:一页是256字节,一扇区是16页,一块是16扇区。 而FLash的储存空间为8M,也就是810241024字节,也就是8102416*256字节 = 8M
-- 有储存器,就会涉及到储存地址,对于FLASH芯片,储存的大小一共是6位16进制数,也就是24位,那么存储大小是2^24次方,也就是16M。
-- 那么为什么用24位的储存地址呢?
因为FLASH芯片是用来储存数据的,
最终要把数据读出来,
要想把数据读出来,要先知道地址。
-- 那么地址是怎么规定的呢?
-- 例如:第一块第二扇区第三页第五字节怎么表示相应的地址?
- 注意:如何换算地址这里是重点!!!
模块之间应该怎么通信呢?-- 看时序图
-- 这里依旧是看该型号的官方文档
-- 先看指令表
-- 首先来看写使能的时序图,主机向从机发送06h指令
-- 然后看读数据(读数据和写数据的时序图类似)
代码编写
更改代码的错误
-- 打开固件库
-- 找到相应的函数后,将其加入工程之后,编译,会出现一个错误
-- 将头文件改成
-- 之后编译,发现出现了更多错误,23个,莫方
-
1、将117行和129行屏蔽,同时把127行赋初值之后编译,同样是23个错误
-
2、接下来改宏定义的问题(在.h中)
-
3、打开原理图,找到对应的引脚
-
4、这些问题是因为官方例程里没有IO初始化,我们需要在初始化函数里加上IO初始化,并配置好对应的模式(.c文件中)
注意这里是外设,所以是复用推挽
//IO初始化----RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure = {0}; //定义结构体变量,并且将结构体变量赋初值GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15; //引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽模式GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空模式GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式GPIO_Init(GPIOB, &GPIO_InitStructure);
- 5、随后发现SPI的初始化没有开时钟,写上开启时钟的代码,并编译,发现依然是23个错误
//---------------------------------------------------------------------------------RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//开SPI2的时钟
- 6、下面开始到.h文件中改宏定义,编译之后发现只有5个错误
加入两行代码
#define sFLASH_CS_GPIO_PORT GPIOB
#define sFLASH_CS_PIN GPIO_Pin_12
- 7、发现错误是未定义外设的名字
在刚刚的宏定义前加上定义外设的代码
#define sFLASH_SPI SPI2
- 8、同样,这里面对指令的宏定义有些也是不对的
将这些宏定义更改为:
#define sFLASH_CMD_WRITE 0x02 /*!< Write to Memory instruction */
#define sFLASH_CMD_WRSR 0x01 /*!< Write Status Register instruction */
#define sFLASH_CMD_WREN 0x06 /*!< Write enable instruction */
#define sFLASH_CMD_READ 0x03 /*!< Read from Memory instruction */
#define sFLASH_CMD_RDSR 0x05 /*!< Read Status Register instruction */
#define sFLASH_CMD_RDID 0x9F /*!< Read identification */
#define sFLASH_CMD_SE 0x20 /*!< Sector Erase instruction */
#define sFLASH_CMD_BE 0xD8 /*!< Bulk Erase instruction */
-- 将这些错误全部更改完毕后,再次编译,就没有错误了
应用函数
-- 其实这么多函数我们目前只会用到其中几个函数
-- 首先是读数据和写数据函数
-- 然后是擦除函数,分别是扇区擦除和块擦除
-- 那么为什么要用擦除函数呢?
首先,观察过我们之前下载程序的时候就可以看出每次都会有擦除的步骤
哪怕是使用J-Link下载,也要擦除,校验,编译
-- 那么什么是擦除呢?
会将flash中保存的数据先擦除,擦除之后,其存储单元的每一位都会变为1
因为向flash中下载数据的时候,只能把1变成0,不能把0变成1
-- 所以写数据之前,要先进行擦除
-- 我们这里简单看一个官方代码,写数据的代码,我们可以看出写数据函数中的核心就是这个函数
-- 我们进入这个函数
注:最小单位的虽然是字节,但是都是以页为单位进行写数据
-- 写使能函数,就是按照时序图来写的
-- 在主函数中编写将数据写入FLASH的代码
//SPI***********************************************************************sFLASH_Init();char abuff[]="123456789qweaszc";sFLASH_EraseSector(0x009000);//写准备写进哪个扇区,就擦除该扇区(第10个扇区)sFLASH_WriteBuffer(abuff,0x009000,strlen(abuff));//第一个参数是abuff的地址,第二个参数是你要写入的扇区地址,第三个参数是要读的数量printf("写入完成\r\n");
-- 但是不知道写成功没,怎么验证呢?读出来
注意:只需要在写之前擦除,不需要在读之前擦除,还有就是今天写的函数,不能放在while循环里面一直擦除,因为flash芯片有寿命,规定了擦除的次数,如果擦除的次数超过规定的次数,将会擦除不了
-- 写入读数据
char bbuff[100]={0};sFLASH_ReadBuffer(bbuff,0x009000,50);//第一个参数是要保存的地址,第三个参数是要要读的数量printf("bbuff:%s\r\n");
-- 从扇区中读50个数的效果图为:
应用:将图片生成的模写入flash中
-- 由于生成的图片的模占的内存很大,所以我们使用flash来保存
-- 首先计算图片的模占的内存大小,算出要擦除的扇区个数
--