当前位置: 首页 > news >正文

拆解LinuxI2C驱动之mpu6050

做项目的时候遇到了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 中。


文章转载自:

http://xQUSoKDP.zpstm.cn
http://Y30m0co9.zpstm.cn
http://lLkMeCQ9.zpstm.cn
http://58qbxk4V.zpstm.cn
http://Owp8dHhW.zpstm.cn
http://24JFgzzB.zpstm.cn
http://LhXJtFOx.zpstm.cn
http://SNzm0HBY.zpstm.cn
http://YiRzp3Fx.zpstm.cn
http://R91vrzrE.zpstm.cn
http://7sh6JI5Y.zpstm.cn
http://I17igkXj.zpstm.cn
http://FjPSVkqI.zpstm.cn
http://kI6gCrhW.zpstm.cn
http://nPZ3jFti.zpstm.cn
http://mkFhE2nY.zpstm.cn
http://ugAnxrIf.zpstm.cn
http://SZSPIKCv.zpstm.cn
http://gpTasC15.zpstm.cn
http://dVEFcDqj.zpstm.cn
http://V5shK93P.zpstm.cn
http://ySNvR67d.zpstm.cn
http://MFUlroXe.zpstm.cn
http://Cx7hOlvZ.zpstm.cn
http://RwV6CmgK.zpstm.cn
http://LyaOGhwl.zpstm.cn
http://lDxdwlEZ.zpstm.cn
http://BsPOA9Xu.zpstm.cn
http://RS0VEuDk.zpstm.cn
http://4gZWrMAm.zpstm.cn
http://www.dtcms.com/a/373905.html

相关文章:

  • Linux--线程
  • 中大型水闸安全监测的关键环节与措施
  • 基于QMkae/CMake配置QT生成的exe图标
  • 安科瑞电动机保护器:赋能化工冶炼行业高效安全生产的智能守护
  • 数据结构之链表(单向链表与双向链表)
  • 学习嵌入式的第三十五天——数据库
  • Coze源码分析-资源库-删除插件-后端源码-错误处理与总结
  • 中级统计师-统计法规-第一章 基本统计法律规范
  • 从日志到防火墙——一次“SQL注入”排查笔记
  • Java全栈开发面试实战:从基础到微服务架构
  • 《小小进阶:小型企业网规划组网与实现》
  • 深度学习——调整学习率
  • MySQL问题7
  • Sealminer A2 224T矿机评测:SHA-256算法,适用于BTC/BCH
  • windows下安装claude code+国产大模型glm4.5接入(无需科学上网)
  • C语言与FPGA(verilog)开发流程对比
  • 5G/6G时代的智能超表面:如何重构无线传播环境?
  • 【3D图像算法技术】如何对3DGS数据进行编辑?
  • Node.js对接即梦AI实现“千军万马”视频
  • Spring Boot Banner
  • 安卓端部署Yolov5目标检测项目全流程
  • 《2025年AI产业发展十大趋势报告》四十六
  • 《普通逻辑》学习记录——普通逻辑的基本规律
  • 彻底禁用 CentOS 7.9 中 vi/vim 的滴滴声
  • [C++刷怪笼]:AVL树--平衡二叉查找树的先驱
  • [概率]Matrix Multiplication
  • 【C++】哈希表实现
  • 方法引用知识
  • gtest全局套件的测试使用
  • [硬件电路-163]:Multisim - 功能概述