查看: 1819|回复: 2

[求助] i2c读取eeprom经常会卡死

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

    [LV.2]偶尔看看I

    5

    主题

    20

    帖子

    0

    注册会员

    Rank: 2

    积分
    68
    最后登录
    2019-12-23
    发表于 2019-12-11 16:50:46 | 显示全部楼层 |阅读模式
    5NXP金币
    新手小白,用的是k60主板,用i2c读取eeprom,有时可以读取成功,有时程序会卡死,使卡死程序以下的程序都不能运行,求助
    #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");
        }
      }

    }

    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    该用户从未签到

    656

    主题

    6312

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    20028
    最后登录
    2024-4-26
    发表于 2019-12-13 15:22:59 | 显示全部楼层
    可以先抓一下波形check一下
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 16:39
  • 签到天数: 365 天

    [LV.9]以坛为家II

    0

    主题

    389

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    1170
    最后登录
    2024-4-25
    发表于 2022-11-17 09:13:51 | 显示全部楼层
    卡死应该是在等待信号吧,是不是用的中断模式处理,卡死可能是在等中断,但是一直不来中断
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-26 18:20 , Processed in 0.109929 second(s), 18 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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