STM32-笔记39-SPI-W25Q128

一、什么是SPI?

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

二、有I2C为什么要使用SPI

1. IIC 是半双工通讯,无法同时收发信息;SPI 是全双工通讯,可以同时收发信息;
2. IIC 通讯协议较复杂,而 SPI 通讯协议较简单;
3. IIC 需要通过地址选择从机,而 SPI 只需一个引脚即可选中从机;
4. IIC 通讯速率一般为 100kHz 左右,而 SPI 可以达到 50MHz ;
5. IIC 需要的通讯线较少,而 SPI 需要较多。

三、SPI物理架构

SPI 总线包含 4 条通讯线,分别为 SS、SCK、MOSI、MISO。

它们的作用介绍如下 :

(1) MISO – Master Input Slave Output,主设备数据输入,从设备数据输出

(2) MOSI – Master Output Slave Input,主设备数据输出,从设备数据输入

(3) SCK – Serial Clock,时钟信号,由主设备产生

(4) CS – Chip Select,片选信号,由主设备控制 STM32F1 系列芯片有 3 个SPI 接口。

四、SPI工作模式

时钟极性(CPOL): 没有数据传输时时钟线的空闲状态电平

0:SCK在空闲状态保持低电平

1:SCK在空闲状态保持高电平

时钟相位(CPHA): 时钟线在第几个时钟边沿采样数据

0:SCK的第一(奇数)边沿进行数据位采样,数据在第一个时钟边沿被锁存

1:SCK的第二(偶数)边沿进行数据位采样,数据在第二个时钟边沿被锁存

模式 0 和模式 3 最常用。
模式 0 时序图:

模式 3 时序图:

五、SPI寄存器及库函数介绍

5.1 SPI控制寄存器 1(SPI_CR1)(I 2 S模式下不使用)

5.2 SPI控制寄存器 2(SPI_CR2)

5.3 SPI 状态寄存器(SPI_SR)

 

5.4 SPI 数据寄存器(SPI_DR)

5.5 库函数

HAL_SPI_Init() ;//初始化SPI函数,主要配置CR1和CR2寄存器

HAL_SPI_TransmitReceive(); //普通的收发SPI(小数据量)

HAL_SPI_TransmitReceive_DMA();//DMA搬运收发SPI(大数据量)

HAL_SPI_TransmitReceive_IT(); //中断收发IT(在中断中用)

六、什么是W25Q128?

一般我们使用存储器,都是ARM、ROM、FLASH

W25Q128是NOR Flash:一种非易失性存储器,它可以在断电或掉电后仍然保持存储的数据,因此被广泛应用于长期数据存储。它具有容量大,可重复擦写、按“扇区/块”擦除的特性。

Flash 是有一个物理特性:只能写 0 ,不能写 1 ,写 1 靠擦除。

W25Q128是华邦公司推出的一款容量为 128M-bit(相当于 16M-byte)的 SPI 接口的 NOR Flash 芯片。

6.1 W25Q128存储架构

W25Q128 将 16M 的容量分为 256 个块(block),每块 64K 字节;每块分为 16 个扇区(sector),一扇区 4K 字节;每扇区分为 16 个页(page),一页 256 字节。

W25Q128 的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。这样我们需要给 W25Q128 开辟一个至少 4K 的缓存区。

6.2 W25Q128常用指令

具体工作时序如下:

  • 写使能 (06H)

执行页写,扇区擦除,块擦除,片擦除,写状态寄存器等指令前,需要写使能。

拉低 CS 片选 → 发送 06H → 拉高 CS 片选

  • 读SR1(05H)

拉低 CS 片选 → 发送 05H → 返回SR1的值 → 拉高 CS 片选

  • 读数据(03H)

拉低 CS 片选 → 发送 03H → 发送24位地址 → 读取数据(1~n)→ 拉高 CS 片选

  • 页写 (02H)

页写命令最多可以向FLASH传输256个字节的数据。

拉低 CS 片选 → 发送 02H → 发送24位地址 → 发送数据(1~n)→ 拉高 CS 片选

  • 扇区擦除(20H)

写入数据前,检查内存空间是否全部都是 0xFF ,不满足需擦除。

拉低 CS 片选 → 发送 20H→ 发送24位地址 → 拉高 CS 片选

6.3 W25Q128状态寄存器

七、W25Q128实验

实验目的

读写W25Q128

复制项目文件19-串口打印功能

重命名为50-读写W25Q128实验

加载文件

main,c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "w25q128.h"uint8_t data_write[4] = {0xAA, 0xBB, 0xCC, 0xDD};
uint8_t data_read[4] = {0};
int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* 初始化LED灯 */uart1_init(115200);w25q128_init();printf("hello world!\r\n");uint16_t device_id = w25q128_read_id();printf("device id: %X\r\n", device_id);w25q128_erase_sector(0x000000);w25q128_write_page(0x000000, data_write, 4);w25q128_read_data(0x000000, data_read, 4);printf("data read: %X, %X, %X, %X\r\n", data_read[0], data_read[1], data_read[2], data_read[3]);while(1){ }
}

w25q128.c

#include "w25q128.h"SPI_HandleTypeDef spi_handle = {0};
void w25q128_spi_init(void)
{spi_handle.Instance = SPI1;//指定哪个SPI?spi_handle.Init.Mode = SPI_MODE_MASTER;//指定主设备还是从设备?主设备spi_handle.Init.Direction = SPI_DIRECTION_2LINES;//全双工还是半双工?全双工spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;//数据长度?8bytespi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;  极性         /* CPOL = 0 */spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;                  /* CPHA = 0 */spi_handle.Init.NSS = SPI_NSS_SOFT;spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;//波特率分频-256spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;//指定高位先行还是低位先行:高位先行spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;spi_handle.Init.CRCPolynomial = 7;HAL_SPI_Init(&spi_handle);
}void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{if(hspi->Instance == SPI1){GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOA_CLK_ENABLE();                           // 使能GPIOB时钟__HAL_RCC_SPI1_CLK_ENABLE();//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_4;          gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;           gpio_initstruct.Pull = GPIO_PULLUP;                    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;          HAL_GPIO_Init(GPIOA, &gpio_initstruct);gpio_initstruct.Pin = GPIO_PIN_5 | GPIO_PIN_7;          gpio_initstruct.Mode = GPIO_MODE_AF_PP;           HAL_GPIO_Init(GPIOA, &gpio_initstruct);gpio_initstruct.Pin = GPIO_PIN_6;          gpio_initstruct.Mode = GPIO_MODE_INPUT;           HAL_GPIO_Init(GPIOA, &gpio_initstruct);}
}
//交换字节 -- 写字节操作
uint8_t w25q128_spi_swap_byte(uint8_t data)
{uint8_t recv_data = 0;HAL_SPI_TransmitReceive(&spi_handle, &data, &recv_data, 1, 1000);return recv_data;
}
//初始化W25Q128
void w25q128_init(void)
{w25q128_spi_init();
}
//读W25Q128的id
uint16_t w25q128_read_id(void)
{uint16_t device_id = 0;W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_ManufactDeviceID);w25q128_spi_swap_byte(0x00);w25q128_spi_swap_byte(0x00);w25q128_spi_swap_byte(0x00);device_id = w25q128_spi_swap_byte(FLASH_DummyByte) << 8;device_id |= w25q128_spi_swap_byte(FLASH_DummyByte);W25Q128_CS(1);return device_id;
}
//W25Q128写使能
void w25q128_writ_enable(void)
{W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_WriteEnable);W25Q128_CS(1);
}
//W25Q128读寄存器(SR1)
uint8_t w25q128_read_sr1(void)
{uint8_t recv_data = 0;W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_ReadStatusReg1);recv_data = w25q128_spi_swap_byte(FLASH_DummyByte);W25Q128_CS(1);return recv_data;
}
//忙等待 - 等待空闲
void w25q128_wait_busy(void)
{while((w25q128_read_sr1() & 0x01) == 0x01);
}
//传入地址
void w25q128_send_address(uint32_t address)
{w25q128_spi_swap_byte(address >> 16);w25q128_spi_swap_byte(address >> 8);w25q128_spi_swap_byte(address);
}
//读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size)
{uint32_t i = 0;W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_ReadData);w25q128_send_address(address);for(i = 0; i < size; i++)data[i] = w25q128_spi_swap_byte(FLASH_DummyByte);W25Q128_CS(1);
}
//页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size)
{uint16_t i = 0;w25q128_writ_enable();W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_PageProgram);w25q128_send_address(address);for(i = 0; i < size; i++)w25q128_spi_swap_byte(data[i]);W25Q128_CS(1);//等待空闲w25q128_wait_busy();
}
//扇区擦除
void w25q128_erase_sector(uint32_t address)
{//写使能w25q128_writ_enable();//等待空闲w25q128_wait_busy();//拉低片选W25Q128_CS(0);//发送扇区擦除指令w25q128_spi_swap_byte(FLASH_SectorErase);//发送地址w25q128_send_address(address);//拉高片选W25Q128_CS(1);//等待空闲w25q128_wait_busy();
}

w25q128.h

#ifndef __W25Q128_H__
#define __W25Q128_H__#include "sys.h"#define W25Q128_CS(x)   do{ x ? \HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET): \HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); \}while(0)/* 指令表 */
#define FLASH_ManufactDeviceID                  0x90
#define FLASH_WriteEnable                       0x06
#define FLASH_ReadStatusReg1                    0x05
#define FLASH_ReadData                          0x03
#define FLASH_PageProgram                       0x02
#define FLASH_SectorErase                       0x20
#define FLASH_DummyByte                         0xFFvoid w25q128_init(void);
uint16_t w25q128_read_id(void);
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size);
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size);
void w25q128_erase_sector(uint32_t address);#endif

 效果实现

FE77

0xAA, 0xBB, 0xCC, 0xDD

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

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

相关文章

【微服务】8、分布式事务 ( XA 和 AT )

文章目录 利用Seata解决分布式事务问题&#xff08;XA模式&#xff09;AT模式1. AT模式原理引入2. AT模式执行流程与XA模式对比3. AT模式性能优势及潜在问题4. AT模式数据一致性解决方案5. AT模式一阶段操作总结6. AT模式二阶段操作分析7. AT模式整体特点8. AT模式与XA模式对比…

CTF知识点总结(三)

空格绕过方式&#xff1a; $IFS ${IFS} $IFS$数字 < <> 三种绕过方式&#xff1a; 1.sh /?ip127.0.0.1;echo$IFS$2Y2F0IGZsYWcucGhw|base64$IFS$2-d|sh 2.变量拼接 /?ip127.0.0.1;ag;cat$IFS$2fla$a.php 3.内联注释(将反引号命令的结果作为输入来执行命令) /?i…

《Spring Framework实战》5:Spring Framework 概述

欢迎观看《Spring Framework实战》视频教程 Spring 使创建 Java 企业应用程序变得容易。它为您提供一切 需要在企业环境中采用 Java 语言&#xff0c;并支持 Groovy 和 Kotlin 作为 JVM 上的替代语言&#xff0c;并且可以灵活地创建许多 类型的架构。从 Spring Framework 6.0 开…

解决npm报错:sill idealTree buildDeps

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 报错信息 使用 npm 安装依赖时报错&#xff1a;sill idealTree buildDeps 解决方案 请按照以下步骤进行相关操作&#xff1a; 1、删除 C:\Users{账户}\ 文件夹中的 .npm…

formik 的使用

礼记有言&#xff1a;独学而无友&#xff0c;则孤陋而寡闻 让我们一起了解更多便捷方法&#xff0c;缩短开发时间去摸鱼&#xff0c;嘿嘿。 框架&#xff1a;react 在写表单的时候&#xff0c;我不太喜欢把验证写的很繁琐&#xff0c;这里讲介绍&#xff0c;验证表单的非常好用…

JVM实战—OOM的生产案例

1.每秒仅上百请求的系统为何会OOM(RPC超时时间设置过长导致QPS翻几倍) (1)案例背景 在这个案例中&#xff0c;一个每秒仅仅只有100请求的系统却因频繁OOM而崩溃。这个OOM问题会涉及&#xff1a;Tomcat底层工作原理、Tomcat内核参数的设置、服务请求超时时间。 (2)系统发生OOM的…

数字IC设计高频面试题

在数字IC设计领域&#xff0c;面试是评估候选人技术能力和问题解决能力的重要环节。数字IC设计的复杂性和要求在不断提高。面试官通常会提出一系列面试题&#xff0c;以考察应聘者在数字设计、验证、时钟管理、功耗优化等方面的专业知识和实践经验。 这些题目不仅涉及理论知识…

OSI模型的网络层中产生拥塞的主要原因?

&#xff08; 1 &#xff09;缓冲区容量有限&#xff1b;&#xff08; 1.5 分&#xff09; &#xff08; 2 &#xff09;传输线路的带宽有限&#xff1b;&#xff08; 1.5 分&#xff09; &#xff08; 3 &#xff09;网络结点的处理能力有限&#xff1b;&#xff08; 1 分…

用OpenCV实现UVC视频分屏

分屏 OpencvUVC代码验证后话 用OpenCV实现UVC摄像头的视频分屏。 Opencv opencv里有很多视频图像的处理功能。 UVC Usb 视频类&#xff0c;免驱动的。视频流格式有MJPG和YUY2。MJPG是RGB三色通道的。要对三通道进行分屏显示。 代码 import cv2 import numpy as np video …

备战蓝桥杯 链表详解

链表概念 上一次我们用顺序存储实现了线性表&#xff0c;这次我们用链式存储结构实现的线性表就叫链表 链表每个节点包含数据本身和下一个节点和上一个节点的地址 链表的分类 单链表 双链表 带头链表 不带头链表 循环链表等等 我们竞赛一般都用的是带头链表 双向链表的…

DeepSeek:性能强劲的开源模型

deepseek 全新系列模型 DeepSeek-V3 首个版本上线并同步开源。登录官网 chat.deepseek.com 即可与最新版 V3 模型对话。 性能对齐海外领军闭源模型​ DeepSeek-V3 为自研 MoE 模型&#xff0c;671B 参数&#xff0c;激活 37B&#xff0c;在 14.8T token 上进行了预训练。 论…

Redis Zset有序集合

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Redis Zset有序集合 收录于专栏[redis] 本专栏旨在分享学习Redis的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 概述 普通命令 ZAD…

Python中的可变对象与不可变对象;Python中的六大标准数据类型哪些属于可变对象,哪些属于不可变对象

Python中的可变对象与不可变对象&#xff1b;Python中的六大标准数据类型哪些属于可变对象&#xff0c;哪些属于不可变对象 Python中的可变对象与不可变对象一、Python的六大标准数据类型1. 数字类型 (Number)2. 字符串 (String)3. 列表 (List)4. 元组 (Tuple)5. 集合 (Set)6. …

Unity 2d描边基于SpriteRender,高性能的描边解决方案

目标 以Unity默认渲染管线为例&#xff0c;打造不需要图片内边距&#xff0c;描边平滑&#xff0c;高性能的描边解决方案 前言 在2d游戏中经常需要给2d对象添加描边&#xff0c;来突出强调2d对象 当你去网上查找2d描边shader&#xff0c;移植到项目里面&#xff0c;大概率会…

Oracle OCP考试常见问题之线上考试流程

首先要注意的是&#xff1a;虽然Oracle官方在国际上取消了获得OCP认证需要培训记录的要求&#xff0c;但在中国区&#xff0c;考生仍然需要参加Oracle的官方或者其合作伙伴组织的培训&#xff0c;并且由Oracle授权培训中心向Oracle提交学员培训记录。考生只有在完成培训并通过考…

基于海思soc的智能产品开发(camera sensor的两种接口)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于嵌入式开发设备来说&#xff0c;除了图像显示&#xff0c;图像输入也是很重要的一部分。说到图像输入&#xff0c;就不得不提到camera。目前ca…

Redis 笔记(二)-Redis 安装及测试

一、什么是 Redis 中文网站 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用 ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value&#xff0c;并提供多种语言的 API。 Redis 开源&#xff0c;遵循 BSD 基…

H2数据库在单元测试中的应用

H2数据库特征 用比较简洁的话来介绍h2数据库&#xff0c;就是一款轻量级的内存数据库&#xff0c;支持标准的SQL语法和JDBC API&#xff0c;工业领域中&#xff0c;一般会使用h2来进行单元测试。 这里贴一下h2数据库的主要特征 Very fast database engineOpen sourceWritten…

通俗易懂之线性回归时序预测PyTorch实践

线性回归&#xff08;Linear Regression&#xff09;是机器学习中最基本且广泛应用的算法之一。它不仅作为入门学习的经典案例&#xff0c;也是许多复杂模型的基础。本文将全面介绍线性回归的原理、应用&#xff0c;并通过一段PyTorch代码进行实践演示&#xff0c;帮助读者深入…

MATLAB深度学习实战文字识别

文章目录 前言视频演示效果1.DB文字定位环境配置安装教程与资源说明1.1 DB概述1.2 DB算法原理1.2.1 整体框架1.2.2 特征提取网络Resnet1.2.3 自适应阈值1.2.4 文字区域标注生成1.2.5 DB文字定位模型训练 2.CRNN文字识别2.1 CRNN概述2.2 CRNN原理2.2.1 CRNN网络架构实现2.2.2 CN…