OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6278|回复: 4

i2c读写eeprom,同样的程序,经常会卡住

[复制链接]

1

主题

1

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2019-11-10
在线时间
1 小时
发表于 2019-12-10 03:00:04 | 显示全部楼层 |阅读模式
2金钱
同样的程序,我今天上午可以实现读取的数据是正确的,然后我把读取给注释掉了,再次写入,然后断电,注释掉写入程序,恢复了读取,结果oled屏就完全不显示东西了。后来我把程序恢复原样,也没有反应了。我想问一下是我上述操作有问题吗,或者猜测一下可能是哪里出现了问题呢
单步执行了几次,它有时会卡在启动传输后的i2c_Wait,有时卡在写地址后面的这个i2c_Wait
我的单片机用的是k60,eeprom用的是24c02
不知道哪里错了,新手小白,求大佬指教
以下是我的程序:
谢谢~
#include "common.h"
#include "include.h"
#include "port.h"
#include "i2c.h"

#define AT24C02_i2c_ADDRESS                         0x50

unsigned char MasterTransmission;
unsigned char SlaveID;

I2C_MemMapPtr I2CN[2] = {I2C0_BASE_PTR, I2C1_BASE_PTR}; //定义两个指针数组保存 I2CN 的地址


/*
*  把I2C通信的每个小步骤都用宏定义来实现,方便编写顶层函数
*  此宏定义参考飞思卡尔公司例程修改所得
*/
#define i2c_DisableAck(I2Cn)        I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_TXAK_MASK

//
#define i2c_RepeatedStart(I2Cn)     I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_RSTA_MASK


//启动信号
#define i2c_Start(I2Cn)             I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_TX_MASK;\
I2C_C1_REG(I2CN[I2Cn]) |= I2C_C1_MST_MASK

//暂停信号
#define i2c_Stop(I2Cn)              I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_MST_MASK;\
I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TX_MASK

//进入接收模式(应答)
#define i2c_EnterRxMode(I2Cn)       I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TX_MASK;\
I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TXAK_MASK
//进入接收模式(不应答)
#define i2c_PutinRxMode(I2Cn)       I2C_C1_REG(I2CN[I2Cn]) &= ~I2C_C1_TX_MASK

//等待 I2C0_S
#define i2c_Wait(I2Cn)              while(( I2C_S_REG(I2CN[I2Cn]) & I2C_S_IICIF_MASK)==0) {} \
I2C_S_REG(I2CN[I2Cn]) |= I2C_S_IICIF_MASK;

//写一个字节
#define i2c_write_byte(I2Cn,data)   I2C_D_REG(I2CN[I2Cn]) = data

/*!
*  @brief      I2C初始化,设置波特率
*  @param      I2Cn_e      I2C模块(I2C0、I2C1)
*  @param      baud        期待的波特率
*  @return                 实际的波特率
*  @since      v5.0
*  Sample usage:       i2c_init(I2C0,400*1000);     // 初始化I2C0,期待的波特率为400k
*/
uint32 i2c_init(I2Cn_e i2cn, uint32 baud)
{
  if(i2cn == I2C0)
  {
    /* 开启时钟 */
#if defined(MK60DZ10)
    SIM_SCGC4 |= SIM_SCGC4_I2C0_MASK;           //开启 I2C0时钟
#elif defined( MK60F15)
    SIM_SCGC4 |= SIM_SCGC4_IIC0_MASK;           //开启 I2C0时钟
#endif

    /* 配置 I2C0功能的 GPIO 接口 */
    if((I2C0_SCL_PIN == PTB0) || (I2C0_SCL_PIN == PTB2) || (I2C0_SCL_PIN == PTD8) )
    {
      PORT_FuncInit (I2C0_SCL_PIN, ALT2 | ODO | PULLUP );
    }
    else
      ASSERT(0);                              //上诉条件都不满足,直接断言失败了,设置管脚有误?

    if((I2C0_SDA_PIN == PTB1) || (I2C0_SDA_PIN == PTB3) || (I2C0_SDA_PIN == PTD9) )
      PORT_FuncInit (I2C0_SDA_PIN, ALT2 | ODO | PULLUP );
    else
      ASSERT(0);                              //上诉条件都不满足,直接断言失败了,设置管脚有误?
  }
  else
  {
    /* 开启时钟 */
#if defined(MK60DZ10)
    SIM_SCGC4 |= SIM_SCGC4_I2C1_MASK;           //开启 I2C1时钟
#elif defined(MK60F15)
    SIM_SCGC4 |= SIM_SCGC4_IIC1_MASK;           //开启 I2C1时钟
#endif
    /* 配置 I2C1功能的 GPIO 接口 */
    if(I2C1_SCL_PIN == PTE1)
      PORT_FuncInit (I2C1_SCL_PIN, ALT6 | ODO | PULLUP );
    else if(I2C1_SCL_PIN == PTC10)
      PORT_FuncInit (I2C1_SCL_PIN, ALT2 | ODO | PULLUP );
    else
      ASSERT(0);                          //上诉条件都不满足,直接断言失败了,设置管脚有误?

    if(I2C1_SDA_PIN == PTE0)
      PORT_FuncInit (I2C1_SDA_PIN, ALT6 | ODO | PULLUP );
    else if (I2C1_SDA_PIN == PTC11)
      PORT_FuncInit (I2C1_SDA_PIN, ALT2 | ODO | PULLUP );
    else
      ASSERT(0);                          //上诉条件都不满足,直接断言失败了,设置管脚有误?
  }

  /* 设置频率 */

  // I2C baud rate = bus speed (Hz)/(mul × SCL divider)  即这里 50MHz/(1 ×128)=390.625kHz
  // SDA hold time = bus period (s) × mul × SDA hold value
  // SCL start hold time = bus period (s) × mul × SCL start hold value
  // SCL stop hold time = bus period (s) × mul × SCL stop hold value

  //查表 ICR 对应的  SCL_divider ,见 《K60P144M100SF2RM.pdf》第1468页的 I2C Divider and Hold Values
  uint16 ICR_2_SCL_divider[0x40]  =
  {
    20, 22, 24, 26, 28, 30, 34, 40, 28, 32, 36, 40, 44, 48, 56, 68,
    48, 56, 64, 72, 80, 88, 104, 128, 80, 96, 112, 128, 144, 160, 192, 240,
    160, 192, 224, 256, 288, 320, 384, 480, 320, 384, 448, 512, 576, 640, 768, 960,
    640, 768, 896, 1024, 1152, 1280, 1536, 1920, 1280, 1536, 1792, 2048, 2304, 2560, 3072, 3840
  };

  uint8 mult;
  if(bus_clk_khz <= 50000)mult = 0;         //bus 一分频
  else  if(bus_clk_khz <= 100000)mult = 1;  //bus 二分频
  else      mult = 2;                       //bus 四分频

  uint16 scldiv =  bus_clk_khz * 1000 / ( (1<<mult) * baud );  //最佳的分频系数

  //需要从 ICR_2_SCL_divider 里找到 与最佳分频系数scldiv最相近的 分频系数
  uint8 icr, n = 0x40;
  uint16 min_Dvalue = ~0, Dvalue;

  while(n)                                            //循环里逐个扫描,找出最接近的 分频系数
  {
    n--;
    Dvalue = abs(scldiv - ICR_2_SCL_divider[n]);
    if(Dvalue == 0)
    {
      icr = n;
      break;                                      //退出while循环
    }

    if(Dvalue < min_Dvalue)
    {
      icr = n;
      min_Dvalue = Dvalue;
    }
  }

  I2C_F_REG(I2CN[i2cn])  = ( 0                        // I2C Frequency Divider register (I2CN_F)  I2C分频寄存器   I2C最大波特率为 400k
                            | I2C_F_MULT(mult)        // 乘数因子 mul =  1<<MULT
                              | I2C_F_ICR(icr)          // 时钟速率 = ICR_2_SCL_divider[ICR] ,查表获得 ICR 与 SCL_divider 映射关系
                                );

  /* 使能 I2C */
  I2C_C1_REG(I2CN[i2cn]) = ( 0
                            | I2C_C1_IICEN_MASK       //使能I2C
                              //| I2C_C1_IICIE_MASK       //使能中断
                              );

  return (  bus_clk_khz * 1000 / ( (1<<mult) * ICR_2_SCL_divider[icr])  );
}
/*!
*  @brief      I2C通信结束后需要调用的函数函数
*  @since      v5.0
*  @note       如果通信失败,可尝试增大此延时值,确认是否延时导致的
*/
void Pause(void)
{
  u16 n;
  for(n = 1; n < 50000; n++)      //注意,这个数据太小,会导致读取错误。
  {
    asm("nop");
  }
}

void i2c_StartTransmission (I2Cn_e i2cn, u8 SlaveID, MSmode Mode)
{

  //ASSERT(Mode == MWSR || Mode == MRSW);         //使用断言,检测 Mode 是否为 读 或 写

  SlaveID = ( SlaveID << 1 ) | Mode ;            //确定写地址和读地址

  /* send start signal */
  i2c_Start(i2cn);

  /* send ID with W/R bit */
  i2c_write_byte(i2cn, SlaveID);
}

/*!
*  @brief      读取I2C设备指定地址寄存器的数据
*  @param      I2Cn_e        I2C模块(I2C0、I2C1)
*  @param      SlaveID     从机地址(7位地址)
*  @param      reg         从机寄存器地址
*  @return                 读取的寄存器值
*  @since      v5.0
*  Sample usage:       uint8 value = i2c_read_reg(I2C0, 0x1D, 1);
*/
u8 i2c_read_reg(I2Cn_e i2cn, u8 SlaveID, u8 Addr)
{
  u8 result;

  /* Send Slave Address */
  i2c_StartTransmission (i2cn, SlaveID, MWSR);
  i2c_Wait(i2cn);

  /* Write Register Address */
  i2c_write_byte(i2cn, Addr);
  i2c_Wait(i2cn);

  /* Do a repeated start */
  i2c_RepeatedStart(i2cn);

  /* Send Slave Address */
  i2c_write_byte(i2cn, ( SlaveID << 1) | MRSW );
  i2c_Wait(i2cn);

  /* Put in Rx Mode */
  i2c_PutinRxMode(i2cn);

  /* Turn off ACK since this is second to last byte being read*/
  i2c_DisableAck(i2cn); //不应答

  /* Dummy read 虚假读取*/
  result = I2C_D_REG(I2CN[i2cn]);
  i2c_Wait(i2cn);

  /* Send stop since about to read last byte */
  i2c_Stop(i2cn);

  /* Read byte */
  result = I2C_D_REG(I2CN[i2cn]);

  return result;
}


/*!
*  @brief      写入一个字节数据到I2C设备指定寄存器地址
*  @param      I2Cn_e        I2C模块(I2C0、I2C1)
*  @param      SlaveID     从机地址(7位地址)
*  @param      reg         从机寄存器地址
*  @param      Data        数据
*  @since      v5.0
*  Sample usage:       i2c_write_reg(I2C0, 0x1D, 1,2);     //向从机0x1D 的寄存器 1 写入数据 2
*/
void i2c_write_reg(I2Cn_e i2cn, u8 SlaveID, u8 Addr, u8 Data)
{
  /* send data to slave */
  i2c_StartTransmission(i2cn, SlaveID, MWSR);    //启动传输
  i2c_Wait(i2cn);

  i2c_write_byte(i2cn, Addr);                    //写地址
  i2c_Wait(i2cn);

  i2c_write_byte(i2cn, Data);                    //写数据
  i2c_Wait(i2cn);

  i2c_Stop(i2cn);

  Pause();                                        //延时太短的话,可能写出错
}

char txt[20];
int b=3;
int main()
{
#define ADDR    0x00
  u8 i = 0;
  u8 Data=0;
  UART_Init(UART0,9600);                        //初始化串口
  LCD_Init();  //显示屏
  i2c_init(I2C0,400 * 1000);                                 //初始化i2c0


  while(1)
  {
    for(i=0;i<2;i++)
    {
      i2c_write_reg(I2C0, AT24C02_i2c_ADDRESS, ADDR, 1);          //i2c向AT24C02_i2c_ADDRESS芯片写入数据 i 到地址为ADDR的寄存器
      DelayMs(50);
      Data =  i2c_read_reg(I2C0,AT24C02_i2c_ADDRESS, ADDR);
      DelayMs(50);
      printf("接收到的数据为:%d\n\n", Data);                     //蓝牙发送数据
      sprintf(txt,"A=%d",Data);
      LCD_P6x8Str(0,0,(uint8*)txt);                                         //oled屏显示数据
    }
  }

}

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

31

主题

2183

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
14408
金钱
14408
注册时间
2018-8-3
在线时间
1156 小时
发表于 2019-12-10 11:44:17 | 显示全部楼层
有在其他的硬件试过吗 可能是eeprom的问题
回复

使用道具 举报

88

主题

7377

帖子

5

精华

资深版主

Rank: 8Rank: 8

积分
14980
金钱
14980
注册时间
2013-11-13
在线时间
1823 小时
发表于 2019-12-10 15:08:27 | 显示全部楼层
IIC,SPI这种通信协议出问题最好接一个逻辑分析仪使用,裸机分析仪可以看到整个波形,可以分析哪一步出问题了,然后重点检查
回复

使用道具 举报

6

主题

1127

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1656
金钱
1656
注册时间
2019-8-15
在线时间
102 小时
发表于 2019-12-10 16:12:34 | 显示全部楼层
帮顶                                
成功没有捷径
回复

使用道具 举报

5

主题

424

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1020
金钱
1020
注册时间
2014-9-4
在线时间
199 小时
发表于 2019-12-10 16:33:09 | 显示全部楼层
一看到iic就想时序的问题,,是不是要应答。不同芯片要不要应答有区别。之前被坑惨了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-22 05:37

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表