一、IIC协议详解
1、ALPHA开发板上有个AP3216C,这是一个IIC接口的器件,这是一个环境光传感器。AP3216C连接到了I2C1上:
I2C1_SCL: 使用的是UART4_TXD这个IO,复用位ALT2
I2C1_SDA: 使用的是UART4_RXD这个IO。复用为ALT2
2、I2C分为SCL和SDA,这两个必须要接上拉电阻到VCC,比如3.3V,一般是4.7K上拉电阻。
3、I2C总线支持多从机,通过从机地址来区分访问哪个从机。
二、6ULL I2C接口详解
1、6UL的I2C频率标准模式100kbit/S,快速模式400Kbit/S
2、时钟源选择perclk_clk_root=ipg_clk_root=66MHz
3、IFDR寄存器设置I2C频率,bit5:0设置频分值,假如我们现在需要100kbit的速率,那么66000000/100000=660。经过查找IC位设置位0X38或0X15的时候,为640分频,66000000/640=103.125Kbit.
4、I2CR寄存器,bit7为I2C使能位,置1使能I2C。bit5为主从模式选择位,为0表示从机,为1表示主机。Bit4为发送/接收设置位,为0的时候是接收,为1的时候是发送
5、I2SR寄存器,bit7为传输完成位,为0表示正在发送,为1表示发送完成。Bit5是I2C忙闲位,为0的时候I2C总线空闲,为1的时候I2C总线忙。Bit0是读确认位,也就是ACK信号
6、I2DR寄存器,数据寄存器。
三、AP3216C简介
1、AP3216C是一个三合一的环境光传感器,ALS+PS+IRLED,ALS是环境光,PS是接近传感器,IR是红外LED灯。I2C接口,最高400Kbit/S的频率。
2、环境光,ALS是16位输出。
3、接近传感器PS,10bit输出。IR传感器也是10bit
4、AP3216C的从机地址位0X1E。
5、0X0A是IR Ddata low。Bit7为0的时候表示IR和PS数据有效,为1的时候IR和PS数据无效。Bit1:0是IR的低2位。
6、0X0B是IR Data high,big7:0是高字节。与0X0A一起组成10bit的数据。
7、0X0C和0X0D分别位ALS的低8位和高8位。
8、0X0E的bit3:0是低4位数据,0X0F的bit5:0是高6位数据。加起来就是10位
9、0X00是系统配置寄存器,bit2:0设置AP3216C开始那些传感器,我们需要设置位011,也就是0x3,表示开始ALS+PS+IR。
//bsp_i2c.h
#ifndef _BSP_I2C_H
#define _BSP_I2C_H
#include "imx6ul.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)
/*
* I2C方向枚举类型
*/
enum i2c_direction
{
kI2C_Write = 0x0, /* 主机向从机写数据 */
kI2C_Read = 0x1, /* 主机从从机读数据 */
} ;
/*
* 主机传输结构体
*/
struct i2c_transfer
{
unsigned char slaveAddress; /* 7位从机地址 */
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);
#endif
//bsp_i2c.c
#include "bsp_i2c.h"
#include "bsp_delay.h"
#include "stdio.h"
//初始化I2C,波特率100KHZ
//要初始化的IIC设置
void i2c_init(I2C_Type *base)
{
/* 1、配置I2C */
base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */
/* 设置波特率为100K
* I2C的时钟源来源于IPG_CLK_ROOT=66Mhz
* IC2 时钟 = PERCLK_ROOT/dividison(IFDR寄存器)
* 设置寄存器IFDR,IFDR寄存器参考IMX6UL参考手册P1260页,表29-3,
* 根据表29-3里面的值,挑选出一个还是的分频数,比如本例程我们
* 设置I2C的波特率为100K, 因此当分频值=66000000/100000=660.
* 在表29-3里面查找,没有660这个值,但是有640,因此就用640,
* 即寄存器IFDR的IC位设置为0X15
*/
base->IFDR = 0X15 << 0;
/*
* 设置寄存器I2CR,开启I2C
* bit[7] : 1 使能I2C,I2CR寄存器其他位其作用之前,此位必须最先置1
*/
base->I2CR |= (1<<7);
}
//发送重新开始信号
//base : 要使用的IIC
//addrss : 设备地址
//direction : 方向
//返回 0 正常 其他值 出错
*/
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] : 要发送的数据,这里写入从设备地址
* 参考资料:IMX6UL参考手册P1249
*/
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
return 0;
}
// 发送开始信号
//base : 要使用的IIC
//addrss : 设备地址
//direction : 方向
//返回 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] : 要发送的数据,这里写入从设备地址
* 参考资料:IMX6UL参考手册P1249
*/
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
return 0;
}
/*
* @description : 检查并清除错误
* @param - base : 要使用的IIC
* @param - status : 状态
* @return : 状态结果
*/
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;
}
/*
* @description : 停止信号
* @param - base : 要使用的IIC
* @param : 无
* @return : 状态结果
*/
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;
}
// 发送数据
//base : 要使用的IIC
//buf : 要发送的数据
//size : 要发送的数据大小
//flags : 标志
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); /* 发送停止信号 */
}
//读取数据
//base : 要使用的IIC
//buf : 读取到数据
//size : 要读取的数据大小
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)); /* 接收数据 */
/* 如果只接收一个字节数据的话发送NACK信号 */
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;
}
}
// I2C数据传输,包括读和写
//base: 要使用的IIC
//xfer: 传输结构体
// 传输结果,0 成功,其他值 失败;
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.h
#ifndef _BSP_AP3216C_H
#define _BSP_AP3216C_H
#include "imx6ul.h"
#define AP3216C_ADDR 0X1E /* AP3216C器件地址 */
/* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR数据低字节 */
#define AP3216C_IRDATAHIGH 0x0B /* IR数据高字节 */
#define AP3216C_ALSDATALOW 0x0C /* ALS数据低字节 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS数据高字节 */
#define AP3216C_PSDATALOW 0X0E /* PS数据低字节 */
#define AP3216C_PSDATAHIGH 0X0F /* PS数据高字节 */
/* 函数声明 */
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);
#endif
//bsp_ap3216c.c
#include "bsp_ap3216c.h"
#include "bsp_i2c.h"
#include "bsp_delay.h"
#include "cc.h"
#include "stdio.h"
// 初始化AP3216C
//返回 0 成功,其他值 错误代码
unsigned char ap3216c_init(void)
{
unsigned char data = 0;
/* 1、IO初始化,配置I2C IO属性
* I2C1_SCL -> UART4_TXD
* I2C1_SDA -> UART4_RXD
*/
IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);
/*
*bit 16:0 HYS关闭
*bit [15:14]: 1 默认47K上拉
*bit [13]: 1 pull功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 驱动能力为R0/6
*bit [0]: 1 高转换率
*/
IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x70B0);
IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0X70B0);
i2c_init(I2C1); /* 初始化I2C1 */
/* 2、初始化AP3216C */
ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X04); /* 复位AP3216C */
delayms(50); /* AP33216C复位至少10ms */
ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X03); /* 开启ALS、PS+IR */
data = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG); /* 读取刚刚写进去的0X03 */
if(data == 0X03)
return 0; /* AP3216C正常 */
else
return 1; /* AP3216C失败 */
}
//向AP3216C写入数据
//addr: 设备地址
//reg : 要写入的寄存器
//data: 要写入的数据
//返回操作结果
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;
/* 配置I2C xfer结构体 */
masterXfer.slaveAddress = addr; /* 设备地址 */
masterXfer.direction = kI2C_Write; /* 写入数据 */
masterXfer.subaddress = reg; /* 要写入的寄存器地址 */
masterXfer.subaddressSize = 1; /* 地址长度一个字节 */
masterXfer.data = &writedata; /* 要写入的数据 */
masterXfer.dataSize = 1; /* 写入数据长度1个字节 */
if(i2c_master_transfer(I2C1, &masterXfer))
status=1;
return status;
}
//从AP3216C读取一个字节的数据
//addr: 设备地址
//reg : 要读取的寄存器
//返回读取到的数据。
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; /* 读取数据长度1个字节 */
i2c_master_transfer(I2C1, &masterXfer);
return val;
}
//读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意:如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
//ir : ir数据
//ps : ps数据
//als : als数据
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_OF位为1,则数据无效 */
*ir = 0;
else /* 读取IR传感器的数据 */
*ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
*als = ((unsigned short)buf[3] << 8) | buf[2]; /* 读取ALS传感器的数据 */
if(buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
*ps = 0;
else /* 读取PS传感器的数据 */
*ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}