#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 ...
随机推荐
- java菜鸟之微信分享
前言:我终于理解了什么叫做教科书:教科书就是把一些简单容易的知识写成一堆谁都看不懂的书,这,就简称“教科书” 这些天接触到微信分享以及回调的问题,因为之前没接触过,所以这次做这个分享,碰了一点点壁,特 ...
- PIVOT和UNPIVOT使用详解
一.使用PIVOT实现数据表的列转行 建表语句: DROP TABLE STUDENT; CREATE TABLE STUDENT ( 学生编号 BYTE) NULL , 姓名 BYTE) NULL ...
- docker 删除所有正在运行的容器
docker images docker rmi wordpress docker rm -f `docker ps -a -q` 删除正在运行的镜像: docker rmi -f ubuntu:1. ...
- LINUX文件内容处理及文本编辑器vim
Linux基本操作命令 echo命令 echo命令: 把echo后面的内容显示到屏幕. -n 显示内容的时候不显示每行结尾的回车 echo 内容 >> 文件名 表示把内容加到一个文件的末尾 ...
- 去重+排序板子(set+map)
题意: 给定n个数,去重排序后输出个数和每个数 1.map实现 #pragma GCC optimize("O2") #include<iostream> #inclu ...
- 3ds max学习笔记(十一)-- 修改器
1.修改器列表: 将常用的修改器放放置在集里: 1.点击[配置]按钮,勾选[显示按钮] 选择[配置修改器集],在新出的弹窗里通过左右拖拽进行设置: 2,选择之后,点击[确定]进行保存:
- 深入理解JVM(9)——类加载的过程
加载是类加载的第一步. 一.加载 a)加载的过程 1)通过一个类的全限定名获取这个类的二进制字节流,也就是class文件 2)将二进制字节流的存储结构转换为特定的数据结构,存储在方法区 3)在内存中创 ...
- [MySQL]查看用户权限与GRANT用法
摘自:http://apps.hi.baidu.com/share/detail/15071849 查看用户权限 show grants for 你的用户 比如:show grants for roo ...
- gitlab简单使用教程【转】
平时一直是用git来管理代码仓库,也用过一段时间github,但是github免费版不能建私有仓库.后来转到了bitbucket,后来被atlassian收购后有点不适应,而且在国内访问经常连不上.还 ...
- NLP第9章 NLP 中用到的机器学习算法——基于统计学(文本分类和文本聚类)