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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/712718.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何避免销售飞单私单!教你如何巧妙避开陷阱,业绩飙升!

明明投入了大量的时间和精力&#xff0c;客户却悄无声息地消失了&#xff1f;或是突然有一天&#xff0c;你发现原本属于你的订单被同事悄悄抢走&#xff1f;这背后&#xff0c;很可能隐藏着销售飞单私单的陷阱。今天&#xff0c;就让我们一起探讨如何巧妙避开这些陷阱&#xf…

TCP与UDP案例

udp不会做拆分整合什么的 多大就是多大

MyBatis使用Demo

文章目录 01、Mybatis 意义02、Mybatis 快速入门04、Mapper 代理开发05、Mybatis 配置文件07、查询所有&结果映射08、查询-查看详情09、查询-条件查询10、查询-动态条件查询多条件动态查询单条件动态查询 11、添加&修改功能添加功能修改功能 12、删除功能删除一个批量删…

【html】学会这一套布局,让你的网页更加

很多小伙伴们在刚刚开始学习网页设计的时候不知道怎么布局今天给大家介绍一种非常实用且更加专业的一种布局。 灵感来源&#xff1a; 小米官网 布局图; 实例效果图&#xff1a; 这是一个简单的HTML模板&#xff0c;包括头部、内容区域和底部。 头部部分包括一个分为左右两部分…

【记录】ChatGLM3-6B大模型部署、微调(二):微调

前言 上文记录了ChatGLM3-6B大模型本地化部署过程&#xff0c;本次对模型进行微调&#xff0c;目的是修改模型自我认知。采用官方推荐微调框架&#xff1a;LLaMA-Factory 安装LLaMA-Factory # 克隆项目 git clone https://github.com/hiyouga/LLaMA-Factory.git 安装依赖 # 安装…

Linux---系统的初步学习【项目一:Linux操作系统的安装与配置】

项目一 Linux操作系统的安装与配置 1.1 项目知识准备 1.1.1 操作系统是什么&#xff1f; ​ 操作系统&#xff08;Operating System&#xff0c;OS&#xff09;是管理计算机硬件与软件资源的计算机程序。操作系统需要处理如管理硬件、决定程序运行的优先次序、管理文件系统等…

逻辑斯蒂回归与最大熵

知识树 感知机的缺陷 修补感知机缺陷-逻辑斯蒂回归 下面这两个值是强制给的,不是推导的 最大熵 最大熵的一个小故事 最大熵模型 我们最终目标是要求P(Y|X) 书上写的是H,但是2我们认为H(Y|X)更合适 咱们最终的目的是要用拉格朗日乘数法,所以需要约束 总结 感觉深度之眼比较模…

汽车级TPSI2140QDWQRQ1隔离式固态继电器,TMUX6136PWR、TMUX1109PWR、TMUX1133PWR模拟开关与多路复用器(参数)

1、TPSI2140-Q1 是一款隔离式固态继电器&#xff0c;专为高电压汽车和工业应用而设计。 TPSI2140-Q1 与 TI 具有高可靠性的电容隔离技术和内部背对背 MOSFET 整合在一起&#xff0c;形成了一款完全集成式解决方案&#xff0c;无需次级侧电源。 该器件的初级侧仅由 9mA 的输入电…

Studio One 6.6.2 for Mac怎么激活,有Studio One 6激活码吗?

如果您是一名音乐制作人&#xff0c;您是否曾经为了寻找一个合适的音频工作站而苦恼过&#xff1f;Studio One 6 for Mac是一款非常适合您的MacBook的音频工作站。它可以帮助您轻松地录制、编辑、混音和发布您的音乐作品。 Studio One 6.6.2 for Mac具有直观的界面和强大的功能…

Zookeeper: 配置参数解读

Zookeeper中的配置文件zoo.cfg中参数含义解读如下&#xff1a; tickTime&#xff1a;通信心跳时间&#xff0c;Zookeeper服务器与客户端心跳时间&#xff0c;单位毫秒。 initLimit: LF初始通信时限 Leader和Follower初始连接时能容忍的最多心跳数。 syncLimit: LF同步通信时…

算法01 递推算法及相关问题详解【C++实现】

目录 递推的概念 训练&#xff1a;斐波那契数列 解析 参考代码 训练&#xff1a;上台阶 参考代码 训练&#xff1a;信封 解析 参考代码 递推的概念 递推是一种处理问题的重要方法。 递推通过对问题的分析&#xff0c;找到问题相邻项之间的关系&#xff08;递推式&a…

mongodb 集群安装

1. 配置域名 Server1&#xff1a; OS version: CentOS Linux release 8.5.2111 hostnamectl --static set-hostname mongo01 vi /etc/sysconfig/network # Created by anaconda hostnamemong01 echo "192.168.88.20 mong1 mongo01.com mongo02.com" >> /…

用GAN网络生成彩票号码

1. 前言 生成对抗网络&#xff08;GAN&#xff0c;Generative Adversarial Network&#xff09;是由Ian Goodfellow等人在2014年提出的一种深度学习模型&#xff0c;用于学习和生成与真实数据分布相似的数据。GAN由生成器&#xff08;Generator&#xff09;和判别器&#xff08…

Python编程环境搭建

简介&#xff1a; Python环境安装比较简单&#xff0c;无需安装其它依赖环境&#xff0c;主要步骤为&#xff1a; 1. 下载并安装Python对应版本解释器 2. 下载并安装一个ide编码工具 一、下载并安装Python解释器 1.1 下载 官网地址&#xff1a;Welcome to Python.org 选择…

STM32-CAN

一、CAN总线简介 1.1 CAN简介 CAN 是 Controller Area Network 的缩写&#xff08;以下称为 CAN&#xff09;&#xff0c;是 ISO 国际标准化的串行通信 协议。异步半双工。 ISO11898&#xff1a;123kbps~1Mbps。 ISO11519&#xff1a;125kbps 特点&#xff1a; 多主控制没…

Dify源码本地部署启动

背景 Dify是一个开源LLM应用程序开发平台。Dify的直观界面结合了人工智能工作流、RAG管道、代理功能、模型管理、可观察性功能等&#xff0c;让您快速从原型到生产。 Dify提供在线试用功能&#xff0c;可以直接在线体验其功能。同时也支持docker部署&#xff0c;源码部署等方…

【Vue】Pinia管理用户数据

Pinia管理用户数据 基本思想&#xff1a;Pinia负责用户数据相关的state和action&#xff0c;组件中只负责触发action函数并传递参数 步骤1&#xff1a;创建userStore 1-创建store/userStore.js import { loginAPI } from /apis/user export const useUserStore defineStore(…

ARP协议相关

把ip地址解析成mac地址这里的mac地址就是路由器的mac地址 免费ARP 源ip和目的ip都是一样的&#xff0c;那怎么让其他人更新arp表呢&#xff1f;&#xff1f; 是因为目标mac是全f&#xff0c;是一个广播报文 如果冲突就是ip一样但是mac又不一样 代理ARP pc1和pc4是在同一个子网…

算法训练营第六十天(延长12天添加图论) | LeetCode 647 回文子串、LeetCode 516 最长回文子序列

LeetCode 67 回文子串 思路很简单&#xff0c;每一个dp[i]等于dp[i-1]加上当前字符向前直到0各个长度字符串回文串个数即可 代码如下&#xff1a; class Solution {public boolean isValid(String s) {int l 0, r s.length() - 1;while (l < r) {if (s.charAt(l) ! s.ch…

谷歌企业开发者账号注册的常见问题及解决方法

今天跟大家分享一下注册谷歌开发者企业号的一些注意事项和干货&#xff0c;少走一些弯路。 首先&#xff0c;各位开发者朋友应该都知道&#xff0c;谷歌平台上有两种类型开发者账号&#xff1a;个人开发者账号和企业开发者账号。个人账号上架周期长&#xff0c;需要14天的封测&…