STM32-HAL-SPI-reading and writing W25Q128FV-JEDEC ID(1)

1. SPI serial communication protocol

1.1 Introduction to SPI communication protocol

SPI is a serial peripheral interface, which was first defined by Motorola on its MC68HCxx series processors. SPI is a high-speed, full-duplex, synchronous communication bus, and it only occupies four wires in the management of the chip, which saves the pins of the chip, and at the same time saves space for the layout of the PCB and provides convenience.

1.2 SPI working principle

The communication principle of SPI is very simple. It works in a master-slave mode. This mode usually has a master device and one or more slave devices . At least 4 wires are required. In fact, 3 wires are also possible (the chip select signal can be directly grounded. , selected by default). The 4 lines of SPI are MOSI master output slave device input), MISO master device input slave device output), CK (clock signal), CS (chip select signal, active low).
insert image description here

1.3 SPI characteristics

  • Three-wire full-duplex synchronous transmission
  • 8-bit or 16-bit transmission frame format selection
  • master or slave operation
  • 8 master mode baud rate prescaler coefficients
  • Programmable clock polarity and phase
  • Programmable data order, MSB first or LSB first
  • Dedicated transmit and receive flags for triggerable interrupts
  • SPI bus busy status flag
  • 1-byte transmit and receive buffers supporting DMA function to generate transmit and receive requests

2. Introduction of W25Q128FV chip

2.1 Introduction to the basic parameters of the chip

W25QXX series memory chips are serial Flash memory based on SPI interface, mainly including W25Q64, W25Q128 and so on. The chip supports working voltage between 2.7~3.6V, the current is less than 5mA in normal operation, and less than 1uA in power-off state. The maximum programming speed is 104MHz. Support SPI and QSPI read and write methods.

  • W25Q128 has 256 bytes per page. A page is the smallest readable and writable unit, as well as the smallest unit of programming and erasing.
  • The W25Q128 has a block size of 64KB. A block is a larger unit of storage consisting of multiple pages, each block typically containing 256 pages.
  • If a certain data in a page or block needs to be changed or erased, the entire page or block must be written or erased.
  • The sector size of W25Q128 is 4KB. A sector is a subunit of a block, and each block contains 16 sectors. Changing or erasing data in one sector will not affect data in other sectors.
  • A byte is the smallest addressable unit. A byte is composed of 8 binary bits and can store a character or a number.

2.2 Chip pin introduction

  • Chip pin diagram
    insert image description here
  • pin name
Pin No. Pin name Functional description
1 /CS Chip select input, selected when low level, unselected when high level
2 TO(IO1) Data output (data input and output 1)
3 /WP(IO2) Write protection input (data input and output 2), low level write protection
4 GND land
5 DI(IO0) input input (data input output 0)
6 CLK serial clock
7 /HOLD(IO3) Data hold (data input and output 3) active low
8 VCC Power positive
  • Pin functions of different SPI modes
Pin functions in standard SPI mode CS FROM DP WP HOLD
Pin function in double SPI mode CS 100 IO1 WP HOLD
Pin function in quadruple SPI mode CS 100 IO1 IO2 IO3

2.3 Technical manual and more information

Click to visit the official website
insert image description here

3. The connection circuit of the onboard Flash of the development board

insert image description here
insert image description here
1. It can be seen from the circuit diagram that the power supply voltage of the chip is 3.3V

2. The data line of the chip is 4 lines, connected to SPI1the bus

3. Chip read and write enable use software chip select

  • Chip and MCU pin correspondence table
SPI1_SCK Clock line PB3
F_CS Chip selection (low level selection) PB14
SPI1_MISO Host input slave output PB4
SPI1_MOSI Master output slave input PB5

4. Test preparation

  • Based on STM32F407ZET6the punctual atom development board (Flash chip is in the upper left corner W25Q128FV)

../../_images/stm32f407_explorer.png

  • Install windowsthe system and install Cubemxthe Keil MDKcomputer

5. Initialize the on-chip peripheral SPI1

Due to hardware reasons, it needs to be modified to correspond to hardware pins

5.1 Initialize SPI1

  • Initialize to full-duplex mode (because read and write operations are required)

  • Directly ignore the pins automatically configured by the software, and manually set the corresponding pins of the hardware to the SPI1corresponding pins

insert image description here

  • The clock source is set to an external high-speed clock

insert image description here
【重要】Check the frequency of the onboard crystal oscillator of the development board (set according to the crystal frequency of your own development board), so set the frequency of the input clock to 8Hz, and finally set the frequency to a maximum of 168MHz after frequency division

  • Clock tree setting, after frequency division and multiplication

insert image description here

  • SPI1Parameterization for the bus

insert image description here

  • Configuration parameter description
Mode Full-Duplex Mater full-duplex mode master mode
Frame Format Motorola (Motorola)
Data Size 8 Bit (available from the technical manual of the chip)
First Bit High bit first: MSB First (check the chip's technical manual)
Prescaler(For BaudRate) Can be set to the highest rate: 42.0MBts/s divided by 2 (check the technical manual of the chip)
Clock Polarity Clock polarity is rising edge: Low (check the technical manual of the chip)
Clock Phase The clock phase is 0: 1 Edge (check the technical manual of the chip)
CRC Calucation Do not set CRC check: Disable
NSS Signal Type Set software chip selection: SoftWare
MSB MSB stands for "Most Significant Bit", the most significant bit, which is the highest-valued bit in a binary number, usually on the left. In multi-byte data transfers, the MSB is usually the byte transmitted first.
LSB LSB stands for "Least Significant Bit", the least significant bit, which is the lowest-weight bit in a binary number, usually on the right. In multi-byte data transfers, the LSB is usually the last byte transferred.

Clock Polarity和Clock PhaseThe specific setting method needs to be adjusted according to the device and application scenario
. In this test, due to W25Q128the read-write mode support of the chip Mode 0(0,0)and Mode3(1,1)mode 0 was selected as the read-write mode

insert image description here

Section 6.1 of the chip's data sheet

model CPOL (polarity) CPHA (phase) SCK clock when idle sampling time
mode 0 0 0 low level odd edge
mode 1 0 1 low level even edge
mode 2 1 0 high level odd edge
mode 3 1 1 high level even edge

Mode 0 and Mode 3 are commonly used. After testing, it can be read and written correctly when set to mode 0 or mode 3.

5.2 Set chip select pin PB14

insert image description here

5.3 Configure the serial port printing mode

Refer to the previous configuration article to set up

5.4 Set to generate Keil-MDK code files

6. Read and write chip JEDEC ID

6.1 Chip technical manual and process of reading JEDEC ID

Receive three bytes of data on the SPI bus, respectively indicating the manufacturer ID, device type, and capacity

insert image description here

Chip Jedec ID command

insert image description here

Chip Jedec ID introduction
  • Common types of memory chips and their Jedec IDs
chip type Eater ID
SST25VF016B_ID 0xBF2541
MX25L1606E_ID 0xC22015
W25Q64BV_ID(BV JV FV) 0xEF4017
W25Q128_ID 0xEF4018

Read the JEDEC ID of W25Q128 through the SPI bus, and integrate the JEDEC ID into a 32-bit value. The specific process is as follows:

  1. sf_SetCSEnable the chip select signal of the W25Q128 chip by calling the function.
  2. Put the read ID命令(0x9F)into the send buffer g_spiTxBufand g_spiLenset the send buffer length to 4 (including a read ID command and 3 address bytes).
  3. Call bsp_spiTransferthe function to send the read ID command, and receive 3 bytes of ID data from the DO pin, and store the received data in the g_spiRxBufreceive buffer.
  4. g_spiRxBufRead 3 bytes of ID data from the receive buffer and store them in id1、id2和id3variables respectively.
  5. Disable the chip select signal of the W25Q128 chip by calling sf_SetCSthe function.
  6. The 3-byte ID data is integrated into a 32-bit value uiID, in which id1 occupies the upper 16 bits, id2 occupies the middle 8 bits, and id3 occupies the lower 8 bits.
  7. Return the integrated 32-bit ID value.

6.2 Write JEDEC ID code for reading chip

The code is from Anfulai Electronics

[bsp_flash.c]

#include "bsp_flash.h"
SFLASH_T g_tSF;  //定义结构体

/*
*********************************************************************************************************
*	函 数 名: sf_SetCS
*	功能说明: 串行FALSH片选控制函数
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_SetCS(uint8_t _Level)
{
    
    
	if (_Level == 0)
	{
    
    
		bsp_SpiBusEnter();     
		SF_CS_0();
	}
	else
	{
    
    		
		SF_CS_1();	
		bsp_SpiBusExit();		
	}
}

/*
*********************************************************************************************************
*	函 数 名: sf_ReadInfo
*	功能说明: 读取器件ID,并填充器件参数
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void sf_ReadInfo(void)
{
    
    
	/* 自动识别串行Flash型号 */
	{
    
    
		g_tSF.ChipID = sf_ReadID();	/* 芯片ID */

		switch (g_tSF.ChipID)
		{
    
    
			case SST25VF016B_ID:
				strcpy(g_tSF.ChipName, "SST25VF016B");
				g_tSF.TotalSize = 2 * 1024 * 1024;	/* 总容量 = 2M */
				g_tSF.SectorSize = 4 * 1024;		/* 扇区大小 = 4K */
				break;

			case MX25L1606E_ID:
				strcpy(g_tSF.ChipName, "MX25L1606E");
				g_tSF.TotalSize = 2 * 1024 * 1024;	/* 总容量 = 2M */
				g_tSF.SectorSize = 4 * 1024;		/* 扇区大小 = 4K */
				break;

			case W25Q64BV_ID:
				strcpy(g_tSF.ChipName, "W25Q64");
				g_tSF.TotalSize = 8 * 1024 * 1024;	/* 总容量 = 8M */
				g_tSF.SectorSize = 4 * 1024;		/* 扇区大小 = 4K */
				break;
			
			case W25Q128_ID:
				strcpy(g_tSF.ChipName, "W25Q128");
				g_tSF.TotalSize = 16 * 1024 * 1024;	/* 总容量 = 8M */
				g_tSF.SectorSize = 4 * 1024;		/* 扇区大小 = 4K */
				break;			

			default:
				strcpy(g_tSF.ChipName, "Unknow Flash");
				g_tSF.TotalSize = 2 * 1024 * 1024;
				g_tSF.SectorSize = 4 * 1024;
				break;
		}
	}
}

/*
*********************************************************************************************************
*	函 数 名: sf_ReadID
*	功能说明: 读取器件ID
*	形    参:  无
*	返 回 值: 32bit的器件ID (最高8bit填0,有效ID位数为24bit)
*********************************************************************************************************
*/
uint32_t sf_ReadID(void)
{
    
    
	uint32_t uiID;
	uint8_t id1, id2, id3;

	sf_SetCS(0);							    /* 使能片选 */
	g_spiLen = 0;
	g_spiTxBuf[0] = (CMD_RDID);	  /* 发送读ID命令 0x9F */
	g_spiLen = 4;
	bsp_spiTransfer();
	
	id1 = g_spiRxBuf[1];					/* 读ID的第1个字节 */
	id2 = g_spiRxBuf[2];					/* 读ID的第2个字节 */
	id3 = g_spiRxBuf[3];					/* 读ID的第3个字节 */
	sf_SetCS(1);							    /* 禁能片选 */

	uiID = ((uint32_t)id1 << 16) | ((uint32_t)id2 << 8) | id3;   /*ID整合*/

	return uiID;
}
[bsp_flash.h]
#ifndef __BSP_FLASH_H_
#define __BSP_FLASH_H_

#include "stm32f4xx_hal.h"
#include "main.h"
#include "spi.h"
#include "string.h"

#define SF_CS_0()					F_CS_GPIO_Port->BSRR = ((uint32_t)F_CS_Pin << 16U) 
#define SF_CS_1()					F_CS_GPIO_Port->BSRR = F_CS_Pin
	  
#define CMD_AAI       0xAD  	/* AAI 连续编程指令(FOR SST25VF016B) */
#define CMD_DISWR	    0x04		/* 禁止写, 退出AAI状态 */
#define CMD_EWRSR	    0x50		/* 允许写状态寄存器的命令 */
#define CMD_WRSR      0x01  	/* 写状态寄存器命令 */
#define CMD_WREN      0x06		/* 写使能命令 */
#define CMD_READ      0x03  	/* 读数据区命令 */
#define CMD_RDSR      0x05		/* 读状态寄存器命令 */
#define CMD_RDID      0x9F		/* 读器件ID命令 */
#define CMD_SE        0x20		/* 擦除扇区命令 */
#define CMD_BE        0xC7		/* 批量擦除命令 */
#define DUMMY_BYTE    0xA5		/* 哑命令,可以为任意值,用于读操作 */

#define WIP_FLAG      0x01		/* 状态寄存器中的正在编程标志(WIP) */

typedef struct
{
    
    
	uint32_t ChipID;		/* 芯片ID */
	char ChipName[16];		/* 芯片型号字符串,主要用于显示 */
	uint32_t TotalSize;		/* 总容量 */
	uint16_t SectorSize;		/* 扇区大小 */
}SFLASH_T;

/* 定义串行Flash ID */
enum
{
    
    
	SST25VF016B_ID = 0xBF2541,
	MX25L1606E_ID  = 0xC22015,
	W25Q64BV_ID    = 0xEF4017, /* BV, JV, FV */
	W25Q128_ID     = 0xEF4018
};

void bsp_InitSFlash(void);
void sf_ReadInfo(void);
uint32_t sf_ReadID(void);
void sf_SetCS(uint8_t _Level);

extern SFLASH_T g_tSF;

#endif

[spi.c]
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    spi.c
  * @brief   This file provides code for the configuration
  *          of the SPI instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "spi.h"

/* USER CODE BEGIN 0 */
enum {
    
    
	TRANSFER_WAIT,
	TRANSFER_COMPLETE,
	TRANSFER_ERROR
};


uint32_t g_spiLen;	
uint8_t  g_spi_busy; /* SPI忙状态,0表示不忙,1表示忙 */
__IO uint32_t wTransferState = TRANSFER_WAIT;


uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];  
uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];

/*
*********************************************************************************************************
*	                             选择DMA,中断或者查询方式
*********************************************************************************************************
*/
//#define USE_SPI_DMA    /* DMA方式  */
//#define USE_SPI_INT    /* 中断方式 */
#define USE_SPI_POLL   /* 查询方式 */

/* USER CODE END 0 */

SPI_HandleTypeDef hspi1;

/* SPI1 init function */
void MX_SPI1_Init(void)
{
    
    

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
    
    

  GPIO_InitTypeDef GPIO_InitStruct = {
    
    0};
  if(spiHandle->Instance==SPI1)
  {
    
    
  /* USER CODE BEGIN SPI1_MspInit 0 */

  /* USER CODE END SPI1_MspInit 0 */
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PB3     ------> SPI1_SCK
    PB4     ------> SPI1_MISO
    PB5     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* USER CODE BEGIN SPI1_MspInit 1 */

  /* USER CODE END SPI1_MspInit 1 */
  }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
    
    

  if(spiHandle->Instance==SPI1)
  {
    
    
  /* USER CODE BEGIN SPI1_MspDeInit 0 */

  /* USER CODE END SPI1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SPI1_CLK_DISABLE();

    /**SPI1 GPIO Configuration
    PB3     ------> SPI1_SCK
    PB4     ------> SPI1_MISO
    PB5     ------> SPI1_MOSI
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);

  /* USER CODE BEGIN SPI1_MspDeInit 1 */

  /* USER CODE END SPI1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
/*
*********************************************************************************************************
*	函 数 名: HAL_SPI_TxRxCpltCallback,HAL_SPI_ErrorCallback
*	功能说明: SPI数据传输完成回调和传输错误回调
*	形    参: SPI_HandleTypeDef 类型指针变量
*	返 回 值: 无
*********************************************************************************************************
*/
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    
    
	wTransferState = TRANSFER_COMPLETE;
}


void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
    
    
	wTransferState = TRANSFER_ERROR;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_SpiBusEnter
*	功能说明: 占用SPI总线
*	形    参: 无
*	返 回 值: 0 表示不忙  1表示忙
*********************************************************************************************************
*/
void bsp_SpiBusEnter(void)
{
    
    
	g_spi_busy = 1;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_SpiBusExit
*	功能说明: 释放占用的SPI总线
*	形    参: 无
*	返 回 值: 0 表示不忙  1表示忙
*********************************************************************************************************
*/
void bsp_SpiBusExit(void)
{
    
    
	g_spi_busy = 0;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_SpiBusBusy
*	功能说明: 判断SPI总线忙,方法是检测其他SPI芯片的片选信号是否为1
*	形    参: 无
*	返 回 值: 0 表示不忙  1表示忙
*********************************************************************************************************
*/
uint8_t bsp_SpiBusBusy(void)
{
    
    
	return g_spi_busy;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_spiTransfer
*	功能说明: 启动数据传输
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_spiTransfer(void)
{
    
    
	if (g_spiLen > SPI_BUFFER_SIZE)
	{
    
    
		return;
	}
	
	/* DMA方式传输 */
#ifdef USE_SPI_DMA
	wTransferState = TRANSFER_WAIT;
	
    if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)	
    {
    
    
        Error_Handler();
    }
	
	while (wTransferState == TRANSFER_WAIT)
	{
    
    
		;
	}
#endif

	/* 中断方式传输 */	
#ifdef USE_SPI_INT
	wTransferState = TRANSFER_WAIT;

    if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)	
    {
    
    
        Error_Handler();
    }
	
	while (wTransferState == TRANSFER_WAIT)
	{
    
    
		;
	}
#endif

	/* 查询方式传输 */	
#ifdef USE_SPI_POLL
	if(HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)	
	{
    
    
		Error_Handler();
	}	
#endif
}

/* USER CODE END 1 */

[spi.h]
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    spi.h
  * @brief   This file contains all the function prototypes for
  *          the spi.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __SPI_H__
#define __SPI_H__

#ifdef __cplusplus
extern "C" {
    
    
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */
#define	SPI_BUFFER_SIZE		(4 * 1024)			
extern uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];
extern uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
extern uint32_t g_spiLen;
extern uint8_t g_spi_busy;

/* USER CODE END Includes */

extern SPI_HandleTypeDef hspi1;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_SPI1_Init(void);

/* USER CODE BEGIN Prototypes */
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
void bsp_SpiBusEnter(void);
void bsp_SpiBusExit(void);
uint8_t bsp_SpiBusBusy(void);
void bsp_spiTransfer(void);


/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __SPI_H__ */

[main.c]

/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "bsp_flash.h"
/* USER CODE END Includes */



/* USER CODE BEGIN 2 */
sf_ReadInfo();//读取芯片的ID
/* 检测串行Flash OK */
printf("检测到串行Flash, ID = %08X, 型号: %s \r\n", g_tSF.ChipID , g_tSF.ChipName);
printf("    容量 : %dM字节, 扇区大小 : %d字节\r\n", g_tSF.TotalSize/(1024*1024), g_tSF.SectorSize);
/* USER CODE END 2 */


[result]

检测到串行Flash, ID = 00EF4018, 型号: W25Q128 
容量 : 16M字节, 扇区大小 : 4096字节


Guess you like

Origin blog.csdn.net/sinat_41690014/article/details/130409216