IIC子系统

news2025/3/3 18:08:43

文章目录

  • 引言
  • 一、I2C 总线驱动框架
  • 二、I2C驱动框图(重点)
  • 三、I2C 子系统软件框架
    • 3.1 I2C子系统的4个关键结构体
    • 3.2 I2C总线与平台总线的结合
    • 3.3 在设备树信息添加i2c从设备
    • 3.4 新增加i2c从设备
  • 四、i2c driver驱动的编写
    • 4.1 陀螺仪和加速度工作原理
    • 4.2 mpu6050的寄存器信息和设置
    • 4.3 ioctl: 给驱动发送不同指令
    • 4.4 代码实现
      • 4.4.1 mpu6050_drv.c
      • 4.4.2 mpu6050.h
      • 4.4.3 mpu6050_test.c
      • 4.4.4 Makefile
  • 参考资料

整理是为了对知识进行复习回顾,融会贯通。本文来源于华清嵌入式知识课堂,如有侵权请联系本人删除。
个人觉得IIC子系统这部分,讲的很透彻。

引言

嵌入式项目中,外设经常通过IIC总线进行设备连接。基于硬件的IIC基本通信原理学习地址。比如,gsenor: 重力传感器,eeprom: 存储等设备,但是在linux中,基于平台总线抽象出了IIC总线模型,逻辑实现上变的复杂,但是在使用上就变的简单了,我们需要理解linux中的IIC总线框架。

一、I2C 总线驱动框架

I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器

I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
I2C驱动:对应的就是I2C设备的驱动程序
I2C设备:是具体硬件设备的一个抽象
I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象

i2c总线维护着两个链表,一个驱动链表,一个设备链表,每当注册进一个驱动(或设备),就会将其添加到总线上相应的链表上,然后遍历总线的设备(或驱动)链表的所有设备(或驱动),通过总线的匹配函数判断是够匹配,如果匹配,则调用驱动的probe函数,然后我们就可以在probe函数注册字符设备,创建设备节点,实现fops集等等

在I2C-core.c这个函数中,掌握下面的几个关键函数就可以了。

1. //增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adapter)

int i2c_del_adapter(struct i2c_adapter *adap)

2. //增加/删除i2c_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

void i2c_del_driver(struct i2c_driver *driver)

3. //i2c_client依附/脱离

int i2c_attach_client(struct i2c_client *client)

int i2c_detach_client(struct i2c_client *client)

4. //I2C传输,发送和接收

int i2c_master_send(struct i2c_client *client,const char *buf ,int count)

int i2c_master_recv(struct i2c_client *client, char *buf ,int count)

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

I2c_transfer这个函数实现了core与adapter的联系。

二、I2C驱动框图(重点)

在这里插入图片描述

三、I2C 子系统软件框架

  应用
   ------------------------------------------
   i2c driver:从设备驱动层
        需要和应用层进行交互
        封包数据,不知道数据是如何写入到硬件

   ------------------------------------------
   i2c 核心层:维护i2c 总线,包括i2c driver, i2c client链表
   drivers/i2c/i2c-core.c
   ---------------------------------------------------
   i2c adapter层:i2c控制层,初始化i2c控制器
           完成将数据写入或读取-从设备硬件
           不知道数据具体是什么,但是知道如何操作从设备
   drivers/i2c/busses/i2c-s3c2410.c

3.1 I2C子系统的4个关键结构体

(1)struct i2c_adapter
描述主机所对应的iic控制器,每个控制器对应一个i2c_adapter结构体(主机驱动)
(2)struct i2c_algorithm
描述主机IIC跟从机iic设备通信的算法,算法由iic控制器执行,i2c_algorithm包含在i2c_adapter结构体中
(3)struct i2c_client
其实就是i2c_device,跟i2c_driver配对,是iic从机的设备信息
(4)struct i2c_driver
iic的设备驱动(从机设备)

在内核编译时,确保i2c core和i2c adatper层必须编译进内核:

    make menuconfig
        Device Drivers  --->
            -*- I2C support  ---> //编译i2c-core.c
                I2C Hardware Bus support  --->
                    <*> S3C2410 I2C Driver // i2c-s3c2410.c

编译后如何确定是否有I2C总线以及i2c adapter控制器, 在如下/sys/bus/i2c目录下查看:
在这里插入图片描述
i2c-0 即 i2c控制器。

3.2 I2C总线与平台总线的结合

两者各司其职,平台总线用于平台升级,由s3c2410-i2c.c就可知, I2C client 和 平台设备pdev都在设备树中描述。
在这里插入图片描述

3.3 在设备树信息添加i2c从设备

i2c信息在设备树的哪个位置
在这里插入图片描述
对应在文件目录下有:/sys/bus/platform/devices
在这里插入图片描述
可以看到在内核中默认就有i2c0,那13860000是什么意思,到数据手册看一下。
在这里插入图片描述
可知,实际上Exynos4412支持9组i2c总线,即有SCL0, SDA0 、SCL1,SDA1… SCL8,SDA8九组i2c总线。

下面以MPU6050为例看一下,i2c从设备与SOC的硬件相关信息
在这里插入图片描述
在这里插入图片描述
MPU6050对应第6组i2c总线,i2c的控制器基地址为0x138B_0000

MPU6050: 从设备地址是0x68
SOC MPU6050

GPB_3 — I2C_SCL5
GPB_2 — I2C_SDA5
GPX3_3 — GYRO_INT

由于内核中没有i2c5,所以需要人工添加进去
exynos4412-fs4412.dts设备树文件中只包含了一部分i2c信息,在文件开头,dts包含了exynos4412.dtsi文件
逐个打开文件搜索i2c,查看文件包含信息
exynos4412-fs4412.dts -->exynos4412.dtsi–>exynos4x12.dtsi–>exynos4,dtsi
最终在exynos4.dtsi下找到大量i2c信息,其中i2c_0, i2c_5信息如下;

描述控制器对应的设备树:arch/arm/boot/dts/exynos4.dtsi // 模板

        i2c_0: i2c@13860000 {
                 #address-cells = <1>;
                 #size-cells = <0>;
                 compatible = "samsung,s3c2440-i2c";
                 reg = <0x13860000 0x100>;
                 interrupts = <0 58 0>;
                 clocks = <&clock 317>;
                 clock-names = "i2c";
                  pinctrl-names = "default";
                 pinctrl-0 = <&i2c0_bus>;
                 status = "disabled";  //状态位disable,
         };   //在内核中不会自动执行adapter控制器的代码
 
           i2c_5: i2c@138B0000 {
                 #address-cells = <1>;
                 #size-cells = <0>;
                 compatible = "samsung,s3c2440-i2c";
                 reg = <0x138B0000 0x100>;
                 interrupts = <0 63 0>;
                 clocks = <&clock 322>;
                 clock-names = "i2c";
                 status = "disabled";
         }

回到exynos4412-fs4412.dts
描述从设备信息的设备树的模板 :exynos4412-fs4412.dts

i2c@13860000 {
        #address-cells = <1>;
        #size-cells = <0>;
        samsung,i2c-sda-delay = <100>;
        samsung,i2c-max-bus-freq = <20000>;
        pinctrl-0 = <&i2c0_bus>;
        pinctrl-names = "default";
        status = "okay";  //状态okay
        
        //子节点:从设备信息--电源管理芯片
        s5m8767_pmic@66 {
                compatible = "samsung,s5m8767-pmic";
                reg = <0x66>;  
               //对应/sys/bus/i2c/devices/i2c_0  -->0_0066
        }
            
} 

在内核目录/sys/bus/i2c/devices/i2c_0目录下
在这里插入图片描述

3.4 新增加i2c从设备

在 arch/arm/boot/dts/exynos4412-fs4412.dts 增加i2c5控制器和它包含了从设备

  1. 确定i2c从设备的从设备号
    看到MPU6050数据手册
    mpu6050的AD0接地,低电平,故7位的从设备地址为b 110 1000 --> 0x68
    在这里插入图片描述
  2. 确定slave名字 – 厂商+型号 – invensense ,mpu6050
 i2c@138B0000 {/*i2c adapter5信息*/
         #address-cells = <1>;
         #size-cells = <0>;
         samsung,i2c-sda-delay = <100>;
         samsung,i2c-max-bus-freq = <20000>;
         pinctrl-0 = <&i2c5_bus>;
         pinctrl-names = "default";
         status = "okay";
 
         mpu6050@68 { /*i2c client信息 查数据手册得从设备号为68*/
                 compatible = "invensense,mpu6050";
                 reg = <0x68>;
         };
 };

保存后,make dtbs ,更新设备树文件。
打开内核目录下平台设备的设备文件
在这里插入图片描述
进入adapter 138b0000.i2c
再查看i2c总线下是否有从设备
在这里插入图片描述
设置好设备树中的硬件信息,就可以开始写驱动了。

四、i2c driver驱动的编写

a. 添加i2c client的信息,必须包含在控制器对应的节点中
  b. 直接编写i2c driver
    1. 构建i2c driver,并注册到i2c总线
    2. 实现probe:
      |
      申请设备号,实现fops
      创建设备文件
      通过i2c的接口去初始化i2c从设备

几个常用的对象:

struct i2c_driver {//表示是一个从设备的驱动对象
		int (*probe)(struct i2c_client *, const struct i2c_device_id *);
		int (*remove)(struct i2c_client *);
		struct device_driver driver; //继承了父类
					|
					const struct of_device_id	*of_match_table;
		const struct i2c_device_id *id_table;//用于做比对,非设备树的情况
	}
	注册和注销
		int i2c_add_driver( struct i2c_driver *driver);
		void i2c_del_driver(struct i2c_driver *driver);
	struct i2c_client {//描述一个从设备的信息,不需要在代码中创建,因为是由i2c adapter帮我们创建
		unsigned short addr;		//从设备地址,来自于设备树中<reg>
		char name[I2C_NAME_SIZE]; //用于和i2c driver进行匹配,来自于设备树中compatible
		struct i2c_adapter *adapter;//指向当前从设备所存在的i2c adapter
		struct device dev;		// 继承了父类
	};
	创建i2c client的函数
	struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct             
                                             i2c_board_info const *info)
struct i2c_adapter {//描述一个i2c控制器,也不是我们要构建,原厂的代码会帮我们构建
		const struct i2c_algorithm *algo; //算法
					|
					int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
		
		struct device dev; //继承了父类,也会被加入到i2c bus
		int nr; //编号
 
	}
	注册和注销:
	int i2c_add_adapter(struct i2c_adapter * adapter);
	void i2c_del_adapter(struct i2c_adapter * adap);
struct i2c_msg {//描述一个从设备要发送的数据的数据包
		__u16 addr;	 //从设备地址,发送给那个从设备
		__u16 flags; //读1还是写0
		__u16 len;		//发送数据的长度
		__u8 *buf;		//指向数据的指针
	};
	//写从设备
int i2c_master_send(const struct i2c_client * client,const char * buf,int count)
	//读从设备
int i2c_master_recv(const struct i2c_client * client,char * buf,int count)
	以上两个函数都调用了:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

4.1 陀螺仪和加速度工作原理

陀螺仪的作用:
    原理:
        小时候玩过陀螺,如果给它一定的旋转速度,陀螺会竖立旋转起来而不会倒,
        主要因为高速旋转有抗拒方向改变的趋向
        
        陀螺仪就是内部的转子高速旋转,形成一个固定的初始化的参考平面,
        这样就可以通过测量初始的参考平面偏差计算出物体的旋转情况
        陀螺仪的强项在于测量设备自身的旋转运动

    陀螺仪的产生:
        1850年法国的物理学家福柯(J.Foucault)为了研究地球自转,首先发现高速转动中的转子(rotor),
        由于惯性作用它的旋转轴永远指向一固定方向,他用希腊字gyro(旋转)和skopein(看)
        两字合为gyro scopei一字来命名这种仪表

    陀螺仪的基本部件:
        (1) 陀螺转子,转子装在一支架内
        (2)内、外环,它是使陀螺自转轴获得所需角转动自由度的结构
            内环可环绕平面两轴作自由运动
            在内环架外加上一外环架,可以环绕平面做三轴作自由运动
        (3) 附件(是指力矩马达、信号传感器等)。

    陀螺仪的数据获取:        
        XYZ分别代表设备围绕XYZ三个轴旋转的角速度,陀螺仪可以捕捉很微小的运动轨迹变化,
        因此可以做高分辨率和快速反应的旋转检测,但不能测量当前的运行方向

    应用:
        1,陀螺仪用于飞行体运动的自动控制系统中,作为水平、垂直、俯仰、航向和角速度传感器
        2,手机上的摄像头配合使用,比如防抖
        3,各类手机游戏的传感器,包括一些第一视角类射击游戏,陀螺仪完整监测游戏者手的位移
           手机中的陀螺仪最早被iphone4应用,所以被大家所熟知
        4,导航,手机配合GPS,导航能力已经可以达到专用的gps导航仪

重力加速度:
    原理:
        重力施加在物体上,使它产生一个加速度,重力大小和此物体的质量成正比
        物体在不同的运行中,会产生不同的重力,从而可以测量出物体的运动情况

    重力加速度的数据获取:
        加速度测量传感器有x、y、z三轴,注意在手机上屏幕的坐标,以左上角作为原点的,而且Y向下。
        注意区分这两个不同的坐标系。
        加速传感器的单位是加速度m/s2。如果手机平放好,x,y在位置为0,
        而z轴方向加速度=当前z方向加速度-g。由于g(重力加速度)垂直向下,
        则g=-9.81m/s2,即z轴 a=0-(-9.81)=9.81m/s2
    应用:
        1,图像自动翻转
        2,游戏控制
        3,计步器功能

4.2 mpu6050的寄存器信息和设置

寄存器:
		#define SMPLRT_DIV		0x19 //采样频率寄存器-25 典型值:0x07(125Hz)
									 //寄存器集合里的数据根据采样频率更新
		#define CONFIG			0x1A //配置寄存器-26-典型值:0x06(5Hz)
									 //DLPF is disabled(DLPF_CFG=0 or 7)
		#define GYRO_CONFIG		0x1B //陀螺仪配置-27,可以配置自检和满量程范围
									 //典型值:0x18(不自检,2000deg/s)
		#define ACCEL_CONFIG	0x1C//加速度配置-28 可以配置自检和满量程范围及高通滤波频率
									 //典型值:0x01(不自检,2G,5Hz)
		#define ACCEL_XOUT_H	0x3B //59-65,加速度计测量值 XOUT_H
		#define ACCEL_XOUT_L	0x3C // XOUT_L
		#define ACCEL_YOUT_H	0x3D //YOUT_H
		#define ACCEL_YOUT_L	0x3E //YOUT_L
		#define ACCEL_ZOUT_H	0x3F //ZOUT_H
		#define ACCEL_ZOUT_L	0x40 //ZOUT_L---64
		#define TEMP_OUT_H		0x41 //温度测量值--65
		#define TEMP_OUT_L		0x42
		#define GYRO_XOUT_H	0x43//陀螺仪值--67,采样频率(由寄存器 25 定义)写入到这些寄存器
		#define GYRO_XOUT_L		0x44
		#define GYRO_YOUT_H		0x45
		#define GYRO_YOUT_L		0x46
		#define GYRO_ZOUT_H		0x47
		#define GYRO_ZOUT_L		0x48 //陀螺仪值--72
		#define PWR_MGMT_1		0x6B //电源管理 典型值:0x00(正常启用)

4.3 ioctl: 给驱动发送不同指令

ioctl: 给驱动发送不同指令
	 应用程序:
		ioctl(fd, cmd, args);
	========================================
	驱动中:xxx_ioctl()
	{
			switch(cmd){
			
			}
	}
 
	如何定义命令:
		1, 直接定义一个数字
			#define IOC_GET_ACCEL  0x9999
		2, 通过系统的接口
			_IO(x,y)
			_IOR(x,y,z)
			_IOW(x,y,z)
 
			参数1:表示magic,字符
			参数2:区分不同命令,整数 
			参数3:传给驱动数据类型

4.4 代码实现

4.4.1 mpu6050_drv.c

#include <linux/init.h>                                                                                                                                                                                              
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <asm/io.h>
#include <asm/uaccess.h>
 
#include "mpu6050.h"
 
#define SMPLRT_DIV  0x19    //采样频率寄存器-25 典型值:0x07(125Hz)
            //寄存器集合里的数据根据采样频率更新
#define CONFIG  0x1A    //配置寄存器-26-典型值:0x06(5Hz)
            //DLPF is disabled(DLPF_CFG=0 or 7)
#define GYRO_CONFIG 0x1B    //陀螺仪配置-27,可以配置自检和满量程范围
            //典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG    0x1C    //加速度配置-28 可以配置自检和满量程范围及高通滤波频率
                                        //典型值:0x01(不自检,2G,5Hz)
#define ACCEL_XOUT_H    0x3B    //59-65,加速度计测量值 XOUT_H
#define ACCEL_XOUT_L    0x3C    // XOUT_L
#define ACCEL_YOUT_H    0x3D    //YOUT_H
#define ACCEL_YOUT_L    0x3E    //YOUT_L
#define ACCEL_ZOUT_H    0x3F    //ZOUT_H
#define ACCEL_ZOUT_L    0x40    //ZOUT_L---64
#define TEMP_OUT_H  0x41    //温度测量值--65
#define TEMP_OUT_L  0x42
#define GYRO_XOUT_H 0x43    //陀螺仪值--67,采样频率(由寄存器 25 定义)写入到这些寄存器
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48    //陀螺仪值--72
#define PWR_MGMT_1  0x6B    //电源管理 典型值:0x00(正常启用)
 
//设计一个全局的设备对象
struct mpu_sensor{
  int dev_major;
  struct device *dev;                                                                                                                                                                                                
  struct class *cls;
  struct i2c_client *client; //记录probe中的client 
};
struct mpu_sensor *mpu_dev;
 
 
int mpu6050_write_bytes(struct i2c_client * client,char * buf,int count)
{
 
    int ret;
    struct i2c_adapter *adapter = client->adapter;
    struct i2c_msg msg;
 
    msg.addr = client->addr;
    msg.flags = 0;
    msg.buf = buf;                      
    msg.len = count;
 
    ret = i2c_transfer(adapter,&msg,1);
    return ret == 1?count:ret;
}
 
 
int mpu6050_read_bytes(struct i2c_client * client,char * buf,int count)
{
 
    int ret;
    struct i2c_adapter *adapter = client->adapter;
    struct i2c_msg msg;
 
    msg.addr = client->addr;
    msg.flags = 1;
    msg.buf = buf;
    msg.len = count;
    ret = i2c_transfer(adapter,&msg,1);
    return ret == 1?count:ret;
}
                                                                                                                                                                                                                     
//读取某个特定寄存器的地址,然后返回值
int mpu6050_read_reg_byte(struct i2c_client * client,char reg)
{
    // 先写寄存器的地址
    int ret;
    struct i2c_adapter *adapter= client->adapter;
    struct i2c_msg msg[2];
    char rxbuf[1];
 
    msg[0].addr = client->addr;
    msg[0].flags = 0;
    msg[0].len = 1;
    msg[0].buf = &reg;
 
 
    //然后在读寄存器的值
    msg[1].addr = client->addr;
    msg[1].flags = 1;
    msg[1].len = 1;
    msg[1].buf = rxbuf;
 
    ret = i2c_transfer(adapter,msg,2);
    if(ret < 0)
    {
     printk("%s error  \n",__FUNCTION__);
     return ret;
    }
 
    return rxbuf[0];
}
 
 
int mpu6050_dv_open (struct inode *inode, struct file *filp)
{
    return 0;
}
 
int mpu6050_drv_close (struct inode *inode, struct file *filp)
{
    return 0;
}   
 
                                                                                                                                                                                                                     
long mpu6050_drv_ioctrl (struct file *filp, unsigned int cmd, unsigned long args)
{
    union mpu6050_data data;
    int ret;
    
    switch(cmd)
    //读数据
    {
     case IOC_GET_ACCEL:
        data.accel.x = mpu6050_read_reg_byte(mpu_dev->client,ACCEL_XOUT_L);
        data.accel.x |= mpu6050_read_reg_byte(mpu_dev->client,ACCEL_XOUT_H)<<8;
    
        data.accel.y = mpu6050_read_reg_byte(mpu_dev->client,ACCEL_YOUT_L);
        data.accel.y |= mpu6050_read_reg_byte(mpu_dev->client,ACCEL_YOUT_H)<<8;
 
        data.accel.z = mpu6050_read_reg_byte(mpu_dev->client,ACCEL_ZOUT_L);
        data.accel.z |= mpu6050_read_reg_byte(mpu_dev->client,ACCEL_ZOUT_H)<<8;
        break;
        
     case IOC_GET_GYRO:
        data.gyro.x = mpu6050_read_reg_byte(mpu_dev->client,GYRO_XOUT_L);
        data.gyro.x |= mpu6050_read_reg_byte(mpu_dev->client,GYRO_XOUT_H)<<8;
    
        data.gyro.y= mpu6050_read_reg_byte(mpu_dev->client,GYRO_YOUT_L);
        data.gyro.y|= mpu6050_read_reg_byte(mpu_dev->client,GYRO_YOUT_H)<<8;
     
        data.gyro.z= mpu6050_read_reg_byte(mpu_dev->client,GYRO_ZOUT_L);
        data.gyro.z|= mpu6050_read_reg_byte(mpu_dev->client,GYRO_ZOUT_H)<<8;
        break;
 
     case IOC_GET_TEMP:
        data.temp = mpu6050_read_reg_byte(mpu_dev->client,TEMP_OUT_L);
        data.temp |= mpu6050_read_reg_byte(mpu_dev->client,TEMP_OUT_H)<<8;
        break;
    
        default:
         printk("invalid cmd\n");
         return -EINVAL;
    }
 
    ret = copy_to_user((void __user *)args,&data,sizeof(data));
    if(ret > 0)
    {                                                                                                                                                                                                                
     printk ("%s error \n",__FUNCTION__);
     return -1;
    }
    
    return 0;
}   
    
    
const struct file_operations mpu6050_fops = {
    .open = mpu6050_dv_open,
    .release = mpu6050_drv_close,
    .unlocked_ioctl = mpu6050_drv_ioctrl,
};      
               
int mpu6050_drv_prob(struct i2c_client *client, const struct i2c_device_id *id)
{       
    printk("-----%s----\n", __FUNCTION__);
    /*
     申请设备号,实现fileoperation
     创建设备文件
     通过i2c接口去初始化i2c丛设备
    */  
     
    mpu_dev = kzalloc(sizeof(struct mpu_sensor),GFP_KERNEL);
        
    mpu_dev->client = client;
 
    mpu_dev->dev_major = register_chrdev(0,"mpu_drv",&mpu6050_fops);
        
    mpu_dev ->cls = class_create(THIS_MODULE,"mpu_cls");
        
    mpu_dev ->dev = device_create(mpu_dev ->cls,NULL,
                MKDEV(mpu_dev->dev_major,0),NULL,"mpu_sensor");
 
    //初始化i2c从设备                                                                                                                                                                                                
    char buf1[2] = {PWR_MGMT_1, 0x0};
    mpu6050_write_bytes(mpu_dev->client, buf1, 2);
 
    char buf2[2] = {SMPLRT_DIV, 0x07};
    mpu6050_write_bytes(mpu_dev->client, buf2, 2);
 
    char buf3[2] = {CONFIG, 0x06};
    mpu6050_write_bytes(mpu_dev->client, buf3, 2);
    
    char buf4[2] ={GYRO_CONFIG, 0x18};
    mpu6050_write_bytes(mpu_dev->client, buf4, 2);
 
    char buf5[2] = {ACCEL_CONFIG, 0x01};
    mpu6050_write_bytes(mpu_dev->client, buf5, 2);
    
    return 0;
    
}
        
int mpu6050_drv_remove(struct i2c_client *client)
{       
    device_destroy(mpu_dev ->cls,mpu_dev ->dev);
    class_destroy(mpu_dev ->cls);
    unregister_chrdev(mpu_dev->dev_major,"mpu_drv");
    kfree(mpu_dev);
}
 
const struct of_device_id of_mpu6050_id[] = {
 
        {.compatible = "invensense,mpu6050"},
        {}, 
};      
 
const struct i2c_device_id mpu_id_table[] = {
 
        {"mpu60xx_drv",0x1234567},
        {},
};
 
struct i2c_driver mpu6050_drv = {
    .probe = mpu6050_drv_prob,
    .remove = mpu6050_drv_remove,
    .driver = {
        .name = "lin_mpu6050_drv",//随便写,/sys/bus/i2c/driver/lin_mpu6050_drv
        .of_match_table = of_match_ptr(of_mpu6050_id),
    },
 
    .id_table = mpu_id_table,//可以不写,为了非设备树匹配
};
 
static int __init mpu6050_drv_init(void)
{
 
    //1.构建i2c driver,并注册到i2c总线
    return i2c_add_driver(&mpu6050_drv);
    
}
 
static void __exit mpu6050_drv_exit(void)
{
    i2c_del_driver(&mpu6050_drv);
 
}
 
module_init(mpu6050_drv_init);
module_exit(mpu6050_drv_exit);                                                                                                                                                                                       
MODULE_LICENSE("GPL");

4.4.2 mpu6050.h

#ifndef __MPU6050_H__                                                                                                                                                                                                
#define __MPU6050_H__
 
union mpu6050_data{
    struct {
        short x;
        short y;
        short z;
    }accel;
 
    struct {
        short x;
        short y;
        short z;
    }gyro;
 
    short temp;
};
 
#define IOC_GET_ACCEL _IOR('M',0x34,union mpu6050_data)
#define IOC_GET_GYRO _IOR('M',0x35,union mpu6050_data)
#define IOC_GET_TEMP _IOR('M',0x36,union mpu6050_data)
 
#endif

4.4.3 mpu6050_test.c

#include <stdio.h>                                                                                                                                                                                                   
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "mpu6050.h"
 
int main(int argc, const char *argv[])
{
    int fd; 
    union mpu6050_data data;
 
    fd = open ("/dev/mpu_sensor",O_RDWR);
    if(fd < 0)
    {   
        perror("open\n");
        exit(1);
    }   
 
    while(1)
    {   
        ioctl(fd,IOC_GET_ACCEL, &data);
        printf("accel data : x = %d , y = %d , z = %d \n",data.accel.x,data.accel.y,data.accel.z);
 
 
        ioctl(fd,IOC_GET_GYRO, &data);
        printf("gyro data : x = %d , y = %d , z = %d \n",data.gyro.x,data.gyro.y,data.gyro.z);
 
 
        ioctl(fd,IOC_GET_TEMP,&data);
        printf("temp data = %d \n",data.temp);
 
        printf("********************************\n");
sleep(1);
 
    }   
 
    return 0;
}
~                                                

4.4.4 Makefile

CROSS_COMPILE=arm-none-linux-gnueabi-                                                                                                                                                                                
CC=$(CROSS_COMPILE)gcc
KERNEL_DIR=/home/linux/fs4412/kernel/linux-3.14
CUR_DIR=$(shell pwd)
ROOTFS_DIR=/home/linux/fs4412/share/rootfs
MODULE_NAME=mpu6050_drv
APP_NAME=mpu6050_test
 
ifeq ($(KERNELRELEASE),)
all:
    make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
    $(CC) $(APP_NAME).c -o $(APP_NAME)
clean:
    make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
 
install:
    cp *.ko $(APP_NAME) $(ROOTFS_DIR)/drv_module
 
else
    obj-m += $(MODULE_NAME).o
    
endif

参考资料

IIC总线驱动框架

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

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

相关文章

Synchronized的锁升级过程

Synchronized的锁升级过程 synchronized锁升级过程&#xff1a;在synchronized中引入了偏向锁、轻量级锁、重量级锁之后&#xff0c;当前具体使用的是synchronzed中的那种类型锁&#xff0c;是根据线程竞争激烈程度来决定的。 偏向锁&#xff1a;在锁对象的对象头中记录一下当…

中间件之Kafka实用篇

目录标题一、一些定义&#xff08;一&#xff09;设计kafka的初衷&#xff08;二&#xff09;消息的持久化&#xff08;三&#xff09;sendfile 技术&#xff08;零拷贝&#xff09;二、获取kafka三、卡夫卡客户端工具四、kafka核心API&#xff08;功能&#xff09;五、spring …

阶段十:总结专题(第三章:虚拟机篇)

阶段十&#xff1a;总结专题&#xff08;第三章&#xff1a;虚拟机篇&#xff09;Day-第三章&#xff1a;虚拟机篇1. JVM 内存结构2. JVM 内存参数3. JVM 垃圾回收4. 内存溢出5. 类加载6. 四种引用7. finalizeDay-第三章&#xff1a;虚拟机篇 1. JVM 内存结构 要求 掌握 JVM…

Spring Cloud Alibaba全家桶(三)——微服务负载均衡器Ribbon与LoadBalancer

前言 本文为 微服务负载均衡器Ribbon与LoadBalancer 相关知识&#xff0c;下边将对什么是Ribbon&#xff08;包括&#xff1a;客户端的负载均衡、服务端的负载均衡、常见负载均衡算法&#xff09;&#xff0c;Nacos使用Ribbon&#xff0c;Ribbon内核原理&#xff08;包括&#…

Qt::QOpenGLWidget 渲染天空壳

在qt窗口中嵌入opengl渲染天空壳和各种立方体一 学前知识天空壳的渲染学前小知识1 立方体贴图 天空壳的渲染就是利用立方体贴图来实现渲染流程2 基础光照 光照模型3 opengl帧缓冲 如何自定义帧缓冲实现后期特效4 glsl常见的shader内置函数 glsl编程常用的内置函数二 shader代码…

部署运行ai智障写作记录【ChatRWKV】

文章目录前言一、环境安装1.python环境&#xff1a;Python 3.10。2.安装一些 pip 库numpy 、tokenizers 、prompt_toolkit3.安装pytorch 1.13.1CUDA 11.7二、运行记录1、下载代码2、下载训练参数3、编辑代码运行总结前言 看到知乎一篇教程&#xff0c; 大佬自己弄得ai小说续写…

AI环境搭建步骤(Windows环境)

1. 安装好Anaconda3版本(1) 安装链接&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/?CM&OD本文使用Anaconda3下载链接&#xff1a;Anaconda5(2) 注意安装anaconda时一定要把环境变量加入windows环境中。要没有勾选&#xff0c;安装完后还有手动加入…

线性代数学习-2

线性代数学习-2矩阵消元消元回代消元矩阵置换矩阵逆矩阵本文转载于https://herosunly.blog.csdn.net/article/details/88713747 该文章本人认为十分有用&#xff0c;便自己敲一遍笔记加固印象原文链接 原文这个笔记感觉比我老师讲的更加透彻&#xff0c;清晰。很好的展示了线性…

车辆热管理测试方案

车辆热管理是在能源危机出现、汽车排放法规日益严格以及人们对汽车舒适性要求更高的背景下应运而生的。将各个系统或部件如冷却系统、润滑系统和空调系统等集成一个有效的热管理系统&#xff1b;控制和优化车辆的热量传递过程&#xff0c;保证各关键部件和系统安全高效运行&…

《C++ Primer Plus》(第6版)第5章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第5章编程练习《C Primer Plus》&#xff08;第6版&#xff09;第5章编程练习1. 计算闭区间内的整数和2. 重新编写程序清单5.43. 累加4. 投资价值5. 销售情况6. 销售情况27. 汽车8. 统计单词数量9. 统计单词数量210. 嵌套循环《…

(五十)大白话深入研究索引之前,先来看看磁盘数据页的存储结构

前面我们已经给大家把MySQL数据库的部分内核原理&#xff0c;更新语句的执行原理&#xff0c;事务原理以及锁原理&#xff0c;都初步的讲给大家听了&#xff0c;同时还穿插了一些相关的数据库性能优化的案例&#xff0c;相信现在大家已经对数据库执行增删改语句的原理有了较为深…

小程序容器与微服务架构:提高应用程序开发效率和部署速度的利器

随着移动互联网的发展&#xff0c;小程序已经成为了一种非常流行的应用方式&#xff0c;它可以在不安装任何应用的情况下&#xff0c;直接在移动终端设备&#xff08;如&#xff1a;App&#xff0c;iPad等&#xff09;中运行。微服务架构则是一种的分布式系统架构&#xff0c;可…

三维数据可视化软件,可视化地图是用什么做的?

可视化地图是用什么做的&#xff1f;数据可视化地图是一种利用空间数据来表现地理信息的方式&#xff0c;能够为人们提供关于地理信息的准确、直观的可视化图形&#xff0c;以便更好地理解相关信息。数据地图可以最直观的表达出数据之间的空间关系&#xff0c;因此在很多数据分…

【组织架构】中国铁路兰州局集团有限公司

1 公司简介 中国铁路兰州局集团有限公司&#xff0c;是中国国家铁路集团有限公司管理的18个铁路局集团有限公司之一&#xff0c;简称“兰局”。经过59年的发展&#xff0c;现已成为西北地区最大的交通运输企业之一&#xff0c;形成了以兰州为枢纽&#xff0c;由陇海铁路、包兰铁…

前端Cookie基础知识

一、简介 ​ Cookie&#xff08;也称为HTTP Cookie、Web Cookie、浏览器 Cookie等等&#xff09;是服务器发送到用户浏览器并保存在本地的一小块数据&#xff0c;该数据通常是用户账号相关的信息&#xff0c;不同浏览器对Cookie的数量和大小限制不同&#xff0c;但一般来说&am…

centos8安装

本文由个人总结&#xff0c;如需转载使用请标明原著及原文地址 1.下载镜像 1.1阿里镜像 可以在阿里云的镜像库里下载&#xff0c;阿里centos8只保留了8.5.2111如果需要别的版本看1.2 http://mirrors.aliyun.com/centos/8/isos/x86_64 1.2其他版本的镜像 下好镜像的可以跳过…

2023年1月京东“白酒”品类销售数据出炉,五粮液表现较好

鲸参谋电商数据监测的2023年1月份京东“白酒”品类销售数据出炉&#xff01; 根据鲸参谋电商数据显示&#xff0c;2023年1月&#xff0c;京东平台上白酒的销量共计980万&#xff0c;环比增长约180%&#xff0c;同比去年增长约13%&#xff1b;销售额将近69亿&#xff0c;环比增长…

2021年全国职业院校技能大赛(中职组)网络安全竞赛试题A(1)

2021年全国职业院校技能大赛&#xff08;中职组&#xff09; 网络安全竞赛试题 &#xff08;1&#xff09; 这里是21的试题就以刷题为主&#xff0c;方法可能就不那么详细&#xff0c;如果是新题会详细过程&#xff0c;其他的详细过程可以看22的试题 目录 2021年全国职业院校…

【LeetCode】Day210-二叉搜索树的后序遍历序列

题目 剑指 Offer 33. 二叉搜索树的后序遍历序列【中等】 题解 二叉搜索树性质&#xff1a;左<根&#xff0c;右>根 后序遍历序&#xff1a;左右根 递归分治 利用递归求解&#xff0c;[ i,j ]区间中找到第一个比根节点大的元素&#xff0c;下标为m&#xff0c;则[i,m…

10月17日|实验报告|paddle paddle|概念辨析

目录 一、安装paddle paddle 第一章 零基础入门深度学习 机器学习和深度学习综述 1.人工智能、机器学习、深度学习的关系 1.1人工智能(Artificial Intelligence,AI) 1.2机器学习 1.2.1机器学习的实现 1.2.2机器学习方法论 1.3深度学习​​​​​​​ 一、安装paddle…