目录
第一步:STM32与Flash的硬件连接
单片机型号:STM32F103ZET6
Flash型号:W25Q64
第二步:配置相关的宏
/**************************SPI接口定义********************************/
#define FLASH_SPIx SPI1
#define FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_CLK RCC_APB2Periph_SPI1
/* CS 片选选普通GPIO即可 */
#define FLASH_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_CS_PORT GPIOA
#define FLASH_SPI_CS_PIN GPIO_Pin_4
/* SCK */
#define FLASH_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_SCK_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_SCK_PORT GPIOA
#define FLASH_SPI_SCK_PIN GPIO_Pin_5
/* MOSI */
#define FLASH_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_MOSI_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MOSI_PORT GPIOA
#define FLASH_SPI_MOSI_PIN GPIO_Pin_7
/* MISO */
#define FLASH_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_MISO_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MISO_PORT GPIOA
#define FLASH_SPI_MISO_PIN GPIO_Pin_6
#define FLASH_SPI_CS_LOW() (GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))
#define FLASH_SPI_CS_HIGH() (GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))
注意:在定义MOSI及MISO的时候,如果反了,会造成后续数据错误。
第三步:SPI初始化
1.SPI相关GPIO初始化
/**
* @brief SPI GPIO初始化
* @param 无
* @retval 无
*/
static void SPI_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能SPI时钟 */
FLASH_SPI_APBxClock_FUN(FLASH_SPI_CLK, ENABLE);
/* 使能SPI引脚相关的时钟 */
FLASH_SPI_CS_APBxClock_FUN(FLASH_SPI_CS_CLK | FLASH_SPI_SCK_CLK | FLASH_SPI_MOSI_CLK | FLASH_SPI_MISO_CLK, ENABLE);
/* 配置SPI的CS引脚,普通IO */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
/* 配置SPI的SCK引脚 复用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
/* 配置SPI的MOSI引脚 复用推挽输出*/
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
/* 配置SPI的MISO引脚 浮空输入*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
/* 停止信号 CS高电平 */
FLASH_SPI_CS_HIGH();
}
2.SPI模式初始化
/**
* @brief SPI 模式初始化
* @param 无
* @retval 无
*/
static void SPI_MODE_Config(void)
{
SPI_InitTypeDef SPI_InitStructure;
/* SPI模式配置 */
SPI_InitStructure.SPI_BaudratePrescaler = SPI_BaudratePrescaler_2; /* 2分频 36MHz */
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; /* 模式3 CPOL = 1 CPHA = 1 */
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CRCpolynomial = 7; /* 未使用,任意值 */
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; /* 数据帧长度为8位 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; /* 双线全双工模式 */
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; /* MSB先行 */
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; /* 主机模式 */
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /* 软件片选 */
SPI_Init(FLASH_SPIx, &SPI_InitStructure);
/* 使能SPI */
SPI_Cmd(FLASH_SPIx, ENABLE);
}
第四步:使用SPI实现发送及接收一个字节的数据
由于SPI为同步通信,在实现发送的时候,也是同步接收的,所以发送及接收本质上是一个函数。
1.发送一个字节
/**
* @brief 使用SPI发送一个字节的数据
* @param byte:要发送的数据
* @retval 返回接收到的数据
*/
u8 SPI_FLASH_SendByte(u8 byte)
{
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空 TXE事件 */
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0)
{
return SPI_TIMEOUT_UserCallback(0);
}
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(FLASH_SPIx, byte);
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0)
{
return SPI_TIMEOUT_UserCallback(1);
}
}
return SPI_I2S_ReceiveData(FLASH_SPIx);
}
在通信过程增加超时的报错。
2.接收一个字节
/**
* @brief 使用SPI读取一个字节的数据
* @param 无
* @retval 无
*/
u8 SPI_FLASH_ReadByte(void)
{
return SPI_FLASH_SendByte(Dummy_Byte);
}
第五步:读取Flash ID的函数
#define W25X_Jedecdeviceid 0x9F
#define Dummy_Byte 0xFF
/**
* @brief 读取FLASH ID
* @param 无
* @retval FLASH ID
*/
u32 SPI_FLASH_ReadID(void)
{
u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
/* 开始通信:CS拉低 */
FLASH_SPI_CS_LOW();
/* 发送JEDEC指令,读取ID */
SPI_FLASH_SendByte(W25X_Jedecdeviceid);
/* 读取一个字节数据 */
Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
/* 读取一个字节数据 */
Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
/* 读取一个字节数据 */
Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
/* 停止通讯:CS拉高 */
FLASH_SPI_CS_HIGH();
Temp = ((Temp0 << 16) | (Temp1 << 8) | Temp2);
return Temp;
}
发送指定的指令,可以获取相应的数据,发送0x9F即可获取Flash ID。
第六步:读取Flash ID的代码实现
1.编程要点
(1) 初始化通讯使用的目标引脚及端口时钟;
(2) 使能 SPI 外设的时钟;
(3) 配置 SPI 外设的模式、地址、速率等参数并使能 SPI 外设;
(4) 编写基本 SPI 按字节收发的函数;
(5) 编写对 FLASH 的Flash ID读取的函数;
(6) 测试
2.代码实现
#ifndef __SPI_FLASH_H
#define __SPI_FLASH_H
#include "stm32f10x.h"
//#define sFLASH_ID 0xEF3015 //W25X16
//#define sFLASH_ID 0xEF4015 //W25Q16
//#define sFLASH_ID 0XEF4018 //W25Q128
#define sFLASH_ID 0XEF4017 //W25Q64
/* FLASH 常用命令 */
#define W25X_WriteEnable 0x06
#define W25X_Writedisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_deviceid 0xAB
#define W25X_Manufactdeviceid 0x90
#define W25X_Jedecdeviceid 0x9F
#define WIP_Flag 0x01
#define Dummy_Byte 0xFF
/**************************SPI接口定义********************************/
#define FLASH_SPIx SPI1
#define FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_CLK RCC_APB2Periph_SPI1
/* CS 片选选普通GPIO即可 */
#define FLASH_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_CS_PORT GPIOA
#define FLASH_SPI_CS_PIN GPIO_Pin_4
/* SCK */
#define FLASH_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_SCK_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_SCK_PORT GPIOA
#define FLASH_SPI_SCK_PIN GPIO_Pin_5
/* MOSI */
#define FLASH_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_MOSI_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MOSI_PORT GPIOA
#define FLASH_SPI_MOSI_PIN GPIO_Pin_7
/* MISO */
#define FLASH_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_MISO_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MISO_PORT GPIOA
#define FLASH_SPI_MISO_PIN GPIO_Pin_6
#define FLASH_SPI_CS_LOW() (GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))
#define FLASH_SPI_CS_HIGH() (GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))
/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))
/*信息输出*/
#define FLASH_DEBUG_ON 0
#define FLASH_INFO(fmt,arg...) printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...) printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...) do{\
if(FLASH_DEBUG_ON)\
printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
}while(0)
void SPI_FLASH_Init(void);
u8 SPI_FLASH_SendByte(u8 byte);
u8 SPI_FLASH_ReadByte(void);
u32 SPI_FLASH_ReadID(void);
#endif /* __SPI_FLASH_H */
/**
******************************************************************************
* @file bsp_spi_flash.c
* @author ZHQ
* @version V1.0
* @date 2021-02-15
* @brief spi flash应用函数bsp
******************************************************************************
*/
#include "./spi/bsp_spi_flash.h"
#include "./usart/bsp_usart.h"
static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT;
static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);
/**
* @brief SPI GPIO初始化
* @param 无
* @retval 无
*/
static void SPI_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能SPI时钟 */
FLASH_SPI_APBxClock_FUN(FLASH_SPI_CLK, ENABLE);
/* 使能SPI引脚相关的时钟 */
FLASH_SPI_CS_APBxClock_FUN(FLASH_SPI_CS_CLK | FLASH_SPI_SCK_CLK | FLASH_SPI_MOSI_CLK | FLASH_SPI_MISO_CLK, ENABLE);
/* 配置SPI的CS引脚,普通IO */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
/* 配置SPI的SCK引脚 复用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
/* 配置SPI的MOSI引脚 复用推挽输出*/
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
/* 配置SPI的MISO引脚 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
/* 停止信号 CS高电平 */
FLASH_SPI_CS_HIGH();
}
/**
* @brief SPI 模式初始化
* @param 无
* @retval 无
*/
static void SPI_MODE_Config(void)
{
SPI_InitTypeDef SPI_InitStructure;
/* SPI模式配置 */
SPI_InitStructure.SPI_BaudratePrescaler = SPI_BaudratePrescaler_2; /* 2分频 36MHz */
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; /* 模式3 CPOL = 1 CPHA = 1 */
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CRCpolynomial = 7; /* 未使用,任意值 */
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; /* 数据帧长度为8位 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; /* 双线全双工模式 */
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; /* MSB先行 */
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; /* 主机模式 */
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /* 软件片选 */
SPI_Init(FLASH_SPIx, &SPI_InitStructure);
/* 使能SPI */
SPI_Cmd(FLASH_SPIx, ENABLE);
}
/**
* @brief SPI 初始化
* @param 无
* @retval 无
*/
void SPI_FLASH_Init(void)
{
SPI_GPIO_Config();
SPI_MODE_Config();
}
/**
* @brief 使用SPI发送一个字节的数据
* @param byte:要发送的数据
* @retval 返回接收到的数据
*/
u8 SPI_FLASH_SendByte(u8 byte)
{
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空 TXE事件 */
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0)
{
return SPI_TIMEOUT_UserCallback(0);
}
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(FLASH_SPIx, byte);
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0)
{
return SPI_TIMEOUT_UserCallback(1);
}
}
return SPI_I2S_ReceiveData(FLASH_SPIx);
}
/**
* @brief 使用SPI读取一个字节的数据
* @param 无
* @retval 无
*/
u8 SPI_FLASH_ReadByte(void)
{
return SPI_FLASH_SendByte(Dummy_Byte);
}
/**
* @brief 读取FLASH ID
* @param 无
* @retval FLASH ID
*/
u32 SPI_FLASH_ReadID(void)
{
u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
/* 开始通信:CS拉低 */
FLASH_SPI_CS_LOW();
/* 发送JEDEC指令,读取ID */
SPI_FLASH_SendByte(W25X_Jedecdeviceid);
/* 读取一个字节数据 */
Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
/* 读取一个字节数据 */
Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
/* 读取一个字节数据 */
Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
/* 停止通讯:CS拉高 */
FLASH_SPI_CS_HIGH();
Temp = ((Temp0 << 16) | (Temp1 << 8) | Temp2);
return Temp;
}
/**
* @brief Basic management of the timeout situation.
* @param errorCode:错误代码,可以用来定位是哪个环节出错.
* @retval 返回0,表示SPI读取失败.
*/
static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* Block communication and all processes */
FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);
return 0;
}
/*********************************************END OF FILE**********************/
/**
******************************************************************************
* @file main.c
* @author ZHQ
* @version V1.0
* @date 2021-02-15
* @brief SPI flash测试,测试信息通过USART1打印在电脑的超级终端
******************************************************************************
*/
#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./spi/bsp_spi_flash.h"
#include <string.h>
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
u32 Flash_ID;
/* 串口初始化 */
USART_Config();
printf("\r\n 这是一个SPI外设(FLASH)读写测试例程 \r\n");
SPI_FLASH_Init();
Flash_ID = SPI_FLASH_ReadID();
printf("\r\n Flash_ID : 0x%X \r\n", Flash_ID);
while (1)
{
;
}
}
/*********************************************END OF FILE**********************/
3.测试结果
得到的结果0xEF4017与该芯片的规格书描述相符合。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。