当前位置: 首页 > news >正文

STM32学习笔记:驱动SPI外设读写FLASH

目录

1、SPI驱动FLASH芯片:思路

2、SPI发送数据和接收数据函数

3、查看SPI传输状态

4、FLASH芯片简介

5、代码

5.1 初始化SPI

5.2 使用SPI发送一个字节,返回收到的数据

5.3 从FLASH指定地址读出指定长度的数据

5.4 从FLASH指定地址写入指定长度的数据

5.5 通过SPI外设给FLASH写数——主函数


1、SPI驱动FLASH芯片:思路

(1)驱动GPIO及端口的时钟
(2)是能SPI外设的时钟
(3)配置SPI外设的模式、地址、速率等参数并使能SPI外设
(4)编写基本SPI按字节收发的函数
(5)编写对FLASH擦除及读写操作的函数

(6)编写main()函数

2、SPI发送数据和接收数据函数

void SPI_I2S_SendData(SPI_TypeDef* SPIx,u16 Data)
u16 SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)

3、查看SPI传输状态

/* 等待发送区为空,TXE */
/* 等待接收缓存区非空,RXNE*/

SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)

4、FLASH芯片简介

W25Q64:将8M容量分为128个块(Block),
每个块64K字节,每个块分为16个扇区(Sector),每个扇区4K字节
W25Q64的最小擦除单位为一个扇区,每次必须擦除4K个字节
因此需要给W25Q24开辟一个4K缓存区;
W25Q64:四线SPI,最大时钟80MHz。

5、代码

5.1 初始化SPI

void SPI_Flash_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI1, ENABLE );
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA5,6,7 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化串口A
    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);//初始化GPIO
    
    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
    SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
    SPI_Init(SPI1, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器
    
    SPI_Cmd(SPI1,ENABLE); //使能SPI外设
    
    SPI1_ReadWriteByte(0xff);
}

5.2 使用SPI发送一个字节,返回收到的数据

/* TxData:发送的数  */

/* u8返回值:读取到的字节 */

u8 SPI1_ReadWriteByte(u8 TxData)
{
	u8 retry=0;
	/* 发送缓存为空 */
	while(SPI_I2S_GetFlagStatus(SPI,SPI_I2S_FLAG_TXE)==RESET)
	{
		retry++;
		if(retry>200)return0;
	};
	SPI_I2S_SendData(SPI1,TxData);//通过外设SPI发送一个数据
	retry=0;
	
	/* 接收缓存为非空 */
	while(SPI_I2S_GetFlagStatus(SPI,SPI_I2S_FLAG_RXNE)==RESET)
	{
		retry++;
		if(retry>200)return0;
	};
	return SPI1_ReceiveData(SPI1);//返回SPI1收到的数据	
}

5.3 从FLASH指定地址读出指定长度的数据

/* 读取指定长度的数据*/
/* pbuffer:数据存储区*/
/* ReadAddr:开始读取的地址(24bit)*/
/* NumByteToRead:要读取的字节数 (最大65535)*/

void SPI_Flash_Read(u8* pbuffer,u32 ReadAddr,u16 NumByteToRead)
{
	u16 i;
	SPI1_FLASH_CS = 0; //开始CS使能
	SPI1_ReadWriteByte(W25X_ReadData);//发送读取命令
	SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //发送 24bit 地址 
	SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
	SPI1_ReadWriteByte((u8)ReadAddr);
	for(i=0;i<NumByteToRead;i++)
	{
		pbuffer[i] = SPI1_ReadWriteByte(0XFF);
	}
	SPI1_FLASH_CS = 1; //取消CS使能
}

5.4 从FLASH指定地址写入指定长度的数据

void SPI_Flash_Write(u8* pbuffer,u32 WriteAddr,u16 NumByteToRead)
{
	u8 SPI_FLASH_BUFFER[4096];
	u32 secpos; u16 secoff;
	u16 secremain; u16 i; 
	secpos=WriteAddr/4096; //扇区地址
	secoff=WriteAddr%4096; //在扇区内的偏移
	secremain=4096-secoff; //扇区剩余空间大小
	
	if(NumBytetoWrite<=secremain)
	{
		secremain = NumBytetoWrite;
	}
	while(1) 
	{
		SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除 
		}
		if(i<secremain)//需要擦除
		{
			SPI_Flash_Erase_Sector(secpos);//擦除这个扇区
			for(i=0;i<secremain;i++) SPI_FLASH_BUF[i+secoff]=pBuffer[i]; //复制
			SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//写整个扇区 
		}else SPI_Flash_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的

		if(NumByteToWrite==secremain)break;//写入结束了

		else//写入未结束
		{
			secpos++; //扇区地址增 1
			secoff=0; //偏移位置为 0 
			 pBuffer+=secremain; //指针偏移
			WriteAddr+=secremain; //写地址偏移 
			 NumByteToWrite-=secremain; //字节数递减
			if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
			else secremain=NumByteToWrite; //下一个扇区可以写完了
		}
	};
}

5.5 通过SPI外设给FLASH写数——主函数

int main(void)
{
	const u8 TEXT_Buffer[] = {"WarShipSTM32 SPI TEST"};
	u8 key;
	u16 i=0;
	u8 datatemp[size];
	u32 FLASH_SIZE;
	delay_init();
	SPI_Flash_Init();
	while(1)
	{
		if(key == WKUP_PRES) //WK_UP按键按下,写入FLASH
		{
			SPI_Flash_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
		}
		if(key == KEY0_PRES) //key0按下,读取字符串并显示
		{
			SPI_Flash_Read(datatemp,FLASH_SIZE-100,SIZE);//从指定地址读 SIZE 字节
		}
		i++;
		delay_ms(10);
	}
	
}

相关文章:

  • 操作系统安全 基本概念
  • 猿创征文——C++|string类2
  • 【51单片机】认识单片机
  • Windows中执行C语言编译的程序乱码的解决方法
  • 商城项目10_JSR303常用注解、在项目中如何使用、统一处理异常、分组校验功能、自定义校验注解
  • 一天时间迅速准备前端面试|JS基础—原型和原型链【三座大山之一,必考】
  • Spring Security详细讲解(JWT+SpringSecurity登入案例)
  • 【Network】网络基础@应用层 —— 协议 | http | https
  • UGUI学习笔记(九)自制3D轮播图
  • R统计-单因素ANOVA/Kruskal-Wallis置换检验
  • 动态开点线段树(C++实现)
  • pytorch保存和加载模型权重以及CUDA在pytorch中的使用
  • UDF提权(mysql)
  • linux内核漏洞(CVE-2022-0847)
  • kubekey 离线部署 kubesphere v3.3.0
  • JavaScript-如何实现克隆(clone)函数
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • docker python 配置
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • HTML-表单
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Javascript编码规范
  • Python连接Oracle
  • SwizzleMethod 黑魔法
  • Vue.js源码(2):初探List Rendering
  • vue--为什么data属性必须是一个函数
  • 笨办法学C 练习34:动态数组
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 利用DataURL技术在网页上显示图片
  • 前端路由实现-history
  • 前端性能优化——回流与重绘
  • 项目管理碎碎念系列之一:干系人管理
  • 学习JavaScript数据结构与算法 — 树
  • 再谈express与koa的对比
  • ​io --- 处理流的核心工具​
  • ​你们这样子,耽误我的工作进度怎么办?
  • # 计算机视觉入门
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • (2)nginx 安装、启停
  • (zt)最盛行的警世狂言(爆笑)
  • (初研) Sentence-embedding fine-tune notebook
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (接口封装)
  • (七)理解angular中的module和injector,即依赖注入
  • (四)汇编语言——简单程序
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转)大型网站架构演变和知识体系
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • .form文件_SSM框架文件上传篇
  • .NET Core引入性能分析引导优化