这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » STM32 I2C读写FM24CL04怪现象?

共8条 1/1 1 跳转至

STM32 I2C读写FM24CL04怪现象?

高工
2014-01-15 23:13:00     打赏

先说明一下遇到的问题:

1、在进行单字节数据循环读写的时候,循环写的数据少了(如<=100)可以,但是多了就会挂(目前随即选了发送120个就挂了)。

2、在多字节读取的函数中,读写的数据量在20(随即选的)以内时木有问题,但是如果一次写的字节数多了(>100),在多字节读的函数中执行到读最后一个字节数据时,就会挂在while();等待EV5处。

3、有时什么都不做,直接初始化完了,进行发送第一个字节的时候,也会莫名其妙的挂在等待总线空闲处。(间歇性的,时好时坏)。

补充一下(FM24CL04:4K的内存,分两页,(从机地址的倒数第二位区分操作的是哪个页),一页2Kbit也就是256个字节:2Kbit=256*8bit).

以下是我的代码,请指正啊:

#include "config.h" 


//PB5/I2C_WP 写保护
#define DIS_WP()    GPIO_ResetBits(GPIOB,GPIO_Pin_5);       // PB5/I2C_WP 低电平可写	
#define EN_WP()     GPIO_SetBits(GPIOB,GPIO_Pin_5);         // PB5/I2C_WP 高电平写保护	
/********************************************************
*函数名称:void I2C1_Init(uint32 fi2c)
*函数功能: STM32 硬件I2C初始化,主程序要配置好I2C总线接口(I2C引脚功能,并已使能I2C主模式)
*未开中断,之前的函数I2C收发都是在中断中完成的。
********************************************************/					 void I2C1_Init(uint32 fi2c)
{ 
	/*PB6 I2C1_SCL  PB7 I2C1_SDA  */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD;  //复用开漏输出
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOB,&GPIO_InitStructure);
  //PB5/I2C1_Wp
  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;;    //开漏输出
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOB,&GPIO_InitStructure);
/********************
配置I2C1为I2C主模式,置I2C 的占空比/快速模式Tlow / Thigh = 2,
置第一个设备自身地址 7位模式,使能应答(ACK),应答 7 位地址,时钟频率200KHz
**********************/
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
  I2C_InitTypeDef I2C_InitStructure;
  I2C_DeInit(I2C1);
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//配置I2C2为主模式
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//快速模式Tlow / Thigh = 2
  I2C_InitStructure.I2C_OwnAddress1 = 0x30;//第一个设备自身地址0x30; 7位模式
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//使能应答(ACK)
  I2C_InitStructure.I2C_AcknowledgedAddress =I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = fi2c;//时钟频率200KHz
  I2C_Init(I2C1, &I2C_InitStructure);//初始化I2C1
//  I2C_ITConfig(I2C1, I2C_IT_BUF | I2C_IT_EVT, ENABLE);//使能缓存和事件中断
  I2C_Cmd(I2C1, ENABLE);//使能I2C1
DS_WP(); //禁止写保护
} 


/*******************************************************
** 函数名称:ISendByte()   从机地址内包含页地址1位,读写1位
** 函数功能:向无子地址器件发送1字节数据。
** 入口参数:sla	要写入数据的地址	器件地址
**           dat	要写入的数据  要发送的数据
***************************************************/
uint8  ISendByte(uint8 sla, uint8 dat)
{    
  I2C_GenerateSTART(I2C1,ENABLE);//发送一个开始讯号
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)){;}//等待EV5 已发送起始条件 
  I2C_Send7bitAddress(I2C1, 0xA2, I2C_Direction_Transmitter);//发送从机地址 写
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){;}//等待EV6 已发送从机地址
  I2C_SendData(I2C1, sla);//发送要写的寄存器地址
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8 第一个数据已发送
  I2C_SendData(I2C1, dat);//发送要写入到从机寄存器地址中的数据
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8
  I2C_GenerateSTOP(I2C1,ENABLE); //停止位
  return 1;//无实意,防止系统警告 13 12 4
}
/******************************************************
** 函数名称:IRcvByte()   从机地址内包含页地址1位,读写1位
** 入口参数:sla		器件地址
**           dat		接收数据的变量指针
** 出口参数:返回值为0时表示操作出错,为1时表示操作正确。
** 说明:使用前要初始化好I2C引脚功能,并已使能I2C主模式
***************************************************/
uint8  IRcvByte(uint8 sla, uint8 *dat)
{    
   I2C_AcknowledgeConfig(I2C1, ENABLE); //使能应答位
   while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //等待I2C
   I2C_GenerateSTART(I2C1, ENABLE);   //产生起始信号
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //等待EV5  
   I2C_Send7bitAddress(I2C1,0xA2, I2C_Direction_Transmitter); //发送从机地址  写
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//EV6                                          
   I2C_SendData(I2C1, sla);  //发送读地址
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8 
   I2C_GenerateSTART(I2C1, ENABLE);  //重新启动
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//EV5
   I2C_Send7bitAddress(I2C1,0xA2, I2C_Direction_Receiver);//发送从机地址  读
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//EV6 
      I2C_AcknowledgeConfig(I2C1, DISABLE); //关闭应答和停止条件产生  
   I2C_GenerateSTOP(I2C1, ENABLE);   //停止条件产生  
   while(!(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))); //EV7
   *(dat++)=I2C_ReceiveData(I2C1);   
   return 1;
}
/************************************************
** 函数名称 :I2C_ReadNByte()  从机地址内包含页地址1位,读写1位
** 函数功能 :从有子地址器件任意地址开始读取N字节数据
** 入口参数 :	sla			器件从地址
**				suba		器件子地址
**				s			数据接收缓冲区指针
**				num			读取的个数
********************************************************/
uint8 I2C_ReadNByte (uint8 sla, uint32 suba_type, uint32 suba, uint8 *s, uint32 num)
{ 
    suba_type=suba_type;//防止系统报错     
    I2C_AcknowledgeConfig(I2C1,ENABLE);//使能应答位
    while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)); //等待I2C空闲
    I2C_GenerateSTART(I2C1,ENABLE);//产生起始信号
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//等待EV5  
///////////////////发送从机地址  写
    I2C_Send7bitAddress(I2C1,sla, I2C_Direction_Transmitter); //发送从机地址 写
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//EV6  
//////////发送要读取的数据的地址
    I2C_SendData(I2C1, suba);   
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8 
    I2C_GenerateSTART(I2C1, ENABLE);//重新发送
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//EV5
////////////////////////////发送从机地址 读
    I2C_Send7bitAddress(I2C1, sla, I2C_Direction_Receiver);//发送从机地址 读
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//EV6  
    
    while(num)
    {    
      if(num==1)  //写255的时候,执行完if后会挂在while(),写20个时没事啊
      {
        I2C_AcknowledgeConfig(I2C1, DISABLE);//关闭应答
        I2C_GenerateSTOP(I2C1, ENABLE);//关闭应答和停止条件产生  OK 
      }
      while(!(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)));//等待EV7
      *(s++)=I2C_ReceiveData(I2C1);
      num--;
    }
    I2C_AcknowledgeConfig(I2C1, ENABLE);//使能i2C 指定的应答功能
    return 1;//无实意,防止警告
}
/************************************************
** 函数名称 :I2C_WriteNByte()   从机地址内包含页地址1位,读写1位
** 函数功能 :向有子地址器件写入N字节数据
** 入口参数 :	sla			器件从地址(区别两个页0XA0对应1页,0XA2对应0页)
**				suba_type	子地址结构	1-单字节地址	3-8+X结构	2-双字节地址
**			  	suba		器件内部物理地址
**			  	*s			将要写入的数据的指针
**			  	num			将要写入的数据的个数
****************************************************/
uint8 I2C_WriteNByte(uint8 sla, uint8 suba_type, uint32 suba, uint8 *s, uint32 num)
{
//此处有两个问题1、 写超过一页。2、写的内存地址(uint32)
   suba_type=suba_type; //防止报错 子地址结构
   suba=(uint8)(suba); //要写入的数据的地址 char型
   while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)); //等待I2C空闲
  
   I2C_GenerateSTART(I2C1,ENABLE);  //产生I2C1传输START条件
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)){;}//等待EV5  
   I2C_Send7bitAddress(I2C1, sla, I2C_Direction_Transmitter);//发送从机地址 写
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){;}//等待EV6
   I2C_SendData(I2C1, suba);//发送要写的寄存器地址
   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8
   while(num)
   {
    I2C_SendData(I2C1, *s++);//发送要写入到从机寄存器地址中的数据
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;}//等待EV8
    num--;
   }
   I2C_GenerateSTOP(I2C1,ENABLE);
  return 1;//无实意,防止系统警告
}
/////////测试任务 I2C FM24CL04
static void I2CFlash_task(void *p_arg)
{
    p_arg=p_arg;
    I2C1_Init(200000);   //FM24cl04的I2C总线初始化 速率200K
    SysDOInit();    //测试任务切换用 开关量输出管脚初始化
  unsigned int i=0;
  uint8 AddrI2C[256],add;  //建立数组用来存储要读、写的数据的地址
  uint8 Readdata_AddrI2C[256]={0};//存储读取来的数据
  uint8 onedata[256]={0};
//////初始化要读取的数据的地址数组
  for(i=0;i<=255;i++)
  {
    AddrI2C[i]=i++;  //初始化要读取的数据的地址数组
  }  
    while(1)
    {
      CtrlDOData(0x80);  //测试任务切换用点灯
///////////////////测试写单个字节 Begin  向24CL04发送256个数据0~~255     
//目前数据发多了(目前测试120)就会进入硬件故障,不知道哪里出了问题?超限了?
    for(i=0;i<=110;i++)
    {
     ISendByte(AddrI2C[i],i);        //向24CL04发送256个数据0~~255
     IRcvByte(AddrI2C[i],&onedata[i]);   //从24CL04读取256个数据0~~255存入onedata[]中
    }
///////////////////测试写单个字节 end    
/////////////////////////测试写多个字节 begin
    add=0x00;  //重新赋0地址
    uint8 AddrI2C1[256]= {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA};
    I2C_WriteNByte(0xA2, 1, add, AddrI2C1, 20); //写多个字节数据到FM24CL04
    I2C_ReadNByte(0xA2,1, add,Readdata_AddrI2C,20);//从FM24CL04读多个字节数据
/////////////////////////测试写多个字节 end   
      OSTimeDlyHMSM(0,0,0,50);
     }         
}







关键词: I2C          FM24CL04    

高工
2014-01-15 23:16:54     打赏
2楼

貌似粘贴代码的功能有点小BUG?

代码中间的两个行号为01的行出错了呢


院士
2014-01-17 21:01:32     打赏
3楼

这个是STM32固有的bug, 貌似只能提升IIC通讯的优先级才能解决。

具体的并没有测试。


高工
2014-01-17 22:42:29     打赏
4楼

现在改为模拟的了,貌似还是会跑飞。。。

单独揪出来试试


院士
2014-01-18 10:49:26     打赏
5楼

我倒觉得没有什么必要研究这个东西了。

本身IIC就有一些bug存在;再者 大数据量的存储与处理还是一个难点。


高工
2014-01-18 14:00:08     打赏
6楼

我就想看看到底是什么BUG,捣鼓半天也没搞明白,貌似封装的库函数有问题,包括启动命令,赶巧了就有可能造成总线忙,总之各种挂。

ST为此专门出了一个修补的I2C库,需要占用最高优先级,就没捣鼓


院士
2014-01-18 19:59:10     打赏
7楼

甭研究了。

看点别的吧


高工
2014-01-18 21:39:48     打赏
8楼

搞通了就不捣鼓了,收拾收拾准备回家过年


共8条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]