使用QSPI驱动PM004MNIA

概述

STT-MRAM PM004MNIA 是一种具有 随机存取(Random Access) 特性的非易失性存储器,无需擦除操作,可进行 任意地址的读写操作。它的核心基于 自旋转移扭矩(Spin Transfer Torque, STT) 技术,通过控制电流方向来改变磁性存储单元的状态,从而实现数据的存储与读取。支持写无延迟(Write No Delay) 的特性,显著提高了存储效率。 广泛应用于需要 高速读写、低功耗和长期数据保存 的场景,例如 IoT 设备、工业控制模块、可穿戴设备、传感器系统 等。

特性 描述
存储容量 4Mbit
存储技术 自旋转移扭矩磁随机存取存储器(STT-MRAM)
随机存取能力 支持任意地址的读写操作,无需擦除,写入延迟低
接口支持 SPI 和 QPI(四线并行)
最大 SPI 频率 50MHz
低功耗 Sleep 模式下仅需 2μA
数据保留时间 可达 20 年(在 85°C 环境下)
写入耐久性 支持高达 10⁸ 次 P/E 周期
写保护功能 支持配置写保护区域,确保数据安全

附录-使用STM32H750驱动程序

1、外设初始化文件

  • quadspi.c文件
#include "quadspi.h"


QSPI_HandleTypeDef hqspi;

/**
 * @brief     quad spi 外设初始化函数
 * @param     无
 * @retval    无
 */ 
void QUADSPI_Init(void)
{
	hqspi.Instance = QUADSPI;
	hqspi.Init.ClockPrescaler = 5;									/* QSPI时钟预分频器(决定实际通信时钟频率) */
	hqspi.Init.FifoThreshold = 32;									/* FIFO阈值(单位:字节),当FIFO中的数据量达到该值时触发中断/DMA请求 */

	/* 采样移位模式:
     * HALFCYCLE:在时钟半周期时采样(提高时序余量)
     * NONE:不在半周期采样 */
	hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;

	hqspi.Init.FlashSize = 23 - 1;									/* FLASH内存大小 = 2 ^ (FlashSize + 1) */
	
	/* 片选高电平时间:
     * 表示两次传输之间CS保持高电平的最小时钟周期数 */
	hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;

	    /* 时钟模式:
     * MODE_0:CLK空闲低电平,第1边沿(上升沿)采样
     * MODE_3:CLK空闲高电平,第2边沿(下降沿)采样
     * 需与Flash芯片规格书一致 */
	hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;

	/* Flash芯片选择:
     * ID_1:使用Bank1(对应BK1_IOx引脚)
     * ID_2:使用Bank2(仅双Flash模式) */
	hqspi.Init.FlashID = QSPI_FLASH_ID_1;

	/* 双Flash模式:
     * DISABLE:单Flash模式
     * ENABLE:并联两个Flash(地址空间翻倍) */
	hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
	if (HAL_QSPI_Init(&hqspi) != HAL_OK)
	{
		Error_Handler();
	}
}

/**
 * @brief     quad spi 外设初始化回调函数
 * @param     qspiHandle:QSPI句柄
 * @retval    无
 */ 
void HAL_QSPI_MspInit(QSPI_HandleTypeDef* qspiHandle)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
	if(qspiHandle->Instance==QUADSPI)
	{
	/** Initializes the peripherals clock
	 */
		PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_QSPI;
		PeriphClkInitStruct.QspiClockSelection = RCC_QSPICLKSOURCE_D1HCLK;
		if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
		{
			Error_Handler();
		}

		/* QUADSPI clock enable */
		__HAL_RCC_QSPI_CLK_ENABLE();
		__HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_GPIOF_CLK_ENABLE();
        
		/**QUADSPI GPIO Configuration
		PB6     ------> QUADSPI_BK1_NCS
		PF6     ------> QUADSPI_BK1_IO3
		PF7     ------> QUADSPI_BK1_IO2
		PF8     ------> QUADSPI_BK1_IO0
		PF10     ------> QUADSPI_CLK
		PF9     ------> QUADSPI_BK1_IO1
		*/
		GPIO_InitStruct.Pin = GPIO_PIN_6;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
		GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_10;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
		GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
		HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
		GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
		HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
	}
}

/**
 * @brief     quad spi 外设重初始化回调函数
 * @param     qspiHandle:QSPI句柄
 * @retval    无
 */ 
void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* qspiHandle)
{

	if(qspiHandle->Instance==QUADSPI)
	{
		/* Peripheral clock disable */
		__HAL_RCC_QSPI_CLK_DISABLE();

		/**QUADSPI GPIO Configuration
		PB6     ------> QUADSPI_BK1_NCS
		PF6     ------> QUADSPI_BK1_IO3
		PF7     ------> QUADSPI_BK1_IO2
		PF8     ------> QUADSPI_BK1_IO0
		PF10     ------> QUADSPI_CLK
		PF9     ------> QUADSPI_BK1_IO1
		*/
		HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);

		HAL_GPIO_DeInit(GPIOF, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_10
							|GPIO_PIN_9);
	}
}

  • quadspi.h文件
#ifndef __QUADSPI_H__
#define __QUADSPI_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include "string.h"

extern QSPI_HandleTypeDef hqspi;

void QUADSPI_Init(void);

#ifdef __cplusplus
}
#endif

#endif /* __QUADSPI_H__ */

2、驱动文件

  • bsp_pm004mniatr.c文件
#include "bsp_pm004mniatr.h"
#include "quadspi.h"

/**
 * @brief   读NOR Flash的状态寄存器
 * @param   addr: 状态寄存器地址(寄存器1、2、3 对应地址为 0x00、0x01、0x02)
 * @retval  状态寄存器的值
 */
uint8_t pm004mniatr_read_sr(uint8_t addr)
{
    uint8_t byte;
    QSPI_CommandTypeDef qspi_command_struct = {0};

    qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    qspi_command_struct.Instruction = PM004MNIATR_COMMAND_READ_MODE_REGISTER;

    qspi_command_struct.AddressMode = QSPI_ADDRESS_1_LINE;
	qspi_command_struct.AddressSize = QSPI_ADDRESS_24_BITS;
	qspi_command_struct.Address = addr;

    qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    qspi_command_struct.DummyCycles = 2;
    qspi_command_struct.DataMode = QSPI_DATA_1_LINE;
    qspi_command_struct.NbData = 1;
    qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;
    qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    qspi_command_struct.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
    
    HAL_QSPI_Receive(&hqspi, &byte, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
    
    return byte;
}

/**
 * @brief   写NOR Flash的状态寄存器
 * @param   addr: 状态寄存器地址(寄存器1、2、3 对应地址为 0x00、0x01、0x02)
 * @param   byte:寄存器写入值
 * @retval  无
 */
void pm004mniatr_write_sr(uint8_t addr, uint8_t byte)
{

    QSPI_CommandTypeDef qspi_command_struct = {0};
    
    qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    qspi_command_struct.Instruction = PM004MNIATR_COMMAND_WRITE_MODE_REGISTER;
    qspi_command_struct.AddressMode = QSPI_ADDRESS_1_LINE;
	qspi_command_struct.AddressSize = QSPI_ADDRESS_24_BITS;
	qspi_command_struct.Address = addr;
    qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    qspi_command_struct.DummyCycles = 0;
    qspi_command_struct.DataMode = QSPI_DATA_1_LINE;
    qspi_command_struct.NbData = 1;
    qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;
    qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    qspi_command_struct.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
    
    HAL_QSPI_Transmit(&hqspi, &byte, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
}

/**
 * @brief   发送快速指令
 * @param   无
 * @retval  无
 */
void pm004mniatr_send_command(uint8_t command)
{
	QSPI_CommandTypeDef qspi_command_struct = {0};
	
	qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_1_LINE;                  /* 设置指令模式:使用1线传输模式 */
	qspi_command_struct.Instruction = command;                                      /* 设置指令代码 */
	qspi_command_struct.AddressMode = QSPI_ADDRESS_NONE;                            /* 地址模式:不使用地址 */
	qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;              /* 备用字节模式:不使用 */
	qspi_command_struct.DummyCycles = 0;                                            /* 空周期数:0 */ 
	qspi_command_struct.DataMode = QSPI_DATA_NONE;                                  /* 数据模式:不使用数据传输 */ 
	qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;                            /* DDR模式:禁用双倍数据率模式 */
	qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;               /* DDR半周期保持:使用模拟延迟 */
	qspi_command_struct.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;                        /* SIOO模式:每次发送指令 */
	
    HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
}


/**
 * @brief   启用quad spi 操作模式
 * @param   无
 * @retval  无
 */
static inline void pm004mniatr_qspi_mode_enable(void)
{
    pm004mniatr_send_command(PM004MNIATR_COMMAND_ENTER_QSPI);
}

/**
 * @brief   退出quad spi 操作模式
 * @param   无
 * @retval  无
 */
void inline pm004mniatr_qspi_mode_disable(void)
{
    pm004mniatr_send_command(PM004MNIATR_COMMAND_EXIT_QSPI);
}

/**
 * @brief   写使能NOR Flash
 * @param   无
 * @retval  无
 */
static inline void pm004mniatr_write_enable(void)
{
    pm004mniatr_send_command(PM004MNIATR_COMMAND_WRITE_ENABLE);
}

/**
 * @brief   进入睡眠模式
 * @param   无
 * @retval  无
 */
static inline void pm004mniatr_sleep(void)
{
    pm004mniatr_send_command(PM004MNIATR_COMMAND_ENTRY_SLEEP);
}


/**
 * @brief   唤醒PM004MNIA
 * @param   无
 * @retval  无
 */
static inline void pm004mniatr_wake_up(void)
{
    pm004mniatr_send_command(PM004MNIATR_COMMAND_EXIT_SLEEP);
}

/**
 * @brief   复位PM004MNIA
 * @param   无
 * @retval  无
 */
static inline void pm004mniatr_reset(void)
{
    pm004mniatr_send_command(PM004MNIATR_COMMAND_RESE_ENABLE);
    pm004mniatr_send_command(PM004MNIATR_COMMAND_RESET);
}

/**
 * @brief   初始化 PM004MNIA
 * @param   无
 * @retval  无
 */
void pm004mniatr_init(void)
{
	/* 初始化quad spi外设 */
    QUADSPI_Init();
    
    /* ------------------安全启动 --------------------*/
    delay_ms(20);
    /* 复位PM004MNIA */
    pm004mniatr_reset();
    delay_ms(10);
    /* 进入睡眠状态 */
    pm004mniatr_sleep();
    delay_ms(10);

    /* 唤醒 */
    pm004mniatr_wake_up();
    delay_ms(10);
    /* ----------------安全启动 END -----------------*/
    
	/* 启动写使能 */
	pm004mniatr_write_enable();
	/* 进入qspi操作模式 */
	pm004mniatr_qspi_mode_enable();
}


/**
 * @brief   读PM004MNIA芯片ID
 * @param   无
 * @retval  PM004MNIA芯片ID
 */
uint16_t pm004mniatr_read_id(void)
{
    uint8_t id[16];
    QSPI_CommandTypeDef qspi_command_struct = {0};
    
    qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_1_LINE;              /* 1线指令模式 */
    qspi_command_struct.Instruction = PM004MNIATR_COMMAND_READ_ID;                /* 读取指令 */
    qspi_command_struct.AddressMode = QSPI_ADDRESS_1_LINE;                      /* 无地址模式 */
    qspi_command_struct.AddressSize = QSPI_ADDRESS_24_BITS;                     /* 24位地址 */
    qspi_command_struct.Address = 0x000000;                                     /* 地址 */
    qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;          /* 无交替字节 */
    qspi_command_struct.DummyCycles = 0;                                        /* 空白周期个数 */
    qspi_command_struct.DataMode = QSPI_DATA_1_LINE;                            /* 1线数据模式 */
    qspi_command_struct.NbData = 2;
    qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;
    qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    qspi_command_struct.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
    
    HAL_QSPI_Receive(&hqspi, id, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
    
    return (((uint16_t)id[0] << 8) | id[1]);
}

/**
 * @brief   从PM004MNIA指定地址开始读取指定长度的数据  
 * @param   pbuf   : 读取到数据保存的地址
 * @param   addr   : 指定开始读取的地址
 * @param   datalen: 指定读取数据的字节数
 * @retval  无
 */
void pm004mniatr_read_data(uint32_t addr, uint8_t *pbuf, uint16_t datalen)
{
    QSPI_CommandTypeDef qspi_command_struct = {0};
    
    qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_4_LINES;
    qspi_command_struct.Instruction = PM004MNIATR_COMMAND_READ_DATA;
    qspi_command_struct.AddressMode = QSPI_ADDRESS_4_LINES;
    qspi_command_struct.AddressSize = QSPI_ADDRESS_24_BITS;
    qspi_command_struct.Address = addr;
    qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    qspi_command_struct.DummyCycles = 12;
    qspi_command_struct.DataMode = QSPI_DATA_4_LINES;
    qspi_command_struct.NbData = datalen;
    qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;
    qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    qspi_command_struct.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

    HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
    
    HAL_QSPI_Receive(&hqspi, pbuf, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
}

/**
 * @brief   PM004MNIA指定地址写入指定长度的数据
 * @note    写入数据的长度不能超过指定地址所在页的剩余字节数
 * @param   addr   : 指定开始写入数据的地址
 * @param   pbuf   : 写入数据指针
 * @param   datalen: 指定写入数据的字节数
 * @retval  无
 */
void pm004mniatr_write_data(uint32_t addr, uint8_t *pbuf, uint16_t datalen)
{
    QSPI_CommandTypeDef qspi_command_struct = {0};
    
	qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_4_LINES;
    qspi_command_struct.Instruction = PM004MNIATR_COMMAND_WRITE_DATA;
    qspi_command_struct.AddressMode = QSPI_ADDRESS_4_LINES;
    qspi_command_struct.AddressSize = QSPI_ADDRESS_24_BITS;
    qspi_command_struct.Address = addr;
    qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    qspi_command_struct.DummyCycles = 0;
    qspi_command_struct.DataMode = QSPI_DATA_4_LINES;
    qspi_command_struct.NbData = datalen;
    qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;
    qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    qspi_command_struct.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    
    HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
    
    HAL_QSPI_Transmit(&hqspi, pbuf, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
}

  • bsp_pm004mniatr.h文件
#ifndef __BSP_PM004MNIATR_H__
#define __BSP_PM004MNIATR_H__

#include "stdint.h"
#include "main.h"
#ifdef __cplusplus
extern "C" {
#endif

#define PM004MNIATR_MODE_REGISTER_1_ADDR            0x00
#define PM004MNIATR_MODE_REGISTER_2_ADDR            0x01
#define PM004MNIATR_MODE_REGISTER_3_ADDR            0x02

#define PM004MNIATR_COMMAND_READ_ID					0x9F

#define PM004MNIATR_COMMAND_ENTER_QSPI				0x35
#define PM004MNIATR_COMMAND_EXIT_QSPI				0xF5

#define PM004MNIATR_COMMAND_WRITE_ENABLE			0x06

#define PM004MNIATR_COMMAND_WRITE_MODE_REGISTER		0xB1
#define PM004MNIATR_COMMAND_READ_MODE_REGISTER		0xB5

#define PM004MNIATR_COMMAND_WRITE_DATA				0x02
#define PM004MNIATR_COMMAND_READ_DATA				0x03

#define PM004MNIATR_COMMAND_ENTRY_SLEEP				0xB9
#define PM004MNIATR_COMMAND_EXIT_SLEEP				0xAB

#define PM004MNIATR_COMMAND_RESE_ENABLE				0x66
#define PM004MNIATR_COMMAND_RESET				    0x99


void pm004mniatr_init(void);

uint8_t pm004mniatr_read_sr(uint8_t addr);
void pm004mniatr_write_sr(uint8_t addr, uint8_t byte);

void pm004mniatr_read_data(uint32_t addr, uint8_t *pbuf, uint16_t datalen);
void pm004mniatr_write_data(uint32_t addr, uint8_t *pbuf, uint16_t datalen);


#ifdef __cplusplus
}
#endif

#endif	/* __BSP_PM004MNIATR_H__ */


posted @ 2025-11-03 23:18  比特向阳  阅读(43)  评论(0)    收藏  举报