SPI2 ペリフェラル ドライバー - W25Q64 SPI インターフェイスの初期化

序文

(1) このシリーズは STM32 プロジェクト ノートに基づいており、浅いものから深いものまで、さまざまな STM32 ペリフェラルの使用法をカバーしています。

(2) 編集者が使用した MCU は STM32F105RCT6 プロジェクト ノートは編集者の実際のプロジェクトに基づいていますが、ブログの内容はさまざまな MCU を開発する学生の学習と使用に適しています。

学習目標

  1. W25Q64 ハードウェア設計。
  2. SPI 通信プロトコルを学習します。
  3. 25Q64 チップの SPI ドライバー プログラミングを完了します。

ハードウェア回路図

上の図から、25Q64 がマイクロコントローラーの SPI2 インターフェイスに接続されており、SPI2 を介して通信していることがわかります。

SPI通信の原理を簡単に紹介(理解)

代表的な配線図

簡単な原理分析

SCK:SPIの通信速度、つまりデータ伝送速度を決定します。

データ: 1 ハイレベル 0 ローレベル。

SPIの4つの通信モード

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

上記の情報をご覧いただけます。

まず最初に、2 つの特殊レジスターが CPOL (Clock POLarity) と CPHA (Clock PHAse) であることを理解する必要があります。

CPOL: SPI バスの極性を設定します。

CPHA: SPI バスの位相を構成します。

SPI バス極性の概念: アイドル時にクロック信号が High か Low か

CPOL = 1; SCK アイドル状態は High

CPOL = 0; SCK アイドル状態は低い

SPIバスの位相の概念

1 クロック サイクルには 2 つの遷移エッジがあり、どの遷移からデータ収集を開始するかは位相によって決まります。

CPHA = 0; 最初のジャンプから収集を開始することを意味します

CPHA = 1; 2 番目の遷移から収集を開始することを意味します

SPIの4つのモード

モード 0: CPOL = 0; CPHA = 0;

モード 1: CPOL = 0; CPHA = 1;

モード 2: CPOL = 1; CPHA = 0;

モード 3: CPOL = 1; CPHA = 1;

データ送信方向

MSB ファースト: MSB

LSBファースト:LSB

SPI単線および2線モード

単一回線: 通常、OLED スクリーンの一方向通信に使用されます。

双方向: 通常、チップ間の双方向通信に使用されます。

特記事項: 通常の状況では、4 つのモードの詳細を意識的に知る必要はありませんが、一般に、チップがサポートするモードはチップ情報からわかります。

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 が初期化され、High にプルされます。

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

おすすめ

転載: blog.csdn.net/weixin_62261692/article/details/132501741