SPI2 peripheral driver - W25Q64 SPI interface initialization

foreword

(1) This series is based on STM32 project notes, covering the use of various STM32 peripherals, from shallow to deep.

(2) The MCU used by the editor is STM32F105RCT6. The project notes are based on the actual project of the editor, but the content in the blog is suitable for the students who develop various MCUs to learn and use.

learning target

  1. W25Q64 hardware design.
  2. Learn the SPI communication protocol.
  3. Complete the SPI driver programming of 25Q64 chip.

Hardware Schematic

It can be seen from the above figure that the 25Q64 is connected to the SPI2 interface of the microcontroller, and communicates through SPI2.

A brief introduction to the principle of SPI communication (understanding)

Typical wiring diagram

Simple principle analysis

SCK: Determines the communication rate of SPI, that is, the data transmission rate.

Data: 1 high level 0 low level.

Four communication modes of SPI

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

You can look at the information above.

First of all, we need to understand that the two special registers are CPOL (Clock POlarity) and CPHA (Clock PHAse).

CPOL: Configure the polarity of the SPI bus

CPHA: Configure the phase of the SPI bus

The concept of SPI bus polarity: whether the clock signal is high or low when idle

CPOL = 1; SCK idle is high

CPOL = 0; SCK idle is low

The concept of the phase of the SPI bus

There are 2 transition edges in one clock cycle, and the phase determines which transition to start collecting data from

CPHA = 0; means to start collecting from the first jump

CPHA = 1; means to start collecting from the second transition

SPI four modes

Mode 0: CPOL = 0; CPHA = 0;

Mode 1: CPOL = 0; CPHA = 1;

Mode 2: CPOL = 1; CPHA = 0;

Mode 3: CPOL = 1; CPHA = 1;

Data transmission direction

MSB first: MSB

LSB first: LSB

SPI single-wire and two-wire mode

Single line: generally used for one-way communication of OLED screen

Two-way: generally used for two-way communication between chips

Special note: Under normal circumstances, we don’t need to learn the details of the four modes deliberately. Generally, the chip information will tell you the modes supported by the chip.

25Q64 SPI2 initialization operation

hal_flash.c code

#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 code

#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 interface initialization process (disassembly code analysis)

● Define the port for SPI communication

● Turn on the associated clock

● Initialize the GPIO ports related to SPI2

● Initialize SPI2 related parameters

● Chip select CS is initialized and pulled high

Define the port for SPI communication
#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
open the relevant clock
/* Enable SPI2 and GPIOA clocks */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
Initialize the GPIO port related to SPI2
/* 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);


Initialize SPI2 related parameters
/* 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 chip select operation , pull high
void hal_spi2CSDrive(unsigned char sta)
{
	if(sta)
		GPIO_SetBits(GPIOB,GPIO_Pin_12);		
	else
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}

SPI data read and write functions
SPI read and write data operation principle

Graphical analysis of SPI read and write operations

code analysis
//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;          //返回收到的数据			    
}

Guess you like

Origin blog.csdn.net/weixin_62261692/article/details/132501741
SPI