MPU6050帶字符驅動的i2c從設備驅動1
阿新 • • 發佈:2017-09-06
val 添加 space res add 操作方法 驅動 move 初始化 開幹:
1、閑言碎語
這個驅動,越寫覺的越簡單,入門難,入門之後感覺還好。Linux開發還是比較友好的。
2、編寫MPU6050帶字符驅動的i2c從設備驅動 要實現的功能就是,將MPU6050作為字符驅動,在應用層,對其進行讀數據。實現簡單的功能。在前面的分析和實踐中,可以看到實現字符驅動主要是實現file_operation中的方法,註冊初始化cdev,讓cdev和file_opration產生聯系,字符驅動的初始化通過module_init來聲明。實現i2c從設備驅動,主要是i2c_client和i2c_driver通過名字匹配,然後調用probe函數對設備進行初始化。那麽,實現字符驅動的i2c從設備驅動,其實就是在i2c從設備驅動中添加字符驅動操作方法,從而在/dev中產生設備節點,讓用戶可以通過Linux application的API對其進行操作。 本文實現了帶字符驅動的i2c從設備驅動,然後編寫了用戶層測試程序,測試結果正確。下面對部分代碼進行分析。
定義i2c_driver,file_operations,
static struct i2c_driver mpu6xxx_driver = {
.driver = {
.name = "mpu6xxx",
.owner = THIS_MODULE,
},
.class = I2C_CLASS_HWMON,
.id_table = mpu6xxx_ids,
.probe = mpu6xxx_probe,
.remove = mpu6xxx_remove,
};
主要實現其中的probe函數。
struct file_operations mpu6xxx_fops = {
owner : THIS_MODULE,
unlocked_ioctl : mpu6xxx_ioctl,
open : mpu6xxx_open,
release : mpu6xxx_release,
};
主要實現其中的ioctl函數。
probe函數實現: static int mpu6xxx_probe(struct i2c_client *client, const struct i2c_device_id *id){
u16 version; int result,retval = -1; struct mpu6xxx_data *mpu6xxx; dev_t dev;
printk(KERN_DEBUG "mpu6xxx driver probe... \n");
if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){ retval = -EINVAL; goto failed; } //核查i2c驅動
mpu6xxx = kzalloc(sizeof(struct mpu6xxx_data),GFP_KERNEL);
//分配用戶數據
if(!mpu6xxx)
{
retval = -ENOMEM;
goto failed;
}
memset(&mpu6xxx->gyro,0,sizeof(struct sub_sensor)); memset(&mpu6xxx->accel,0,sizeof(struct sub_sensor)); mutex_init(&mpu6xxx->lock);
mpu6xxx->client = client; i2c_set_clientdata(client,mpu6xxx); //用來在驅動中獲取數據 this_client = client;
version = i2c_smbus_read_byte_data(client,MPU6050_REG_WHO_AM_I); if(version != 0x70) { printk("error retval %d , version %.2x \n",retval , version); retval = -2; goto failed; } //檢查version
result = alloc_chrdev_region(&dev,this_major,1,"mpu6xxx"); if(result < 0) { retval = result; goto failed; }
this_major = MAJOR(dev); cdev_init(&mpu6xxx->cdev,&mpu6xxx_fops); mpu6xxx->cdev.owner = THIS_MODULE;
result = cdev_add(&mpu6xxx->cdev,dev,1); if(result) { retval = result; goto failed; }
mpu_cls = class_create(THIS_MODULE,"mpu6xxx"); if(IS_ERR(mpu_cls)) { retval = -3; goto failed; }
mpu_device = device_create(mpu_cls,NULL,dev,NULL,"mpu6xxx"); if(IS_ERR(mpu_device)) { class_destroy(mpu_cls); } //註冊cdev,以及在/dev中產生節點
mpu6xxx_reset(mpu6xxx); mpu6xxx_disable(mpu6xxx);
retval = 0;
printk(KERN_DEBUG "mpuxxx probe succeed...\n");
return retval;
failed:
return retval; };
ioctl實現:
static int mpu6xxx_ioctl(struct file *file, unsigned int cmd,unsigned long arg) { int re = -1; void __user *argp = (void __user *)arg; struct mpu6xxx_data *mpu6xxx = i2c_get_clientdata(this_client);
printk("ioctl ... cmd %d mpucmd %d BUFFERSIZE %d\n",cmd,MPU_IOCTL_GETGYRO,BUFFERSIZE);
switch(cmd) { case MPU_IOCTL_GETGYRO: // ioctl這個cmd碼,可以參考博客http://blog.csdn.net/shanshanpt/article/details/19897897 mutex_lock(&(mpu6xxx->lock) ); //通過互斥操作來避免頻繁讀寫
re = mpu6xxx_read_data(mpu6xxx,0); if(re != 0) return re; printk("x : %d \n", mpu6xxx->gyro.x.value); if(copy_to_user(argp,&mpu6xxx->gyro,sizeof(struct sub_sensor))) //將數據拷貝到用戶空間 { printk("failed copy data .. \n"); mutex_unlock(&mpu6xxx->lock); return -EFAULT; } printk("ioctl succed...\n"); mutex_unlock(&mpu6xxx->lock); break ; case MPU_IOCTL_GETACCEL: mutex_lock(&(mpu6xxx->lock) );
re = mpu6xxx_read_data(mpu6xxx,1); if(re != 0) return re; if(copy_to_user(argp,&mpu6xxx->accel,sizeof(struct sub_sensor))) { mutex_unlock(&(mpu6xxx->lock) ); printk("failed copy data .. \n"); return -EFAULT; }
mutex_unlock(&(mpu6xxx->lock) );
break; default: break; }
return 0; }
驅動加載結果:
應用層代碼結果:
在動6050時,數據會變動。並且數據大小正常。
2、編寫MPU6050帶字符驅動的i2c從設備驅動 要實現的功能就是,將MPU6050作為字符驅動,在應用層,對其進行讀數據。實現簡單的功能。在前面的分析和實踐中,可以看到實現字符驅動主要是實現file_operation中的方法,註冊初始化cdev,讓cdev和file_opration產生聯系,字符驅動的初始化通過module_init來聲明。實現i2c從設備驅動,主要是i2c_client和i2c_driver通過名字匹配,然後調用probe函數對設備進行初始化。那麽,實現字符驅動的i2c從設備驅動,其實就是在i2c從設備驅動中添加字符驅動操作方法,從而在/dev中產生設備節點,讓用戶可以通過Linux application的API對其進行操作。 本文實現了帶字符驅動的i2c從設備驅動,然後編寫了用戶層測試程序,測試結果正確。下面對部分代碼進行分析。
probe函數實現: static int mpu6xxx_probe(struct i2c_client *client, const struct i2c_device_id *id){
u16 version; int result,retval = -1; struct mpu6xxx_data *mpu6xxx; dev_t dev;
printk(KERN_DEBUG "mpu6xxx driver probe... \n");
if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){ retval = -EINVAL; goto failed; } //核查i2c驅動
memset(&mpu6xxx->gyro,0,sizeof(struct sub_sensor)); memset(&mpu6xxx->accel,0,sizeof(struct sub_sensor)); mutex_init(&mpu6xxx->lock);
mpu6xxx->client = client; i2c_set_clientdata(client,mpu6xxx); //用來在驅動中獲取數據 this_client = client;
version = i2c_smbus_read_byte_data(client,MPU6050_REG_WHO_AM_I); if(version != 0x70) { printk("error retval %d , version %.2x \n",retval , version); retval = -2; goto failed; } //檢查version
result = alloc_chrdev_region(&dev,this_major,1,"mpu6xxx"); if(result < 0) { retval = result; goto failed; }
this_major = MAJOR(dev); cdev_init(&mpu6xxx->cdev,&mpu6xxx_fops); mpu6xxx->cdev.owner = THIS_MODULE;
result = cdev_add(&mpu6xxx->cdev,dev,1); if(result) { retval = result; goto failed; }
mpu_cls = class_create(THIS_MODULE,"mpu6xxx"); if(IS_ERR(mpu_cls)) { retval = -3; goto failed; }
mpu_device = device_create(mpu_cls,NULL,dev,NULL,"mpu6xxx"); if(IS_ERR(mpu_device)) { class_destroy(mpu_cls); } //註冊cdev,以及在/dev中產生節點
mpu6xxx_reset(mpu6xxx); mpu6xxx_disable(mpu6xxx);
retval = 0;
printk(KERN_DEBUG "mpuxxx probe succeed...\n");
return retval;
failed:
return retval; };
ioctl實現:
static int mpu6xxx_ioctl(struct file *file, unsigned int cmd,unsigned long arg) { int re = -1; void __user *argp = (void __user *)arg; struct mpu6xxx_data *mpu6xxx = i2c_get_clientdata(this_client);
printk("ioctl ... cmd %d mpucmd %d BUFFERSIZE %d\n",cmd,MPU_IOCTL_GETGYRO,BUFFERSIZE);
switch(cmd) { case MPU_IOCTL_GETGYRO: // ioctl這個cmd碼,可以參考博客http://blog.csdn.net/shanshanpt/article/details/19897897 mutex_lock(&(mpu6xxx->lock) ); //通過互斥操作來避免頻繁讀寫
re = mpu6xxx_read_data(mpu6xxx,0); if(re != 0) return re; printk("x : %d \n", mpu6xxx->gyro.x.value); if(copy_to_user(argp,&mpu6xxx->gyro,sizeof(struct sub_sensor))) //將數據拷貝到用戶空間 { printk("failed copy data .. \n"); mutex_unlock(&mpu6xxx->lock); return -EFAULT; } printk("ioctl succed...\n"); mutex_unlock(&mpu6xxx->lock); break ; case MPU_IOCTL_GETACCEL: mutex_lock(&(mpu6xxx->lock) );
re = mpu6xxx_read_data(mpu6xxx,1); if(re != 0) return re; if(copy_to_user(argp,&mpu6xxx->accel,sizeof(struct sub_sensor))) { mutex_unlock(&(mpu6xxx->lock) ); printk("failed copy data .. \n"); return -EFAULT; }
mutex_unlock(&(mpu6xxx->lock) );
break; default: break; }
return 0; }
驅動加載結果:
應用層代碼結果:
在動6050時,數據會變動。並且數據大小正常。
MPU6050帶字符驅動的i2c從設備驅動1