嵌入式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数据elsememcpy(&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;elsemessages.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