查看: 4474|回复: 7

[求助] i2c读取eeprom错误

[复制链接]
  • TA的每日心情
    开心
    2019-12-23 15:26
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    5

    主题

    20

    帖子

    0

    注册会员

    Rank: 2

    积分
    68
    最后登录
    2019-12-23
    发表于 2019-12-9 02:09:43 | 显示全部楼层 |阅读模式
    读取时一直是255,不知道哪里错了,新手小白,求大佬指教
    以下是我的程序:
    谢谢~
    #include "common.h"
    #include "include.h"
    #include "port.h"
    #include "i2c.h"

    #define AT24C02_i2c_ADDRESS                         0xA0

    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);
          printf("a");
        }
      }

    }

    我知道答案 目前已有7人回答
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    该用户从未签到

    712

    主题

    6371

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    24875
    最后登录
    2025-7-18
    发表于 2019-12-9 09:53:32 | 显示全部楼层
    本帖最后由 小恩GG 于 2019-12-10 09:14 编辑

    #define AT24C02_i2c_ADDRESS   0xA0 ,0xa0改成 0x50
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-12-23 15:26
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    5

    主题

    20

    帖子

    0

    注册会员

    Rank: 2

    积分
    68
    最后登录
    2019-12-23
     楼主| 发表于 2019-12-10 01:51:38 | 显示全部楼层
    本帖最后由 小恩GG 于 2019-12-10 16:28 编辑
    小恩GG 发表于 2019-12-9 09:53
    #define AT24C02_i2c_ADDRESS   0xA0 ,0xa0改成 0x50

    同样的程序,我今天上午把0xA0改成了0X50,然后可以实现oled屏上显示读取的数据是正确的,然后我把读取给注释掉了,再次写入,然后断电,注释掉写入,恢复了读取,结果oled就不显示了。后来我把程序恢复原样,也没有反应了。我想问一下是我上述操作有问题吗,或者猜测一下可能是哪里出现了问题呢,谢谢
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-12-23 15:26
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    5

    主题

    20

    帖子

    0

    注册会员

    Rank: 2

    积分
    68
    最后登录
    2019-12-23
     楼主| 发表于 2019-12-10 02:11:49 | 显示全部楼层
    本帖最后由 小恩GG 于 2019-12-10 16:28 编辑
    小恩GG 发表于 2019-12-9 09:53
    #define AT24C02_i2c_ADDRESS   0xA0 ,0xa0改成 0x50

       单步执行了几次,它有时会卡在启动传输后的i2c_Wait,有时卡在写地址后面的这个i2c_Wait,请问可能是程序问题还是硬件方面的问题呢?  
          i2c_StartTransmission(I2C0, SlaveID, MWSR);    //启动传输
          i2c_Wait(I2C0);
          
          i2c_write_byte(I2C0, ADDR);                    //写地址
          i2c_Wait(I2C0);
          
          i2c_write_byte(I2C0, Data);                    //写数据
          i2c_Wait(I2C0);
          
          i2c_Stop(I2C0);
          
          Pause();  
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    712

    主题

    6371

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    24875
    最后登录
    2025-7-18
    发表于 2019-12-10 10:23:05 | 显示全部楼层
    本帖最后由 小恩GG 于 2019-12-10 10:33 编辑
    ycat 发表于 2019-12-10 02:11
    单步执行了几次,它有时会卡在启动传输后的i2c_Wait,有时卡在写地址后面的这个i2c_Wait,请问可能是程 ...

    不要用debug去检查程序,会影响iic的时序,用示波器根据时序图去查。你用的什么mcu,官网应该都有库的。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-12-23 15:26
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    5

    主题

    20

    帖子

    0

    注册会员

    Rank: 2

    积分
    68
    最后登录
    2019-12-23
     楼主| 发表于 2019-12-10 16:09:57 | 显示全部楼层
    小恩GG 发表于 2019-12-10 10:23
    不要用debug去检查程序,会影响iic的时序,用示波器根据时序图去查。你用的什么mcu,官网应该都有库的。 ...

    我用的是山外的k60核心板,用的是商家给的库
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-12-23 15:26
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    5

    主题

    20

    帖子

    0

    注册会员

    Rank: 2

    积分
    68
    最后登录
    2019-12-23
     楼主| 发表于 2019-12-11 16:46:16 | 显示全部楼层
    现在出现了一个很奇怪的现象,有时候可以实现功能,有时候不能
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    712

    主题

    6371

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    24875
    最后登录
    2025-7-18
    发表于 2019-12-11 16:54:34 | 显示全部楼层
    ycat 发表于 2019-12-11 16:46
    现在出现了一个很奇怪的现象,有时候可以实现功能,有时候不能

    可能时序实现的不太好吧
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-7-20 08:02 , Processed in 0.102649 second(s), 28 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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