SPI-读写串行FLASH

news2024/12/26 0:45:59

简介

是由摩托罗拉公司提出的通讯协议,即串行外围设备接口,是一种高速全双工的通信总线。它被广
泛地使用在 ADC LCD 等设备与 MCU 间,要求通讯速率较高的场合。

特性

1、全双工(即可以同时收发)
2、最少需要占用4条线:
  •   SS:从设备选择信号线,常称为片选信号线,也称为NSS、CS
  •   SCK(Serial Clock):时钟信号线,用于通讯数据同步
  •   MOSI (Master Output Slave Input) : 主设备输出/ 从设备输入引脚。
  •   MISO(Master Input, Slave Output) : 主设备输入/ 从设备输出引脚。

3、 多从机只需要增加SS片选信号线

4、速率高,最高频率可达到fplck/2,受限于低速设备(例如STM32F407的APB2总线最高可达42MHz)

通讯过程

  •  NSS(片选信号线)由高变低,是SPI的起始信号
  • 触发:是数据在交换位,此时数据无线
  • 采样:是数据有效,读取数据采样
  • NSS线又低变高,意味着SPI通讯结束
  • MOSI和MISO是同步的,每发送一位就可以接收一位

采样模式

 

 通过切换时钟极性(CPOL)和时钟相位(CPHA)可以更改SPI的采样模式

CPOL = 0 :SCK起始信号为低电平

CPOL = 1 :SCK起始信号为高电平

CPHA = 0 :对奇数边缘采样

CPHA = 1 :对偶数边缘采样

 一般常用的是模式0和模式3,例如flash的W25Q128只支持模式0和模式3

详细通讯过程

模式3

 对flash发送数据需要等待TXE发送寄存器为reset,接收则需要等待RXNE接收非空寄存器reset。

下面是对flash写入和接收1字节数据的函数代码

/* ------------------SPI对flash写入1字节数据----------------------- */
// data   :要写入flash的数据
uint32_t SPI_WriteByte(uint8_t data)
{
	//等待事件响应
	TimeOut_count = SPI_time_out;
	while (SPI_I2S_GetFlagStatus(SPI_FLASH,SPI_I2S_FLAG_TXE) == RESET)
	{
		if ((TimeOut_count--) == 0)			return SPI_timeout_callback(0);
	}

	//发送要写入的数据
	SPI_I2S_SendData(SPI_FLASH, data);

	//等待事件响应
	TimeOut_count = SPI_time_out;
	while (SPI_I2S_GetFlagStatus(SPI_FLASH,SPI_I2S_FLAG_RXNE) == RESET)
	{
		if ((TimeOut_count--) == 0)			return SPI_timeout_callback(1);
	}

	//接受返回的数据
	return SPI_I2S_ReceiveData(SPI_FLASH);
}

发送和接收都是这个函数,因为SPI是全双工的,在发送1个字节的同时就会返回1个字节的数据

代码编写过程

对SPI在总线上查找

 

 查找spi1对应引脚,对应开发板硬件原理图,我的开发板是STMF407

 

根据开发板原理图

cs片选引脚:PG6

SCK:PB3      

MISO:PB4  

MOSI:PB5

根据引脚可以编写对应的SPI头文件宏

/*SPI引脚参数定义*/
#define SPI_FLASH                           SPI1
#define SPI_FLASH_CLK                       RCC_APB2Periph_SPI1
#define SPI_FLASH_INIT									    RCC_APB2PeriphClockCmd
/*SCK引脚*/
#define SPI_FLASH_SCK_PIN                  GPIO_Pin_3                 
#define SPI_FLASH_SCK_GPIO_PORT            GPIOB                       
#define SPI_FLASH_SCK_GPIO_CLK             RCC_AHB1Periph_GPIOB
#define SPI_FLASH_SCK_SOURCE               GPIO_PinSource3
#define SPI_FLASH_SCK_AF                   GPIO_AF_SPI1
/*MISO引脚*/
#define SPI_FLASH_MISO_PIN                  GPIO_Pin_4                 
#define SPI_FLASH_MISO_GPIO_PORT            GPIOB                       
#define SPI_FLASH_MISO_GPIO_CLK             RCC_AHB1Periph_GPIOB
#define SPI_FLASH_MISO_SOURCE               GPIO_PinSource4
#define SPI_FLASH_MISO_AF                   GPIO_AF_SPI1
/*MOSI引脚*/
#define SPI_FLASH_MOSI_PIN                  GPIO_Pin_5                 
#define SPI_FLASH_MOSI_GPIO_PORT            GPIOB                       
#define SPI_FLASH_MOSI_GPIO_CLK             RCC_AHB1Periph_GPIOB
#define SPI_FLASH_MOSI_SOURCE               GPIO_PinSource5
#define SPI_FLASH_MOSI_AF                   GPIO_AF_SPI1
/*CS引脚*/
#define SPI_FLASH_CS_PIN                	  GPIO_Pin_6                 
#define SPI_FLASH_CS_GPIO_PORT          	  GPIOG                       
#define SPI_FLASH_CS_GPIO_CLK           	  RCC_AHB1Periph_GPIOG
/*拉高拉低CS引脚*/
#define SPI_FLASH_CS_LOW()									GPIO_ResetBits(SPI_FLASH_CS_GPIO_PORT,SPI_FLASH_CS_PIN)
#define SPI_FLASH_CS_HIGH()									GPIO_SetBits(SPI_FLASH_CS_GPIO_PORT,SPI_FLASH_CS_PIN)	

 功能函数

对应flash W25Q128数据手册,编写对应的功能函数

根据对应的功能写出对应的宏增加代码可读性

上图的DUMMY是无效数据就用0xFF

#define DUMMY							0xFF

/*命令定义-开头*******************************/
#define W25X_WriteEnable		      0x06 
#define W25X_WriteDisable		      0x04 
#define W25X_ReadStatusReg		      0x05 
#define W25X_WriteStatusReg		      0x01 
#define W25X_ReadData			      0x03 
#define W25X_FastReadData		      0x0B 
#define W25X_FastReadDual		      0x3B 
#define W25X_PageProgram		      0x02 
#define W25X_BlockErase			      0xD8 
#define W25X_SectorErase		      0x20 
#define W25X_ChipErase			      0xC7 
#define W25X_PowerDown			      0xB9 
#define W25X_ReleasePowerDown	      0xAB 
#define W25X_DeviceID			      0xAB 
#define W25X_ManufactDeviceID         0x90 
#define W25X_JedecDeviceID		      0x9F 

下面是函数介绍

写使能功能函数

/*
	写使能函数
*/
void	SPI_FLASH_WriteEnable(void)
{
		SPI_FLASH_CS_LOW();
	
		SPI_WriteByte(W25X_WriteEnable);

		SPI_FLASH_CS_HIGH();
}

等待写完毕状态函数 

/*
	等待BUSY位为0,即等待Flash内部数据写入完毕
*/
void SPI_FLASH_WaitForWriteEnd(void)
{
	uint8_t flash_status = 0;
	
	SPI_FLASH_CS_LOW();
	
	SPI_WriteByte(W25X_ReadStatusReg);
	TimeOut_count = SPI_time_out;
	
	do
	{
		flash_status = SPI_WriteByte(DUMMY);
		
		if((TimeOut_count--) == 0)
		{
				SPI_timeout_callback(2);
				break;
		}
	}	while((flash_status & 0x01) == SET);
	
	SPI_FLASH_CS_HIGH();
}

扇区擦除功能函数(Sector_Erase)

/*
	扇区擦除函数
	addr:要擦除的扇区
*/
void	Sector_Erase(uint32_t addr)
{
		SPI_FLASH_WriteEnable();
		SPI_FLASH_WaitForWriteEnd();
		SPI_FLASH_CS_LOW();
	
		SPI_WriteByte(W25X_SectorErase);
		SPI_WriteByte((addr>>16) & 0xFF);
		SPI_WriteByte((addr>>8) & 0xFF);
		SPI_WriteByte(addr & 0xFF);
	
		SPI_FLASH_CS_HIGH();
		SPI_FLASH_WaitForWriteEnd();
}

页读取功能函数(Page_write)

/*
	写一页flash数据
	addr:要写入的地址起始
	buff:写入的的暂存缓冲区
	size:写的字节数  page一定要在256以内
*/
void Page_write(uint32_t addr,uint8_t *buff,uint32_t size)
{
		SPI_FLASH_WriteEnable();
		SPI_FLASH_WaitForWriteEnd();
		SPI_FLASH_CS_LOW();
	
		SPI_WriteByte(W25X_PageProgram);
		SPI_WriteByte((addr>>16) & 0xFF);
		SPI_WriteByte((addr>>8) & 0xFF);
		SPI_WriteByte(addr & 0xFF);
	
		while(size--)
		{
			SPI_WriteByte(*buff);
			buff++;
		}
			
		SPI_FLASH_CS_HIGH();
		SPI_FLASH_WaitForWriteEnd();
}

读取Flash_ID函数

/*
	发送0xAB读取flashID
*/
uint8_t Read_flash_ID(void)
{
	uint8_t id;
	//拉低CS片选引脚
	SPI_FLASH_CS_LOW();
	
	//写指令
	SPI_WriteByte(W25X_ReleasePowerDown);
	SPI_WriteByte(DUMMY);
	SPI_WriteByte(DUMMY);
	SPI_WriteByte(DUMMY);
	//读指令
	id = SPI_WriteByte(DUMMY);
	//拉高CS片选引脚   传输结束
	SPI_FLASH_CS_HIGH();
	
	return id;
}

大量数据写入函数(不限制与page页大小)

基于Page_write函数做了逻辑处理

/*
	写flash数据
	addr:要写入的地址起始
	buff:写入的的暂存缓冲区
	size:写的字节数  
*/
void Buffer_write(uint32_t addr,uint8_t *buff,uint32_t size)
{
	u8 num_signgle , num_page , count ,temp;
	
	num_signgle = addr % 256;			//求出首地址是否对齐
	count = 256 - num_signgle;		//首页剩余要写的字节
	num_page = size / 256;				//若对齐的页数
	
	temp = size % 256;						//如果对齐的话最后一页剩余要补的字节
	
	if(num_signgle != 0)		//首页没对齐的情况
	{
		num_page = (size - count)/256;	//重新算出没对齐后的页数
		if(num_page == 0)			
		{
			if(size > count)		//虽然是0页但也可能存在尾部跨页数的存在
			{
				Page_write(addr , buff , count);
				addr += count;
				buff += count;
				
				Page_write(addr , buff , (size-count));
			}
			else								//没跨页数
			{
				Page_write(addr , buff , size);
			}
		}
		else		//没对齐且不止一页
		{
				Page_write(addr , buff , count);		//补齐首页
				addr += count;
				buff += count;
			
				while(num_page--)										//写中间完整页
				{
					Page_write(addr , buff , 256);
					addr += 256;
					buff += 256;
				}
				
				temp = (size-count)%256;						//若有剩余补尾页页
				if(temp != 0)
				{
					Page_write(addr , buff , temp);
				}
		}
	}
	else
	{
		if(num_page == 0)						//对齐0页直接写
		{
			Page_write(addr , buff , size);
		}
		else
		{
			while(num_page--)					//对齐直接完整页
			{
				Page_write(addr , buff , 256);
				addr += 256;
				buff += 256;
			}
			if(temp != 0)							//若有剩余补尾页页
			{
				Page_write(addr , buff , temp);
			}
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175205.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图解统计学 10 | 贝叶斯公式与全概率公式

文章目录概率联合概率条件概率全概率公式贝叶斯公式过年了,作为水果店老板的我们,一共进了三种水果,其中:西瓜:50个 香蕉:30个 橙子:20个 为了方便顾客挑选,放在如下的格子里&…

[Android]Shape Drawable

ShapeDrawable可以理解为通过颜色来构造的图形 <android.widget.Buttonandroid:id"id/button1"android:layout_width"wrap_content"android:layout_height"wrap_content"android:text"Button"android:background"drawable/sha…

MongoDB学习笔记【part4】SpringBoot集成MongoDB、MongoTemplate开发CURD

一、Spring Boot 集成 Mongodb spring-data-mongodb 提供了 MongoTemplate 与 MongoRepository 两种方式访问mongodb&#xff0c;MongoRepository 操作简单&#xff0c;但 MongoTemplate 更加灵活&#xff0c;我们在项目中可以灵活使用这两种方式操作mongodb。 第一步&#x…

铸造性能监控平台【grafana+influxdb/prometheus+Linux/Windows】

目录一、grafanainfluxdbjmeter1、前言2、安装grafana和influxdb3、启动grafana4、访问grafana5、启动influxdb6、配置influxdb和jmeter7、在grafana中显示数据8、其他模板二、grafanaprometheusexporter1、前言2、grafana启动3、exporter安装与运行4、prometheus安装与运行5、…

代码随想录算法训练营第23天 二叉树 java : 669. 修剪二叉搜索树108.将有序数组转换为二叉搜索树538.把二叉搜索树转换为累加树

文章目录LeetCode 669. 修剪二叉搜索树题目讲解思路LeetCode 108.将有序数组转换为二叉搜索树题目讲解思路LeetCode 538.把二叉搜索树转换为累加树题解思路总结LeetCode 669. 修剪二叉搜索树 题目讲解 思路 在1到3的区间选择 元素 如何超过3 或者 小于1 如果小于1 叫要考虑 …

NeRF: Representing Scenesas Neural Radiance Fieldsfor View Synthesis论文阅读

注意&#xff1a;和很多文章一样&#xff0c;在Google搜索到最终版本时&#xff0c;有链接指出其有7个历史版本&#xff0c;但内容较详细的却不是最终版本&#xff0c;而是ECCV (2020)版&#xff0c;阅读时可以两个版本配合着阅读。 1. 摘要 我们提出了一种方法&#xff0c;通…

202301读书笔记|《命运》蔡崇达

202301读书笔记|《命运》蔡崇达 《命运》是我读的蔡崇达的第二本书&#xff0c;第一本是《皮囊》印象最深的一句就是“肉体是拿来用来的&#xff0c;不是拿来伺候的。” 当时读完第一本就很受触动&#xff0c;这一次读完《命运》依然很触动我。作者真的很厉害&#xff0c;这个故…

SpringBoot看这一篇文章就够了

第一章 SpringBoot简介 第1节 SpringBoot是什么 1 21.SpringBoot是一个可以快速创建可运行的、独立的、生产级的基于Spring的应用程序 2.SpringBoot采用一种约定优于配置的设计理念,可以快速让用户创建出一个可运行的基于Spring的应用第2节 SpringBoot的优势 1 2 3 4 51.快速构…

nacos源码解析==SPI和spring.factories机制-服务注册-心跳发送-服务拉取-服务调用

Spring.Factories这种机制实际上是仿照java中的SPI扩展机制实现的 springboot核心基础之spring.factories机制 - 知乎 SpringBoot1IDEA编写一个自己的starter_一个java开发的博客-CSDN博客_idea创建spring starter spring-cloud-starter-alibaba-nacos-discovery 将要注册到…

know sth. new 大话C#的进阶必知点解析第1章 第5节 名贵中药材程序WPF显示图片报错,找不到资源? 什么原因

1 Ui布局代码&#xff1b; 布局方面&#xff0c;主要还是继承了原先的布局方式。包括图片的展示&#xff0c;也是用了最外层border边框的方式&#xff0c;边框加入背景颜色方式的图片展示&#xff1b; 去把目标图片进行显示出来&#xff0c;这个没有太多技术含量。 至于图片的…

Spring Boot操作数据库学习之整合Druid

文章目录一 Druid 简介二 配置数据源创建项目步骤及数据库内容三 整合操作3.1 添加Druid数据源依赖3.2 编写配置文件3.3 测试3.4 自定义绑定数据源设置3.5 导入Log4j的依赖&配置日志输出3.6 添加DruidDataSource组件3.7 测试3.8 配置 Druid 数据源监控3.9 配置过滤器一 Dru…

SAPIEN PowerShell Studio 介绍

PowerShell Studio是一款优秀的基于PowerShell研发的脚本编辑器&#xff0c;它拥有全新的代码分析、智能预选、xaml支持功能&#xff0c;能够给用户提供一套完整的软件开发环境&#xff0c;让用户能够更加轻松的工作&#xff0c;这样一来大家开发项目的效率就会大大提升。创建模…

Day866.binlogredoLog -MySQL实战

日志系统 Hi&#xff0c;我是阿昌&#xff0c;今天学习的是关于MySql的binlog&redoLog的内容。 一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块&#xff0c;最后到达存储引擎。 那么&#xff0c;一条更新语句的执行流程又是怎样的呢&#x…

人大金仓数据库的用户与角色

创建用户 create user 用户名 授予用户创建数据库权限 alter user 用户名 要给的权限 然后查看用户信息 \du 用户名 设置用户密码 没有口令不能登录 alter user 用户名 password ‘kingbase’; 修改用户的并发连接数 alter user 用户 connection limit 要设置的连接数; 修改…

as-if-serialhappens-before

一、as-if-serialas-if-serial语义的意思是&#xff1a;不管怎么重排序&#xff08;编译器和处理器为了提高并行度&#xff09;&#xff0c;&#xff08;单线程&#xff09;程序的执行结果不能被改变。编译器、runtime和处理器都必须遵守as-if-serial语义。 为了遵守as-if-seri…

java类成员/final/static都涉及到了2023025

类成员&#xff1a; 在Java类里只能包含成员变量、方法、构造器、初始化块、内部类&#xff08;包括接口、枚举&#xff09;这5种成员&#xff0c;目前已经介绍了前面4种&#xff0c;其中static可以修饰成员变量、方法、初始化块、内部类&#xff08;包括接口&#xff0c;枚举&…

显示器的相关知识

目录 显示器的作用 显示器的尺寸 人眼的可视角度 显示器的分辨率 显示器的刷新率 显示器的灰阶响应时间 显示器的色域 显示器的色深 显示器的色准 显示器的HDR参数 显示器的面板 画面撕裂 前言 导致画面撕裂的原因 防画面撕裂技术 视频的码率 显示器的作用 把…

Golang 多模块开发

Golang 多模块开发 今天学习下Golang中多模块的基础知识&#xff0c;学习多模块的运行原理&#xff0c;使用多模块的方式&#xff0c;可以让开发者的代码在其他多个模块中构建、运行。提高代码的复用&#xff0c;从而提高开发效率。 在今天的学习中&#xff0c;将在工作工作空…

bfs入门教程(广度优先搜索)(含图解)

源自《啊哈算法》 目录 bfs正文 题目 思路 完整代码1 完整代码2 再解炸弹人 题目 思路 完整代码1 完整代码2 总结 bfs正文 第四章--深度优先搜索中&#xff0c;我们用dfs找到了寻找小哈的最短路径 接下来&#xff0c;我们要用bfs&#xff08;Breadth First Sear…

Zookeeper的本地安装部署和分布式安装部署

文章目录一. 本地模式安装部署1&#xff09;安装前准备2&#xff09;配置修改3&#xff09;操作Zookeeper1.2 配置参数解读二. 分布式安装部署1&#xff09;集群规划2&#xff09;解压安装3&#xff09;配置服务器编号4&#xff09;配置zoo.cfg文件5&#xff09;集群操作客户端…