嵌入式Linux操作I2C设备,我们一般会在内核态编写I2C驱动程序。另外还能在用户空间编写I2C程序,下面介绍相关代码的实现。
i2c-dev框架在内核中封装了I2C通信所需要的所有通信细节,I2C适配器会在/dev目录下创建字符设备,例如:/dev/i2c-0,通过系统调用操作/dev/i2c-0就可以实现与I2C设备通信。
一、I2C适配器操作函数
下面介绍如何在用户空间打开I2C适配器,并使用I2C适配器读写I2C设备。
1、打开I2C适配器
调用open系统调用打开/dev/i2c-n文件
/* 打开字符设备 */
s32 hal_i2c_open(u32 u32I2cIdx, s32 *ps32Fd)
{
s32 s32Fd = 0;
s8 s8Fname[128] = {0,};
sprintf((char *)s8Fname, "/dev/i2c-%u", u32I2cIdx);
s32Fd = open((char *)s8Fname, 0);
if (0 >= s32Fd){
LOG_WARN("i2c open %s s32Fd=%d,retry it\n",s8Fname,s32Fd);
s32Fd = open((char *)s8Fname, 0);
if (0 >= s32Fd){
LOG_ERR("Open %s error, s32Fd %d,u32I2cIdx:0x%X!\n",s8Fname, s32Fd,u32I2cIdx);
return -1;
}
}
*ps32Fd = s32Fd;
return 0;
}
/* 关闭字符设备 */
s32 hal_i2c_close(s32 s32Fd)
{
if(0 >= s32Fd){
LOG_ERR("failed !\n");
return -1;
}
close(s32Fd);
return 0;
}
2、I2C适配器读写
通过ioctl去读写I2C适配器从而与I2C设备通信
/*
*****************************************************************************************
* 函 数 名: hal_i2c_read
* 功能说明: I2C读
* 形 参: s32Fd : I2C节点
* u16DevAddr : 设备地址
* u16RegAddr : 寄存器地址
* u16RegLen : 寄存器长度
* pu8Buf : 读取数据buf
* u16DataLen : 需要读取数据长度
* 返 回 值: 返回0:OK
* 其他: ERROR
*****************************************************************************************
*/
s32 hal_i2c_read(s32 s32Fd, u16 u16DevAddr, u16 u16RegAddr,u16 u16RegLen, u8 *pu8Buf, u16 u16DataLen)
{
u8 u8Buf[2] = {0,};
struct i2c_rdwr_ioctl_data rdwr = {0};
struct i2c_msg msg[2] = {0};
if(I2C_DEVICE_REG_LEN_2BIT == u16RegLen){
u8Buf[0] = (u16RegAddr >> 8) & 0xff;
u8Buf[1] = u16RegAddr & 0xff;
}else{ //8位寄存器
u8Buf[0] = u16RegAddr & 0xff;
}
msg[0].addr = u16DevAddr; //设备地址
msg[0].flags = 0;
msg[0].len = u16RegLen;
msg[0].buf = u8Buf; //存放寄存器的地址
msg[1].addr = u16DevAddr;
msg[1].flags = 0;
msg[1].flags |= I2C_M_RD;
msg[1].len = u16DataLen; //需要读取的数据长度
msg[1].buf = pu8Buf;//存放返回数据的地址
rdwr.msgs = &msg[0];
rdwr.nmsgs = 2;
if(ioctl(s32Fd, I2C_RDWR, &rdwr) != 2){
LOG_ERR("CMD_I2C_READ error! \n");
goto Error;
}
return 0;
Error:
return -1;
}
/*
*****************************************************************************************
* 函 数 名: hal_i2c_write
* 功能说明: I2C写
* 形 参: s32Fd : I2C节点
* u16DevAddr : 设备地址
* u16RegAddr : 寄存器地址
* u16RegLen : 寄存器长度
* pu8Buf : 写入数据buf
* len : 需要写入数据长度
* 返 回 值: 返回0:OK
* 其他: ERROR
*****************************************************************************************
*/
s32 hal_i2c_write(s32 s32Fd, u16 u16DevAddr, u16 u16RegAddr,u16 u16RegLen, u8 *pu8Buf, u16 len)
{
u8 *pu8SendBuff = NULL;
struct i2c_rdwr_ioctl_data rdwr = {0,};
struct i2c_msg messages = {0,};
if(I2C_DEVICE_REG_LEN_2BIT == u16RegLen)
{
pu8SendBuff = (u8 *)malloc(len + I2C_DEVICE_REG_LEN_2BIT); //需要传递的buffer中,头2个字节是寄存器地址
pu8SendBuff[0] = (u16RegAddr >> 8) & 0xff; //保存寄存器地址值
pu8SendBuff[1] = u16RegAddr & 0xff;
}
else
{
pu8SendBuff = (u8 *)malloc(len + I2C_DEVICE_REG_LEN_1BIT);
pu8SendBuff[0] = u16RegAddr & 0xff; //保存寄存器地址值
}
if (NULL == pu8SendBuff){
LOG_ERR("malloc failed!\n");
return -1;
}
if (NULL != pu8Buf)
{
if(I2C_DEVICE_REG_LEN_2BIT == u16RegLen)
memcpy(&pu8SendBuff[I2C_DEVICE_REG_LEN_2BIT], pu8Buf, len); //寄存器地址长度为2,需要从pu8SendBuff[2]开始存放需要写的buffer数据
else
memcpy(&pu8SendBuff[I2C_DEVICE_REG_LEN_1BIT], pu8Buf, len);
}
messages.addr = u16DevAddr; //设备地址
messages.flags = 0;
messages.buf = pu8SendBuff;
if(I2C_DEVICE_REG_LEN_2BIT == u16RegLen)
messages.len = len + I2C_DEVICE_REG_LEN_2BIT;
else
messages.len = len + I2C_DEVICE_REG_LEN_1BIT;
rdwr.msgs = &messages;
rdwr.nmsgs = 1;
if(0 >= ioctl(s32Fd, I2C_RDWR, (unsigned long)&rdwr)){
perror("error:");
LOG_ERR("I2C send failed!\n");
goto Error;
}
free(pu8SendBuff);
return 0;
Error:
free(pu8SendBuff);
return -1;
}
二、实例
ap3216C是一款三合一环境传感器,它内部集成了:数字环境光传感器(Ambilent Light Aensors,ALS)、距离传感器(Proximity Sense,PS)和一个红外LED(Infrared Radiation LED,IR LED),该芯片通过IIC接口连接ARM板,设备地址为0x1E。
下图为主要的几个寄存器:
首先,我们可以用i2cdetect工具去探测I2C1上的ap3216c芯片,看硬件通路是否正常
$ i2cdetect -r -y 0
1、读ap3216c芯片的数据
代码下载链接:https://download.csdn.net/download/hinewcc/89438732
从I2C适配器接口操作ap3216c的代码如下:
/* 打开/dev/i2c-0 */
static s32 ap3216c_i2c_open(u32 _u32I2cIdx, s32 *_ps32Fd)
{
hal_i2c_open(_u32I2cIdx, _ps32Fd);
return 0;
}
/* I2C写单个寄存器 */
static s32 ap3216c_write_reg(s32 _s32Fd, u8 _u8Reg, u8 _u8Data)
{
HAL_DEV_I2C_INFO_S stData = {0};
stData.u16DevAddr = AP3216C_ADDR; //设备地址
stData.u16RegAddr = _u8Reg; //寄存器地址
stData.u16RegLen = AP3216C_REG_W; //寄存器地址长度
return hal_i2c_write(_s32Fd, stData.u16DevAddr, stData.u16RegAddr, stData.u16RegLen, &_u8Data, 1);
}
/* I2C读单个寄存器 */
static s32 ap3216c_read_reg(s32 _s32Fd, u8 _u8Reg, u8 *_pData)
{
HAL_DEV_I2C_INFO_S stData = {0};
u8 data = 0;
stData.u16DevAddr = AP3216C_ADDR; //设备地址
stData.u16RegAddr = _u8Reg; //寄存器地址
stData.u16RegLen = AP3216C_REG_W; //寄存器地址长度
return hal_i2c_read(_s32Fd, stData.u16DevAddr, stData.u16RegAddr, stData.u16RegLen, _pData, 1);
}
/* 关闭i2c */
static s32 ap3216c_i2c_close(s32 _s32Fd)
{
hal_i2c_close(_s32Fd);
return 0;
}
最终,ap3216c适配层会提供如下接口给应用层调用
/*
*********************************************************************************************************
* 函 数 名: ap3216c_init
* 功能说明: 初始化
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
int ap3216c_init(void)
{
ap3216c_i2c_open(I2C_INDEX, &g_stAp3216c.s32Fd);
/* 初始化AP3216C */
ap3216c_write_reg(g_stAp3216c.s32Fd, AP3216C_SYSTEMCONG, 0x04); /* 复位AP3216C */
usleep(100000); /* AP3216C复位最少10ms */
ap3216c_write_reg(g_stAp3216c.s32Fd, AP3216C_SYSTEMCONG, 0X03); /* 开启ALS、PS+IR */
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: ap3216c_deinit
* 功能说明: 初始化
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
int ap3216c_deinit(void)
{
if (g_stAp3216c.s32Fd != NULL) {
ap3216c_i2c_close(g_stAp3216c.s32Fd);
g_stAp3216c.s32Fd = NULL;
}
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: ap3216c_getdata
* 功能说明: 读数据
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
int ap3216c_getdata(HAL_AP3216C_DATA_T *_pstData)
{
u8 u8Buf[6] = {0};
int i;
unsigned short ir, als, ps;
if (g_stAp3216c.s32Fd == NULL) {
return -1;
}
for(i = 0; i < 6; i++) {
ap3216c_read_reg(g_stAp3216c.s32Fd, AP3216C_REG_IR_L + i, &u8Buf[i]); //读单个寄存器
}
if(u8Buf[0] & 0X80) /* IR_OF位为1,则数据无效 */
ir = 0;
else /* 读取IR传感器的数据 */
ir = ((unsigned short)u8Buf[1] << 2) | (u8Buf[0] & 0X03);
als = ((unsigned short)u8Buf[3] << 8) | u8Buf[2]; /* 读取ALS传感器的数据 */
if(u8Buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
ps = 0;
else /* 读取PS传感器的数据 */
ps = ((unsigned short)(u8Buf[5] & 0X3F) << 4) | (u8Buf[4] & 0X0F);
_pstData->usIr = ir;
_pstData->usAls = als;
_pstData->usPs = ps;
return 0;
}
main函数while循环中读取ap3216c的数据
int main(int argc, char* argv[])
{
HAL_AP3216C_DATA_T stAp3216c;
ap3216c_init(); //初始化
while(1){
ap3216c_getdata(&stAp3216c); //读数据
printf("ir = %d, als = %d, ps = %d\r\n", stAp3216c.usIr, stAp3216c.usAls, stAp3216c.usPs);
usleep(200000); /* 200ms */
}
return 0;
}
2、测试结果
/ # ./test_app
Ap3216c: ir = 0, als = 12, ps = 0
Ap3216c: ir = 3, als = 15, ps = 0
Ap3216c: ir = 4, als = 11, ps = 0
Ap3216c: ir = 3, als = 12, ps = 0