基于STM32读取W25Q64(模拟SPI)

  • 1、读取8位 Device ID
  • 2、读取MANUFACTURER ID和16位Device ID
  • 3、扇区擦除
    • a、写使能
    • b、等待BUSY标志位置0
  • 4、写入数据
    • a、页写入数据
    • b、任意位置写入任意大小数据(不超过地址范围)
  • 5、读取数据
  • 6、主函数

如有错误,希望能够提出
关于模拟SPI的介绍详见博主另外一篇博客

1、读取8位 Device ID

/*读取设备ID*/
void Read_Device_ID(void)
{u8 ID;SPI_CS_0();Software_SPI_Write_Read(Device_ID);Software_SPI_Write_Read(Dummy_Bytes);Software_SPI_Write_Read(Dummy_Bytes);Software_SPI_Write_Read(Dummy_Bytes);ID = Software_SPI_Write_Read(Dummy_Bytes);SPI_CS_1();printf("Device_ID:%x\r\n",ID);
}

=SPI在读取数据时,为什么我们必须发送虚拟字节Dummy_Bytes才能接收结果?=
SPI必须生成时钟脉冲才能将数据移出。对于大多数(如果不是全部)SPI主机,产生时钟脉冲的唯一方式是发送字节。如果你仔细想想,这是有道理的。
总结:Dummy_Bytes无实际意义,只是为了产生时钟脉冲,这样才能读取数据。

2、读取MANUFACTURER ID和16位Device ID

/*读取制造商ID*/
void Read_JEDEC_ID(void)
{u32 Temp;u8 Temp0,Temp1,Temp2;SPI_CS_0();Software_SPI_Write_Read(JEDEC_ID);Temp0 = Software_SPI_Write_Read(Dummy_Bytes);Temp1 = Software_SPI_Write_Read(Dummy_Bytes);Temp2 = Software_SPI_Write_Read(Dummy_Bytes);SPI_CS_1();/*把数据组合起来,作为函数的返回值*/Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;printf("JEDEC_ID:%x\r\n",Temp);
}

读取成功

3、扇区擦除

a、写使能

在扇区擦除之前要进行写使能

/**写使能
*/
void FLASH_WriteEnable(void)
{SPI_CS_0();Software_SPI_Write_Read(Write_Enable);SPI_CS_1();
}

b、等待BUSY标志位置0

BUSY是状态寄存器(S0)中的只读位,当器件执行页面编程、扇区擦除、挡路擦除、芯片擦除或写入状态寄存器指令时,该位设置为1状态。在此期间,器件将忽略除读取状态寄存器和擦除挂起指令之外的其他指令(参见交流特性中的TW、TPP、Tse、TBE和Tce)。当编程、擦除或写入状态寄存器指令完成时,BUSY位将被清除为0状态,表示器件已准备好接受进一步的指令。

开始扇区擦除

为什么要进行数据擦除:因为Flash芯片内的数据只能由1变0,不能由0变1
扇区擦除指令将一个扇区(4K字节)擦除,擦除后扇区位都为1,在执行扇区擦除指令之前需要先执行写使能指令,保证WEL位为1。WEL位是一个只读位,在状态寄存器的S1位。在执行完“写使能”指令后,该位会被硬件自动置1。当芯片掉电后和执行“写禁能”、“页编程”、“扇区擦除”、“块区擦除”以及“芯片擦除”指令都会进入“写保护状态0”。

 /*** @brief  擦除FLASH扇区* @param  SectorAddr:要擦除的扇区地址* @retval 无*/
void FLASH_SectorErase(u32 SectorAddr)
{FLASH_WriteEnable();//写使能SPI_FLASH_WaitForWriteEnd();//等待其他操作完成SPI_CS_0();Software_SPI_Write_Read(Sector_Erase);//发送擦除指令Software_SPI_Write_Read((SectorAddr&0XFF0000)>>16);Software_SPI_Write_Read((SectorAddr&0X00FF00)>>8);Software_SPI_Write_Read((SectorAddr&0X0000FF)>>0);SPI_CS_1();//开始擦除SPI_FLASH_WaitForWriteEnd();//等待擦除完毕printf("擦除完毕\r\n");
}

擦除成功

4、写入数据

a、页写入数据

/** @brief 页写入数据,调用本函数写入数据前必须先擦除扇区 * @para  pBuffer 待写入数据的指针* @para  WriteAddr 写入地址* @para  NumByteToWrite 数据长度,必须小于SPI_FLASH_PerWritePageSize
*/
void SPI_FLASH_PageWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{FLASH_WriteEnable();//在写数据之前一定要进行写使能,不然会写入失败SPI_CS_0();Software_SPI_Write_Read(Page_Program);//发送页写指令Software_SPI_Write_Read((WriteAddr&0XFF0000)>>16);Software_SPI_Write_Read((WriteAddr&0x00FF00)>>8);Software_SPI_Write_Read((WriteAddr&0x0000FF)>>0);if(NumByteToWrite>SPI_FLASH_PerWritePageSize){NumByteToWrite = SPI_FLASH_PerWritePageSize;printf("超出页写最大范围\r\n");}while(NumByteToWrite--){Software_SPI_Write_Read(*pBuffer);pBuffer++;}SPI_CS_1();SPI_FLASH_WaitForWriteEnd();//等待写入完毕
}

页面编程指令允许从1字节到256字节(一页)的数据进行编程
在写数据之前一定要进行写使能,不然会写入失败,博主之前研究半天写入失败结果发现是没有写使能

b、任意位置写入任意大小数据(不超过地址范围)

 /*** @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区* @param  pBuffer,要写入数据的指针* @param  WriteAddr,写入地址* @param  NumByteToWrite,写入数据长度* @retval 无*/
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;/*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/Addr = WriteAddr % SPI_FLASH_PageSize;/*差count个数据值,刚好可以对齐到页地址*/count = SPI_FLASH_PageSize - Addr;/*计算出要写多少整数页*/NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;/*mod运算求余,计算出剩余不满一页的字节数*/NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */if (Addr == 0){/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0) {SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}else /* NumByteToWrite > SPI_FLASH_PageSize */{ /*先把整数页都写了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr +=  SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不满一页的数据,把它写完*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}/* 若地址与 SPI_FLASH_PageSize 不对齐  */else {/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0){/*当前页剩余的count个位置比NumOfSingle小,一页写不完*/if (NumOfSingle > count) {temp = NumOfSingle - count;/*先写满当前页*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);WriteAddr +=  count;pBuffer += count;/*再写剩余的数据*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);}else /*当前页剩余的count个位置能写完NumOfSingle个数据*/{SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}}else /* NumByteToWrite > SPI_FLASH_PageSize */{/*地址不对齐多出的count分开处理,不加入这个运算*/NumByteToWrite -= count;NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;/* 先写完count个数据,为的是让下一次要写的地址对齐 */SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);/* 接下来就重复地址对齐的情况 */WriteAddr +=  count;pBuffer += count;/*把整数页都写了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr +=  SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不满一页的数据,把它写完*/if (NumOfSingle != 0){SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}}
}

5、读取数据

 /*** @brief   读取FLASH数据* @param    pBuffer,存储读出数据的指针* @param   ReadAddr,读取地址* @param   NumByteToRead,读取数据长度* @retval  无*/
void Flash_Read(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{SPI_CS_0();//读指令Software_SPI_Write_Read(Read_Data);//读地址Software_SPI_Write_Read((ReadAddr&0XFF0000)>>16);Software_SPI_Write_Read((ReadAddr&0x00FF00)>>8);Software_SPI_Write_Read((ReadAddr&0x0000FF)>>0);//读取数据while(NumByteToRead--){*pBuffer = Software_SPI_Write_Read(Dummy_Bytes);pBuffer++;}SPI_CS_1();
}

6、主函数

float data[] = {0.11,1.12,2.23,3.34,4.45,5.55};
float readdata[6]={0};int   num[] = {1,2,3,4,5,6};
int   rnum[6] = {0};
/*** @brief  主函数* @param  无  * @retval 无*/
int main(void)
{   u8 flag=0xcc,i=0;u8 rflag;USART_Config();Software_SPI_Init();Read_Device_ID();SysTick_Delay_Us(10);Read_JEDEC_ID();while(1){Flash_Read(&rflag,SPI_FLASH_PageSize*0,1);if(rflag==0xcc)//有数据{printf("\r\n有数据,进行读取\r\n");Flash_Read((void*)readdata,SPI_FLASH_PageSize*1,sizeof(data));Flash_Read((void*)rnum,SPI_FLASH_PageSize*2,sizeof(num));for(i=0;i<6;i++){printf("%f\r\n%d\r\n",readdata[i],rnum[i]);}printf("读取完成,进行擦除,请复位重新写入\r\n");FLASH_SectorErase(0);while(1);}else{printf("无数据 开始写入\r\n");SPI_FLASH_BufferWrite(&flag,SPI_FLASH_PageSize*0,1);   SPI_FLASH_BufferWrite((void*)data,SPI_FLASH_PageSize*1,sizeof(data));//小数SPI_FLASH_BufferWrite((void*)num,SPI_FLASH_PageSize*2,sizeof(num));//整数printf("写入完成,请复位读取\r\n");while(1);}}}

效果演示


本文参考了一下两篇文章,感谢
关于flahs存取小数问题
W25Q64说明手册
工程下载

基于STM32读取W25Q64(模拟SPI)相关推荐

  1. 基于stm32单片机的模拟IIC时序(附源码)

    我下面要说的是基于stm32单片机的模拟IIC时序,以及是一些要注意的事项:结合自己所做的MMA7455加速度传感器,我把模拟IIC的源代码贴了出来,大家可以参考一下. 1.因为在IIC协议中,当总线 ...

  2. stm32使用gpio模拟spi

    本文介绍如何使用STM32标准外设库的GPIO端口模拟SPI,本例程使用PA5.PA6和PA7模拟一路SPI.SPI有4种工作模式,模拟SPI使用模式0,即空闲时SCK为低电平,在奇数边沿采样. 本文 ...

  3. 基于STM32的无线模拟病房呼叫系统

    一. 系统设计和框图 本系统主要功能为模拟病房的无线呼叫系统,呼叫端(病床)为从机,被呼叫端(护士站)为主机,系统采用一主多从的通信方式,主机和从机之间采用无线通信方式. 主机端包含OLED显示模块( ...

  4. STM32普通IO模拟SPI和W25Q32通信调试

    目录 1.参考网上的程序编写SPI读写函数 2.参考w25q32的例程读取芯片ID 3.读取芯片ID时遇到的问题 问题1:没有返回数据 问题2:返回芯片ID错误 4.优化后的收发函数和时序波形 1.参 ...

  5. io口模拟spi,stm32f103与MS5611基于spi总线的温度压力高度数据读取

    以下文件为源文件 /** -----------------------MS5611驱动 && IO口模拟SPI驱动------------------------- ******** ...

  6. STM32外挂FLASH模拟U盘(基于HAL库)

    STM32外挂FLASH模拟U盘(基于HAL库) 1.背景 1.1这篇文章能给你带来什么 1.2根据你要解决的问题,精确快速跳转到相应位置 1.3我在做完这个后还有不明白的地方,希望能有大触解答困惑 ...

  7. 用GPIO模拟SPI接口读取传感器数据

    本文基于平头哥开发板RVB2601,简要介绍了用GPIO模拟SPI时序逻辑,实现SPI协议,按照特定温度传感器的时序,读取其数据,及示例程序 一.概述 SPI(Serial Peripheral In ...

  8. 关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结(fishing_8)

    趁着帮老师代上嵌入式实验课的机会,又重新熟悉了一遍stm32的通信协议:串口协议.SPI协议.I2C协议.RS485协议.大概半年前,是过了一遍的,但也只停留于读了遍代码,跑了下例程,最近又过了一遍( ...

  9. 基于stm32的模拟楼道光控开关的实验

    实验目的:本文基于stm32开发板做一个模拟光控开关的实验,实验最后的实现的功能是,在光照强度大于临界值LED灯就变暗,如果光照强度小于临界值LED灯就变亮. 文章目录 一.硬件设计 1.本实验所用到 ...

  10. STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片

    STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片 STM32基础工程生成 首先使用STM32CUBEMX生成STM32的基础工程,配置时钟到7 ...

最新文章

  1. BP神经网络公式推导及实现(MNIST)
  2. python 获取 程序运行时间
  3. 使用基于注解的mybatis时,利用反射和注解生成sql语句
  4. XV6850成功刷机步骤
  5. 【effective c++】继承与面向对象设计
  6. Boot loader: Grub入门(转)
  7. mysql 动态索引_MySQL的索引
  8. Android中文语音合成(TTS)各家引擎对比 .
  9. java 高德地图数据库_【爬虫】Java关于高德地图爬取数据
  10. 高可用的Redis主从复制集群,从理论到实践
  11. 玩转PHP关联数组的10个技巧(3)
  12. ORACLE数据库测试题(二)
  13. MySql 报错:In aggregated query without GROUP BY, expression #1 of SELECT list contains....
  14. 微信小程序-电影app程序遇到得问题
  15. uniapp使用picker
  16. 想和大家讲述一个普通人的生活:现在差劲不可怕,只要最后是好的就行
  17. GET 和 POST请求的本质区别
  18. Android APK打包流程
  19. 十万万个为什么|二维码支付为啥要限额?
  20. vip混合测试v号打卡好的卡仕达看哈看收到货卡仕达库哈斯

热门文章

  1. matlab电磁场 有限元,电磁场有限元Matlab解法.pdf
  2. 着迷英语900句_字体令人着迷
  3. 2022-2028年全球与中国基于汽车摄像头的ADAS行业深度分析
  4. 常用计算机维修方法有哪些,计算机常见硬件故障的诊断及其处理分析
  5. onpropertychange oninput兼容性
  6. u盘装杀毒软件给计算机杀毒,安装在U盘的杀毒软件时刻护卫U盘文件安全
  7. 树莓派挂载硬盘/U盘以及分区教程
  8. 测试家里网速用什么软件,怎么测试网速,测试网速用什么软件
  9. 下载官方Intel的Windows 10网卡驱动
  10. Android Studio查看MD5