SPI2外设驱动-W25Q64 SPI接口初始化

前言

(1)本系列是基于STM32的项目笔记,内容涵盖了STM32各种外设的使用,由浅入深。

(2)小编使用的单片机是STM32F105RCT6,项目笔记基于小编的实际项目,但是博客中的内容适用于各种单片机开发的同学学习和使用。

学习目标

  1. W25Q64硬件设计。
  2. 学习SPI通讯协议。
  3. 完成25Q64芯片的SPI驱动程序编写。

硬件原理图

从上图可以看出 25Q64连接的是单片机的SPI2接口,通过SPI2来通讯的。

SPI通讯原理简单介绍(理解)

典型连线图

简单原理分析

SCK:决定SPI的通信速率,即 数据传输速率。

数据:1高电平 0 低电平。

SPI的四种通讯模式

https://mp.weixin.qq.com/s/ytAad2jdKczzdhD3b92apA

可以看一下上面的资料。

首先我们要了解两个特殊寄存器 分别是 CPOL (Clock POlarity)和 CPHA (Clock PHAse)。

CPOL:配置SPI总线的极性

CPHA:配置SPI总线的相位

SPI总线极性的概念: 空闲的时候时钟信号是高电平还是低电平

CPOL = 1; SCK 空闲是高电平

CPOL = 0; SCK 空闲是低电平

SPI总线的相位的概念

一个时钟周期有2个跳变沿,相位决定从那个跳变开始采集数据

CPHA = 0; 表示从第一个跳变 开始采集

CPHA = 1; 表示从第二个跳变 开始采集

SPI四种模式

模式0: CPOL = 0; CPHA = 0;

模式1:CPOL = 0; CPHA = 1;

模式2:CPOL = 1; CPHA = 0;

模式3:CPOL = 1; CPHA = 1;

数据传输方向

高位在前:MSB

低位在前: LSB

SPI的单线 和双线 模式

单线:一般用于OLED屏幕单向通讯

双向:一般用于芯片之间的双向通讯

特别说明: 一般情况下,我们不用刻意去学习四种模式的具体细节,一般芯片资料里面都会告诉你芯片支持的模式。

25Q64 SPI2的初始化操作

hal_flash.c代码

#include "stm32F10x.h"
#include "hal_flash.h"void hal_spi2Init(void)
{SPI_InitTypeDef  SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* Enable SPI2 and GPIOA clocks */RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);/* Configure SPI2 pins: NSS, SCK, MISO and MOSI */GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN | SPI2_MISO_PIN | SPI2_MOSI_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(SPI2_SCK_PORT, &GPIO_InitStructure);//SPI2 NSS GPIO_InitStructure.GPIO_Pin = SPI2_NSS_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(SPI2_NSS_PORT, &GPIO_InitStructure);GPIO_SetBits(SPI2_NSS_PORT,SPI2_NSS_PIN);/* SPI2 configuration */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI1设置为两线全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	                     //设置SPI1为主模式SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  //SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;	 		                   //串行时钟在不操作时,时钟为高电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		                   //第二个时钟沿开始采样数据SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;			                     //NSS信号由软件(使用SSI位)管理SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;				         //数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7;						               //CRC值计算的多项式SPI_Init(SPI2, &SPI_InitStructure);/* Enable SPI2  */SPI_Cmd(SPI2, ENABLE); 											  //使能SPI2外设hal_spi2CSDrive(1);//空闲时将片选信号拉高,初始化为空闲状态}  void hal_spi2CSDrive(unsigned char sta)
{if(sta)GPIO_SetBits(SPI2_NSS_PORT,SPI2_NSS_PIN);		elseGPIO_ResetBits(SPI2_NSS_PORT,SPI2_NSS_PIN);
}//SPIx 读写一个字节
//返回值:读取到的字节
unsigned char  hal_spi2ReadWriteByte(unsigned char  TxData)
{		unsigned char retry=0;				 while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET)//等待发送区空	{retry++;if(retry>200)return 0;}	SPI_I2S_SendData(SPI2,TxData);	retry=0;while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET)//等待发送区空	{retry++;if(retry>200)return 0;}	  						    return SPI_I2S_ReceiveData(SPI2);//SPI2->DR;          //返回收到的数据						    
}

hal_flash.h代码

#ifndef _HAL_FLASH_H
#define _HAL_FLASH_H#define SPI2_SCK_PORT       GPIOB
#define SPI2_SCK_PIN        GPIO_Pin_13#define SPI2_MOSI_PORT       GPIOB
#define SPI2_MOSI_PIN        GPIO_Pin_15#define SPI2_MISO_PORT       GPIOB
#define SPI2_MISO_PIN        GPIO_Pin_14#define SPI2_NSS_PORT       GPIOB
#define SPI2_NSS_PIN        GPIO_Pin_12void hal_spi2Init(void);
void hal_spi2CSDrive(unsigned char sta);
unsigned char  hal_spi2ReadWriteByte(unsigned char  TxData);#endif

SPI2接口初始化流程(拆解代码分析)

● 定义SPI通讯的端口

● 打开相关时钟

● 初始化SPI2相关的GPIO口

● 初始化SPI2相关参数

● 片选CS初始化 拉高

定义SPI通讯的端口
#define SPI2_SCK_PORT       GPIOB
#define SPI2_SCK_PIN        GPIO_Pin_13#define SPI2_MOSI_PORT       GPIOB
#define SPI2_MOSI_PIN        GPIO_Pin_15#define SPI2_MISO_PORT       GPIOB
#define SPI2_MISO_PIN        GPIO_Pin_14#define SPI2_NSS_PORT       GPIOB//其实就是CS,片选引脚
#define SPI2_NSS_PIN        GPIO_Pin_12
打开相关时钟
/* Enable SPI2 and GPIOA clocks */RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
初始化SPI2相关的GPIO口
/* Configure SPI2 pins: NSS, SCK, MISO and MOSI */GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN | SPI2_MISO_PIN | SPI2_MOSI_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(SPI2_SCK_PORT, &GPIO_InitStructure);//SPI2 NSS   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB,GPIO_Pin_12);
初始化SPI2相关参数
/* SPI2 configuration */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI2设置为两线全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	   //设置SPI2为主模式SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;   //SP2发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;	//串行时钟在不操作时,时钟为高电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//第二个时钟沿开始采样数据SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件(使用SSI位)管理SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7;		//CRC值计算的多项式SPI_Init(SPI2, &SPI_InitStructure);/* Enable SPI2  */SPI_Cmd(SPI2, ENABLE); 					//使能SPI2外设
25Q64片选操作,拉高
void hal_spi2CSDrive(unsigned char sta)
{if(sta)GPIO_SetBits(GPIOB,GPIO_Pin_12);		elseGPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
SPI数据读写函数
SPI读写数据操作原理

SPI 读写操作图示分析

代码分析
//SPIx 读写一个字节
//返回值:读取到的字节
unsigned char  hal_spi2ReadWriteByte(unsigned char  TxData)
{		unsigned char retry=0;				 while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET)//等待发送区空	{retry++;if(retry>200)return 0;}	SPI_I2S_SendData(SPI2,TxData);	retry=0;while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET)//	
{retry++;if(retry>200)return 0;}	  						    return SPI_I2S_ReceiveData(SPI2);//SPI2->DR;          //返回收到的数据			    
}

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

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

相关文章

上传镜像到阿里云的ACR

1、开通阿里云ACR 2、在ACR 中创建命名空间 3、本地安装docker 4、登录到 开通ACR,需要配置访问凭证 [rootmaster ~]# docker login --username***lb registry.cn-beijing.aliyuncs.com Password: 5、给镜像打标签 [rootmaster ~]# docker images REPOSITORY …

leetcode496. 下一个更大元素 I 【单调栈】

【简单题】&#xff08;暴力遍历法很简单&#xff09;但是时间复杂度很高&#xff0c;n的立方级别了。。。 代码&#xff1a; class Solution { public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {vector<int&g…

【网络安全】防火墙知识点全面图解(三)

本系列文章包含&#xff1a; 【网络安全】防火墙知识点全面图解&#xff08;一&#xff09;【网络安全】防火墙知识点全面图解&#xff08;二&#xff09;【网络安全】防火墙知识点全面图解&#xff08;三&#xff09; 防火墙知识点全面图解&#xff08;三&#xff09; 39、什…

猜数游戏-Rust版

cargo new guessing_game 创建项目 输入任意内容&#xff0c;并打印出来 main.rs: use std::io; // 像String这些类型都在预先导入的prelude里&#xff0c;如果要使用的不在prelude里&#xff0c;则需要显式导入fn main() { println!("猜数"); println!("…

Rust常用加密算法

哈希运算(以Sha256为例) main.rs: use crypto::digest::Digest;use crypto::sha2::Sha256;fn main() { let input "dashen"; let mut sha Sha256::new(); sha.input_str(input); println!("{}", sha.result_str());} Cargo.toml: [package]n…

uniapp,使用canvas制作一个签名版

先看效果图 我把这个做成了页面&#xff0c;没有做成组件&#xff0c;因为之前我是配合uview-plus的popup弹出层使用的&#xff0c;这种组件好像是没有生命周期的&#xff0c;第一次打开弹出层可以正常写字&#xff0c;但是关闭之后再打开就不会显示绘制的线条了&#xff0c;还…

波奇学C++:stl的list模拟实现

list是双向带头链表。所以迭代器end()相当于哨兵卫的头。 list不支持和[]重载&#xff0c;原因在于list空间不是连续的&#xff0c;和[]的代价比较大。 访问第n个节点&#xff0c;只能用for循环&#xff0c;来实现 list<int> l; l.push_back(0); l.push_back(1); l.pu…

C++信息学奥赛1130:找第一个只出现一次的字符

这段代码的功能是找出输入字符串中第一个重复出现的字符&#xff0c;并输出该字符。 解析注释后的代码如下&#xff1a; #include<bits/stdc.h> using namespace std; int main() {string arr;getline(cin, arr); int a0;for(int i0;i<arr.length();i){for(int j0;j…

NC65 树表型参照 搜索全部 按钮点击事件后获取sql的方法

NC65 树表型参照 搜索全部 按钮点击事件后获取sql的方法。 /*** 返回 UIbtnLocQuery 特性值。* * return nc.ui.pub.beans.UIButton*/ /* 警告&#xff1a;此方法将重新生成。 */ private nc.ui.pub.beans.UIButton getUIbtnLocQuery() {// 搜索全部 按钮return getButtonPan…

90. 子集 II

给你一个整数数组 nums &#xff0c;其中可能包含重复元素&#xff0c;请你返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。返回的解集中&#xff0c;子集可以按 任意顺序 排列 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的…

6个主流的工业3D管道设计软件

3D 管道设计软件是大多数行业工程工作的主要部分&#xff0c;例如&#xff1a; 电力、石油和天然气、石化、炼油厂、纸浆和造纸、化学品和加工业。 全球各工程公司使用了近 50 种工厂或管道设计软件。 每个软件都有优点和缺点&#xff0c;包括价格点。 EPC 和业主部门当前的趋势…

C#获取DataTable的前N行数据然后按指定字段排序

获取DataTable的前N行数据然后按指定字段排序 可以使用以下三种代码&#xff1a; 第一种&#xff1a;使用Linq DataTable dtLast dataTable.AsEnumerable().Take(count).OrderBy(dataRow > Convert.ToInt32(dataRow["Sequence"])).CopyToDataTable(); 第二种…