1. 基于HAL库配置SPI1总线

    使用STM32CubeMX生成SPI1配置代码,HAL库会自动初始化该函数,因为本来存在虚函数。如下代码所示:

//SPI5底层驱动,时钟使能,引脚配置
//此函数会被HAL_SPI_Init()调用
//hspi:SPI句柄
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE();       //使能GPIOA时钟__HAL_RCC_SPI1_CLK_ENABLE();        //使能SPI1时钟//PA5,6,7GPIO_Initure.Pin=GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;GPIO_Initure.Mode=GPIO_MODE_AF_PP;              //复用推挽输出GPIO_Initure.Pull=GPIO_PULLUP;                  //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;             //快速GPIO_Initure.Alternate=GPIO_AF5_SPI1;           //复用为SPI1HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}

2. W25Q64芯片

  1. W25Q64 (64M-bit), W25Q16(16M-bit)和W25Q32(32M-bit)是为系统提供一个最小的空间、引脚和功耗的存储器解决方案的串行 Flash 存储器。 25Q系列比普通的串行 Flash 存储器更灵活,性能更优越。基于双倍/四倍的 SPI,它们能够可以立即完成提供数据给 RAM,包括存储声音、文本和数据。芯片支持的工作电压 2.7V 到 3.6V,正常工作时电流小于 5mA,掉电时低于1uA。所有芯片提供标准的封装。
  2. W25Q64/16/32 由每页 256 字节组成。 每页的 256 字节用一次页编程指令即可完成。 每次可以擦除 16 页(1个扇区)、 128 页(32KB 块)、 256 页(64KB 块)和全片擦除。
  3. W25Q64 的内存空间结构: 一页 256 字节,4K(4096 字节)为一个扇区, 16 个扇区为 1 块, 容量为 8M 字节,共有 128 个块,2048 个扇区。
  4. W25Q64/16/32 支持标准串行外围接口(SPI),和高速的双倍/四倍输出,双倍/四倍用的引脚:串行时钟、片选端、串行数据I/O0(DI)、 I/O1(DO)、 I/O2(WP)和 I/O3(HOLD)。 SPI 最高支持80MHz,当用快读双倍/四倍指令时,相当于双倍输出时最高速率 160MHz,四倍输出时最高速率 320MHz。这个传输速率比得上 8位和 16 位的并行 Flash 存储器。
  5. 结构框图如下图所示:

3. 控制和状态寄存器

3.1 状态寄存器


    忙是只读的状态寄存器(S0)被设置为1状态时,表示设备正在执行程序(可能是在擦除芯片)或写状态寄存器指令。这个时候设备将忽略传来的指令, 除了读状态寄存器和擦除暂停指令,写指令或写状态指令无效, 当 S0 为 0 状态时指示设备已经执行完毕,可以进行下一步操作。

3.2 指令表

3.3 W25Q64工作原理

3.3.1 读状态寄存器(05hor35h)


    读取状态寄存器的指令是 8 位的指令。 发送指令之前, 先将/ CS 拉低, 再发送指令码“05 h”或者“35h”。 设备收到读取状态寄存器的指令后, 将状态信息(高位)依次移位发送出去, 上图 6 示。读出的状态信息, 最低位为 1 代表忙, 最低位为 0 代表可以操作, 状态信息读取完毕, 将片选线拉高。

/******************************************************
函数功能:读状态寄存器1 -05h
形参:无
返回值:rt_uint8_t status -- 返回从器件的状态(忙或空闲)
说明:发送 05H 读取状态寄存器的值之后, W25Q64 就会一直
返回状态寄存器的值,直到片选被拉高。
*******************************************************/
rt_uint8_t w25qxx_read_status(void)
{rt_uint8_t status = 0;SPI_FLASH_CS_LOW();                         //拉低片选spi1_read_write_byte(W25X_ReadStatusReg1);  //发送指令 0x05status = spi1_read_write_byte(0xff);        //status = 读数据SPI_FLASH_CS_HIGH();return status;
}

3.3.2 写使能(06h)

    写使能指令将状态寄存器中的Write Enable Latch (WEL)位设置为1。 WEL位必须在每个页面程序、扇区擦除、块擦除、芯片擦除和之前设置 写入状态寄存器指令。 写使能指令通过驱动/CS low输入,将指令码“06h”移至CLK上升沿的数据输入(Data Input, DI)引脚,然后驱动/CS high。

/******************************************************
函数功能:写使能———0x06
形参:无
返回值:无
说明:在执行每一个写操作之前都需要通过写使能指令将状态
寄存器的 WEL 置 1,让芯片进入写使能状态
*******************************************************/
void w25qxx_write_enble(void)
{SPI_FLASH_CS_LOW();                     //拉低片选--选中从器件spi1_read_write_byte(W25X_WriteEnable);   //发送指令 0x06SPI_FLASH_CS_HIGH();                    //拉高片选--释放从器件
}

3.3.3 读数据(03h)


    读取数据指令允许按顺序读取一个字节的内存数据。当片选 CS/拉低之后, 紧随其后是一个 24 位的地址(A23-A0)(需要发送 3 次, 每次 8 个字节, 先发高位)。芯片收到地址后,将要读的数据按字节大小转移出去, 数据是先转移高位,对于单片机,时钟下降沿发送数据,上升沿接收数据。 读数据时, 地址会自动增加, 允许连续的读取数据。这意味着读取整个内存的数据,只要用一个指令就可以读完。 数据读取完成之后, 片选信号/ CS 拉高。读取数据的指令序列,如上图所示。 如果一个读数据指令而发出的时候, 设备正在擦除扇区,或者(忙= 1), 该读指令将被忽略,也不会对当前周期有什么影响。

/******************************************************
函数功能: 读字节数据-0x03
形参:@rt_uint32_t addr : 起始地址@rt_uint16_t size :读数据长度@rt_uint8_t *buff :存取读取数据
返回值:无
说明:
********************************************************/
void w25qxx_read_data(rt_uint32_t addr, rt_uint8_t *buff, rt_uint8_t size)
{rt_uint8_t i;SPI_FLASH_CS_LOW();                           //拉低片选spi1_read_write_byte(W25X_ReadData);              //发送指令 0x03spi1_read_write_byte((addr & 0xff0000) >> 16);spi1_read_write_byte((addr & 0x00ff00) >> 8);spi1_read_write_byte((addr & 0x0000ff) >> 0);   //发送 24 位地址,先发送高 8 位for(i = 0; i < size; i++){buff[i] = spi1_read_write_byte(0xff);       //byte = 读数据}SPI_FLASH_CS_HIGH();                          //拉高片选
}

3.3.4 页编程(02h)


    页编程指令允许从一个字节到 256 字节的数据编程(一页)(编程之前必须保证内存空间是 0XFF)。允许写入指令之前,必须先发送设备写使能指令。 写使能开启后, 设备才能接收编程指令。 开启页编程先拉底/ CS, 然后发送指令代码“02 h”, 接着发送一个 24 位地址(A23-A0)(发送 3 次, 每次 8 位) 和至少一个数据字节(数据字节不能超过 256字节)。 数据字节发送完毕, 需要拉高片选线 CS/,, 并判断状态位, 等待写入结束。

/******************************************************
函数功能: 页写 - 0x02
形参:@rt_uint32_t addr : 起始地址@rt_uint16_t size :写多数据的长度@rt_uint8_t *buff :写入的数据
返回值:无
说明:
*******************************************************/
void w25qxx_page_write(rt_uint32_t addr, rt_uint8_t *buff, rt_uint8_t size)
{rt_uint8_t i;w25qxx_write_enble();                         //写使能SPI_FLASH_CS_LOW();                            //拉低片选--选中从器件spi1_read_write_byte(W25X_PageProgram);        //发送指令 0x02spi1_read_write_byte((addr & 0xff0000) >> 16);spi1_read_write_byte((addr & 0x00ff00) >> 8);spi1_read_write_byte((addr & 0x0000ff) >> 0);for(i = 0;i < size; i++){spi1_read_write_byte(buff[i]);}SPI_FLASH_CS_HIGH();while(w25qxx_read_status() & (1 << 0));        //判断状态位,等待可以进行写
}

3.3.5 扇区擦除(20h)


    扇区擦除指令可以擦除指定一个扇区(4 k 字节)内所有数据, 将内存空间恢复到 0xFF 状态。 写入扇区擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06), 并判断状态寄存器(状态寄存器位最低位必须等于 0才能操作)。发送的扇区擦除指令前, 先拉低/ CS, 接着发送扇区擦除指令码”20 h”, 和 24 位地址(A23-A0), 地址发送完毕后,拉高片选线 CS/,, 并判断状态位, 等待擦除结束。 擦除一个扇区的最少需要 150ms 时间。

/**********************************************************
函数功能: 扇区擦除-0x20
形参:@rt_uint32_t addr : 起始地址
返回值:无
说明:将指定区域的存储数据全部置为 0xFF,假如传入地址addr,
则被擦除的空间为 addr所在的扇区的整个扇区,而不会偏移到其
他扇区。
***********************************************************/
void w25qxx_sector_erase(rt_uint32_t addr)
{w25qxx_write_enble();//写使能SPI_FLASH_CS_LOW();spi1_read_write_byte(W25X_SectorErase);spi1_read_write_byte((addr & 0xff0000) >> 16);spi1_read_write_byte((addr & 0x00ff00) >> 8);spi1_read_write_byte((addr & 0x0000ff) >> 0);SPI_FLASH_CS_HIGH();while(w25qxx_read_status() & (1 << 0));
}

3.3.6 64KB 块擦除


    块擦除指令将指定块(64K-bytes)内的所有内存设置为1(FFh)的擦除状态。在设备接受块擦除之前,必须执行允许写指令 (状态寄存器位WEL必须等于1)。指令由驱动/CS引脚低启动 ,然后在一个24位的块地址(A23-A0)之后移动指令代码“D8h” 。在最后一个字节的第8位被锁存后,/CS引脚必须被驱动到高电平。 如果不这样做块擦除指令将不会被执行。 在/CS驱动高后,自动块擦除 指令将在tBE的一段时间内开始(见AC特性)。 而Block Erase循环 时,仍可访问读取状态寄存器指令,以检查忙状态。 BUSY位在块擦除周期中为1,在块擦除周期结束时为0
并且设备已经准备好再次接受其他指令。 在块擦除周期完成后 状态寄存器中的Write Enable Latch (WEL)位被清除为0。 块擦除指令不会被删除 如果寻址页面被Block Protect (SEC, TB, BP2, BP1和BP0)位保护,则执行
状态寄存器内存保护表)。

/**********************************************************
函数功能: 块擦除 - 0xD8
形参:@rt_uint32_t addr : 起始地址
返回值:无
说明:将指定区域的存储数据全部置为 0xFF,假如传入地址addr,
则被擦除的空间为 addr所在的块区的整个块区,而不会偏移到其
他块区。
***********************************************************/
void w25qxx_block_erase(rt_uint32_t addr)
{w25qxx_write_enble();//写使能SPI_FLASH_CS_LOW();spi1_read_write_byte(W25X_BlockErase);spi1_read_write_byte((addr & 0xff0000) >> 16);spi1_read_write_byte((addr & 0x00ff00) >> 8);spi1_read_write_byte((addr & 0x0000ff) >> 0);SPI_FLASH_CS_HIGH();while(w25qxx_read_status() & (1 << 0));
}

3.3.7 全片擦除


    全芯片擦除指令, 可以将整个芯片的所有内存数据擦除, 恢复到 0XFF 状态。 写入全芯片擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06), 并判断状态寄存器(状态寄存器位最低位必须等于 0 才能操作)。发送全芯片擦除指令前, 先拉低/ CS, 接着发送擦除指令码”C7h”或者是”60h”, 指令码发送完毕后, 拉高片选线 CS/,, 并判断状态位, 等待擦除结束。 全片擦除指令尽量少用, 擦除会缩短设备的寿命。

/**********************************************************
函数功能: 芯片擦除-0xC7
形参:无
返回值:无
说明:
***********************************************************/
void w25qxx_chip_erase(void)
{w25qxx_write_enble();//写使能SPI_FLASH_CS_LOW();spi1_read_write_byte(W25X_ChipErase);SPI_FLASH_CS_HIGH();while(w25qxx_read_status() & (1 << 0));
}

3.3.8 读取制造商/芯片 ID(90h)


    读取制造商/设备 ID 指令可以读取,制造商 ID 和特定的设备 ID。 读取之间, 拉低 CS 片选信号, 接着发送指令代码“90h” , 紧随其后的是一个 24 位地址(A23-A0)000000h。 之后, 设备发出, 华邦电子制造商 ID(EFh) 和设备ID(w25q64 为 16h)。如果 24 位地址设置为 000001 h 的设备 ID 会先发出,然后跟着制造商 ID。制造商和设备 ID 可以连续读取。完成指令后, 片选信号/ CS 拉高。

/***********************************************************
函数功能:MCU读取W25Q64的ID函数
函数形参:None
函数返回值:读到ID
备注:90H  厂家0xEF   芯片ID:0x16    正常:0xEF16
************************************************************/
rt_uint16_t w25qxx_read_id(void)
{rt_uint16_t id;SPI_FLASH_CS_LOW();                             //拉低片选--选中从器件spi1_read_write_byte(W25X_ManufactDeviceID);   //发送指令 0x90spi1_read_write_byte(0x00);spi1_read_write_byte(0x00);spi1_read_write_byte(0x00);id = spi1_read_write_byte(0xff);id <<= 8;id |= spi1_read_write_byte(0xff);SPI_FLASH_CS_HIGH();                         //拉高片选--释放从器件return id;
}

3.3.9 跨页写

    页编程只能在一页中写入数据,不能写到下一页中,若要想连续的写到下一页中,则可以通过地址的偏移来实现;如下代码所示:

/***********************************************************
函数功能:跨页写@rt_uint32_t addr : 起始地址@rt_uint8_t *buff :写入的内容@rt_uint16_t size :写入数据长度
函数返回值:None
备注:
************************************************************/
void w25qxx_auto_write(rt_uint32_t addr, rt_uint8_t *buff, rt_uint8_t size)
{rt_uint16_t sx_bytes = 0; //保存本页剩下的可写字节数sx_bytes = 256 - addr%256;   //得到本页剩下的可写字节数  if(size <= sx_bytes){sx_bytes = size;}while(1){w25qxx_page_write(addr,buff,sx_bytes);if(sx_bytes == size){break;         //写完了}addr += sx_bytes;   //256buff += sx_bytes;    //写完本页,数据地址偏移sx_bytessize -= sx_bytes;if( size <= 255)  //说明再一页可写完{sx_bytes = size;}else{sx_bytes = 256;}}
}

3.4 片选信号CS,并初始化W25QXX

//初始化SPI FLASH的IO口
void w25qxx_init(void)
{GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOB_CLK_ENABLE();                       //使能GPIOB时钟//PB14GPIO_Initure.Pin=FLASH_CS_PIN;                      //PB14GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;              //推挽输出GPIO_Initure.Pull=GPIO_PULLUP;                      //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;                 //快速HAL_GPIO_Init(FLASH_CS_GPIO_PORT,&GPIO_Initure);    //初始化SPI_FLASH_CS_HIGH();                                //SPI FLASH不选中spi1_init();                                        //初始化SPIrt_kprintf("w25q64_id = %#X\n",w25qxx_read_id());
}

Cortex-M4-SPI总线-读写W25Q64(二)相关推荐

  1. Xilinx-Spartan6-学习笔记(24):通过SPI总线读写FLASH

    Xilinx-Spartan6-学习笔记(24):通过SPI总线读写FLASH 利用SPI总线实现对FLASH进行读写,写入255个数据再读出255个数据.(这里为了模拟SDO信号,随机生成了0~1信 ...

  2. STM32系列(HAL库)——F103C8T6通过SPI方式读写W25Q64—(Flash存储模块)

    1.软件准备 (1)编程平台:Keil5 (2)CubeMX (3)XCOM(串口调试助手) 2.硬件准备 (1)W25Q64模块 (2)F1的板子,本例使用经典F103C8T6 (3)ST-link ...

  3. 学习STM32 Flash存储 W25Q64 SPI总线存储模块进行读写数据

    今天学习 Flash 存储芯片进行数据写入和读取方法.了解W25Q64 存储芯片的使用.能够用 STM32 单片机对 W25Q64 进行写入数据,擦除数据,读取数据. w25q64 是什么? W25Q ...

  4. linux内核SPI总线驱动分析(一)

    下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) SPI总线驱动分析   1 SPI概述     ...

  5. STM-32:SPI通信协议/W25Q64简介—软件SPI读写W25Q64

    目录 一.SPI简介 1.1电路模式 1.2通信原理 1.3SPI时序基本单元 1.3.1起始和终止 1.3.2交换字节 二.W25Q64 2.1W25Q64简介 2.2W25Q64硬件电路 2.3W ...

  6. LINUX SPI设备驱动模型分析之二 SPI总线模块分析

    上一篇文章我们简要介绍了SPI驱动模块,本章我们详细说明一下spi总线.设备.驱动模块的注册.注销以及这几个模块之间的关联. SPI总线的注册 spi模块也是基于LINUX设备-总线-驱动模型进行开发 ...

  7. 51单片机对SPI总线上挂接多个25AA040的读写操作+Proteus仿真

    51单片机对SPI总线上挂接多个X5045的读写操作+Proteus仿真 Proteus仿真图 实例代码 //对SPI总线上挂接多个25AA040的读写操作 #include<reg51.h&g ...

  8. Linux SPI总线和设备驱动架构之二:SPI通用接口层

    通过上一篇文章的介绍,我们知道,SPI通用接口层用于把具体SPI设备的协议驱动和SPI控制器驱动联接在一起,通用接口层除了为协议驱动和控制器驱动提供一系列的标准接口API,同时还为这些接口API定义了 ...

  9. STM32CubeMX+SPI+FATFS读写SD卡

    一.软件硬件说明 软件:STM32CubeMX V6.6.1 /KEIL5 V5.29 硬件:正点原子mini开发板,SD卡,通过SPI方式驱动SD卡,用的是SPI1接口 以上内容来源于正点原子min ...

  10. I2C与SPI总线对比

    最近2周一直在调试IIC和SPI总线设备,这里记录一下2种总线,以备后忘. 一 IIC总线 I2C--INTER-IC串行总线的缩写,是PHILIPS公司推出的芯片间串行传输总线.它以1根串行数据线( ...

最新文章

  1. linux Makefile编写的整理
  2. MySQL中如何插入数据(DML)
  3. (转)Virtual PC 2007虚拟网络设置
  4. (精)tomcat 源码学习
  5. 动态给H5页面绑定数据,基本万能无错误!
  6. 设计模式之建造者模式(Builder)
  7. 拷贝带隐藏目录的所有文件 到另外一个目录指令
  8. hid在linux上的轮训时间,linux 自定义hid速度优化
  9. Django中的富文本编辑器的使用
  10. 基于R语言GD包的Risk Map制作(批量生成)
  11. 用 reStructuredText 写作:快速入门指南
  12. 一刀工具箱 - 图片转链接(图床)工具
  13. 管家婆服务器怎么找文件夹,请问管家婆数据备份在哪里?如何恢复?
  14. 聊聊pert图的那些事儿~
  15. 要重复多少次变成潜意识_潜意识是如何控制你的行为,让你做事效率提高的?...
  16. [批处理]在线获取双色球开奖结果历史数据
  17. 《人性的弱点》-[美]戴尔·卡耐基
  18. U号租号平台技术服务支持
  19. 通向互联网未来的七个路标
  20. GD32报错Feature(s) : RDI, FlashBP, FlashDL, JFlash, GDB

热门文章

  1. 北京市密云区谷歌卫星地图下载
  2. iis打不开php,php出现404找不到网页错误 iis配置问题解决
  3. 【UV打印机】PrintExp打印软件教程(六)-高级模式(马达)
  4. 电信网速怎么测试软件,电信网速测试在线(中国电信自助测速app)
  5. WAP在线浏览器大全
  6. 在计算机上配置超级终端,电脑中如何添加超级终端?添加超级终端的方法
  7. 计算机硬件故障解决方法,计算机硬件故障分析与解决方法
  8. 免杀Payload生成工具Veil的下载与使用
  9. 关于onpropertychange与oninput的兼容问题
  10. 抱米花-豆丁文档下载器 20100529