SPI

一、简介

        SPI是一种同步、全双工、主从式接口。来自主机或从机的数据在 时钟上升沿或下降沿同步。主机和从机可以同时传输数据。SPI接 口可以是3线式或4线式。

  • MOSI(Master Output Slave Input) – 主设备输出/从设备输入信号;
  • MISO(Master Input Slave Output) – 主设备输入/从设备输出信号;
  • SCLK(Serial Clock) – 串行时钟信号,由主设备产生,用来同步数据比特;
  •  SS(Slave Select)/CS(Chip Select) – 从设备片选信号,低电平有效,由主设备控制;

        主机和从机之间传输的数据与 主机产生的时钟同步。同I 2 C接口相比,SPI器件支持更高的时钟 频率。 SPI接口只能有一个主机,但可以有一个或多个从机。主机的片选信号用于选择从机。这通常是一个低电平有效信 号,拉高时从机与SPI总线断开连接。当使用多个从机时,主机需 要为每个从机提供单独的片选信号。MOSI和MISO是数据线。MOSI将数据从主机发送到从机,MISO将数 据从从机发送到主机。

二、数据传输

        数据传输 要开始SPI通信,主机必须发送时钟信号,并通过使能CS信号选择 从机。片选通常是低电平有效信号。因此,主机必须在该信号上 发送逻辑0以选择从机。SPI是全双工接口,主机和从机可以分别 通过MOSI和MISO线路同时发送数据。在SPI通信期间,数据的发送 (串行移出到MOSI/SDO总线上)和接收(采样或读入总线(MISO/ SDI)上的数据)同时进行。串行时钟沿同步数据的移位和采样。SPI 接口允许用户灵活选择时钟的上升沿或下降沿来采样和/或移位数 据。欲确定使用SPI接口传输的数据位数,请参阅器件数据手册。

三、时钟极性和时钟相位

        时钟极性和时钟相位 在SPI中,主机可以选择时钟极性和时钟相位。在空闲状态期 间,CPOL位设置时钟信号的极性。空闲状态是指传输开始时CS为 高电平且在向低电平转变的期间,以及传输结束时CS为低电平且 在向高电平转变的期间。CPHA位选择时钟相位。根据CPHA位的状 态,使用时钟上升沿或下降沿来采样和/或移位数据。主机必须根 据从机的要求选择时钟极性和时钟相位。根据CPOL和CPHA位的选 择,有四种SPI模式可用。表1显示了这4种SPI模式。

传输的开始和结束用绿色虚线表示,采样边沿用橙色虚线表示,移位边沿用蓝色虚线表示。

四、多从机配置

4.1  常规SPI模式

 常规SPI模式: 在常规模式下,主机需要为每个从机提供单独的片选信号。一旦 主机使能(拉低)片选信号,MOSI/MISO线上的时钟和数据便可用 于所选的从机。如果使能多个片选信号,则MISO线上的数据会被 破坏,因为主机无法识别哪个从机正在传输数据。 从图6可以看出,随着从机数量的增加,来自主机的片选线的数量 也增加。这会快速增加主机需要提供的输入和输出数量,并限制 可以使用的从机数量。可以使用其他技术来增加常规模式下的从 机数量,例如使用多路复用器产生片选信号。

4.2  菊花链模式

在菊花链模式下,所有从机的片选信号连接在一起,数据从一个 从机传播到下一个从机。在此配置中,所有从机同时接收同一SPI 时钟。来自主机的数据直接送到第一个从机,该从机将数据提供 给下一个从机依此类推。 使用该方法时,由于数据是从一个从机传播到下一个从机,所以 传输数据所需的时钟周期数与菊花链中的从机位置成比例。例如 在图7所示的8位系统中,为使第3个从机能够获得数据,需要24个 时钟脉冲,而常规SPI模式下只需8个时钟脉冲。图8显示了时钟周 期和通过菊花链的数据传播。并非所有SPI器件都支持菊花链模 式。请参阅产品数据手册以确认菊花链是否可用。

 

五、软件SPI读写W25Q64

myspi.h

#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif

myspi.c

#include "stm32f10x.h"     /*引脚配置层*//*** 函    数:SPI写SS引脚电平* 参    数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		//根据BitValue,设置SS引脚的电平
}/*** 函    数:SPI写SCK引脚电平* 参    数:BitValue 协议层传入的当前需要写入SCK的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCK为低电平,当BitValue为1时,需要置SCK为高电平*/
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);		//根据BitValue,设置SCK引脚的电平
}/*** 函    数:SPI写MOSI引脚电平* 参    数:BitValue 协议层传入的当前需要写入MOSI的电平,范围0~0xFF* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置MOSI为低电平,当BitValue非0时,需要置MOSI为高电平*/
void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);		//根据BitValue,设置MOSI引脚的电平,BitValue要实现非0即1的特性
}
/*** 函    数:I2C读MISO引脚电平* 参    数:无* 返 回 值:协议层需要得到的当前MISO的电平,范围0~1* 注意事项:此函数需要用户实现内容,当前MISO为低电平时,返回0,当前MISO为高电平时,返回1*/
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);			//读取MISO电平并返回
}/*** 函    数:SPI初始化* 参    数:无* 返 回 值:无* 注意事项:此函数需要用户实现内容,实现SS、SCK、MOSI和MISO引脚的初始化*/
void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA4、PA5和PA7引脚初始化为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA6引脚初始化为上拉输入/*设置默认电平*/MySPI_W_SS(1);											//SS默认高电平MySPI_W_SCK(0);											//SCK默认低电平
}/*协议层*//*** 函    数:SPI起始* 参    数:无* 返 回 值:无*/
void MySPI_Start(void)
{MySPI_W_SS(0);				//拉低SS,开始时序
}/*** 函    数:SPI终止* 参    数:无* 返 回 值:无*/
void MySPI_Stop(void)
{MySPI_W_SS(1);				//拉高SS,终止时序
}/*** 函    数:SPI交换传输一个字节,使用SPI模式0* 参    数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到for (i = 0; i < 8; i ++)						//循环8次,依次交换每一位数据{MySPI_W_MOSI(ByteSend & (0x80 >> i));		//使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线MySPI_W_SCK(1);								//拉高SCK,上升沿移出数据if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}	//读取MISO数据,并存储到Byte变量//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0MySPI_W_SCK(0);								//拉低SCK,下降沿移入数据}return ByteReceive;								//返回接收到的一个字节数据
}

W25Q64.h

#ifndef __W25Q64_H
#define __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);#endif

W25Q64_INS.h 

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q

W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"/*** 函    数:W25Q64初始化* 参    数:无* 返 回 值:无*/
void W25Q64_Init(void)
{MySPI_Init();					//先初始化底层的SPI
}/*** 函    数:MPU6050读取ID号* 参    数:MID 工厂ID,使用输出参数的形式返回* 参    数:DID 设备ID,使用输出参数的形式返回* 返 回 值:无*/
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID);			//交换发送读取ID的指令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收MID,通过输出参数返回*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收DID高8位*DID <<= 8;									//高8位移到高位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交换接收DID的低8位,通过输出参数返回MySPI_Stop();								//SPI终止
}/*** 函    数:W25Q64写使能* 参    数:无* 返 回 值:无*/
void W25Q64_WriteEnable(void)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交换发送写使能的指令MySPI_Stop();								//SPI终止
}/*** 函    数:W25Q64等待忙* 参    数:无* 返 回 值:无*/
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交换发送读状态寄存器1的指令Timeout = 100000;							//给定超时计数时间while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循环等待忙标志位{Timeout --;								//等待时,计数值自减if (Timeout == 0)						//自减到0后,等待超时{/*超时的错误处理代码,可以添加到此处*/break;								//跳出等待,不等了}}MySPI_Stop();								//SPI终止
}/*** 函    数:W25Q64页编程* 参    数:Address 页编程的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray	用于写入数据的数组* 参    数:Count 要写入数据的数量,范围:0~256* 返 回 值:无* 注意事项:写入的地址范围不能跨页*/
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();						//写使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交换发送页编程的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位for (i = 0; i < Count; i ++)				//循环Count次{MySPI_SwapByte(DataArray[i]);			//依次在起始地址后写入数据}MySPI_Stop();								//SPI终止W25Q64_WaitBusy();							//等待忙
}/*** 函    数:W25Q64扇区擦除(4KB)* 参    数:Address 指定扇区的地址,范围:0x000000~0x7FFFFF* 返 回 值:无*/
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();						//写使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交换发送扇区擦除的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位MySPI_Stop();								//SPI终止W25Q64_WaitBusy();							//等待忙
}/*** 函    数:W25Q64读取数据* 参    数:Address 读取数据的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray 用于接收读取数据的数组,通过输出参数返回* 参    数:Count 要读取数据的数量,范围:0~0x800000* 返 回 值:无*/
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_DATA);			//交换发送读取数据的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位for (i = 0; i < Count; i ++)				//循环Count次{DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//依次在起始地址后读取数据}MySPI_Stop();								//SPI终止
}

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

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

相关文章

经典卷积神经网络-ResNet

经典卷积神经网络-ResNet 一、背景介绍 残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的。ResNet 在2015 年的ILSVRC&#xff08;ImageNet Large Scale Visual Recognition Challenge&#xff09;中取得了冠军。残差神经网络的主要贡献是发现了…

Leetcode的AC指南 —— 字符串:541. 反转字符串 II

摘要&#xff1a; Leetcode的AC指南 —— 字符串&#xff1a;541. 反转字符串 II。题目介绍&#xff1a;给定一个字符串 s 和一个整数 k&#xff0c;从字符串开头算起&#xff0c;每计数至 2k 个字符&#xff0c;就反转这 2k 字符中的前 k 个字符。 如果剩余字符少于 k 个&…

Nginx 反向代理负载均衡

Nginx 反向代理负载均衡 普通的负载均衡软件&#xff0c;如 LVS&#xff0c;其实现的功能只是对请求数据包的转发、传递&#xff0c;从负载均衡下的节点服务器来看&#xff0c;接收到的请求还是来自访问负载均衡器的客户端的真实用户&#xff1b;而反向代理就不一样了&#xf…

Android混淆那些事

前言 作为一个Android开发&#xff0c;大家或多或少都有一些关于混淆的了解&#xff08;毕竟披个纱布也比裸奔要好的多吧&#xff09;。混淆的概念虽然容易理解&#xff0c;但相信大多数开发可能还是在网上搜索通用配置后通过C-V大法接入到自己的项目中&#xff0c;这也使得混…

7、python-函数

一.可变参数 参数前面有一个*号&#xff0c;表示是可变的。在函数内部&#xff0c;如&#xff1a;参数 numbers 接收到的是一个 tuple def add(*numbers):print(numbers)add(1, adc, ["q", w], {"1": "2"}) # (1, adc, [q, w], {1: 2})二.关键…

canvas绘制网格线示例

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

docker 部署教学版本

文章目录 一、docker使用场景及常用命令1&#xff09;docker使用场景2&#xff09;rocky8(centos8)安装 docker3&#xff09;docker 常用命令补充常用命令 二、 单独部署每个镜像&#xff0c;部署spring 应用镜像推荐&#xff08;2023-12-18&#xff09;1、 安装使用 mysql1.1 …

高可用解决方案 Keepalived 概述

概述 Keepalived 介绍 Keepalived 是 Linux 下一个轻量级别的高可用解决方案&#xff0c;通过 **VRRP 协议&#xff08;虚拟路由冗余协议&#xff09;**来实现服务或者网络的高可用&#xff0c;可以利用其来解决单点故障。 起初是为 LVS 设计的&#xff0c;一个 LVS 服务会有 …

canvas实现文字自动换行,段落居中,单行的时候居中

canvas实现文字自动换行&#xff0c;段落居中,单行的时候居中方法&#xff0c;主要用到以下方法&#xff1a; /****绘制自动换行的字符串****///ctx_2d getContext("2d") 对象 //lineheight 段落文本行高 //bytelength 设置单字节文字一行内的数量 …

计算机毕业论文内容参考|基于智能搜索引擎的图书管理系统的设计与实现

文章目录 摘要前言绪论课题背景国内外现状与趋势课题内容相关技术与方法介绍系统分析系统设计系统实现系统测试总结与展望摘要 本文介绍了基于智能搜索引擎的图书管理系统的设计与实现。该系统旨在提供一个高效、智能化的图书管理平台,帮助用户更快、更准确地找到所需的图书资…

2024年PMP报考需要什么条件?怎么报名?

一、PMP是什么 PMP 是项目管理的入门级证书&#xff0c;全称是项目管理专业人士资格认证&#xff0c;由美国项目管理协会&#xff08;PMI&#xff09;举办的&#xff0c;受到全球200多个国家的认可&#xff0c;从1999 年到现在已经有20多年发展历史了。 顾名思义&#xff0c;…

【WPF.NET开发】预览事件

本文内容 先决条件预览标记为“已处理”的事件通过控件解决事件禁止问题 预览事件&#xff0c;也称为隧道事件&#xff0c;是从应用程序根元素向下遍历元素树到引发事件的元素的路由事件。 引发事件的元素在事件数据中报告为Source 。 并非所有事件场景都支持或需要预览事件。…

物理内存不够怎么办???centos9下如何设置大的swap空间

在做数据分析时&#xff0c;大家除了cpu速度不够以外&#xff0c;还有就是内存经常会爆掉&#xff0c;下面就介绍一下如何利用硬盘空间给物理内存扩容。 当然硬盘的速度要慢很多&#xff0c;如果要使用硬盘扩容也建议使用性能较高的ssd盘来做。 在CentOS 9系统下设置或增加大的…

【大数据面试知识点】分区器Partitioner:HashPartitioner、RangePartitioner

Spark HashParitioner的弊端是什么&#xff1f; HashPartitioner分区的原理很简单&#xff0c;对于给定的key&#xff0c;计算其hashCode&#xff0c;并除于分区的个数取余&#xff0c;如果余数小于0&#xff0c;则用余数分区的个数&#xff0c;最后返回的值就是这个key所属的…

阶段十-分布式-nginx服务器

一、Nginx简介 Nginx 是高性能的 HTTP 和反向代理的服务器&#xff0c;处理高并发能力是十分强大的&#xff0c;能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。tomcat并发数量理论值是500&#xff0c;实际也就300左右。 1.2 正向代理 正向代理代理的是客户…

步进电机转速数码管显示

/*----------------------------------------------- 内容&#xff1a;本程序用于测试4相步进电机常规驱动 使用1-2相励磁 1-2相激励功率增倍&#xff0c;步进角度减半&#xff0c;抖动减少 顺序如下 a-ab-b-bc-c-cd-d-da 又称4相8拍 数码管…

世微 DW01 4.2V锂电池保护电路芯片 专业电源管理芯片

一、 描述 DW01A 是一个锂电池保护电路&#xff0c;为避免锂电池因过充电、过放电、电流过大导致电池寿命缩短或电池被损坏而设计的。它具有高精确度的电压检测与时间延迟电路。 二、 主要特点 工作电流低&#xff1b; 过充检测 4.3V&#xff0c;过充释放 4.05V&#xff1b; 过…

缅怀一代传奇!TVP创始委员陈皓与他的《左耳听风:传奇程序员练级攻略》

引言 中文技术圈时常被一种浮躁所困扰。互联网企业历经跑马圈地的红利期后&#xff0c;开始在精细化运营的路上艰难求索&#xff1b;圈子里的程序员们&#xff0c;也被日益放缓的业务需求和不断内卷的行业态势所影响&#xff0c;职业困境、年龄危机成了老生常谈的话题。 成长往…

188.【2023年华为OD机试真题(C卷)】中文分词模拟器(字典树动态规划算法—JavaPythonC++JS实现)

请到本专栏顶置查阅最新的华为OD机试宝典 点击跳转到本专栏-算法之翼:华为OD机试 🚀你的旅程将在这里启航!本专栏所有题目均包含优质解题思路,高质量解题代码,详细代码讲解,助你深入学习,深度掌握! 文章目录 188.【2023年华为OD机试真题(C卷)】中文分词模拟器(…

CSMM(软件开发能力成熟度模型)办理需要什么条件?

CSMM&#xff08;软件开发能力成熟度模型&#xff09;的办理通常需要企业满足一定的条件&#xff0c;这些条件包括但不限于&#xff1a; 1. 企业资质&#xff1a;申请CSMM认证的企业需要具备合法的营业执照和相关的行业资质。 2. 软件开发能力&#xff1a;企业需要有一定的软件…