STM32F429IGT6使用CubeMX配置SPI通信(W25Q256芯片)

1、硬件电路

STM32F429IGT6使用CubeMX配置SPI通信(W25Q256芯片)_第1张图片

需要系统性的看一下W25Q256芯片手册 

2、设置RCC,选择高速外部时钟HSE,时钟设置为180MHz

3、配置SPI

STM32F429IGT6使用CubeMX配置SPI通信(W25Q256芯片)_第2张图片

 

4、生成工程配置

 

5、相关代码

#define sFLASH_ID 0XEF4019 // W25Q256

#define SPI_FLASH_PageSize 256
#define SPI_FLASH_PerWritePageSize 256

#define W25Q256_WriteEnable 0x06    // 写使能指令
#define W25Q256_WriteDisable 0x04   // 写屏蔽指令
#define W25Q256_ReadStatusReg 0x05  // 读取状态寄存器1
#define W25Q256_WriteStatusReg 0x01 // 写入状态寄存器1
//#define W25Q256_ReadData 0x03       // 3字节模式读取数据指令
#define W25Q256_ReadData 0x13       // 4字节模式读取数据指令
#define W25Q256_FastReadData 0x0B   //
#define W25Q256_FastReadDual 0x3B
#define W25Q256_PageProgram 0x02 // 页写入指令
#define W25Q256_BlockErase 0xD8
#define W25Q256_SectorErase 0x20
#define W25Q256_ChipErase 0xC7
#define W25Q256_PowerDown 0xB9
#define W25Q256_ReleasePowerDown 0xAB
#define W25Q256_DeviceID 0xAB
#define W25Q256_ManufactDeviceID 0x90
#define W25Q256_JedecDeviceID 0x9F
#define W25Q256_Enter4ByteMode 0xB7 // 4字节地址模式指令
#define W25Q256_ReadStatusRegister3 0x15
#define W25Q256_WriteStatusRegister3 0x11   //写状态寄存器3指令
#define WIP_Flag 0x01
#define Dummy_Byte 0xFF

#define FLASH_WriteAddress 0x00000
#define FLASH_ReadAddress FLASH_WriteAddress
#define FLASH_SectorToErase FLASH_WriteAddress

/* 获取缓冲区的长度 */
#define TxBufferSize1 (countof(TxBuffer1) - 1)
#define RxBufferSize1 (countof(TxBuffer1) - 1)
#define countof(a) (sizeof(a) / sizeof(*(a)))
#define BufferSize (countof(Tx_Buffer) - 1)

#define FLASH_WriteAddress 0x00000
#define FLASH_ReadAddress FLASH_WriteAddress
#define FLASH_SectorToErase FLASH_WriteAddress

/* 发送缓冲区初始化 */
extern uint8_t ReadBuff[4096];
extern uint8_t WriteBuff[4096];

/**
 * @brief   拉低片选线
 * @param   无
 * @retval  无
 */
void SPI_FLASH_NSS_LOW(void)
{
    HAL_GPIO_WritePin(SPI5_NSS_GPIO_Port, SPI5_NSS_Pin, GPIO_PIN_RESET);
}

/**
 * @brief   拉高片选线
 * @param   无
 * @retval  无
 */
void SPI_FLASH_NSS_HIGH(void)
{
    HAL_GPIO_WritePin(SPI5_NSS_GPIO_Port, SPI5_NSS_Pin, GPIO_PIN_SET);
}

/**
 * @brief   获取 FLASH ID
 * @param   无
 * @retval  FLASH ID
 */
uint32_t SPI_FLASH_ReadID(void)
{
    uint32_t temp = 0;
    uint32_t temp0 = 0;
    uint32_t temp1 = 0;
    uint32_t temp2 = 0;
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送获取W25Q256芯片ID指令
    SPI_FLASH_SendByte(W25Q256_JedecDeviceID);
    // 接收数据
    temp0 = SPI_FLASH_ReadByte();
    temp1 = SPI_FLASH_ReadByte();
    temp2 = SPI_FLASH_ReadByte();
    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();
    temp = temp0 << 16 | temp1 << 8 | temp2;
    return temp;
}

/**
 * @brief   发送一个字节
 * @param   无
 * @retval  无
 */
void SPI_FLASH_SendByte(uint8_t ch)
{
    HAL_SPI_Transmit(&hspi5, &ch, 1, 500);
}

/**
 * @brief   发送n个字节
 * @param   pData:发送数据首地址
 * @param   data_number:发送数据个数(以字节为单位)
 * @retval  无
 */
void SPI_FLASH_SendnByte(uint8_t *pData, uint32_t data_number)
{
    HAL_SPI_Transmit(&hspi5, pData, data_number, 500);
}

/**
 * @brief   接收一个字节
 * @param   无
 * @retval  接收的数据
 */
uint8_t SPI_FLASH_ReadByte(void)
{
    uint8_t rxData = 0;
    HAL_SPI_Receive(&hspi5, &rxData, 1, 500);
    return rxData;
}

/**
 * @brief   接收n个字节
 * @param   pData:接收数据首地址
 * @param   data_number:接收数据个数(以字节为单位)
 * @retval  无
 */
void SPI_FLASH_ReadnByte(uint8_t *pData, uint32_t data_number)
{
    HAL_SPI_Receive(&hspi5, pData, data_number, 500);
}

/**
 * @brief   使能写命令
 * @param   无
 * @param   无
 * @retval  无
 */
void SPI_FLASH_WriteEnable(void)
{
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送写使能指令
    SPI_FLASH_SendByte(W25Q256_WriteEnable);
    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();
}

/**
 * @brief   等待写入、擦除等操作完成
 * @param   无
 * @param   无
 * @retval  无
 */
void SPI_FLASH_WaitForWriteEnd(void)
{
    uint8_t FLASH_Status = 0;
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送写状态寄存器1指令
    SPI_FLASH_SendByte(W25Q256_ReadStatusReg);
    do
    {
        // 获取写状态寄存器1的值并做判断。0:空闲、1:忙碌
        FLASH_Status = SPI_FLASH_ReadByte();
    } while (SET == (FLASH_Status & WIP_Flag));

    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();
}

/**
 * @brief   擦除扇区
 * @param   SectorAddr:擦除扇区首地址
 * @retval  无
 */
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
    // uint8_t ADDR[4] = {0x00,0x00,0x00,0x00};
    //  使能写命令
    SPI_FLASH_WriteEnable();
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送擦除扇区命令
    SPI_FLASH_SendByte(W25Q256_SectorErase);
    // 发送擦除地址24 ~ 31bit
    SPI_FLASH_SendByte((SectorAddr & 0xFF000000) >> 24);
    // 发送擦除地址16 ~ 23bit
    SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
    // 发送擦除地址8 ~ 15bit
    SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
    // 发送擦除地址0 ~ 7bit
    SPI_FLASH_SendByte(SectorAddr & 0xFF);
    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();
    // HAL_Delay(3000);
    // 等待擦除操作结束
    SPI_FLASH_WaitForOperaEnd();
}

/**
 * @brief   配置4字节模式
 * @param   无
 * @retval  无
 */
void SPI_FLASH_FOUR_MODE(void)
{
    uint8_t temp = 0;
    // 使能写命令
    SPI_FLASH_WriteEnable();
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送写状态寄存器3命令
    SPI_FLASH_SendByte(W25Q256_WriteStatusRegister3);
    // 发送要写的数据
    SPI_FLASH_SendByte(0x02);

    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();

    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送读状态寄存器3命令
    SPI_FLASH_SendByte(W25Q256_ReadStatusRegister3);
    // 读取数据
    temp = SPI_FLASH_ReadByte();
    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();

    if (1 == (0x02 & temp))
    {
        // 拉低片选线,开始通信
        SPI_FLASH_NSS_LOW();
        // 发送配置四字节模式指令
        SPI_FLASH_SendByte(W25Q256_Enter4ByteMode);
        // 拉高片选线,结束通信
        SPI_FLASH_NSS_HIGH();
    }
    SPI_FLASH_WaitForOperaEnd();
}

/**
 * @brief   对 FLASH 按页写入数据,调用本函数写入数据前需要先擦除扇区
 * @param   pBuffer:要写入数据的指针
 * @param   WriteAddr:写入数据地址
 * @param   NumByteToWrite:写入数据长度。必须小于等于SPI_FLASH_PerWritePageSize
 * @retval  无
 */
void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    // 使能写命令
    SPI_FLASH_WriteEnable();
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送页写入指令
    SPI_FLASH_SendByte(W25Q256_PageProgram);
    // 发送写入地址[24,31]bit
    SPI_FLASH_SendByte((WriteAddr & 0xFF000000) >> 24);
    // 发送写入地址[16,23]bit
    SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    // 发送写入地址[8,15]bit
    SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    // 发送写入地址[0,7]bit
    SPI_FLASH_SendByte(WriteAddr & 0xFF);
    if (NumByteToWrite > SPI_FLASH_PerWritePageSize)
    {
        NumByteToWrite = SPI_FLASH_PerWritePageSize;
        printf("256\r\n");
    }
    for (int i = 0; i < NumByteToWrite; i++)
    {
        SPI_FLASH_SendByte(pBuffer[i]);
    }
    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();
}

/**
 * @brief   FALSH不定量数据写入函数,调用本函数写入数据前需要先擦除扇区
 * @param   pBuffer:要写入数据的指针
 * @param   WriteAddr:写入数据地址
 * @param   NumByteToWrite:写入数据长度
 * @retval  无
 */
void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    uint8_t NumOfPage = 0;
    uint8_t NumOfSingle = 0;
    uint8_t Addr = 0;
    uint8_t count = 0;
    uint8_t temp = 0;
    /*mod 运算求余,若 writeAddr 是 SPI_FLASH_PageSize 整数倍,运算结果 Addr 值为0*/
    Addr = WriteAddr % SPI_FLASH_PageSize;
    /* 差 count 个数据值,刚好可以对齐到页地址 */
    count = SPI_FLASH_PageSize - Addr;
    /* 计算出要写多少整数页 */
    NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
    /*mod 运算求余,计算出剩余不满一页的字节数 */
    NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
    /* Addr=0, 则 WriteAddr 刚好按页对齐 aligned */
    if (0 == Addr)
    {
        /*NumByteToWrite < SPI_FLASH_PageSize*/
        if (0 == NumOfPage)
        {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
            SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
        }
        else
        {
            /* 先把整数页都写了 */
            for (int i = 0; i < NumOfPage; i++)
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
                SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            /* 若有多余的不满一页的数据,把它写完 */
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
            SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
        }
    }
    else /* 若地址与 SPI_FLASH_PageSize 不对齐 */
    {
        /*NumByteToWrite < SPI_FLASH_PageSize*/
        if (0 == NumOfPage)
        {
            /* 当前页剩余的 count 个位置比 NumOfSingle 小,写不完 */
            if (NumOfSingle > count)
            {
                temp = NumOfSingle - count;
                /* 先写满当前页 */
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
                SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
                WriteAddr += count;
                pBuffer += count;
                /* 再写剩余的数据 */
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
                SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
            }
            else /* 当前页剩余的 count 个位置能写完 NumOfSingle 个数据 */
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
                SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
            }
        }
        else /*NumByteToWrite > SPI_FLASH_PageSize*/
        {
            /*先把距离页地址的count个数据减去,计算需要写的页数和NumOfSingle,然后写数据时先把原来减去的count个数据写入,写满当前页*/
            /*再写剩余数据,即计算好的NumOfPage和NumOfSingle*/
            NumByteToWrite -= count;
            NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
            NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
            SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
            WriteAddr += count;
            pBuffer += count;
            /* 把整数页都写了 */
            for (int i = 0; i < NumOfPage; i++)
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            if (0 != NumOfSingle)
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
                SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
            }
        }
    }
}

/**
 * @brief   读取FLASH数据
 * @param   pBuffer:存储读出数据的指针
 * @param   WriteAddr:读取地址
 * @param   NumByteToRead:读取数据长度
 * @retval  无
 */
void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送读取数据指令
    SPI_FLASH_SendByte(W25Q256_ReadData);
    // 发送读取地址[24,31]bit
    SPI_FLASH_SendByte((ReadAddr & 0xFF000000) >> 24);
    // 发送读取地址[16,23]bit
    SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    // 发送读取地址[8,15]bit
    SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    // 发送读取地址[0,7]bit
    SPI_FLASH_SendByte(ReadAddr & 0xFF);
    // 读取数据
    for (int i = 0; i < NumByteToRead; i++)
    {
        // 读取一个字节数据
        pBuffer[i] = SPI_FLASH_ReadByte();
    }
    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();
    SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
}

/**
 * @brief   等待写入、擦除等操作结束
 * @param   none
 * @param   none
 * @param   none
 * @retval  none
 */
void SPI_FLASH_WaitForOperaEnd(void)
{
    uint8_t FLASH_Status = 0;
    // 拉低片选线,开始通信
    SPI_FLASH_NSS_LOW();
    // 发送读状态寄存器1指令
    SPI_FLASH_SendByte(W25Q256_ReadStatusReg);
    do
    {
        // 接收读取状态寄存器1寄存器内容
        FLASH_Status = SPI_FLASH_ReadByte();
    } while (SET == (FLASH_Status & WIP_Flag));
    // 拉高片选线,结束通信
    SPI_FLASH_NSS_HIGH();
}
int main(void)
{
    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_SPI5_Init();
    MX_USART1_UART_Init();
    /* USER CODE BEGIN 2 */
    // 进入4字节地址模式
    SPI_FLASH_FOUR_MODE();
    printf("W25Q256 SPI readwrite test!!!\r\n");
    device_ID = SPI_FLASH_ReadID();
    printf("device_ID = 0x%x\r\n", device_ID);
    SPI_FLASH_SectorErase(0x00); // 擦除扇区数据
    // 读取擦除后的数据
    SPI_FLASH_BufferRead(ReadBuff, 0x00, 4096);
    printf("*****************读取擦出后的数据*****************\r\n");
    for (int i = 0; i < 4096; i++)
    {
        printf("ReadBuff[%d] = 0x%02x\t", i, ReadBuff[i]);
        if (0 == (i + 1) % 8 && (i + 1) >= 8)
        {
            printf("\r\n");
        }
    }
    for (int i = 0; i < 256; i++)
    {
        WriteBuff[i] = i;
    }
    SPI_FLASH_BufferWrite(WriteBuff, 0xFF, 256);
    SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
    // 读数据
    SPI_FLASH_BufferRead(ReadBuff, 0xFF, 256);
    SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
    printf("*****************读取写入后的数据*****************\r\n");
    for (int i = 0; i < 256; i++)
    {
        printf("ReadBuff[%d] = 0x%02x\t", i, ReadBuff[i]);
        if (0 == (i + 1) % 8 && (i + 1) >= 8)
        {
            printf("\r\n");
        }
    }
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        LED_TIME();
    }
    /* USER CODE END 3 */
}

6、实验现象

STM32F429IGT6使用CubeMX配置SPI通信(W25Q256芯片)_第3张图片

 

你可能感兴趣的:(stm32,单片机)