Linux学习——I2C-MPU6050驱动移植记录

news/2025/2/12 21:30:54/

开发板:Nvidia Jetson Nano b01
模块:MPU6050

I2C设备

在这里插入图片描述

接线

vcc — 3.3v(17)
gnd — GND(25)
scl — I2C0_SCL(28) -> pJ1
sda — I2C0_SDA(27)
//AD0:从机地址设置引脚 :1.接地或悬空时, 地址为0x68;2.接VCC时,地址为0x69
//INT: 中断输出引脚 (暂未用)

设备树移植

  • 我的目录在 ~/kernel-4.9/hardware/nvidia/platform/t210/porg/kernel-dts/tegra210-p3448-0000-p3449-0000-b00.dts
  • kernel为软连接,kernel-dts已经在kernel-4.9下创建软连接
  • kernel-dts -> /home/yhai/kernel-4.9/hardware/nvidia/platform/t210/porg/kernel-dts
//tegra210-p3448-0000-p3449-0000-b00.dts 
i2c@7000c000 { //i2c设备mp6050,要挂载到的i2c-0控制器,对应 I2C0_SCL(28),同名节点内容会叠加mpu6050-3-axi@68 {compatible = "invensense,mpu6050";reg = <0x68>;   //从机地址来源于MPU6050手册PS-MPU-6000A-00v3.4.pdf   i2c slave address  };
};

驱动程序

//mpu6050.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/delay.h>#include <asm/uaccess.h>#include "mpu6050.h"MODULE_LICENSE("GPL");#define SMPLRT_DIV		0x19
#define CONFIG			0x1A
#define GYRO_CONFIG		0x1B
#define ACCEL_CONFIG	0x1C
#define ACCEL_XOUT_H	0x3B
#define ACCEL_XOUT_L	0x3C
#define ACCEL_YOUT_H	0x3D
#define ACCEL_YOUT_L	0x3E
#define ACCEL_ZOUT_H	0x3F
#define ACCEL_ZOUT_L	0x40
#define TEMP_OUT_H		0x41
#define TEMP_OUT_L		0x42
#define GYRO_XOUT_H		0x43
#define GYRO_XOUT_L		0x44
#define GYRO_YOUT_H		0x45
#define GYRO_YOUT_L		0x46
#define GYRO_ZOUT_H		0x47
#define GYRO_ZOUT_L		0x48
#define PWR_MGMT_1		0x6B#define MPU6050_MAJOR 500
#define MPU6050_MINOR 5struct mpu6050_device {   //定义mpu6050设备结构体,便于描述struct cdev cdev; //继承于字符设备,因为要使用ioctl,open等字符设备中的文件操作struct i2c_client *client;//继承于i2c设备
};
struct mpu6050_device *mpu6050; /*这里的代码经过测试有问题
static int mpu6050_read_byte(struct i2c_client *client, unsigned char reg)
{int ret;char txbuf[1] = { reg };char rxbuf[1];struct i2c_msg msg[2] = {   //设定 I2C消息格式{client->addr, 0, 1, txbuf},       //0         指定i2c设备里的寄存器地址   txbuf 存放其地址{client->addr, I2C_M_RD, 1, rxbuf} //I2C_M_RD  表示读i2c设备里该寄存器的数据};ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); //传输I2C消息if (ret < 0) {printk("ret = %d\n", ret);return ret;}return rxbuf[0];
}
*///后面发现是mpu6050_write_byte函数有问题,应改为
static int mpu6050_write_byte(struct i2c_client *client, uint8_t reg_addr, uint8_t data)
{int ret = 0;uint8_t w_buf[2];struct i2c_msg msg;w_buf[0] = reg_addr;w_buf[1] = data;msg.addr = client->addr;msg.buf = w_buf;msg.flags = 0; // I2C direction write msg.len = sizeof(w_buf);ret = i2c_transfer(client->adapter, &msg, 1);if (ret < 0){printk("i2c transfer err\n");return ret;}else if (ret != 1){printk("i2c transfer err -EIO\n");return -EIO;}//printk("i2c transfer ok\n");return 0;
}static int mpu6050_write_byte(struct i2c_client *client, unsigned char reg, unsigned char val)
{char txbuf[2] = {reg, val};struct i2c_msg msg[2] = {{client->addr, 0, 2, txbuf},};printk("i2c write!\n");i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));printk("i2c write 2!\n");return 0;
}static int mpu6050_open(struct inode *inode, struct file *file) 
{return 0;
}static int mpu6050_release(struct inode *inode, struct file *file) 
{return 0;
}static long mpu6050_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{union mpu6050_data data;struct i2c_client *client = mpu6050->client;switch(cmd) {case GET_ACCEL:data.accel.x = mpu6050_read_byte(client, ACCEL_XOUT_L);data.accel.x |= mpu6050_read_byte(client, ACCEL_XOUT_H) << 8;data.accel.y = mpu6050_read_byte(client, ACCEL_YOUT_L);data.accel.y |= mpu6050_read_byte(client, ACCEL_YOUT_H) << 8;data.accel.z = mpu6050_read_byte(client, ACCEL_ZOUT_L);data.accel.z |= mpu6050_read_byte(client, ACCEL_ZOUT_H) << 8;break;case GET_GYRO:data.gyro.x = mpu6050_read_byte(client, GYRO_XOUT_L);data.gyro.x |= mpu6050_read_byte(client, GYRO_XOUT_H) << 8;data.gyro.y = mpu6050_read_byte(client, GYRO_YOUT_L);data.gyro.y |= mpu6050_read_byte(client, GYRO_YOUT_H) << 8;data.gyro.z = mpu6050_read_byte(client, GYRO_ZOUT_L);data.gyro.z |= mpu6050_read_byte(client, GYRO_ZOUT_H) << 8;break;case GET_TEMP:	data.temp = mpu6050_read_byte(client, TEMP_OUT_L);data.temp |= mpu6050_read_byte(client, TEMP_OUT_H) << 8;break;default:printk("invalid argument\n");return -EINVAL;}if (copy_to_user((void *)arg, &data, sizeof(data)))return -EFAULT;return sizeof(data);
}struct file_operations mpu6050_fops = {.owner 		= THIS_MODULE,.open		= mpu6050_open,.release 	= mpu6050_release,.unlocked_ioctl = mpu6050_ioctl,
};static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)//probe//在probe中使得mpu6050指向具体实体
{int ret;dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);//合并设备号printk("match OK!\n");mpu6050 = kzalloc(sizeof(*mpu6050), GFP_KERNEL);//分配康健if (mpu6050 == NULL) {return -ENOMEM;}mpu6050->client = client;ret = register_chrdev_region(devno, 1, "mpu6050");if (ret < 0) {printk("failed to register char device region!\n");goto err1;}cdev_init(&mpu6050->cdev, &mpu6050_fops);mpu6050->cdev.owner = THIS_MODULE;ret = cdev_add(&mpu6050->cdev, devno, 1);if (ret < 0) {printk("failed to add device\n");goto err2;}mpu6050_write_byte(client, PWR_MGMT_1, 0x00);mpu6050_write_byte(client, SMPLRT_DIV, 0x07);mpu6050_write_byte(client, CONFIG, 0x06);mpu6050_write_byte(client, GYRO_CONFIG, 0xF8);mpu6050_write_byte(client, ACCEL_CONFIG, 0x19);return 0;
err2:unregister_chrdev_region(devno, 1);
err1:kfree(mpu6050);return ret;
}static int mpu6050_remove(struct i2c_client *client)
{dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);cdev_del(&mpu6050->cdev);unregister_chrdev_region(devno, 1);kfree(mpu6050);return 0;
}static const struct i2c_device_id mpu6050_id[] = {{ "mpu6050", 0},{}
}; static struct of_device_id mpu6050_dt_match[] = {{.compatible = "invensense,mpu6050" },  //id和设备树一致{/*northing to be done*/},
};struct i2c_driver mpu6050_driver = {.driver = {.name 			= "mpu6050",.owner 			= THIS_MODULE,.of_match_table = of_match_ptr(mpu6050_dt_match),},.probe 		= mpu6050_probe,//insmod设备后触发probe,进入probe.remove 	= mpu6050_remove,.id_table 	= mpu6050_id,
};module_i2c_driver(mpu6050_driver);

应用程序

//test_mpu6050.c#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>#include "mpu6050.h"int main(int argc, const char *argv[])
{int fd;union mpu6050_data data; fd = open("/dev/mpu6050", O_RDWR);if (fd < 0) {perror("open");exit(1);}while(1) {//三轴加速度ioctl(fd, GET_ACCEL, &data);printf("1 acceleration data: x = %04x, y = %04x, z = %04x\n", data.accel.x, data.accel.y, data.accel.z);//三轴陀螺仪(三轴角速度)ioctl(fd, GET_GYRO, &data);printf("2 gyroscope data: x = %04x, y = %04x, z = %04x\n", data.accel.x, data.accel.y, data.accel.z);//温度值	ioctl(fd, GET_TEMP, &data);printf("3 temp data: = %x \n", data.temp);sleep(1);}close(fd);return 0;
}

头文件

//mpu6050.h
#ifndef MPU6050_HHHH
#define MPU6050_HHHH#define MPU6050_MAGIC 'K'union mpu6050_data
{struct {unsigned short x;unsigned short y;unsigned short z;}accel; //三轴加速度struct {unsigned short x;unsigned short y;unsigned short z;}gyro; //三轴陀螺仪(三轴角速度)unsigned short temp; //温度值
};#define GET_ACCEL _IOR(MPU6050_MAGIC, 0, union mpu6050_data)
#define GET_GYRO  _IOR(MPU6050_MAGIC, 1, union mpu6050_data) 
#define GET_TEMP  _IOR(MPU6050_MAGIC, 2, union mpu6050_data)#endif

Makefile

//Makefile
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= ~/kernel-4.9   #这里为内核目录
PWD := $(shell pwd)all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules* a.out elseobj-m := mpu6050.o
endif	

验证测试-linux虚拟机端

$ cd ~/kernel-4.9   #修改完设备树文件后进入内核目录
$ make dtbs         #编译设备树
$ cp arch/arm64/boot/dts/tegra210-p3448-0000-p3449-0000-b00.dtb   /tftpboot/   #拷贝设备树文件到tftpboot
$ cd mpu6050	#进到mpu6050文件夹下,文件夹下应该由驱动程序、头文件、应用程序、Makefile
$ make          #编译驱动程序,生成ko模块文件
$ cp mpu6050.ko /nfs/rootfs/_install    #拷贝ko模块到nfs目录下,此处为我的目录
$ aarch64-linux-gnu-gcc test_mpu6050.c    #编译应用文件,注意是交叉编译应用文件
$ cp a.out /nfs/rootfs   #编译完成生成的.out文件拷贝到nfs目录下,传到板子上

验证测试-板子端

insmod mpu6050.ko   #加载设备
mknod  /dev/mpu6050 c 500 5  #创建设备 内核自带的模块只需要配置载入就可以,不用创建设备
./a.out  #执行应用程序
rmmod mpu6050.ko   #卸载设备,若要重新加载必须先卸载
#能成功读到下面值,摇晃mpu6050 值会相应变化

出现问题(需要注意细节)的总结

1、cp kernel-4.9/arch/arm64/boot/dts/tegra210-p3448-0000-p3449-0000-b00.dtb /tftpboot/
dtbs为设备树源码,dtb为make之后的二进制文件;在kernel-4.9目录下make dtbs之后,拷贝该文件夹下面生成的dtb设备树文 件到tftp,我的猜测就是make dtbs之后生成的dtb就是在这个arch/arm64/boot/dts/指定文件夹下面。
2、aarch64-linux-gnu-gcc test_mpu6050.c
交叉编译的是测试文件(应用文件),生成a.out文件
3、板子上电自动tftp下载的uboot设置

# setenv bootargs  root=/dev/nfs rw nfsroot=192.168.9.119:/nfs/rootfs/_install,v3 console=ttyS0,115200 init=/linuxrc ip=192.168.9.9
# setenv nfsboot pci enum \; pci \; ext4load mmc 1:1 0x84000000 /home/bjbd/Image \; tftp 83100000 tegra210-p3448-0000-p3449-0000-b00.dtb \; booti 0x84000000 - 83100000
# saveenv  //保存环境变量
# run nfsboot  //手动运行# setenv bootcmd run nfsboot  //测试成功后,可设为自启动命令,避免每次都手动运行
# saveenv	

http://www.ppmy.cn/news/157085.html

相关文章

当刷机工具遇到SetupConnection时的解决方法

当刷机工具遇到SetupConnection时&#xff0c;解决方法 首先&#xff0c;在此贴上原文地址&#xff0c;已表感激之情。 http://blog.sina.com.cn/s/blog_636fd7d90101drak.html http://bbs.gfan.com/android-4032375-1-1.html 据说程序员是比较挑剔的&#xff0c;骨子里难…

NVIDIA Jetson TX1刷机,Ubuntu16.04+Jetpack3.0

注意事项 Jetpack3.x只支持ubuntu14.04和ubuntu16.04操作系统&#xff0c;而且系统语言必须是英文。保证网络通畅。 官方回答说3.2版本可以支持大陆&#xff0c;但是我尝试了Jetpack3.2.1&#xff0c;也不能连接到服务器。不知道4.4是否可行。 用虚拟机的小伙伴要额外研究一下…

PSDK v3.3版本测试、老版本对比

前面博客文章介绍 DJI M30无人机运行PSDK V3.1/3.2 。新版本 PSDK v3.3 在2022年11月发布,支持3E/3T,在系统上usb bulk配置发生了变化。 文章目录 1、不同机型usb功能1.1、M300 RTK1.2、M30/M30T1.3、M3E/M3T1.4、串口、网口、usb的使用区别2、代码差异、系统usb bulk配置2.1…

三星7存平板 Galaxy Table 3 T210如何进入安全模式

长按 电源 按钮&#xff0c;根据提示选择“重新启动” 出现Samsung Galaxy字样的时候&#xff0c;安装 音量减少 按键&#xff0c;直到平板进入到 锁屏 界面。 屏幕左下角显示 安全模式 参考&#xff1a; http://www.samsung.com/us/support/faq/FAQ00048193/52333/

T210 课程表||

现在你总共有 n 门课需要选&#xff0c;记为 0 到 n-1。 在选修某些课程之前需要一些先修课程。 例如&#xff0c;想要学习课程 0 &#xff0c;你需要先完成课程 1 &#xff0c;我们用一个匹配来表示他们: [0,1] 思想&#xff1a;也就是各个课程的选择具有偏序关系&#xff0…

COMETRICS T210-353G2

提起词&#xff0c;更像是一时心绪&#xff0c;寥寥几字&#xff0c;言说的意境却成全了千年辗转&#xff0c;捧读一抹风月之人的情衷。 或许是繁盛后的沉静&#xff0c;是极度张狂后的内敛温润&#xff0c;那些高远辽阔的意境&#xff0c;转瞬成为袅袅青烟般细腻绵延&#xf…

SM-T210 三星 4.1.2 ROOT

SM T210 4.1.2 三星的平板&#xff0c;网上不大好root。 现在安全RooT成功。 1.首选 百度一键ROOT.如果不OK&#xff0c;百度一键ROOT对我的2个设备可以&#xff08;n个月以前&#xff09;&#xff0c;其他的同型号设备不行&#xff08;n个月以后&#xff09;。 无法确定是一…

GOMETRICS T210-353G2

一眼就看中了“喜上眉梢”的胸针&#xff0c;红梅、金羽&#xff0c;灵动温暖。民间传说&#xff0c;鹊能报喜&#xff0c;故称喜鹊。它让我想起小寒的物候&#xff1a;“一候雁北乡&#xff0c;二候鹊始巢&#xff0c;三候雉始鸲”。 小寒是二十四节气中的第二十三个节气&…