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; //定义波特率预分频的值:波特率预分频值为8
	SPI_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);		
	else
		GPIO_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_12
 

void 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; //定义波特率预分频的值:波特率预分频值为8
	SPI_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);		
	else
		GPIO_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;          //返回收到的数据			    
}
相关推荐
qq_3975623129 分钟前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
6.9438 分钟前
Scala——身份证号码查询籍贯
学习·scala
爱吃西瓜的小菜鸡1 小时前
【C语言】矩阵乘法
c语言·学习·算法
艺术家天选1 小时前
STM32点亮LED灯
stm32·单片机·嵌入式硬件
向阳逐梦1 小时前
基于STM32F4单片机实现ROS机器人主板
stm32·单片机·机器人
初学者7.2 小时前
Webpack学习笔记(2)
笔记·学习·webpack
委员2 小时前
基于NodeMCU的物联网空调控制系统设计
单片机·mcu·物联网·智能家居
小猪写代码2 小时前
STM32 水质水位检测项目 (调试模块)和(延时模块)
stm32·单片机·嵌入式硬件
阿昊真人2 小时前
电源转换模块 arduino
单片机
御风_212 小时前
STM32单片机使用CAN协议进行通信
stm32·单片机·嵌入式硬件