#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 ...
随机推荐
- Maya插件开发的几种方式归纳
目前仅仅是一时兴趣,想要探索一下Maya插件开发的具体方法,但因为没有时间所以只起了个头 首先来到Autodesk Developer Network http://usa.autodesk.com/ ...
- Codeforces.472F.Design Tutorial: Change the Goal(构造 线性基 高斯消元)
题目链接 \(Description\) 给定两个长为\(n\)的数组\(x_i,y_i\).每次你可以选定\(i,j\),令\(x_i=x_i\ \mathbb{xor}\ x_j\)(\(i,j\ ...
- Victoria的舞会2——图的连通性及连通分量
[Vijos1022]]Victoria的舞会2 Description Victoria是一位颇有成就的艺术家,他因油画作品<我爱北京天安门>闻名于世界.现在,他为了报答帮助他的同行们, ...
- BZOJ1423 : Optimus Prime
设$f[x]$表示为了保证自己可以取到质数$x$,第一步在$[0,n]$中可以选的数是多少. 这个数是唯一的,因为如果存在两个$f[x]=a,b(a<b)$,那么如果先手取了$a$,后手就能取$ ...
- C++程序设计方法5:接口设计实例
//例子:旋转方阵填充,要求矩阵大小任意,尺寸在运行时输入 设计思路:从上到下 int main() { cout << "Please input N:"; int ...
- ae:org.apache.shiro.authc.AuthenticationException: Authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken] could not be authenticated by any configured realms. Please ensure
看问题是说要保证有一个realm正确,但是之前运行就一直成功,后来就不行了.有可能是:以为自己输入了正确的用户名和密码,结果是因为用户名前面添加了空格导致了这个错误发生. 实质问题是:你输入的用户名和 ...
- rem自适应手机端布局
通过js根据屏幕设备尺寸的大小,改变根元素的值: <script> var html = document.querySelector("html"); var rem ...
- redis:set集合类型的操作(无序集合)
1. set集合类型的操作(无序集合) 集合具有无序性(没有顺序).确定性(描述是确定的).唯一性(没有重复的元素) 1.1. sadd key member [member ...] 语法:sadd ...
- PHP02
PHP02 1.虚拟主机配置完毕后,机器上的ip和localhost都会默认直接请求第一个虚拟主机 2.解析文本文件显示表格 将文本文件中的数据呈现在一个表格中 1)读取文件内容 包含文本的字符串数据 ...
- [MySQL]查看用户权限与GRANT用法
摘自:http://apps.hi.baidu.com/share/detail/15071849 查看用户权限 show grants for 你的用户 比如:show grants for roo ...