I2C简介
I2C是很常见的一种总线协议,I2C是NXP公司设计的,I2C使用两条线在主控制器和从机之间进行数据通信。一条是SCL(串行时钟线),另外一条是SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候SCL和SDA处于高电平。I2C总线标准模式下速度可以达到100Kb/S,快速模式下可以达到400Kb/S。
I2C是支持多从机的:
一般会接一个4.7k的上拉电阻(VDD)。
I2C总线工作是按照一定的协议来运行的 ,其中相关术语:
起始位
I2C通信起始标志,在SCL为高电平的时候,SDA出现下降沿就表示为起始位。
停止位
停止I2C通信的标志位,和起始位的功能相反。在SCL位高电平的时候,SDA出现上升沿就表示为停止位。
数据传输
I2C总线在数据传输的时候要保证在SCL高电平期间,SDA上的数据稳定,因此SDA上的数据变化只能在SCL低电平期间发生。
应答信号
当I2C主机发送完8位数据以后会将SDA设置为输入状态,等待I2C从机应答,也就是等到I2C从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完8位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将SDA拉低来表示发出应答信号,表示通信成功,否则表示通信失败。
I2C主机与从机之间的操作只有读与写两个操作。其时序分别为:
1)、开始信号。
2)、发送I2C设备地址,每个I2C器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C器件。这是一个8位的数据,其中高7位是设备地址,最后1位是读写位,为1的话表示这是一个读操作,为0的话表示这是一个写操作。
3)、 I2C器件地址后面跟着一个读写位,为0表示写操作,为1表示读操作。
4)、从机发送的ACK应答信号。
5)、重新发送开始信号。
6)、发送要写写入数据的寄存器地址。
7)、从机发送的ACK应答信号。
8)、发送要写入寄存器的数据。
9)、从机发送的ACK应答信号。
10)、停止信号。
1)、主机发送起始信号。
2)、主机发送要读取的I2C从设备地址。
3)、读写控制位,因为是向I2C从设备发送数据,因此是写信号。
4)、从机发送的ACK应答信号。
5)、重新发送START信号。
6)、主机发送要读取的寄存器地址。
7)、从机发送的ACK应答信号。
8)、重新发送START信号。
9)、重新发送要读取的I2C从设备地址。
10)、读写控制位,这里是读信号,表示接下来是从I2C从设备里面读取数据。
11)、从机发送的ACK应答信号。
12)、从I2C器件里面读取到的数据。
13)、主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
14)、主机发出STOP信号,停止I2C通信。
操作I2C需要几个重要的寄存器:
I2Cx_IADR(x=1~4)寄存器:寄存器I2Cx_IADR只有ADR(bit7:1)位有效,用来保存I2C从设备地址数据。当我们要访问某个I2C从设备的时候就需要将其设备地址写入到ADR里面。
寄存器I2Cx_IFDR:寄存器I2Cx_IFDR也只有IC(bit5:0)这个位,用来设置I2C的波特率,I2C的时钟源可以选择IPG_CLK_ROOT=66MHz,I.MX6U的I2C支持两种模式:标准模式和快速模式,标准模式下I2C数据传输速率最高是100Kbits/s,在快速模式下数据传输速率最高为400Kbits/s。通过设置IC位既可以得到想要的I2C波特率。
这里可以设置为0x15或0x38,即分频为640。
寄存器I2Cx_I2CR:这个是I2C控制寄存器,其各个位的作用:
IEN(bit7):I2C使能位,为1的时候使能I2C,为0的时候关闭I2C。
IIEN(bit6):I2C中断使能位,为1的时候使能I2C中断,为0的时候关闭I2C中断。
MSTA(bit5):主从模式选择位,设置IIC工作在主模式还是从模式,为1的时候工作在主模式,为0的时候工作在从模式。
MTX(bit4):传输方向选择位,用来设置是进行发送还是接收,为0的时候是接收,为1的时候是发送。
TXAK(bit3):传输应答位使能,为0的话发送ACK信号,为1的话发送NO ACK信号。
RSTA(bit2):重复开始信号,为1的话产生一个重新开始信号。
寄存器I2Cx_I2SR:I2C的状态寄存器 。
寄存器I2Cx_I2SR的各位含义如下:
ICF(bit7):数据传输状态位,为0的时候表示数据正在传输,为1的时候表示数据传输完成。
IAAS(bit6):当为1的时候表示I2C地址,也就是I2Cx_IADR寄存器中的地址是从设备地址。
IBB(bit5):I2C总线忙标志位,当为0的时候表示I2C总线空闲,为1的时候表示I2C总线忙。
IAL(bit4):仲裁丢失位,为1的时候表示发生仲裁丢失。
SRW(bit2):从机读写状态位,当I2C作为从机的时候使用,此位用来表明主机发送给从机的是读还是写命令。为0的时候表示主机要向从机写数据,为1的时候表示主机要从从机读取数据。
IIF(bit1):I2C中断挂起标志位,当为1的时候表示有中断挂起,此位需要软件清零。
RXAK(bit0):应答信号标志位,为0的时候表示接收到ACK应答信号,为1的话表示检测到NO ACK信号。
最后一个寄存器就是I2Cx_I2DR,这是I2C的数据寄存器,此寄存器只有低8位有效,当要发送数据的时候将要发送的数据写入到此寄存器,如果要接收数据的话直接读取此寄存器即可得到接收到的数据。
了解后开始写:
#include "bsp_i2c.h"
#include "bsp_delay.h"
#include "stdio.h"
void i2c_init(I2C_Type *base)
{
/* 1、配置I2C */
base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */
base->IFDR = 0X15 << 0;
/* 设置寄存器I2CR,开启I2C */
base->I2CR |= (1<<7);
}
unsigned char i2c_master_repeated_start(I2C_Type *base,
unsigned char address,
enum i2c_direction direction) {
/* I2C忙并且工作在从模式,跳出 */
if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0))
return 1;
/*
* 设置寄存器I2CR
* bit[4]: 1 发送
* bit[2]: 1 产生重新开始信号
*/
base->I2CR |= (1 << 4) | (1 << 2);
/*
* 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
*/
base->I2DR = ((unsigned int)address << 1) |
((direction == kI2C_Read)? 1 : 0);
return 0;
}
unsigned char i2c_master_start(I2C_Type *base,
unsigned char address,
enum i2c_direction direction)
{
if(base->I2SR & (1 << 5)) /* I2C忙 */
return 1;
/*
* 设置寄存器I2CR
* bit[5]: 1 主模式
* bit[4]: 1 发送
*/
base->I2CR |= (1 << 5) | (1 << 4);
/*
* 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
*/
base->I2DR = ((unsigned int)address << 1) |
((direction == kI2C_Read)? 1 : 0);
return 0;
}
unsigned char i2c_check_and_clear_error(I2C_Type *base,
unsigned int status)
{
if(status & (1<<4)) /* 检查是否发生仲裁丢失错误 */
{
base->I2SR &= ~(1<<4); /* 清除仲裁丢失错误位 */
base->I2CR &= ~(1 << 7); /* 先关闭I2C */
base->I2CR |= (1 << 7); /* 重新打开I2C */
return I2C_STATUS_ARBITRATIONLOST;
}
else if(status & (1 << 0)) /* 没有接收到从机的应答信号 */
{
return I2C_STATUS_NAK; /* 返回NAK(No acknowledge) */
}
return I2C_STATUS_OK;
}
unsigned char i2c_master_stop(I2C_Type *base)
{
unsigned short timeout = 0XFFFF;
/* 清除I2CR的bit[5:3]这三位 */
base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
while((base->I2SR & (1 << 5))) /* 等待忙结束 */
{
timeout--;
if(timeout == 0) /* 超时跳出 */
return I2C_STATUS_TIMEOUT;
}
return I2C_STATUS_OK;
}
void i2c_master_write(I2C_Type *base, const unsigned char *buf,
unsigned int size)
{
while(!(base->I2SR & (1 << 7))); /* 等待传输完成 */
base->I2SR &= ~(1 << 1); /* 清除标志位 */
base->I2CR |= 1 << 4; /* 发送数据 */
while(size--)
{
base->I2DR = *buf++; /* 将buf中的数据写入到I2DR寄存器 */
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
base->I2SR &= ~(1 << 1); /* 清除标志位 */
/* 检查ACK */
if(i2c_check_and_clear_error(base, base->I2SR))
break;
}
base->I2SR &= ~(1 << 1);
i2c_master_stop(base); /* 发送停止信号 */
}
void i2c_master_read(I2C_Type *base, unsigned char *buf,
unsigned int size)
{
volatile uint8_t dummy = 0;
dummy++; /* 防止编译报错 */
while(!(base->I2SR & (1 << 7))); /* 等待传输完成 */
base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
base->I2CR &= ~((1 << 4) | (1 << 3)); /* 接收数据 */
if(size == 1) /* 如果只接收一个字节数据的话发送NACK信号 */
base->I2CR |= (1 << 3);
dummy = base->I2DR; /* 假读 */
while(size--)
{
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
base->I2SR &= ~(1 << 1); /* 清除标志位 */
if(size == 0)
i2c_master_stop(base); /* 发送停止信号 */
if(size == 1)
base->I2CR |= (1 << 3);
*buf++ = base->I2DR;
}
}
unsigned char i2c_master_transfer(I2C_Type *base,
struct i2c_transfer *xfer)
{
unsigned char ret = 0;
enum i2c_direction direction = xfer->direction;
base->I2SR &= ~((1 << 1) | (1 << 4)); /* 清除标志位 */
while(!((base->I2SR >> 7) & 0X1)){}; /* 等待传输完成 */
/* 如果是读的话,要先发送寄存器地址,所以要先将方向改为写 */
if ((xfer->subaddressSize > 0) && (xfer->direction ==
kI2C_Read))
direction = kI2C_Write;
ret = i2c_master_start(base, xfer->slaveAddress, direction);
if(ret)
return ret;
while(!(base->I2SR & (1 << 1))){}; /* 等待传输完成 */
ret = i2c_check_and_clear_error(base, base->I2SR);
if(ret)
{
i2c_master_stop(base); /* 发送出错,发送停止信号 */
return ret;
}
/* 发送寄存器地址 */
if(xfer->subaddressSize)
{
do
{
base->I2SR &= ~(1 << 1); /* 清除标志位 */
xfer->subaddressSize--; /* 地址长度减一 */
base->I2DR = ((xfer->subaddress) >> (8 *
xfer->subaddressSize));
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
/* 检查是否有错误发生 */
ret = i2c_check_and_clear_error(base, base->I2SR);
if(ret)
{
i2c_master_stop(base); /* 发送停止信号 */
return ret;
}
} while ((xfer->subaddressSize > 0) && (ret ==
I2C_STATUS_OK));
if(xfer->direction == kI2C_Read) /* 读取数据 */
{
base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
i2c_master_repeated_start(base, xfer->slaveAddress,
kI2C_Read);
while(!(base->I2SR & (1 << 1))){}; /* 等待传输完成 */
/* 检查是否有错误发生 */
ret = i2c_check_and_clear_error(base, base->I2SR);
if(ret)
{
ret = I2C_STATUS_ADDRNAK;
i2c_master_stop(base); /* 发送停止信号 */
return ret;
}
}
}
/* 发送数据 */
if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0))
i2c_master_write(base, xfer->data, xfer->dataSize);
/* 读取数据 */
if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0))
i2c_master_read(base, xfer->data, xfer->dataSize);
return 0;
}