Peixu 发表于 2025-6-16 16:22

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 */


慢动作 发表于 2025-6-19 00:50

在命令 0x9F 发出后,应在 MISO 上看到连续 3 字节回应
页: [1]
查看完整版本: APM32F402 驱动 SPI Flash Demo