查看: 1517|回复: 0

LPC800系列分享:探索集成电路芯片间的通信总线技术

[复制链接]
  • TA的每日心情
    开心
    2025-7-10 13:07
  • 签到天数: 43 天

    连续签到: 1 天

    [LV.5]常住居民I

    56

    主题

    548

    帖子

    0

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    1528

    热心会员

    最后登录
    2025-7-23
    发表于 2024-9-30 15:04:17 | 显示全部楼层 |阅读模式
    本帖最后由 y369369 于 2024-9-30 15:21 编辑

    LPC800系列微控制器,自其问世以来便以其高性能和灵活性赢得了众多开发者的青睐。历经数代技术革新,LPC800携全新特性强势回归,旨在重新定义嵌入式应用的可能性。
    今天,首先为大家带来的是关于集成电路芯片间通信总线的详细介绍,这一核心技术对于理解LPC800的工作原理及其优势至关重要。敬请期待接下来的的系列文章,与我们一起探索LPC800的过去、现在与未来。
    I2C又写做IIC或I2C,英文发音是:I-squared-C,中文通常读为“Iái-方fāngC-xī”。
    I2C是英文Inter-Integrated Circuit的缩写,是飞利浦半导体(现恩智浦半导体)在上世纪80年代初,开发的一个简单的双线总线,用于电路板上控制器芯片与设备芯片之低速数据通信,也常用于一个系统中跨电路板的芯片间通信。下图是I2C总线的图标。

    微信图片_20240930151135.png

    I2C总线已经成为一个事实的工业标准,目前有成千上万的芯片实现了I2C接口。另外,还有很多控制系统也是基于I2C开发的,例如常用于可充电电池管理的SMBus(System Management Bus)、用于电源管理的PMBus(Power Management Bus)、用于电脑显卡与显示器之间通信的DDC(Display Data Channel)等。
    I2C是一种总线协议,具有以下特点:
    ▲只使用二根信号线,一个信号线是SDA(Serial Data Line 串行数据线),另一个信号线是SCL(Serial Clock Line串行时钟线)。
    ▲设备之间的通信始终是主从的关系,只有主设备才能发起数据传输。每个连接到I2C总线的设备都有一个唯一的地址,主设备使用从设备地址确定通信对象。
    ▲总线上允许多个主设备的存在,当两个或多个主设备同时要发起数据传输时,通过冲突检测和仲裁能够防止数据被破坏或丢失。
    ▲数据传输是串行的、始终是以每8个比特位为单位,传输速率有多种模式:
    标准模式:100 kbit/s
    快速模式:400 kbit/s
    快速模式+:1 Mbit/s
    高速模式:3.4 Mbit/s
    超高速模式:5 Mbit/s,此种模式的数据传输是单向的,通常用于微处理器与LED控制器的通信,或与那些不需要回传数据的设备的通信。
    !!!注意!!!传输速率是指时钟速率,而不是有效的数据传输率。
    由于协议的额外开销,实际数据传输率会比时钟速率低。
    一般为保险起见,应以时钟速率的1/3或更低来计算实际数据传输率。
    与SPI相比,I2C总线具有较少的信号线,可以在不增加信号线的情况下,挂接很多设备,挂接设备的数目只受设备地址数目和最大总线电容的限制。
    一个典型的I2C总线配置如下图,这里有两个MCU作为主设备,还有四个从设备。

    微信图片_20240930151138.png
    图1.I2C总线配置示意图
    一、I2C模块特性
    LPC800系列的每一个型号都内置了至少一个I2C硬件模块。软件对所有型号中每个I2C模块的操作都是一样的,用户可以很方便地把对应的I2C驱动代码,应用在任一LPC800产品上、任一的I2C模块上,做到一次编写多次使用。
    下面按照用户手册的顺序列出了I2C模块的主要功能和一个简要的说明:
    微信图片_20240930151140.png
    图2. I2C模块主要功能一览图

    二、I2C模块的内部构成
    2.1寄存器组一览
    每个I2C模块的操作寄存器组都是相同的,除了寄存器组的起始地址不同,每个寄存器相对起始地址的偏移量都是相同的,这非常方便用户使用同一组I2C的操作代码,操作不同的I2C模块,同时也方便用户在LPC800的不同型号之间移植代码。
    下表是寄存器组一览,共18个寄存器,每个寄存器占据4个字节的地址空间。这些寄存器按照功能可以划分为四组,表中以双分割线隔离:
    ■涉及所有功能的功能配置寄存器组,包含CFG、STAT、INTENSET、INTENCLR、TIMEOUT、CLKDIV和INTSTAT,共7个寄存器。
    ■控制主机功能的寄存器,包含MSTCTL、MSTTIME和MSTDAT,共3个寄存器。
    ■控制从机功能的寄存器,包含SLVCTL、SLVDAT、SLVADR0~ SLVADR3和SLVQUAL0,共7个寄存器。
    ■监测功能的寄存器,只有一个MONRXDAT。
    最后一列“有效位”用阴影标出了对应的有效控制/数据位,白底的位是保留位。“有效位”这一列可以让读者有一个直观的印象和参考,在编写程序时,有哪些寄存器和多少控制位需要考虑,做到心中有数。
    微信图片_20240930145341.png
    图3. I2C模块寄存器一览图
    注(1):LPC81x没有DMA模块,不包含DMA控制位
    2.2 模块框图
    接下来,让我们看下I2C模块的功能框图:

    微信图片_20240930151145.png
       图4. I2C模块的功能框图
    从这个框图也可以明显地看出,主机、从机和监测功能分别是三个独立的模块,它们可以互不干扰地工作。对于这三个独立模块,我们可以理解在每个独立模块中都有一个移位寄存器,分别单独负责主机功能、从机功能和监测功能的数据发送或接收。特别是对于只有一套移位寄存器的产品,是不能实现监测功能的。
    利用这一点,可以实现单一I2C模块的自发自收,或自我监测以确保主从功能正常工作;尤其是自我监测功能可以有效地用于软件的调试,下面会用一个例程说明如何使用监测功能进行调试。

    三、I2C主机收发操作步骤-轮询方式
    3.1 初始化:I2C模块主机收发 (轮询方式)
    对I2C模块的初始化,分为几个步骤:
    ▲打开I2C模块的时钟,并同时打开开关矩阵的时钟;
    ▲为I2C的外部信号线分配引脚;
    LPC82x的I2C0引脚是固定的,不能移动;其它I2C模块的引脚可以移动;
    LPC81x只有I2C0,其引脚可以移动。如果使用默认引脚时,即P0_10和P0_11,则速率可达1Mbit/s;如果使用其它引脚,则速率可达400 kbit/s;
    ▲配置I2C的时钟分频器。这一步产生的时钟频率仅用于I2C模块内部,还不是最终SCL信号线的频率。在上面框图中,该信号以I2C_PCLK表示;
    ▲配置SCL信号线高低电平的长度,这个长度的基准就是前一步时钟分配器输出的信号(I2C_PCLK);
    下面是对应的初始化函数代码,要求得到的SCL速率为100kHz:
    代码片段1. I2C0主机初始化函数
    1. 01  void I2C0_Master_Init(void)

    2. 02  {

    3. 03      // Enable clocks to I2C0, SWM

    4. 04      LPC_SYSCON->SYSAHBCLKCTRL |= (I2C0 | SWM);

    5. 05  

    6. 06  #if TARGET_BOARD == 824

    7. 07      // For LPC824, I2C0_SDA and I2C0_SCL are fixed pin.

    8. 08      LPC_SWM->PINENABLE0 &= ~(I2C0_SCL|I2C0_SDA);

    9. 09  #endif

    10. 10      

    11. 11  #if TARGET_BOARD == 812

    12. 12      // On the LPC812, I2C0_SDA and I2C0_SCL are movable.

    13. 13      ConfigSWM(I2C0_SCL, P0_10);

    14. 14      ConfigSWM(I2C0_SDA, P0_11);

    15. 15  #endif

    16. 16  

    17. 17      // Give I2C0 a reset

    18. 18      LPC_SYSCON->PRESETCTRL &= (I2C0_RST_N);

    19. 19      LPC_SYSCON->PRESETCTRL |= ~(I2C0_RST_N);

    20. 20  

    21. 21      // Set clock divider to get 100kHz bit rate.

    22. 22      LPC_I2C0->DIV = (75 - 1);

    23. 23  

    24. 24      // WARNING: default value is 0x77

    25. 25      LPC_I2C0->MSTTIME = 0x0;

    26. 26  

    27. 27      // Configure the I2C0 CFG register

    28. 28      LPC_I2C0->CFG = CFG_MSTENA;

    29. 29  }
    复制代码

    微信图片_20240930151148.png
    3.2 主机发送(轮询方式)
    下图是I2C主机发送的状态图,以及对应的软硬件操作:
    1.png
                         图5. I2C主机发送状态图

    上图以I2C主机发送的状态变化图为主线,在各处相关的节点,标注了应该执行的操作。
    下面直接以一个写入EEPROM的程序代码,详细说明这些操作。下图是写入EEPROM的操作流程图:
    2.png
                       图6.写入EEPROM操作流程图

    首先先看图6的第一个操作,发出START位的函数,该函数执行图5中的B操作:
    代码片段2. I2C0作为主机发送器时输出START位的函数
    1. 00  // 等待主机状态为空闲,然后发送START和从机地址,读/写位为”写”;
    2. // 反复发送START和从机地址,直到从机ACK为止  
    3. 01  void I2C_Start_Tx(LPC_I2C_TypeDef* pI2C, unsigned char slave_addr)  
    4. 02  {   uint32_t  mststate;  
    5. 03   
    6. 04      I2C_Wait_Master(pI2C, I2C_STAT_MSTST_IDLE);

    7. 05      do {  
    8. 06          pI2C->MSTDAT = (slave_addr<<1) | 0;  
    9. 07          pI2C->MSTCTL = CTL_MSTSTART;  
    10. 08          while(!(pI2C->STAT & STAT_MSTPEND));

    11. 09          mststate = pI2C->STAT & MASTER_STATE_MASK;

    12. 10      } while (mststate != I2C_STAT_MSTST_TX);  
    13. 11  }
    复制代码

    函数I2C_Start_Tx()返回时,意味着硬件状态已经到达位置D,软件可以开始发送数据字节了。
    函数I2C_Start_Tx(),通过调用以下的函数,等待主机状态。后面的代码中还会经常使用这个函数。
    代码片段3. I2C等待主机状态函数
    1. 00  // 等待主机状态为以下条件:STAT_MSTIDLE(空闲)、STAT_MSTRX(接收就绪)、STAT_MSTTX(发送就绪)、

    2.   //                     STAT_MSTNACKADDR(从机NACK地址)或STAT_MSTNACKTX(从机NACK数据)

    3. 01  void I2C_Wait_Master(LPC_I2C_TypeDef* pI2C, uint32_t state)

    4. 02  {   uint32_t mstate;

    5. 03      while(!(pI2C->STAT & STAT_MSTPEND));     // 等待主机就绪

    6. 04      mstate = pI2C->STAT & MASTER_STATE_MASK;

    7. 05      if (mstate != state) {  // 如果主机状态不是期待的状态,则点亮红灯

    8. 06          LED_Rotate();

    9. 07          while(1);           // 这里是个死循环,操作者需要检查哪里出了问题

    10. 08      }

    11. 09  }
    复制代码
    函数I2C_Wait_Master ()只是简单地等待主机功能就绪,然后查看主机状态是否为期待的状态,如果有所差池,则循环点亮红灯进入一个死循环,操作者在看到红灯后就知道发生错误,可以停下调试器查找原因。
    写入EEPROM的程序代码,需要用到这些变量定义:
    1. #define RXBUF_SIZE      128          // 与EEPROM进行数据交换的缓冲区大小  

    2. 定义数据缓冲区


    3.   unsigned char   txbuf[RXBUF_SIZE];   // 与EEPROM进行数据交换的缓冲区  

    4.   uint32_t        txbuf_ptr;           // 操作txbuf的索引变量  

    5.   uint32_t        txbuf_len;           // 控制txbuf中有效数据的数量
    复制代码
    上述变量定义段不一定和下面的代码在同一个地方。
    接下来这个程序片段,完整地实现了写入EEPROM一组数据的功能:
    代码片段4.  I2C0写入24C02 EEPROM函数
    1. 00  // 初始化I2C0并从txbuf[]缓冲区中,发送txbuf_len个字节至EEPROM中

    2. 01      I2C0_Master_Init();

    3. 02         

    4. 03      txbuf_ptr = 0;

    5. 04      while (txbuf_ptr < txbuf_len) {

    6. 05          I2C_Start_Tx(LPC_I2C0, I2C_EEPROM_ADDR);

    7. 06          I2C_Send_Byte(LPC_I2C0, txbuf_ptr);

    8. 07  

    9. 08          unsigned char eepage_ptr;

    10. 09          for (eepage_ptr = 0; eepage_ptr < EEPAGE_SIZE; eepage_ptr++) {

    11. 10              I2C_Send_Byte(LPC_I2C0, txbuf[txbuf_ptr]);

    12. 11              txbuf_ptr++;

    13. 12              if (txbuf_ptr >= txbuf_len)

    14. 13                  break;

    15. 14          }

    16. 15          I2C_Stop(LPC_I2C0);

    17. 16      }
    复制代码

    程序执行步骤说明如下:
    1. 初始化I2C0,并使能主机功能——语句01;
    2. 检测缓冲区指针是否到达结尾——语句04;
    3. 发送START、从机地址和读写位——语句05;
    4. 发送EEPROM的起始地址——语句06;
    5. 循环写入EEPROM一页的数据(最多8个字节)——语句09~14;
    6. 在循环中判断缓冲区指针是否到达结尾——语句12;
    7. 发送STOP——语句15;
    8. 返回步骤2,继续下一页的操作。

    下面是函数I2C_Send_Byte()的代码:
      代码片段5. I2C输出一个字节的函数
    1. 00  // 发出一个字节,并等待发送结束

    2. 01  void I2C_Send_Byte(LPC_I2C_TypeDef* pI2C, unsigned char databyte)

    3. 02  {

    4. 03      pI2C->MSTDAT = databyte;

    5. 04      pI2C->MSTCTL = CTL_MSTCONTINUE;

    6. 05      I2C_Wait_Master(pI2C, I2C_STAT_MSTST_TX);

    7. 06  }
    复制代码
    发送一个字节的操作很直接:把输出字节写入MSTDAT寄存器,并设置MSTCTL.MSTCONTINUE=”1”,随后硬件就会输出对应的SCL和SDA信号,然后等待EEPROM的ACK或NACK。见图5的E操作。
    最后是函数I2C_Stop()的代码:
    代码片段6. I2C输出STOP的函数
    1. 00  // 发出STOP,并等待发送结束

    2. 01  void I2C_Stop(LPC_I2C_TypeDef* pI2C)

    3. 02  {

    4. 03      pI2C->MSTCTL = CTL_MSTSTOP;

    5. 04      while(!(pI2C->STAT & STAT_MSTPEND));

    6. 05  }
    复制代码

    微信图片_20240930151200.png
    下面两张图片是示波器的截图,清楚地显示了完整的STOP位,和因为上述错误的复位导致不成功的STOP。图中上行信号是SDA,下行显示的信号是SCL。

    微信图片_20240930151203.png
      图7.正确的STOP输出(上图)和被终止的STOP输出(下图)
    下图中也可以看到,最后一个SCL低也被截断变得不完整:上图显示最后一个SCL低持续大约27μs,但下图中最后一个SCL低只持续了大约9μs,同时SDA信号也变得紊乱。
    综合一下上述操作过程,可以看出了LPC800的I2C模块功能十分强大,在初始化好I2C模块后,软件只需做到以下三个步骤即可完成整个I2C的发送流程:
    写一个数据寄存器:MSTDAT寄存器
    检测两个状态域:STAT状态寄存器的MSTSTATE 域(空闲和发送就绪状态),和MSTPENDING位
    三个控制位:主机控制寄存器的MSTSTART、MSTCONTINUE和MSTSTOP位
    还要始终记住一件事,每次设置任一个主机控制寄存器的控制位后,进入下一个操作前,都要确认主机已经就绪(状态寄存器的MSTPENDING位),即前一步操作结束,否则很可能出现意想不到的问题。

    3.3 主机接收(轮询方式)
    下图是I2C主机接收的状态图,以及对应的软硬件操作:
    4.png
                          图8. I2C主机接收状态图
    上图以I2C主机接收的状态变化图为主线,在各处相关的节点,标注了应该执行的操作。
    下面以前述写入EEPROM的程序为基础,说明如何把写入的数据读出来。下图是读出EEPROM数据的操作流程:

    5.png
               图9. 从EEPROM读出N字节数据的流程图

    图9的上半部分是一个写操作(读写位为”0”),用于输出EEPROM读出区域的首地址,这个过程的程序与代码片段4的05~06行相同。图9的下半部分是一个读操作(读写位为”1”),用于从EEPROM中读出数据。
    下面来看发出读操作START位的函数,该函数执行图8中的B操作:
    代码片段7. I2C0作为主机接收器时输出START位的函数
    1. 00  // 等待主机状态为Pending,然后发送START和从机地址,读/写位为”读”;

    2. // 反复发送START和从机地址,直到从机ACK为止

    3. 01  void I2C_Start_Rx(LPC_I2C_TypeDef* pI2C, unsigned char slave_addr)

    4. 02  {

    5. 03      uint32_t  mststate;

    6. 04      while(!(pI2C->STAT & STAT_MSTPEND));    // Wait for master pending

    7. 05      do {

    8. 06          pI2C->MSTDAT = (slave_addr<<1) | 1;

    9. 07          pI2C->MSTCTL = CTL_MSTSTART;

    10. 08  

    11. 09          while(!(pI2C->STAT & STAT_MSTPEND));

    12. 10  

    13. 11          mststate = pI2C->STAT & MASTER_STATE_MASK;

    14. 12      } while (mststate != I2C_STAT_MSTST_RX);

    15. 13  }
    复制代码

    函数I2C_Start_Rx()返回时,意味着硬件状态已经到达位置E,即硬件已经发出了START、7位从机地址、读写位,并且得到从机ACK响应,第一个8位数据也已经移入了数据寄存器。

    这个函数与前面I2C主机发送的START函数在流程上差不多,但也有几处明显的不同:
    函数I2C_Start_Rx()首先要等待I2C模块进入Pending状态(04行),而不是等待Idle状态。因为我们现在需要发送图69的第二个START, I2C总线不是处于Idle状态。
    读写位=”1”(06行),而不是”0”。
    循环 (05~12行) 的条件是“接收就绪”,而不是“发送就绪”。

    图8的F操作需要特别留意一下,当软件读出接收到的数据后,在置位MSTCTL.MSTCONTINUE=“1”之前主机不会发出ACK,如果处理时间过长,则有可能造成从机超时,尽管一般的I2C设备没有超时控制,我们还是应该尽量把对数据的处理放到所有I2C操作结束以后进行,尽快置位MSTCTL.MSTCONTINUE=“1”,向从机发出ACK。
    缩短所有I2C的操作,减少从START至STOP之间的时间间隔,可以尽早关闭I2C模块和让外部I2C设备进入睡眠状态,有利于减小总体功耗;在多主系统中,也可以避免某个主机过长霸占总线的情况。

    下面是实现图8和图9状态流程图的代码:
    代码片段8. I2C0读出24C02 EEPROM
    00      // 从EEPROM读出数据至rxbuf[RXBUF_SIZE]01      rxbuf_ptr = 0;          // 复位读缓存指针02      rxbuf_len = txbuf_len;  // 读出数据段长度03  04      I2C_Start_Tx(LPC_I2C0, I2C_EEPROM_ADDR);05      I2C_Send_Byte(LPC_I2C0, 0)06          07      I2C_Start_Rx(LPC_I2C0, I2C_EEPROM_ADDR); 08      rxbuf[rxbuf_ptr++] = LPC_I2C0->MSTDAT;09          10      while (rxbuf_ptr < rxbuf_len) {11          LPC_I2C0->MSTCTL = CTL_MSTCONTINUE; 12          I2C_Wait_Master(LPC_I2C0, I2C_STAT_MSTST_RX); 13          rxbuf[rxbuf_ptr++] = LPC_I2C0->MSTDAT;      14      }15      I2C_Stop(LPC_I2C0);
    3.4 I2C主机功能的三个控制位
    这里需要特别说明的是MSTCTL中三个控制位,在I2C主机接收时的作用与作为主机发送时的作用有所不同:
    ▲MSTSTART位:基本功能是发送START、7位从机地址和读写位,接受从机回送的ACK或NACK状态:
    对于读写位为“写”时,当从机回送ACK或NACK后,硬件设置MSTPENDING位,软件开始进行状态判断和下一步操作;
    对于读写位为“读”时,当从机回送ACK后,主机继续发送SCL时钟并按位移入从机送出的数据,完成全部8位数据后,硬件设置MSTPENDING位,软件开始进行状态判断和下一步操作。注意,此时主机不会发送ACK,也不会发送NACK;
    ▲MSTCONTINUE位:基本功能是继续之前已经开始的“读”或“写”操作:
    对于写操作,主机继续发送SCL和一个字节的数据,当从机回送ACK或NACK后,硬件设置MSTPENDING位,软件开始进行状态判断和下一步操作;
    对于读操作,主机首先回送一个ACK给从机,然后主机继续发送SCL时钟并按位移入从机送出的数据,随后硬件设置MSTPENDING位,软件开始进行状态判断和下一步操作;
    ▲MSTSTOP位:基本功能是发出STOP位:
    对于写操作,主机发出STOP位,然后硬件设置MSTPENDING位,主机进入IDLE状态;
    对于读操作,主机首先回送NACK给从机,然后再发出STOP位,随后是硬件设置MSTPENDING位并进入IDLE状态;
    综合上述说明,下面的状态图中标示了各控制位的作用域和各状态位变化的情况:
    6.png
      图10.主机发送流程控制位、状态位和外部信号的时序关系图
    7.png
      图11.主机接收流程控制位、状态位和外部信号的时序关系图
    3.5  轮询方式读写EEPROM的完整代码
    前面分段介绍了主机功能初始化和读/写EEPROM的代码段,下面把所有主程序代码集中在一起,方便读者查看。相关调用函数的代码,请参考前面部分。
    代码片段9. I2C0作为主机读写EEPROM
    1. 00  const unsigned char the_prompt[] = "Type any key to continue.\n\r";

    2. 01  // 定义EEPROM的一些常量

    3. 02  #define I2C_EEPROM_ADDR 0x50    // EEPROM 24C02 的7位I2C地址

    4. 03  #define EEPROM_SIZE     255     // EEPROM存储区的大小(字节)

    5. 04  #define EEPAGE_SIZE     8       // EEPROM存储区每页的大小(字节)

    6. 05  

    7. 06  unsigned char eeprom_string[] = "1234567890ABCD\0";  // 测试数据

    8. 07  #define TEST_SIZE       12                           // 操作字节数目

    9. 08  int main(void) {

    10. 09      setup_debug_uart(); // Configure the debug UART

    11. 10  

    12. 11      while(1) {

    13. 12          // Display promote string on UART screen

    14. 13          PutTerminalString(LPC_USART0, (uint8_t *)the_prompt);

    15. 14  

    16. 15          uart_handshake = false;

    17. 16          while (!uart_handshake);

    18. 17  

    19. 18          memcpy(txbuf, eeprom_string, TEST_SIZE);

    20. 19          txbuf_len = TEST_SIZE;

    21. 20  

    22. 21  // 将txbuf[TXBUF_SIZE]的数据写入EEPROM,总共TEST_SIZE个字节

    23. 22          // Initialize I2C0 as master

    24. 23          I2C0_Master_Init();

    25. 24          I2C0_Enable_Monitor();

    26. 25  

    27. 26          txbuf_ptr = 0;

    28. 27          while (txbuf_ptr < txbuf_len) {

    29. 28              I2C_Start_Tx(LPC_I2C0, I2C_EEPROM_ADDR);

    30. 29              I2C_Send_Byte(LPC_I2C0, txbuf_ptr);

    31. 30  

    32. 31              unsigned char eepage_ptr;

    33. 32              for (eepage_ptr = 0; eepage_ptr < EEPAGE_SIZE; eepage_ptr++) {

    34. 33                  I2C_Send_Byte(LPC_I2C0, txbuf[txbuf_ptr]);

    35. 34                  txbuf_ptr++;

    36. 35                  if (txbuf_ptr >= txbuf_len)

    37. 36                      break;

    38. 37              }

    39. 38              I2C_Stop(LPC_I2C0);

    40. 39          }

    41. 40  

    42. 41          // Reading data from EEPROM to rxbuf[RXBUF_SIZE]

    43. 42          rxbuf_ptr = 0;          // Reset the pointer of receiver

    44. 43          rxbuf_len = txbuf_len;  // Get back all writing bytes

    45. 44  

    46. 45          I2C_Start_Tx(LPC_I2C0, I2C_EEPROM_ADDR);  // Send a START condition

    47. 46          I2C_Send_Byte(LPC_I2C0, 0);               // Send the memory address of EEPROM

    48. 47         

    49. 48          I2C_Start_Rx (LPC_I2C0, I2C_EEPROM_ADDR); // Send a START condition as receiver

    50. 49          rxbuf[rxbuf_ptr++] = LPC_I2C0->MSTDAT;    // Get the first received byte

    51. 50         

    52. 51          while (rxbuf_ptr < rxbuf_len) {

    53. 52              LPC_I2C0->MSTCTL = CTL_MSTCONTINUE;          // Continue the transaction

    54. 53              I2C_Wait_Master(LPC_I2C0, I2C_STAT_MSTST_RX);// Wait for the data to be received

    55. 54              rxbuf[rxbuf_ptr++] = LPC_I2C0->MSTDAT;       // Get one received byte

    56. 55          }

    57. 56          I2C_Stop(LPC_I2C0);

    58. 57      } // end of while 1

    59. 58  } // end of main
    复制代码
    转载自: 恩智浦MCU加油站
    如有侵权请联系删除


    签到签到
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2025-10-10 05:48 , Processed in 0.089145 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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