首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

【晒FRAM铁电存储器样片】+MB85RC64芯片读写测试(IO模拟IIC总线—STM32平台)

【晒FRAM铁电存储器样片】+MB85RC64芯片读写测试(IO模拟IIC总线—STM32平台)

本帖最后由 fengye5340 于 2014-8-6 22:13 编辑


硬件准备
在深入了解了富士通FRAM的特性后,这次来进行MB85RC64FRAM的详细测试。测试平台采用STM32开发板,其芯片型号是:STM32F103VBT6,这个是一个中等容量的FLASH芯片,和准备项目应用的芯片STM32F103VCT6在软件和代码是兼容的,方便了程序的修改和移植。

在开始全面测试之前,需要准备好硬件资源,这里借助STM32F0DISCOVERY上的ST-LINKV2作为仿真调试器,采用自制的CH341USB转串口芯片作为串口调试终端的连接器。一个功能比较完备的STM32F103VBT6开发板。为了实现MB85RC64 FRAM芯片的稳定测试,找了一个半成品板子(没有采用万用板连线方式,焊接后线会比较乱),见下图。



焊好四根通信线



后来发现该板子没有上拉电阻,接到开发板上必须得找一个带上拉电阻的端口,麻烦。于是,又换了一个带有上拉电阻的计数器板子,这个板子的外观如下




      
焊上四根通信线



原理图:





这样相当于为MB85RC64 FRAM做好了一个成品模块,直接接入STM32IO口就可以了。

因为STM32硬件IIC总线代码不太稳定,这里采用IO模拟IIC总线的方式,只需要两个IO端口即可,查看了一下板子,通常用PB6/PB7端口已经被板子其它IIC器件占据,这里我们修改端口IOPC6--SDA/PC7—SCL。连线图如下所示。


A 底层驱动代码实现


底层驱动主要是用IO端口模拟出IIC总线的通信时序,这个比较简单,从51时代就开始模拟IC总线,这个没有什么难度,代码如下:

#include "stm32f10x.h"
#include  "stm32f10x_it.h"
  #include  <stdio.h>
  #include  <string.h>
  #include  "stm32f10x_conf.h"
  #include  "system_stm32f10x.h"
#include  "I2C_103VCT6.H"
/* 类型定义 typedef-----------------------------------------------------------*/
/* 预定义字符     ------------------------------------------------------------*/
/* 宏定义        -------------------------------------------------------------*/
// 对I2C数据总线进行操作。
//===========================================================
// PC7--SCL
// PC6--SDA

#define I2C_PRORT         GPIOC
#define I2C_PORT_CLK      RCC_APB2Periph_GPIOC

#define I2C_SDA_PIN       GPIO_Pin_6
#define I2C_SCL_PIN       GPIO_Pin_7


#define    I2C_SDA_H         GPIO_SetBits(I2C_PRORT, I2C_SDA_PIN)
#define    I2C_SDA_L         GPIO_ResetBits(I2C_PRORT, I2C_SDA_PIN)
#define I2C_SDA_VAL       GPIO_ReadInputDataBit(I2C_PRORT,I2C_SDA_PIN)
#define    I2C_SCL_H         GPIO_SetBits(I2C_PRORT, I2C_SCL_PIN )
#define    I2C_SCL_L         GPIO_ResetBits(I2C_PRORT, I2C_SCL_PIN
/* 变量定义          ---------------------------------------------------------*/
extern void Delay_Us(__IO uint32_t nCount);
/*******************************************************************************
* 函数名称: I2C_Gpio_Config();
* 功能描述: 设置I2C引脚,用软件模拟的方法实现I2C功能
* 输入参数: void
* 返回参数: 无
********************************************************************************/
void I2C_GPIO_Init(void)
{
     GPIO_InitTypeDef GPIO_InitStructure ;

     RCC_APB2PeriphClockCmd(I2C_PORT_CLK ,ENABLE);

     //配置I2C_SCL/I2C_SDA引脚
     GPIO_InitStructure.GPIO_Pin   = I2C_SDA_PIN |I2C_SCL_PIN ;
     GPIO_InitStructure.GPIO_Mode  =  GPIO_Mode_Out_OD;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(I2C_PRORT, &GPIO_InitStructure);
     GPIO_SetBits(I2C_PRORT, I2C_SDA_PIN |I2C_SCL_PIN );
         
}
/*******************************************************************************
* 函数名称: I2C_SDA_OUT();
* 功能描述: 设置SDA端口为输出模式
* 输入参数: void
* 返回参数: 无
********************************************************************************/
void I2C_SDA_OUT(void)
{
      GPIO_InitTypeDef  GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Pin   = I2C_SDA_PIN  ;
      GPIO_InitStructure.GPIO_Mode  =  GPIO_Mode_Out_OD;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
      GPIO_Init(I2C_PRORT, &GPIO_InitStructure);
}
/*******************************************************************************
* 函数名称: I2C_SDA_IN();
* 功能描述: 设置SDA端口为输入模式
* 输入参数: void
* 返回参数: 无
********************************************************************************/
void I2C_SDA_IN(void)
{
      GPIO_InitTypeDef  GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Pin   = I2C_SDA_PIN  ;
      GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING ;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(I2C_PRORT, &GPIO_InitStructure);
}
/*******************************************************************************
* 函数名称: I2C_Start();
* 功能描述: 开始条件
* 输入参数: void
* 返回参数: 无
********************************************************************************/
void I2C_Start(void)
{
    I2C_SDA_OUT();
    I2C_SDA_H ;            
    I2C_SCL_H ;
    Delay_Us(4);
     I2C_SDA_L;   //START:when CLK is high,DATA change form high to low
    Delay_Us(4);
    I2C_SCL_L  ;//钳住I2C总线,准备发送或接收数据
  Delay_Us(4);
}
/*******************************************************************************
* 函数名称: I2C_Stop();
* 功能描述: 停止条件
* 输入参数: void
* 返回参数: 无
********************************************************************************/
void I2C_Stop(void)
{
          I2C_SDA_OUT();
        //I2C_SCL_L  ;
          I2C_SDA_L; //STOP:when CLK is high DATA change form low to high            
          Delay_Us(4);
        I2C_SCL_H ;
          Delay_Us(4);
          I2C_SDA_H ; //发送I2C总线结束信号
        Delay_Us(4);   
}
/*******************************************************************************
* 函数名称: I2C_Wait_Ack();
* 功能描述: 检查从机的应答操作
* 输入参数: 无
* 返回参数: 从机是否有应答:1--失败,0--成功
********************************************************************************/
unsigned char I2C_Wait_Ack(void)
{
    unsigned char utemp ;
   
          I2C_SDA_H ;
        Delay_Us(2);      
          I2C_SCL_H ;
        Delay_Us(2);     
          I2C_SDA_IN();  //SDA设置为输入   
        utemp = I2C_SDA_VAL;
        Delay_Us(2);
        I2C_SCL_L  ;   
        Delay_Us(2);
        I2C_SDA_OUT();
        if( utemp)       return  1 ;
        else             return  0 ;
   
}
/*******************************************************************************
* 函数名称: I2C_Ack();
* 功能描述: 产生I2C的主机应答操作
* 输入参数: 无
* 返回参数: 无
********************************************************************************/
void I2C_Ack(void)
{      
        
    I2C_SCL_L;
  I2C_SDA_OUT();   
    I2C_SDA_L;
    Delay_Us(2);
    I2C_SCL_H ;
    Delay_Us(2);
    I2C_SCL_L;
        //I2C_SDA_H;
}
/*******************************************************************************
* 函数名称: I2C_NAck();
* 功能描述: 不产生I2C的主机应答操作
* 输入参数: 无
* 返回参数: 无
********************************************************************************/
void I2C_NAck(void)
{
    I2C_SCL_L;
  I2C_SDA_OUT();   
    I2C_SDA_H;
    Delay_Us(2);
    I2C_SCL_H ;
    Delay_Us(2);
    I2C_SCL_L;
}
/*******************************************************************************
* 函数名称: I2C_Write_1();
* 功能描述: 向IIC总线发送一个1
* 输入参数: 无
* 返回参数: 无
********************************************************************************/
void I2C_Write_1(void)
{
    I2C_SDA_H;
    Delay_Us(2);
    I2C_SCL_H ;
    Delay_Us(2);
    I2C_SCL_L;        
    Delay_Us(2);
}
/*******************************************************************************
* 函数名称: I2C_Write_0();
* 功能描述: 向IIC总线发送一个0
* 输入参数: 无
* 返回参数: 无
********************************************************************************/
void I2C_Write_0(void)
{
    I2C_SDA_L;
    Delay_Us(2);
    I2C_SCL_H ;
    Delay_Us(2);
    I2C_SCL_L;        
    Delay_Us(2);
}
/*******************************************************************************
* 函数名称: I2C_Send_Byte();
* 功能描述: 向IIC总线发送一个字节的数据
* 输入参数: ucData--发送的数据
* 返回参数: 无
********************************************************************************/
void I2C_Send_Byte(unsigned char ucData)
{   
      unsigned char i;
        //I2C_SDA_OUT();   
        //I2C_SCL_L;      
      for(i = 8;i > 0;i--)
      {
          if( ucData & 0x80)  I2C_Write_1();
          else                I2C_Write_0();
        Delay_Us(2);
          ucData <<= 1;
       }                 
   
       I2C_SDA_H;
             Delay_Us(2);
           
}
/*******************************************************************************
* 函数名称: I2C_Send_Byte();
* 功能描述: 向IIC总线发送N个字节的数据
* 输入参数: pbuffer--指向发送数据存放首地址
            n--数据的个数
* 返回参数: 发送是否成功的标志:0--成功,1--失败
********************************************************************************/
unsigned char I2C_Send_NByte(unsigned char * pbuffer, unsigned char n)
{
        unsigned char i;
        for(i = 0;i < n;i++)
        {
           I2C_Send_Byte(* pbuffer);
             if(I2C_Wait_Ack()==0)     
             {
                pbuffer++;           
              }
              else
             {
               I2C_Stop();
                return 1;
              }
        }
         I2C_Stop();
         return 0;   
}
/*******************************************************************************
* 函数名称: I2C_Read_Byte();
* 功能描述: 从IIC总线读取一个字节
* 输入参数: 无
* 返回参数: 读取的数据
********************************************************************************/
unsigned char I2C_Read_Byte(void)
{   
      unsigned char  uData =0x00;
      unsigned char utemp,i;
      for(i = 8;i > 0;i--)
      {  
          I2C_SDA_H;
            Delay_Us(2);
            I2C_SCL_H ;
             Delay_Us(2);
          I2C_SDA_IN();  //SDA设置为输入      
          Delay_Us(2);
          utemp = I2C_SDA_VAL;
          uData <<= 1;
          if(utemp) uData |= 0x01;
          Delay_Us(2);
          I2C_SCL_L;      
          Delay_Us(2);
          I2C_SDA_OUT();   
   
       }                     
          I2C_SDA_H;
          Delay_Us(2);
                return( uData );
}
/*******************************************************************************
* 函数名称: I2C_Read_NByte();
* 功能描述: 从IIC总线读取N个字节的数据
* 输入参数: pbuffer--指向发送数据存放首地址
            n--数据的个数
* 返回参数: 读取的数据
********************************************************************************/
void I2C_Read_NByte(unsigned char * pbuffer, unsigned char n)
{   
        unsigned char  i;
    for(i = 0;i < n;i++)
    {
        pbuffer[i] = I2C_Read_Byte();
        if( i < (n-1))   I2C_Ack();  //应答
        else             I2C_NAck(); //无应答
    }
     
    I2C_Stop();

}

/********************************************************************************
函数名称:Delay()
功    能:延时函数
参    数:无
返 回 值:无
*********************************************************************************/
void Delay(__IO uint32_t nCount)
{
  /* Decrement nCount value */
  while (nCount != 0)
  {
        nCount--;
  }
}
/********************************************************************************
函数名称:Delay_Us()
功    能:软件延时微秒级函数
参    数:无
返 回 值:无
*********************************************************************************/
void Delay_Us(__IO uint32_t nCount)
{
  /* 10uS延迟=>10.8uS,100uS延迟=>104uS*/
  while (nCount != 0)
  {
      nCount--;  
       Delay(350);
  }
}
/********************************************************************************
函数名称:Delay_Ms()
功    能:软件延时豪秒级函数
参    数:无
返 回 值:无
*********************************************************************************/
void Delay_Ms(__IO uint32_t nCount)
{
  /* 10uS延迟=>10.8uS,100uS延迟=>104uS*/
  while (nCount != 0)
  {
    nCount--;  
    Delay(35000);
  }
}
B 应用层驱动代码实现



这个代码可以不经修改,直接应用到任何硬件平台中,主要实现FRAM器件的读写函数。为了兼容EERPOM器件,这里函数名称沿用原来的,没有重新命名和优化,因为MB85RC64兼容AT24C64EERPOM的。
extern void Delay_Ms(__IO uint32_t nCount);
extern void Delay_Us(__IO uint32_t nCount);
#define Device_Address  0xA0  //24CXX的设备地址
/*******************************************************************************
* 函数名称:
EEPROM_Read_Byte()
* 功能描述: 从24CXX的指定地址读取1个字节的数据
* 输入参数: dataaddress--数据读取的地址
* 返回参数: 读取的数据
********************************************************************************/
unsigned char EEPROM_Read_Byte(unsigned char dataaddress)
{
        unsigned char temp;
   
          I2C_Start();                         // 起始条件
          I2C_Send_Byte(Device_Address);       // 向从器件发送设备地址
          if(I2C_Wait_Ack()==0)                // 如果有从器件应答
            I2C_Send_Byte(dataaddress);      // 发送数据地址
          else
             return 0;
          if(I2C_Wait_Ack()==0)
          {
              I2C_Start();                       // 起始条件
              I2C_Send_Byte((Device_Address|0x01));
          }
          else   
             return 0;
          if(I2C_Wait_Ack()==0)   
             temp = I2C_Read_Byte();
          else
             return 0;

           I2C_NAck();
           I2C_Stop();
           return temp;
         
}
/*******************************************************************************
* 函数名称:
EEPROM_Read_NByte()
* 功能描述: 从24CXX的指定地址读取N个字节的数据
* 输入参数: pbuffer--指向保存数据地址的指针
            n--读取数据的个数
            dataaddress--数据读取的首地址
* 返回参数: 读取结果:0--成功,1--失败
********************************************************************************/
unsigned char  EEPROM_Read_NByte( unsigned char *pbuffer, unsigned char n, unsigned char dataaddress )
{
        I2C_Start();                         // 起始条件
          I2C_Send_Byte(Device_Address);       // 向从器件发送设备地址
          if(I2C_Wait_Ack()==0)                // 如果有从器件应答
            I2C_Send_Byte(dataaddress);      // 发送数据地址
          else
               return 1;
          if(I2C_Wait_Ack()==0)
          {
            I2C_Start();                       // 起始条件
            I2C_Send_Byte((Device_Address|0x01));
          }
          else      
                        return 1;
          if(I2C_Wait_Ack()==0)   
               I2C_Read_NByte(pbuffer, n);
           else
               return 1;
   
          return  0;   
}
/*******************************************************************************
* 函数名称:  
EEPROM_Write_Byte();
* 功能描述:  向24CXX的指定地址中写入1个字节的数据  
* 输入参数:  wdata--写入的数据
             dataaddress--数据读取的首地址
* 返回参数:  写入结果:0--成功,1--失败
********************************************************************************/
unsigned char  EEPROM_Write_Byte( unsigned char dataaddress, unsigned char wdata )
{
        I2C_Start();                         // 起始条件
        I2C_Send_Byte(Device_Address);       // 向从器件发送设备地址
          if(I2C_Wait_Ack()==0)                // 如果有从器件应答
            I2C_Send_Byte(dataaddress);      // 发送数据地址
          else
            return 1;
        if(I2C_Wait_Ack()==0)                // 如果从器件应答
            I2C_Send_Byte(wdata);            // 发送要写入的数据
        else
               return 1 ;
        if(I2C_Wait_Ack()==0)                // 如果从器件应答
               I2C_Stop();                   // 如果从器件应答 ,发送停止条件
        else           
               return 1;
   
        Delay_Ms(10);                 //等待EEPROM完成内部写入
          return 0 ;
}
/*******************************************************************************
* 函数名称:
EEPROM_Write_NByte();
* 功能描述:  向24CXX的指定地址中写入1个字节的数据  
* 输入参数:  dataaddress--数据读取的地址
* 返回参数:  写入结果:0--成功,1--失败
********************************************************************************/
unsigned char  EEPROM_Write_NByte( unsigned char *pbuffer, unsigned char n, unsigned char dataaddress )
{
         unsigned char  temp;
   
           I2C_Start();                         // 起始条件
         I2C_Send_Byte(Device_Address);       // 向从器件发送设备地址
           if(I2C_Wait_Ack()==0)                // 如果有从器件应答
            I2C_Send_Byte(dataaddress);      // 发送数据地址
           else
               return 1;
         if(I2C_Wait_Ack()==0)                // 如果从器件应答
             temp = I2C_Send_NByte(pbuffer, n);
           else
               return 1;
         Delay_Ms(10);                 //等待EEPROM完成内部写入
           if(temp==0)    return 0;
           else           return 1;

}




这应用层的四个函数可以满足大部分应用需求,在测试环节会用到。
本帖最后由 fengye5340 于 2014-8-6 22:19 编辑

三、读写测试:
1、编程器读写测试

在使用之前,先用自制的编程器,对MB85RC64进行了读写测试,如下图:
   



因为编程器上位机软件里面没有实现MB85RC64这个型号,这里选择AT24C64芯片型号,可以看到芯片整片读取时间为2.78S, 擦写时间:2.90S,如果是小容量的话,读写是比较快一些的。
2多字节缓冲区读写测试

在项目应用中,FRAM多字节缓冲区读写主要用于特定字符,测试记录等内容的存储操作。


先来测试多字节读写函数,这里定义了两个缓冲区,E_buf[16]W_buf[16]


内容见截图:
        



利用EEPROM_Write_NByte函数将E_buf[16]的内容写入地址0x20开始的16个地址中,然后利用EEPROM_Read_NByte将地址0x20开始的16个字节再读取出来,打印到串口助手上显示,并进行对照。结果如下:
  
  
  



通过串口助手显示的数据正确无误!为了保险起见,又将MB85RC64的存储数据用编程器读出来看看。
        


看到这样的结果,验证了读写函数的正确性。

现在反过来验证一下,先用编程器编辑一部分烧写数据,然后烧录到MB85RC64里面,然后再用多字节读函数EEPROM_Read_NByte读取,然后打印到串口助手看看:


烧录的数据如下:
   


  读函数代码:
       


这里读取0x00开始的255个字节,也就是0XFE处结束。串口助手显示内容截图:

            
            
      



读出来的和通过编程器烧录的数据时一样的!可以进行下一下步了。
本帖最后由 fengye5340 于 2014-8-6 22:23 编辑

3单字节缓冲区读写测试
在项目应用中,FRAM单字节读写应用范围更广,主要用于触摸屏校准标记,ADC校准标记,触摸屏密码设定、程序运行状态储存、各项系统参数存储,掉电数据存储等功能实现。



在这里先简单测试一下,在实际应用中再做详细介绍,

修改原来的缓冲区W_buf[16]内容为相等字符,然后利用循环单个写入0XA0起始的16个地址中,然后分别读写0X
A0
0XB0起始的16个地址的字符

        
        
串口助手显示字符结果:
        
        
        


       这里读取了两个16字节,第一个是0XA0-0XAF,可以看到结果是一样的。0XB0-0XBF的内容是上次通过编程器烧录的,
           


结果也是正确的,有了这些测试基础,可以进行应用性测试了。一个器件要保证稳定性应用,必须要做大量的测试才行。

四、总结:

通过上面的详细测试过程,MB85RC64FRAMEEPROM完全兼容,且能够发挥自己的优势,下面计划把它应用到一个硬度计的项目中,随样机做更多稳定性测试。
发帖子上去,现在看不到图片了,不知道如何解决这个问题!
终于可以显示图片了
返回列表