STM32L4+HAL+QSPI+DMA读写W25Q64/128

文章目录

  • 前言
    • 参考资料:
  • STM32 CubeMX配置
  • API
    • W25QXX命令
    • 擦除
    • 写使能
    • 等待命令完成
  • 测试

前言

本文程序运行于STM32L452CEUX系列单片机(适用于L4),使用MDK5.33、STM32CubeMX,实现QSPI通过DMA四线读写W25Q64或128JV。

参考资料:

  • STM32L4X2用户手册RM0394
  • W25Q64JV数据手册
  • 安富莱V7开发板用户手册
  • ST官方例程(STM32Cube_FW_L4_V1.16.0\Projects\32L496GDISCOVERY\Examples\QSPI)

STM32 CubeMX配置

  • QSPI 外设配置
    2^23 = 8MB(25Q64), 2^24 = 16MB(25Q128)。L452仅有一个Bank。
    STM32L4+HAL+QSPI+DMA读写W25Q64/128_第1张图片

  • QSPI DMA配置
    STM32L4+HAL+QSPI+DMA读写W25Q64/128_第2张图片

  • 中断配置STM32L4+HAL+QSPI+DMA读写W25Q64/128_第3张图片

  • 时钟配置STM32L4+HAL+QSPI+DMA读写W25Q64/128_第4张图片

API

W25QXX命令

用到的命令如下:

/* For W25Q64JV or W25Q128JV */
#define WRITE_STATUS_CFG_REG_CMD                            (0x01)
#define WRITE_ENABLE_CMD                                    (0x06)
#define READ_ID_CMD2                                        (0x9F)
#define READ_STATUS_REG_CMD                                 (0x05)
#define QUAD_INOUT_FAST_READ_CMD                            (0xEB)
#define QUAD_IN_FAST_PROG_CMD                               (0x32)
#define SUBSECTOR_ERASE_CMD                                 (0x20)

W25Q64JV数据手册中的描述如下:
W25Q64Read
左上角的(1-4-4)表示指令阶段使用 1 个 IO,地址阶段使用 4 个 IO,数据阶段也是使用 4 个 IO

void QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize) 
{
    uint8_t cnt = 0;
    QSPI_CommandTypeDef sCommand = {0};
    /* 基本配置 */ 
    sCommand.InstructionMode          = QSPI_INSTRUCTION_1_LINE;        /* 1 线方式发送指令 */
    sCommand.AddressSize              = QSPI_ADDRESS_24_BITS;           /* 24 位地址 */
    sCommand.AlternateByteMode        = QSPI_ALTERNATE_BYTES_NONE;      /* 无交替字节 */
    sCommand.DdrMode                  = QSPI_DDR_MODE_DISABLE;          /* W25Q64JV 不支持 DDR */
    sCommand.DdrHoldHalfCycle         = QSPI_DDR_HHC_ANALOG_DELAY;      /* DDR 模式,数据输出延迟 */
    sCommand.SIOOMode                 = QSPI_SIOO_INST_EVERY_CMD;       /* 每次传输要发指令 */

    /* 读取数据 */ 
    sCommand.Instruction  = QUAD_INOUT_FAST_READ_CMD;        /* 24bit 地址的 4 线快速读取命令 */ 
    sCommand.DummyCycles  = 6;                      /* 空周期 */
    sCommand.AddressMode  = QSPI_ADDRESS_4_LINES;   /* 4 线地址 */ 
    sCommand.DataMode     = QSPI_DATA_4_LINES;      /* 4 线数据 */
    sCommand.NbData       = _uiSize;                /* 读取的数据大小 */
    sCommand.Address      = _uiReadAddr;            /* 读取数据的起始地址 */

    if (HAL_QSPI_Command(&hqspi, &sCommand, 10000) != HAL_OK) 
    {
        Error_Handler(); 
    }
    /* 读取 */ 
    qspi_RxCplt = 0;
    if (HAL_QSPI_Receive_DMA(&hqspi, _pBuf) != HAL_OK) 
    {
        Error_Handler(); 
    } 
    while (!qspi_RxCplt)
    {
        cnt++;
        if (cnt == 200)
        {
            cnt = 0;
            Debug("Read Timeout");
            break;
        }
        HAL_Delay(50);
    }
} 

qspi_RxCplt在接收完成中断置位:

__IO uint8_t qspi_RxCplt = 1;
void HAL_QSPI_RxCpltCallback(QSPI_HandleTypeDef *hqspi)
{
  qspi_RxCplt++;
}

擦除

写数据之前需要擦除
W25Q64JV数据手册中关于Sector擦除的描述如下:
STM32L4+HAL+QSPI+DMA读写W25Q64/128_第5张图片
左上角的(1-1-1)表示指令阶段使用 1 个 IO,地址阶段使用 1个 IO,数据阶段也是使用 1 个 IO

/* ********************************************************************************************************* 
* 函 数 名: QSPI_EraseSector 
* 功能说明: 页擦除 
* 形 参: _uiSectorAddr : 擦除该地址所在的页 
* 返 回 值: None
********************************************************************************************************* */ 
void QSPI_EraseSector(uint32_t _uiSectorAddr)
{
    QSPI_CommandTypeDef sCommand = {0};
    /* 写使能 */ 
    QSPI_WriteEnable(&hqspi);
    /* 基本配置 */ 
    sCommand.InstructionMode          = QSPI_INSTRUCTION_1_LINE;        /* 1 线方式发送指令 */
    sCommand.AddressSize              = QSPI_ADDRESS_24_BITS;           /* 32 位地址 */
    sCommand.AlternateByteMode        = QSPI_ALTERNATE_BYTES_NONE;      /* 无交替字节 */
    sCommand.DdrMode                  = QSPI_DDR_MODE_DISABLE;          /* W25Q64JV 不支持 DDR */
    sCommand.DdrHoldHalfCycle         = QSPI_DDR_HHC_ANALOG_DELAY;      /* DDR 模式,数据输出延迟 */
    sCommand.SIOOMode                 = QSPI_SIOO_INST_EVERY_CMD;       /* 仅发送一次命令 */
    /* 写序列配置 */ 
    sCommand.Instruction  = SUBSECTOR_ERASE_CMD; /* 32bit 地址的扇区擦除命令 */ 
    sCommand.DummyCycles  = 0;                        /* 不需要空周期 */
    sCommand.AddressMode  = QSPI_ADDRESS_1_LINE;      /* 1 线地址 */ 
    sCommand.NbData       = QSPI_DATA_NONE;           /* 无需发送数据 */
    sCommand.Address      = _uiSectorAddr;            /* 扇区首地址,保证是4KB整数倍 */

    if (HAL_QSPI_Command(&hqspi, &sCommand, 10000) != HAL_OK) 
    {
        //return 0; 
        Error_Handler();
    }

    QSPI_AutoPollingMemReady(&hqspi); 
  
}

写使能

写之前需要执行写使能

/* ********************************************************************************************************* 
* 函 数 名: QSPI_WriteEnable 
* 功能说明: 写使能 
* 形 参:    None
* 返 回 值: None
********************************************************************************************************* */
static void QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi)
{
    QSPI_CommandTypeDef sCommand = {0};
    QSPI_AutoPollingTypeDef sConfig;

    /* 基本配置 */ 
    sCommand.InstructionMode          = QSPI_INSTRUCTION_1_LINE;        /* 1 线方式发送指令 */
    sCommand.AddressSize              = QSPI_ADDRESS_24_BITS;           /* 24 位地址 */
    sCommand.AlternateByteMode        = QSPI_ALTERNATE_BYTES_NONE;      /* 无交替字节 */
    sCommand.DdrMode                  = QSPI_DDR_MODE_DISABLE;          /* W25Q64JV 不支持 DDR */
    sCommand.DdrHoldHalfCycle         = QSPI_DDR_HHC_ANALOG_DELAY;      /* DDR 模式,数据输出延迟 */
    sCommand.SIOOMode                 = QSPI_SIOO_INST_EVERY_CMD;       /* 每次传输要发指令 */

    /* 读取数据 */ 
    sCommand.Instruction  = WRITE_ENABLE_CMD;       /* 写使能命令 */ 
    sCommand.AddressMode  = QSPI_ADDRESS_NONE;      /* 无需地址 */
    sCommand.DataMode     = QSPI_DATA_NONE;         /* 无需数据 */
    sCommand.DummyCycles  = 0;                      /* 空周期 */

    if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        Error_Handler(); 
    }

    /* Configure automatic polling mode to wait for write enabling ---- */  
    sConfig.Match           = 0x02;
    sConfig.Mask            = 0x02;
    sConfig.MatchMode       = QSPI_MATCH_MODE_AND;
    sConfig.StatusBytesSize = 1;
    sConfig.Interval        = 0x10;
    sConfig.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;

    sCommand.Instruction    = READ_STATUS_REG_CMD;
    sCommand.DataMode       = QSPI_DATA_1_LINE;

    if (HAL_QSPI_AutoPolling(hqspi, &sCommand, &sConfig, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
}

W25Q64JV数据手册中的描述如下:
W25Q64Write

/* ********************************************************************************************************* 
* 函 数 名: QSPI_WriteBuffer 
* 功能说明: 页编程,页大小 256 字节,任意页都可以写入 
* 形 参: _pBuf : 数据源缓冲区; 
*     _uiWriteAddr :目标区域首地址,即页首地址,比如 0, 256, 512 等。
*     _usWriteSize :数据个数,不能超过页面大小,范围 1 - 256。
* 返 回 值: 1:成功, 0:失败 
********************************************************************************************************* */ 
uint8_t QSPI_WriteBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize) 
{
    uint8_t cnt = 0;
    QSPI_CommandTypeDef sCommand = {0};
    /* 写使能 */ 
    QSPI_WriteEnable(&hqspi);
    /* 基本配置 */ 
    sCommand.InstructionMode          = QSPI_INSTRUCTION_1_LINE;        /* 1 线方式发送指令 */
    sCommand.AddressSize              = QSPI_ADDRESS_24_BITS;           /* 24 位地址 */
    sCommand.AlternateByteMode        = QSPI_ALTERNATE_BYTES_NONE;      /* 无交替字节 */
    sCommand.DdrMode                  = QSPI_DDR_MODE_DISABLE;          /* W25Q64JV 不支持 DDR */
    sCommand.DdrHoldHalfCycle         = QSPI_DDR_HHC_ANALOG_DELAY;      /* DDR 模式,数据输出延迟 */
    sCommand.SIOOMode                 = QSPI_SIOO_INST_ONLY_FIRST_CMD;  /* 仅发送一次命令 */
    /* 写序列配置 */ 
    sCommand.Instruction  = QUAD_IN_FAST_PROG_CMD; /* 24bit 地址的 4 线快速写入命令 */ 
    sCommand.DummyCycles  = 0;                        /* 不需要空周期 */
    sCommand.AddressMode  = QSPI_ADDRESS_1_LINE;      /* 1 线地址 */ 
    sCommand.DataMode     = QSPI_DATA_4_LINES;        /* 4 线数据 */
    sCommand.NbData       = _usWriteSize;             /* 写数据大小 */
    sCommand.Address      = _uiWriteAddr;             /* 写入地址 */

    if (HAL_QSPI_Command(&hqspi, &sCommand, 10000) != HAL_OK) 
    {
        //return 0; 
        Error_Handler();
    }
    /* 启动传输 */ 
    qspi_TxCplt = 0;
    if (HAL_QSPI_Transmit_DMA(&hqspi, _pBuf) != HAL_OK) 
    {
        //return 0; 
        Error_Handler();
    } 
    while (!qspi_TxCplt)
    {
        cnt++;
        if (cnt == 200)
        {
            cnt = 0;
            Debug("Write Timeout");
            break;
        }
        HAL_Delay(50);
    }
    QSPI_AutoPollingMemReady(&hqspi); 
    return 1;
}

qspi_TxCplt在发送完成中断中置位:

__IO uint8_t qspi_TxCplt = 1;
void HAL_QSPI_TxCpltCallback(QSPI_HandleTypeDef *hqspi)
{
  qspi_TxCplt++; 
}

等待命令完成

STM32L4+HAL+QSPI+DMA读写W25Q64/128_第6张图片

static void QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi)
{
    QSPI_CommandTypeDef sCommand = {0};
    QSPI_AutoPollingTypeDef sConfig = {0};
    /* 基本配置 */ 
    sCommand.InstructionMode          = QSPI_INSTRUCTION_1_LINE;        /* 1 线方式发送指令 */
    sCommand.AddressSize              = QSPI_ADDRESS_24_BITS;           /* 24 位地址 */
    sCommand.AlternateByteMode        = QSPI_ALTERNATE_BYTES_NONE;      /* 无交替字节 */
    sCommand.DdrMode                  = QSPI_DDR_MODE_DISABLE;          /* W25Q64JV 不支持 DDR */
    sCommand.DdrHoldHalfCycle         = QSPI_DDR_HHC_ANALOG_DELAY;      /* DDR 模式,数据输出延迟 */
    sCommand.SIOOMode                 = QSPI_SIOO_INST_EVERY_CMD;       /* 每次传输要发指令 */

    /* 读取数据 */ 
    sCommand.Instruction    = READ_STATUS_REG_CMD;        /* 读取状态命令 */ 
    sCommand.AddressMode    = QSPI_ADDRESS_NONE;          /* 无需地址 */ 
    sCommand.DataMode       = QSPI_DATA_1_LINE;           /* 1线数据 */
    sCommand.DummyCycles    = 0;                          /* 空周期 */

    sConfig.Mask            = 0x01;
    sConfig.Match           = 0x00;
    sConfig.MatchMode       = QSPI_MATCH_MODE_AND;
    sConfig.StatusBytesSize = 1;
    sConfig.Interval        = 0x10;
    sConfig.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_QSPI_AutoPolling(hqspi, &sCommand, &sConfig, 10000) != HAL_OK) 
    {
        Error_Handler(); 
    }
}

测试

uint8_t read_buf[1024];
void Normal_Test(void)
{
    memset(read_buf, 0x79, 128);
    memset(read_buf + 128, 0x34, 128);

    QSPI_EraseSector(1);

    QSPI_WriteBuffer(read_buf, 0, 256);

    memset(read_buf, 0x77, 256);

    QSPI_ReadBuffer((uint8_t *)read_buf, 0, 256);
    
    for (size_t i = 0; i < 256; i++)
    {
        Debug("0x%2X ", read_buf[i]);
    }
}

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