APM32F402 驱动 SPI Flash Demo
本帖最后由 Peixu 于 2025-6-16 16:29 编辑一、引言:嵌入式存储解决方案概述
APM32F402 作为一款高性能 ARM Cortex-M4 微控制器,搭配SPI Flash 存储芯片,能够为各类嵌入式应用提供稳定可靠的大容量数据存储解决方案。
本文将从硬件设计、驱动开发、功能测试实现基于 APM32F402 的 SPI Flash 通信完整实现方案。
二、硬件设计:APM32F402 与 SPI Flash的电路连接
2.1 引脚连接方案
APM32F402 与 SPI Flash 通信需要以下关键引脚连接:
APM32F402 引脚
SPI Flash 引脚
功能说明
PA4(GPIO_PIN_4)
CS
片选信号(低电平有效)
PA5(GPIO_PIN_5)
CLK
SPI 时钟信号
PA6(GPIO_PIN_6)
DO/MISO
主设备输入 / 从设备输出
PA7(GPIO_PIN_7)
DI/MOSI
主设备输出 / 从设备输入
三、测试与验证:串口输出与 SPI 波形分析
ID 识别验证:
发送 0xAB获取设备 ID, 发送9F 获取厂商 ID
擦写读闭环测试:
擦除指定扇区(0x20 命令)
写入 字节测试数据
读取相同地址数据并比对
串口输出比对结果(PASS/FAIL)
串口输出测试结果:
Device ID:0x17
Flash ID:0x234018
SPI write Data to flash
Write data:
0x1011 0x1012 0x1013 0x1014 0x1015 0x1016 0x1017 0x1011
0x1012 0x1013 0x1014 0x1015 0x1016 0x1017 0x1011 0x1012
0x1013 0x1014 0x1015 0x1016 0x1017 0x1011 0x1012 0x1013
0x1014 0x1015 0x1016 0x1017 0x1011 0x1012 0x1013 0x1014
0x1015 0x1016 0x1017 0x1011 0x1012 0x1013 0x1014 0x1015
0x1016 0x1017 0x1011 0x1012 0x1013 0x1014 0x1015 0x1016
0x1017 0x1011 0x1012 0x1013 0x1014 0x1015 0x1016 0x1017
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
Read data:
0x1011 0x1012 0x1013 0x1014 0x1015 0x1016 0x1017 0x1011
0x1012 0x1013 0x1014 0x1015 0x1016 0x1017 0x1011 0x1012
0x1013 0x1014 0x1015 0x1016 0x1017 0x1011 0x1012 0x1013
0x1014 0x1015 0x1016 0x1017 0x1011 0x1012 0x1013 0x1014
0x1015 0x1016 0x1017 0x1011 0x1012 0x1013 0x1014 0x1015
0x1016 0x1017 0x1011 0x1012 0x1013 0x1014 0x1015 0x1016
0x1017 0x1011 0x1012 0x1013 0x1014 0x1015 0x1016 0x1017
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010 0x1010
SPI Flash test OK! LED2 onmain.c文件/**
* @file main.c
*
* @brief Main program body
*
* @version V1.0.0
*
* @date 2024-12-01
*
* @attention
*
*Copyright (C) 2024-2025 Geehy Semiconductor
*
*You may not use this file except in compliance with the
*GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
*
*The program is only for reference, which is distributed in the hope
*that it will be useful and instructional for customers to develop
*their software. Unless required by applicable law or agreed to in
*writing, the program is distributed on an "AS IS" BASIS, WITHOUT
*ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
*See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
*and limitations under the License.
*/
/* Includes ***************************************************************/
#include "main.h"
#include "bsp_w25q16.h"
/* Private includes *******************************************************/
#include <stdio.h>
/* Private macro **********************************************************/
/* printf function configs to USART */
#define DEBUG_USARTUSART1
#define DATA_BUF_SIZE 128
typedef enum {FALSE, TRUE} BOOL;
W25Q16_INFO_T w25q16Info;
uint16_t txDataBufSPI1 = {0};
uint16_t rxDataBufSPI1 = {0};
/** SPI TX Buffer*/
const uint16_t SPI_Data_TX =
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0X07,
};
/* Private function prototypes ********************************************/
void USART_Init(void);
void Delay(uint32_t count);
void W25Q16_SPI_Test(void);
void Print_Buffer(uint16_t* pBuffer, uint16_t BufferLength);
uint8_t BufferCompare(uint16_t* buf1, uint16_t* buf2, uint8_t size);
/* External variables *****************************************************/
/* External functions *****************************************************/
/*!
* @brief Main program
*
* @param None
*
* @retvalNone
*/
int main(void)
{
USART_Init();
W25Q16_SPI_Init();
/* Get Flash Device ID */
w25q16Info.deviceID = W25Q16_ReadFlashDeviceID();
printf("Device ID:0x%X\r\n",w25q16Info.deviceID);
/* Get Flash ID */
w25q16Info.flashID = W25Q16_ReadFlashID();
printf("FlashID:0x%X\r\n",w25q16Info.flashID);
W25Q16_SPI_Test();
/* Infinite loop */
while (1)
{
}
}
/*!
* @brief USART INIT
*
* @param None
*
* @retval None
*
* @note
*/
void USART_Init(void)
{
USART_Config_T USART_ConfigStruct;
GPIO_Config_T GPIO_ConfigStruct;
// 使能 USART1 和 GPIO 端口时钟
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1 | RCM_APB2_PERIPH_GPIOA);
// 配置 TX 引脚为复用推挽输出
GPIO_ConfigStruct.pin = GPIO_PIN_9;
GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;
GPIO_ConfigStruct.speed = GPIO_SPEED_50MHz;
GPIO_Config(GPIOA, &GPIO_ConfigStruct);
// 配置 RX 引脚为浮空输入
GPIO_ConfigStruct.pin = GPIO_PIN_10;
GPIO_ConfigStruct.mode = GPIO_MODE_IN_FLOATING;
GPIO_Config(GPIOA, &GPIO_ConfigStruct);
// USART 配置
USART_ConfigStructInit(&USART_ConfigStruct);
USART_ConfigStruct.baudRate = 115200;
USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
USART_ConfigStruct.mode = USART_MODE_TX_RX;// 同时启用发送和接收模式
USART_ConfigStruct.parity = USART_PARITY_NONE;
USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
USART_Config(USART1, &USART_ConfigStruct);
// 使能 USART1
USART_Enable(USART1);
}
/*!
* @brief Using SPI operate W25Q16 flash
*
* @param None
*
* @retval None
*
* @note
*/
void W25Q16_SPI_Test(void)
{
uint16_t i = 0;
/* initializationBuffer*/
for (i = 0; i < DATA_BUF_SIZE; i++)
{
txDataBufSPI1 = SPI_Data_TX + 0x1010;
rxDataBufSPI1 = 0;
}
printf("SPI write Data to flash \r\n");
/* Erase SPI FLASH Sector */
W25Q16_EraseSector(W25Q16_FLASH_WRITE_ADDR);
/* Write data to flash */
W25Q16_WriteBuffer((uint8_t*)txDataBufSPI1, W25Q16_FLASH_WRITE_ADDR, DATA_BUF_SIZE * 2);
printf("\r\nWrite data: \r\n");
Print_Buffer(txDataBufSPI1, DATA_BUF_SIZE);
/* Read data from flash */
W25Q16_ReadBuffer((uint8_t*)rxDataBufSPI1, W25Q16_FLASH_READ_ADDR, DATA_BUF_SIZE * 2);
printf("\r\nRead data: \r\n");
Print_Buffer(rxDataBufSPI1, DATA_BUF_SIZE);
/* Compare receive Buffer */
/* Data is ok then turn on LED2 */
if (BufferCompare(txDataBufSPI1, rxDataBufSPI1, DATA_BUF_SIZE) == TRUE)
{
printf("\r\nSPI Flash test OK! LED2 on\n\r");
}
else
{
printf("\r\nSPI Flash test fail! LED3 on\n\r");
}
}
/*!
* @brief Compares two buffers
*
* @param buf1: First buffer to be compared
*
* @param buf1: Second buffer to be compared
*
* @param size: Buffer size
*
* @retval Return TRUE if buf1 = buf2. If not then return FALSE
*/
uint8_t BufferCompare(uint16_t* buf1, uint16_t* buf2, uint8_t size)
{
uint8_t i;
for (i = 0; i < size; i++)
{
if (buf1 != buf2)
{
return FALSE;
}
}
return TRUE;
}
/*!
* @brief Print Buffer Data
*
* @param pBuffer:buffer
*
* @param length : length of the Buffer
*
* @retval None
*/
void Print_Buffer(uint16_t* pBuffer, uint16_t BufferLength)
{
uint16_t i;
for (i=0; i< BufferLength; i++)
{
printf("0x%04X", pBuffer);
if ((i+1)%8 == 0)
{
printf("\r\n");
}
}
}
/*!
* @brief Delay
*
* @param count:delay count
*
* @retval None
*/
void Delay(uint32_t count)
{
uint16_t i = 0;
while (count--)
{
i = 7995;
while (i--);
}
}
#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
/*!
* @brief Redirect C Library function printf to serial port.
* After Redirection, you can use printf function.
*
* @param ch:The characters that need to be send.
*
* @param *f:pointer to a FILE that can recording all information
* needed to control a stream
*
* @retval The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
/* send a byte of data to the serial port */
USART_TxData(DEBUG_USART, (uint8_t)ch);
/* wait for the data to be send */
while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
return (ch);
}
#elif defined (__GNUC__)
/*!
* @brief Redirect C Library function printf to serial port.
* After Redirection, you can use printf function.
*
* @param ch:The characters that need to be send.
*
* @retval The characters that need to be send.
*
* @note
*/
int __io_putchar(int ch)
{
/* send a byte of data to the serial port */
USART_TxData(DEBUG_USART, ch);
/* wait for the data to be send */
while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
return ch;
}
/*!
* @brief Redirect C Library function printf to serial port.
* After Redirection, you can use printf function.
*
* @param file:Meaningless in this function.
*
* @param *ptr:Buffer pointer for data to be sent.
*
* @param len:Length of data to be sent.
*
* @retval The characters that need to be send.
*
* @note
*/
int _write(int file, char* ptr, int len)
{
UNUSED(file);
int i;
for (i = 0; i < len; i++)
{
__io_putchar(*ptr++);
}
return len;
}
#else
#warning Not supported compiler type
#endif
SPI Flash 驱动.c
/*!
* @file bsp_sdio.c
*
* @brief SDIO board support package body
*
* @version V1.0.0
*
* @date 2022-09-30
*
* @attention
*
*Copyright (C) 2022 Geehy Semiconductor
*
*You may not use this file except in compliance with the
*GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
*
*The program is only for reference, which is distributed in the hope
*that it will be useful and instructional for customers to develop
*their software. Unless required by applicable law or agreed to in
*writing, the program is distributed on an "AS IS" BASIS, WITHOUT
*ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
*See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
*and limitations under the License.
*/
/* Includes */
#include "bsp_w25q16.h"
/** @addtogroup Board
@{
*/
/** @addtogroup Board_APM32E103_EVAL
@{
*/
/** @defgroup APM32E103_EVAL_Fuctions
@{
*/
/*!
* @brief W25Q16 SPI Initialization
*
* @param None
*
* @retval None
*/
void W25Q16_SPI_Init(void)
{
GPIO_Config_T gpioConfig;
SPI_Config_T spiConfig;
/* Enable related Clock */
RCM_EnableAPB2PeriphClock(FLASH_SPI_BUS_CLK | FLASH_SPI_GPIO_CLK | FLASH_SPI_CS_GPIO_CLK |RCM_APB2_PERIPH_AFIO);
GPIO_ConfigPinRemap(GPIO_REMAP_SWJ_JTAGDISABLE);
/* Configure FLASH_SPI pins: SCK */
gpioConfig.pin =FLASH_SPI_SCK_PIN ;
gpioConfig.mode = GPIO_MODE_AF_PP;
gpioConfig.speed = GPIO_SPEED_50MHz;
GPIO_Config(FLASH_SPI_GPIO_PORT, &gpioConfig);
/* Configure FLASH_SPI pins: MOSI */
gpioConfig.pin = FLASH_SPI_MOSI_PIN;
GPIO_Config(FLASH_SPI_GPIO_PORT, &gpioConfig);
/* Configure FLASH_SPI pins: MISO */
gpioConfig.pin = FLASH_SPI_MISO_PIN;
gpioConfig.mode = GPIO_MODE_IN_FLOATING;
GPIO_Config(FLASH_SPI_GPIO_PORT, &gpioConfig);
/* Configure FLASH_SPI_CS_PIN pin: sFLASH Card CS pin */
gpioConfig.pin = FLASH_SPI_CS_PIN;
gpioConfig.mode = GPIO_MODE_OUT_PP;
GPIO_Config(FLASH_SPI_CS_GPIO_PORT, &gpioConfig);
/* Deselect the FLASH: Chip Select high */
FLASH_SPI_CS_SET();
/* SPI configuration */
spiConfig.direction = SPI_DIRECTION_2LINES_FULLDUPLEX;
spiConfig.mode = SPI_MODE_MASTER;
spiConfig.length = SPI_DATA_LENGTH_8B;
spiConfig.polarity = SPI_CLKPOL_HIGH;
spiConfig.phase = SPI_CLKPHA_2EDGE;
spiConfig.nss = SPI_NSS_SOFT;
spiConfig.baudrateDiv = SPI_BAUDRATE_DIV_4;
spiConfig.firstBit = SPI_FIRSTBIT_MSB;
spiConfig.crcPolynomial =0;
SPI_Config(FLASH_SPI_BUS, &spiConfig);
/* Enable SPI*/
SPI_Enable(FLASH_SPI_BUS);
}
/*!
* @brief Erase Falsh Sector
*
* @param SectorAddr:Sector Addr
*
* @retval None
*/
void W25Q16_EraseSector(uint32_t SectorAddr)
{
/* Flash Write Enable*/
W25Q16_EnableFlashWrite();
W25Q16_WaitFlashWriteEnd();
/* Select the FLASH */
FLASH_SPI_CS_CLR();
/* send cmd */
W25Q16_SendByte(W25Q16_SECTOR_ERASE);
/* send Sector Addr */
W25Q16_SendByte((SectorAddr & 0xFF0000) >> 16);
W25Q16_SendByte((SectorAddr & 0xFF00) >> 8);
W25Q16_SendByte(SectorAddr & 0xFF);
/* Deselect the FLASH */
FLASH_SPI_CS_SET();
/* wait Falsh Erase end */
W25Q16_WaitFlashWriteEnd();
}
/**
* @brief擦除FLASH扇区,整片擦除
* @param无
* @retval 无
*/
void SPI_FLASH_BulkErase(void)
{
/* 发送FLASH写使能命令 */
W25Q16_EnableFlashWrite();
/* 整块 Erase */
/* 选择FLASH: CS低电平 */
FLASH_SPI_CS_CLR();
/* 发送整块擦除指令*/
W25Q16_SendByte(W25Q16_CHIP_ERASE);
/* 停止信号 FLASH: CS 高电平 */
FLASH_SPI_CS_SET();
/* 等待擦除完毕*/
W25Q16_WaitFlashWriteEnd();
}
/*!
* @brief Flash Write Page
*
* @param pBuffer: pointer to the Write buffer
*
* @param WriteAddr: Write the flash Address
*
* @param NumToWrite: the number of byte to write
*
* @retval None
*/
void W25Q16_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumToWrite)
{
/* Enable Flash Write */
W25Q16_EnableFlashWrite();
/* Select the FLASH */
FLASH_SPI_CS_CLR();
/* W25Q16 Page Program */
W25Q16_SendByte(W25Q16_PAGE_PROGRAM);
W25Q16_SendByte((WriteAddr & 0xFF0000) >> 16);
W25Q16_SendByte((WriteAddr & 0xFF00) >> 8);
W25Q16_SendByte(WriteAddr & 0xFF);
if (NumToWrite > W25Q16_FLASH_PAGE_SIZE)
{
NumToWrite = W25Q16_FLASH_PAGE_SIZE;
}
/* Write data to flash*/
while (NumToWrite--)
{
/* 发送当前要写入的字节数据 */
W25Q16_SendByte(*pBuffer);
/* 指向下一字节数据 */
pBuffer++;
}
/* Deselect the FLASH */
FLASH_SPI_CS_SET();
/* Wait Write End*/
W25Q16_WaitFlashWriteEnd();
}
/*!
* @brief Flash Write Buffer
*
* @param pBuffer: pointer to the Write buffer
*
* @param WriteAddr: Write the flash Address
*
* @param NumToWrite: the number of byte to write
*
* @retval None
*/
void W25Q16_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumToWrite)
{
uint8_t i = 0;
uint8_t PageNumber = 0;
uint8_t RemainNumber = 0;
uint8_t PageRemainNumber = 0;
/* complete Page need to write */
PageNumber =NumToWrite / W25Q16_FLASH_PAGE_SIZE;
/* Remain byte */
RemainNumber = NumToWrite % W25Q16_FLASH_PAGE_SIZE;
/* if WriteAddr align */
if ((WriteAddr % W25Q16_FLASH_PAGE_SIZE) == 0)
{
/* NumToWrite < W25Q16_FLASH_PAGE_SIZE */
if (PageNumber == 0)
{
W25Q16_WritePage(pBuffer, WriteAddr, NumToWrite);
}
else
{
/* write complete Page */
for (i = 0; i < PageNumber; i++)
{
W25Q16_WritePage(pBuffer, WriteAddr, W25Q16_FLASH_PAGE_SIZE);
WriteAddr +=W25Q16_FLASH_PAGE_SIZE;
pBuffer += W25Q16_FLASH_PAGE_SIZE;
}
/* write remain data */
if (RemainNumber > 0)
{
W25Q16_WritePage(pBuffer, WriteAddr, RemainNumber);
}
}
}
else
{
/* Write Address Page Remain Number */
PageRemainNumber = W25Q16_FLASH_PAGE_SIZE - (WriteAddr % W25Q16_FLASH_PAGE_SIZE);
/* NumToWrite < W25Q16_FLASH_PAGE_SIZE */
if (PageNumber == 0)
{
if (RemainNumber > PageRemainNumber)
{
/* write all over the current page */
W25Q16_WritePage(pBuffer, WriteAddr, PageRemainNumber);
WriteAddr +=PageRemainNumber;
pBuffer += PageRemainNumber;
RemainNumber = RemainNumber - PageRemainNumber;;
/* write remain data */
W25Q16_WritePage(pBuffer, WriteAddr, RemainNumber);
}
else
{
W25Q16_WritePage(pBuffer, WriteAddr, RemainNumber);
}
}
else
{
/* write all over the current page */
W25Q16_WritePage(pBuffer, WriteAddr, PageRemainNumber);
WriteAddr +=PageRemainNumber;
pBuffer += PageRemainNumber;
NumToWrite -= PageRemainNumber;
PageNumber =NumToWrite / W25Q16_FLASH_PAGE_SIZE;
RemainNumber = NumToWrite % W25Q16_FLASH_PAGE_SIZE;
/* write complete Page */
for (i = 0; i < PageNumber; i++)
{
W25Q16_WritePage(pBuffer, WriteAddr, W25Q16_FLASH_PAGE_SIZE);
WriteAddr +=W25Q16_FLASH_PAGE_SIZE;
pBuffer += W25Q16_FLASH_PAGE_SIZE;
}
/* write remain data */
if (RemainNumber > 0)
{
W25Q16_WritePage(pBuffer, WriteAddr, RemainNumber);
}
}
}
}
/*!
* @brief Flash Read Buffer
*
* @param pBuffer: pointer to the read buffer
*
* @param ReadAddr: read the flash Address
*
* @param NumToWrite: the number of byte to read
*
* @retval None
*/
void W25Q16_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumToRead)
{
uint16_t i = 0;
/* Select the FLASH */
FLASH_SPI_CS_CLR();
/* send read cmd */
W25Q16_SendByte(W25Q16_READ_DATA);
W25Q16_SendByte((ReadAddr & 0xFF0000) >> 16);
W25Q16_SendByte((ReadAddr& 0xFF00) >> 8);
W25Q16_SendByte(ReadAddr & 0xFF);
/* read data to flash*/
for (i = 0; i < NumToRead; i++)
{
*pBuffer = W25Q16_SendByte(W25Q16_DUMMY_BYTE);
pBuffer++;
}
/* Deselect the FLASH */
FLASH_SPI_CS_SET();
}
/*!
* @brief Flash Read ID
*
* @param None
*
* @retval None
*/
uint32_t W25Q16_ReadFlashID(void)
{
uint32_t TempBuffer = {0};
uint32_t FlashID = 0;
/* Select the FLASH */
FLASH_SPI_CS_CLR();
/* send read ID cmd */
W25Q16_SendByte(W25Q16_JEDEC_DEVICE_ID);
TempBuffer = W25Q16_SendByte(W25Q16_DUMMY_BYTE);
TempBuffer = W25Q16_SendByte(W25Q16_DUMMY_BYTE);
TempBuffer = W25Q16_SendByte(W25Q16_DUMMY_BYTE);
/* Deselect the FLASH */
FLASH_SPI_CS_SET();
FlashID = (TempBuffer << 16) | (TempBuffer << 8) | TempBuffer;
return FlashID;
}
/*!
* @brief Read Flash DeviceID
*
* @param None
*
* @retval None
*/
uint32_t W25Q16_ReadFlashDeviceID(void)
{
uint32_t DeviceID = 0;
/* Select the FLASH */
FLASH_SPI_CS_CLR();
/* Send W25Q16 DeviceID cmd */
W25Q16_SendByte(W25Q16_DEVICE_ID);
W25Q16_SendByte(W25Q16_DUMMY_BYTE);
W25Q16_SendByte(W25Q16_DUMMY_BYTE);
W25Q16_SendByte(W25Q16_DUMMY_BYTE);
/* Read DeviceID from the FLASH */
DeviceID = W25Q16_SendByte(W25Q16_DUMMY_BYTE);
/* Deselect the FLASH */
FLASH_SPI_CS_SET();
return DeviceID;
}
/*!
* @brief SPI read Byte from flash
*
* @param None
*
* @retval None
*/
uint8_t W25Q16_ReadByte(void)
{
uint8_t temp = 0;
temp = W25Q16_SendByte(W25Q16_DUMMY_BYTE);
return temp;
}
/*!
* @brief SPI Send Byte to flash
*
* @param None
*
* @retval None
*/
uint8_t W25Q16_SendByte(uint8_t data)
{
/* SPI master send data */
while (SPI_I2S_ReadStatusFlag(FLASH_SPI_BUS, SPI_FLAG_TXBE) == RESET);
SPI_I2S_TxData(FLASH_SPI_BUS, data);
/* SPI slave receive data */
while (SPI_I2S_ReadStatusFlag(FLASH_SPI_BUS, SPI_FLAG_RXBNE) == RESET);
return SPI_I2S_RxData(FLASH_SPI_BUS);
}
/*!
* @brief Enable Flash Write
*
* @param None
*
* @retval None
*/
void W25Q16_EnableFlashWrite(void)
{
FLASH_SPI_CS_CLR();
/* send W25Q16 Write Enable cmd */
W25Q16_SendByte(W25Q16_WRITE_ENABLE);
FLASH_SPI_CS_SET();
}
/*!
* @brief Wait Flash Write End
*
* @param None
*
* @retval None
*/
void W25Q16_WaitFlashWriteEnd(void)
{
uint8_t RegStatus = 0;
FLASH_SPI_CS_CLR();
/* send Read W25Q16 Status cmd */
W25Q16_SendByte(W25Q16_READ_STATUS_REG);
do
{
/* Read W25Q16 Status Reg */
RegStatus = W25Q16_SendByte(W25Q16_DUMMY_BYTE);
}
while ((RegStatus & W25Q16_WIP_FLAG) != 0);
FLASH_SPI_CS_SET();
}
/*!
* @brief Flash Into PowerDown
*
* @param None
*
* @retval None
*/
void W25Q16_IntoPowerDown(void)
{
FLASH_SPI_CS_CLR();
/* send PowerDown cmd*/
W25Q16_SendByte(W25Q16_POWER_DOWN);
FLASH_SPI_CS_SET();
}
/**@} end of group APM32E103_EVAL_Functions */
/**@} end of group Board_APM32E103_EVAL */
/**@} end of group Board */
SPI Flash 驱动.h
/* Define to prevent recursive inclusion */
#ifndef __BSP_SPI_FLASH_H
#define __BSP_SPI_FLASH_H
/* Includes */
#include "main.h"
/* W25Q16 */
#define W25Q16_FLASH_ID 0xEF4015
#define W25Q16_FLASH_PAGE_SIZE 256
#define W25Q16_FLASH_WRITE_ADDR 0x00000
#define W25Q16_FLASH_READ_ADDR W25Q16_FLASH_WRITE_ADDR
/* W25Q16 CMD */
#define W25Q16_WRITE_ENABLE 0x06
#define W25Q16_WRITE_DISABLE 0x04
#define W25Q16_READ_STATUS_REG 0x05
#define W25Q16_WRITE_STATUS_REG 0x01
#define W25Q16_READ_DATA 0x03
#define W25Q16_FAST_READ_DATA 0x0B
#define W25Q16_FAST_RAED_DUAL 0x3B
#define W25Q16_PAGE_PROGRAM 0x02
#define W25Q16_BLOCK_ERASE 0xD8
#define W25Q16_SECTOR_ERASE 0x20
#define W25Q16_CHIP_ERASE 0xC7
#define W25Q16_POWER_DOWN 0xB9
#define W25Q16_RELEASE_POWER_DOWN 0xAB
#define W25Q16_DEVICE_ID 0xAB
#define W25Q16_MANUFACT_DEVICE_ID 0x90
#define W25Q16_JEDEC_DEVICE_ID 0x9F
#define W25Q16_WIP_FLAG 0x01
#define W25Q16_DUMMY_BYTE 0xFF
/* FLASH CS */
#define FLASH_SPI_BUS SPI1
#define FLASH_SPI_BUS_CLK RCM_APB2_PERIPH_SPI1
#define FLASH_SPI_MOSI_PIN GPIO_PIN_7
#define FLASH_SPI_MISO_PIN GPIO_PIN_6
#define FLASH_SPI_SCK_PIN GPIO_PIN_5
#define FLASH_SPI_GPIO_CLK RCM_APB2_PERIPH_GPIOA
#define FLASH_SPI_GPIO_PORT GPIOA
#define FLASH_SPI_CS_PIN GPIO_PIN_4
#define FLASH_SPI_CS_GPIO_CLK RCM_APB2_PERIPH_GPIOA
#define FLASH_SPI_CS_GPIO_PORT GPIOA
//#define FLASH_SPI_MOSI_SOURCE GPIO_PIN_SOURCE_15
//#define FLASH_SPI_MISO_SOURCE GPIO_PIN_SOURCE_14
//#define FLASH_SPI_SCK_SOURCE GPIO_PIN_SOURCE_13
//#define FLASH_SPI_CS_SOURCE GPIO_PIN_SOURCE_12
//#define FLASH_SPI_GPIO_AF GPIO_AF_PIN1
#define FLASH_SPI_MOSI_CLR() GPIO_ResetBit(FLASH_SPI_GPIO_PORT, FLASH_SPI_MOSI_PIN)
#define FLASH_SPI_MOSI_SET() GPIO_SetBit(FLASH_SPI_GPIO_PORT, FLASH_SPI_MOSI_PIN)
#define FLASH_SPI_MISO_CLR() GPIO_ResetBit(FLASH_SPI_GPIO_PORT, FLASH_SPI_MISO_PIN)
#define FLASH_SPI_MISO_SET() GPIO_SetBit(FLASH_SPI_GPIO_PORT, FLASH_SPI_MISO_PIN)
#define FLASH_SPI_SCK_CLR() GPIO_ResetBit(FLASH_SPI_GPIO_PORT, FLASH_SPI_SCK_PIN)
#define FLASH_SPI_SCK_SET() GPIO_SetBit(FLASH_SPI_GPIO_PORT, FLASH_SPI_SCK_PIN)
#define FLASH_SPI_CS_CLR() GPIO_ResetBit(FLASH_SPI_CS_GPIO_PORT, FLASH_SPI_CS_PIN)
#define FLASH_SPI_CS_SET() GPIO_SetBit(FLASH_SPI_CS_GPIO_PORT, FLASH_SPI_CS_PIN)
/**@} end of group APM32E103_EVAL_Macros*/
/** @defgroup APM32E103_EVAL_Structures Structures
@{
*/
typedef struct {
uint32_t deviceID;
uint32_t flashID;
} W25Q16_INFO_T;
/**@} end of group APM32E103_EVAL_Structures*/
/** @defgroup APM32E103_EVAL_Functions Functions
@{
*/
void W25Q16_IntoPowerDown(void);
uint8_t W25Q16_SendByte(uint8_t byte);
uint8_t W25Q16_ReadByte(void);
void W25Q16_SPI_Init(void);
void W25Q16_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumToWrite);
void W25Q16_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumToWrite);
void W25Q16_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumToRead);
uint32_t W25Q16_ReadFlashID(void);
uint32_t W25Q16_ReadFlashDeviceID(void);
void W25Q16_EnableFlashWrite(void);
void W25Q16_WaitFlashWriteEnd(void);
void W25Q16_EraseSector(uint32_t SectorAddr);
void SPI_FLASH_BulkErase(void);
#endif /* __BSP_SPI_FLASH_H */
在命令 0x9F 发出后,应在 MISO 上看到连续 3 字节回应
页:
[1]