基于STM32读取W25Q64(模拟SPI)
基于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)相关推荐
- 基于stm32单片机的模拟IIC时序(附源码)
我下面要说的是基于stm32单片机的模拟IIC时序,以及是一些要注意的事项:结合自己所做的MMA7455加速度传感器,我把模拟IIC的源代码贴了出来,大家可以参考一下. 1.因为在IIC协议中,当总线 ...
- stm32使用gpio模拟spi
本文介绍如何使用STM32标准外设库的GPIO端口模拟SPI,本例程使用PA5.PA6和PA7模拟一路SPI.SPI有4种工作模式,模拟SPI使用模式0,即空闲时SCK为低电平,在奇数边沿采样. 本文 ...
- 基于STM32的无线模拟病房呼叫系统
一. 系统设计和框图 本系统主要功能为模拟病房的无线呼叫系统,呼叫端(病床)为从机,被呼叫端(护士站)为主机,系统采用一主多从的通信方式,主机和从机之间采用无线通信方式. 主机端包含OLED显示模块( ...
- STM32普通IO模拟SPI和W25Q32通信调试
目录 1.参考网上的程序编写SPI读写函数 2.参考w25q32的例程读取芯片ID 3.读取芯片ID时遇到的问题 问题1:没有返回数据 问题2:返回芯片ID错误 4.优化后的收发函数和时序波形 1.参 ...
- io口模拟spi,stm32f103与MS5611基于spi总线的温度压力高度数据读取
以下文件为源文件 /** -----------------------MS5611驱动 && IO口模拟SPI驱动------------------------- ******** ...
- STM32外挂FLASH模拟U盘(基于HAL库)
STM32外挂FLASH模拟U盘(基于HAL库) 1.背景 1.1这篇文章能给你带来什么 1.2根据你要解决的问题,精确快速跳转到相应位置 1.3我在做完这个后还有不明白的地方,希望能有大触解答困惑 ...
- 用GPIO模拟SPI接口读取传感器数据
本文基于平头哥开发板RVB2601,简要介绍了用GPIO模拟SPI时序逻辑,实现SPI协议,按照特定温度传感器的时序,读取其数据,及示例程序 一.概述 SPI(Serial Peripheral In ...
- 关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结(fishing_8)
趁着帮老师代上嵌入式实验课的机会,又重新熟悉了一遍stm32的通信协议:串口协议.SPI协议.I2C协议.RS485协议.大概半年前,是过了一遍的,但也只停留于读了遍代码,跑了下例程,最近又过了一遍( ...
- 基于stm32的模拟楼道光控开关的实验
实验目的:本文基于stm32开发板做一个模拟光控开关的实验,实验最后的实现的功能是,在光照强度大于临界值LED灯就变暗,如果光照强度小于临界值LED灯就变亮. 文章目录 一.硬件设计 1.本实验所用到 ...
- STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片
STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片 STM32基础工程生成 首先使用STM32CUBEMX生成STM32的基础工程,配置时钟到7 ...
最新文章
- BP神经网络公式推导及实现(MNIST)
- python 获取 程序运行时间
- 使用基于注解的mybatis时,利用反射和注解生成sql语句
- XV6850成功刷机步骤
- 【effective c++】继承与面向对象设计
- Boot loader: Grub入门(转)
- mysql 动态索引_MySQL的索引
- Android中文语音合成(TTS)各家引擎对比 .
- java 高德地图数据库_【爬虫】Java关于高德地图爬取数据
- 高可用的Redis主从复制集群,从理论到实践
- 玩转PHP关联数组的10个技巧(3)
- ORACLE数据库测试题(二)
- MySql 报错:In aggregated query without GROUP BY, expression #1 of SELECT list contains....
- 微信小程序-电影app程序遇到得问题
- uniapp使用picker
- 想和大家讲述一个普通人的生活:现在差劲不可怕,只要最后是好的就行
- GET 和 POST请求的本质区别
- Android APK打包流程
- 十万万个为什么|二维码支付为啥要限额?
- vip混合测试v号打卡好的卡仕达看哈看收到货卡仕达库哈斯
热门文章
- matlab电磁场 有限元,电磁场有限元Matlab解法.pdf
- 着迷英语900句_字体令人着迷
- 2022-2028年全球与中国基于汽车摄像头的ADAS行业深度分析
- 常用计算机维修方法有哪些,计算机常见硬件故障的诊断及其处理分析
- onpropertychange oninput兼容性
- u盘装杀毒软件给计算机杀毒,安装在U盘的杀毒软件时刻护卫U盘文件安全
- 树莓派挂载硬盘/U盘以及分区教程
- 测试家里网速用什么软件,怎么测试网速,测试网速用什么软件
- 下载官方Intel的Windows 10网卡驱动
- Android Studio查看MD5