项目中要用到光感传感器,选型SFH7779,als&ps功能
其中als包括可见光和红外,项目中因为不需要距离和红外,所以只使用了可将光功能
SFH7779接口如下,看到其实是有中断脚的,但是项目中实际只有i2c和power,中断脚没有接
所以在写程序的时候,打算使用input的poll功能进行数据上报。
直接贴代码
/*
* Driver for SFH7779 als&ps sensor
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
int sfh_debug;
module_param_named(debug, sfh_debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define sfh_dbg(level, debug, dev, fmt, arg...) \
do { \
if (debug >= (level)) \
printk(KERN_CRIT "%s: " fmt, (dev)->name, ## arg); \
} while (0)
#define SFH7779_NAME "sfh7779"
#define SFH7779_CHIP_ID 0x09
#define SFH7779_CHIP_ID_MASK 0x3f
#define SFH7779_SYS_CTL_REG 0x40
#define SFH7779_MODE_CTL_REG 0x41
#define SFH7779_ALSPS_CTL_REG 0x42
#define SFH7779_ALS_VIS_DL_REG 0x46
#define SFH7779_ALS_VIS_DH_REG 0x47
struct sfh7779_dev {
struct i2c_client *client;
struct input_polled_dev *poll_dev;
struct mutex sfh_lock;
};
static void alsps_poll(struct input_polled_dev *poll_dev)
{
struct sfh7779_dev *sfh_dev = poll_dev->private;
struct i2c_client *client = sfh_dev->client;
struct input_dev *input= sfh_dev->poll_dev->input;
int als_l, als_h, als_value;
static u8 cnt = 0;
sfh_dbg(1, sfh_debug, client, "%s\n", __func__);
if (cnt % 2) {
i2c_smbus_write_byte_data(client, SFH7779_ALSPS_CTL_REG, 0x7f);
i2c_smbus_write_byte_data(client, SFH7779_MODE_CTL_REG, 0x0a);
} else {
als_l = i2c_smbus_read_byte_data(client, SFH7779_ALS_VIS_DL_REG) & 0xff;
als_h = i2c_smbus_read_byte_data(client, SFH7779_ALS_VIS_DH_REG) & 0xff;
als_value = (als_h << 8 ) | als_l;
sfh_dbg(1, sfh_debug, client, "als_value = 0x%04x\n", als_value);
input_report_abs(input, ABS_MISC, als_value);
input_sync(input);
}
cnt++;
}
int sfh7779_check_id(struct i2c_client *client)
{
int data;
data = i2c_smbus_read_byte_data(client, SFH7779_SYS_CTL_REG);
if (data < 0) {
printk(KERN_CRIT "id read error\n");
return -1;
}
if ((data & SFH7779_CHIP_ID_MASK) != SFH7779_CHIP_ID) {
printk(KERN_CRIT "id wrong!\n");
return -1;
}
return 0;
}
static int sfh7779_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct sfh7779_dev *sfh_dev;
struct input_polled_dev *poll_dev;
struct input_dev *input;
int value;
int ret;
if (sfh7779_check_id(client) != 0) {
dev_err(dev,"sfh7779 not exist, please check!\n");
return -1;
}
sfh_dev = devm_kzalloc(dev, sizeof(*sfh_dev), GFP_KERNEL);
if (!sfh_dev)
return -ENOMEM;
sfh_dev->client = client;
poll_dev = devm_input_allocate_polled_device(dev);
if (!poll_dev) {
dev_err(dev, "failed to allocate input device\n");
return -ENOMEM;
}
mutex_init(&sfh_dev->sfh_lock);
if (!device_property_read_u32(dev, "poll-interval", &value)) {
poll_dev->poll_interval = value;
} else {
poll_dev->poll_interval = 500;
}
poll_dev->poll = alsps_poll;
poll_dev->private = sfh_dev;
sfh_dev->poll_dev = poll_dev;
input = poll_dev->input;
input->name = client->name;
input->dev.parent = dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0001;
input_set_abs_params(input, ABS_MISC, 0, 0xffff, 0, 0);
input_set_drvdata(input, sfh_dev);
i2c_set_clientdata(client, sfh_dev);
ret = input_register_polled_device(poll_dev);
if (ret) {
dev_err(dev, "Unable to register input device, error: %d\n",
ret);
return ret;
}
printk(KERN_CRIT "%s\n", __func__);
return 0;
}
static int sfh7779_remove(struct i2c_client *client)
{
struct sfh7779_dev *sfh_dev = i2c_get_clientdata(client);
mutex_destroy(&sfh_dev->sfh_lock);
return 0;
}
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id sfh7779_of_match[] = {
{ .compatible = "sfh7779" },
{},
};
MODULE_DEVICE_TABLE(of, sfh7779_of_match);
#endif
static const struct i2c_device_id sfh7779_match_id[] = {
{"sfh7779", 0 },
{ },
};
static struct i2c_driver sfh7779_i2c_driver = {
.driver = {
.name = SFH7779_NAME,
.of_match_table = of_match_ptr(sfh7779_of_match),
},
.probe = &sfh7779_probe,
.remove = &sfh7779_remove,
.id_table = sfh7779_match_id,
};
static int __init sfh7779_mod_init(void)
{
return i2c_add_driver(&sfh7779_i2c_driver);
}
static void __exit sfh7779_mod_exit(void)
{
i2c_del_driver(&sfh7779_i2c_driver);
}
device_initcall_sync(sfh7779_mod_init);
module_exit(sfh7779_mod_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dianlong Li <dianlong_lee@163.com>");
MODULE_DESCRIPTION("Driver for als&ps sensor SFH7779");
代码中input上报事件选择的是EV_ABS,绝对坐标的方式上报,这里不能以为EV_ABS就是用于触摸屏这种,如果有一天我们使用了温度传感器,那么温度的值也完全可以使用EV_ABS事件,只要和应用协商好事件以及事件使用的编码,通信完全没有问题
下面再说一下代码的部分细节,在poll函数中,默认是500ms轮询一次,但是在poll中发现,其实先进行启动als检测,再次poll的时候才进行数据上报,这样的原因是,当前配置的als增益比较高,导致每次转换都需要大约400ms的事件,所以这里才会这样操作。
ok,这个就到这里了。