网站建设开发公司微信公众号开发,网络架构图,解决网,梅州建站公司一、I2C总线背景知识
SOC芯片平台的外设分为#xff1a;
一级外设#xff1a;外设控制器集成在SOC芯片内部二级外设#xff1a;外设控制器由另一块芯片负责#xff0c;通过一些通讯总线与SOC芯片相连 Inter-Integrated Circuit#xff1a; 字面意思是用于“集成电路之间…一、I2C总线背景知识
SOC芯片平台的外设分为
一级外设外设控制器集成在SOC芯片内部二级外设外设控制器由另一块芯片负责通过一些通讯总线与SOC芯片相连 Inter-Integrated Circuit 字面意思是用于“集成电路之间”的通信总线简写IIC(或者I2C) i2c传输的要点就是 传输一个字节 后面必然紧跟一个响应信号----应答信号.这个响应信号可能来自主机或者是从机具体是谁就要看传输方向。 传输方向分两种情况(每种情况又有两种可能: A无应答和 B有应答)
1.主机-从机主机对从机发一个字节之后主机要读取从机的响应信号(主机读SDA线)
A) 主机读SDA为高电平说明从机无应答意味着从机接收完毕主机发送停止信号
B) 主机读SDA为低电平说明从机有应答。可继续发送下一个字节2.从机-主机 主机读取从机一个字节之后主机要向从机发送一个响应信号(主机写SDA线)
A) 主机写SDA为高电平从机收到主机的无应答信号之后从机停止传输等待主机的停止信号。
B) 主机写SDA为低电平从机收到主机的应答信号之后从机继续输出下一字节二、Exynos4412 I2C收发实现之裸机版
I2CCON寄存器控制寄存器 第7位决定是否允许产生应答信号无论发送还是接收前需置1
第6位传输时时钟线分频一般选置1
第5位决定是否开启发送或接收结束时发通知无论发送还是接收前需置1
第4位接收或发送是否完毕可以通过检查此位是否为1接收或发送完毕后需置0
I2CSTAT寄存器状态寄存器
第6、7位每次传输前需选择传输模式
第5位置0产生将产生终止信号传输前置1产生起始信号
第4位使能数据输出传输前需置1
I2CDS寄存器数据寄存器发送前被发送的数据存放处接收后结果也从此处读取
2.1 发送 void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
{// 从设备寻址I2C5.I2CDS slave_addr;I2C5.I2CCON 17 | 16 | 15; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX */I2C5.I2CSTAT 0x3 6 | 15 | 14; /* 主传输模式, 启动, 启用 RX/TX */while(!(I2C5.I2CCON (14)));I2C5.I2CDS addr;I2C5.I2CCON ~(14); // 清除挂起位以恢复while(!(I2C5.I2CCON (14)));// 发送数据I2C5.I2CDS data; // 数据I2C5.I2CCON ~(14); // 清除挂起位以恢复while(!(I2C5.I2CCON (14)));I2C5.I2CSTAT 0xD0; // 停止I2C5.I2CCON ~(14); // 清除挂起位以恢复mydelay_ms(10);
}
2.2 接收 void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data)
{// 从设备寻址I2C5.I2CDS slave_addr;I2C5.I2CCON 17 | 16 | 15; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX 中断使能 */I2C5.I2CSTAT 0x3 6 | 15 | 14; /* 主传输模式, 启动, 启用 RX/TX */while(!(I2C5.I2CCON (14))); /* 对应位为1表示slave_addr传输完成线路处于挂起状态 */I2C5.I2CDS addr;I2C5.I2CCON ~(14); // 清除挂起位以继续传输while(!(I2C5.I2CCON (14)));I2C5.I2CSTAT 0xD0; // 停止 第5位写0表示要求产生stop信号// 接收数据I2C5.I2CDS slave_addr | 0x01; // 读取I2C5.I2CCON 17 | 16 | 15; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX 中断使能 */I2C5.I2CSTAT 26 | 15 | 14; /* 主接收模式, 启动, 启用 RX/TX, 0xB0 */while(!(I2C5.I2CCON (14)));I2C5.I2CCON ~((17) | (14)); /* 恢复操作 无 ACK */while(!(I2C5.I2CCON (14)));I2C5.I2CSTAT 0x90; // 停止 第5位写0表示要求产生stop信号I2C5.I2CCON ~(14); /* 清除中断挂起位 */*data I2C5.I2CDS;mydelay_ms(10);
}
三、Linux内核对I2C总线的支持 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);/*
功能根据参数adapinfoaddraddr_list动态创建i2c_client并且进行注册
参数
adapi2c_client所依附的适配器结构地址
infoi2c_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总线上
adi2c_get_adapter(2);//自己填充board_info
strcpy(inf.type,xxxxx);
info.flags0;
//动态创建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总线上
adi2c_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
返回值
失败负数
成功成功发送的字节数
*/
四、MPU6050
三轴角速度三轴加速度温度传感器 #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通道
5.1 预备工作
5.1.1 exynos4412平台每个i2c通道的信息是通过设备树提供的因此需要首先在exynos4412-fs4412.dts中增加5通道的节点 不要忘记
回内核源码顶层目录执行make dtbs将新生成的dtb拷贝到/tftpboot
5.1.2 i2c总线驱动层提供了一个字符设备驱动以便于应用层可以直接通过它去使用i2c总线通讯去操作二级外设但需要
内核编译时添加此字符设备驱动代码(i2c-dev.c),因此需要修改make menuconfig的配置 不要忘记
回内核源码顶层目录执行make uImage将新生成的uImage拷贝到/tftpboot
5.2 应用层直接使用i2c总线的代码实现
缺点
需要应用程序开发人员查阅原理图和芯片手册增加了他们的开发负担开发出的应用程序缺乏可移植性
5.2.1 调用read、write实现接收、发送
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.hint 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(不自检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地址#define I2C_SLAVE 0x0703 /* Use this slave address */
#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, ! 0 for 10 bit */#endifmpu6050_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 设备的从设备地址设置为 0x68ret 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.hint 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;
}5.2.2 调用ioctl实现接收、发送
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.hint 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 的数量 */
};
#endifmpu6050_op_ioctl.c
#include mpu6050.hstatic 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.hint 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总线二级外设驱动开发方法 查阅原理图以便得知二级外设挂在哪条I2C总线上、二级外设的身份标识二级外设自身的地址 参照platform样式搭建二级外设驱动框架 查询二级外设芯片手册以便得知驱动需要用到的寄存器地址 注意 此处寄存器是指二级外设内部的寄存器每个寄存器在芯片手册里有个对应编号也被称为地址但不是内存地址特别提醒此寄存器不是SOC芯片内部参与内存统一编址的寄存器更不是ARM核-CPU的寄存器通过调用i2c_tranfer函数完成与相应寄存器的数据交互 参照字符驱动完成其余代码编写 创建对应的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总线二级外设驱动开发之名称匹配
这种匹配方式需要自己创建i2c_client对象
创建i2c_client对象有三种方式
1. i2c_register_board_info
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. 这种方式严重依赖平台缺乏灵活性基本会被遗弃2. i2c_new_device明确二级外设地址的情况下可用
i2c二级外设client框架
#include linux/kernel.h
#include linux/module.h
#include linux/i2c.hstatic 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_Hstruct 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_client.c
#include linux/kernel.h
#include linux/module.h
#include linux/i2c.hstatic 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(不自检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地址#define PWR_MGMT_1 0x6Bint 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 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;
}
输出结果
3. i2c_new_probed_device不明确二级外设地址
i2c二级外设client框架不明确二级外设地址但是知道是可能几个值之一的情况下可用
#include linux/kernel.h
#include linux/module.h
#include linux/i2c.hstatic 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_Hstruct 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_client_probed.c
#include linux/kernel.h
#include linux/module.h
#include linux/i2c.hstatic 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 0x6Bint 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 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;
}
输出结果
八、I2C总线二级外设驱动开发之设备树匹配 完整代码
mpu6050.h
#ifndef MPU_6050_H
#define MPU_6050_Hstruct 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 0x6Bint 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);
#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;
}
输出结果