当前位置: 首页 > news >正文

I2C接口(续三)

以上讨论了所有I2C接口所涉及的寄存器,现在来看一下该接口的具体使用方法。

首先来看基本的寄存器配置,可通过下列寄存器来配置I2C接口:

•在SYSAHBCLKCTRL寄存器中,置位相应I2C接口的位,使能接口时钟。
•使用PRESETCTRL寄存器来清除I2C外设复位。
•使能(或禁用)相应I2C接口的NVIC中断插槽(7、8、21、22)中的I2C中断。
•通过开关矩阵(PINENABLE、PINASSIGN等寄存器)配置I2C引脚功能。
•I2C的外设时钟即是系统时钟。

下面给出一个I2C初始化配置的函数。

void i2c_init()
{LPC_SYSCON->SYSAHBCLKCTRL|=1<<7;          //开启SWM时钟LPC_SWM->PINENABLE0 &= ~(0x3<<11);        //使能I2C0引脚LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<7);     //关闭SWM时钟LPC_IOCON->PIO0_10 = 0x80;                //配置I2C0的SCL引脚为标准/快速模式(默认值 )LPC_IOCON->PIO0_11 = 0x80;                //配置I2C0的SDA引脚为标准/快速模式(默认值 )LPC_SYSCON->SYSAHBCLKCTRL |= 1<<5;        //开启I2C0时钟LPC_SYSCON->PRESETCTRL &= ~(1<<6);        //开启I2C0外设复位LPC_SYSCON->PRESETCTRL |= 1<<6;           //关闭I2C0外设复位        LPC_I2C0->CLKDIV = 0x09;                  //配置SCL时钟为系统时钟的10分频LPC_I2C0->MSTTIME = 0x00;                 //将每个SCL高位和低位时间都设为2个时钟周期(默认值 )LPC_I2C0->CFG |= 1<<0;                    //使能主机模式
}

上述代码中,对I2C0模块进行了基本配置。由于I2C0模块的引脚不经过矩阵选择,所以只需要通过PINENABLE0寄存器使能引脚GPIO0_10的GPIO0_11的I2C功能即可。若使用其他I2C模块(I2C1~I2C3),则需要配置相应的PINASSIGN寄存器来选择具体引脚。当系统使用默认12MHz的IRC时钟时,经过上述配置的I2C时钟频率为300kHz。一般在低于400kHz时,引脚可使用标准/快速模式,即默认模式。高于时就要配置成超快速模式了。

下面分为主机和从机两种情况来讨论。

先看主机模式下的I2C发送/接收方式,在该模式下,I2C被设置为主机,主机向从机发送8位数据,然后从从机接收8位数据。使能PIO0_11和PIO0_10引脚上的I2C0_SCL和I2C0_SDA功能,或者通过开关阵列将任何其他的I2C模块的SCL和SDA功能分配至引脚。地址和数据位的传输受MSTPENDING状态位的状态管控,只要处于主机挂起状态,主机即可对MSTDAT寄存器执行读/写操作,并通过写入MSTCTL寄存器来进入下一步传输协议。

主机模式下又分两种情况来讨论,如下。

1、主机写入从机。

先把I2C配置为主机,在CFG寄存器中,将MSTEN位设置为1。接着按以下步骤向从机写入数据。
(1)将RW位设置为0的从机地址写入到主机数据寄存器MSTDAT。
(2)在主机控制寄存器中,将MSTSTART位设置为1,开始传输数据,此时会产生以下事件:
         a、挂起状态被清除,I2C总线处于忙碌状态。
         b、I2C主机将带有RW位的开始位和地址发送至从机。
(3)通过轮询STAT寄存器,等待挂起状态置位(MSTPENDING = 1)。
(4)在MSTDAT寄存器中写入8位数据。
(5)在主机控制寄存器中,将MSTCONT位设置为1,以便继续传输数据,此时会产生以下事件:
         a、挂起状态被清除,I2C总线处于忙碌状态。
         b、I2C主机将数据位发送至从机地址。
(6)通过轮询STAT寄存器,等待挂起状态置位(MSTPENDING = 1)。
(7)在主机控制寄存器中,将MSTSTOP位设置为1,以此停止传输。

按照上述步骤,下面给出一个写命令的函数。

void i2c_writeCmd(uint8_t cmd)
{    while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = 0xA0;                  //要写入的器件地址(写方向)LPC_I2C0->MSTCTL = 1<<1;                  //启动传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = cmd;                   //要写入的命令(1字节)LPC_I2C0->MSTCTL = 1<<0;                  //启动重新传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTCTL = 1<<2;                  //停止传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止
}

上述函数用于向地址为0xA0的器件(AT24C02,写方向)发送一条cmd命令,命令长度为8位。若需要向器件特定的地址发送数据,则使用下面的发送数据函数。

void i2c_writeData(uint8_t Addr, uint8_t Data)
{    while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = 0xA0;                  //要写入的器件地址(写方向)LPC_I2C0->MSTCTL = 1<<1;                  //启动传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = Addr;                  //要写入的地址(1字节)LPC_I2C0->MSTCTL = 1<<0;                  //启动重新传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = Data;                  //要写入的数据(1字节)LPC_I2C0->MSTCTL = 1<<0;                  //启动重新传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTCTL = 1<<2;                  //停止传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止
}

上述函数用于向地址为0xA0的器件(AT24C02,写方向)的Addr地址,发磅Data的数据。地址和数据长度均为8位。

2、主机读取从机。

先把I2C配置为主机,在CFG寄存器中,将MSTEN位设置为1,接着按以下步骤读取从机数据。
(1)将RW位设置为1的从机地址写入到主机数据寄存器MSTDAT。
(2)在主机控制寄存器中,将MSTSTART位设置为1,开始传输数据。此时会产生以下事件:
         a、挂起状态被清除,I2C总线处于忙碌状态。
         b、I2C主机将带有RW位的开始位和地址发送至从机。
         c、从机发送8位数据。
(3)通过轮询STAT寄存器,等待挂起状态置位(MSTPENDING = 1)。
(4)在MSTDAT寄存器中读取8位数据。
(5)在主机控制寄存器中,将MSTSTOP位设置为1,以此停止传输。

按照上述步骤,下面给出一个读数据的函数。

uint8_t i2c_readData(uint8_t Addr)
{uint8_t temp;                             //定义获取数据的变量while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = 0xA0;                  //要写入的器件地址(写方向)LPC_I2C0->MSTCTL = 1<<1;                  //启动传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = Addr;                  //要读取的地址(1字节)LPC_I2C0->MSTCTL = 1<<0;                  //启动重新传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = 0xA1;                  //要写入的器件地址(读方向)LPC_I2C0->MSTCTL = 1<<1;                  //启动传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x2)abort(); //若返回的状态不为空闲则终止temp = LPC_I2C0->MSTDAT;                  //读取数据(1字节)LPC_I2C0->MSTCTL = 1<<2;                  //停止传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止return temp;                              //返回读取的数据
}

上述函数用于向地址为0xA1的器件(AT24C02,读方向)的Addr地址读取一个字节的数据。

接下来看从机模式下的I2C接收/发送方式,在该模式下,I2C被设置为从机,从机接收来自主机的8位,并向主机发送8位。使能PIO0_11和PIO0_10引脚上的I2C0_SCL和I2C0_SDA功能,或者通过开关阵列将任何其他的I2C模块的SCL和SDA功能分配至引脚。地址和数据位的传输受SLVPENDING状态位的状态管控,只要处于从机挂起状态,从机即可确认(“ack”)或发送或接收地址和数据。接收的数据或待发送至主机的数据都包含在SLVDAT寄存器中,在发送和接收数据之后,向SLVCTL寄存器进行写入,以便进入下一步传输协议。

以下仍然分两种情况来讨论。

1、从机从主机读取。

先把I2C配置为从机,采用地址x。在CFG寄存器中,将SLVEN位设为1。将从机地址x写入地址0匹配寄存器。接着按以下步骤读取主机数据。
(1)通过轮询STAT寄存器,等待挂起状态置位(SLVPENDING = 1)。
(2)在从机控制寄存器中,通过将SLVCONTINUE 设置为等于1来确认(“ack”)地址。
(3)通过轮询STAT寄存器,等待挂起状态置位(SLVPENDING = 1)。
(4)在SLVDAT寄存器中读取8位数据。
(5)在从机控制寄存器中,通过将SLVCONTINUE 设置为1来确认(“ack”)数据。

2、从机写入主机。

先I2C配置为从机,采用地址x。在CFG寄存器中,将SLVEN位设为1。将从机地址x写入地址0匹配寄存器。接着按以下步骤向主机写入数据。
(1)通过轮询STAT寄存器,等待挂起状态置位(SLVPENDING = 1)。
(2)在从机控制寄存器中,通过将SLVCONTINUE 设置为等于1来确认地址。
(3)通过轮询STAT寄存器,等待挂起状态置位(SLVPENDING = 1)。
(4)在SLVDAT寄存器中写入8位数据。
(5)在从机控制寄存器中,通过将SLVCONTINUE 设置为1来继续执行事务。

下面看一个I2C通信的实例,以LPC824读写I2C接口的EEPROM为例,电路原理图如下。

无标题5

从上图中可以看到,AT24C04接到了LPC824的I2C0端口,并通过接在GPIO0_26和GPIO0_27引脚上的两个按键进行写入数据的加减控制,同时通过USART0循环发送从AT24C04读取的数据。代码如下。

#include <LPC82x.h>
#include <stdlib.h>
//*************************I2C初始化**********************************
void i2c_init()
{LPC_SYSCON->SYSAHBCLKCTRL|=1<<7;          //开启SWM时钟LPC_SWM->PINENABLE0 &= ~(0x3<<11);        //使能I2C0引脚LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<7);     //关闭SWM时钟LPC_IOCON->PIO0_10 = 0x80;                //配置I2C0的SCL引脚为标准/快速模式(默认值 )LPC_IOCON->PIO0_11 = 0x80;                //配置I2C0的SDA引脚为标准/快速模式(默认值 )LPC_SYSCON->SYSAHBCLKCTRL |= 1<<5;        //开启I2C0时钟LPC_SYSCON->PRESETCTRL &= ~(1<<6);        //开启I2C0外设复位LPC_SYSCON->PRESETCTRL |= 1<<6;           //关闭I2C0外设复位        LPC_I2C0->CLKDIV = 0x09;                  //配置SCL时钟为系统时钟的10分频LPC_I2C0->MSTTIME = 0x00;                 //将每个SCL高位和低位时间都设为2个时钟周期(默认值 )LPC_I2C0->CFG |= 1<<0;                    //使能主机模式
}
//***************************写数据************************************
void i2c_writeData(uint8_t Addr, uint8_t Data)
{    while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = 0xA0;                  //要写入的器件地址(写方向)LPC_I2C0->MSTCTL = 1<<1;                  //启动传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = Addr;                  //要写入的地址(1字节)LPC_I2C0->MSTCTL = 1<<0;                  //启动重新传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = Data;                  //要写入的数据(1字节)LPC_I2C0->MSTCTL = 1<<0;                  //启动重新传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTCTL = 1<<2;                  //停止传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止
}
//***************************读取数据************************************
uint8_t i2c_readData(uint8_t Addr)
{uint8_t temp;                             //定义获取数据的变量while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = 0xA0;                  //要写入的器件地址(写方向)LPC_I2C0->MSTCTL = 1<<1;                  //启动传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = Addr;                  //要读取的地址(1字节)LPC_I2C0->MSTCTL = 1<<0;                  //启动重新传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x4)abort(); //若返回的状态不为空闲则终止LPC_I2C0->MSTDAT = 0xA1;                  //要写入的器件地址(读方向)LPC_I2C0->MSTCTL = 1<<1;                  //启动传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x2)abort(); //若返回的状态不为空闲则终止temp = LPC_I2C0->MSTDAT;                  //读取数据(1字节)LPC_I2C0->MSTCTL = 1<<2;                  //停止传输while(!(LPC_I2C0->STAT & 0x01));          //等待I2C的状态就绪if((LPC_I2C0->STAT & 0xE) != 0x0)abort(); //若返回的状态不为空闲则终止return temp;                              //返回读取的数据
}
//************************串口初始化***********************************
void uart_init(void)
{LPC_SYSCON->SYSAHBCLKCTRL|=1<<7;          //开启SWM时钟LPC_SWM->PINASSIGN0 &= ~0xFFFF;LPC_SWM->PINASSIGN0 |= 0x04;LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<7);     //关闭SWM时钟(使用完之后记得关闭,节省功耗)LPC_SYSCON->SYSAHBCLKCTRL |= 1<<14;       //开启USART0时钟LPC_SYSCON->PRESETCTRL &= ~(1<<3);        //开启复位USART0LPC_SYSCON->PRESETCTRL |= (1<<3);         //关闭复位USART0LPC_USART0->CFG &= ~(1<<0);               //串口USART0禁止LPC_USART0->CFG|=1<<2;                    //配置USART0为8位数据方式LPC_SYSCON->UARTCLKDIV = 1;LPC_SYSCON->UARTFRGDIV = 0xFF;LPC_SYSCON->UARTFRGMULT = 0;              //配置串口的外设时钟U_PCLKLPC_USART0->BRG = 12;                     //配置USART0的波特率为115200LPC_USART0->CFG |= 1<<0;                  //串口USART0使能
}
//***************************延时函数************************************
void delay_ms(uint32_t ms)
{SysTick->LOAD = (((12000)*ms)-1);         //载入初始值SysTick->VAL = 0;                         //写当前值寄存器使其清零SysTick->CTRL |= (1<<0);                  //启动定时器,选择半系统时钟while(!(SysTick->CTRL & 0x10000));        //循环查询,等待定时时间到SysTick->CTRL &= ~(1<<0);                 //关闭定时器
}
//***************************主函数************************************
int main(void)
{uint8_t temp;                               //定义要写入的数据变量i2c_init();                                 //调用I2C初始化函数uart_init();                                //调用UART初始化函数LPC_GPIO_PORT->DIRCLR0 |= (1<<26) | (1<<27);//设置端口为输入方向while(1){if(!LPC_GPIO_PORT->B26)                 //当接在26引脚的按键K1按下
        {delay_ms(20);                       //延时进行按键消抖if(!LPC_GPIO_PORT->B26)             //确认按键按下
            {while(!LPC_GPIO_PORT->B26);     //等待按键释放temp = i2c_readData(0x1);       //读取AT24C04地址1处的数据if(temp++ > 0xfe)               //数据边界判定temp = 0xff;delay_ms(10);                   //延时10msi2c_writeData(0x1, temp);       //向AT24C04地址1处写入数据
            }}if(!LPC_GPIO_PORT->B27)                 //当接在27引脚的按键K2按下
        {delay_ms(20);                       //延时进行按键消抖if(!LPC_GPIO_PORT->B27)             //确认按键按下
            {while(!LPC_GPIO_PORT->B27);     //等待按键释放temp = i2c_readData(0x1);       //读取AT24C04地址1处的数据if(temp-- < 1)                  //数据边界判定temp = 0;delay_ms(10);                   //延时10msi2c_writeData(0x1, temp);       //向AT24C04地址1处写入数据
            }}delay_ms(200);                          //延时200msLPC_USART0->TXDAT = i2c_readData(0x1);  //发送读取到的AT24C04地址1处数据
    }
}

把上述代码编译后下载到LPC824中,并按电路接好连线。打开串口助手,就可以看到发来的读取AT24C04数据,按动K1按键可以增加写入的数据值,按动K2按键可以减小写入的数据值。并且在断电后重新上电,读取到的依然是上次写入的值,表明LPC824通过I2C0端口操作AT24C04是成功的。

http://www.jsqmd.com/news/845111/

相关文章:

  • 2026西安婚纱摄影权威测评报告|品质分级榜单+新人零踩坑决策指南 - charlieruizvin
  • llama-cpp-python:企业级本地大语言模型部署的Python实战指南
  • 2026实业设备闲置盘活攻略:塑胶注塑、电力变压器、机床制冷压铸及电缆线回收厂家优选指南 - 海棠依旧大
  • RAG检索质量提升秘籍!成本收益分析,教你花小钱办大事
  • 深圳超出圈的纹眉老店,久匠凭什么征服同城女生?十年技术实力过硬 - 企业博客发布
  • CNN与RNN入门技术博客
  • Hitboxer终极指南:免费专业解决游戏按键冲突的SOCD重映射工具
  • LPA分层审核指标是什么?读懂LPA分层审核指标才能评估审核有效性
  • 保姆级避坑指南:在Ubuntu 20.04 ROS Noetic下搞定宇树Z1机械臂仿真环境
  • 农业科学论文降AI工具免费推荐:2026年农业科学毕业论文降AI99.26%达标知网4.8元完整指南 - 还在做实验的师兄
  • RHCE第四次练习
  • 别再手动折腾了!用Microsoft Intune搞定企业设备管理的保姆级入门指南
  • 2026广州搬家公司推荐 专业靠谱设备齐全,天河区搬家更省心 - 从来都是英雄出少年
  • SpringBoot+Vue毕设实战:手把手教你从零搭建校园志愿者管理系统(附完整源码和数据库脚本)
  • D2DX完整指南:5个简单步骤让《暗黑破坏神2》在现代PC上完美运行
  • 魔兽争霸3终极优化指南:5分钟解决Windows 11兼容性问题
  • 2026年专科毕业论文降AI攻略:专科生毕业论文AIGC超标4.8元一次过知网完整处理指南 - 还在做实验的师兄
  • SAP开发日常:用SE10的‘副本传输’功能,让DEV到QAS的测试流程快一倍
  • 通达信缠论智能分析插件:5分钟实现专业K线结构可视化
  • 3个神奇步骤:用QRazyBox轻松修复任何损坏的二维码
  • 2026年导师返修意见后论文降AI攻略:返修后重写段落AIGC超标4.8元快速达标免费完整指南 - 还在做实验的师兄
  • Harness层服务鉴权流程优化
  • FPGA新手避坑指南:手把手教你配置Xilinx 7系列GTX的10G光通信IP核(Vivado 2022.1)
  • 世纪联华超市卡在线回收指南 - 购物卡回收找京尔回收
  • 2026 公众号排版实操:如何用 AI 智能生成风格统一的图文? - 鹅鹅鹅ee
  • LeetCode 1019.链表中的下一个更大节点
  • Python开发者如何快速接入Taotoken调用多模型API服务
  • 我的第 128 天创作里程碑:从 C 语言入门到 Java 学习之路
  • 2026年盲审AIGC检测标准解读:各高校盲审阶段AI率要求差异与免费应对完整指南 - 还在做实验的师兄
  • 2026无锡包包变现实力测评:奢包回收综合优势拉满,首选推荐 - 奢侈品回收测评