学了这么久驱动开发,是时候要自己做一下了。
修改设备树
本次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和数据