#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include "log.h"
#include "mpu6050_reg.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kevin Liu");
struct axis_data {
s16 value;
int standby;
};
struct sub_sensor {
int st; // self-test
int reset; // reset
int sel; // full scale range
struct axis_data x;
struct axis_data y;
struct axis_data z;
};
struct temp_sensor {
int enable;
int reset;
s16 value;
};
struct pwr_mgmt {
int reset;
int sleep;
int cycle;
int cycle_HZ;
int clksel;
int all_standby;
};
struct mpu6050_data {
struct mutex lock;
struct i2c_client *client;
struct delayed_work work;
struct workqueue_struct *wq;
int delay_ms;
struct sub_sensor gyro;
struct sub_sensor accel;
struct temp_sensor temp_s;
struct pwr_mgmt power;
int dlph;
int dhph;
};
enum {
RANGE,
LSB
};
static float accel_sel[][2] = {
{2, 16384},
{4, 8192},
{8, 4096},
{16, 2048}
};
static float gyro_sel[][2] = {
{250, 131},
{500, 65.5},
{1000, 32.8},
{2000, 16.4}
};
static void mpu6050_enable(struct mpu6050_data *mpu6050)
{
struct i2c_client *client = mpu6050->client;
i2c_smbus_write_byte_data(client, MPU6050_REG_PWR_MGMT_1, 0);
}
static void mpu6050_disable(struct mpu6050_data *mpu6050)
{
struct i2c_client *client = mpu6050->client;
i2c_smbus_write_byte_data(client, MPU6050_REG_PWR_MGMT_1,
1 << PWR_1_SLEEP_OFFSET);
}
static void mpu6050_reset(struct mpu6050_data *mpu6050)
{
struct i2c_client *client = mpu6050->client;
i2c_smbus_write_byte_data(client, MPU6050_REG_PWR_MGMT_1,
1 << PWR_1_DEVICE_RESET_OFFSET);
}
/*
* Get gyro/accel/temprature data
* @type : 0 - gyro
* 1 - accel
* 2 - temprature
*/
static int mpu6050_read_data(struct mpu6050_data *mpu6050, int type)
{
s16 values[3];
int i, addr, ret;
struct i2c_client *client = mpu6050->client;
switch(type) {
case 0:
addr = MPU6050_REG_GYRO_XOUT_H;
break;
case 1:
addr = MPU6050_REG_ACCEL_XOUT_H;
break;
case 2:
addr = MPU6050_REG_TEMP_OUT_H;
break;
default:
addr = MPU6050_REG_GYRO_XOUT_H;
break;
}
if (type == 0 || type == 1) {
ret = i2c_smbus_read_i2c_block_data(client, addr,
6, (u8 *)values);
if (ret < 0) {
E("error read gyro\n");
return ret;
}
for (i = 0; i < 3; i++) {
values[i] = be16_to_cpu(values[i]);
}
} else if (type == 2) {
ret = i2c_smbus_read_i2c_block_data(client, addr,
2, (u8 *)values);
if (ret < 0) {
E("error read gyro\n");
return ret;
}
for (i = 0; i < 1; i++) {
values[i] = be16_to_cpu(values[i]);
}
}
switch(type) {
case 0:
mpu6050->gyro.x.value = values[0];
mpu6050->gyro.y.value = values[1];
mpu6050->gyro.z.value = values[2];
break;
case 1:
mpu6050->accel.x.value = values[0];
mpu6050->accel.y.value = values[1];
mpu6050->accel.z.value = values[2];
break;
case 2:
mpu6050->temp_s.value = values[0];
break;
default:
break;
}
return 0;
}
static int mpu6050_read_gyro(struct mpu6050_data *mpu6050)
{
return mpu6050_read_data(mpu6050, 0);
}
static int mpu6050_read_accel(struct mpu6050_data *mpu6050)
{
return mpu6050_read_data(mpu6050, 1);
}
static int mpu6050_read_temprature(struct mpu6050_data *mpu6050)
{
return mpu6050_read_data(mpu6050, 2);
}
static void mpu6050_dump_all(struct mpu6050_data *mpu6050)
{
D("Gyro(X:%d Y:%d Z:%d)\tAccel(X:%d Y:%d Z:%d)\tTemp:%d\n",
mpu6050->gyro.x.value, mpu6050->gyro.y.value,
mpu6050->gyro.z.value, mpu6050->accel.x.value, mpu6050->accel.y.value,
mpu6050->accel.z.value, mpu6050->temp_s.value);
}
static void mpu6050_work(struct work_struct *work)
{
int ret;
struct mpu6050_data *mpu6050 = container_of(
(struct delayed_work *)work, struct mpu6050_data, work);
mpu6050_read_gyro(mpu6050);
mpu6050_read_accel(mpu6050);
mpu6050_read_temprature(mpu6050);
mpu6050_dump_all(mpu6050);
schedule_delayed_work(&mpu6050->work,
msecs_to_jiffies(mpu6050->delay_ms));
}
static int mpu6050_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mpu6050_data *mpu6050;
u16 version;
D("Probe match happend, ID %s\n", id->name);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
E("I2C check error\n");
return -EINVAL;
}
mpu6050 = kzalloc(sizeof(*mpu6050), GFP_KERNEL); //申请内存
if (!mpu6050) {
E("Mem error\n");
return -ENOMEM;
} else
D("Alloc OK\n");
mpu6050->client = client;
i2c_set_clientdata(client, mpu6050); //mmpu6050在clent中注册
mutex_init(&mpu6050->lock);
mpu6050->delay_ms = 1000;
D("Set OK\n");
INIT_DELAYED_WORK(&mpu6050->work, mpu6050_work);
D("Work queue OK\n");
//INIT_DELAYED_WORK 初始化带延时的工作队列work,将mpu6050_work这个函数放到工作队列中,然后等到调用schedule_delayed_work时执行。
version = i2c_smbus_read_byte_data(client, MPU6050_REG_WHO_AM_I);
if (version != 0x68) {
E("Version check error 0x%X, skip\n", version);
goto free_all;
} else
D("Version Check OK\n");
// 读ID
mpu6050_reset(mpu6050);
mpu6050_enable(mpu6050);
schedule_delayed_work(&mpu6050->work,
msecs_to_jiffies(mpu6050->delay_ms));
//这里调用异步执行mpu6050_work这个函数。
return 0;
free_all:
kfree(mpu6050);
E("A oh!!!ooops...\n");
return -EINVAL;
}
static int mpu6050_remove(struct i2c_client *client)
{
struct mpu6050_data *mpu6050 = i2c_get_clientdata(client);
mpu6050_disable(mpu6050);
cancel_delayed_work(&mpu6050->work);
kfree(mpu6050);
return 0;
}
static struct i2c_device_id mpu6050_ids[] = {
{SENSOR_NAME, 0},
{ },
};
static struct i2c_driver mpu6050_driver = {
.driver = {
.name = SENSOR_NAME,
.owner = THIS_MODULE,
},
.class = I2C_CLASS_HWMON,
.id_table = mpu6050_ids,
.probe = mpu6050_probe,
.remove = mpu6050_remove,
};
/*上面定义i2c_driver结构体,整个文件的目的就是实现i2c_driver结构体,并通过module_i2c_driver
注册i2c驱动,当i2c_driver和i2c_client的name一样,系统就对其进行probe,也就是运行mpu6050_probe函数。*/
module_i2c_driver(mpu6050_driver);
查看整个驱动,实现了i2c_driver,挂载了i2c设备获得了i2c_cilent,使用了工作队列,实现数据的延时连续读取。这里分别对mpu_client.c 和mpu_driver.c进行编译得到mpu_client.ko,mpu_driver.ko。先加载mpu_client,再加载mpu_driver,得到:
可以看到驱动确实在工作,在不断获取陀螺仪数据,这里并没有将数据进行转化。
- MPU6050带字符驱动的i2c从设备驱动1
开干: 1.闲言碎语 这个驱动,越写觉的越简单,入门难,入门之后感觉还好.Linux开发还是比较友好的. 2.编写MPU6050带字符驱动的i2c从设备驱动 要实现的功能就是,将MPU6050作为字符 ...
- 【Linux高级驱动】linux设备驱动模型之平台设备驱动机制
[1:引言: linux字符设备驱动的基本编程流程] 1.实现模块加载函数 a.申请主设备号 register_chrdev(major,name,file_operations); b.创 ...
- Linux中总线设备驱动模型及平台设备驱动实例
本文将简要地介绍Linux总线设备驱动模型及其实现方式,并不会过多地涉及其在内核中的具体实现,最后,本文将会以平台总线为例介绍设备和驱动程序的实现过程. 目录: 一.总线设备驱动模型总体介绍及其实现方 ...
- 【linux驱动分析】misc设备驱动
misc设备驱动.又称混杂设备驱动. misc设备驱动共享一个设备驱动号MISC_MAJOR.它在include\linux\major.h中定义: #define MISC_MAJO ...
- Linux驱动之I2C总线设备以及驱动
[ 导读] 本文通过阅读内核代码,来梳理一下I2C子系统的整体视图.在开发I2C设备驱动程序时,往往缺乏对于系统整体的认识,导致没有一个清晰的思路.所以从高层级来分析一下I2C系统的设计思路,将有助于 ...
- Linux 驱动框架---cdev字符设备驱动和misc杂项设备驱动
字符设备 Linux中设备常见分类是字符设备,块设备.网络设备,其中字符设备也是Linux驱动中最常用的设备类型.因此开发Linux设备驱动肯定是要先学习一下字符设备的抽象的.在内核中使用struct ...
- Linux设备驱动编程之复杂设备驱动
这里所说的复杂设备驱动涉及到PCI.USB.网络设备.块设备等(严格意义而言,这些设备在概念上并不并列,例如与块设备并列的是字符设备,而PCI.USB设备等都可能属于字符设备),这些设备的驱动中又涉及 ...
- Linux驱动编写(块设备驱动代码)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 按照ldd的说法,linux的设备驱动包括了char,block,net三种设备.char设备 ...
- Linux gadget驱动分析3------复合设备驱动
windows上面对usb复合设备的识别需要下面条件. “ 如果设备满足下列要求,则总线驱动程序还会报告 USB\COMPOSITE 的兼容标识符: 设备描述符的设备类字段 (bDeviceClass ...
随机推荐
- CODEVS 2455 繁忙的都市 SCOI2005(洛谷 P2330)
题目描述 Description 城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造.城市C的道路是这样分布的:城市中有n个交叉路口,有些交叉路口之间有道路相连,两 ...
- LeetCode(976. 三角形的最大周长)
问题描述 给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的.面积不为零的三角形的最大周长. 如果不能形成任何面积不为零的三角形,返回 0. 示例 1: 输入:[2,1,2] 输出:5 ...
- BZOJ3877 : [Ahoi2014&Jsoi2014]保龄球
考虑从前往后放所有轮. 如果上一轮是全中: 那么这一轮如果是补中,一定放第一次最小的,这样可以让第一次大的放在其它补中之后. 如果这一轮是失误,那么一定放总分最大的. 如果上一轮是补中: 这一轮一定放 ...
- python网络编程(十三)
协程-greenlet版 为了更好使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变的更加简单 安装方式 使用如下命令安装greenlet模块: sudo pip ...
- 游戏UI规范
在满足效果的前提下,尽量做到UI资源做到复用和最小化 1. 背景1和背景2分开切,可以组合成各种不同的面包背景图 2. 背景1和背景2在没有花纹的情况下,中间纯色的部分切4个像素做就公共个缩放就可 ...
- Html表单标签:
表单用于收集不同的类型的用户输入,表单由不同类型的标签组成,相关标签及属性如下: (1)<form>标签 定义整体的表单区域 -- action属性 定义表单数据提交址址 -- metho ...
- Java中map集合系列原理剖析
看了下JAVA里面有HashMap.Hashtable.HashSet三种hash集合的实现源码,这里总结下,理解错误的地方还望指正 HashMap和Hashtable的区别 HashSet和Hash ...
- quartz详解2:quartz由浅入深
http://blog.itpub.net/11627468/viewspace-1763498/ 一.quartz核心概念 先来看一张图: scheduler 任务调度器 trigger 触 ...
- 重写父类、多线程、多进程、logging模块
1. 重写父类 1) 子类定义父类同名函数后,父类函数被覆盖: 2) 如果需要,可以在子类中调用父类方法:”父类名.方法(self)”.”父类名().方法()”.”super(子类名,self). ...
- oracle增删改字段
添加字段的语法:alter table tablename add (column datatype [default value][null/not null],….); 修改字段的语法:alter ...