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", &reg, &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函数里面。当初始化完成以后,大部分的函数都是在做读操作而已。我们可以根据芯片手册来了解它的配置。同时比较难理解的是它本次采用的驱动框架。其他没什么难的,和之气的输入子系统都是一样的。

实验现象:

0 0
原创粉丝点击