开发板: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

Linux学习——I2C-MPU6050驱动移植记录相关推荐

  1. 安卓平台下的GPS架构介绍及驱动移植记录

    一.前言 我的工作是关于汽车车机BSP部分. 汽车车机,其实基本和人们日常所用的手机一样,也是安卓平台的.所谓安卓,就是一层安卓服务包裹着Linux内核所形成的操作系统. BSP组,主要工作内容就是负 ...

  2. Android(4.2) Sensors 学习——G-sensor,Gyroscope驱动移植

    Android(4.2) Sensors 学习--G-sensor,Gyroscope驱动移植 目录(?)[-] Sensors 种类和功能 G-sensor驱动code G-sensor HAL 注 ...

  3. linux下GT911触摸屏驱动优化记录

    linux下GT911触摸屏驱动优化记录 背景 由于最近要做linux内核启动速度优化,所以就对着驱动一点一点优化,加上QT应用程序的初始化,总共的启动时间要做到4S以内.目前先调试GT911驱动程序 ...

  4. 基于Android8.1的博通bcm89342蓝牙驱动的驱动移植记录

    基于Android8.1的博通蓝牙BCM89342的驱动移植记录 说明 一 .软硬件平台 二.蓝牙移植流程 2.1 kernel 对蓝牙的驱动支持配置 2.2 kernel层编写蓝牙电源管理(bt r ...

  5. 从零开始学习linux的I2C设备驱动框架——写一个简单的SHT20驱动

    目录 0.测试环境说明 1.设备树的修改 2.设备驱动框架 3.I2C数据传输过程 3.1 struct i2c_msg 3.2 SHT20的数据收发 4.I2C适配器超时等待时间的修改 本文资源 参 ...

  6. 博通蓝牙驱动linux,基于Android8.1的博通bcm89342蓝牙驱动的驱动移植记录-Go语言中文社区...

    说明 本文档是基于Android 8.1版本的一款博通蓝牙的驱动移植全过程记录.本驱动是通过/dev/ttyS2和蓝牙进行通信.因为较android以前的版本有很大变化尤其是上层关于蓝牙协议栈的部分, ...

  7. STM32MP157 Linux系统移植开发篇14:Linux内核RGB LCD驱动移植

    本文章为<STM32MP157 Linux系统移植开发篇>系列中的一篇,笔者使用的开发平台为华清远见FS-MP1A开发板(STM32MP157开发板).stm32mp157是ARM双核,2 ...

  8. Linux的I2C 设备驱动 -- mini2440 上i2c接口触摸屏驱动

    本篇记录在友善之臂 mini2440 平台上挂载I2C接口触摸屏的驱动开发过程. 内核版本linux-2.6.32.2, 平台是ARM9 S3C2440+I2C接口的触摸屏 如上篇 Linux的I2C ...

  9. linux系统 RTL8152B网卡驱动移植-USB转网卡

    本文描述了在imx6q上对百兆网卡RTL8152B进行移植的过程,有相关的疑惑的可参考本文进行移植. 感谢国芯科技的范工 y_fan 的耐心指点,RTL8152B相关移植的资料比较少,范工给予重要调试 ...

最新文章

  1. python文件输出-python文件流
  2. LeetCode Implement Trie (Prefix Tree)(字典树)
  3. VMWare假造机上装配Ubuntu Linux体例-1
  4. yum 安装mariadb
  5. 利用div的定位制作复杂的页面布局
  6. linux编译ko文件(不同内核源码版本)
  7. TortoiseSVN 配合 Beyond Compare 3 或 WinMerge 的设置
  8. Yii 2.0 权威指南(3) 使用数据库
  9. 角度传感器原理和应用
  10. 泰凌微ble mesh蓝牙模组天猫精灵学习之旅 ④ 初认识阿里天猫精灵官方Genie BT mesh Stack框架, windows平台搭建打印Hello World !
  11. win10下什么拼音输入法好用
  12. 2022-2027年中国血液制品行业市场深度分析及投资战略规划报告
  13. 女性内分泌失调要小心
  14. jfinal jboot 拦截器过滤文件上传请求 和 跨域解决方法
  15. linux服务器上查看显卡(nvidia)型号
  16. GTX1060安装gpu版tensorflow经验分享
  17. 学习前端常用到的网站
  18. 标准清洗槽中的质量参数的监控方法
  19. DNSPod十问肖雨浓:“无服务器计算”动了谁的奶酪?
  20. 10种打死你都不会去学的编程语言(第1季)

热门文章

  1. 治愈大脑,人机共生,马斯克为“脑机接口”辩护
  2. DotNetBar的groupPanel和buttonX背景色问题
  3. 自动驾驶八大主流应用场景——2021年度盘点
  4. tiup cluster reload
  5. 滴滴打车悄悄上市,市值超5000亿!这和“眼光”有关。
  6. 从 0 开始搭建 IoT 平台
  7. 自主命题计算机专业课,计算机专业考研统考是所有的科目都统考还是专业课统考?...
  8. 【报错】Duplicate entry '0' for key 'PRIMARY'
  9. 异星工厂 factorio 0.16.51 下载
  10. 在线旅游产品目前有哪些急需解决的痛点?