IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 为BeagleBone AI添加MPU6050加速度传感器的Linux驱动程序 -> 正文阅读

[嵌入式]为BeagleBone AI添加MPU6050加速度传感器的Linux驱动程序

作者:recommend-item-box type_blog clearfix

一、修改设备树

不要使用插件设备树更改pinmux,因为在很多板子上都无效,BeagleBone AI也不例外。首先对设备树作如下更改:

  1. 将pinmux@1400节点的cape_pins_default子节点内的CTRL_CORE_PAD_VIN1A_D20(0x4a003544 - 0x4a000000 - 0x2000 - 0x1400 = 0x00000144)、CTRL_CORE_PAD_GPIO6_15(0x4a00368C - 0x4a000000 - 0x2000 - 0x1400 = 0x0000028c)、CTRL_CORE_PAD_GPIO6_14(0x4A003688 - 0x4a000000 - 0x2000 - 0x1400 = 0x00000288)三个寄存器的值分别改为0x0001000f(PIN_OUTPUT | MUX_MODE15)、0x00060009(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE9)、0x00060009(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE9);
  2. 关闭AE2管脚是因为它和i2c3_sda对应的E21管脚在BeagleBone AI上是连在一起的,这个可以通过看板子的原理图和芯片的数据手册知道;
  3. 将i2c3对应的i2c@48060000节点的status属性值从"disabled"更改为"okay";
  4. 在i2c@48060000节点内添加clock-frequency = <0x00061a80>;属性,这个属性指定了i2c的速度为400kHz;
  5. 在i2c@48060000节点内添加i2c_mpu6050@68节点及其内容。

如下为更改后的am5729-beagleboneai.dts文件:

...
    ocp {
        compatible = "ti,dra7-l3-noc", "simple-bus";
        #address-cells = <0x00000001>;
        #size-cells = <0x00000001>;
        ranges = <0x00000000 0x00000000 0x00000000 0xc0000000>;
        ti,hwmods = "l3_main_1", "l3_main_2";
        reg = <0x00000000 0x44000000 0x00000000 0x01000000 0x00000000 0x45000000 0x00000000 0x00001000>;
        interrupts-extended = <0x00000001 0x00000000 0x00000004 0x00000004 0x00000008 0x00000000 0x0000000a 0x00000004>;
        phandle = <0x00000139>;
        l4@4a000000 {
            compatible = "ti,dra7-l4-cfg", "simple-bus";
            #address-cells = <0x00000001>;
            #size-cells = <0x00000001>;
            ranges = <0x00000000 0x4a000000 0x0022c000>;
            phandle = <0x0000013a>;
            scm@2000 {
                compatible = "ti,dra7-scm-core", "simple-bus";
                reg = <0x00002000 0x00002000>;
                #address-cells = <0x00000001>;
                #size-cells = <0x00000001>;
                ranges = <0x00000000 0x00002000 0x00002000>;
                phandle = <0x0000013b>;
...
                pinmux@1400 {
                    compatible = "ti,dra7-padconf", "pinctrl-single";
                    reg = <0x00001400 0x00000468>;
                    #address-cells = <0x00000001>;
                    #size-cells = <0x00000000>;
                    #pinctrl-cells = <0x00000001>;
                    #interrupt-cells = <0x00000001>;
                    interrupt-controller;
                    pinctrl-single,register-width = <0x00000020>;
                    pinctrl-single,function-mask = <0x3fffffff>;
                    phandle = <0x0000013f>;
...
                    cape_pins_default {
                        pinctrl-single,pins = <
                        	...
                            0x00000144 0x0001000f   /* vin1a_d20.off */
                            0x0000028c 0x00060009   /* gpio6_15.i2c3_scl */
                            0x00000288 0x00060009   /* gpio6_14.i2c3_sda */
                        	...
                        >;
                        phandle = <0x00000137>;
                    };
...
                };
...
            };
...
        };
...
        i2c@48060000 {
            compatible = "ti,omap4-i2c";
            reg = <0x48060000 0x00000100>;
            interrupts = <0x00000000 0x00000038 0x00000004>;
            #address-cells = <0x00000001>;
            #size-cells = <0x00000000>;
            ti,hwmods = "i2c3";
            status = "okay";
            clock-frequency = <0x00061a80>;
            phandle = <0x000001db>;
            i2c_mpu6050@68 {
                compatible = "charming,i2c_mpu6050";
                reg = <0x68>;
                status = "okay";
            };
        };
...
    };
...

执行如下命令编译设备树文件:

dtc -I dts -O dtb -o am5729-beagleboneai.dtb am5729-beagleboneai.dts

然后将编译好的am5729-beagleboneai.dtb文件替换掉开发板根文件系统中的/boot/dtbs/4.14.108-ti-r143/am5729-beagleboneai.dtb文件。

二、编写程序

i2c_mpu6050.c:

#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 <asm/mach/map.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 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 WHO_AM_I		0x75

/*------------------字符设备内容----------------------*/
#define DEV_NAME		"I2C3_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函数带回。

/*通过i2c 向mpu6050写入数据
*mpu6050_client:mpu6050的i2c_client结构体。
*address, 数据要写入的地址,
*data, 要写入的数据
*返回值,错误返回-1,成功返回0
*/
static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
{
	int error = 0;
	u8 write_data[2];
	struct i2c_msg send_msg; //要发送的数据结构体

	/*设置要发送的数据*/
	write_data[0] = address;
	write_data[1] = data;

	/*发送 iic要写入的地址 reg*/
	send_msg.addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
	send_msg.flags = 0;					  //标记为发送数据
	send_msg.buf = write_data;			  //写入的首地址
	send_msg.len = 2;					  //reg长度

	/*执行发送*/
	error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
	if (error != 1)
	{
		printk(KERN_DEBUG "\n i2c_transfer error \n");
		return -1;
	}
	return 0;
}

/*通过i2c 向mpu6050写入数据
*mpu6050_client:mpu6050的i2c_client结构体。
*address, 要读取的地址,
*data,保存读取得到的数据
*length,读长度
*返回值,错误返回-1,成功返回0  
*/
static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
{
	int error = 0;
	u8 address_data = address;
	struct i2c_msg mpu6050_msg[2];
	/*设置读取位置msg*/
	mpu6050_msg[0].addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
	mpu6050_msg[0].flags = 0;					//标记为发送数据
	mpu6050_msg[0].buf = &address_data;			//写入的首地址
	mpu6050_msg[0].len = 1;						//写入长度

	/*设置读取位置msg*/
	mpu6050_msg[1].addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
	mpu6050_msg[1].flags = I2C_M_RD;			//标记为读取数据
	mpu6050_msg[1].buf = data;					//读取得到的数据保存位置
	mpu6050_msg[1].len = length;				//读取长度

	error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);

	if (error != 2)
	{
		printk(KERN_DEBUG "\n i2c_read_mpu6050 error \n");
		return -1;
	}
	return 0;
}

/*初始化i2c
*返回值,错误返回-1,成功返回0
*/
static int mpu6050_init(void)
{
	int error = 0;
	/*配置mpu6050电源管理,0x00,正常启动*/
	error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
	/*设置MPU6050的采样频率*/
	error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
	/*设置数字低通滤波器和帧同步引脚采样*/
	error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
	/*设置量程和 X、Y、Z 轴加速度自检*/
	error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);

	if (error < 0)
	{
		/*初始化错误*/
		printk(KERN_DEBUG "\n mpu6050_init error \n");
		return -1;
	}
	return 0;
}

/*字符设备操作函数集,open函数实现*/
static int mpu6050_open(struct inode *inode, struct file *filp)
{
	// printk("\n mpu6050_open \n");

	/*向 mpu6050 发送配置数据,让mpu6050处于正常工作状态*/
	mpu6050_init();
	return 0;
}

/*字符设备操作函数集,.read函数实现*/
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;
	//保存mpu6050转换得到的原始数据
	short mpu6050_result[6]; 

	/*读取3轴加速度原始值*/
	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;

	/*读取3轴角速度原始值*/
	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;
}

/*字符设备操作函数集,.release函数实现*/
static int mpu6050_release(struct inode *inode, struct file *filp)
{
	// printk("\n mpu6050_release \n");
	return 0;
}

/*字符设备操作函数集*/
static struct file_operations mpu6050_chr_dev_fops =
	{
		.owner = THIS_MODULE,
		.open = mpu6050_open,
		.read = mpu6050_read,
		.release = mpu6050_release,
};

/*----------------平台驱动函数集-----------------*/
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{

	int ret = -1; //保存错误状态码

	printk(KERN_EMERG "\t  match successed  \n");
	/*---------------------注册 字符设备部分-----------------*/

	//采用动态分配的方式,获取设备编号,次设备号为0,
	//设备名称为rgb-leds,可通过命令cat  /proc/devices查看
	//DEV_CNT为1,当前只申请一个设备编号
	ret = alloc_chrdev_region(&mpu6050_devno, 0, DEV_CNT, DEV_NAME);
	if (ret < 0)
	{
		printk("fail to alloc mpu6050_devno\n");
		goto alloc_err;
	}

	//关联字符设备结构体cdev与文件操作结构体file_operations
	mpu6050_chr_dev.owner = THIS_MODULE;
	cdev_init(&mpu6050_chr_dev, &mpu6050_chr_dev_fops);

	// 添加设备至cdev_map散列表中
	ret = cdev_add(&mpu6050_chr_dev, mpu6050_devno, DEV_CNT);
	if (ret < 0)
	{
		printk("fail to add cdev\n");
		goto add_err;
	}

	/*创建类 */
	class_mpu6050 = class_create(THIS_MODULE, DEV_NAME);

	/*创建设备 DEV_NAME 指定设备名,*/
	device_mpu6050 = device_create(class_mpu6050, NULL, mpu6050_devno, NULL, DEV_NAME);
	mpu6050_client = client;
	return 0;

add_err:
	// 添加设备失败时,需要注销设备号
	unregister_chrdev_region(mpu6050_devno, DEV_CNT);
	printk("\n error! \n");
alloc_err:

	return -1;
}

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;
}

/*定义ID 匹配表*/
static const struct i2c_device_id gtp_device_id[] = {
	{"charming,i2c_mpu6050", 0},
	{}};

/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
	{.compatible = "charming,i2c_mpu6050"},
	{/* sentinel */}};

/*定义i2c总线设备结构体*/
struct i2c_driver mpu6050_driver = {
	.probe = mpu6050_probe,
	.remove = mpu6050_remove,
	.id_table = gtp_device_id,
	.driver = {
		.name = "charming,i2c_mpu6050",
		.owner = THIS_MODULE,
		.of_match_table = mpu6050_of_match_table,
	},
};

/*
*驱动初始化函数
*/
static int __init mpu6050_driver_init(void)
{
	int ret;
	pr_info("mpu6050_driver_init\n");
	ret = i2c_add_driver(&mpu6050_driver);
	return ret;
}

/*
*驱动注销函数
*/
static void __exit mpu6050_driver_exit(void)
{
	pr_info("mpu6050_driver_exit\n");
	i2c_del_driver(&mpu6050_driver);
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");

app.c:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    short resive_data[6];  //保存收到的 mpu6050转换结果数据,依次为 AX(x轴角度), AY, AZ 。GX(x轴加速度), GY ,GZ

    /*打开文件*/
    int fd = open("/dev/I2C3_mpu6050", O_RDWR);
    if(fd < 0)
    {
		printf("open file : %s failed !\n", argv[0]);
		return -1;
	}

    /*读取数据*/
    int error = read(fd,resive_data,12);
    if(error < 0)
    {
        printf("write file error! \n");
        close(fd);
        /*判断是否关闭成功*/
    }

    /*打印数据*/
    printf("AX=%d, AY=%d, AZ=%d ",(int)resive_data[0],(int)resive_data[1],(int)resive_data[2]);
	printf("    GX=%d, GY=%d, GZ=%d \n \n",(int)resive_data[3],(int)resive_data[4],(int)resive_data[5]);

    /*关闭文件*/
    error = close(fd);
    if(error < 0)
    {
        printf("close file error! \n");
    }
    
    return 0;
}

Makefile:

KERNEL_DIR=/opt/ti-processor-sdk-linux-am57xx-evm-05.03.00.07/board-support/linux-4.14.79+gitAUTOINC+e669d52447-ge669d52447

ARCH=arm
CROSS_COMPILE=/opt/ti-processor-sdk-linux-am57xx-evm-05.03.00.07/linux-devkit/sysroots/x86_64-arago-linux/usr/bin/arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE

obj-m := i2c_mpu6050.o

all:app
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules

app:	
	${CROSS_COMPILE}gcc app.c -o App

.PHONE:clean copy 

clean:
	rm *.o *.order *.symvers *.mod.c

delete:
	rm App i2c_mpu6050.ko

执行如下命令编译驱动程序和应用程序:

make
make clean

三、运行程序

执行如下命令加载i2c_mpu6050.ko模块,并且执行App:

debian@beaglebone:~$ sudo insmod i2c_mpu6050.ko
[sudo] password for debian:
[  112.814058]    match successed

Message from syslogd@beaglebone at Mar 22 06:01:50 ...
 kernel:[  112.814058]    match successed
debian@beaglebone:~$ chmod +x ./App
debian@beaglebone:~$ sudo ./App
AX=206, AY=14350, AZ=7764     GX=-246, GY=-126, GZ=-29

debian@beaglebone:~$ sudo ./App
AX=212, AY=14332, AZ=7772     GX=-302, GY=-126, GZ=-29
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-24 00:45:14  更:2022-03-24 00:45:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:47:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码