赞
踩
SOC芯片平台的外设分为:
i2c传输的要点就是: 传输一个字节 后面必然紧跟一个"响应"信号----应答信号.这个响应信号可能来自主机,或者是从机,具体是谁,就要看传输方向。
传输方向分两种情况(每种情况又有两种可能: A无应答和 B有应答):
1.主机->从机,主机对从机发一个字节之后,主机要读取从机的响应信号(主机读SDA线)
A) 主机读SDA为高电平,说明从机无应答(意味着从机接收完毕,主机发送停止信号)
B) 主机读SDA为低电平,说明从机有应答。(可继续发送下一个字节)
2.从机->主机, 主机读取从机一个字节之后,主机要向从机发送一个响应信号(主机写SDA线)
A) 主机写SDA为高电平,从机收到主机的无应答信号之后,从机停止传输,等待主机的停止信号。
B) 主机写SDA为低电平,从机收到主机的应答信号之后,从机继续输出下一字节
I2CCON寄存器:控制寄存器
第7位:决定是否允许产生应答信号,无论发送还是接收前,需置1
第6位:传输时时钟线分频,一般选置1
第5位:决定是否开启发送或接收结束时发通知,无论发送还是接收前,需置1
第4位:接收或发送是否完毕可以通过检查此位是否为1,接收或发送完毕后需置0
I2CSTAT寄存器:状态寄存器
第6、7位:每次传输前需选择传输模式
第5位:置0产生将产生终止信号,传输前置1产生起始信号
第4位:使能数据输出,传输前需置1
I2CDS寄存器:数据寄存器,发送前被发送的数据存放处,接收后结果也从此处读取
void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data) { // 从设备寻址 I2C5.I2CDS = slave_addr; I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX */ I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4; /* 主传输模式, 启动, 启用 RX/TX */ while(!(I2C5.I2CCON & (1<<4))); I2C5.I2CDS = addr; I2C5.I2CCON &= ~(1<<4); // 清除挂起位以恢复 while(!(I2C5.I2CCON & (1<<4))); // 发送数据 I2C5.I2CDS = data; // 数据 I2C5.I2CCON &= ~(1<<4); // 清除挂起位以恢复 while(!(I2C5.I2CCON & (1<<4))); I2C5.I2CSTAT = 0xD0; // 停止 I2C5.I2CCON &= ~(1<<4); // 清除挂起位以恢复 mydelay_ms(10); }
void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data) { // 从设备寻址 I2C5.I2CDS = slave_addr; I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX 中断使能 */ I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4; /* 主传输模式, 启动, 启用 RX/TX */ while(!(I2C5.I2CCON & (1<<4))); /* 对应位为1表示slave_addr传输完成,线路处于挂起状态 */ I2C5.I2CDS = addr; I2C5.I2CCON &= ~(1<<4); // 清除挂起位以继续传输 while(!(I2C5.I2CCON & (1<<4))); I2C5.I2CSTAT = 0xD0; // 停止 第5位写0,表示要求产生stop信号 // 接收数据 I2C5.I2CDS = slave_addr | 0x01; // 读取 I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX 中断使能 */ I2C5.I2CSTAT = 2<<6 | 1<<5 | 1<<4; /* 主接收模式, 启动, 启用 RX/TX, 0xB0 */ while(!(I2C5.I2CCON & (1<<4))); I2C5.I2CCON &= ~((1<<7) | (1<<4)); /* 恢复操作 & 无 ACK */ while(!(I2C5.I2CCON & (1<<4))); I2C5.I2CSTAT = 0x90; // 停止 第5位写0,表示要求产生stop信号 I2C5.I2CCON &= ~(1<<4); /* 清除中断挂起位 */ *data = I2C5.I2CDS; mydelay_ms(10); }
I2C设备驱动(driver驱动层):即挂接在I2C总线上的二级外设的驱动,也称客户(client)驱动,实现对二级外设的各种操作,二级外设的几乎所有操作全部依赖于对其自身内部寄存器的读写,对这些二级外设寄存器的读写又依赖于I2C总线的发送和接收
I2C总线驱动(访问抽象层、硬件实现控制层):即对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二级外设驱动开发涉及到核心结构体及其相关接口函数:
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结构信息,让子系统动态创建,并且注册。 */
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相连) */ /* 获得/释放 i2c_adapter 路径:i2c-core.c linux-3.5\drivers\i2c */ /*功能:通过i2c总线编号获得内核中的i2c_adapter结构地址,然后用户可以使用这个结构地址就可以给i2c_client结构使用,从而实现i2c_client进行总线绑定,从而增加适配器引用计数。 返回值: NULL:没有找到指定总线编号适配器结构 非NULL:指定nr的适配器结构内存地址*/ struct i2c_adapter *i2c_get_adapter(int nr); /*减少引用计数:当使用·i2c_get_adapter·后,需要使用该函数减少引用计数。(如果你的适配器驱动不需要卸载,可以不使用)*/ void i2c_put_adapter(struct i2c_adapter *adap); /* 功能:根据参数adap,info,addr,addr_list动态创建i2c_client并且进行注册 参数: adap:i2c_client所依附的适配器结构地址 info:i2c_client基本信息 addt_list: i2c_client的地址(地址定义形式是固定的,一般是定义一个数组,数组必须以I2C_CLIENT_END结束,示例:unsigned short ft5x0x_i2c[]={0x38,I2C_CLIENT_END}; probe:回调函数指针,当创建好i2c_client后,会调用该函数,一般没有什么特殊需求传递NULL。 返回值: 非NULL:创建成功,返回创建好的i2c_client结构地址 NULL:创建失败 */ struct i2c_client * i2c_new_probed_device ( struct i2c_adapter *adap, struct i2c_board_info *info, unsigned short const *addr_list, int (*probe)(struct i2c_adapter *, unsigned short addr) ); /*示例: struct i2c_adapter *ad; struct i2c_board_info info={""}; unsigned short addr_list[]={0x38,0x39,I2C_CLIENT_END}; //假设设备挂在i2c-2总线上 ad=i2c_get_adapter(2); //自己填充board_info strcpy(inf.type,"xxxxx"); info.flags=0; //动态创建i2c_client并且注册 i2c_new_probed_device(ad,&info,addr_list,NULL); i2c_put_adapter(ad); */ /*注销*/ void i2c_unregister_device(struct i2c_client *pclt) struct i2c_client * i2c_new_device ( struct i2c_adapter *padap, struct i2c_board_info const *pinfo ); /*示例: struct i2c_adapter *ad; struct i2c_board_info info={ I2C_BOARD_INFO(name,二级外设地址) }; //假设设备挂在i2c-2总线上 ad=i2c_get_adapter(2); //动态创建i2c_client并且注册 i2c_new_device(ad,&info); i2c_put_adapter(ad); */
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);
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:读写数据的数据长度 */ /*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数量 */ /*I2C读取数据函数*/ int i2c_master_recv(const struct i2c_client *client, char *buf, int count) /*功能:实现标准的I2C读时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+读方向这一环节了 参数: client:设备结构 buf:读取数据存放缓冲区 count:读取数据大小 不大于64k 返回值: 失败:负数 成功:成功读取的字节数 */ /*I2C发送数据函数*/ int i2c_master_send(const struct i2c_client *client, const char *buf, int count) /*功能:实现标准的I2C写时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+写方向这一环节了 参数: client:设备结构地址 buf:发送数据存放缓冲区 count:发送数据大小 不大于64k 返回值: 失败:负数 成功:成功发送的字节数 */
三轴角速度+三轴加速度+温度传感器
#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(正常启用)
不要忘记:
内核编译时添加此字符设备驱动代码(i2c-dev.c),因此需要修改make menuconfig的配置:
不要忘记:
缺点:
mpu6050.h
#ifndef MPU_6050_H #define MPU_6050_H #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> int init_mpu6050(int fd); int read_accelx(int fd); int read_accely(int fd); int read_accelz(int fd); int read_temp(int fd); int read_gyrox(int fd); int read_gyroy(int fd); int read_gyroz(int fd); /****************MPU6050内部寄存器地址****************/ #define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz) #define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz) #define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s) #define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,2G,5Hz) #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地址 #define I2C_SLAVE 0x0703 /* Use this slave address */ #define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */ #endif
mpu6050_op_rw.c
#include "mpu6050.h" // 从MPU6050读取数据的函数 static int read_data_from_mpu6050(int fd, unsigned char reg, unsigned char *pdata) { int ret = 0; unsigned char buf[1] = {reg}; // 准备要写入的寄存器地址 // 写入要读取的寄存器地址 ret = write(fd, buf, 1); if (ret != 1) { printf("write reg failed, in read_data_from_mpu6050\n"); return -1; // 写入失败,返回错误 } buf[0] = 0; // 读取数据 ret = read(fd, buf, 1); if (ret != 1) { printf("read data failed, in read_data_from_mpu6050\n"); return -1; // 读取失败,返回错误 } *pdata = buf[0]; // 将读取的数据存储到 pdata 中 return 0; // 成功读取数据,返回0 } // 向MPU6050写入数据的函数 static int write_data_to_mpu6050(int fd, unsigned char reg, unsigned char data) { unsigned char buf[2] = {reg, data}; // 准备要写入的寄存器地址和数据 int ret = 0; // 写入数据 ret = write(fd, buf, 2); if (ret != 2) { printf("write data failed, in write_data_to_mpu6050\n"); return -1; // 写入失败,返回错误 } return 0; // 成功写入数据,返回0 } // 初始化MPU6050的函数 int init_mpu6050(int fd) { int ret = 0; // 使用ioctl配置I2C设备,将 I2C 设备配置为使用 7 位地址模式 ret = ioctl(fd, I2C_TENBIT, 0); if (ret < 0) { printf("ioctl I2C_TENBIT failed, in init_mpu6050\n"); return -1; // 配置失败,返回错误 } //设置I2C从设备地址,将 I2C 设备的从设备地址设置为 0x68 ret = ioctl(fd, I2C_SLAVE, 0x68); if (ret < 0) { printf("ioctl I2C_TENBIT failed, in init_mpu6050\n"); return -1; // 配置失败,返回错误 } // 向MPU6050写入初始化数据,设置各个寄存器的值 ret = write_data_to_mpu6050(fd, PWR_MGMT_1, 0x00); ret += write_data_to_mpu6050(fd, SMPLRT_DIV, 0x07); ret += write_data_to_mpu6050(fd, ACCEL_CONFIG, 0x19); ret += write_data_to_mpu6050(fd, GYRO_CONFIG, 0xF8); if (ret < 0) { printf("write init data to mpu6050 failed, in init_mpu6050\n"); return -1; // 写入初始化数据失败,返回错误 } return 0; // 初始化成功,返回0 } // 读取MPU6050加速度计X轴数据的函数 int read_accelx(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; // 从MPU6050读取低位数据 ret = read_data_from_mpu6050(fd, ACCEL_XOUT_L, &d); val = d; // 从MPU6050读取高位数据 ret = read_data_from_mpu6050(fd, ACCEL_XOUT_H, &d); val |= d << 8; if (ret < 0) { printf("read accel x value failed, in read_accelx\n"); return -1; // 读取失败,返回错误 } else { return val; // 返回读取到的加速度计X轴数据 } } int read_accely(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,ACCEL_YOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,ACCEL_YOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read accel y value failed,in read_accely\n"); return -1; } else { return val; } } int read_accelz(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read accel z value failed,in read_accelz\n"); return -1; } else { return val; } } int read_temp(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,TEMP_OUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,TEMP_OUT_H,&d); val |= d << 8; if(ret < 0) { printf("read temp value failed,in read_temp\n"); return -1; } else { return val; } } int read_gyrox(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,GYRO_XOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,GYRO_XOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read gyro x value failed,in read_gyrox\n"); return -1; } else { return val; } } int read_gyroy(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,GYRO_YOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,GYRO_YOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read gyro y value failed,in read_gyroy\n"); return -1; } else { return val; } } int read_gyroz(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,GYRO_ZOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,GYRO_ZOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read gyro z value failed,in read_gyroz\n"); return -1; } else { return val; } }
main.c
#include "mpu6050.h" int main(int argc, char *argv[]) { int fd = -1; if(argc < 2) { printf("Argument is too few\n"); return 0; } /* open */ fd = open(argv[1], O_RDWR); if(fd < 0) { printf("open %s failed\n", argv[1]); return -1; } /* init mpu6050 */ init_mpu6050(fd); while(1) { sleep(2); /* read and printf data from mpu6050 */ printf("Accel-X : 0x%x\n", read_accelx(fd)); printf("Accel-Y : 0x%x\n", read_accely(fd)); printf("Accel-Z : 0x%x\n", read_accelz(fd)); printf("Temp : 0x%x\n", read_temp(fd)); printf("Gyrox-X : 0x%x\n", read_gyrox(fd)); printf("Gyroy-X : 0x%x\n", read_gyroy(fd)); printf("Gyroz-X : 0x%x\n", read_gyroz(fd)); } /* close */ close(fd); fd = -1; return 0; }
mpu6050.h
#ifndef MPU_6050_H #define MPU_6050_H #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int init_mpu6050(int fd); int read_accelx(int fd); int read_accely(int fd); int read_accelz(int fd); int read_temp(int fd); int read_gyrox(int fd); int read_gyroy(int fd); int read_gyroz(int fd); #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #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 #define I2C_SLAVE 0x0703 /* 使用此从设备地址 */ #define I2C_TENBIT 0x0704 /* 0 表示 7 位地址,非 0 表示 10 位地址 */ #define I2C_RDWR 0x0707 /* 组合的读/写传输(只有一个 STOP) */ struct i2c_msg { unsigned short addr; /* 从设备地址 */ unsigned short flags; #define I2C_M_TEN 0x0010 /* 这是十位地址的芯片地址 */ #define I2C_M_RD 0x0001 /* 读取数据,从从设备传输到主设备 */ unsigned short len; /* 消息长度 */ unsigned char *buf; /* 指向消息数据的指针 */ }; /* 此结构在 I2C_RDWR ioctl 调用中使用 */ struct i2c_rdwr_ioctl_data { struct i2c_msg *msgs; /* 指向 i2c_msg 的指针 */ unsigned int nmsgs; /* i2c_msg 的数量 */ }; #endif
mpu6050_op_ioctl.c
#include "mpu6050.h" static int read_data_from_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char *pdata) { struct i2c_rdwr_ioctl_data work = {NULL}; struct i2c_msg msgs[2] = {{0}}; unsigned char buf1[1] = {reg}; unsigned char buf2[1] = {0}; int ret = 0; work.msgs = msgs; work.nmsgs = 2; msgs[0].addr = slave; msgs[0].flags = 0; msgs[0].buf = buf1; msgs[0].len = 1; msgs[1].addr = slave; msgs[1].flags = I2C_M_RD; msgs[1].buf = buf2; msgs[1].len = 1; ret = ioctl(fd,I2C_RDWR,&work); if(ret < 0) { printf("ioctl I2C_RDWR failed,in read_data_from_mpu6050\n"); return -1; } else { *pdata = buf2[0]; return 0; } } static int write_data_to_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char data) { struct i2c_rdwr_ioctl_data work = {NULL}; struct i2c_msg msg = {0}; unsigned char buf[2] = {reg,data}; int ret = 0; work.msgs = &msg; work.nmsgs = 1; msg.addr = slave; msg.flags = 0; msg.buf = buf; msg.len = 2; ret = ioctl(fd,I2C_RDWR,&work); if(ret < 0) { printf("ioctl I2C_RDWR failed,in write_data_to_mpu6050\n"); return -1; } else { return 0; } } int init_mpu6050(int fd) { int ret = 0; ret = ioctl(fd,I2C_TENBIT,0); if(ret < 0) { printf("ioctl I2C_TENBIT failed,in init_mpu6050\n"); return -1; } ret = ioctl(fd,I2C_SLAVE,0x68); if(ret < 0) { printf("ioctl I2C_TENBIT failed,in init_mpu6050\n"); return -1; } ret = write_data_to_mpu6050(fd,0x68,PWR_MGMT_1,0x00); ret += write_data_to_mpu6050(fd,0x68,SMPLRT_DIV,0x07); ret += write_data_to_mpu6050(fd,0x68,ACCEL_CONFIG,0x19); ret += write_data_to_mpu6050(fd,0x68,GYRO_CONFIG,0xF8); if(ret < 0) { printf("write init data to mpu6050 failed,in init_mpu6050\n"); return -1; } return 0; } int read_accelx(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read accel x value failed,in read_accelx\n"); return -1; } else { return val; } } int read_accely(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read accel y value failed,in read_accely\n"); return -1; } else { return val; } } int read_accelz(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read accel z value failed,in read_accelz\n"); return -1; } else { return val; } } int read_temp(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_H,&d); val |= d << 8; if(ret < 0) { printf("read temp value failed,in read_temp\n"); return -1; } else { return val; } } int read_gyrox(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read gyro x value failed,in read_gyrox\n"); return -1; } else { return val; } } int read_gyroy(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read gyro y value failed,in read_gyroy\n"); return -1; } else { return val; } } int read_gyroz(int fd) { unsigned short val = 0; unsigned char d = 0; int ret = 0; ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_L,&d); val = d; ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_H,&d); val |= d << 8; if(ret < 0) { printf("read gyro z value failed,in read_gyroz\n"); return -1; } else { return val; } }
main.c
#include "mpu6050.h" int main(int argc,char *argv[]) { int fd = -1; if(argc < 2) { printf("Argument is too few\n"); return 1; } /*open*/ fd = open(argv[1],O_RDWR); if(fd < 0) { printf("open %s failed\n",argv[1]); return 2; } /*init mpu6050*/ init_mpu6050(fd); while(1) { sleep(2); /*read and print data from 6050*/ printf("Accel-X:0x%x\n",read_accelx(fd)); printf("Accel-Y:0x%x\n",read_accely(fd)); printf("Accel-Z:0x%x\n",read_accelz(fd)); printf("Temp:0x%x\n",read_temp(fd)); printf("GYRO-X:0x%x\n",read_gyrox(fd)); printf("GYRO-Y:0x%x\n",read_gyroy(fd)); printf("GYRO-z:0x%x\n",read_gyroz(fd)); printf("\n"); } /*close*/ close(fd); fd = -1; return 0; }
查阅原理图以便得知二级外设挂在哪条I2C总线上、二级外设的身份标识(二级外设自身的地址)
参照platform样式搭建二级外设驱动框架
查询二级外设芯片手册以便得知驱动需要用到的寄存器地址
注意:
参照字符驱动完成其余代码编写
创建对应的i2c_client对象
linux-3.14\Documentation\i2c\instantiating-devices
匹配方式:
名称匹配
设备树匹配
ACPI匹配
Advanced Configuration and Power Management Interface 高级配置和电源管理接口
PC机平台采用的一种硬件配置接口
i2c二级外设驱动框架:
//其它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对象,同时对其主要成员进行初始化*/ 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); MODULE_LICENSE("GPL");
这种匹配方式需要自己创建i2c_client对象
创建i2c_client对象有三种方式:
1. 当开发板上电内核跑起来的时候,肯定是架构相关的程序首先运行,也就是mach-xxx.c
2. mach-xxx.c文件里首先会定义i2c_board_info的结构体数组,在mach-xxx.c的初始化函数里调用
i2c_register_board_info函数把i2c_board_inifo链接进内核的i2c_board_list链表当中去
3. 在驱动i2c目录下和开发板板对应的驱动文件i2c-xxx.c里,创建i2c_adapter对象
4. 这种方式严重依赖平台,缺乏灵活性,基本会被遗弃
i2c二级外设client框架:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c.h> static struct i2c_board_info mpu6050_info = { I2C_BOARD_INFO("mpu6050",二级外设地址) }; static struct i2c_client *mpu6050_client; static int __init mpu6050_dev_init(void) { struct i2c_adapter *padp = NULL; padp = i2c_get_adapter(i2c通道编号); mpu6050_client = i2c_new_device(padp,&mpu6050_info); i2c_put_adapter(padp); return 0; } module_init(mpu6050_dev_init); static void __exit mpu6050_dev_exit(void) { i2c_unregister_device(mpu6050_client); } module_exit(mpu6050_dev_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) #endif
mpu6050_client.c
#include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c.h> static struct i2c_board_info mpu6050_info = { I2C_BOARD_INFO("mpu6050",0x68) }; static struct i2c_client *gpmpu6050_client = NULL; static int __init mpu6050_client_init(void) { struct i2c_adapter *padp = NULL; padp = i2c_get_adapter(5); gpmpu6050_client = i2c_new_device(padp,&mpu6050_info); i2c_put_adapter(padp); return 0; } static void mpu6050_client_exit(void) { i2c_unregister_device(gpmpu6050_client); } module_init(mpu6050_client_init); module_exit(mpu6050_client_exit); MODULE_LICENSE("GPL");
mpu6050_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/mm.h> #include <linux/io.h> #include <asm/uaccess.h> #include <asm/atomic.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(不自检,2G,5Hz) #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地址 #define PWR_MGMT_1 0x6B int major = 11; int minor = 0; int mpu6050_num = 1; struct mpu6050_dev { struct cdev mydev; struct i2c_client *pclt; }; 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} }; 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,mpu6050_num,"mpu6050"); if(ret) { ret = alloc_chrdev_region(&devno,minor,mpu6050_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,mpu6050_num); printk("kmalloc 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,mpu6050_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,mpu6050_num); kfree(pgmydev); pgmydev = NULL; return 0; } 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 0 int __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); #else module_i2c_driver(mpu6050_driver); #endif MODULE_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.h" int 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-x=0x%x\n",data.accel.x); printf("Accel-y=0x%x\n",data.accel.y); printf("Accel-z=0x%x\n",data.accel.z); ioctl(fd,GET_GYRO,&data); printf("Gyro-x=0x%x\n",data.gyro.x); printf("Gyro-y=0x%x\n",data.gyro.y); printf("Gyro-z=0x%x\n",data.gyro.z); ioctl(fd,GET_TEMP,&data); printf("Temp=0x%x\n",data.temp); printf("\n"); } close(fd); fd = -1; return 0; }
输出结果:
i2c二级外设client框架:不明确二级外设地址,但是知道是可能几个值之一的情况下可用
#include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c.h> static const unsigned short addr_list[] = { 0x68, //..... I2C_CLIENT_END }; static struct i2c_client *mpu6050_client; static int __init mpu6050_dev_init(void) { struct i2c_adapter *padp = NULL; struct i2c_board_info mpu6050_info = {""}; strcpy(mpu6050_info.type,"mpu6050"); padp = i2c_get_adapter(i2c通道编号); mpu6050_client = i2c_new_probed_device(padp,&mpu6050_info,addr_list,NULL); i2c_put_adapter(padp); if(mpu6050_client != NULL) { return 0; } else { return -ENODEV; } } module_init(mpu6050_dev_init); static void __exit mpu6050_dev_exit(void) { i2c_unregister_device(mpu6050_client); } module_exit(mpu6050_dev_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) #endif
mpu6050_client_probed.c
#include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c.h> static unsigned short mpu6050_addr_list[] = { 0x68, 0x69, I2C_CLIENT_END }; 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); 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");
mpu6050_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/mm.h> #include <linux/io.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include "mpu6050.h" #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #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 int major = 11; int minor = 0; int mpu6050_num = 1; struct mpu6050_dev { struct cdev mydev; struct i2c_client *pclt; }; 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} }; 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,mpu6050_num,"mpu6050"); if(ret) { ret = alloc_chrdev_region(&devno,minor,mpu6050_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,mpu6050_num); printk("kmalloc 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,mpu6050_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,mpu6050_num); kfree(pgmydev); pgmydev = NULL; return 0; } 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 0 int __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); #else module_i2c_driver(mpu6050_driver); #endif MODULE_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.h" int 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-x=0x%x\n",data.accel.x); printf("Accel-y=0x%x\n",data.accel.y); printf("Accel-z=0x%x\n",data.accel.z); ioctl(fd,GET_GYRO,&data); printf("Gyro-x=0x%x\n",data.gyro.x); printf("Gyro-y=0x%x\n",data.gyro.y); printf("Gyro-z=0x%x\n",data.gyro.z); ioctl(fd,GET_TEMP,&data); printf("Temp=0x%x\n",data.temp); printf("\n"); } close(fd); fd = -1; return 0; }
输出结果:
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) #endif
mpu6050_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/mm.h> #include <linux/io.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include "mpu6050.h" #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #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 int major = 11; int minor = 0; int mpu6050_num = 1; struct mpu6050_dev { struct cdev mydev; struct i2c_client *pclt; }; 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} }; 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,mpu6050_num,"mpu6050"); if(ret) { ret = alloc_chrdev_region(&devno,minor,mpu6050_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,mpu6050_num); printk("kmalloc 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,mpu6050_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,mpu6050_num); kfree(pgmydev); pgmydev = NULL; return 0; } 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, }; #if 0 int __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); #else module_i2c_driver(mpu6050_driver); #endif MODULE_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.h" int 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-x=0x%x\n",data.accel.x); printf("Accel-y=0x%x\n",data.accel.y); printf("Accel-z=0x%x\n",data.accel.z); ioctl(fd,GET_GYRO,&data); printf("Gyro-x=0x%x\n",data.gyro.x); printf("Gyro-y=0x%x\n",data.gyro.y); printf("Gyro-z=0x%x\n",data.gyro.z); ioctl(fd,GET_TEMP,&data); printf("Temp=0x%x\n",data.temp); printf("\n"); } close(fd); fd = -1; return 0; }
输出结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。