Linux应用编程 - i2c-dev操作I2C

        嵌入式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

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Linux应用层,我们可以使用相应的库函数来进行读写i2c设备的操作。下面是一个简单的示例代码: 首先,我们需要打开i2c设备文件,可以使用open函数来实现: ```c int fd = open("/dev/i2c-0", O_RDWR); if (fd < 0) { perror("Failed to open i2c device"); return -1; } ``` 接下来,我们需要设置要访问的i2c设备的地址,可以使用ioctl函数来实现: ```c int addr = 0x50; // 替换为你要访问的i2c设备地址 if (ioctl(fd, I2C_SLAVE, addr) < 0) { perror("Failed to set i2c device address"); return -1; } ``` 然后,我们可以使用read函数来读取i2c设备的数据: ```c unsigned char buffer[10]; int length = 5; // 要读取的字节数 if (read(fd, buffer, length) != length) { perror("Failed to read from i2c device"); return -1; } ``` 类似地,我们可以使用write函数来向i2c设备写入数据: ```c unsigned char data[10] = {0x00, 0x01, 0x02, 0x03, 0x04}; // 要写入的数据 int length = 5; // 要写入的字节数 if (write(fd, data, length) != length) { perror("Failed to write to i2c device"); return -1; } ``` 最后,我们需要关闭i2c设备文件,可以使用close函数来实现: ```c close(fd); ``` 通过以上的代码,我们可以在Linux应用层进行i2c设备的读写操作。请注意,示例中使用的设备文件路径和i2c设备地址可能需要根据实际情况进行修改。 ### 回答2: 在Linux中,要在应用层读写I2C设备,可以使用Linux提供的I2C工具以及编程接口。 1. 使用I2C工具:Linux提供了一些命令行工具来读写I2C设备,最常用的是`i2c-tools`包中的`i2cget`和`i2cset`命令。可以通过安装`i2c-tools`来获得这些工具。使用时,需要知道目标I2C设备的地址以及要读写的寄存器地址,然后可以使用`i2cget`命令读取该寄存器的值,或使用`i2cset`命令向该寄存器写入数据。 2. 使用编程接口:在应用程序中使用编程接口可以更灵活地读写I2C设备。在Linux中,可以使用标准的Linux编程接口,如`ioctl`和`open`等函数来操作I2C设备。首先需要使用`open`函数打开I2C设备文件,然后使用`ioctl`函数设置I2C设备的地址、通信速率等参数。接下来可以使用`read`和`write`函数来读取和写入I2C设备的数据。 这些方法都能实现在Linux应用层中对I2C设备进行读写操作。使用哪种方法取决于具体的需求和场景。如果只是简单的读写操作,可以选择使用I2C工具;如果需要更复杂的控制和处理逻辑,可以选择使用编程接口来实现。 ### 回答3: 在Linux应用层读写I2C,我们可以使用内核提供的i2c-dev驱动来实现。以下是一种基本的方法: 1. 打开I2C设备 首先,我们需要在应用程序中打开I2C设备文件。设备文件的路径通常为"/dev/i2c-N",其中N为I2C控制器的编号。通过调用open()函数打开设备文件,可以获得一个文件描述符(file descriptor)。 2. 设置I2C设备的从属地址 在进行I2C通信之前,我们需要设置I2C设备的从属地址(slave address)。通过ioctl()函数调用I2C_SLAVE命令,将从属地址传递给i2c-dev驱动。 3. 发送和接收数据 在已设置好从属地址的前提下,我们可以通过write()函数向I2C设备发送数据。数据应该是一个字节数组,可以包含多个字节。通过调用read()函数,可以从I2C设备中接收数据,同样以字节数组的形式返回。 4. 关闭I2C设备 在完成I2C通信后,我们应该关闭I2C设备文件,释放资源。通过调用close()函数,可以关闭文件描述符。 需要注意的是,读写I2C设备需要具有对应的权限。通常情况下,我们需要以超级用户或者具有I2C访问权限的用户身份运行应用程序。 以上是一个在Linux应用层读写I2C的简单示例。实际应用中,可能需要更复杂的数据处理和通信协议实现,但基本的读写操作是类似的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值