目录

一、SPI简介

1、全双工与半双工

2、同步与异步

3、SPI通信方式

二、SPI工作模式

三、W25Q128BV

1、读ID Read Manufacturer/Device ID(90h)

2、读ID代码实现(硬件SPI)

3、IO口模拟SPI时序图实现 (软件SPI)  模式3


一、SPI简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI是一种高速的(10Mbps),全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议,如NRF24L01、VS1053、SD卡等。

1、全双工与半双工

a:半双工常见于485总线,即下图所示,使用的是差分信号,这样子就决定了是半双工,即任何一个传输时刻,只有一个传输方向,要么是发送,要么是接收。优点在于传输距离远,理论上达到1.2km。

d:全双工常见SPI通信,即下图所示,两边可以同时收发数据,优点是速度快,延迟小。

2、同步与异步

a:同步的典型特征:有时钟线,可以控制通信的速度。

b:异步的典型特征:约束好通信的波特率,数据帧格式.

3、SPI通信方式

根据上图我们可以看到,由于SPI是全双工,同步的通信总线,SCLK,MOSI,MISO这三个引脚可以共用于从机,根据片选引脚(/SS)来区别来区别从机(输出低电平的从机有效工作),注意:硬件连接看起来是一对多,一个主机对应多个从机,但是实际上通信只能一对一通信,当一个硬件处于通信状态(输出低电平),其他硬件处于等待状态(输出高电平),不然容易出现数据的冲突

SCLK(Serial Clock):串行时钟线

MOSI(Master Output Slave Input):主机输出数据,从机接收数据

MISO(Master Input Slave Output):从机输出数据,主机接收数据

/SS(slave select):从机选择,该引脚输出低电平,该引脚有效工作

二、SPI工作模式

SPI总线有四种工作模式,其中使用最为广泛的是模式0和模式3方式。

CPOL(Clock Polarity):时钟极性选择,为0时SPI总线空闲时,时钟线为低电平 ; 为1时SPI总线空闲时,时钟线为高电平。

CPHA(Clock Phase):时钟相位选择,为0时在SCLK第一个跳变沿,主机对MISO引脚电平采样;为1时在SCLK第二个跳变沿,主机对MISO引脚电平采样

通过上面两张图我们可以发现,模式0是从机先发送第一个数据,模式3是主机先发送第一个数据,它们之间有什么区别呢,其实没有什么区别,作用就是做好通信前的准备,约定好数据的采集时机

三、W25Q128BV

W25Q128BV:内存128MBit/16MType,每个可编程页256字节,支持SPI模式0和模式3。具体可以看相关手册。由于手册繁多且复杂,这边就不多做描述。

关于SPI SLASH具体描述可参考博主:(15条消息) W25Q128数据手册阅读总结_百里之外的博客-CSDN博客https://blog.csdn.net/qq_40993639/article/details/122053404

        1、读ID Read Manufacturer/Device ID(90h)

本文我们讲一讲常用的指令中的一种,读ID Read Manufacturer/Device ID(90h),下图是完整的时序图,数据手册是分两个图来看,我这里合成一个图看着更方便一点。关于这个时序图的工作方式在手册有总结,是一段英文解释,我这边用自己的话解释这个时序图的工作原理。

在这个指令传输之前,我们要使/cs(/ss)引脚拉低移位传输指令码90h(1001 0000)对应时钟线(CLK)0-7,每一个时钟周期对应一个Bit(比特),紧接着我们要传输24-BIt(比特)地址(A23-A0),其对应时钟线(CLK)8-31,也就是3个字节地址,完成上诉工作之后,厂商ID和设备ID就会发给我们主机,通信结束后,使/cs(/ss)引脚拉高。

通过读ID往往能够检测到SPI通信是否正常,也能够知道从机设备是否在线。

2、读ID代码实现(硬件SPI)

编程环境:keil5 STM32f407

首先初始化w25q128。

GPIO_InitTypeDef  GPIO_InitStructure;
SPI_InitTypeDef  SPI_InitStructure;void w25q128_init(void)
{//使能端口B的硬件时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能SPI1的硬件时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//配置PB3~PB5为复用功能模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5; //指定3、4、5号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//配置为复用功能模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻GPIO_Init(GPIOB, &GPIO_InitStructure);   //将PB3~PB5连接到SPI1的硬件GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);  GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);       //配置PB14为输出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;    //指定14号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为推挽功能模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻GPIO_Init(GPIOB, &GPIO_InitStructure); //PB14初始电平状态为高电平,因为总线空闲的时候,SS引脚(片选引脚)为高电平PBout(14)=1;//SPI1参数的配置        //硬件SPI设置  模拟SPI不需要这个则删除掉SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主机模式SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//每次传输最小单元为字节SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//根据从机的手册来配置,模式3SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//根据从机的手册来配置,模式3,第二跳变沿//由软件代码控制片选引脚     SPI_NSS_Hard表示由硬件控制引脚SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//波特率  根据从机的手册来配置,SPI的硬件时钟=84MHz/16=5.25MHzSPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;//根据从机的手册来配置 MSB表示最高有效位传输SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_Init(SPI1, &SPI_InitStructure);//使能SPI1硬件工作SPI_Cmd(SPI1,ENABLE);}

读ID

//官方代码,
uint8_t SPI1_SendByte(uint8_t byte)
{/*!< Loop while DR register in not emplty *///检测是否发送完毕while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);    /*!< Send byte through the SPI1 peripheral *///如果发送完,就把数据发送出去SPI_I2S_SendData(SPI1, byte);/*!< Wait to receive a byte */while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);/*!< Return the byte read from the SPI bus *///对方返回一个字节回来return SPI_I2S_ReceiveData(SPI1);
}void w25q128_read_id(uint8_t *m_id,uint8_t *d_id)
{W25Q128_SS=0;SPI1_SendByte(0x90);SPI1_SendByte(0x00);SPI1_SendByte(0x00);SPI1_SendByte(0x00);//这里参数随便填,从机会忽略引脚电平状态*m_id=SPI1_SendByte(0xFF);*d_id=SPI1_SendByte(0xFF);W25Q128_SS=1; //拉高结束
}

获取厂商ID和设备ID并打印

int main()
{uint8_t m_id,d_id;//串口初始化usart1_init(115200);w25q128_init();w25q128_read_id(m_id,d_id);printf("m_id=%X,d_id=%x\r\n",m_id,d_id);while(1){}
}

打印结果

从0地址连续读64字节的数据实现        Read Data(03H)

基本上不变,指令变为03H,后面就是传输连续字节,接收完一个字节,其指向会指向新的地址,每个地址都指向有效数据(会自动偏移)。

//通用写法
void w25q128_read(uint32_t addr,uint8_t *buf,uint32_t len)
{uint8_t *p = buf;//拉低开始W25Q128_SS=0;SPI1_SendByte(0x03);//假如有个地址为0x123456.SPI1_SendByte(addr>>16);    //0x12SPI1_SendByte(addr>>8);      //0x34SPI1_SendByte(addr);      //0x56while(len--){*p=SPI1_SendByte(0xFF);p++;}//拉高结束W25Q128_SS=1;
}

打印

int main()
{uint8_t m_id,d_id;uint8_t buf[64]={0};//串口初始化usart1_init(115200);w25q128_init();//读IDw25q128_read_id(m_id,d_id);printf("m_id=%X,d_id=%x\r\n",m_id,d_id);//读64字节数据w25q128_read(0,buf,sizeof buf);printf("read data:");for(i=0; i<64; i++){printf("%02X ",buf[i]);}while(1){}
}

3、IO口模拟SPI时序图实现 (软件SPI)  模式3

初始化模拟SPI w25q128

GPIO_InitTypeDef  GPIO_InitStructure;
SPI_InitTypeDef  SPI_InitStructure;
#define W25Q128_SS      PBout(14)
#define W25Q128_SCLK    PBout(3)
#define W25Q128_MOSI    PBout(5)
#define W25Q128_MISO    PBin(4)void w25q128_init(void)
{//使能端口B的硬件时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);   //配置PB3 PB5 PB14为输出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_5|GPIO_Pin_14;  //指定3、5、14号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻GPIO_Init(GPIOB, &GPIO_InitStructure);   //配置PB4为输入模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;  //指定4号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//配置为输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高,功耗就越高,但是响应速度也更快GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻GPIO_Init(GPIOB, &GPIO_InitStructure);  //PB14初始电平状态为高电平,因为总线空闲的时候,SS引脚(片选引脚)为高电平W25Q128_SS=1;//CPOL=1,SCLK引脚在SPI总线空闲的时候为高电平W25Q128_SCLK=1;//MOSI引脚随意电平    可高可低W25Q128_MOSI=1;}

读ID,只不过发送需要自己写

uint8_t SPI1_SendByte(uint8_t byte)
{int32_t i=0;uint8_t d=0;//一个周期的变化for(i=7; i>=0; i--){if(byte & (1<<i))W25Q128_MOSI=1;elseW25Q128_MOSI=0;W25Q128_SCLK=0;    //模式3单个周期  SCLK拉低delay_us(1);       //延时1微秒W25Q128_SCLK=1;    //模式3单个周期  SCLK拉高delay_us(1);       //延时1微秒//判断MISO是否是高电平if(W25Q128_MISO)d|=1<<i;}return d;
}void w25q128_read_id(uint8_t *m_id,uint8_t *d_id)
{W25Q128_SS=0;SPI1_SendByte(0x90);SPI1_SendByte(0x00);SPI1_SendByte(0x00);SPI1_SendByte(0x00);*m_id=SPI1_SendByte(0xFF);*d_id=SPI1_SendByte(0xFF);W25Q128_SS=1;
}

成功打印数据,与硬件SPI打印是一样的 

本次就分享到这里,如看到我理解错的地方也请指正我,感谢各位的观看❤

SPI FLASH(W25Q128BV) 包含SPI工作原理相关推荐

  1. linux下spi flash驱动程序,关于spi flash芯片m25p80驱动以及其简单的mtd驱动分析

    项目中用到了spi flash芯片MX25L25635E,之前在uboot下简单分析了驱动代码,调试该flash擦除的bug,一直没有时间分 析内核中关于该芯片的驱动,以下是对该芯片驱动的一个简单分析 ...

  2. SPI Flash芯片W25Q32英文版数据手册解读(一)---------引脚功能,工作模式

    W25Q32芯片是一个可以通过SPI(串行外围设备接口)操作的flash存储器,这篇文章备忘和总结一下英文版数据手册的一些解读.有关时序及具体用STC单片机编写程序的内容等下一篇文章. 一.芯片引脚功 ...

  3. 上海航芯技术分享 | 基于SPI Flash的U盘程序,从STM32F103到ACM32F403

    前言 本项目是以SPI Flash(如W25Q128等)存储元件作为存储单元,MCU主控完成USB接口通信并根据SCSI协议实现U盘功能.其结构如下图所示: SPI Flash部分移植 SPI功能部分 ...

  4. 如何给ESP32选择外接SPI Flash

    给ESP32选择外接SPI Flash [前言]:  许多用户在使用 ESP32 做开发的时候,都会碰到如何选择外接 Flash 的难题,因为不同厂商的 Flash 存在很多差异,Flash 支持的工 ...

  5. Lichee Zero SPI Flash编译实战记录

    1.系统 物理机 ubuntu1804 2.创建编译环境 1)下载 gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz wget ht ...

  6. SPI Flash,NOR Flash,NAND Flash,eMMC对比

    快闪存储器(英语:Flash Memory),是一种电子式可清除程序化只读存储器的形式,允许在操作中被多次擦或写的存储器.这种科技主要用于一般性数据存储,以及在电脑与其他数字产品间交换传输数据,如储存 ...

  7. 深入理解Arduino下的ESP8266_Non-OS_SDK API④ SPI Flash接口

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... 共同学习成长QQ群 622368884,不喜勿 ...

  8. SPI硬件电路设计,SPI协议

    SPI硬件电路设计 1  SPI简介 2  SPI的特点和基本概念 2.1 SPI的特点 2.2 SPI的基本概念 3  SPI协议(摩托罗拉) 3.1 SPI控制器内部寄存器 3.2 时钟极性(CP ...

  9. Solr的工作原理(最直白的解释,简单易懂)懂?

    Solr 什么是Solr Solr是一个开源搜索平台,用于构建搜索应用程序. 它建立在Lucene(全文搜索引擎)之上. Solr是企业级的,快速的和高度可扩展的. 使用Solr构建的应用程序非常复杂 ...

最新文章

  1. Win2K下关联进程/端口之代码初步分析
  2. 微信公众平台开发 OAuth2.0网页授权认证
  3. MySQL 规范及优化
  4. JZOJ 4822. 【NOIP2016提高A组集训第1场10.29】完美标号
  5. 简单QT应用到通过手写布局实现QT应用
  6. Mysql的sql语句,Delete 中包含 not in
  7. Apache2.4开启php
  8. Python程序员的30个常见错误
  9. mysql 备份配置文件_mySQL配置文件、备份与恢复
  10. webapi get请求 FromUri list参数传递
  11. python处理ini文件_python对ini配置文件处理
  12. H3C WAC360 基于Win2012 NPS 802.1x 认证
  13. 邮件病毒***后清除步骤
  14. 线程取消(pthread_cancel)
  15. MySQL复制表的三种方式
  16. 智能,服务,生态:华为调制的AIOps,味道有何不同?
  17. 基于图像识别的表格数据提取系统
  18. Joint Discriminative and Generative Learning for Person Re-identification 论文翻译
  19. 张勋说:棒磨机钢棒直径的配置(热处理调质耐磨钢棒)
  20. DuerOS业绩亮眼,如何成为了百度财报新增长点?

热门文章

  1. 有趣的排序算法——Monkey King排序 详细介绍
  2. 常见嵌入式硬件部分面试题总结
  3. A* 流程+代码详细注释
  4. 第四节:逻辑控制【java】
  5. 《Context Contrasted Feature and Gated Multi-Scale Aggregation for Scene Segmentation》论文阅读
  6. 单链表排序之直接插入排序
  7. springboot 2.0 配置全局时间格式化
  8. 项目添加到服务器报错,基于github+travis自动部署vue项目到远端服务器
  9. 如何理解「数据驱动业务」?
  10. 智慧天下:专业版和绿色版有什么区别?