针对MB85RS2M富士通FRAM的SPI驱动程序,关联其他SPI的FRAM

针对富士通的MB85RS2M驱动程序

软件模拟SPI

首先是初始化GPIO引脚,因为是软件模拟,所以GPIO不做要求,任意引脚即可。

#define BSP_MB85RS2M
#include "bsp_mb85rs2m.h"
/*
MX_SPI1_Init
------------------
Des:SPI初始化
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
void MX_SPI1_Init(void)
{	  
    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};	  
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
    /**SPI1 GPIO Configuration
    PA5   ------> SPI1_SCK
    PA6   ------> SPI1_MISO
    PA7   ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = LL_GPIO_PIN_5|LL_GPIO_PIN_7;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	  
    GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);		
		
    GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

编写SPI的读字节程序

/*
SpiReadByte
------------------
Des:SPI读字节函数
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
static uint8_t SpiReadByte(void)
{
    uint8_t u8Index, u8Rdata = 0;
    
    FM_CLR_CLK;
    for(u8Index=0; u8Index<8; u8Index++)
    {
        if(FM_STA_MISO)
        {
            u8Rdata |= 0x80 >> u8Index;
        }
        FM_SET_CLK;    		
        FM_CLR_CLK;
    }
    return u8Rdata;
}

编写SPI写字节函数

/*
SpiWriteByte
------------------
Des:SPI写字节函数
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
void SpiWriteByte(uint8_t u8Wdata)
{
    uint8_t u8Index;
    
    FM_CLR_CLK;
    for(u8Index = 0; u8Index < 8; u8Index++)
    {
        if(u8Wdata & (0x80 >> u8Index))
        {
            FM_SET_MOSI;
        }
        else
        {
            FM_CLR_MOSI;
        }
        FM_SET_CLK;
        FM_CLR_CLK;
    }
}

下面是读写内存的函数,是重点
写一条数据到FRAM指定地址

首先看FRAM的数据手册,数据手册上说需要先对WEL寄存器进行操作,在写数据之前,首先要发送写使能的命令;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到,写数据前先要发送解锁命令后才能进行常规操作,在解锁之后发送写使能命令,然后发送要写入的地址和要写入的数据,所以程序如下:

/*
FM_Write_Datas
------------------
Des:Write memory data,在例程中时钟为18432000Hz条件下,写200字节的时间为6ms
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
uint8_t FM_Write_Datas(uint32_t u32Addr, uint8_t *pu8Buf, uint32_t u32PLen)
{
    uint32_t u32Index;
    
    if(u32PLen == 0)
    {
        return 0;
    }   
    if((u32Addr + u32PLen) > (MB85RS2M_ADDR_MAX + 1))
    {
        return 0;
    }
    /*发送解锁命令*/
    FM_CLR_CS;        
    SpiWriteByte(MB85RS256A_WREN_INST);               
    FM_SET_CS;
    /*发送写使能命令*/
    FM_CLR_CS;
    SpiWriteByte(MB85RS256A_WRITE_INST);
    SpiWriteByte((u32Addr & 0x3FFFF) >> 16);
    SpiWriteByte((u32Addr & 0xFFFF) >> 8);
    SpiWriteByte(u32Addr & 0xFF);
    for(u32Index=0; u32Index < u32PLen; u32Index++)
    {
        SpiWriteByte(pu8Buf[u32Index]);
    }
    FM_SET_CS;
		
    return 1;
}

程序解释
u32Addr为要写入的地址,pu8Buf为写入数据的指针,u32PLen为要写入数据的长度,第一个if用于长度判断,第二个if用于地址溢出判断,防止写入地址出错。然后发送解锁指令,解锁指令为0x06,为什么要先拉高片选再拉低发送写命令,是因为手册的时序图不允许连续发送命令,在一个命令发送后需要对片选进行拉高。写入命令发送后就要发送地址,因为2M的FRAM地址为24位,高位在前,所以将地址分为三个字节进行发送,发送之后就是循环发送要写入的数据,最后将片选拉高,完成写操作。

读一条数据到FRAM指定地址
读取的操作比写少了一步解锁,基本原理相同,所以不做解释,下面附上代码

/*
FM_Read_Datas
------------------
Des:Read memory data
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
uint8_t FM_Read_Datas(uint32_t u32Addr, uint8_t *pu8Buf, uint32_t u32PLen)
{
    uint32_t u32Index;
    
    if(u32PLen == 0)
    {
        return 0;
    }   
    if((u32Addr + u32PLen) > (MB85RS2M_ADDR_MAX + 1))
    {
        return 0;
    }
    FM_CLR_CS;
    SpiWriteByte(MB85RS256A_READ_INST);
    SpiWriteByte((u32Addr & 0x3FFFF) >> 16);
    SpiWriteByte((u32Addr & 0xFFFF) >> 8);
    SpiWriteByte(u32Addr & 0xFF);
    for(u32Index=0; u32Index < u32PLen; u32Index++)
    {
        pu8Buf[u32Index] = SpiReadByte();
    }
    FM_SET_CS;
		
    return 1;
}

最后是读芯片的ID

/*
FM_Read_DeviceID
------------------
Des:Read device ID
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
void FM_Read_DeviceID(uint8_t *pID)
{
    uint8_t u8Index;

    FM_CLR_CS;
    SpiWriteByte(MB85RS256A_RDID_INST);
    for(u8Index=0; u8Index < 9; u8Index++)
    {
        pID[u8Index] = SpiReadByte();
    }
    FM_SET_CS;
}

最后是读写的实际时序图
在这里插入图片描述

开启硬件SPI

第一步开始初始化引脚,GPIO初始化采用LL库,SPI采用寄存器操作,方便移植,对应不同的库函数,只需要更改相应的GPIO初始化以及使能SPI时钟即可。

/*
MX_SPI1_Init
------------------
Des:SPI初始化
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
void MX_SPI1_Init(void)
{	  
    /* Peripheral clock enable */
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
	  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
    /**SPI1 GPIO Configuration
    PA5   ------> SPI1_SCK
    PA6   ------> SPI1_MISO
    PA7   ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = LL_GPIO_PIN_5|LL_GPIO_PIN_7;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	  
    GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);	
		
	 /*SPI主机模式*/
	 SPI1->CR1|=1<<2;
	 /*NSS引脚软件模式*/
	 SPI1->CR1|=1<<9; 		
	 SPI1->CR1|=1<<8;  
	 /*使能SPI*/
	 SPI1->CR1|=1<<6; 		
}

开启硬件SPI只有四条指令,因查阅寄存器后均可使用默认值所以未做修改.
在这里插入图片描述
在这里插入图片描述
根据SPI的寄存器,我们仅使能SPI为主设备,软件管理NSS引脚,开启SPI,时钟极性和时钟相位都选择0,这点也是依据手册来的。记得将MISO,MOSI,.CLK引脚设置为复用功能,片选为推挽输出。
在这里插入图片描述
下面是读写一条数据,因为SPI设置为全双工,所以在写之前要查询发送是否为空,如果为空则发送数据,并且读引脚,读完引脚要检查读取引脚是否为空,如果为空则进行下一步,切记写之后要读引脚,否则时序会错,具体原因应该是SPI的发送寄存器和接收寄存器共用一个寄存器DR,所以在发送之后要对引脚进行读取,手册是这样说的:
在这里插入图片描述
虽然手册上说是有俩缓冲区,但是对应的是一个寄存器,在操作的过程中我发现如果用软件模拟不存在问题,因为没有使用这个寄存器,但是在硬件SPI中,对这个寄存器写之后需要进行读取,否则SPI_SR寄存器中的RXNE位将处于空状态,会引发程序不进行数据等待而直接将片选拉高。
在这里插入图片描述
附上读写一条SPI指令程序

/*
SpiWriteByte
------------------
Des: 读写一条SPI数据
NOTE:
REF:
------------------
Author:zx
Date:2023年05月19日
*/
uint8_t SpiReadWriteByte(uint8_t u8TxData)
{
	  uint16_t u16Count = 0;
	  while((SPI1->SR&1<<1) == 0)
		{
			  u16Count++;
			  if(u16Count >= 0xFFFE)
				{
					  return 0;
				}
		}
		SPI1->DR = u8TxData;
		u16Count = 0;
		while((SPI1->SR&1<<0) == 0)
		{
				u16Count++;
			  if(u16Count >= 0xFFFE)
				{
					  return 0;
				}
		}
		return SPI1->DR;
}

下面是写一条数据到FRAM

/*
FM_Write_Datas
------------------
Des: Write memory data
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
uint8_t FM_Write_Datas(uint32_t u32Addr, uint8_t *pu8Buf, uint32_t u32PLen)
{
    uint32_t u32LoopData;
    
    if(u32PLen == 0)
    {
        return 0;
    }   
    if((u32Addr + u32PLen) > (MB85RS2M_ADDR_MAX + 1))
    {
        return 0;
    }
		FM_CLR_CS;        
    SpiReadWriteByte(MB85RS256A_WREN_INST);   	
    FM_SET_CS;
    FM_CLR_CS;
    /*写命令*/		
    SpiReadWriteByte(MB85RS256A_WRITE_INST);
    SpiReadWriteByte((u32Addr & 0x3FFFF) >> 16);
    SpiReadWriteByte((u32Addr & 0xFFFF) >> 8);
    SpiReadWriteByte(u32Addr & 0xFF);
    for(u32LoopData=0; u32LoopData < u32PLen; u32LoopData++)
    {
        SpiReadWriteByte(pu8Buf[u32LoopData]);
    }
    FM_SET_CS;
		
    return 1;
}

程序的具体逻辑参考软件模拟的,是一样的

下面是读一条FRAM中的数据程序

/*
FM_Read_Datas
------------------
Des:Read memory data
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
uint8_t FM_Read_Datas(uint32_t u32Addr, uint8_t *pu8Buf, uint32_t u32PLen)
{
    uint32_t u32LoopData;
    
    if(u32PLen == 0)
    {
        return 0;
    }   
    if((u32Addr + u32PLen) > (MB85RS2M_ADDR_MAX + 1))
    {
        return 0;
    }
    FM_CLR_CS; 	
		/*读命令*/
    SpiReadWriteByte(MB85RS256A_READ_INST);
    SpiReadWriteByte((u32Addr & 0x3FFFF) >> 16);
    SpiReadWriteByte((u32Addr & 0xFFFF) >> 8);
    SpiReadWriteByte(u32Addr & 0xFF);
    for(u32LoopData=0; u32LoopData < u32PLen; u32LoopData++)
    { 
        pu8Buf[u32LoopData] = SpiReadWriteByte(0xFF);   		
    }		
    FM_SET_CS;		
    return 1;
}

最后是读取ID

/*
FM_Read_DeviceID
------------------
Des:读取设备ID
NOTE:
REF:
------------------
Author:zx
Date:2023年05月11日
*/
void FM_Read_DeviceID(uint8_t *pID)
{
    uint8_t u8LoopData;

    FM_CLR_CS;
    SpiReadWriteByte(MB85RS256A_RDID_INST);
    for(u8LoopData=0; u8LoopData < 9; u8LoopData++)
    {
        pID[u8LoopData] = SpiReadWriteByte(0xFF);
    }
    FM_SET_CS;
}

最后附上.h文件

//
//               bsp_mb85rs2m.h
// 描述:         2mFram驱动程序
//
// 履历:      
// -------------------------------
// 【1】创建模块              zx              2023年05月19日
// -------------------------------
#ifndef __HH_BSP_MB85RS2M_H_HH__
#define __HH_BSP_MB85RS2M_H_HH__	

#ifdef BSP_MB85RS2M
#define BSP_MB85RS2M_EX
#else
#define BSP_MB85RS2M_EX extern
#endif
#include "include.h"

/*MB85RS256A寄存器定义*/
/*设置写使能锁存器*/
#define MB85RS256A_WREN_INST      (0x06)		
/*写禁止*/
#define MB85RS256A_WRDI_INST      (0x04)		
/*读状态寄存器*/
#define MB85RS256A_RDSR_INST      (0x05)		
/*写状态寄存器*/
#define MB85RS256A_WRSR_INST      (0x01)		
/*读存储器数据*/
#define MB85RS256A_READ_INST      (0x03)		
/*写存储器数据*/
#define MB85RS256A_WRITE_INST     (0x02)		
#define MB85RS256A_STATUS_REG     (0x0)		  
#define MB85RS256A_INIT_STATE     (0x09)		
/*读器件ID*/
#define MB85RS256A_RDID_INST      (0x9F)		

#define FM_SET_CLK      LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_5)
#define FM_CLR_CLK      LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_5)
#define FM_SET_MOSI     LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_7)
#define FM_CLR_MOSI     LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_7)
#define FM_STA_MISO     LL_GPIO_ReadInputPort(GPIOA) & 0x0040
#define FM_SET_CS		    LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_4)
#define FM_CLR_CS		    LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_4)

void MX_SPI1_Init(void);
void FM_Read_DeviceID(uint8_t *pID);
uint8_t SpiReadWriteByte(uint8_t u8TxData);
uint8_t FM_Write_Datas(uint32_t u32Addr, uint8_t *pu8Buf, uint32_t u32PLen);
uint8_t FM_Read_Datas(uint32_t u32Addr, uint8_t *pu8Buf, uint32_t u32PLen);

#endif

在最后的最后没有附时序图,是因为SPI挂载在APB2上,APB2的时钟是72MHZ,在SPI寄存器配置速度的时候是/2,所以速度是36MHz,我的逻辑分析仪最大支持24MHz,读不出来- - 所以没图。配置为36MHz也是看手册的,手册上说器件最大支持40MHz,所以36MHz是稳稳可以跑起来的。
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值