I2C使用两条线在主控制器和从机之间进行数据通信,一条串行时钟线(SCL),一条是串行数据线(SDA),这两条线需要接上拉电阻(阻值一般4.7K),总线空闲的时候SCL和SDA都处于高电平。I2C总线标准模式下速度可达100KB/s,快速模式下可达400KB/s
I2C支持多从机,这些不同的I2C从设备有不同的器件地址,I2C控制器可以通过从设备的器件地址访问指定的I2C设备
I2C总线工作是按照一定的协议来运行的,下面介绍I2C有关的术语
- 起始位:SCL为高电平时,SDA出现下降沿表示起始位
- 停止位:SCL为高电平时,SDA出现上升沿表示停止位
- 数据传输:数据传输时要保证SCL高电平期间,SDA上的数据稳定;因此SDA上的数据变化只能发生在SCL低电平期间
- 应答信号:I2C主机发送完8位数据后会将SDA设置为输入状态,等待I2C从机应答;应答信号由从机发出,通过将SDA拉低来发出应答信号,表示通信成功
- I2C写时序:I2C总线单字节写时序如下图示
– 1. 开始信号
– 2. 发送I2C设备地址
– 3. 读写位,0表示写操作,1表示读操作
– 4. 从机发送的ACK应答信号
– 5. 重新发送开始信号
– 6. 发送要写入数据的寄存器地址
– 7. 从机发送的ACK应答信号
– 8. 发送要写入寄存器的数据
– 9. 从机发送的ACK应答信号
– 10. 停止信号
- I2C读时序:I2C总线单字节读时序如下图示
1.2 IMX6U I2C介绍– 1. 主机发送起始信号
– 2. 主机发送要读取的I2C从设备地址
– 3. 读写控制位,此时是向从设备发送数据,因此是写信号
– 4. 从机发送的ACK应答信号
– 5. 重新发送开始信号
– 6. 主机发送要读取的寄存器地址
– 7. 从机发送的ACK应答信号
– 8. 重新发送开始信号
– 9. 从新发送要读取的I2C从设备地址
– 10. 读写控制位,此时是从I2C从设备里读取数据,因此是读信号
– 11. 从机发送的ACK应答信号
– 12. 从I2C器件里读取到的数据
– 13. 主机发出NO ACK信号表示读取完成,不需要从机再发ACK信号了
– 14. 主机发出停止信号
IMX6U提供了4个I2C外设,通过这四个I2C外设即可完成与I2C从器件进行通信。IMX6U的I2C支持两种模式:标准模式(100KB/s)和快速模式(400KB/s)
I2C外设几个重要的寄存器
- I2Cx_IADR 寄存器:I2C地址寄存器 (x = 1 ~ 4),用来保存从设备地址(bit7:1有效)
- I2Cx_IFDR 寄存器:分频寄存器,用来设置波特率,通过IC位设置波特率
- I2Cx_I2CR 寄存器:I2C控制寄存器
– IEN:I2C使能位,为1使能,为0关闭
– IIEN:I2C中断使能位,为1使能,为0关闭
– MSTA:主从模式选择位,为1时工作为主模式,为0时工作在从模式
– MTX:传输方向选择位,为0时接收,为1时发送
– TXAK:传输应答使能,为0发送ACK信号,为1发送NO ACK信号
– RSTA:重复开始信号,为1表示产生一个重新开始信号
- I2Cx_I2SR 寄存器:I2C状态寄存器
– ICF:数据传输状态位,为0表示数据正在传输,为1表示数据传输完成
– IAAS:为1时表示I2C地址,即I2Cx_IADR寄存器中的地址是从设备地址
– IBB:I2C总线忙标志位,为0表示I2C总线空闲,为1表示总线忙
– IAL:仲裁丢失位,为1时表示发生仲裁丢失
– SRW:从机读写状态位,为0表示主机要向从机写数据,为1表示从从机读取数据
– IIF:I2C中断挂起标志位,为1时表示有中断挂起,此位需要软件清零
– RXAK:应答信号标志位,为0时表示接收到ACK应答信号,为1时表示NO ACK信号
- I2Cx_I2DR 寄存器:I2C数据寄存器,低8位有效;当要发送数据的时候将要发送的数据写入到此寄存器,当要接收数据的话直接读取此寄存器即可得到接收到的数据
AP3216C传感器是一个支持环境光强度(ALS)、接近距离(PS)和红外强度(IR)的三合一环境传感器。该芯片可以通过I2C接口与主控制相连,并且支持中断
AP3216C常被用于手机、平板、导航设备等,其内置的接近传感器可以用于检测是由有物体接近,也可以使用环境光传感器检测光强度,可以实现自动背光亮度调节。AP3216C结构图如下示
AP3216的设备地址为0x1E,AP3216C内部也有一些寄存器,通过这些寄存器可以配置AP3216C的工作模式,并读取相应的数据,AP3216C内部寄存器如下表示:
综上所述,AP3216C的配置步骤如下:
二、硬件介绍
- 初始化相应的IO,设置复用功能,若使用中断还需要设置中断IO
- 初始化I2C1,设置波特率
- 初始化AP3216C ,读取AP3216C的数据
本例程需要用到的硬件资源:
- LED0
- RGB LCD接口
- UART
- AP3216C
AP3216C的原理图如下示
- 新建i2c文件夹,在文件夹中创建实时时钟驱动文件bsp_i2c.c和bsp_i2c.h
#define I2C_STATUS_OK (0)
#define I2C_STATUS_BUSY (1)
#define I2C_STATUS_IDLE (2)
#define I2C_STATUS_NAK (3)
#define I2C_STATUS_ARBITRATIONLOST (4)
#define I2C_STATUS_TIMEOUT (5)
#define I2C_STATUS_ADDRNAK (6)
enum i2c_direction
{
kI2C_Write = 0x0,
kI2C_Read = 0x1,
} ;
struct i2c_transfer
{
unsigned char slaveAddress;
enum i2c_direction direction;
unsigned int subaddress;
unsigned char subaddressSize;
unsigned char *volatile data;
volatile unsigned int dataSize;
};
void i2c_init(I2C_Type *base);
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction);
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address, enum i2c_direction direction);
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status);
unsigned char i2c_master_stop(I2C_Type *base);
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size);
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size);
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer);
void i2c_init(I2C_Type *base)
{
base->I2CR &= ~(1 << 7);
base->IFDR = 0x15 << 0;
base->I2CR |= (1<<7);
}
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
{
if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0))
return 1;
base->I2CR |= (1 << 4) | (1 << 2);
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))
return 1;
base->I2CR |= (1 << 5) | (1 << 4);
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);
base->I2CR |= (1 << 7);
return I2C_STATUS_ARBITRATIONLOST;
}
else if(status & (1 << 0))
{
return I2C_STATUS_NAK;
}
return I2C_STATUS_OK;
}
unsigned char i2c_master_stop(I2C_Type *base)
{
unsigned short timeout = 0xffff;
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++;
while(!(base->I2SR & (1 << 1)));
base->I2SR &= ~(1 << 1);
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)
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)); //向I2DR寄存器写入子地址
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;
}
- 新建ap3216c文件夹,在文件夹中创建实时时钟驱动文件bsp_ap3216c.c和bsp_ap3216c.h
#define AP3216C_ADDR 0x1E #define AP3216C_SYSTEMCONG 0x00 #define AP3216C_INTSTATUS 0x01 #define AP3216C_INTCLEAR 0x02 #define AP3216C_IRDATALOW 0x0A #define AP3216C_IRDATAHIGH 0x0B #define AP3216C_ALSDATALOW 0x0C #define AP3216C_ALSDATAHIGH 0x0D #define AP3216C_PSDATALOW 0x0E #define AP3216C_PSDATAHIGH 0x0F unsigned char ap3216c_init(void); unsigned char ap3216c_readonebyte(unsigned char addr,unsigned char reg); unsigned char ap3216c_writeonebyte(unsigned char addr,unsigned char reg, unsigned char data); void ap3216c_readdata(unsigned short *ir, unsigned short *ps, unsigned short *als);
unsigned char ap3216c_init(void)
{
unsigned char data = 0;
IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);
IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x70B0);
IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0x70B0);
i2c_init(I2C1);
ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0x04);
delayms(50);
ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0x03);
data = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG);
if(data == 0x03)
return 0;
else
return 1;
}
unsigned char ap3216c_writeonebyte(unsigned char addr,unsigned char reg, unsigned char data)
{
unsigned char status=0;
unsigned char writedata=data;
struct i2c_transfer masterXfer;
masterXfer.slaveAddress = addr;
masterXfer.direction = kI2C_Write;
masterXfer.subaddress = reg;
masterXfer.subaddressSize = 1;
masterXfer.data = &writedata;
masterXfer.dataSize = 1;
if(i2c_master_transfer(I2C1, &masterXfer))
status=1;
return status;
}
unsigned char ap3216c_readonebyte(unsigned char addr,unsigned char reg)
{
unsigned char val=0;
struct i2c_transfer masterXfer;
masterXfer.slaveAddress = addr;
masterXfer.direction = kI2C_Read;
masterXfer.subaddress = reg;
masterXfer.subaddressSize = 1;
masterXfer.data = &val;
masterXfer.dataSize = 1;
i2c_master_transfer(I2C1, &masterXfer);
return val;
}
void ap3216c_readdata(unsigned short *ir, unsigned short *ps, unsigned short *als)
{
unsigned char buf[6];
unsigned char i;
for(i = 0; i < 6; i++)
{
buf[i] = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_IRDATALOW + i);
}
if(buf[0] & 0x80)
*ir = 0;
else
*ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0x03);
*als = ((unsigned short)buf[3] << 8) | buf[2];
if(buf[4] & 0x40)
*ps = 0;
else
*ps = ((unsigned short)(buf[5] & 0x3F) << 4) | (buf[4] & 0x0F);
}
- 主函数main.c中编写测试程序
int main(void)
{
unsigned short ir, als, ps;
unsigned char state = OFF;
int_init();
imx6u_clkinit();
delay_init();
clk_enable();
led_init();
beep_init();
uart_init();
lcd_init();
tftlcd_dev.forecolor = LCD_RED;
lcd_show_string(30, 50, 200, 16, 16, (char*)"ALPHA-IMX6U IIC TEST");
lcd_show_string(30, 70, 200, 16, 16, (char*)"AP3216C TEST");
lcd_show_string(30, 90, 200, 16, 16, (char*)"ATOM@ALIENTEK");
lcd_show_string(30, 110, 200, 16, 16, (char*)"2019/3/26");
while(ap3216c_init())
{
lcd_show_string(30, 130, 200, 16, 16, (char*)"AP3216C Check Failed!");
delayms(500);
lcd_show_string(30, 130, 200, 16, 16, (char*)"Please Check! ");
delayms(500);
}
lcd_show_string(30, 130, 200, 16, 16, (char*)"AP3216C Ready!");
lcd_show_string(30, 160, 200, 16, 16, (char*)" IR:");
lcd_show_string(30, 180, 200, 16, 16, (char*)" PS:");
lcd_show_string(30, 200, 200, 16, 16, (char*)"ALS:");
tftlcd_dev.forecolor = LCD_BLUE;
while(1)
{
ap3216c_readdata(&ir, &ps, &als);
lcd_shownum(30 + 32, 160, ir, 5, 16);
lcd_shownum(30 + 32, 180, ps, 5, 16);
lcd_shownum(30 + 32, 200, als, 5, 16);
delayms(120);
state = !state;
led_switch(LED0,state);
}
return 0;
}
四、下载验证
- 修改Makefile文件:修改TARGET为ap3216c,追加“bsp/ap3216c”和“bsp/i2c”文件夹
- 使用imxdownload软件将bin文件下载到SD卡中
- 烧写成功后,插入SD卡,复位开发板后,LCD屏幕上会显示环境光强度(ALS)、接近距离(PS)和红外强度(IR)的数值



