查看: 4762|回复: 8

[求助] I2C中断方式传输数据出现问题

[复制链接]
  • TA的每日心情
    无聊
    2024-6-3 15:51
  • 签到天数: 19 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    4

    主题

    54

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    246
    最后登录
    2024-9-4
    发表于 2019-4-21 19:30:11 | 显示全部楼层 |阅读模式
    用的是LPC1768的单片机,I2C芯片是AT24C02。用中断的方式进行I2C传输,但是发现每次读出数据的前16byte的高四位是上次写数据最后16位的高四位。如下图(包含逻辑分析仪的结果): 3.PNG 2.PNG 1.PNG
    代码如下:
    /*****************************************************************************
    *   i2c.c:  I2C C file for NXP LPC17xx Family Microprocessors
    *****************************************************************************/
    #include "i2c_periph.h"

    // 定义用于和I2C中断传递信息的全局变量                     
    volatile uint8_t     I2C0_Slave_Addr;                           // I2C器件从地址               
    volatile uint32_t    I2C0_Buffer_Addr;                          // I2C器件内部子地址            
    volatile uint8_t     I2C0_Buffer_AddrType;                      // I2C子地址字节数              
    volatile uint8_t     *pI2C0_Data;                               // 数据缓冲区指针               
    volatile uint32_t    I2C0_Byte_Num;                             // 要读取/写入的数据个数        
    volatile uint8_t     I2C0_End_Flag;                             // I2C总线结束标志:结束总线是置1  
    volatile uint8_t     I2C_Buffer_Dir;                            // 子地址控制。0--子地址已经处理或者不需要子地址 1--读取操作 2--写操作

    void I2C_Delay(uint32_t data)
    {
            uint32_t i,j,k;
            for(i=0;i<data;i++)
                    for(j=0;j<19;j++)
                            for(k=0;k<1000;k++);
    }
    /*****************************************************************************
    ** Function name:                I2C_Init()                       
    *****************************************************************************/
    void I2C0_Init( uint32_t I2cMode )
    {
      LPC_SC->PCONP |= (1 << 7);                                          //使能功率控制寄存器,i2c0--7,i2c1--19,i2c2--26默认都是使能的,
                                                        //还有选择时钟,默认所有外设时钟都是cclk/4即为CPU主频时钟
           
      LPC_PINCON -> PINSEL1     |= (1UL<<22)|(1U<<24);// P0.27 - SDA0; P0.28 -- SCL0
            LPC_PINCON -> PINMODE_OD0 |= (1ul<<27)|(1ul<<28);  //引脚开漏设置,I2C0默认开漏模式,但是I2C1和I2C2需要设置开漏模式
    //        LPC_PINCON -> I2CPADCFG   |= (1UL<<0)|(1UL<<2); //配置成高速模式
           
      LPC_I2C0->I2SCLL   = I2SCLL_SCLL;
      LPC_I2C0->I2SCLH   = I2SCLH_SCLH;
           
      if ( I2cMode == I2C_Slave ) {        LPC_I2C0->I2ADR0 = 0xA0;}                 //装入器件从地址   

      NVIC_EnableIRQ(I2C0_IRQn);                           //使能I2C0中断
      LPC_I2C0->I2CONSET = I2CONSET_I2EN;                                     //使能I2C接口
    }


    /*****************************************************************************
    ** Function name:                I2C0_中断服务函数
    *****************************************************************************/
    void I2C0_IRQHandler(void)  
    {
      uint8_t StateValue;
      StateValue = LPC_I2C0->I2STAT & 0xf8;                                      // I2C状态寄存器只有3~7位为有效位
           
      switch ( StateValue )
      {
            case 0x08:                                                                                                                              // 已发送起始条件
            if (I2C_Buffer_Dir == 1){        LPC_I2C0->I2DAT = I2C0_Slave_Addr &0xfe;}                           // 存放刚发送或接收的一个字节,发送从机地址,不含读写位
            else{        LPC_I2C0->I2DAT = I2C0_Slave_Addr;}       
            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                                 // 清除中断标志位、起始标志位
            break;
           
            case 0x10:                                                                                                                             // 已发送重复起始条件,再发送从机地址包含读写位
            LPC_I2C0->I2DAT    = I2C0_Slave_Addr;
            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                                 // 清除中断标志位、起始标志位
            break;
           
            case 0x18:                                                                                                                             // 已接收ACK 发送数据
            case 0x28:                                                                                                                             // 已发送I2DAT中的数据,已接收ACK
            if (I2C_Buffer_Dir == 0){                                                                                     // 子地址已经处理或者不需要子地址
              if(I2C0_Byte_Num>0){                                                         // 写入数据
                      LPC_I2C0->I2DAT    = *pI2C0_Data++;
                            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                             // 清除中断标志位、起始标志位
                      I2C0_Byte_Num--;
                      I2C_Delay(1);
              }
              else{
                    LPC_I2C0->I2CONSET = I2CONSET_STO;                                                      // 置位停止标志位
                    LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                              // 清除中断标志位、起始标志位
                    I2C0_End_Flag =1;
              }
            }
            if(I2C_Buffer_Dir == 1){                                                                                              // 读取操作
                    if(I2C0_Buffer_AddrType == 2){
                      LPC_I2C0->I2DAT    = ((I2C0_Buffer_Addr >> 8) & 0xff);         
                            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                            // 清除中断标志位、起始标志位
                      I2C0_Buffer_AddrType--;
                      break;
                     }
                    if(I2C0_Buffer_AddrType == 1){                                                                                // 字节数为1
                      LPC_I2C0->I2DAT    = (I2C0_Buffer_Addr & 0xff);       
                            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                            // 清除中断标志位、起始标志位
                      I2C0_Buffer_AddrType--;
                      break;
                     }
                     if(I2C0_Buffer_AddrType == 0){
                      LPC_I2C0->I2CONCLR = I2CONCLR_SIC;                                                            // 清除中断标志位
                      LPC_I2C0->I2CONSET = I2CONSET_STA;                                                            // 置位起始标志位
                      I2C_Buffer_Dir = 0;
                      break;                                   
                     }          
            }

            if ( I2C_Buffer_Dir == 2 ){                                                                                          // 写操作       
              if(I2C0_Buffer_AddrType > 0){
                      if(I2C0_Buffer_AddrType == 2){                                                                              // 字节数为2
                             LPC_I2C0->I2DAT    = ((I2C0_Buffer_Addr >> 8) & 0xff);               
                             LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                          // 清除中断标志位、起始标志位
                             I2C0_Buffer_AddrType--;
                             break;
                      }
                      if(I2C0_Buffer_AddrType == 1){                                                                           // 字节数为1
                              LPC_I2C0->I2DAT = (I2C0_Buffer_Addr & 0xff);       
                                    LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                      // 清除中断标志位、起始标志位
                              I2C0_Buffer_AddrType--;
                              I2C_Buffer_Dir  = 0;
                              break;
                      }
              }
            }
            break;
           
            case 0x40:                                                                                                             //已发送SLA+R,已接收ACK
            if (I2C0_Byte_Num <= 1){LPC_I2C0->I2CONCLR = I2CONCLR_AAC;}                         ///////////清除应答标志位
            else{LPC_I2C0->I2CONSET = I2CONSET_AA;}                                             //置位应答标志位
            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                       //清除中断标志位、起始标志位
            break;

            case 0x20:                                                                                                      //已发送SLA+W,已接收非应答
            case 0x30:                                                                                                       //已发送I2DAT中的数据,已接收非应答  
            case 0x38:               
            case 0x48:
            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                      //清除中断标志位、起始标志位
            I2C0_End_Flag = 0xFF;
            break;
                   
            case 0x50:                                                                                                     //已接收数据字节,已返回ACK8
            *pI2C0_Data++ = LPC_I2C0->I2DAT;
            I2C0_Byte_Num--;
            if(I2C0_Byte_Num ==1){LPC_I2C0->I2CONCLR = I2CONCLR_AAC|I2CONCLR_SIC|I2CONCLR_STAC;}        //接收最后一个字节 STA,SI,AA = 0            
            else{       
                    LPC_I2C0->I2CONSET = I2CONSET_AA;                                                        //置位应答标志位
                    LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                      //清除中断标志位、起始标志位
                    }
            break;

            case 0x58:                                                                                                        //已接收数据字节,已返回非应答       
            *pI2C0_Data++ = LPC_I2C0->I2DAT;                                                   //读出数据
            LPC_I2C0->I2CONSET = I2CONSET_STO;                                           
            LPC_I2C0->I2CONCLR = I2CONCLR_SIC|I2CONCLR_STAC;                      //清除中断标志位、起始标志位
            I2C0_End_Flag = 1;                                                                                 
            break;

            default:
            break;
      }

    }


    /********************************************************************************
    ** Function name:I2C_WriteByte
    ** Descriptions :向有子地址器件写入N字节数据
    ** parameters   :Slave                           器件从地址
    **                                        Addr_Type           子地址结构        1-单字节地址        3-8+X结构        2-双字节地址
    **                                        Buff_Addr                 器件内部物理地址
    **                                        *p                             将要写入的数据的指针
    **                                        Num                             将要写入的数据的个数
    ** Returned value:        1                             操作成功
    **                                          0                             操作失败
    *********************************************************************************/
    uint8_t I2C0_WriteByte(uint8_t Slave, uint8_t Addr_Type, uint32_t Buff_Addr, uint8_t *p, uint32_t Num)
    {
           
        if (Num > 0)                                            // 如果读取的个数为0,则返回错  
            {
                    if (Addr_Type == 1)                                                                                // 单字节
                    {
                        I2C0_Slave_Addr         = Slave;                              // 读器件的从地址               
                        I2C0_Buffer_Addr        = Buff_Addr;                             // 器件子地址                 
                        I2C0_Buffer_AddrType    = 1;                                // 器件子地址为1字节               
                       }
                    if (Addr_Type == 2)                                                                                // 双字节
                    {
                        I2C0_Slave_Addr         = Slave;                              // 读器件的从地址            
                        I2C0_Buffer_Addr        = Buff_Addr;                             // 器件子地址                  
                        I2C0_Buffer_AddrType    = 2;                                // 器件子地址为1字节               
                       }
                    if (Addr_Type == 3)                                                                                // 8+x结构
                    {
                        I2C0_Slave_Addr         = Slave + ((Buff_Addr >> 7 )& 0x0e);       // 读器件的从地址            
                        I2C0_Buffer_Addr        = Buff_Addr;                             // 器件子地址                  
                        I2C0_Buffer_AddrType    = 1;                                // 器件子地址为1字节                       
                       }

                pI2C0_Data              = p;                                    // 数据                       
                I2C0_Byte_Num    = Num;                                  // 数据个数                    
                I2C_Buffer_Dir   = 2;                                    // 有子地址,写操作            
                I2C0_End_Flag    = 0;
      LPC_I2C0->I2CONCLR = I2CONCLR_AAC|I2CONCLR_SIC|I2CONCLR_STAC;                                                        // 向该寄存器写1清零相应的位
      LPC_I2C0->I2CONSET = I2CONSET_STA|I2CONSET_I2EN;                                                        // 起始标志置位        I2C接口使能

            while(I2C0_End_Flag == 0){};                                                                               
                    if(I2C0_End_Flag){I2C_Delay(20);return 1;}                                                                                                          // 低到高跳变结束总线
                    else{I2C_Delay(20);return 0;}       
      }
      I2C_Delay(20);
      return 0;
    }

    /****************************************************************************************
    ** Function name:I2C_ReadNByte
    ** Descriptions: 从有子地址器件任意地址开始读取N字节数据
    ** parameters:          sla                        器件从地址
    **                                        suba_type        子地址结构        1-单字节地址        2-8+X结构        2-双字节地址
    **                                        suba                器件子地址
    **                                        s                          数据接收缓冲区指针
    **                                        num                        读取的个数
    ** Returned value:        1                        操作成功
    **                                          0                        操作失败
    *****************************************************************************************/
    uint8_t I2C0_ReadByte (uint8_t Slave, uint8_t Addr_Type, uint32_t Buff_Addr, uint8_t *p, uint32_t Num)
    {
            if (Num > 0)
            {
                    if (Addr_Type == 1)
                    {
                            I2C0_Slave_Addr         = Slave + 1;                                // 读器件的从地址,R=1         
                            I2C0_Buffer_Addr        = Buff_Addr;                                   // 器件子地址                  
                            I2C0_Buffer_AddrType    = 1;                                      // 器件子地址为1字节            
                    }
                    if (Addr_Type == 2)
                    {
                            I2C0_Slave_Addr         = Slave + 1;                                // 读器件的从地址,R=1         
                            I2C0_Buffer_Addr        = Buff_Addr;                                   // 器件子地址                  
                            I2C0_Buffer_AddrType    = 2;                                      // 器件子地址为1字节           
                    }
                    if (Addr_Type == 3)
                    {
                            I2C0_Slave_Addr         = Slave + ((Buff_Addr >> 7 )& 0x0e) + 1;         // 读器件的从地址,R=1         
                            I2C0_Buffer_Addr        = Buff_Addr & 0x0ff;                           // 器件子地址                  
                            I2C0_Buffer_AddrType    = 1;                                      // 器件子地址为1字节            
                    }

            pI2C0_Data              = p;                                            // 数据接收缓冲区指针           
            I2C0_Byte_Num    = Num;                                          // 要读取的个数                 
            I2C_Buffer_Dir         = 1;                                            // 有子地址读                  
            I2C0_End_Flag    = 0;
           
      LPC_I2C0->I2CONCLR = I2CONCLR_AAC|I2CONCLR_SIC|I2CONCLR_STAC;                                                        // 向该寄存器写1清零相应的位
      LPC_I2C0->I2CONSET = I2CONSET_STA|I2CONSET_I2EN;                                                        // 起始标志置位        I2C接口使能
           
            while(I2C0_End_Flag == 0){};
                    if(I2C0_End_Flag){I2C_Delay(20);return 1;}                                                                                                                       // 低到高跳变结束总线                       
                    else{I2C_Delay(20);return 0;}
            }
            I2C_Delay(20);
            return 0;
    }
    /******************************************************************************
    **                            End Of File
    ******************************************************************************/
    程序工程如下:


    我知道答案 目前已有8人回答

    逻辑分析仪

    逻辑分析仪

    I2C.rar

    415.89 KB, 下载次数: 0, 下载积分: 威望 1

    工程

    哎...今天够累的,签到来了~
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2024-6-3 15:51
  • 签到天数: 19 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    4

    主题

    54

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    246
    最后登录
    2024-9-4
     楼主| 发表于 2019-4-21 19:32:04 | 显示全部楼层
    还请各位大佬帮忙看下是什么原因。(对IIC中断方式传输数据的具体原理理解的不够透彻)
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2024-6-3 15:51
  • 签到天数: 19 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    4

    主题

    54

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    246
    最后登录
    2024-9-4
     楼主| 发表于 2019-4-21 19:37:25 | 显示全部楼层
    各位在用附件验证的时候注意,里面的程序读的过程中避开了前16位,主函数里面的读数据还应改成I2C0_ReadByte(0xae,1,0x00,ReadData,256);
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    712

    主题

    6371

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    24879
    最后登录
    2025-7-20
    发表于 2019-4-23 14:01:10 | 显示全部楼层
    楼主你好,我看你打印出来的读数据和你I2C总线上是一样的。
    如果你有问题,建议你直接把问题数据透传出来。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2024-6-3 15:51
  • 签到天数: 19 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    4

    主题

    54

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    246
    最后登录
    2024-9-4
     楼主| 发表于 2019-4-23 14:05:31 | 显示全部楼层
    小恩GG 发表于 2019-4-23 14:01
    楼主你好,我看你打印出来的读数据和你I2C总线上是一样的。
    如果你有问题,建议你直接把问题数据透传出来。 ...

    注意读数据的第一行应该是“0x00,0x01,0x02,0x03”而实际读出来的数据是“0xf0,0xf1,0xf2,0xf3”,而且这个f是跟上一次写的数据有关的,上次最后写的是0xff,下次读的第一行就是F什么,上次写的是3什么,下次第一行读的就是3什么
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    712

    主题

    6371

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    24879
    最后登录
    2025-7-20
    发表于 2019-4-23 17:30:29 | 显示全部楼层
    第一张图里面,你的逻辑分析仪测试I2Cbus的数据也是F开头的。说明总线上就是这个数据。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2024-6-3 15:51
  • 签到天数: 19 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    4

    主题

    54

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    246
    最后登录
    2024-9-4
     楼主| 发表于 2019-4-23 18:41:06 | 显示全部楼层
    小恩GG 发表于 2019-4-23 17:30
    第一张图里面,你的逻辑分析仪测试I2Cbus的数据也是F开头的。说明总线上就是这个数据。 ...

    这个我知道,但是我写的是0,为什么读出来就是F了,是程序的那个环节出错了,还是这个芯片有问题?
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    712

    主题

    6371

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    24879
    最后登录
    2025-7-20
    发表于 2019-4-24 17:20:51 | 显示全部楼层
    jackcheng2532 发表于 2019-4-23 18:41
    这个我知道,但是我写的是0,为什么读出来就是F了,是程序的那个环节出错了,还是这个芯片有问题? ...

    哦哦,你这样,debug下看看,在发送的时候,放到I2C buffer的是不是不带F的。
    还有在I2C操作之前,先把错误标志都清除一遍。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2024-6-3 15:51
  • 签到天数: 19 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    4

    主题

    54

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    246
    最后登录
    2024-9-4
     楼主| 发表于 2019-4-28 08:34:34 | 显示全部楼层
    问题已解决,是因为没有页写的原因
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2025-7-20 16:11 , Processed in 0.116856 second(s), 29 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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