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
- W25Q64 hardware design.
- Learn the SPI communication protocol.
- 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; //返回收到的数据
}