嵌入式学习 day52 IMX6ULL裸机开发-I2C
一、I2C
又称I2C,是是IICBus简称,所以中文应该叫集成电路总线,是一种半双工、串行、同步、芯片与芯片之间的通信,通常只有一根数据总线,采用主从应答方式
二、总线
上图所示是IIC的总线的使用场景,所有挂载在IIC总线上的设备都有两根信号线,一根是数据线SDA,另一根是时钟线SCL。这两个信号线都是双向的。作为一种通信方式,IIC总线在某一时刻,总线只允许有一个设备处于发送状态,所发生的数据被总线上所有的设备所接收。IIC通信协议包含有设备地址,只有发送方携带的地址与某个接收方的地址相同时,接收方才真正执行相关的指令。
注:1、上拉电阻:
IIC总线规定,设备在空闲时,两根总线都处于高电平状态。为保证这种状态,数据线SDA和时钟线SCL都要外接上拉电阻。对于I.MX来说,这个上拉电阻也可以在引脚电器配置中设置。
2、线与特性(逻辑与)
A | B | 总电平 |
1 | 1 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
0 | 0 | 0 |
3、释放总线:
将电平置为高电平,则数据线上的电平由对方决定
三、IIC通信时序
1、起始信号
IIC总线上连接的若干设备中,每次通信前,发送方首先发送一个“起始”信号,其实信号就是在SCL为高电平时,SDA发送一个低电平。当其它设备接收到这个其实信号后,将进行一次“总线仲裁”。意思就是设备(除发送其实信号的那个设备以外的)都将处于聆听状态。
2、数据传输
IIC总线进行数据传送时,时钟线(SCL)上的信号为高电平期间,数据线(SDA)上的数据必须保持稳定。只有在时钟线(SCL)上的信号为低电平期间,数据线(SCL)上的高电平或低电平状态才允许变化。同时,SCL信号由数据启动发送的设备提供。输出到数据线(SDA)上的每个字节必须是8位。数据传送时,先传送最高位(MSB),后传送最低位(LSB)。
3、应答
发送器每发送一个字节(8个bit),就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。 主机SCL拉高,读取从机SDA的电平。对于反馈有效应答位ACK的要求是:接收器在第9个时钟脉冲之前的低电平期间将数据线SDA拉低,并且确保在该时钟的高电平期间为稳定的低电平。
(1)数据线(SDA)为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节。
(2)数据线(SDA)为高电平时,规定为非应答位(NACK),表示接收器没有成功接收该字节。
4、终止信号
当发送方发送完最后一个bit后,需要发送一个结束标志来终止整个通信过程。当时钟线SCL 为高电平时,数据线SDA 由低电平向高电平跳变。
5、完整时序
四、读写时序
1、写时序:
首先由主机发送一个起始位,主机再以高位先行原则发送7个bit位的设备地址和1个bit位的数据流向位(0,表示主机发从机收),从机应答一个ACK,主机发送存入数据的寄存器地址,从机再次应答ACK,主机发送数据,从机应答ACK,循环发送n个数据(每个数据两个字节),n不大于8个,从机应答ACK,主机发送停止信号
2、读时序
首先由主机发送一个起始位,主机再以高位先行原则发送7个bit位的设备地址和1个bit位的数据流向位(0,表示主机发从机收),从机应答一个ACK,主机发送读取数据的寄存器地址,从机再次应答ACK,主机再发送一个起始位,再以高位先行原则发送7个bit位的设备地址和1个比特位的数据流向位(1,表示主机收从机发),从机应答一个ACK,从机向主机发送数据,主机应答一个ACK,直到最后一个数据发送完成后,主机应答一个NACK表示数据读取完毕,主机再发送停止信号
五、I2C的时钟
由时钟配置的66M
六、I2C寄存器
1、IFDR(分频寄存器)
2、I2CR(控制寄存器)
3、I2SR(状态寄存器)
4、数据寄存器
七、步骤
1、初始化
(1)引脚复用功能和电器属性
(2)寄存器设置
void init_i2c1(void)
{
IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA,1);
IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL,1);
IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA,0x70b0);
IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL,0x70b0);
I2C1->I2CR &= ~(1 << 7);
I2C1->IFDR = 0x15;
I2C1->I2CR |= (1 << 7);
}
2、写数据
void i2c_write(I2C_Type *base,unsigned char dev_addr,unsigned char reg_addr,const unsigned char* data,unsigned int len)
{
base->I2SR &= ~((1 << IAL) | (1 << IIF));
while((base->I2SR & (1 << ICF)) == 0);
base->I2CR |= (1 << MSTA) | (1 << MTX);
base->I2CR &= ~(1 << TXAK);
base->I2SR &= ~(1 << IIF);
base->I2DR = dev_addr << 1;
while((base->I2SR & (1 << IIF)) == 0);
base->I2SR &= ~(1 << IIF);
base->I2DR = reg_addr;
while((base->I2SR & (1 << IIF)) == 0);
while(len--)
{
base ->I2SR &= ~(1 << IIF);
base->I2DR = *data++;
while((base->I2SR & (1 << IIF)) == 0);
}
base->I2CR &= ~(1 << MSTA);
int t = 0;
while((base->I2SR & (1 << IBB)) != 0 && t < 10)
{
++t;
delay_us(100);
}
}
3、读数据
void i2c_read(I2C_Type *base,unsigned char dev_addr,unsigned char reg_addr,unsigned char* data,unsigned int len)
{
base->I2SR &= ~((1 << IAL) | (1 << IIF));
while((base->I2SR & (1 << ICF)) == 0);
base->I2CR |= (1 << MSTA) | (1 << MTX);
base->I2CR &= ~(1 << TXAK);
base->I2SR &= ~(1 << IIF);
base->I2DR = dev_addr << 1;
while((base->I2SR & (1 << IIF)) == 0);
base->I2SR &= ~(1 << IIF);
base->I2DR = reg_addr << 1;
while((base->I2SR & (1 << IIF)) == 0);
base->I2CR |= (1 << RSTA);
base->I2SR &= ~(1 << IIF);
base->I2DR = dev_addr << 1 | (1 << 0);
while((base->I2SR & (1 << IIF)) == 0);
base->I2CR &= ~(1 << MTX);
base->I2SR &= ~(1 << IIF);
if(1 == len)
{
base->I2CR |= (1 << TXAK);
}
*data = base->I2DR;
while(len--)
{
while((base->I2SR & (1 << IIF)) == 0);
base->I2SR &= ~(1 << IIF);
if(1 == len)
{
base->I2CR |= (1 << TXAK);
}
else if(0 == len)
{
base->I2CR &= ~((1 << MSTA) | (1 << TXAK));
int t = 0;
while((base->I2SR & (1 << IBB)) != 0 && t < 10)
{
++t;
delay_us(100);
}
}
*data++ = base->I2DR;
}
}