目录

  • 一、SPI简介
    • 1.1电路模式
    • 1.2通信原理
    • 1.3SPI时序基本单元
      • 1.3.1起始和终止
      • 1.3.2交换字节
  • 二、W25Q64
    • 2.1W25Q64简介
    • 2.2W25Q64硬件电路
    • 2.3W25Q64框图
    • 2.4Flash操作注意事项
  • 三、软件SPI读写W25Q64
    • 3.1接线图
    • 3.2程序代码

一、SPI简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据,SPI没有定义速度限制,通常能达到甚至超过10M/bps。

SPI有主、从两种模式,通常由一个主模块和一个或多个从模块组成(SPI不支持多主机),主模块选择一个从模块进行同步通信,从而完成数据的交换。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。

SPI通信原理很简单,需要至少4根线,单向传输时3根线,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和CS/SS(片选):

MISO( Master Input Slave Output):主设备数据输入,从设备数据输出;
MOSI(Master Output Slave Input):主设备数据输出,从设备数据输入;
SCLK(Serial Clock):时钟信号,由主设备产生;
CS/SS(Chip Select/Slave Select):从设备使能信号,由主设备控制,一主多从时,CS/SS是从芯片是否被主芯片选中的控制信号,只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。

1.1电路模式

采用一主多从的模式、同步,全双工

所有SPI设备的SCK、MOSI、MISO分别连在一起
主机另外引出多条SS控制线,分别接到各从机的SS引脚
输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入
推挽输出:高低电平都有很强的驱动能力,使得SPI引脚信号的下降沿和上升沿非常迅速
(IIC因为要实现半双工,经常切换输出输入,IIC又要实现多主机的时钟同步和总线仲裁,若使用推挽输出任意电源短路)
SPI的MISO可能有冲突,一位内主机是输入,三个从机都是输出,若三个从机始终是推挽输出,势必会导致冲突。
故SPI有个规定:
当从机的SS引脚为高电平时,即从机未被选中,其MISO引脚必须切换成高阻态,高阻态相当于引脚断开,不输出任何电平,这样可以防止一条线有多个输出,导致电平冲突问题
SS为低电平时,MISO才允许变为推挽输出(切换在从机中,不需要关注)

1.2通信原理

SPI主设备和从设备都有一个串行移位寄存器,主设备通过向它的SPI串行寄存器写入一个字节来发起一次传输。

SPI数据通信的流程可以分为以下几步:

1、主设备发起信号,将CS/SS拉低,启动通信。

2、主设备通过发送时钟信号,来告诉从设备进行写数据或者读数据操作(采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低),因为SPI有四种模式,后面会讲到),它将立即读取数据线上的信号,这样就得到了一位数据(1bit)。

3、主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(缓存长度不一定,看单片机配置),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。

4、从机(Slave)也将自己的串行移位寄存器(缓存长度不一定,看单片机配置)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。

1.3SPI时序基本单元

1.3.1起始和终止

起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平

1.3.2交换字节

Mode0:CPOL=0,CPHA =0:当空闲态时,SCK处于低电平,数据采样是在第1个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。

Mode1:CPOL=0,CPHA=1:当空闲态时,SCK处于低电平,数据发送是在第2个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

Mode2:CPOL=1,CPHA=0:当空闲态时,SCK处于高电平,数据采集是在第1个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

Mode3:CPOL=1,CPHA=1:当空闲态时,SCK处于高电平,数据发送是在第2个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

二、W25Q64

2.1W25Q64简介

W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景
存储介质:Nor Flash(闪存)
时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
存储容量(24位地址):
W25Q40: 4Mbit / 512KByte
W25Q80: 8Mbit / 1MByte
W25Q16: 16Mbit / 2MByte
W25Q32: 32Mbit / 4MByte
W25Q64: 64Mbit / 8MByte
W25Q128: 128Mbit / 16MByte
W25Q256: 256Mbit / 32MByte

2.2W25Q64硬件电路

2.3W25Q64框图

2.4Flash操作注意事项

写入操作时:
写入操作前,必须先进行写使能
每个数据位只能由1改写为0,不能由0改写为1
写入数据前必须先擦除,擦除后,所有数据位变为1
擦除必须按最小擦除单元进行
连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:
直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

三、软件SPI读写W25Q64

3.1接线图

3.2程序代码

MySPI.c

#include "stm32f10x.h"                  // Device headervoid MySPI_W_CS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);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);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);MySPI_W_CS(1);MySPI_W_SCK(0);
}void MySPI_Start(void)
{MySPI_W_CS(0);
}void MySPI_Stop(void)
{MySPI_W_CS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}

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

W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"void W25Q64_Init(void)
{MySPI_Init();
}void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(W25Q64_JEDEC_ID);*MID = MySPI_SwapByte(0xFF);*DID = MySPI_SwapByte(0xFF);*DID <<= 8;*DID |= MySPI_SwapByte(0xFF);MySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);Timeout = 100000;while ((MySPI_SwapByte(0xFF) & 0x01) == 0x01){Timeout --;if (Timeout == 0){break;}}MySPI_Stop();
}void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_ChipErase(void)
{W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_CHIP_ERASE);MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint8_t i;W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);for (i = 0; i < Count; i ++){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint8_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);for (i = 0; i < Count; i ++){DataArray[i] = MySPI_SwapByte(0xFF);}MySPI_Stop();
}

W25Q64.h

#ifndef __W25Q64_H
#define __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ChipErase(void);
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint32_t Count);
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 W25Q64_FAST_READ_DUAL_OUTPUT                0x3B
#define W25Q64_FAST_READ_DUAL_IO                    0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT                0x6B
#define W25Q64_FAST_READ_QUAD_IO                    0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO              0xE3#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;
uint16_t DID;uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];int main(void)
{OLED_Init();W25Q64_Init();OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");W25Q64_ReadID(&MID, &DID);OLED_ShowHexNum(1, 5, MID, 2);OLED_ShowHexNum(1, 12, DID, 4);W25Q64_SectorErase(0x000000);W25Q64_PageProgram(0x000000, ArrayWrite, 4);W25Q64_ReadData(0x000000, ArrayRead, 4);OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}

STM-32:SPI通信协议/W25Q64简介—软件SPI读写W25Q64相关推荐

  1. 三大通信协议(3)SPI——寄存器配置

    目录 一.SPI通信协议简介 二.SPI通信时序 1.主从通信 2.模式选择 三.实例 总结 一.SPI通信协议简介 SPI是串行外设接口(Serial Peripheral Interface)的缩 ...

  2. SPI通信协议详解(二)

    1.SPI简介 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口.是Motorola首先在其MC68HCXX系列处理器上定义的.SPI接口主要应 ...

  3. Verilog实现的SPI通信协议(主机模式)

    一.前言 最近在使用FPGA调试一个MCP2515CAN芯片的时候,需要用到SPI通信协议,也在网上看了许多不同人写的博客,也学习了很多种不同的写法,从结果来看,网上给出的大部分例子都能实现SPI通信 ...

  4. 【51单片机快速入门指南】5:软件SPI

    目录 硬知识 SPI协议简介 SPI接口介绍 SPI接口连接图 SPI数据传输方向 SPI传输模式 软件SPI程序源码 Soft_SPI.c Soft_SPI.h 普中51-单核-A2 STC89C5 ...

  5. 【嵌入式】STM32基于SPI通信协议OLED屏显示

    STM32基于SPI通信协议OLED屏显示 一.SPI协议和OLED介绍 1.SPI协议介绍 物理层 协议层 2.OLED显示屏介绍 二.显示个人学号姓名实验 1.题目要求 2.代码部分 1.完整代码 ...

  6. spi通信协议_arduino的SPI通信

    SPI通信简介 SPI全称Serial Peripheral Interface,即串行外设接口. 由Motorola公司提出的一种同步串行数据传输标准. 所谓同步,即数据收发双方共用一个时钟: 所谓 ...

  7. GD32F303调试小记(二)之SPI(软件SPI、硬件SPI、硬件SPI+DMA)

    前言 目前有一个项目中用到了TFT-LCD,其驱动芯片为ILI9341.为更好的达到显示效果,在最终的代码中我们会使用单片机自带的硬件SPI+DMA模块(由于调试过程中SPI+DMA输出的波形没能驱屏 ...

  8. STM32L475 硬件SPI+软件SPI驱动ST7789V2

    前言 最近购买了IoT Board 潘多拉开发板来研究,学习使用STM32CubeMX工具配置SPI,然后驱动了TFTLCD.潘多拉开发板的TFTLCD驱动IC是ST7789V2,结合原子哥的TFTL ...

  9. STM32 SPI通信协议详细讲解—小白入门

    文章目录 (一)SPI协议简介 (二)SPI物理层 (三)SPI协议层 3.1.SPI基本通信过程 3.2.通信的起始和终止信号 3.3.数据有效性 3.4.CPOL/CPHA及通信模式 (四)STM ...

最新文章

  1. 【Kali渗透全方位实战】子域名的概念与探测(DNSenum工具和Sublist3r脚本的使用)
  2. 【笛卡尔树】【树状数组】Beautiful Pair(P4755)
  3. windows mobile 上面固定比例图像缩放
  4. 语音识别如何识别中英混杂的语句?或者别的不同语言混合的语句?
  5. 自动驾驶_感知_目标检测(激光雷达)
  6. 自适应滤波器(Adaptive Filter)(1)--简介
  7. html5文档加载前调用函数,html调用javascript外部文件显示函数未定义
  8. 【复杂网络】当机器学习遇上复杂网络:解析微信朋友圈 Lookalike 算法
  9. C. Alternating Sum(数论——等比数列公式与变换)
  10. ubuntu18.04 下海康工业相机hikrobot_camera的使用及问题的解决
  11. vue将链接转为二维码的方法即插件
  12. Microsoft Access 数据库和表
  13. 第十三章 相关方管理 权利利益方格 凸显模式 相关方立方体 相关方登记册 相关方参与评估矩阵
  14. git与gerrit基础概念
  15. vue拿到某个节点的属性_vue实现将某个dom元素或组件挂载到根节点
  16. 为什么要做计划管理?因为稳住Flag就能赢
  17. swapidc如何销售服务器,swapidc对接云服务器
  18. 第十七讲:神州三层交换机DHCP服务器配置
  19. 2019 秋招前端面试总结
  20. JavaSE写仿QQ聊天室

热门文章

  1. 华为语音解锁设置_华为手机语音转文字怎么设置,如何完成音频在线转换
  2. 那些让面试官直呼内行的Java知识点(一)
  3. 怎么调大计算机浏览器内字体,电脑浏览器怎么设置字体大小
  4. 京东 vs 苏宁:两个穷人的流血战争
  5. LOL如何拯救小学生
  6. Easyexcel·读取excel
  7. 回文是指正读反读均相同的字符序列;如“abba”和“abdba”均是回文,但“good”不是回文。试写一个算法判定给定的字符序列是否是回文。(提示:将一半字符入栈)
  8. 栈和堆的区别是什么? 为什么说栈的速度快,堆的速度慢?
  9. js用正则表达式完成邮箱验证
  10. 移动目标定位技术笔记1:WiFi、ZigBee、UWB技术