IMX6ULL驱动MPU6050

        学了这么久驱动开发,是时候要自己做一下了。

修改设备树

        本次mpu6050挂载在i2c1下。

更新设备树

        因为我这边烧录了正点原子的出厂系统,没有使用网络启动,所以大体的更新方法就是,将新的设备树文件发到板子上,然后把这个文件替换掉原来的设备树文件。

发送文件到板子上

        这里使用了SCP命令发送(要提前安装配置环境)

scp seriaApp 远端用户名@192.168.1.251(远端ip):/home/root.......
//scp imx6ull-alientek-emmc.dtb root@192.168.1.106:/home/root/dtb_user

         如下所示已经发送成功了

 替换原设备树文件

        下一步是更新设备树文件,这里用了一种骚方法,是我网上看来的。大体就是挂载一个储存设备树和zImage的系统分区,把这个系统分区里的.dtb给直接换掉。

root@ATK-IMX6U:~/dtb_user# chmod 777 imx6ull-alientek-emmc.dtb
root@ATK-IMX6U:~/dtb_user# ls
imx6ull-alientek-emmc.dtb
root@ATK-IMX6U:~/dtb_user# cd /
root@ATK-IMX6U:/# mount /dev/mmcblk1p1 /mnt/
root@ATK-IMX6U:/# cd /mnt
root@ATK-IMX6U:/mnt# ls
imx6ull-14x14-emmc-10.1-1280x800-c.dtb  imx6ull-14x14-emmc-4.3-800x480-c.dtb  imx6ull-14x14-emmc-7-800x480-c.dtb  imx6ull-14x14-emmc-vga.dtb
imx6ull-14x14-emmc-4.3-480x272-c.dtb    imx6ull-14x14-emmc-7-1024x600-c.dtb   imx6ull-14x14-emmc-hdmi.dtb         zImage
root@ATK-IMX6U:/mnt# cd /
root@ATK-IMX6U:/# cd /mnt/
root@ATK-IMX6U:/mnt# ls
imx6ull-14x14-emmc-10.1-1280x800-c.dtb  imx6ull-14x14-emmc-4.3-800x480-c.dtb  imx6ull-14x14-emmc-7-800x480-c.dtb  imx6ull-14x14-emmc-vga.dtb
imx6ull-14x14-emmc-4.3-480x272-c.dtb    imx6ull-14x14-emmc-7-1024x600-c.dtb   imx6ull-14x14-emmc-hdmi.dtb         zImage
root@ATK-IMX6U:/mnt# mv imx6ull-14x14-emmc-7-800x480-c.dtb imx6ull-14x14-emmc-7-800x480-c.dtb-old
root@ATK-IMX6U:/mnt# ls
imx6ull-14x14-emmc-10.1-1280x800-c.dtb  imx6ull-14x14-emmc-4.3-800x480-c.dtb  imx6ull-14x14-emmc-7-800x480-c.dtb-old  imx6ull-14x14-emmc-vga.dtb
imx6ull-14x14-emmc-4.3-480x272-c.dtb    imx6ull-14x14-emmc-7-1024x600-c.dtb   imx6ull-14x14-emmc-hdmi.dtb             zImage
root@ATK-IMX6U:/mnt# cp /home/root/dtb_user/imx6ull-alientek-emmc.dtb /mnt/imx6ull-14x14-emmc-7-800x480-c.dtb
root@ATK-IMX6U:/mnt# ls
imx6ull-14x14-emmc-10.1-1280x800-c.dtb  imx6ull-14x14-emmc-4.3-800x480-c.dtb  imx6ull-14x14-emmc-7-800x480-c.dtb      imx6ull-14x14-emmc-hdmi.dtb  zImage
imx6ull-14x14-emmc-4.3-480x272-c.dtb    imx6ull-14x14-emmc-7-1024x600-c.dtb   imx6ull-14x14-emmc-7-800x480-c.dtb-old  imx6ull-14x14-emmc-vga.dtb
root@ATK-IMX6U:/mnt#
//替换成功

重启开发板看看设备树有没有更新好 ,在设备树下已经看到了mpu6050这个节点了。

驱动的编写

        PS:很重要很重要的一点,千万不要忽略,在i2c_driver结构体中,成员.id_table千万不要空着,哪怕你是用设备树匹配的也不能空着。不然就会死活probe不上,这个问题我搞了几个小时。

具体驱动代码不赘述,用了Regmap的写法::

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include "mpu6050reg.h"

#define MPU6050_NAME "mpu6050"
#define MPU6050_CNT  1


struct mpu6050_dev{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct i2c_client *client;
    struct regmap *regmap;
    struct regmap_config regmap_config;

    unsigned char mpu6050_id;
    short int accelX;
    short int accelY;
    short int accelZ;
    short int gyroX;
    short int gyroY;
    short int gyroZ;
    short int temper;

};

static struct mpu6050_dev mpu6050dev;
//写一个寄存器
static void mpu6050_write_reg(struct mpu6050_dev *dev,u8 reg,u8 value)
{
    regmap_write(dev->regmap,reg,value);
}
//读一个
static unsigned char mpu6050_read_reg(struct mpu6050_dev *dev,u8 reg)
{
    u8 ret;
    unsigned int data;
    ret = regmap_read(dev->regmap,reg,&data);
    return(unsigned char)data;
}

static short int mpu6050_read_two(struct mpu6050_dev *dev,u8 reg)
{
    u8 ret;
    int data[2];
    short int data_ret;
    ret = regmap_read(dev->regmap,reg,&data[0]);
    ret = regmap_read(dev->regmap,reg+1,&data[1]);
    data_ret = (short int)data[0]*256+(short int)data[1];
    return(short int)data_ret;
}
//器件寄存器初始化
static int mpu6050_reg_init(void)
{
    mpu6050dev.mpu6050_id = mpu6050_read_reg(&mpu6050dev,MPU6050_WHO_AM_I);
    if(mpu6050dev.mpu6050_id!=0x68)
    {
        printk("error id\n");
        return -1;
    }
    printk("mpu6050_id:%x\n",mpu6050dev.mpu6050_id);
    mpu6050_write_reg(&mpu6050dev,MPU6050_PWR_MGMT_1,0x01);
    mpu6050_write_reg(&mpu6050dev,MPU6050_PWR_MGMT_2,0x00);
    mpu6050_write_reg(&mpu6050dev,MPU6050_SMPLRT_DIV,0x09);
    mpu6050_write_reg(&mpu6050dev,MPU6050_CONFIG,0x06);
    mpu6050_write_reg(&mpu6050dev,MPU6050_GYRO_CONFIG,0x18);
    mpu6050_write_reg(&mpu6050dev,MPU6050_ACCEL_CONFIG,0x18);
    return 0;
}
//获取mpu6050数据
void mpu6050_get(void)
{
    mpu6050dev.accelX = mpu6050_read_two(&mpu6050dev,MPU6050_ACCEL_XOUT_H);
    mpu6050dev.accelY = mpu6050_read_two(&mpu6050dev,MPU6050_ACCEL_YOUT_H);
    mpu6050dev.accelZ = mpu6050_read_two(&mpu6050dev,MPU6050_ACCEL_ZOUT_H);
    mpu6050dev.gyroX = mpu6050_read_two(&mpu6050dev,MPU6050_GYRO_XOUT_H);
    mpu6050dev.gyroY = mpu6050_read_two(&mpu6050dev,MPU6050_GYRO_YOUT_H);
    mpu6050dev.gyroZ = mpu6050_read_two(&mpu6050dev,MPU6050_GYRO_ZOUT_H);
    mpu6050dev.temper = mpu6050_read_two(&mpu6050dev,MPU6050_TEMP_OUT_H);
}


//字符操作集的打开,实现器件初始化
static int mpu6050_open(struct inode *inode,struct file *filp)
{
    filp->private_data = &mpu6050dev;
    mpu6050_reg_init();
    return 0;
}
//读数据
static ssize_t mpu6050_read(struct file *filp,char __user *buf,size_t cnt,loff_t *off)
{
    struct mpu6050_dev *dev = (struct mpu6050_dev *)filp->private_data;
    short int data[7];
    mpu6050_get();
    data[0] = dev->gyroX;
    data[1] = dev->gyroY;
    data[2] = dev->gyroZ;
    data[3] = dev->accelX;
    data[4] = dev->accelY;
    data[5] = dev->accelZ;
    data[6] = dev->temper;
    copy_to_user(buf,data,sizeof(data));
    return 0;
}
//释放,没有实现
static int mpu6050_release(struct inode *inode ,struct file *filp)
{
    return 0;
}

static const struct file_operations mpu6050_ops = {
    .owner = THIS_MODULE,
    .open = mpu6050_open,
    .read = mpu6050_read,
    .release = mpu6050_release,
};
//iic器件匹配后会执行
//这里主要是注册了字符设备,和regmap的初始化
static int mpu6050_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
    printk("mpu6050_probe\n");
    //remap的初始化
    mpu6050dev.regmap_config.reg_bits = 8;
    mpu6050dev.regmap_config.val_bits = 8;
    mpu6050dev.regmap = regmap_init_i2c(client,&mpu6050dev.regmap_config);

    if(mpu6050dev.major)
    {
        mpu6050dev.minor=0;
        mpu6050dev.devid = MKDEV(mpu6050dev.major,mpu6050dev.minor);
        register_chrdev_region(mpu6050dev.devid,MPU6050_CNT,MPU6050_NAME);
    }
    else
    {
        alloc_chrdev_region(&mpu6050dev.devid,0,MPU6050_CNT,MPU6050_NAME);
        mpu6050dev.major = MAJOR(mpu6050dev.devid);
        mpu6050dev.minor = MINOR(mpu6050dev.devid);
    }

    cdev_init(&mpu6050dev.cdev,&mpu6050_ops);
    cdev_add(&mpu6050dev.cdev,mpu6050dev.devid,MPU6050_CNT);

    mpu6050dev.class = class_create(THIS_MODULE,MPU6050_NAME);
    if(IS_ERR(mpu6050dev.class))
    {
        return PTR_ERR(mpu6050dev.class);
    }

    mpu6050dev.device = device_create(mpu6050dev.class,NULL,mpu6050dev.devid,NULL,MPU6050_NAME);
    if(IS_ERR(mpu6050dev.device))
    {
        return PTR_ERR(mpu6050dev.device);
    }
    mpu6050dev.client = client;
    return 0;
    
}
//驱动卸载函数
static int mpu6050_remove(struct i2c_client *client)
{
    
    cdev_del(&mpu6050dev.cdev);
    unregister_chrdev_region(mpu6050dev.devid,MPU6050_CNT);

    device_destroy(mpu6050dev.class,mpu6050dev.devid);
    class_destroy(mpu6050dev.class);
    regmap_exit(mpu6050dev.regmap);
    return 0;
}
//这里非常重要,哪怕用设备树配对也不能不写,否则会probe失败
 static const struct i2c_device_id ap3216c_id[] = {
    {"myiic,mpu6050", 0},
    {}
 };

static const struct of_device_id mpu6050_of_match[] = {
    {.compatible = "myiic,mpu6050"},
    {}
};


//iic驱动结构体
static struct i2c_driver mpu6050_driver = {
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "mpu6050_dev",
        .of_match_table = mpu6050_of_match,

    },
    .id_table = ap3216c_id,
};


static int __init mpu6050_init(void)
{
    int ret = 0;
    ret = i2c_add_driver(&mpu6050_driver);
    return ret;
}   

static void __exit mpu6050_exit(void)
{
    i2c_del_driver(&mpu6050_driver);
}


module_init(mpu6050_init);
module_exit(mpu6050_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Norgin");

测试APP

int main(int argc, char *argv[])

{

	int fd, err;

	char *filename;

	unsigned short data[3];

	unsigned short ax,ay,az,gx,gy,gz,temper;



	if(argc !=2){

		printf("error usage!\r\n");

		return -1;

	}

	filename = argv[1];

	fd = open(filename,O_RDWR);

	if(fd<0){

		printf("file %s open failed!\r\n",filename);

		return -1;

	}

	while (1)

	{

		err = read(fd,&data,sizeof(data));

		if(err == 0){

			ax = data[3];

			ay = data[4];

			az = data[5];

			gx = data[0];

			gy = data[1];

			gz = data[2];

			temper = data[6];

			printf("mpu6050 ax = %d,ay = %d,az = %d,gx = %d, gy = %d,gz = %d,temper = %.3f\r\n",ax,ay,az,gx,gy,gz,(((double)temper)/340.0f+36.53f));

		}

		usleep(2000000);

	}

	

	close(fd);

	return 0;

}

现象观测

        设备树配对成功

        执行测试APP成功读出ID和数据

单片机,又称单片微控制器,并非仅完成某一逻辑功能的芯片,而是将整个计算机系统集成到一个芯片上。其相当于一个微型计算机,与标准计算机相比,单片机仅缺少I/O设备。简而言之,一块芯片即构成了一台计算机。单片机具有体积小、质量轻、价格便宜的特点,为学习、应用和开发提供了便利条件。学习使用单片机是了解计算机原理与结构的最佳选择。 单片机的使用领域十分广泛,如智能仪表、实时工控、通讯设备、导航系统、家用电器等。一旦产品用上了单片机,就能实现产品的升级换代,使产品具有更高的智能化水平,常在产品名称前冠以“智能型”形容词,如智能型洗衣机等。此外,单片机在国防、电子玩具、厨房和家居设备等领域也有广泛的应用。 单片机技术还在不断发展,其在智能家居和智能城市、物联网设备和系统、边缘计算和边缘人工智能等领域的应用日益广泛。例如,通过单片机与传感器、执行器等设备的连接,可以实现智能家居设备的远程控制、自动化调节和智能化管理;作为物联网设备的核心控制单元,单片机能够实现物联网设备之间的互联互通,为物联网系统的运行提供基础支持;在边缘计算和边缘人工智能方面,单片机可以与人工智能技术结合,实现设备端数据的实时处理和智能分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值