丹东有做公司网站的吗,哪个网站可以做兼职ppt,长春做网站费用,装饰公司做网站I2C总线二级外设驱动开发是一个涉及多个步骤和函数调用的过程#xff0c;主要目的是使得挂接在I2C总线上的外设能够正常工作。
一、I2C总线二级外设驱动开发概述 I2C总线是一种广泛使用的串行通信总线#xff0c;用于连接微控制器及其外围设备。在Linux内核中#xff0c;I2… I2C总线二级外设驱动开发是一个涉及多个步骤和函数调用的过程主要目的是使得挂接在I2C总线上的外设能够正常工作。
一、I2C总线二级外设驱动开发概述 I2C总线是一种广泛使用的串行通信总线用于连接微控制器及其外围设备。在Linux内核中I2C总线驱动开发包括I2C总线控制器驱动也称为适配器驱动和挂接在I2C总线上的二级外设驱动也称为客户驱动或设备驱动。 疑惑1二级外设驱动编写好那总线上的那个iic驱动不需要编写吗 当你为挂接在I2C总线上的二级外设编写驱动程序也称为客户驱动或设备驱动时你通常不需要从头开始编写整个I2C总线也称为I2C适配器的驱动程序除非该I2C总线控制器是全新的、且Linux内核中尚不支持的。
Linux内核对I2C总线的支持 本次实验仅是对driver驱动层进行开发在iic核心层以下则是开发商已经帮我们做好的第一次移植我们进行第二次移植即可用mpu6050作为示例芯片为fs4412。
I2C设备驱动driver驱动层我们需要设计的地方 即挂接在I2C总线上的二级外设的驱动也称客户client驱动实现对二级外设的各种操作二级外设的几乎所有操作全部依赖于对其自身内部寄存器的读写对这些二级外设寄存器的读写又依赖于I2C总线的发送和接收
I2C总线驱动访问抽象层、硬件实现控制层内核已经做好初始化了解如何使用api即可 即对I2C总线自身控制器的驱动一般SOC芯片都会提供多个I2C总线控制器每个I2C总线控制器提供一组I2C总线SDA一根SCL一根每一组被称为一个I2C通道Linux内核里将I2C总线控制器叫做适配器adapter适配器驱动主要工作就是提供通过本组I2C总线与二级外设进行数据传输的接口每个二级外设驱动里必须能够获得其对应的adapter对象才能实现数据传输
I2C核心 内核已经做好初始化 承上启下为I2C设备驱动和I2C总线驱动开发提供接口为I2C设备驱动层提供管理多个i2c_driver、i2c_client对象的数据结构为I2C总线驱动层提供多个i2c_algorithm、i2c_adapter对象的数据结构 四大核心对象之间的关系图 结合第一张图可知重点
一个i2c总线控制器也叫I2C适配器的算法i2c_algorithm 可以被n个i2c总线控制器使用。一个二级外设设备驱动程序i2c_driver可以被n个外设i2c_client使用。每个外设通过其i2c_client表示在系统中都是唯一的并且通常只与一个i2c_driver相关联。每个外设i2c_client都要与一个i2c总线控制器i2c_adapter通过其总线编号和I2C地址和一个i2c_driver通过其ID表或其他匹配机制相关联。
二、开发步骤详解
1. 查阅fs4412开发板以及mpu6050原理图
目的了解二级外设挂在哪条I2C总线上以及外设的身份标识如设备地址。 再去mpu-6050原理图可得 上图可知ADO接地为0SCL与SDA复用引脚GPB2,3查询可得使用i2c5号总线。 以及一些必要的宏设置
#define SMPLRT_DIV 0x19 //陀螺仪采样率典型值0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率典型值0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围典型值0xF8(不自检/-2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围典型值0x19(不自检/-G)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#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
#define PWR_MGMT_1 0x6B //电源管理典型值0x00(正常启用)2. 搭建驱动框架
目的参照platform样式或其他标准驱动框架为二级外设驱动搭建驱动框架。
//其它struct file_operations函数实现原理同硬编驱动static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{//做硬编驱动模块入口函数的活
}static int mpu6050_remove(struct i2c_client *pclt)
{//做硬编驱动模块出口函数的活
}/*名称匹配时定义struct i2c_device_id数组*/
static struct i2c_device_id mpu6050_ids
{{mpu6050,0},//.....{}
};/*设备树匹配时定义struct of_device_id数组*/
static struct of_device_id mpu6050_dts
{{.compatible invensense,mpu6050},//....{}
};/*通过定义struct i2c_driver类型的全局变量来创建i2c_driver对象同时对其主要成员进行初始化1.向Linux内核的I2C子系统注册一个能够管理和控制这些设备的驱动程序。2.i2c_driver结构包含了驱动程序与I2C子系统交互所需的所有关键信息包括如何识别设备、如何与设备通信、如何初始化设备以及如何处理设备的特定操作等。
*/
struct i2c_driver mpu6050_driver
{.driver {.name mpu6050,.owner THIS_MODULE,.of_match_table mpu6050_dts,},.probe mpu6050_probe,.remove mpu6050_remove,.id_table mpu6050_ids,
};/*以下其实是个宏展开后相当于实现了模块入口函数和模块出口函数*/
module_i2c_driver(mpu6050_driver);
/*
实则为
int __init mpu6050_driver_init(void){i2c_add_driver(mpu6050_driver);//功能向内核注册一个i2c_driver对象}void __exit mpu6050_driver_exit(void){i2c_del_driver(mpu6050_driver);//从内核注销一个i2c_driver对象}module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
*/MODULE_LICENSE(GPL);上述代码框架解释
struct i2c_driver {unsigned int class;/* 标准驱动模型接口 */int (*probe)(struct i2c_client *, const struct i2c_device_id *);int (*remove)(struct i2c_client *);/* 与枚举无关的驱动模型接口 */void (*shutdown)(struct i2c_client *);int (*suspend)(struct i2c_client *, pm_message_t mesg);int (*resume)(struct i2c_client *);void (*alert)(struct i2c_client *, unsigned int data);/* 类似ioctl的命令可用于执行特定功能 */int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);struct device_driver driver;const struct i2c_device_id *id_table;/* 用于自动设备创建的设备检测回调 */int (*detect)(struct i2c_client *, struct i2c_board_info *);const unsigned short *address_list;struct list_head clients;
};
/* 重要成员
probe在i2c_client与i2c_driver匹配后执行该函数
remove在取消i2c_client与i2c_driver匹配绑定后执行该函数
driver这个成员类型在平台设备驱动层中也有而且使用其中的name成员来实现平台设备匹配但是i2c子系统中不使用其中的name进行匹配这也是i2c设备驱动模型和平台设备模型匹配方法的一点区别
id_table:用来实现i2c_client与i2c_driver匹配绑定当i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候就匹配上了。补充i2c_client与i2c_driver匹配问题
- i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候
- i2c_client指定的信息在物理上真实存放对应的硬件并且工作是正常的才会绑定上并执行其中的probe接口函数这第二点要求和平台模型匹配有区别平台模型不要求设备层指定信息在物理上真实存在就能匹配
*//* 功能向内核注册一个i2c_driver对象
返回值0成功负数 失败*/
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);/* 功能从内核注销一个i2c_driver对象
返回值无 */
void i2c_del_driver(struct i2c_driver *driver);
然后需要
定义i2c_client结构体每个二级外设都需要一个对应的i2c_client结构体来表示。这个结构体包含了外设的地址、名称、适配器adapter等信息。实现probe和remove函数probe函数是驱动加载时调用的函数用于初始化外设remove函数是驱动卸载时调用的函数用于清理资源。定义i2c_driver结构体这个结构体包含了驱动的名称、probe和remove函数指针等用于向I2C核心注册驱动。 i2c_client结构体注释
struct i2c_client {unsigned short flags;unsigned short addr;char name[I2C_NAME_SIZE];struct i2c_adapter *adapter;struct i2c_driver *driver;struct device dev;int irq;struct list_head detected;
};
/*重要成员
flags:地址长度如是10位还是7位地址默认是7位地址。如果是10位地址器件则设置为I2C_CLIENT_TEN
addr具体I2C器件如(at24c02)设备地址,低7位
name:设备名用于和i2c_driver层匹配使用的可以和平台模型中的平台设备层platform_driver中的name作用是一样的。
adapter:本设备所绑定的适配器结构(CPU有很多I2C适配器类似单片机有串口1、串口2等等在linux中每个适配器都用一个结构描述)
driver指向匹配的i2c_driver结构不需要自己填充匹配上后内核会完成这个赋值操作
dev:内嵌的设备模型可以使用其中的platform_data成员传递给任何数据给i2c_driver使用。
irq设备需要使用到中断时把中断编号传递给i2c_driver进行注册中断如果没有就不需要填充。(有的I2C器件有中断引脚编号与CPU相连)
*/struct i2c_adapter *i2c_get_adapter(int nr);
/* 获得/释放 i2c_adapter 路径i2c-core.c linux-3.5\drivers\i2c */
/*功能通过i2c总线编号获得内核中的i2c_adapter结构地址然后用户可以使用这个结构地址就可以给i2c_client结构使用从而实现i2c_client进行总线绑定从而增加适配器引用计数。
返回值
NULL没有找到指定总线编号适配器结构
非NULL指定nr的适配器结构内存地址*/void i2c_put_adapter(struct i2c_adapter *adap);
/*减少引用计数当使用·i2c_get_adapter·后需要使用该函数减少引用计数。如果你的适配器驱动不需要卸载可以不使用*/ 具体实现代码部分
struct mpu6050_dev
{struct cdev mydev;//说明是一个字符设备struct i2c_client *pclt;/*存储了MPU6050设备在I2C总线上的相关信息如设备的I2C地址、适配器即I2C主设备、传输速率等。通过pclt指针驱动程序可以访问MPU6050设备在I2C总线上的配置信息并执行数据的读写操作。*/
};struct mpu6050_dev *pgmydev NULL;static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{int ret 0;dev_t devno MKDEV(major, minor);/* 手动申请设备号 */ret register_chrdev_region(devno, char_num, mpu6050);if (ret) {/* 动态申请设备号 */ret alloc_chrdev_region(devno, minor, char_num, mpu6050);if(ret){printk(get devno failed\n);return -1;}/*申请成功 更新设备号*/major MAJOR(devno);}pgmydev (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);if(NULL pgmydev) {unregister_chrdev_region(devno, char_num);printk(kmalloc for struct mpu6050_dev failed\n);return -1;}memset(pgmydev, 0, sizeof(struct mpu6050_dev));/* 给struct cdev对象指定操作函数集 */cdev_init(pgmydev-mydev, myops);/* 将struct cdev对象添加到内核对应的数据结构中 */pgmydev-mydev.owner THIS_MODULE;cdev_add(pgmydev-mydev, devno, char_num);return 0;
}static int mpu6050_remove(struct i2c_client *pclt)
{dev_t devno MKDEV(major, minor);/* 从内核中移除一个字符设备 */cdev_del(pgmydev-mydev);/* 回收设备号 */unregister_chrdev_region(devno, char_num);/* 释放内存 */kfree(pgmydev);pgmydev NULL;return 0;
}/*名称匹配时定义struct i2c_device_id数组*/
struct i2c_device_id mpu6050_ids[]
{{mpu6050,0},{}
};struct i2c_driver mpu6050_driver {.driver {.name mpu6050,.owner THIS_MODULE,},.probe mpu6050_probe,.remove mpu6050_remove,.id_table mpu6050_ids,
};
3.注册和匹配以及初始化mpu6050
名称匹配通过定义i2c_device_id结构体数组并在i2c_driver结构体中指定id_table成员实现名称匹配。这种方法需要事先知道外设的名称。本文使用的方法设备树匹配在设备树Device Tree中定义外设的信息如compatible属性然后在i2c_driver结构体中指定of_match_table成员实现设备树匹配。这种方法更加灵活适用于大多数现代Linux系统。本文使用的方法初始化mpu6050.
void init_mpu6050(struct i2c_client *pclt)
{mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);mpu6050_write_byte(pclt,CONFIG,0x06);mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}
4.数据传输
使用i2c_transfer函数通过这个函数可以发送和接收数据mpu6050_write_bytempu6050_read_byte。它允许你指定传输的方向读或写、传输的数据长度以及数据缓冲区。处理中断和错误根据需要处理外设产生的中断并处理可能的错误情况如传输失败、设备无响应等。 i2c_transfer函数注释
/*i2c收发一体化函数,收还是发由参数msgs的成员flags决定*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
/*
功能根据msgs进行手法控制
参数
adap使用哪一个适配器发送信息一般是取i2c_client结构中的adapter指针作为参数
msgs具体发送消息指针一般情况下是一个数组
num:表示前一个参数msgs数组有多少个消息要发送的
返回值
负数失败0 表示成功发送i2c_msg数量
*/struct i2c_adapter
/*i2c_adapter 结构体封装了与特定 I2C 总线相关的所有必要信息包括硬件细节、当前状态、传输函数等使得内核能够管理和控制该总线上的设备。*/struct i2c_msg {__u16 addr; /* slave address */__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */__u16 len; /* msg length */__u8 *buf; /* pointer to msg data */
};
/* 重要成员
addr:要读写的二级外设地址
flags表示地址的长度读写功能。如果是10位地址必须设置I2C_M_TEN如果是读操作必须设置有I2C_M_RD······可以使用或运算合成。
buf要读写的数据指针。写操作数据源 读操作指定存放数据的缓存区
len读写数据的数据长度
*/发送和接收数据函数详解
int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
{int ret 0;char txbuf[1] {reg};char rxbuf[1] {0};struct i2c_msg msg[2] {{pclt-addr,0,1,txbuf},{pclt-addr,I2C_M_RD,1,rxbuf}};//利用i2c_transfer将msg里面两个数组发送给mpu6050其中第一个数组是告诉mpu6050要读取哪个寄存器然后mpu6050将该寄存器地址里面的数据通过msg[1]的rxbuf返回ret i2c_transfer(pclt-adapter,msg,ARRAY_SIZE(msg));if(ret 0){printk(ret %d,in mpu6050_read_byte\n,ret);return ret;}return rxbuf[0];
}int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
{int ret 0;char txbuf[2] {reg,val};struct i2c_msg msg[1] {{pclt-addr,0,2,txbuf},};ret i2c_transfer(pclt-adapter,msg,ARRAY_SIZE(msg));if(ret 0){printk(ret %d,in mpu6050_write_byte\n,ret);return ret;}return 0;
}
5.I2C总线二级外设驱动开发之名称匹配
这种匹配方式需要自己创建i2c_client对象
struct i2c_board_info {char type[I2C_NAME_SIZE];unsigned short flags;unsigned short addr;void *platform_data;struct dev_archdata *archdata;struct device_node *of_node;int irq;
};
/*用来协助创建i2c_client对象
重要成员
type用来初始化i2c_client结构中的name成员
flags用来初始化i2c_client结构中的flags成员
addr用来初始化i2c_client结构中的addr成员
platform_data用来初始化i2c_client结构中的.dev.platform_data成员
archdata用来初始化i2c_client结构中的.dev.archdata成员
irq:用来初始化i2c_client结构中的irq成员关键就是记住该结构和i2c_client结构成员的对应关系。在i2c子系统不直接创建i2c_client结构只是提供struct i2c_board_info结构信息让子系统动态创建并且注册。
*/介绍两种方法
1.i2c_new_device明确二级外设地址的情况下可用 mpu6050_client.c:
#include linux/kernel.h
#include linux/module.h
#include linux/i2c.h//用来协助创建i2c_client对象
static struct i2c_board_info mpu6050_info
{I2C_BOARD_INFO(mpu6050,0x68)//一个在 Linux 内核 I2C 子系统中使用的宏用于定义一个 I2C 设备的信息
};/*用于存储通过I2C总线找到的MPU6050设备的i2c_client结构体指针。*/
static struct i2c_client *gpmpu6050_client NULL;static int __init mpu6050_client_init(void)
{struct i2c_adapter *padp NULL;/*通过I2C适配器编号这里是5获取I2C适配器的指针*/padp i2c_get_adapter(5);/*使用之前获取的I2C适配器和设备信息创建一个新的I2C设备。如果成功它会返回一个指向新创建的i2c_client结构体的指针并将其存储在gpmpu6050_client中。*/gpmpu6050_client i2c_new_device(padp,mpu6050_info);/*释放之前获取的I2C适配器指针。*/i2c_put_adapter(padp);return 0;
}static void mpu6050_client_exit(void)
{/*注销之前注册的MPU6050 I2C设备*/i2c_unregister_device(gpmpu6050_client);
}module_init(mpu6050_client_init);
module_exit(mpu6050_client_exit);
MODULE_LICENSE(GPL);
完整代码展示
mpu6050.h
#ifndef MPU_6050_H
#define MPU_6050_H//加速度
struct accel_data
{unsigned short x;unsigned short y;unsigned short z;
};
//角速度
struct gyro_data
{unsigned short x;unsigned short y;unsigned short z;
};
//联合体
union mpu6050_data
{struct accel_data accel;struct gyro_data gyro;unsigned short temp;//温度
};#define MPU6050_MAGIC K#define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)//读操作
#define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)
#define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)#endifmpu6050_drv.c
#include linux/module.h
#include linux/kernel.h
#include linux/fs.h
#include linux/i2c.h
#include linux/cdev.h
#include linux/wait.h
#include linux/sched.h
#include linux/poll.h
#include linux/slab.h
#include linux/io.h
#include asm/uaccess.h
#include asm/ioctl.h#include mpu6050.h/****************MPU6050内部寄存器地址****************/#define SMPLRT_DIV 0x19 //陀螺仪采样率典型值0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率典型值0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围典型值0x18(不自检2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率典型值0x18(不自检2G5Hz)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#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
#define PWR_MGMT_1 0x6B //电源管理典型值0x00(正常启用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68只读)
#define SlaveAddress 0x68 //MPU6050-I2C地址int major 11; //主设备号
int minor 0; //次设备号
int char_num 1; //设备号数量struct mpu6050_dev
{struct cdev mydev;//说明是一个字符设备struct i2c_client *pclt;/*存储了MPU6050设备在I2C总线上的相关信息如设备的I2C地址、适配器即I2C主设备、传输速率等。通过pclt指针驱动程序可以访问MPU6050设备在I2C总线上的配置信息并执行数据的读写操作。*/
};
struct mpu6050_dev *pgmydev NULL;int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
{int ret 0;char txbuf[1] {reg};char rxbuf[1] {0};struct i2c_msg msg[2] {{pclt-addr,0,1,txbuf},{pclt-addr,I2C_M_RD,1,rxbuf}};//利用i2c_transfer将msg里面两个数组发送给mpu6050其中第一个数组是告诉mpu6050要读取哪个寄存器吗然后mpu6050将该寄存器地址里面的数据通过msg[1]的rxbuf返回ret i2c_transfer(pclt-adapter,msg,ARRAY_SIZE(msg));if(ret 0){printk(ret %d,in mpu6050_read_byte\n,ret);return ret;}return rxbuf[0];
}int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
{int ret 0;char txbuf[2] {reg,val};struct i2c_msg msg[1] {{pclt-addr,0,2,txbuf},};ret i2c_transfer(pclt-adapter,msg,ARRAY_SIZE(msg));if(ret 0){printk(ret %d,in mpu6050_write_byte\n,ret);return ret;}return 0;
}int mpu6050_open (struct inode *pnode, struct file *pfile)//打开设备
{pfile-private_data (void *) (container_of(pnode-i_cdev, struct mpu6050_dev,mydev));return 0;
}int mpu6050_close(struct inode *pnode, struct file *pfile)//关闭设备
{return 0;
}long mpu6050_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{struct mpu6050_dev *pmydev (struct mpu6050_dev *)pfile-private_data;union mpu6050_data data;switch(cmd) {case GET_ACCEL:data.accel.x mpu6050_read_byte(pmydev-pclt,ACCEL_XOUT_L);data.accel.x mpu6050_read_byte(pmydev-pclt,ACCEL_XOUT_H) 8;data.accel.y mpu6050_read_byte(pmydev-pclt,ACCEL_YOUT_L);data.accel.y mpu6050_read_byte(pmydev-pclt,ACCEL_YOUT_H) 8;data.accel.z mpu6050_read_byte(pmydev-pclt,ACCEL_ZOUT_L);data.accel.z mpu6050_read_byte(pmydev-pclt,ACCEL_ZOUT_H) 8;break;case GET_GYRO:data.gyro.x mpu6050_read_byte(pmydev-pclt,GYRO_XOUT_L);data.gyro.x mpu6050_read_byte(pmydev-pclt,GYRO_XOUT_H) 8;data.gyro.y mpu6050_read_byte(pmydev-pclt,GYRO_YOUT_L);data.gyro.y mpu6050_read_byte(pmydev-pclt,GYRO_YOUT_H) 8;data.gyro.z mpu6050_read_byte(pmydev-pclt,GYRO_ZOUT_L);data.gyro.z mpu6050_read_byte(pmydev-pclt,GYRO_ZOUT_H) 8;break;case GET_TEMP:data.temp mpu6050_read_byte(pmydev-pclt,TEMP_OUT_L);data.temp mpu6050_read_byte(pmydev-pclt,TEMP_OUT_H) 8;break;default:return -EINVAL;}if(copy_to_user((void *)arg,data,sizeof(data)))//用于将数据从内核空间安全地复制到用户空间{return -EFAULT;}return sizeof(data);
}void init_mpu6050(struct i2c_client *pclt)
{mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);mpu6050_write_byte(pclt,CONFIG,0x06);mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}struct file_operations myops {.owner THIS_MODULE,.open mpu6050_open,.release mpu6050_close,.unlocked_ioctl mpu6050_ioctl,
};static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{int ret 0;dev_t devno MKDEV(major, minor);/* 手动申请设备号 */ret register_chrdev_region(devno, char_num, mpu6050);if (ret) {/* 动态申请设备号 */ret alloc_chrdev_region(devno, minor, char_num, mpu6050);if(ret){printk(get devno failed\n);return -1;}/*申请成功 更新设备号*/major MAJOR(devno);}pgmydev (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);if(NULL pgmydev) {unregister_chrdev_region(devno, char_num);printk(kmalloc for struct mpu6050_dev failed\n);return -1;}memset(pgmydev, 0, sizeof(struct mpu6050_dev));pgmydev-pclt pclt;/* 给struct cdev对象指定操作函数集 */cdev_init(pgmydev-mydev, myops);/* 将struct cdev对象添加到内核对应的数据结构中 */pgmydev-mydev.owner THIS_MODULE;cdev_add(pgmydev-mydev, devno, char_num);init_mpu6050(pgmydev-pclt);return 0;
}static int mpu6050_remove(struct i2c_client *pclt)
{dev_t devno MKDEV(major, minor);/* 从内核中移除一个字符设备 */cdev_del(pgmydev-mydev);/* 回收设备号 */unregister_chrdev_region(devno, char_num);/* 释放内存 */kfree(pgmydev);pgmydev NULL;return 0;
}/*名称匹配时定义struct i2c_device_id数组*/
struct i2c_device_id mpu6050_ids[]
{{mpu6050,0},{}
};struct i2c_driver mpu6050_driver {.driver {.name mpu6050,.owner THIS_MODULE,},.probe mpu6050_probe,.remove mpu6050_remove,.id_table mpu6050_ids,
};#if 0int __init mpu6050_driver_init(void){i2c_add_driver(mpu6050_driver);}void __exit mpu6050_driver_exit(void){i2c_del_driver(mpu6050_driver);}module_init(mpu6050_driver_init);module_exit(mpu6050_driver_exit);
#elsemodule_i2c_driver(mpu6050_driver);
#endifMODULE_LICENSE(GPL);
testapp.c
#include sys/types.h
#include sys/stat.h
#include sys/ioctl.h
#include fcntl.h
#include unistd.h#include stdio.h#include mpu6050.hint main(int argc,char *argv[])
{int fd -1;union mpu6050_data data;if(argc 2){printf(The argument is too few\n);return 1;}fd open(argv[1],O_RDONLY);if(fd 0){printf(open %s failed \n,argv[1]);return 2;}while(1){sleep(2);ioctl(fd,GET_ACCEL,data);printf(Accel-x0x%x\n,data.accel.x);printf(Accel-y0x%x\n,data.accel.y);printf(Accel-z0x%x\n,data.accel.z);ioctl(fd,GET_GYRO,data);printf(Gyro-x0x%x\n,data.gyro.x);printf(Gyro-y0x%x\n,data.gyro.y);printf(Gyro-z0x%x\n,data.gyro.z);ioctl(fd,GET_TEMP,data);printf(Temp0x%x\n,data.temp);printf(\n);}close(fd);fd -1;return 0;
}
实验结果 2. i2c_new_probed_device不明确二级外设地址
i2c二级外设client框架不明确二级外设地址但是知道是可能几个值之一的情况下可用
将外设程序改为如下程序其他代码不变即可 mpu6050_client_probed.c
#include linux/kernel.h
#include linux/module.h
#include linux/i2c.h//数组列出了MPU6050可能的I2C地址0x68和0x69并以I2C_CLIENT_END作为结束标记。
static unsigned short mpu6050_addr_list[]
{0x68,0x69,I2C_CLIENT_END
};//用于存储找到的MPU6050设备的I2C客户端信息
static struct i2c_client *gpmpu6050_client NULL;static int __init mpu6050_client_init(void)
{struct i2c_adapter *padp NULL;struct i2c_board_info mpu6050_info {};strcpy(mpu6050_info.type,mpu6050);padp i2c_get_adapter(5);//尝试在指定的I2C总线上找到与mpu6050_info和mpu6050_addr_list匹配的设备。gpmpu6050_client i2c_new_probed_device(padp,mpu6050_info,mpu6050_addr_list,NULL);i2c_put_adapter(padp);if(gpmpu6050_client ! NULL){return 0;}else{return -ENODEV;}
}static void mpu6050_client_exit(void)
{i2c_unregister_device(gpmpu6050_client);
}module_init(mpu6050_client_init);
module_exit(mpu6050_client_exit);
MODULE_LICENSE(GPL); 6.I2C总线二级外设驱动开发之设备树匹配
将驱动程序改为
//设备树
struct of_device_id mpu6050_dt[]
{{.compatible invensense,mpu6050},{}
};struct i2c_device_id mpu6050_ids[]
{{mpu6050,0},{}
};struct i2c_driver mpu6050_driver {.driver {.name mpu6050,.owner THIS_MODULE,.of_match_table mpu6050_dt,},.probe mpu6050_probe,.remove mpu6050_remove,.id_table mpu6050_ids,
}; 实验结果