做项目的时候遇到了mpu6050这个加速度传感器,今天来复现重新看一下i2c总线下如何添加一个驱动代码。
一、注册
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include "i2c_mpu6050.h"
/*------------------字符设备内容----------------------*/
#define DEV_NAME "I2C4_mpu6050"
#define DEV_CNT (1)
/*定义 led 资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址*/
static dev_t mpu6050_devno; //定义字符设备的设备号
static struct cdev mpu6050_chr_dev; //定义字符设备结构体chr_dev
struct class *class_mpu6050; //保存创建的类
struct device *device_mpu6050; // 保存创建的设备
struct device_node *mpu6050_device_node; //rgb_led的设备树节点结构体
/*------------------IIC设备内容----------------------*/
struct i2c_client *mpu6050_client = NULL; //保存mpu6050设备对应的i2c_client结构体,匹配成功后由.prob函数带回。
static int __init mpu6050_driver_init(void){
int ret;
printk("mpu6050 init\n");
ret = i2c_add_driver(&mpu6050_driver);
return 0;
}
static void __exic mpu6050_driver_exit(void){
printk("mpu6050 exit\n");
i2c_del_driver(&mpu6050_driver);
return;
}
二、设置i2c总线下的结构体,类似platform_dirver
/*定义ID 匹配表*/
static const struct i2c_device_id gtp_device_id[] = {
{"fire,i2c_mpu6050", 0},
{}};
/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
{.compatible = "fire,i2c_mpu6050"},
{/* sentinel */}};
struct i2c_driver mpu6050_driver = {
.probe = mpu6050_probe,
.remove = mpu6050_remove,
.id_table = gtp_device_id,
.driver = {
.name = "fire,i2c_mpu6050",
.owner = THIS_MODULE,
.of_match_table = mpu6050_of_match_table,
},
};
三、提供probe 函数,在probe函数去注册字符设备
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
/* int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name) */
ret = alloc_chrdev_region(&mpu6050_devne, 0, DEV_CNT, DEV_NAME);
/* 这里使用的是cdev_init */
mpu6050_chr_dev.owner = THIS_MODULE;
/* 将fops挂到这个设备 */
cdev_init(&mpu6050_chr_dev, &mpu6050_chr_dev_fops);
ret = cdev_add(&mpu6050_chr_dev, mpu6050_devno, DEV_CNT);
class_mpu6050 = class_create(THIS_MODULE, DEV_NAME);
device_mpu6050 = device_create(class_mpu6050, NULL, mpu6050_devno, NULL, DEV_NAME);
return 0;
}
static int mpu6050_remove(struct i2c_client *client)
{
/*删除设备*/
device_destroy(class_mpu6050, mpu6050_devno); //清除设备
class_destroy(class_mpu6050); //清除类
cdev_del(&mpu6050_chr_dev); //清除设备号
unregister_chrdev_region(mpu6050_devno, DEV_CNT); //取消注册字符设备
return 0;
}
四、提供file_operations结构体
static struct file_operations mpu6050_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = mpu6050_open,
.read = mpu6050_read,
.release = mpu6050_release,
};
4.1 open函数,初始化i2c设备
static int mpu6050_open(struct inode *inode, struct file *filp)
{
// printk("\n mpu6050_open \n");
/*向 mpu6050 发送配置数据,让mpu6050处于正常工作状态*/
mpu6050_init();
return 0;
}
static int mpu6050_init(void)
{
int error = 0;
/*配置mpu6050*/
error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);
if (error < 0)
{
/*初始化错误*/
printk(KERN_DEBUG "\n mpu6050_init error \n");
return -1;
}
return 0;
}
4.2 如何对i2c设备进行初始化?
主要是对mpu6050寄存器的一些配置,写寄存器操作。
这里我们看一下对应的write函数
static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
{
int error;
u8 write_data[2];
struct i2c_msg send_msg;
write_data[0] = address;
write_data[1] = data;
send_msg.addr = mpu6050_client->addr;
send_msg.flag = 0; // 写操作
send_msg.data = &write_data;
send_msg.len = 2;
error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
return 0;
}
五、初始化结束之后就可以对该i2c设备进行读操作了
static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
{
int error;
u8 address_data = address;
struct i2c_msg mpu6050_msg[2];
mpu6050_msg[0].addr = mpu6050_client->addr;
mpu6050_msg[0].flag = 0;
mpu6050_msg[0].data = &address_data;
mpu6050_msg[0].len = 1;
mpu6050_msg[1].addr = mpu6050_client->addr;
mpu6050_msg[1].flag = I2C_M_RD; // 读数据
mpu6050_msg[1].buf = data; //读到的数据保存的位置
mpu6050_msg[1].len = 1;
error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
return 0;
}
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
char data_H;
char data_L;
int error;
short mpu6050_result[6]; //保存mpu6050转换得到的原始数据
// printk("\n mpu6050_read \n");
i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_H, &data_H, 1);
i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_L, &data_L, 1);
mpu6050_result[0] = data_H << 8;
mpu6050_result[0] += data_L;
i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_H, &data_H, 1);
i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_L, &data_L, 1);
mpu6050_result[1] = data_H << 8;
mpu6050_result[1] += data_L;
i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_H, &data_H, 1);
i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_L, &data_L, 1);
mpu6050_result[2] = data_H << 8;
mpu6050_result[2] += data_L;
i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_H, &data_H, 1);
i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_L, &data_L, 1);
mpu6050_result[3] = data_H << 8;
mpu6050_result[3] += data_L;
i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_H, &data_H, 1);
i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_L, &data_L, 1);
mpu6050_result[4] = data_H << 8;
mpu6050_result[4] += data_L;
i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_H, &data_H, 1);
i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_L, &data_L, 1);
mpu6050_result[5] = data_H << 8;
mpu6050_result[5] += data_L;
// printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_result[0],(int)mpu6050_result[1],(int)mpu6050_result[2]);
// printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_result[3],(int)mpu6050_result[4],(int)mpu6050_result[5]);
/*将读取得到的数据拷贝到用户空间*/
error = copy_to_user(buf, mpu6050_result, cnt);
if(error != 0)
{
printk("copy_to_user error!");
return -1;
}
return 0;
}
到指定寄存器去读数据,读到的数据存入需要的result 中。