linux驱动开发:mma7660 sensor的配置
来源:互联网 发布:mac os 修复磁盘权限 编辑:程序博客网 时间:2024/06/11 06:32
上一章节有介绍过这颗IC,是freescale的一个低精度的重力传感器吧。我认为,可以区分8个方向。x,y,z三轴。精度6bit,采样值范围[-32,+32].还有测量shake和tap等数据。
这边的驱动没有自己写.只是有看懂它原先的驱动是如何实现和工作的.
它的驱动引入了一个 hwmon device的注册。具体的作用是在sys/class/hwmon下生成了一个目录hwmonx。
它可以添加你设备的一些属性attr,以此来方便用户去read那些属性的状态。网上也有说借此来导出内核的信息非常方便,几乎所有的sensor都喜欢这样用。我们可以借用内核导出x,y,z三轴的值,来方便查看数据.介于此,我们可以添加相关的属性,当访问这些属性文件时,实则是在调用对应的接口函数。比如读相应的寄存器,再显示给用户界面。
另外一个:传感器元件,都有一个特性。周期性的采样数据并上报数据。
我们再实现触摸屏驱动的时候,当你有手触摸时,立即便会上报一次数据,否则则没有.按键也是一样。
像有的温度传感器,基本上是每次间隔一定的时间,才能采样一次准确的数据。并不是立刻就能获得数据的.需要一定的时间。
所以这边引入了一个input子系统的扩展:input_polled_dev.
我们依旧把它作为输入子系统来实现。上报x,y,z三轴的绝对坐标.
所以整个驱动的框架没变。新多出来的功能是 属性导出功能,hwmon设备的引入,和input_polled_dev的引入。
程序我都有注释过,初始化完毕后,当有触发过中断时,程序便会更新一次TILT状态寄存器。此时我们可以去读我们需要的数据。根据读到的数据做出相应的判断.比如说是否旋转屏幕。想一想手机的自动旋转功能,当我们把屏幕横着放时,画面会对应做旋转,这里面也是通过sensor来识别当前的位置,根据位置做出画面的相应调整。
常用的传感器很多很多。温度传感器,压力传感器,靠近传感器,声音传感器,加速度传感器等等。
我们今天实现的只是很小的一类中的特定一个传感器而已。
驱动实例:
/* * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */#ifndef __LINUX_MMA7660_H#define __LINUX_MMA7660_H#include <linux/types.h>#ifdef __KERNEL__/* MMA7760 Registers */#define MMA7660_XOUT 0x00 // 6-bit output value X#define MMA7660_YOUT 0x01 // 6-bit output value Y#define MMA7660_ZOUT 0x02 // 6-bit output value Z#define MMA7660_TILT 0x03 // Tilt status#define MMA7660_SRST 0x04 // Sampling Rate Status#define MMA7660_SPCNT 0x05 // Sleep Count#define MMA7660_INTSU 0x06 // Interrupt Setup#define MMA7660_MODE 0x07 // Mode#define MMA7660_SR 0x08 // Auto-Wake/Sleep and Debounce Filter#define MMA7660_PDET 0x09 // Tap Detection#define MMA7660_PD 0x0a // Tap Debounce Countstruct mma7660_platform_data { /* eint connected to chip */ int irq; /* parameters for input device */ int poll_interval; int input_fuzz; int input_flat;};#endif /* __KERNEL__ */#endif /* __LINUX_MMA7660_H */
这边因为是挂在IIC总线上,同样,IIC设备的实例化我们还是按照老方法来做:
static struct mma7660_platform_data mma7660_pdata = { .irq = IRQ_EINT(11), .poll_interval = 100, .input_fuzz = 4, .input_flat = 4,};static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = { { I2C_BOARD_INFO("24c08", 0x50), }, { I2C_BOARD_INFO("mma7660", 0x4c), .platform_data = &mma7660_pdata, },};
同样,我们可以看到,在iic0上挂了两个设备,一个是24c08,一个是mma7660,因为slave addr的不同,我们可以访问到不同的从设备。
驱动代码:
#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/i2c.h>#include <asm/uaccess.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/delay.h>#include <linux/err.h>#include <linux/input-polldev.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h>#include <linux/mma7660.h>#include <mach/hardware.h>#define MMA7660_NAME "mma7660" #define POLL_INTERVAL 100#define INPUT_FUZZ 4#define INPUT_FLAT 4#define __need_retry(__v) (__v & (1 << 6))#define __is_negative(__v) (__v & (1 << 5))static const char *mma7660_bafro[] = { "Unknown", "Front", "Back"};static const char *mma7660_pola[] = { "Unknown", "Left", "Right", "Rsvd", "Rsvd", "Down", "Up", "Rsvd",};static const struct i2c_device_id mma7660_id[] ={ {"mma7660",0}, {}};struct mma7660_dev{ int poll_interval; int input_fuzz; int input_flat; struct device *hwmon_dev; //hwmon dev struct input_polled_dev *ip_dev; //input poll dev struct i2c_client * mma_iic_client; //iic client struct mma7660_platform_data *pdata; // platform data};static int last_tilt = 0;static int oper_mode;static struct input_dev *i_dev = NULL;static struct mma7660_dev *mma7660_struct = NULL;static int mma7660_read_tilt(struct i2c_client *client, int *tilt);static void mma7660_worker(struct work_struct *work){#if 0 int bafro, pola, shake, tap;#endif int val = 0; mma7660_read_tilt(mma7660_struct->mma_iic_client, &val);#if 0 bafro = val & 0x03; if(bafro != (last_tilt & 0x03)) { printk("%s\n", mma7660_bafro[bafro]); } pola = (val >> 2) & 0x07; if(pola != ((last_tilt >> 2) & 0x07)) { printk("%s\n", mma7660_pola[pola]); } shake = (val >> 5) & 0x01; if(shake && shake != ((last_tilt >> 5) & 0x01)) { printk("Shake\n"); } tap = (val >> 7) & 0x01; if(tap && tap != ((last_tilt >> 7) & 0x01)) { printk("Tap\n"); }#endif /* Save current status */ last_tilt = val;}DECLARE_WORK(mma7660_work, mma7660_worker);static irqreturn_t mma7660_interrupt(int irq, void *dev_id) { schedule_work(&mma7660_work); return IRQ_HANDLED;}/*read the data from reg TILT */static int mma7660_read_tilt(struct i2c_client *client, int *tilt){ int val; do { val = i2c_smbus_read_byte_data(client, MMA7660_TILT); if(val < 0) { dev_err(&client->dev, "Read register %02x failed, %d\n",MMA7660_TILT, val); return -EIO; } }while(__need_retry(val));//when bit6 is 1,can't read value,so do this to wait the TILT reg ready to read *tilt = (val & 0xff); return 0;}static int mma7660_read_xyz(struct i2c_client *client, int idx, int *xyz){ int val; do { val = i2c_smbus_read_byte_data(client, idx + MMA7660_XOUT); if(val < 0) { dev_err(&client->dev, "Read register %02x failed, %d\n",idx + MMA7660_XOUT, val); return -EIO; } }while(__need_retry(val)); *xyz = __is_negative(val) ? (val | ~0x3f) : (val & 0x3f); return 0;}static int mma7660_initial(struct i2c_client *client){ int val; //1.set mode to stand by mode first by set bit0 (mode bit)to 0 i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00); mdelay(10); // 2.set mode to test mode,it should set in stand by mode by set the bit 2(ton bit) to 1 i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x04); mdelay(10); //3.in test mode can set the reg MMA7660_XOUT,MMA7660_YOUT,MMA7660_ZOUT //3.1 clear the bit6(alert bit) i2c_smbus_write_byte_data(client, MMA7660_XOUT, 0x3f); //3.2 clear the bit6(alert bit) i2c_smbus_write_byte_data(client, MMA7660_YOUT, 0x3f); //3.3 clear the bit6(alert bit) i2c_smbus_write_byte_data(client, MMA7660_ZOUT, 0x3f); val = i2c_smbus_read_byte_data(client, MMA7660_ZOUT); if(val != 0x3f) { printk("2.test write and read reg 0x02 but get wrong data: %d, no dev!!!\n",val); return -ENODEV; } //reset to stand by mode i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00); mdelay(10); //AMSR[2:0]bit0-bit2 :samples rate: 001----64 samples/sec in Active and Auto-Sleep Mode //AWSR[1:0]:bit4-bit3: samples rate: 01-----16 Samples/Second Auto-Wake Mode //bit7-bit5 :samples rate 001----- 3samples/sec TILT i2c_smbus_write_byte_data(client, MMA7660_SR, ((2<<5) | (1<<3) | 1)); //sleep count:160,when reach to the timeout time,will go in auto wake mode,ps i2c_smbus_write_byte_data(client, MMA7660_SPCNT, 0xA0); //Tap detection threshold is ±4 counts //X-axis,Y-axis,Z-axis is enabled for tap detection i2c_smbus_write_byte_data(client, MMA7660_PDET, 4); //Tap detection debounce filtering requires 16 adjacent tap detection tests to be the same to trigger a tap event and set the Tap //bit in the TILT (0x03) register, and optionally set an interrupt if PDINT is set in the INTSU (0x06) register. Tap detection //response time is nominally 1.04 ms. i2c_smbus_write_byte_data(client, MMA7660_PD, 15); /* Enable interrupt except exiting Auto-Sleep */ //FBINT bit 0 ---1: Front/Back position change causes an interrupt //PLINT bit 1---1:Up/Down/Right/Left position change causes an interrupt //PDINT bit 2---1: Successful tap detection causes an interrupt //ASINT bit 3---0: Exiting Auto-Sleep does not cause an interrupt //GINT bit4---0:There is not an automatic interrupt after everymeasurement //SHINTX bit 5---1:Shake detected on the X-axis causes an interrupt, and sets the Shake bit in the TILT register //SHINTY bit6---1: Shake detected on the Y-axis causes an interrupt, and sets the Shake bit in the TILT register //SHINTZ bit7---1: Shake detected on the Z-axis causes an interrupt, and sets the Shake bit in the TILT register. i2c_smbus_write_byte_data(client, MMA7660_INTSU, 0xe7); /* IPP, Auto-wake, auto-sleep and standby */ //Active mode: bit0 set to 1 ,bit 2 set o 0 // AWE bit3----1:Auto-Wake is enabled //ASE bit4----1: Auto-Sleep is enabled //SCPS bit5---0: The prescaler is divide-by-1. The 8-bit internal Sleep Counter input clock is the samples per second set by //AMSR[2:0], so the clock range is 120 Hz to 1 Hz depending on AMSR[2:0] setting. Sleep Counter timeout range is //256 times the prescaled clock //IPP bit6---1: Interrupt output INT is push-pull //IAH bit7---0: Interrupt output INT is active low i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x59); mdelay(10); /* Save current tilt status */ mma7660_read_tilt(client, &last_tilt); return 0;}/* attr*/static ssize_t mma7660_show_regs(struct device *dev,struct device_attribute *attr, char *buf){ int reg, val; int i, len = 0; for(reg = 0; reg < 0x0b; reg++) { val = i2c_smbus_read_byte_data(mma7660_struct->mma_iic_client, reg); len += sprintf(buf + len, "REG: 0x%02x = 0x%02x ...... [ ", reg, val); for(i = 7; i >= 0; i--) { len += sprintf(buf + len, "%d", (val >> i) & 1); if((i % 4) == 0) { len += sprintf(buf + len, " "); } } len += sprintf(buf + len, "]\n"); } return len;}static ssize_t mma7660_write_reg(struct device *dev,struct device_attribute *attr, const char *buf, size_t count){ unsigned int reg, val; int ret; ret = sscanf(buf, "%x %x", ®, &val); if(ret == 2) { if (reg >= 0 && reg <= 0x0a) { i2c_smbus_write_byte_data(mma7660_struct->mma_iic_client, reg, val); val = i2c_smbus_read_byte_data(mma7660_struct->mma_iic_client, reg); printk("REG: 0x%02x = 0x%02x\n", reg, val); } } return count;}static ssize_t mma7660_show_xyz_g(struct device *dev,struct device_attribute *attr, char *buf){ int axis[3]; int i; for (i = 0; i < 3; i++) { mma7660_read_xyz(mma7660_struct->mma_iic_client, i, &axis[i]); } return sprintf(buf, "%3d, %3d, %3d\n", axis[0], axis[1], axis[2]);}static ssize_t mma7660_show_axis_g(struct device *dev,struct device_attribute *attr, char *buf){ int n = to_sensor_dev_attr(attr)->index; int val; mma7660_read_xyz(mma7660_struct->mma_iic_client, n, &val); return sprintf(buf, "%3d\n", val);}static ssize_t mma7660_show_tilt(struct device *dev,struct device_attribute *attr, char *buf){ int val = 0, len = 0; mma7660_read_tilt(mma7660_struct->mma_iic_client, &val); len += sprintf(buf + len, "%s", mma7660_bafro[val & 0x03]); len += sprintf(buf + len, ", %s", mma7660_pola[(val >> 2) & 0x07]); if(val & (1 << 5)) { len += sprintf(buf + len, ", Tap"); } if(val & (1 << 7)) { len += sprintf(buf + len, ", Shake"); } len += sprintf(buf + len, "\n"); return len;}static SENSOR_DEVICE_ATTR(registers, S_IRUGO | S_IWUGO,mma7660_show_regs, mma7660_write_reg, 0);static SENSOR_DEVICE_ATTR(x_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 0);static SENSOR_DEVICE_ATTR(y_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 1);static SENSOR_DEVICE_ATTR(z_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 2);static SENSOR_DEVICE_ATTR(all_axis_g, S_IRUGO, mma7660_show_xyz_g, NULL, 0);static SENSOR_DEVICE_ATTR(tilt_status, S_IRUGO, mma7660_show_tilt, NULL, 0);static struct attribute* mma7660_attrs[] = { &sensor_dev_attr_registers.dev_attr.attr, &sensor_dev_attr_x_axis_g.dev_attr.attr, &sensor_dev_attr_y_axis_g.dev_attr.attr, &sensor_dev_attr_z_axis_g.dev_attr.attr, &sensor_dev_attr_all_axis_g.dev_attr.attr, &sensor_dev_attr_tilt_status.dev_attr.attr, NULL};static const struct attribute_group mma7660_group = { .attrs = mma7660_attrs,};//Input interfacesstatic void mma7660_report_abs(void){ int axis[3]; int i; for(i = 0; i < 3; i++) { mma7660_read_xyz(mma7660_struct->mma_iic_client, i, &axis[i]); } input_report_abs(mma7660_struct->ip_dev->input, ABS_X, axis[0]); input_report_abs(mma7660_struct->ip_dev->input, ABS_Y, axis[1]); input_report_abs(mma7660_struct->ip_dev->input, ABS_Z, axis[2]); input_sync(mma7660_struct->ip_dev->input); //printk("3-Axis ... %3d, %3d, %3d\n", axis[0], axis[1], axis[2]);}static void mma7660_dev_poll(struct input_polled_dev *dev){ mma7660_report_abs();}/*****int (*probe)(struct i2c_client *, const struct i2c_device_id *)*****/static int mma7660_probe(struct i2c_client *uc_i2c_client, const struct i2c_device_id * uc_i2c_id_table){ int err=0; //check iic if(!i2c_check_functionality(uc_i2c_client->adapter,I2C_FUNC_I2C)) { err = -ENODEV; goto FAIL_CHECK_FUNC; } //check platdata if(!uc_i2c_client->dev.platform_data) { err = -ENODATA; goto FAIL_NO_PLATFORM_DATA; } //allooc buf mma7660_struct = kzalloc(sizeof(struct mma7660_dev),GFP_KERNEL); if(!mma7660_struct) { err=-ENOMEM; goto FAIL_KZALLOC; } //initial start mma7660_struct->mma_iic_client = uc_i2c_client; mma7660_struct->pdata = uc_i2c_client->dev.platform_data; mma7660_struct->poll_interval =((mma7660_struct->pdata->poll_interval >0) ? mma7660_struct->pdata->poll_interval : POLL_INTERVAL); mma7660_struct->input_flat =((mma7660_struct->pdata->input_flat >0) ? mma7660_struct->pdata->input_flat : INPUT_FLAT); mma7660_struct->input_fuzz =((mma7660_struct->pdata->input_fuzz >0) ? mma7660_struct->pdata->input_fuzz : INPUT_FUZZ); if(mma7660_struct->pdata->irq) { mma7660_struct->mma_iic_client->irq = mma7660_struct->pdata->irq; } else { err = -ENODATA; printk("3.the platformdata no irq data\n"); goto FAIL_NO_IRQ_DATA; } //initial the dev err = mma7660_initial(mma7660_struct->mma_iic_client); if(err < 0) { goto FAIL_MMA7660_INIT; } err = sysfs_create_group(&mma7660_struct->mma_iic_client->dev.kobj, &mma7660_group); if(err) { printk("4.create sysfs group failed!\n"); goto FAIL_CREATE_GROUP; } // register to hwmon device mma7660_struct->hwmon_dev = hwmon_device_register(&mma7660_struct->mma_iic_client->dev); if(IS_ERR(mma7660_struct->hwmon_dev)) { err = PTR_ERR(mma7660_struct->hwmon_dev); printk("5.hwmon register failed!\n"); goto FAIL_HWMON_REGISTER; } //alloc the input poll device mma7660_struct->ip_dev = input_allocate_polled_device(); if(!mma7660_struct->ip_dev) { err = -ENOMEM; printk("6.alloc poll device failed!\n"); goto FAIL_ALLLOC_INPUT_PDEV; } mma7660_struct->ip_dev->poll = mma7660_dev_poll; mma7660_struct->ip_dev->poll_interval =mma7660_struct->pdata->poll_interval; i_dev = mma7660_struct->ip_dev->input; i_dev->name = MMA7660_NAME; i_dev->id.bustype = BUS_I2C; i_dev->id.vendor = 0x12FA; i_dev->id.product = 0x7660; i_dev->id.version = 0x0100; i_dev->dev.parent = &mma7660_struct->mma_iic_client->dev; set_bit(EV_ABS, i_dev->evbit); set_bit(ABS_X, i_dev->absbit); set_bit(ABS_Y, i_dev->absbit); set_bit(ABS_Z, i_dev->absbit); input_set_abs_params(i_dev, ABS_X, -512, 512, mma7660_struct->input_fuzz, mma7660_struct->input_flat); input_set_abs_params(i_dev, ABS_Y, -512, 512, mma7660_struct->input_fuzz, mma7660_struct->input_flat); input_set_abs_params(i_dev, ABS_Z, -512, 512, mma7660_struct->input_fuzz, mma7660_struct->input_flat); err = input_register_polled_device(mma7660_struct->ip_dev); if(err) { printk("7.register poll device failed!"); goto FAIL_INPUT_REGISTER_PDEV; } //register interrupt handle err = request_irq(mma7660_struct->pdata->irq, mma7660_interrupt,IRQF_TRIGGER_FALLING, MMA7660_NAME, NULL); if(err) { printk("8.request irq failed!\n"); goto FAIL_REQUEST_IRQ; } printk("mma7660 driver probe success!\n"); return 0;FAIL_REQUEST_IRQ: input_unregister_polled_device(mma7660_struct->ip_dev);FAIL_INPUT_REGISTER_PDEV: input_free_polled_device(mma7660_struct->ip_dev);FAIL_ALLLOC_INPUT_PDEV: hwmon_device_unregister(mma7660_struct->hwmon_dev);FAIL_HWMON_REGISTER: sysfs_remove_group(&mma7660_struct->mma_iic_client->dev.kobj,&mma7660_group);FAIL_CREATE_GROUP:FAIL_MMA7660_INIT:FAIL_NO_IRQ_DATA: kfree(mma7660_struct);FAIL_KZALLOC:FAIL_NO_PLATFORM_DATA:FAIL_CHECK_FUNC: return err;}/*****int (*remove)(struct i2c_client *)*****/static int mma7660_remove(struct i2c_client *uc_i2c_client){ disable_irq(mma7660_struct->pdata->irq); free_irq(mma7660_struct->pdata->irq,NULL); input_unregister_polled_device(mma7660_struct->ip_dev); input_free_polled_device(mma7660_struct->ip_dev); hwmon_device_unregister(mma7660_struct->hwmon_dev); sysfs_remove_group(&mma7660_struct->mma_iic_client->dev.kobj,&mma7660_group); return 0;}static int mma7660_suspend(struct i2c_client *client, pm_message_t state){ int ret; oper_mode = i2c_smbus_read_byte_data(client, MMA7660_MODE); ret = i2c_smbus_write_byte_data(client, MMA7660_MODE, 0); if(ret) { printk("%s: set mode (0) for suspend failed, ret = %d\n",MMA7660_NAME, ret); } return 0;}static int mma7660_resume(struct i2c_client *client){ int ret; ret = i2c_smbus_write_byte_data(client, MMA7660_MODE, oper_mode); if (ret) { printk("%s: set mode (%d) for resume failed, ret = %d\n",MMA7660_NAME, oper_mode, ret); } return 0;}static struct i2c_driver mma7660_drv ={ .driver = { .name = MMA7660_NAME, .owner= THIS_MODULE, }, .probe = mma7660_probe, .remove = mma7660_remove, .suspend = mma7660_suspend, .resume = mma7660_resume, //match use .id_table = mma7660_id,};int __init mma7660_init(void){ i2c_add_driver(&mma7660_drv); return 0;}void __exit mma7660_exit(void){ i2c_del_driver(&mma7660_drv);}module_init(mma7660_init);module_exit(mma7660_exit);MODULE_LICENSE("GPL");
整个驱动最难理解的地方应该还是在probe函数里面。当初始化完成以后,大部分的函数都是在做读操作而已。我们可以根据芯片手册来了解它的配置。同时比较难理解的是它本次采用的驱动框架。其他没什么难的,和之气的输入子系统都是一样的。
实验现象:
- linux驱动开发:mma7660 sensor的配置
- mma7660(gsensor)的hwmon驱动
- linux驱动调试技巧:灌寄存器---------以mma7660为例
- linux驱动调试技巧:灌寄存器---------以mma7660为例
- linux驱动调试技巧:灌寄存器---------以mma7660为例
- Android Sensor驱动开发实例
- Linux驱动开发--S3C6410GPIO的配置
- linux驱动开发环境的配置
- mma7660(G-senser) 驱动
- 从Tiny4412--mma7660 gsensor驱动探索Linux内核I2C总线驱动框架
- freescale的MMA7660-gsensor
- freescale的MMA7660-gsensor
- linux sensor 驱动之 psensor(proximity-sensor距离传感器)
- 配置驱动的开发
- 配置驱动的开发
- Sensor在Linux内核中的驱动分析
- 如何增加gsensor驱动(MMA7660)
- android 如何增加gsensor驱动(MMA7660)
- linux下的mysql安装方式一(yum安装)
- 基于springboot+mybatis的微信公众号开发第一篇-基本架构
- JDBC
- HTML5高级之position(定位)
- 已经使用ant的项目如何利用maven来管理依赖
- linux驱动开发:mma7660 sensor的配置
- Eclipse在windows中的快捷键
- pycharm 2016 注册码
- vue插件篇
- 13.6 Thread类自定义线程类
- 打印菱形
- Spring框架的IOC/DI的自己实现
- 单例模式
- Android应用进程间通信之Messenger信使使用及源码浅析