SPI总线协议的W25Q64主要是用来存放更新的程序

SPI协议是一个四线制的协议,是全双工的,具体的解释可以看看这篇文章

发送流程

SPI数据通信的流程可以分为以下几步:

1、主设备发起信号,将CS/SS拉低,启动通信。

2、主设备通过发送时钟信号,来告诉从设备进行写数据或者读数据操作(采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低),因为SPI有四种模式,后面会讲到),它将立即读取数据线上的信号,这样就得到了一位数据(1bit)。

3、主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(缓存长度不一定,看单片机配置),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。

4、从机(Slave)也将自己的串行移位寄存器(缓存长度不一定,看单片机配置)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。

在数据帧的第一位发送之后,TBE(发送缓冲区空)位置1。TBE(transmit buffer empty)标志位置 1,说明发送缓冲区为空,此时如果需要发送更多数据,软件应该继续写SPI_DATA寄存器。

在主机模式下,若想要实现连续发送功能,那么在当前数据帧发送完成前,软件应该将下一个 数据写入SPI_DATA 寄存器中。
接收流程
在最后一个采样时钟边沿之后,接收到的数据将从移位寄存器存入到接收缓冲区,且 RBNE (接收缓冲区非空)位置1 。软件通过读 SPI_DATA 寄存器获得接收的数据,此操作会自动清除 RBNE 标志位。在MRU 和 MRB 模式中,为了接收下一个数据帧,硬件需要连续发送时钟信号,而在全 双工主机模式(MFD )中,当发送缓冲区非空时,硬件只接收下一个数据帧。
注意: 当 SPI 处于从机模式,输入的时钟周期数不是 8 或 16 (配置的位宽决定)的整数倍个,片选关闭,此时SPI 不会清除计数,片选使能后会再等相应数量的时钟周期后才收发新的数据。 可以通过软件主动复位SPI 模块来解决此问题。

NSS(CS)低电平有效

注意与IIC有区别IIC是高位在前低位在后的顺序发送数据,SPI是低位在前高位在后

(144条消息) 一文搞懂SPI通信协议_spi协议_不脱发的程序猿的博客-CSDN博客(144条消息) SPI协议详解(图文并茂+超详细)_小麦大叔的博客-CSDN博客

接下来就按照数据手册来连线W25Q64

spi.h

#ifndef SPI_H
#define SPI_H
#include <stdint.h>void SPI0_Init(void);
uint8_t SPI0_ReadWriteByte(uint8_t txd);
void SPI0_Write(uint8_t *wdata,uint16_t datalen);
void SPI0_Read(uint8_t *rdata,uint16_t datalen);#endif

spi.c

#include "gd32f10x.h"
#include "spi.h"void SPI0_Init(void)
{rcu_periph_clock_enable(RCU_GPIOA);        //开启GPIOB时钟                                                 rcu_periph_clock_enable(RCU_SPI0);  //开启SPI0时钟      gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_7|GPIO_PIN_5);  gpio_init(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_6);    spi_parameter_struct spi_parameter ={.device_mode                  = SPI_MASTER , // 主机模式.trans_mode                      = SPI_TRANSMODE_FULLDUPLEX ,// 全双工模式.frame_size                    = SPI_FRAMESIZE_8BIT , // 8位数据.nss                             = SPI_NSS_SOFT ,   //软件片选.endian                         = SPI_ENDIAN_MSB ,//配置字节序.clock_polarity_phase =    SPI_CK_PL_LOW_PH_1EDGE ,//空闲电平为低,第一个边沿上升沿采样.prescale                         = SPI_PSC_2 //时钟二分频};spi_i2s_deinit(SPI0); //复位SPI0spi_init(SPI0,&spi_parameter);//初始化SPI0spi_enable(SPI0);//使能SPI0
}uint8_t SPI0_ReadWriteByte(uint8_t txd)
{while(spi_i2s_flag_get(SPI0,SPI_FLAG_TBE) != 1); //等待TBE标志位置1说明发送寄存器为空可以发送spi_i2s_data_transmit(SPI0,txd);while(spi_i2s_flag_get(SPI0,SPI_FLAG_RBNE) != 1); //等待RBNE标志位置1说了接收寄存器不为空,可以接收return spi_i2s_data_receive(SPI0);
}void SPI0_Write(uint8_t *wdata,uint16_t datalen)
{uint16_t i;for(i=0;i<datalen;i++){SPI0_ReadWriteByte(wdata[i]);}
}void SPI0_Read(uint8_t *rdata,uint16_t datalen)
{uint16_t i;for(i=0;i<datalen;i++){rdata[i] = SPI0_ReadWriteByte(0xff);}
}

w25q64 :是先发送下一步的命令再执行,且收发数据之前需要检查是否忙

地址的计算:地址编号*大小(比如64K = 64*1024)

 

W25Q64.h

#ifndef W25Q64_H
#define W25Q64_H#include <stdint.h>#define CS_ENABLE          gpio_bit_reset(GPIOA,GPIO_PIN_4)
#define CS_DISENABLE  gpio_bit_set(GPIOA,GPIO_PIN_4)void W25Q64_Init(void);
void W25Q64_WaitBusy(void);
void W25Q64_WriteEnable(void);
void W25Q64_Erase64K(uint8_t BlockNum);
void W25Q64_PageWrite(uint8_t *wbuffer,uint16_t PageNum);
void W25Q64_Read(uint8_t *rbuffer,uint32_t addr,uint32_t datalen);#endif

W25Q64.c

#include "gd32f10x.h"
#include "spi.h"
#include "w25q64.h"void W25Q64_Init(void){rcu_periph_clock_enable(RCU_GPIOA);gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_4);CS_DISENABLE;SPI0_Init();
}void W25Q64_WaitBusy(void){uint8_t res;do{CS_ENABLE;SPI0_ReadWriteByte(0x05);res = SPI0_ReadWriteByte(0xff);//不关心发送什么,在意的是返回回来的值,因为是全双工发送一个数必须返回一个数回来,SPI是先发送命令让他知道下一个动作想干嘛CS_DISENABLE;}while((res&0x01)==0x01);
}void W25Q64_Enable(void){W25Q64_WaitBusy();CS_ENABLE;SPI0_ReadWriteByte(0x06);CS_DISENABLE;
}void W25Q64_Erase64K(uint8_t blockNB){uint8_t wdata[4];wdata[0] = 0xD8;wdata[1] = (blockNB*64*1024)>>16; //根据数据手册可知,先传命令然后传输地址wdata[2] = (blockNB*64*1024)>>8;wdata[3] = (blockNB*64*1024)>>0;W25Q64_WaitBusy();W25Q64_Enable();CS_ENABLE;SPI0_Write(wdata,4);CS_DISENABLE;W25Q64_WaitBusy();
}void W25Q64_PageWrite(uint8_t *wbuff, uint16_t pageNB){uint8_t wdata[4];wdata[0] = 0x02;wdata[1] = (pageNB*256)>>16;wdata[2] = (pageNB*256)>>8;wdata[3] = (pageNB*256)>>0;W25Q64_WaitBusy();W25Q64_Enable();CS_ENABLE;SPI0_Write(wdata,4);SPI0_Write(wbuff,256);CS_DISENABLE;
}void W25Q64_Read(uint8_t *rbuff, uint32_t addr, uint32_t datalen){uint8_t wdata[4];wdata[0] = 0x03;wdata[1] = (addr)>>16;wdata[2] = (addr)>>8;wdata[3] = (addr)>>0;W25Q64_WaitBusy();CS_ENABLE;SPI0_Write(wdata,4);SPI0_Read(rbuff,datalen);CS_DISENABLE;
}

main.c

测试代码每个页输出不同的值

#include "gd32f10x.h"
#include "usart.h"
#include "sys.h"
#include "iic.h"
#include "AT24C02.h"
#include "w25q64.h"
#include "spi.h"uint8_t wdata[256];
uint8_t rdata[256];int main(void){uint16_t i,j;Sys_Init();Usart0_Init(921600);W25Q64_Init();W25Q64_Erase64K(0);for(i=0;i<256;i++){for(j=0;j<256;j++) wdata[j] = i;W25Q64_PageWrite(wdata,i);}Ms_Delay(50);for(i=0;i<256;i++){W25Q64_Read(rdata,i*256,256);for(j=0;j<256;j++) u0_printf("地址%d=%x\r\n",i*256+j,rdata[j]);}while(1){}
}

[OTA-day3SPI]W25Q64擦写相关推荐

  1. 环境监测设备中HCL190FLAS擦写问题定位——BOOT

    笔者一直从事资产跟踪类设备的开发,近期笔者收到了关于环境监测的新项目,客户提出了关于环境监测设备中HCL190FLAS擦写问题定位--BOOT.笔者希望借此机会能够分享环境监测设备的应用以及关于客户的 ...

  2. linux下查看emmc读写次数,通过串口命令查看EMMC擦写次数的三大方法

    方法一(适用于大部分机芯): 在系统启动之后查看EMMC擦写次数,串口打印信息输入命令如下: tclsu (或者su) cat /sys/kernel/debug/mmc0/mmc0:0001/ext ...

  3. 【STM32】FLASH擦写+FLASH相关操作+注意事项

    有关FLASH读写的函数: 有关库函数: 实现掉电不丢失的主函数(Flash一定要先擦后写): #define FLASH_START_ADDR 0x0801f000 //写入的起始地址int mai ...

  4. NOR Flash擦写和原理分析 (二)

    Nor Flash上电后处于数据读取状态(Reading Array Data).此状态可以进行正常的读.这和读取SDRAM/SRAM/ROM一样.(要是不一样的话,芯片上电后如何从NorFlash中 ...

  5. flash不同颗粒SLC、MLC、TLC、QLC擦写次数

    flash有四种颗粒SLC.MLC.TLC.QLC. SLC质量最好也最贵,MLC.TLC.QLC依次变差变便宜. 下面是具体的介绍,来自网络. SLC(单层存储单元) 速度最快,寿命最长,价格最贵的 ...

  6. nand读寿命_Nand Flash的擦写次数与使用寿命

    Nand Flash的擦写次数与使用寿命 作者: 来源: 发布时间:2019-09-04 20:28:27   浏览:2086 Nand Flash因为其电气特性,读和写是按页来读取的,而擦除是按照块 ...

  7. spi flash擦写调试记录

    最近在调试spi flash的擦写,问题是flash擦写uboot时,不成功.原来的uboot丢失. 测试发现是因为擦能成功,但是写时的地址不是secent大小,一个secent大小为64K. 看da ...

  8. STM32系列内部Flash擦写程序

    stm32内部Flash擦写流程,对于FLash必须按页擦写,不同型号的flash页大小不同,需要根据实际修改 #define FLASH_PAGE_SIZE 2048 //定义Flash页大小,RC ...

  9. STM32擦写失败case

    利用HAL_FLASHEx_Erase(&FlashSet, &PageError)擦写时总是会进入到FLASH_WaitForLastOperation函数的while (__HAL ...

最新文章

  1. 生成对抗网络GAN综述
  2. #if...#endif的用法总结
  3. java scanner怎么用_Java Scanner delimiter()用法及代码示例
  4. Cpp / 空指针对象调用函数的不同结果
  5. 对话OTTVerse创始人Krishna Rao Vijayanagar:创业之初,挑战与机遇并存
  6. C语言用循环结构算平均值,C语言循环结构选择题().doc
  7. Qt中UDP通信的简单示例
  8. mount 安卓system只读_Android如何让system分区可读写(MTK安卓6.0)-阿里云开发者社区...
  9. pycharm在创建py文件时如何自动注释
  10. birt插件 web_birt 集成到现有的web应用中
  11. 大数据分析体系由哪些层级构成
  12. mysql数据传出_从MySQL读取数据,以List方式传出
  13. java语言的数据类型_Java语言的数据类型
  14. chrome 41 空格 nbsp;
  15. 如何做网站推广?如何提高网站浏览量?
  16. shopex PHP Notice,SHOPEX 4.8.5 最新注入漏洞后台拿SHELL
  17. destoon标签大集合
  18. 第十一届蓝桥杯c/c++省赛大学B组(第一次)
  19. 何恺明目前的学术成果是否够得上计算机视觉领域历史第一人?
  20. 并发编程合集(1)上下文切换详解、死锁及解决方案详解

热门文章

  1. b丅151组成的充电器电路_手机万能充电器电路原理与维修
  2. Js Event Loop
  3. 2021-04-30双螺杆挤出机与挤塑机之间有什么区别?
  4. MSTSC 自动登录
  5. SQLyog数据库列表(对象浏览器)字体大小设置方法,不是普通字体大小设置
  6. 重写 toFixed 方法,解决 四舍六入五成双 的问题
  7. 开源项目-超市订单管理系统
  8. cesium模型不遮挡点线面_cesium 学习(八) 基础绘制(点线面)
  9. int数据类型的取值范围是多少?怎么计算的?
  10. kali工具Crunch