00. 目录
文章目录
- 00. 目录
- 01. SPI简介
- 02. W25Q64简介
- 03. SPI相关API
- 3.1 SPI_Init
- 3.2 SPI_Cmd
- 3.3 SPI_I2S_SendData
- 3.4 SPI_I2S_ReceiveData
- 3.5 SPI_I2S_GetFlagStatus
- 3.6 SPI_I2S_ClearFlag
- 3.7 SPI_InitTypeDef
- 04. 硬件SPI读写W25Q64接线图
- 05. 硬件SPI读写W25Q64示例
- 06. 程序下载
- 07. 附录
01. SPI简介
在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持I2S音频协议。SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。
在小容量和中容量产品上,不支持I2S音频协议。
串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。
它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用CRC校验的可靠通信。
I2S也是一种3引脚的同步串行接口通讯协议。它支持四种音频标准,包括飞利浦I2S标准,MSB和LSB对齐标准,以及PCM标准。它在半双工通讯中,可以工作在主和从2种模式下。当它作为主设备时,通过接口向外部的从设备提供时钟信号。
02. W25Q64简介
•W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景
•存储介质:Nor Flash(闪存)
•时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
•存储容量(24位地址)
03. SPI相关API
3.1 SPI_Init
/**
* @brief Initializes the SPIx peripheral according to the specified
* parameters in the SPI_InitStruct.
* @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral.
* @param SPI_InitStruct: pointer to a SPI_InitTypeDef structure that
* contains the configuration information for the specified SPI peripheral.
* @retval None
*/
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
功能:
根据 SPI_InitStruct 中指定的参数初始化外设 SPIx 寄存器
参数:
SPIx:x 可以是 1 或者 2,来选择 SPI 外设
SPI_InitStruct:指向结构 SPI_InitTypeDef 的指针,包含了外设 SPI 的配置信息
返回值:
无
3.2 SPI_Cmd
/**
* @brief Enables or disables the specified SPI peripheral.
* @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral.
* @param NewState: new state of the SPIx peripheral.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState)
功能:
使能或者失能SPI外设
参数:
SPIx:x 可以是 1 或者 2,来选择 SPI 外设
NewState: 外设 SPIx 的新状态这个参数可以取:ENABLE 或者 DISABLE
返回值:
无
3.3 SPI_I2S_SendData
/**
* @brief Transmits a Data through the SPIx/I2Sx peripheral.
* @param SPIx: where x can be
* - 1, 2 or 3 in SPI mode
* - 2 or 3 in I2S mode
* @param Data : Data to be transmitted.
* @retval None
*/
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
功能:
通过外设 SPIx 发送一个数据
参数:
SPIx:x 可以是 1 或者 2,来选择 SPI 外设
Data: 待发送的数据
返回值:
无
3.4 SPI_I2S_ReceiveData
/**
* @brief Returns the most recent received data by the SPIx/I2Sx peripheral.
* @param SPIx: where x can be
* - 1, 2 or 3 in SPI mode
* - 2 or 3 in I2S mode
* @retval The value of the received data.
*/
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
功能:
返回通过 SPIx 最近接收的数据
参数:
SPIx:x 可以是 1 或者 2,来选择 SPI 外设
返回值:
接收到的字
3.5 SPI_I2S_GetFlagStatus
/**
* @brief Checks whether the specified SPI/I2S flag is set or not.
* @param SPIx: where x can be
* - 1, 2 or 3 in SPI mode
* - 2 or 3 in I2S mode
* @param SPI_I2S_FLAG: specifies the SPI/I2S flag to check.
* This parameter can be one of the following values:
* @arg SPI_I2S_FLAG_TXE: Transmit buffer empty flag.
* @arg SPI_I2S_FLAG_RXNE: Receive buffer not empty flag.
* @arg SPI_I2S_FLAG_BSY: Busy flag.
* @arg SPI_I2S_FLAG_OVR: Overrun flag.
* @arg SPI_FLAG_MODF: Mode Fault flag.
* @arg SPI_FLAG_CRCERR: CRC Error flag.
* @arg I2S_FLAG_UDR: Underrun Error flag.
* @arg I2S_FLAG_CHSIDE: Channel Side flag.
* @retval The new state of SPI_I2S_FLAG (SET or RESET).
*/
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
功能:
检查指定的 SPI 标志位设置与否
参数:
SPIx:x 可以是 1 或者 2,来选择 SPI 外设
SPI_I2S_FLAG:待检查的 SPI 标志位
返回值:
SPI_FLAG 的新状态(SET 或者 RESET)
3.6 SPI_I2S_ClearFlag
/**
* @brief Clears the SPIx CRC Error (CRCERR) flag.
* @param SPIx: where x can be
* - 1, 2 or 3 in SPI mode
* @param SPI_I2S_FLAG: specifies the SPI flag to clear.
* This function clears only CRCERR flag.
* @note
* - OVR (OverRun error) flag is cleared by software sequence: a read
* operation to SPI_DR register (SPI_I2S_ReceiveData()) followed by a read
* operation to SPI_SR register (SPI_I2S_GetFlagStatus()).
* - UDR (UnderRun error) flag is cleared by a read operation to
* SPI_SR register (SPI_I2S_GetFlagStatus()).
* - MODF (Mode Fault) flag is cleared by software sequence: a read/write
* operation to SPI_SR register (SPI_I2S_GetFlagStatus()) followed by a
* write operation to SPI_CR1 register (SPI_Cmd() to enable the SPI).
* @retval None
*/
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
功能:
清除 SPIx 的待处理标志位
参数:
SPIx:x 可以是 1 或者 2,来选择 SPI 外设
SPI_I2S_FLAG:待清除的 SPI 标志位
返回值:
无
3.7 SPI_InitTypeDef
typedef struct
{
uint16_t SPI_Direction; /*!< Specifies the SPI unidirectional or bidirectional data mode.
This parameter can be a value of @ref SPI_data_direction */
uint16_t SPI_Mode; /*!< Specifies the SPI operating mode.
This parameter can be a value of @ref SPI_mode */
uint16_t SPI_DataSize; /*!< Specifies the SPI data size.
This parameter can be a value of @ref SPI_data_size */
uint16_t SPI_CPOL; /*!< Specifies the serial clock steady state.
This parameter can be a value of @ref SPI_Clock_Polarity */
uint16_t SPI_CPHA; /*!< Specifies the clock active edge for the bit capture.
This parameter can be a value of @ref SPI_Clock_Phase */
uint16_t SPI_NSS; /*!< Specifies whether the NSS signal is managed by
hardware (NSS pin) or by software using the SSI bit.
This parameter can be a value of @ref SPI_Slave_Select_management */
uint16_t SPI_BaudRatePrescaler; /*!< Specifies the Baud Rate prescaler value which will be
used to configure the transmit and receive SCK clock.
This parameter can be a value of @ref SPI_BaudRate_Prescaler.
@note The communication clock is derived from the master
clock. The slave clock does not need to be set. */
uint16_t SPI_FirstBit; /*!< Specifies whether data transfers start from MSB or LSB bit.
This parameter can be a value of @ref SPI_MSB_LSB_transmission */
uint16_t SPI_CRCPolynomial; /*!< Specifies the polynomial used for the CRC calculation. */
}SPI_InitTypeDef;
SPI_Direction
/** @defgroup SPI_data_direction
* @{
*/
#define SPI_Direction_2Lines_FullDuplex ((uint16_t)0x0000)
#define SPI_Direction_2Lines_RxOnly ((uint16_t)0x0400)
#define SPI_Direction_1Line_Rx ((uint16_t)0x8000)
#define SPI_Direction_1Line_Tx ((uint16_t)0xC000)
SPI_Mode
/** @defgroup SPI_mode
* @{
*/
#define SPI_Mode_Master ((uint16_t)0x0104)
#define SPI_Mode_Slave ((uint16_t)0x0000)
#define IS_SPI_MODE(MODE) (((MODE) == SPI_Mode_Master) || \
((MODE) == SPI_Mode_Slave))
SPI_DataSize
/** @defgroup SPI_data_size
* @{
*/
#define SPI_DataSize_16b ((uint16_t)0x0800)
#define SPI_DataSize_8b ((uint16_t)0x0000)
SPI_CPOL
/** @defgroup SPI_Clock_Polarity
* @{
*/
#define SPI_CPOL_Low ((uint16_t)0x0000)
#define SPI_CPOL_High ((uint16_t)0x0002)
SPI_CPHA
/** @defgroup SPI_Clock_Phase
* @{
*/
#define SPI_CPHA_1Edge ((uint16_t)0x0000)
#define SPI_CPHA_2Edge ((uint16_t)0x0001)
SPI_NSS
/** @defgroup SPI_Slave_Select_management
* @{
*/
#define SPI_NSS_Soft ((uint16_t)0x0200)
#define SPI_NSS_Hard ((uint16_t)0x0000)
SPI_BaudRatePrescaler
/** @defgroup SPI_BaudRate_Prescaler
* @{
*/
#define SPI_BaudRatePrescaler_2 ((uint16_t)0x0000)
#define SPI_BaudRatePrescaler_4 ((uint16_t)0x0008)
#define SPI_BaudRatePrescaler_8 ((uint16_t)0x0010)
#define SPI_BaudRatePrescaler_16 ((uint16_t)0x0018)
#define SPI_BaudRatePrescaler_32 ((uint16_t)0x0020)
#define SPI_BaudRatePrescaler_64 ((uint16_t)0x0028)
#define SPI_BaudRatePrescaler_128 ((uint16_t)0x0030)
#define SPI_BaudRatePrescaler_256 ((uint16_t)0x0038)
SPI_FirstBit
/** @defgroup SPI_MSB_LSB_transmission
* @{
*/
#define SPI_FirstBit_MSB ((uint16_t)0x0000)
#define SPI_FirstBit_LSB ((uint16_t)0x0080)
SPI_CRCPolynomial
CRC校验值
04. 硬件SPI读写W25Q64接线图
05. 硬件SPI读写W25Q64示例
spi.h
#ifndef __SPI_H__
#define __SPI_H__
#include "stm32f10x.h"
void spi_init(void);
void spi_start(void);
void spi_stop(void);
uint8_t spi_swap_byte(uint8_t val);
#endif /*__SPI_H__*/
spi.c
#include "spi.h"
#include "stm32f10x_spi.h"
/*
CS: PA4
CLK: PA5
DO: PA6
DI: PA7
*/
//SS写 PA4
void spi_W_SS(uint8_t bitval)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)bitval);
}
void spi_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
//使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//A4 CS
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//A5 A7 CLK DI
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//A6 DO
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CRCPolynomial = 7;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
spi_W_SS(1);
}
void spi_start(void)
{
spi_W_SS(0);
}
void spi_stop(void)
{
spi_W_SS(1);
}
uint8_t spi_swap_byte(uint8_t val)
{
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
SPI_I2S_SendData(SPI1, val);
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
return SPI_I2S_ReceiveData(SPI1);
}
w25q64.h
#ifndef __W25Q64_H__
#define __W25Q64_H__
#include "stm32f10x.h"
#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3
#define W25Q64_DUMMY_BYTE 0xFF
void W25Q64_init(void);
void W25Q64_read_id(uint8_t *mid, uint16_t *did);
//写使能
void W25Q64_write_enable(void);
//等待 直到空闲
void W25Q64_wait_busy(void);
void W25Q64_sector_erase(uint32_t addr);
void W25Q64_page_program(uint32_t addr, uint8_t *arr, uint16_t len);
void W25Q64_read_data(uint32_t addr, uint8_t *arr, uint16_t len);
#endif /*__W25Q64_H__*/
w25q64.c
#include "w25q64.h"
#include "spi.h"
void W25Q64_init(void)
{
spi_init();
}
void W25Q64_read_id(uint8_t *mid, uint16_t *did)
{
spi_start();
spi_swap_byte(W25Q64_JEDEC_ID);
*mid = spi_swap_byte(W25Q64_DUMMY_BYTE);
*did = spi_swap_byte(W25Q64_DUMMY_BYTE);
*did <<= 8;
*did |= spi_swap_byte(W25Q64_DUMMY_BYTE);
spi_stop();
}
void W25Q64_write_enable(void)
{
spi_start();
spi_swap_byte(W25Q64_WRITE_ENABLE);
spi_stop();
}
void W25Q64_wait_busy(void)
{
uint32_t timeout;
spi_start();
spi_swap_byte(W25Q64_READ_STATUS_REGISTER_1);
timeout = 100000;
while((spi_swap_byte(W25Q64_DUMMY_BYTE) & 0x1) == 0x01)
{
timeout--;
if (0 == timeout)
{
break;
}
}
spi_stop();
}
void W25Q64_page_program(uint32_t addr, uint8_t *arr, uint16_t len)
{
uint8_t i;
W25Q64_write_enable();
spi_start();
spi_swap_byte(W25Q64_PAGE_PROGRAM);
spi_swap_byte(addr >> 16);
spi_swap_byte(addr >> 8);
spi_swap_byte(addr);
for (i = 0; i < len; i++)
{
spi_swap_byte(arr[i]);
}
spi_stop();
W25Q64_wait_busy();
}
void W25Q64_sector_erase(uint32_t addr)
{
W25Q64_write_enable();
spi_start();
spi_swap_byte(W25Q64_SECTOR_ERASE_4KB);
spi_swap_byte(addr >> 16);
spi_swap_byte(addr >> 8);
spi_swap_byte(addr);
spi_stop();
W25Q64_wait_busy();
}
void W25Q64_read_data(uint32_t addr, uint8_t *arr, uint16_t len)
{
uint8_t i = 0;
spi_start();
spi_swap_byte(W25Q64_READ_DATA);
spi_swap_byte(addr >> 16);
spi_swap_byte(addr >> 8);
spi_swap_byte(addr);
for (i = 0; i < len; i++)
{
arr[i] = spi_swap_byte(W25Q64_DUMMY_BYTE);
}
spi_stop();
}
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "oled.h"
#include "w25q64.h"
int main(void)
{
uint8_t mid;
uint16_t did;
uint8_t array_w[4] = {0x11, 0x22, 0x33, 0x44};
uint8_t array_r[4];
//初始化
OLED_Init();
W25Q64_init();
//显示一个字符
//OLED_ShowChar(1, 1, 'A');
//显示字符串
//OLED_ShowString(1, 3, "SPI Test");
OLED_ShowString(1, 1, "MID: DID:");
OLED_ShowString(2, 1, "W:");
OLED_ShowString(3, 1, "R:");
W25Q64_read_id(&mid, &did);
OLED_ShowHexNum(1, 5, mid, 2);
OLED_ShowHexNum(1, 12, did, 4);
//擦除扇区
W25Q64_sector_erase(0x0);
//写扇区
W25Q64_page_program(0x0, array_w, 4);
//读数据
W25Q64_read_data(0x0, array_r, 4);
OLED_ShowHexNum(2, 3, array_w[0], 2);
OLED_ShowHexNum(2, 6, array_w[1], 2);
OLED_ShowHexNum(2, 9, array_w[2], 2);
OLED_ShowHexNum(2, 12, array_w[3], 2);
OLED_ShowHexNum(3, 3, array_r[0], 2);
OLED_ShowHexNum(3, 6, array_r[1], 2);
OLED_ShowHexNum(3, 9, array_r[2], 2);
OLED_ShowHexNum(3, 12, array_r[3], 2);
while(1)
{
}
return 0;
}
06. 程序下载
31-硬件SPI.rar
07. 附录
参考: 【STM32】江科大STM32学习笔记汇总