STM32存储左右互搏 SPI总线读写FRAM MB85RS2M

STM32存储左右互搏 SPI总线读写FRAM MB85RS2M

在中低容量存储领域,除了FLASH的使用,,还有铁电存储器FRAM的使用,相对于FLASH,FRAM写操作时不需要预擦除,所以执行写操作时可以达到更高的速度,其主要优点为没有FLASH持续写操作跨页地址需要变换的要求。相比于SRAM则具有非易失性, 因此价格方面会高一些。MB85RS2M是256K Byte(2M bit)的FRAM,能够按字节进行写入且没有写入等待时间。其管脚功能兼容FLASH:

这里介绍STM32访问FRAM MB85RS2M的例程。采用STM32CUBEIDE开发平台,以STM32F401CCU6芯片为例,通过STM32 SPI硬件电路实现读写操作,通过USB虚拟串口进行控制。

STM32工程配置

首先建立基本工程并设置时钟:


配置硬件SPI接口:


增加配置PA4作为SPI软件代码控制输出的片选管脚

并增加PA2和PA3连接到/WP和/HOLD管脚,并保持输出高电平:

配置USB作为通讯口:




保存并生成初始工程代码:

STM32工程代码

USB虚拟串口的使用参考:STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL)

代码里用到的微秒延时函数参考: STM32 HAL us delay(微秒延时)的指令延时实现方式及优化

这里的测试逻辑实现为:当USB虚拟串口收到任何数据时,STM32在内部对MB85RS2M写入从USB虚拟串口收到的数据,然后再回读出来,通过USB虚拟串口发送出去。

USB接收数据的代码:

bash 复制代码
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
	extern uint8_t cmd;
	extern uint8_t * RData;
	extern uint32_t RDataLen;

	RData = Buf;
	RDataLen = *Len;
	cmd = 1;

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

新建MB85RS2M访问函数头文件MB85RS2M.h

bash 复制代码
#ifndef INC_MB85RS2M_H_
#define INC_MB85RS2M_H_
#include "main.h"

/*To define operation code*/
#define WREN 0x06    //Set Write Enable Latch
#define WRDI 0x04    //Reset Write Enable Latch
#define RDSR 0x05    //Read Status Register
#define WRSR 0x01    //Write Status Register
#define READ 0x03    //Read Memory Code
#define WRITE 0x02   //Write Memory Code
#define RDID 0x9F    //Read Device ID

#define MB85RS2M_ID 0x03487F04

uint32_t MB85RS2M_ReadID(void);
uint8_t MB85RS2M_Init(void);
void MB85RS2M_Set_Write_Enable_Latch(void);
void MB85RS2M_Reset_Write_Enable_Latch(void);
void MB85RS2M_Write_Status_Register(uint8_t SRV);
uint8_t MB85RS2M_Read_Status_Register(void);
void MB85RS2M_Write_Memory(uint8_t * wd, uint32_t addr, uint32_t len);
void MB85RS2M_Read_Memory(uint8_t * rd, uint32_t addr, uint32_t len);

#endif /* INC_MB85RS2M_H_ */

新建MB85RS16访问函数源文件MB85RS2M.c

bash 复制代码
//Written by Pegasus Yu in 2023

#include "MB85RS2M.h"
#include <string.h>

#define SPI1_CS_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define SPI1_CS_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
extern SPI_HandleTypeDef hspi1;
extern void PY_Delay_us_t(uint32_t Delay);


uint32_t MB85RS2M_ReadID(void)
{
	uint8_t ftd[5];
	uint8_t frd[5];
	uint8_t Manufacturer_ID;
	uint8_t Continuation_Code;
	uint8_t Product_ID_L;
	uint8_t Product_ID_H;

	ftd[0]=RDID;


	SPI1_CS_L;

	HAL_SPI_TransmitReceive(&hspi1, ftd, frd, 5, 0xFFFFFFFF);

	SPI1_CS_H;

	Manufacturer_ID = frd[1];
	Continuation_Code = frd[2];
	Product_ID_L = frd[3];
	Product_ID_H = frd[4];

	return ((Product_ID_H<<24)|(Product_ID_L<<16)|(Continuation_Code<<8)|(Manufacturer_ID));
}

uint8_t MB85RS2M_Init(void)
{
	uint8_t st = 0;

	for(uint8_t i=0; i<4; i++)
	{
		if(MB85RS2M_ReadID()==MB85RS2M_ID)
		{
			st = 1;
			break;
		}

	}

	return st;

}


/*
 * WEL is reset after the following operations which means every write operation must follow once WREN operation MB85RS2M_Set_Write_Enable_Latch().
 * After power ON.
 * After WRDI command recognition.
 * At the rising edge of CS after WRSR command recognition.
 * At the rising edge of CS after WRITE command recognition.
 */
void MB85RS2M_Set_Write_Enable_Latch(void)
{
    uint8_t cmd = WREN;
	SPI1_CS_L;

	HAL_SPI_Transmit(&hspi1, &cmd, 1, 0xFFFFFFFF);

	SPI1_CS_H;
}


void MB85RS2M_Reset_Write_Enable_Latch(void)
{
    uint8_t cmd = WRDI;
	SPI1_CS_L;

	HAL_SPI_Transmit(&hspi1, &cmd, 1, 0xFFFFFFFF);

	SPI1_CS_H;
}


void MB85RS2M_Write_Status_Register(uint8_t SRV)
{
    uint8_t data[2];
    data[0] = WRSR;
    data[1] = SRV;

    MB85RS2M_Set_Write_Enable_Latch();

    PY_Delay_us_t(2);

	SPI1_CS_L;

	HAL_SPI_Transmit(&hspi1, data, 2, 0xFFFFFFFF);

	SPI1_CS_H;
}

uint8_t MB85RS2M_Read_Status_Register(void)
{
    uint8_t cmd[2];
    uint8_t data[2];
    uint8_t SRV;

    cmd[0] = RDSR;

	SPI1_CS_L;

	HAL_SPI_TransmitReceive(&hspi1, cmd, data, 2, 0xFFFFFFFF);

	SPI1_CS_H;

	SRV = data[1];
	return SRV;

}

/*
 * wd: data buffer pointer
 * addr: address to operate for MB85RS2M
 * len: data length to be written
 */

void MB85RS2M_Write_Memory(uint8_t * wd, uint32_t addr, uint32_t len)
{
    uint8_t data[len+4];
    data[0] = WRITE;
    data[1] = (uint8_t)(addr>>16);
    data[2] = (uint8_t)(addr>>8);
    data[3] = (uint8_t)addr;

    memcpy(data+4, wd, len);

    MB85RS2M_Set_Write_Enable_Latch();

    PY_Delay_us_t(2);

	SPI1_CS_L;

	HAL_SPI_Transmit(&hspi1, data, len+4, 0xFFFFFFFF);

	SPI1_CS_H;
}


/*
 * rd: data buffer pointer
 * addr: address to operate for MB85RS2M
 * len: data length to be written
 */

void MB85RS2M_Read_Memory(uint8_t * rd, uint32_t addr, uint32_t len)
{
    uint8_t cmd[len+4];
    uint8_t data[len+4];
    cmd[0] = READ;
    cmd[1] = (uint8_t)(addr>>16);
    cmd[2] = (uint8_t)(addr>>8);
    cmd[3] = (uint8_t)addr;

	SPI1_CS_L;

	HAL_SPI_TransmitReceive(&hspi1, cmd, data , len+4, 0xFFFFFFFF);

	SPI1_CS_H;

	memcpy(rd, data+4, len);
}

完整的main.c主文件代码如下:

bash 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
//Written by Pegasus Yu in 2023
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "MB85RS2M.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
__IO float usDelayBase;
void PY_usDelayTest(void)
{
  __IO uint32_t firstms, secondms;
  __IO uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  usDelayBase = ((float)counter)/1000;
}

void PY_Delay_us_t(uint32_t Delay)
{
  __IO uint32_t delayReg;
  __IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}

void PY_usDelayOptimize(void)
{
  __IO uint32_t firstms, secondms;
  __IO float coe = 1.0;

  firstms = HAL_GetTick();
  PY_Delay_us_t(1000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  usDelayBase = coe*usDelayBase;
}

void PY_Delay_us(uint32_t Delay)
{
  __IO uint32_t delayReg;

  __IO uint32_t msNum = Delay/1000;
  __IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t cmd=0;          //for status control
uint8_t * RData;        //USB rx data pointer
uint32_t RDataLen;      //USB rx data length
uint8_t * TData;        //USB tx data pointer
uint32_t TDataLen;      //USB tx data length


uint8_t MB85RS2M_Status = 0;
uint16_t MB85RS2M_OPADDR = 0;
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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_USB_DEVICE_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */
  PY_usDelayTest();
  PY_usDelayOptimize();

  MB85RS2M_Status = MB85RS2M_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	if(cmd==1)
	{
		cmd = 0;

		if(MB85RS2M_Status==1)
		{
			MB85RS2M_OPADDR = 0; //Set operation address here

			MB85RS2M_Write_Memory(RData, MB85RS2M_OPADDR, RDataLen);

	        PY_Delay_us_t(2);

			uint8_t rd[RDataLen];
			MB85RS2M_Read_Memory(rd, MB85RS2M_OPADDR, RDataLen);

			TData = rd;
			TDataLen = RDataLen;
			CDC_Transmit_FS(TData, TDataLen);

		}
		else
		{
			CDC_Transmit_FS("MB85RS2M ID read failure!\r\n", strlen("MB85RS2M ID read failure!\r\n"));
		}

	}

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief SPI1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  /* SPI1 parameter configuration*/
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4, GPIO_PIN_SET);

  /*Configure GPIO pins : PA2 PA3 PA4 */
  GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

STM32范例测试

上述范例的测试效果如下:

STM32例程下载

STM32F401CCU6 I2C总线读写FRAM MB85RS2M例程

--End--

相关推荐
wxmtwfx1 天前
Linux内核时钟芯片DS3232驱动源码分析
linux·驱动开发·spi·i2c·ds3232
俊俊谢1 天前
华大HC32F460轮询方式SPI通讯配置
单片机·嵌入式硬件·spi·hc32f460
trayvontang2 天前
SpringBoot自动配置原理
spring boot·自动配置·spi·自动配置原理
喜喜安16 天前
串口、IIC、SPI通信协议
uart·iic·spi
时光の尘17 天前
嵌入式面试八股文(十九)·裸机开发与RTOS开发的区别
linux·stm32·单片机·iic·rtos·spi
一个平凡而乐于分享的小比特23 天前
I2C、SPI、CAN、串口通信详细对比
can·uart·spi·i2c
一个平凡而乐于分享的小比特25 天前
SPI通信协议全面详解
spi·通信协议
Darken031 个月前
什么是SPI协议?
单片机·spi
sheepwjl2 个月前
《嵌入式硬件(十九):基于IMX6ULL的SPI操作》
单片机·嵌入式硬件·spi·adxl345
时光の尘2 个月前
STM32外设面试速通:UART、I2C、SPI、DMA、RAM/FLASH与ADC/DAC
stm32·dma·iic·adc·flash·spi·ram