本次实验我们选择i2c2接口与mpu6050相连。实物连接图如下:


一,MPU6050设备树插件
设备树插件(Device Tree Overlay,简称DTO)是Linux内核(通常在4.4版本及以上引入)中一种强大的动态设备树扩展机制。可以把它理解成能给正在运行的系统“打补丁”,允许在系统运行时动态地添加、修改或删除设备树中的节点和属性,而无需重新编译和烧写整个设备树。
1.设备树源码
/dts-v1/;
/plugin/;
#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/*
* Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
*/
&{/chosen} {
overlays {
kone-pocketbeagle2.kernel = __TIMESTAMP__;
};
};
&main_i2c2 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
mpu6050@68{
status = "okay";
compatible = "kone,i2c_mpu6050";
reg = <0x68>;
};
};
2.编译设备树插件
设置正确的环境变量
export PATH="/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH"
# 设置架构和交叉编译前缀
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-
makefile中添加新增的设备树
nano /home/mxz/pocketbeagle_2/sdk/linux/arch/arm64/boot/dts/ti/Makefile
dtb-$(CONFIG_ARCH_K3) += kone-pocketbeagle2.dtbo


编译
# 进入内核源码目录
cd /home/mxz/pocketbeagle_2/sdk/linux
# 编译所有设备树
make dtbs


3.加载设备树插件
将生成的 kone-pocketbeagle2.dtbo copy到/boot/firmware/overlays/下
更新引导配置
sudo nano /boot/firmware/extlinux/extlinux.conf
label Linux
kernel /Image.gz
fdtdir /
append console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02860000 root=/dev/mmcblk1p2 ro rootfstype=ext4 rootwait net.ifnames=0
fdtoverlays /overlays/kone-pocketbeagle2.dtbo验证
# 重启
sudo reboot确认设备树覆盖层加载成功
# 查看覆盖层时间戳(确认编译时间)
cat /sys/firmware/devicetree/base/chosen/overlays/kone-pocketbeagle2.kernel
# 查看所有加载的覆盖层
ls /sys/firmware/devicetree/base/chosen/overlays/

查看 I2C2 总线
ls /sys/bus/i2c2/devices/

二,MPU6050驱动
驱动源码
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.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/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> //==================================define====================================== /*---- MPU6050 设备地址与寄存器宏 ----*/ #define Address 0x68 // MPU6050默认I2C地址(AD0接地)。若AD0接VCC则地址为0x69 #define SMPLRT_DIV 0x19 // 采样率分频器寄存器:配置陀螺仪输出率分频系数(默认0x07) #define CONFIG 0x1A // 配置寄存器:设置数字低通滤波器带宽 #define GYRO_CONFIG 0x1B // 陀螺仪配置寄存器:量程选择(±250/500/1000/2000°/s)与自检 #define ACCEL_CONFIG 0x1C // 加速度计配置寄存器:量程选择(±2/4/8/16g)与自检 /* 加速度计数据输出寄存器 (16-bit, 高位在前) */ #define ACCEL_XOUT_H 0x3B // 加速度计X轴高8位数据 #define ACCEL_XOUT_L 0x3C // 加速度计X轴低8位数据 #define ACCEL_YOUT_H 0x3D // 加速度计Y轴高8位数据 #define ACCEL_YOUT_L 0x3E // 加速度计Y轴低8位数据 #define ACCEL_ZOUT_H 0x3F // 加速度计Z轴高8位数据 #define ACCEL_ZOUT_L 0x40 // 加速度计Z轴低8位数据 /* 温度与陀螺仪数据输出寄存器 */ #define TEMP_OUT_H 0x41 // 温度传感器高8位数据(计算公式:TEMP/340 + 36.53) #define TEMP_OUT_L 0x42 // 温度传感器低8位数据 #define GYRO_XOUT_H 0x43 // 陀螺仪X轴高8位数据 #define GYRO_XOUT_L 0x44 // 陀螺仪X轴低8位数据 #define GYRO_YOUT_H 0x45 // 陀螺仪Y轴高8位数据 #define GYRO_YOUT_L 0x46 // 陀螺仪Y轴低8位数据 #define GYRO_ZOUT_H 0x47 // 陀螺仪Z轴高8位数据 #define GYRO_ZOUT_L 0x48 // 陀螺仪Z轴低8位数据 /* 关键控制寄存器 */ #define PWR_MGMT_1 0x6B // 电源管理寄存器1:控制时钟源、睡眠模式、温度传感器禁用 #define WHO_AM_I 0x75 // 设备ID寄存器:读取值应为0x68(用于验证连接) /*---- I2C控制参数宏 (Linux ioctl专用) ----*/ #define SlaveAddress 0xD0 // I2C写地址 = (Address << 1) | 0。读地址应为0xD1 #define I2C_RETRIES 0x0701 // ioctl命令:设置I2C传输失败重试次数 #define I2C_TIMEOUT 0x0702 // ioctl命令:设置I2C总线超时时间(单位:10ms步进) #define I2C_SLAVE 0x0703 // ioctl命令:设置I2C从机地址(需配合Address使用) #define I2C_BUS_MODE 0x0780 // ioctl命令:设置I2C总线模式(标准模式100kbps/快速模式400kbps) #define DEV_NAME "mpu6050" static dev_t dev_mpu6050; static struct cdev cdev_mpu6050; struct class *class_mpu6050; struct device *device_mpu6050; struct i2c_client* mpu6050_client = NULL; static int i2c_write_mpu6050(struct i2c_client*mpu6050_client,uint8_t address,uint8_t data) { int ret; uint8_t write_data[2]; struct i2c_msg send_msg; write_data[0] = address; write_data[1] = data; send_msg.addr = mpu6050_client->addr; send_msg.flags = 0; send_msg.buf = write_data; send_msg.len = 2; ret = i2c_transfer(mpu6050_client->adapter,&send_msg,1); if(ret != 1) { printk("i2c_write_mpu6050_i2c_transfer is error\n"); return -1; } return 0; } static int i2c_read_mpu6050(struct i2c_client *i2c_client,uint8_t address,uint8_t *data,uint32_t len) { int ret; uint8_t address_data = address; struct i2c_msg i2c_msg[2] ; i2c_msg[0].addr = i2c_client->addr; i2c_msg[0].flags = 0; //写入 i2c_msg[0].buf = &address_data; i2c_msg[0].len = 1; i2c_msg[1].addr = i2c_client->addr; i2c_msg[1].flags = I2C_M_RD; i2c_msg[1].buf = data; i2c_msg[1].len = len; ret = i2c_transfer(i2c_client->adapter,i2c_msg,2); if(ret != 2) { printk("i2c_read_mpu6050 i2c_transfer is error\n"); return -1; } return 0; } static int mpu6050_init(void) { static uint8_t last_register = 0; /* 检查I2C客户端是否有效 */ if(!mpu6050_client) { printk(KERN_ERR "MPU6050: Invalid I2C client\n"); return -EINVAL; } /* 配置电源管理寄存器 */ last_register = PWR_MGMT_1; if(i2c_write_mpu6050(mpu6050_client,PWR_MGMT_1,0x00) < 0) { goto init_failed; } /* 配置采样率分频器 */ last_register = SMPLRT_DIV; if(i2c_write_mpu6050(mpu6050_client,SMPLRT_DIV,0x07) < 0) { goto init_failed; } /* 配置低通滤波器 */ last_register = CONFIG; if(i2c_write_mpu6050(mpu6050_client,CONFIG,0X06) < 0) { goto init_failed; } /* 配置加速度计量程 */ last_register = ACCEL_CONFIG; if(i2c_write_mpu6050(mpu6050_client,ACCEL_CONFIG,0x01) < 0) { goto init_failed; } /* 初始化成功 */ printk(KERN_DEBUG "MPU6050 initialized successfully\n"); return 0; init_failed: printk(KERN_ERR "MPU6050 init failed at register 0x%02X\n", last_register); return -EIO; } static int mpu6050_open(struct inode*inode,struct file*file) { mpu6050_init(); return 0; } static ssize_t mpu6050_read(struct file*file,char __user*buf,size_t size,loff_t*off) { uint8_t data_H,data_L; int ret; short mpu6050_data[7]; // 加速度 i2c_read_mpu6050(mpu6050_client,ACCEL_XOUT_H,&data_H,1); i2c_read_mpu6050(mpu6050_client,ACCEL_XOUT_L,&data_L,1); mpu6050_data[0] = (data_H << 8) | (data_L & 0xff); i2c_read_mpu6050(mpu6050_client,ACCEL_YOUT_H,&data_H,1); i2c_read_mpu6050(mpu6050_client,ACCEL_YOUT_L,&data_L,1); mpu6050_data[1] = (data_H << 8) | (data_L & 0xff); i2c_read_mpu6050(mpu6050_client,ACCEL_ZOUT_H,&data_H,1); i2c_read_mpu6050(mpu6050_client,ACCEL_ZOUT_L,&data_L,1); mpu6050_data[2] = (data_H << 8) | (data_L & 0xff); //角速度 i2c_read_mpu6050(mpu6050_client,GYRO_XOUT_H,&data_H,1); i2c_read_mpu6050(mpu6050_client,GYRO_XOUT_L,&data_L,1); mpu6050_data[3] = (data_H << 8) | (data_L & 0xff); i2c_read_mpu6050(mpu6050_client,GYRO_YOUT_H,&data_H,1); i2c_read_mpu6050(mpu6050_client,GYRO_YOUT_L,&data_L,1); mpu6050_data[4] = (data_H << 8) | (data_L & 0xff); i2c_read_mpu6050(mpu6050_client,GYRO_ZOUT_H,&data_H,1); i2c_read_mpu6050(mpu6050_client,GYRO_ZOUT_L,&data_L,1); mpu6050_data[5] = (data_H << 8) | (data_L & 0xff); //温度 i2c_read_mpu6050(mpu6050_client,TEMP_OUT_H,&data_H,1); i2c_read_mpu6050(mpu6050_client,TEMP_OUT_L,&data_L,1); mpu6050_data[6] = (data_H << 8) | (data_L & 0xff); ret = copy_to_user(buf,mpu6050_data,size);//目标add 源add if(ret != 0) { printk("copy_to_user is error\n"); } return 0; } static int mpu6050_release(struct inode*inode,struct file*file) { return 0; } static struct file_operations mpu6050_fops = { .owner = THIS_MODULE, .open = mpu6050_open, .read = mpu6050_read, .release = mpu6050_release, }; //平台驱动函数集,主要用于申请设备号,注册添加设备,创建class等 static int mpu6050_probe(struct i2c_client*client) { int ret; int major,minor; ret = alloc_chrdev_region(&dev_mpu6050,0,1,DEV_NAME); if(ret < 0) { printk("alloc_chrdev_region is error \n"); return -1; } major = MAJOR(dev_mpu6050); minor = MINOR(dev_mpu6050); printk("major is %d \n minor is %d\n",major,minor); cdev_mpu6050.owner = THIS_MODULE; cdev_init(&cdev_mpu6050,&mpu6050_fops); ret = cdev_add(&cdev_mpu6050,dev_mpu6050,1); if(ret < 0) { printk("cdev_add is error\n"); return -1; } class_mpu6050 = class_create(DEV_NAME); device_mpu6050 = device_create(class_mpu6050,NULL,dev_mpu6050,NULL,DEV_NAME); mpu6050_client = client; return 0; } static void mpu6050_remove(struct i2c_client*client) { device_destroy(class_mpu6050,dev_mpu6050); class_destroy(class_mpu6050); cdev_del(&cdev_mpu6050); unregister_chrdev_region(dev_mpu6050,1); } //定义ID匹配表 static const struct i2c_device_id mpu6050_device_id[] = { {"kone,i2c_mpu6050"}, {}, }; //定义设备树匹配表 static const struct of_device_id mpu6050_of_match_table[] = { {.compatible = "kone,i2c_mpu6050"}, {}, }; struct i2c_driver mpu6050_driver = { .probe = mpu6050_probe, .remove = mpu6050_remove, .id_table = mpu6050_device_id, .driver = { .name = "kone,i2c_mpu6050", .owner = THIS_MODULE, .of_match_table = mpu6050_of_match_table, }, }; static int __init mpu6050_driver_init(void) { printk("mpu6050_driver_init\n"); i2c_add_driver(&mpu6050_driver); return 0; } static void __exit mpu6050_driver_exit(void) { i2c_del_driver(&mpu6050_driver); printk("mpu6050_driver_exit \n"); } module_init(mpu6050_driver_init); module_exit(mpu6050_driver_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("kone");Makefile
#export PATH="/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH" # 确保系统工具目录在 PATH 的最前面 export PATH="/bin:/usr/bin:/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH" # 设置架构和交叉编译前缀 export ARCH=arm64 export CROSS_COMPILE=aarch64-linux- obj-m:=mpu6050.o KDIR:=/home/mxz/pocketbeagle_2/sdk/linux PWD:=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
测试app
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { int fd,ret; short mpu6050_data[7] = {0}; fd = open(argv[1],O_RDWR); if(fd < 0) { perror("open file is error \n"); return -1; } while(1) { ret = read(fd,mpu6050_data,sizeof(mpu6050_data)); if(ret < 0) { perror("read file is error\n"); return -1; } printf("ax:%d,ay:%d,az:%d\n",mpu6050_data[0],mpu6050_data[1],mpu6050_data[2]); printf("gx:%d,gy:%d,gz:%d\n",mpu6050_data[3],mpu6050_data[4],mpu6050_data[5]); printf("temperature:%d \n",mpu6050_data[6]); sleep(1); } close(fd); return 0; }
三,演示
将生成的mpu6050.ko和app.c copy到开发板上



我要赚赏金
