android Sensor 驱动编写--opt3001光感驱动为例

分析Android sensor

Android sensor Framework 层以及APP如何读取sensor 数据。网上有很多文章不再累述。

由于我使用的是Android 5.1(kernel 3.10) ,不自带 opt3001的驱动,我发现kernel 4.10 已经自带opt3001的驱动。我不会为了一个驱动而换掉 kernel。而且 kernel 4.10 opt3001的驱动高度依赖与iio。两个kernel 版本的iio修改较大。移植也不是简单的复制。索性不看 kernel 4.10 上的代码。我们分析 Android 5.1(kernel 3.10)的代码。

其实写一个驱动读取 opt3001 中的数据并不难。看一看opt3001 的数据手册,就几个寄存器。配置也很简单。

然后我们看一下Android 是如何从kernel 中读取 光感数据的。自然是去看 HAL层的代码。我可以在hardware目录下找到LightSensor.cpp 。一般我们拿到的源码 HAL层到Framework,数据通路都是好的。不需要修改。
我们分析一下 LightSensor.cpp 我们可以看到class LightSensor 继承自 SamsungSensorBase 。SamsungSensorBase 又继承自 SensorBase。分析这几个类。并没有什么难的。

我们先看 SensorBase。

SensorBase::SensorBase(
        const char* dev_name,
        const char* data_name)
    : dev_name(dev_name), data_name(data_name),
      dev_fd(-1), data_fd(-1)
{
    if (data_name) {
        data_fd = openInput(data_name);
    }
}
int SensorBase::open_device() {
    if (dev_fd<0 && dev_name) {
        dev_fd = open(dev_name, O_RDONLY);
        LOGE_IF(dev_fd<0, "Couldn't open %s (%s)", dev_name, strerror(errno));
    }
    return 0;
}
static int getInput(const char *inputName)
{
    int fd = -1;
    unsigned i; 
    static bool first = true;
    static struct input_dev dev[255];

    if (first) { 
        int fd = -1;
        const char *dirname = "/dev/input";
        char devname[PATH_MAX];
        char *filename;
        DIR *dir;
        struct dirent *de;

        first = false;
        for (i = 0; i < sizeof(dev)/sizeof(dev[0]); i++) {
            dev[i].fd = -1;
            dev[i].name[0] = '\0';
        } 
        i = 0;

        dir = opendir(dirname);
        if (dir == NULL)
            return -1;
        strcpy(devname, dirname);
        filename = devname + strlen(devname);
        *filename++ = '/';
        while ((de = readdir(dir))) {
            if (de->d_name[0] == '.' &&
                (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
                    continue;
            strcpy(filename, de->d_name);
            fd = open(devname, O_RDONLY);
            if (fd >= 0) {
                char name[80];
                if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) >= 1) {
                    dev[i].fd = fd;
                    strncpy(dev[i].name, name, sizeof(dev[i].name));
                }
            }
            i++;
        }
        closedir(dir);
    }

    for (i = 0; i < sizeof(dev)/sizeof(dev[0]); i++) {
        if (!strncmp(inputName, dev[i].name, sizeof(dev[i].name))) {
            fd = dev[i].fd;
            break;
        }
    }
    LOGE_IF(fd < 0, "couldn't find '%s' input device", inputName);

    return fd;
}
int SensorBase::openInput(const char* inputName) {
    int fd = -1;
    const char *dirname = "/dev/input";
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;

    return getInput(inputName);

    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
                (de->d_name[1] == '\0' ||
                        (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;
        strcpy(filename, de->d_name);
        fd = open(devname, O_RDONLY);
        if (fd>=0) {
            char name[80];
            if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
                name[0] = '\0';
            }
            if (!strcmp(name, inputName)) {
                strcpy(input_name, filename);
                break;
            } else {
                close(fd);
                fd = -1;
            }
        }
    }
    closedir(dir);
    LOGE_IF(fd<0, "couldn't find '%s' input device", inputName);
    return fd;
}

这段代码很简单了。可以看出:
这里要用到 dev_name 和 data_name。
dev_name 用open()函数打开,这个最熟悉不过了。
data_name 从代码来看 是一个input 设备,从 getInput() 方法中很清楚的看到,程序要在 /dev/input目录下打开所有input 设备, 查找一个name 匹配 data_name的设备。

我们看一下 SamsungSensorBase

SamsungSensorBase::SamsungSensorBase(const char *dev_name,
                                     const char *data_name,
                                     int sensor_code)
    : SensorBase(dev_name, data_name),
      mEnabled(false), //cvt_zxl modify
      mHasPendingEvent(false),
      mInputReader(4),
      mSensorCode(sensor_code),
      mLock(PTHREAD_MUTEX_INITIALIZER)
{
    mPendingEvent.version = sizeof(sensors_event_t);
    memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));
    if (!data_fd)
        return;
    mInputSysfsEnable = makeSysfsName(input_name, "enable");
    if (!mInputSysfsEnable) {
        ALOGE("%s: unable to allocate mem for %s:enable", __func__,
             data_name);
        return;
    }
    mInputSysfsPollDelay = makeSysfsName(input_name, "poll_delay");
    if (!mInputSysfsPollDelay) {
        ALOGE("%s: unable to allocate mem for %s:poll_delay", __func__,
             data_name);
        return;
    }

    int flags = fcntl(data_fd, F_GETFL, 0);
    fcntl(data_fd, F_SETFL, flags | O_NONBLOCK);
}

int SamsungSensorBase::readEvents(sensors_event_t* data, int count)
{
    if (count < 1)
        return -EINVAL;

    pthread_mutex_lock(&mLock);
    int numEventReceived = 0;

    if (mHasPendingEvent) {
        mHasPendingEvent = false;
        if (mEnabled) {
            mPendingEvent.timestamp = getTimestamp();
            *data = mPendingEvent;
            numEventReceived++;
        }
        goto done;
    }

    input_event const* event;
    while (count && mInputReader.readEvent(data_fd, &event)) {
        if (event->type == EV_ABS) {
            if (event->code == mSensorCode) {
                if (mEnabled && handleEvent(event)) {
                    mPendingEvent.timestamp = timevalToNano(event->time);
                    *data++ = mPendingEvent;
                    count--;
                    numEventReceived++;
                }
            }
        }
        mInputReader.next();
    }

done:
    pthread_mutex_unlock(&mLock);
    return numEventReceived;

}

readEvents()这个方法 显然是在读input 设备上报的数据。充这个方法我们可以看到 input event 的 type 必须是 EV_ABS ,还有 code 必须是 mSensorCode ,mSensorCode 这个参数很显然 需要具体的实现类。
在 SamsungSensorBase 的构造函数中,可以看到 mSensorCode。
读取到 event 之后,把event 交给 handleEvent。handleEvent(),也由具体实现类来处理。
是时候去看一下 LightSensor 了

LightSensor::LightSensor()
    : SamsungSensorBase("/dev/lightsensor", "lightsensor-level", ABS_MISC)
{
    mPendingEvent.sensor = ID_L;
    mPendingEvent.type = SENSOR_TYPE_LIGHT;
    mPreviousLight = -1;
}

int LightSensor::handleEnable(int en) {
    mPreviousLight = -1;
    int flags = en ? 1 : 0;
    int err = 0;
    if (flags != mEnabled) {
        if (!mEnabled) {
            open_device();
        }
        err = ioctl(dev_fd, LIGHTSENSOR_IOCTL_ENABLE, &flags);
        err = err<0 ? -errno : 0;
        ALOGE_IF(err, "LIGHTSENSOR_IOCTL_ENABLE failed (%s)", strerror(-err));
        if (!err) {
            mEnabled = en ? 1 : 0;
        }
        if (!mEnabled) {
            close_device();
        }
    }
    return err;
}

bool LightSensor::handleEvent(input_event const *event) {
    if (event->value == -1) {
        return false;
    }
    mPendingEvent.light = indexToValue(event->value);
   // mPendingEvent.light = event->value;
    if (mPendingEvent.light != mPreviousLight) {
        mPreviousLight = mPendingEvent.light;
        return true;
    }
    return true;
}

float LightSensor::indexToValue(size_t index) const {
    /* Driver gives a rolling average adc value.  We convert it lux levels. */
    //subingxi modify
    float val;
    val = index/1000.0;
    return val;
}

从这里我们就看到了:
dev_name 为 /dev/lightsensor
data_name 为 lightsensor-level
mSensorCode 为 ABS_MISC
看 handleEvent(); 就知道,最终数据被填充到了 mPendingEvent.light。

整个过程就非常清晰了。

另外看一眼 handleEnable 就知道了 dev_name 用 ioctl(dev_fd, LIGHTSENSOR_IOCTL_ENABLE, &flags); 来控制使能。

驱动要求

知道这些,写驱动 就非常简单了。
我们需要写一个 miscdevice :

  • name 为 lightsensor
  • 支持ioctl ,LIGHTSENSOR_IOCTL_ENABLE 控制使能

我们要写一个input 设备:

  • name 为 lightsensor-level
  • 事件类型 EV_ABS
  • code 为 ABS_MISC

驱动编写

分析到这里,写驱动 就太简单了。列出几个代码片段

static struct miscdevice opt3001_lsensor_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "lightsensor",
    .fops = &opt3001_lsensor_fops
};



//读芯片id
static int opt3001_read_id(struct i2c_client *client)
{
    char manufacturer[2];
    u16 device_id;
    int ret;

    ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID);
    if (ret < 0) {
        printk("failed to read register %02x\n",
                OPT3001_MANUFACTURER_ID);
        return ret;

    }

    manufacturer[0] = ret >> 8;
    manufacturer[1] = ret & 0xff;

    ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID);
    if (ret < 0) {
        printk("failed to read register %02x\n",
                OPT3001_DEVICE_ID);
        return ret;

    }

    device_id = ret;

    printk("Found %c%c OPT%04x\n", manufacturer[0],
            manufacturer[1], device_id);

    return 0;

}
//配置 opt3001
static int opt3001_configure(struct i2c_client *client)
{
    int ret;
    ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION,
            OPT3001_CONFIGURATION_VAL);
    if (ret < 0) {
        printk("failed to write register %02x\n",
                OPT3001_CONFIGURATION);
        return ret;
    }



    return 0;
}
//注册 input设备
static int init_input_dev(void) {

    int ret;
    input_dev = input_allocate_device();
    if(!input_dev){
        printk(KERN_ERR"%s: could not allocate input device for lsensor\n", __FUNCTION__);
        return -ENOMEM;
    }
    input_dev->name = "lightsensor-level";
    set_bit(EV_ABS, input_dev->evbit);
    input_set_abs_params(input_dev, ABS_MISC, 0, 83865600, 0, 0);
    ret = input_register_device(input_dev);
    if(ret < 0) {
        printk(KERN_ERR"%s: could not register input device for lsensor\n", __FUNCTION__);
        return ret;
    }
    return 0;

}
//读取 opt3001 光感强度
static int opt3001_get_lux(struct i2c_client *client)
{
    int ret;
    u16 mantissa;
    u8 exponent;
    u32 value;
    int lux;
    ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT);
        if (ret < 0) {
            printk("failed to read register %02x\n",
                    OPT3001_RESULT);
            return -1;
        }

    value = ret;
    exponent = OPT3001_REG_EXPONENT(value);
    mantissa = OPT3001_REG_MANTISSA(value);
    lux = 10 * (mantissa << exponent);


    return lux;

}
//上传光强度
void opt3001_handler(struct work_struct *work) {
    struct i2c_client * client = g_client;
    int val;
    if(g_enable) {
      val = opt3001_get_lux(client);
      input_report_abs(input_dev,ABS_MISC,val);
      input_sync(input_dev);
    }
    schedule_delayed_work(&getwork,100);
}


  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
opt3001数字环境光传感器驱动的实现包括以下步骤: 1. 初始化:首先需要初始化I2C总线,并设置适当的时钟、波特率以及传感器的I2C地址。然后通过I2C总线向传感器写入配置寄存器的值,包括采样率和其他参数的设定。 2. 数据读取:通过I2C总线向传感器的结果寄存器发送读取命令,并将读取的数据存储在适当的变量中。传感器的结果寄存器中包含了环境光的强度值,该值可以用于判断当前环境光的明暗程度。 3. 数据处理:将读取的环境光强度值进行相应的处理,例如转换成光强度的百分比或者亮度等级。根据不同的应用需求,可以使用适当的算法对数据进行处理,以满足特定的使用要求。 4. 数据输出:将处理后的环境光强度数据输出到外部设备或系统中,例如通过串口、SPI或其他通信接口发送到上位机或者其他外部设备进行显示或进一步处理。 5. 异常处理:在驱动实现的过程中,可能会发生一些异常情况,例如传感器通信异常、数据读取错误等。为了确保驱动的稳定性和可靠性,需要对这些异常情况进行处理,例如重新初始化传感器、进行错误提示或记录错误日志等。 总的来说,opt3001数字环境光传感器驱动实现需要通过I2C总线与传感器进行通信,并根据需要对传感器的配置进行设定,然后读取传感器的环境光强度数据并进行相应的处理和输出。通过合适的异常处理,可以提高驱动的稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值