I2C 是两线串行通信接口,可以支持主、从机两种模式的通信方式。主要功能如下:
• 兼容标准的I2C主从机工作模式
• 可编程到时钟频率支持不同的I2C数据传输速率
• 支持从机工作模式下双向数据传输
• 串行时钟同步使得传输速率不同的器件能够通过一条串行总线进行通信
• 支持达 400KHz速率
• 可设定为多达4 个不同的从机地址
• I2C 总线可用于测试和诊断
其总线应用框图如下:
图1 I2C总线框图
2、I2C操作
I2C总线可以工作在主机和从机模式。在从机模式下,I2C硬件时刻监视总线上是否出现4个已定义的从机地址。如地址被检测到,就会发出中断请求。如控制器设置为主机模式,I2C硬件检测到总线空闲时才会进入到主机模式,从机通讯不会因此而中断。
2-1 主机发送模式
该模式下,数据由主机发送到从机。在进入主发送模式前,CONSET寄存器必须被初始化。MASL置1选择为主机,TXRX置1选择发送模式。I2CEN必须置1以使能I2C功能。如果AA位为0,则当另一个器件为总线上的主机时,I2C接口不会对任何地址作出应答,因此不能进入从机模式。STA、STO和SI位必须为0。通过向CONCLR寄存器中的SIC 位写1来清零SI位。写从机地址后应清零STA位。STA, STO和SI位必须为0。SI位可用写1到CONCLR寄存器的SIC来清除。写从机地址后应清零STA位。具体的寄存器配置如下表格所示:
图2 I2C总线寄存器配置
发送的第一个字节包含接收器件的从机地址(7位)和数据方向位。在该模式下,数据方向位(R/W)应为0,表示执行写操作。发送的第一个字节包含从机地址和写操作位。一次发送8位数据。每发送完一个字节后,接收到一个应答位。输出起始和停止条件指示串行传输的起始和结束。
2-2 主机接收模式
在主机接收模式中,主机所接收的数据字节来自从机。传输按照主接收模式中的情况初始化。当发送完起始条件后,中断服务程序必须把从机地址和数据方向位(SLA+R)装入 DAT。必须先清零CON中的SI位,串行传输才能继续。当发送完从机地址和数据方向位且接收到一个应答位时,串行中断标志(SI)再次置位,STAT中可能是一系列不同的状态代码。主机模式下为0x0b, 0x4b或0x1d.,从机模式为0x68、0x78或0xB0。
3、I2C函数
在例程LIB->common->Drivers->Source文件夹内有i2c.c文件,这个就是提供的I2C驱动文件,里面包含了一些常用的驱动函数。
3-1 初始化程序
初始化I2C成主机或从机例子。
1. 将自身的从属地址装入ADR。
2. 使能I2C中断。
3. 向寄存器CONSET写入0x42来置位EN和清除MASL位,并使能从机功能。对于主机功能,可向寄存器CONSET写入0x40。
void I2C_Init(uint8_t Mode, uint32_t ClockRate, uint32_t SlaveAddress){ //使能I2C时钟并复位 SYSCON->PRESETCTRL_b.I2C_RST_N |= 1; SYSCON->SYSAHBCLKCTRL_b.I2C_CLK |= 1; //端口复用为I2C功能(根据实际应用进行配置) IOCON->PIO1_10 = 0x01 + (1<<10); // I2C SCL I/O config, PIO1_10-->SCL IOCON->PIO1_11 = 0x01 + (1<<10); // I2C SDA I/O config, PIO1_11-->SDA //清除I2C标志 I2C->CONCLR = 0xFF; //设置I2C时钟主频 I2C->SCLH_b.SCLH= SystemCoreClock/ClockRate; I2C->SCLL_b.SCLL = SystemCoreClock/ClockRate; //如果是从机,则配置地址 if (!Mode) { I2C->ADR0 = SlaveAddress; // Slave address } //设置主机/从机模式 if (Mode) { I2C->CONSET = 0x42; // Master mode } else { I2C->CONSET = 0x40; // Slave mode } //使能I2C中断 NVIC_EnableIRQ(I2C_IRQn); }
复制代码继续学习
3-2 主机模式发送数据
通过建立缓冲区、指针和数据计数、然后启动起始条件来执行主机发送操作。
1. 建立数据将被发送到的从属地址,并且添加写位。
2. 在主发送缓冲区内建立要发送的数据。
3. 初始化主机数据计数器。
4. 向CONSET写入0x20来置位STA位,启动发送。
uint8_t I2C_WriteArray(uint8_t SlaveAddress, uint8_t SubAddr, uint8_t *Buff, uint8_t Length){ uint8_t i; I2CMasterBuffer[0] = SlaveAddress; //从机地址 I2CMasterBuffer[1] = SubAddr; //操作地址 for( i = 0; i < Length; i++ ) { I2CMasterBuffer[i+2] = *( Buff + i ); } Re_Start = 0; I2CReadLength = 0; I2CWriteLength = Length+2; //发送长度 I2C_SetFlag(I2C_CONSET_STA); //启动发送 I2CMasterState = I2C_IDLE; while(( I2CMasterState != I2C_OK )&&( I2CMasterState != I2C_NACK ));//等待I2C操作结束 i = 0xFF; while(i--); return I2CMasterState; }
复制代码通过建立缓冲区、指针和数据计数、然后启动起始条件来执行主机接收操作。
1. 初始化主机数据计数器。
2. 建立数据将被发送到的从属地址,并且添加写位。
3. 向CONSET写入0x20 来置位STA 位。
4. 在主发送缓冲区内建立要发送的数据。
5. 建立主接收缓冲区。
void I2C_ReadArray(uint8_t SlaveAddress, uint8_t SubAddr, uint8_t Lenth, uint8_t *Buffer){ uint8_t i; I2CMasterBuffer[0] = SlaveAddress; //从机地址 I2CMasterBuffer[1] = SubAddr; //操作地址 I2CWriteLength = 2; I2CReadLength = Lenth; //读取的数据长度 Re_Start = 1; I2C_SetFlag(I2C_CONSET_STA); //启动读取 I2CMasterState = I2C_IDLE; while( I2CMasterState != I2C_OK ); //等待I2C操作结束 for(i = 0; i < Lenth; i++) { *(Buffer+i) = I2CSlaveBuffer[i]; //获取I2C数据 } i = 0xFF; while(i--); }
复制代码确定 I2C 的状态和处理该状态的状态程序。具体参考I2C函数文件中的中断服务程序I2C_IRQHandler。另外请注意i2c.c文件主要针对的是I2C主机模式的应用,其中断程序也是如此。I2C从机模式的应用可以参考i2c_slave.c文件,在后面的学习中会遇到。
1、从STA 中读出I2C 的状态。
2、使用状态值跳转到13个可能状态服务程序中的一个。
来源:老马识途单片机 https://www.toutiao.com/a6891102629535941134/