Linux驱动-I2C子系统基本分析

news2024/11/22 14:28:23

​第一:Linux中I2C驱动框架分析

I2C核心(i2c_core)

I2C核心维护了i2c_bus结构体,提供了I2C总线驱动设备驱动的注册、注销方法,维护了I2C总线的驱动、设备链表,实现了设备、驱动的匹配探测。此部分代码由Linux内核提供。

I2C总线驱动

I2C总线驱动维护了I2C适配器数据结构(i2c_adapter)和适配器的通信方法数据结构(i2c_algorithm)。所以I2C总线驱动可控制I2C适配器产生start、stop、ACK等。此部分代码由具体的芯片厂商提供,比如Samsung、高通。

I2C设备驱动

I2C设备驱动主要维护两个结构体:i2c_driver和i2c_client,实现和用户交互的文件操作集合fops、cdev等。此部分代码就是驱动开发者需要完成的。

​第二:Linux内核中描述I2C的四个核心结构体

1)i2c_client—挂在I2C总线上的I2C从设备

每一个i2c从设备都需要用一个i2c_client结构体来描述,i2c_client对应真实的i2c物理设备device。

struct i2c_client {     unsigned short flags;     //标志位 (读写)   unsigned short addr;      //7位的设备地址(低7位)   char name[I2C_NAME_SIZE]; //设备的名字,用来和i2c_driver匹配   struct i2c_adapter *adapter; //依附的适配器(adapter),适配器指明所属的总线(i2c0/1/2_bus)   struct device dev;           //继承的设备结构体   int irq;                     //设备申请的中断号  struct list_head detected;   //已经被发现的设备链表 };

但是i2c_client不是我们自己写程序去创建的,而是通过以下常用的方式自动创建的:

方法一: 分配、设置、注册i2c_board_info

方法二: 获取adapter调用i2c_new_device

方法三: 通过设备树(devicetree)创建

方法1和方法2通过platform创建,这两种方法在内核3.0版本以前使用所以在这不详细介绍;**方法3是最新的方法,**3.0版本之后的内核都是通过这种方式创建的,文章后面的案例就按方法3。

2)i2c_adapter

I2C总线适配器,即soc中的I2C总线控制器,硬件上每一对I2C总线都对应一个适配器来控制它。在Linux内核代码中,每一个adapter提供了一个描述它的结构(struct i2c_adapter),再通过i2c core层将i2c设备与i2c adapter关联起来。主要用来完成i2c总线控制器相关的数据通信,此结构体在芯片厂商提供的代码中维护。

struct i2c_adapter {    struct module *owner;    unsigned int class;               //允许匹配的设备的类型    const struct i2c_algorithm *algo; //指向适配器的驱动程序,实现发送数据的算法    struct device dev;                //指向适配器的设备结构体    char name[48];                    //适配器的名字};

3)i2c_algorithm
I2C总线数据通信算法,通过管理I2C总线控制器,实现对I2C总线上数据的发送和接收等操作。亦可以理解为I2C总线控制器(适配器adapter)对应的驱动程序,每一个适配器对应一个驱动程序,用来描述适配器和设备之间的通信方法,由芯片厂商去实现的。

struct i2c_algorithm {    //传输函数指针,指向实现IIC总线通信协议的函数    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);        };

4)i2c_driver
用于管理I2C的驱动程序和i2c设备(client)的匹配探测,实现与应用层交互的文件操作集合fops、cdev等。

struct i2c_driver {    int (*probe)(struct i2c_client *, const struct i2c_device_id *); //设备匹配成功调用的函数    int (*remove)(struct i2c_client *);                              //设备移除之后调用的函数    struct device_driver driver;                                     //设备驱动结构体    const struct i2c_device_id *id_table;   //设备的ID表,匹配用platform创建的client};

​第三:应用实例,实现mpu6050驱动,读取温度

在设备树中描述I2C设备信息

@i2c-0 {//表示这个i2c_client所依附的adapter是i2c-0    //对应i2c_client的name = "invensense,mpu6050"    compatible = "invensense,mpu6050";    //对应i2c_client的addr = 0x69  -- 从机设备的地址    reg = <0x69>;    //对应i2c_client的irq    interrupts = <70>;};

最终内核会将这个设备树的节点解析为一个i2c_client结构体与i2c_driver结构体进行匹配。

​第四:编写驱动代码

分配、设置、注册i2c_driver结构体

struct i2c_driver mpu6050_driver = { .  driver = {     .name = "mpu6050",      .owner = THIS_MODULE,      .of_match_table = of_match_ptr(mpu6050_of_match),    },    .probe = mpu6050_probe, .remove = mpu6050_remove, };static int mpu6050_init(void){    printk("%s called\n", __func__);    i2c_add_driver(&mpu6050_driver);    return 0;}

i2c总线驱动模型属于设备模型中的一类,同样struct i2c_driver结构体继承于struct driver,匹配方法和设备模型中讲的一样,这里要去匹配设备树,所以必须实现i2c_driver结构体中的driver成员中的of_match_table成员:

/* 用来匹配mpu6050的设备树 */static struct of_device_id mpu6050_of_match[] = {    {.compatible = "invensense,mpu6050"},    {},};

如果和设备树匹配成功,那么就好调用probe函数

/* 匹配函数,设备树中的mpu6050结点对应转换为一个client结构体 */ static int mpu6050_probe(struct i2c_client * client, const struct i2c_device_id * id) {   int ret;   printk("mpu6050 match ok!\n");     mpu6050_dev.client = client; /* 注册设备号 */   mpu6050_dev.devno = MKDEV(MAJOR, MINOR);   ret = register_chrdev_region(mpu6050_dev.devno, 1, "mpu6050");   if (ret < 0) goto err1;     cdev_init(&mpu6050_dev.cdev, &mpu6050_fops);   mpu6050_dev.cdev.owner = THIS_MODULE;   ret = cdev_add(&mpu6050_dev.cdev, mpu6050_dev.devno, 1);   if (ret < 0) goto err2;     return 0; err2:   unregister_chrdev_region(mpu6050_dev.devno, 1); err1:   return -1; }

实现文件操作集合

struct file_operations mpu6050_fops = {   .owner = THIS_MODULE,   .open = mpu6050_open,   .release = mpu6050_release,   .unlocked_ioctl = mpu6050_ioctl, };static int mpu6050_open(struct inode * inodep, struct file * filep) {   printk("%s called\n", __func__);   mpu6050_write_byte(mpu6050_dev.client, PWR_MGMT_1, 0x00);   mpu6050_write_byte(mpu6050_dev.client, SMPLRT_DIV, 0x07);   mpu6050_write_byte(mpu6050_dev.client, CONFIG, 0x06);   mpu6050_write_byte(mpu6050_dev.client, GYRO_CONFIG, 0xF8);   mpu6050_write_byte(mpu6050_dev.client, ACCEL_CONFIG, 0x19);   return 0; } static int mpu6050_release(struct inode * inodep, struct file * filep) {   printk("%s called\n", __func__);   return 0; } void get_temp(union mpu6050_data * data) {   data->temp = mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_L);   data->temp |= mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_H) << 8; } static long mpu6050_ioctl(struct file * filep, unsigned int cmd, unsigned long arg) {   union mpu6050_data data;   switch (cmd)   {     case GET_TEMP:       get_temp(&data);       break;     default:       break;   }   if (copy_to_user((unsigned int *)arg, &data, sizeof(data)))     return -1;   return 0; }

如何实现对i2c从设备的读写操作?

/* 读取mpu6050中一个字节的数据,将读取的数据的地址返回 */ static int mpu6050_read_byte(struct i2c_client * client, unsigned char reg_add) {   int ret; /* 要读取的那个寄存器的地址 */   char txbuf = reg_add; /* 用来接收读到的数据 */   char rxbuf[1]; /* i2c_msg指明要操作的从机地址,方向,缓冲区 */   struct i2c_msg msg[] = {     {client->addr, 0, 1, &txbuf}, //0表示写,向往从机写要操作的寄存器的地址     {client->addr, I2C_M_RD, 1, rxbuf}, //读数据   };   /* 通过i2c_transfer函数操作msg */   ret = i2c_transfer(client->adapter, msg, 2); //执行2条msg   if (ret < 0)   {     printk("i2c_transfer read err\n");     return -1;   }   return rxbuf[0]; } static int mpu6050_write_byte(struct i2c_client * client, unsigned char reg_addr, unsigned char data) {   int ret; /* 要写的那个寄存器的地址和要写的数据 */   char txbuf[] = {reg_addr, data}; /* 1个msg,写两次 */  struct i2c_msg msg[] = {     {client->addr, 0, 2, txbuf}   };   ret = i2c_transfer(client->adapter, msg, 1); if (ret < 0)   {     printk("i2c_transfer write err\n");     return -1;   }   return 0; }

在实现读写操作的时候,使用了一个重要的函数i2c_transfer(),这个函数是i2c核心提供给设备驱动的,通过它发送的数据需要被打包成i2c_msg结构,这个函数最终会回调相应i2c_adapter->i2c_algorithm->master_xfer()接口将i2c_msg对象发送到i2c物理控制器。

struct i2c_msg {    __u16 addr;     /* slave address  */    __u16 flags;    /* 1 - 读  0 - 写 */    __u16 len;      /* msg length     */    __u8 *buf;      /* 要发送的数据   */};

以上是我对Linux中I2C驱动框架的分析及实际案例分析,如有不足欢迎指出

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CNN 01(CNN简介)

一、卷积神经网络的发展 convolutional neural network 在计算机视觉领域&#xff0c;通常要做的就是指用机器程序替代人眼对目标图像进行识别等。那么神经网络也好还是卷积神经网络其实都是上个世纪就有的算法&#xff0c;只是近些年来电脑的计算能力已非当年的那种计算水平…

sql语句中的ddl和dml

操作数据库&#xff1a;CRUD C&#xff08;create&#xff09; 创建 *数据库创建出来默认字符集为utf8 如果要更改字符集就 Create database 名称 character set gbk&#xff08;字符集&#xff09; *创建数据库&#xff1a;create database 名称 *先检查是否有该数据库在…

Python第三方库纵览

Python第三方库纵览 知识点 更广泛的Python计算生态&#xff0c;只要求了解第三方库的名称&#xff0c;不限于以下领域: 网络爬虫、数据分析、文本处理、数据可视化、用户图形界面、机器学习、Web开发、游戏开发等 知识导图 1、网络爬虫方向 网络爬虫是自动进行HTTP访问并捕…

【JAVA】实现API 接口参数签名

使用sa-tokenSpringBoot拦截器实现API 接口参数签名 在涉及跨系统接口调用时&#xff0c;我们容易碰到以下安全问题&#xff1a; 1.请求身份被伪造。 2.请求参数被篡改。 3.请求被抓包&#xff0c;然后重放攻击。 1.引入 sa-token sa-token官方文档:https://sa-token.cc/doc.ht…

HCIP-HCS华为私有云

1、概述 HCS&#xff08;HuaweiCoudStack&#xff09;华为私有云&#xff1a;6.3 之前叫FusionSphere OpenStack&#xff0c;6.3.1 版本开始叫FusionCloud&#xff0c;6.5.1 版本开始叫HuaweiCloud Stack (HCS)华为私有云软件。 开源openstack&#xff0c;发放云主机的流程&am…

第五章 树与二叉树 一、树的定义与考点

一、定义 1.树是由n (n > 0) 个节点组成的有限集合。 2.当n0时&#xff0c;称为空树。 3.在非空树中&#xff0c;有且仅有一个节点没有前驱&#xff0c;其他节点都有且仅有一个前驱&#xff0c;称为根节点。 4.每个节点有零个或多个子节点&#xff0c;而每个子节点又有零…

多态/虚函数/虚函数表

OVERVIEW 多态/虚函数/虚函数表1.虚函数引入后类发生的变化&#xff1f;2.虚函数表的生成时机和生成原因&#xff1f;3.虚函数表指针赋值的时机&#xff1f;4.类对象在内存中的布局&#xff1f;5.虚函数的工作原理和多态性的体现&#xff1f;6.其他问题 多态/虚函数/虚函数表 n…

Android JNI系列详解之生成指定CPU的库文件

一、前提 这次主要了解Android的cpu架构类型&#xff0c;以及在使用CMake工具的时候&#xff0c;如何指定生成哪种类型的库文件。 如上图所示&#xff0c;是我们之前使用CMake工具默认生成的四种cpu架构的动态库文件&#xff1a;arm64-v8a、armeabi-v7a、x86、x86_64&#xff0…

昇腾Ascend+C编程入门教程(纯干货)

2023年5月6日&#xff0c;在昇腾AI开发者峰会上&#xff0c;华为正式发布了面向算子开发场景的昇腾Ascend C编程语言。Ascend C原生支持C/C编程规范&#xff0c;通过多层接口抽象、并行编程范式、孪生调试等技术&#xff0c;极大提高了算子的开发效率&#xff0c;帮助AI开发者低…

go学习之流程控制语句

文章目录 流程控制语句1.顺序控制2.分支控制2.1单分支2.2双分支单分支和双分支的四个题目switch分支结构 3.循环控制for循环控制while 和do...while的实现 4.跳转控制语句breakcontinuegotoreturngotoreturn 流程控制语句 介绍&#xff1a;在程序中&#xff0c;程序运行的流程…

星际争霸之小霸王之小蜜蜂(七)--消失的子弹

目录 前言 一、删除子弹 二、限制子弹数量 三、继续重构代码 总结 前言 昨天我们已经让子弹飞了起来&#xff0c;但是会面临一个和之前小蜜蜂一样的问题&#xff0c;小蜜蜂的行动应该限制在窗口内&#xff0c;那么子弹也是有相同之处&#xff0c;也需要限制一个移动范围&…

智慧监狱整体解决方案PPT

导读&#xff1a;原文《智慧监狱整体解决方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 喜欢文章&#xff0c;您可以点赞评论转发本文&#xff0c;了解更多…

全球互联网裁员下测试人员何去何从?

时间好像突然加快了步伐瞬间觉得匆匆&#xff0c;转眼已经23年&#xff0c;从20年到23年。回想起来恍恍惚惚&#xff0c;疫情中经历的种种就好像没有发生过一样&#xff0c;很多的魑魅魍魉荒唐可笑真实又虚幻&#xff0c;时光向前人生向后&#xff0c;那些魔幻的人和事也慢慢消…

可解释性的相关介绍

一、可解释性的元定义&#xff08;Meta-definitions of Interpretability&#xff09; The extent to which an individual can comprehend the cause of a model’s outcome. [1]The degree to which a human can consistently predict a model’s outcome. [2] 可解释性&am…

Flutter 项目结构文件

1、Flutter项目的文件结构 先helloworld项目&#xff0c;看看它都包含哪些组成部分。首先&#xff0c;来看一下项目的文件结构&#xff0c;如下图所示。 2、介绍上图的内容。 -litb/main.dart文件&#xff1a;整个应用的入口文件&#xff0c;其中的main函数是整个Flutter应…

Fei-Fei Li-Lecture 16:3D Vision 【斯坦福大学李飞飞CV课程第16讲:3D Vision】

目录 P1 2D Detection and Segmentation​编辑 P2 Video 2D time series P3 Focus on Two Problems P4 Many more topics in 3D Vision P5-10 Multi-View CNN P11 Experiments – Classification & Retrieval P12 3D Shape Representations P13--17 3D Shape Rep…

【集合学习ConcurrentHashMap】ConcurrentHashMap集合学习

ConcurrentHashMap集合学习 一、JDK1.7 和 1.8 版本ConcurrenHashMap对比分析 JDK 1.7版本 在JDK 1.7版本ConcurrentHashMap使用了分段锁的方式&#xff08;对Segment进行加锁&#xff09;&#xff0c;其实际结构为&#xff1a;Segment数组 HashEntry数组 链表。由很多个 …

蓝蓝设计ui设计公司作品案例-中节能现金流抗压测试软件交互及界面设计

中国节能是以节能环保为主业的中央企业。中国节能以生态文明建设为己任&#xff0c;长期致力于让天更蓝、山更绿、水更清&#xff0c;让生活更美好。经过多年发展&#xff0c;中国节能已构建起以节能、环保、清洁能源、健康和节能环保综合服务为主业的41产业格局&#xff0c;成…

计算机组成原理 | 第一章 计算机系统概述

目录 计算机发展历程 计算机系统层次结构 计算机的性能指标 计算机发展历程 电子计算机的发展已经历了4代&#xff0c;这4代计算机的主要元件分别是电子管、晶体管、中小规模集成电路、大规模集成电路。微型计算机的发展以微处理器技术为标志。可以在计算机中直接执行的语…