BMA150 博世 三轴加速度传感器
SPI(4线,3线),i2c,中断引脚
频响+/- 2g,4g,8g;带宽25~1500hz,中断触发内部加速度求值
低功耗,快速唤醒
包含数据寄存器,控制寄存器,状态寄存器,设置寄存器及EEPROM
寄存器读写使用i2c接口,所以需要驱动i2c设备
数据需要获取xyz值,所以可以添加成input设备
用一个bma150_data数据对象来描述整个设备
struct bma150_data { struct i2c_client *client; //i2c 客户端 struct input_polled_dev *input_polled; //轮询输入设备 struct input_dev *input; //输入设备 u8 mode; //记录状态模式 };
如果bma150有中断响应则用输入设备,数据在中断处理完上报
没有则使用轮询输入设备,不断访问数据
bma150的配置信息用一个结构体bma150_cfg去表述
struct bma150_cfg { //bma150配置 bool any_motion_int; /*any-motion 中断*/ bool hg_int; /*high-G 中断 */ bool lg_int; /*low-G 中断 */ unsigned char any_motion_dur; /* Any-motion 持续时间 */ unsigned char any_motion_thres; /* Any-motion 阀值 */ unsigned char hg_hyst; /* High-G 延迟 */ unsigned char hg_dur; /* High-G 持续时间 */ unsigned char hg_thres; /* High-G 阀值 */ unsigned char lg_hyst; /* Low-G 延迟 */ unsigned char lg_dur; /* Low-G 持续时间 */ unsigned char lg_thres; /* Low-G 阀值 */ unsigned char range; /* 频响 */ unsigned char bandwidth; /* 带宽 */ };
三个中断使能值及其中断属性,频响,带宽
驱动i2c设备需要i2c设备驱动及i2c设备
所以板级驱动用I2C_BOARD_INFO("bma150", (0x38))注册i2c设备
内核中bma150.c文件包含bma150芯片的i2c设备驱动
module_i2c_driver(bma150_driver);//声明模块入口出口
加载模块的时候注册bma150对应的i2c设备驱动bma150_driver
static struct i2c_driver bma150_driver = { //i2c设备驱动 .driver = { .owner = THIS_MODULE, .name = BMA150_DRIVER, //"bma150" .pm = &bma150_pm, }, .class = I2C_CLASS_HWMON, .id_table = bma150_id, //匹配id表(兼容smb380,bma023) .probe = bma150_probe, //probe方法 .remove = bma150_remove, //remove方法 };
i2c设备与驱动匹配后会调用驱动的probe方法bma150_probe
static int bma150_probe(struct i2c_client *client,const struct i2c_device_id *id) { const struct bma150_platform_data *pdata = client->dev.platform_data; //获取bma150平台数据 const struct bma150_cfg *cfg; //bma150配置 struct bma150_data *bma150; //bma150数据 int chip_id; int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { //检查i2c适配器性能是否支持 dev_err(&client->dev, "i2c_check_functionality error\n"); return -EIO; } chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG); //获取芯片ID,0x00寄存器 if (chip_id != BMA150_CHIP_ID) { //-----010b dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id); return -EINVAL; } bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL); //分配bna150数据内存 if (!bma150) return -ENOMEM; bma150->client = client; //bma150数据捆绑i2c客户端 if (pdata) { //存在平台数据?(板级驱动提供) if (pdata->irq_gpio_cfg) { //存在中断gpio配置函数? error = pdata->irq_gpio_cfg(); //调用中断gpiio配置函数 if (error) { dev_err(&client->dev,"IRQ GPIO conf. error %d, error %d\n",client->irq, error); goto err_free_mem; } } cfg = &pdata->cfg; //获取bma150配置 } else { cfg = &default_cfg; //默认bma150配置 } error = bma150_initialize(bma150, cfg); //bma150初始化--解析1 if (error) goto err_free_mem; if (client->irq > 0) { //存在中断号 error = bma150_register_input_device(bma150); //注册输入设备--解析2 if (error) goto err_free_mem; error = request_threaded_irq(client->irq, //请求中断 NULL, bma150_irq_thread, //中断回调函数--解析3 IRQF_TRIGGER_RISING | IRQF_ONESHOT,//上升沿触发|线程函数执行完才会重开中断 BMA150_DRIVER, bma150); if (error) { dev_err(&client->dev,"irq request failed %d, error %d\n",client->irq, error); input_unregister_device(bma150->input); goto err_free_mem; } } else { //不中断则轮询 error = bma150_register_polled_device(bma150); //注册轮询输入设备--解析4 if (error) goto err_free_mem; } i2c_set_clientdata(client, bma150);//&client->dev->p->driver_data=bma150 pm_runtime_enable(&client->dev); return 0; err_free_mem: kfree(bma150); return error; }
解析1:bma150初始化
static int bma150_initialize(struct bma150_data *bma150,const struct bma150_cfg *cfg) { int error; error = bma150_soft_reset(bma150); //bma150软复位(0x0a寄存器位1置1) if (error) return error; error = bma150_set_bandwidth(bma150, cfg->bandwidth); //bma设置带宽(0x14寄存器0~2位) if (error) return error; error = bma150_set_range(bma150, cfg->range); //bma150设置频响(0x14寄存器3~4位) if (error) return error; if (bma150->client->irq) { //设置中断触发 error = bma150_set_any_motion_interrupt(bma150, //设置any_motion中断(0x0b寄存器6位) cfg->any_motion_int, //(0x15寄存器6位) cfg->any_motion_dur, //(0x11寄存器6,7位) cfg->any_motion_thres); //(0x10寄存器) if (error) return error; error = bma150_set_high_g_interrupt(bma150, //设置high_g中断 cfg->hg_int, //(0x0b寄存器1位) cfg->hg_hyst, //(0x11寄存器3~5位) cfg->hg_dur, //(0x0f寄存器) cfg->hg_thres); //(0x0e寄存器) if (error) return error; error = bma150_set_low_g_interrupt(bma150, //设置low_g中断 cfg->lg_int, //(0x00寄存器0位) cfg->lg_hyst, //(0x11寄存器0~2位) cfg->lg_dur, //(0x0D寄存器) cfg->lg_thres); //(0x0C寄存器) if (error) return error; } //(0x15寄存器0位,0x0a寄存器0位) return bma150_set_mode(bma150, BMA150_MODE_SLEEP); //设置睡眠唤醒模式 }
解析2:注册输入设备
static int bma150_register_input_device(struct bma150_data *bma150) { struct input_dev *idev; int error; idev = input_allocate_device(); //分配输入设备内存 if (!idev) return -ENOMEM; bma150_init_input_device(bma150, idev); //初始化输入设备--2.1 idev->open = bma150_irq_open; //设置打开方法 idev->close = bma150_irq_close; //设置关闭方法 input_set_drvdata(idev, bma150); //&idev->dev->p->driver_data=bma150 error = input_register_device(idev); //注册输入设备 if (error) { input_free_device(idev); return error; } bma150->input = idev; //捆绑输入设备 return 0; }
2.1初始化输入设备
static void bma150_init_input_device(struct bma150_data *bma150,struct input_dev *idev) { idev->name = BMA150_DRIVER; idev->phys = BMA150_DRIVER "/input0"; idev->id.bustype = BUS_I2C; idev->dev.parent = &bma150->client->dev; idev->evbit[0] = BIT_MASK(EV_ABS); //绝对位移事件 input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);//x轴位移 input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);//y轴位移 input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);//z轴位移 }
解析3:中断回调函数
static irqreturn_t bma150_irq_thread(int irq, void *dev) { bma150_report_xyz(dev); //上报事件数据--3.1 return IRQ_HANDLED; }
3.1上报事件数据
static void bma150_report_xyz(struct bma150_data *bma150) { u8 data[BMA150_XYZ_DATA_SIZE]; s16 x, y, z; s32 ret; //(0x02~0x07寄存器获取xyz轴数据) ret = i2c_smbus_read_i2c_block_data(bma150->client,BMA150_ACC_X_LSB_REG, BMA150_XYZ_DATA_SIZE, data); if (ret != BMA150_XYZ_DATA_SIZE) return; x = ((0xc0 & data[0]) >> 6) | (data[1] << 2);//x轴数据 y = ((0xc0 & data[2]) >> 6) | (data[3] << 2);//y轴数据 z = ((0xc0 & data[4]) >> 6) | (data[5] << 2);//z轴数据 /* sign extension */ x = (s16) (x << 6) >> 6; y = (s16) (y << 6) >> 6; z = (s16) (z << 6) >> 6; input_report_abs(bma150->input, ABS_X, x);//上报x轴数据 input_report_abs(bma150->input, ABS_Y, y);//上报y轴数据 input_report_abs(bma150->input, ABS_Z, z);//上报z轴数据 input_sync(bma150->input);//同步数据 }
解析4:注册轮询输入设备
static int bma150_register_polled_device(struct bma150_data *bma150) { struct input_polled_dev *ipoll_dev; int error; ipoll_dev = input_allocate_polled_device(); //分配轮询输入设备内存 if (!ipoll_dev) return -ENOMEM; ipoll_dev->private = bma150;//私有数据位bma150数据 ipoll_dev->open = bma150_poll_open; //设置打开方法 ipoll_dev->close = bma150_poll_close; //设置关闭方法 ipoll_dev->poll = bma150_poll; //设置轮询方法 ipoll_dev->poll_interval = BMA150_POLL_INTERVAL; //设置轮询时间戳 ipoll_dev->poll_interval_min = BMA150_POLL_MIN; ipoll_dev->poll_interval_max = BMA150_POLL_MAX; bma150_init_input_device(bma150, ipoll_dev->input); //初始化bma150输入设备--4.1 error = input_register_polled_device(ipoll_dev); //注册轮询输入设备 if (error) { input_free_polled_device(ipoll_dev); return error; } bma150->input_polled = ipoll_dev;//bma150数据捆绑轮询输入设备 bma150->input = ipoll_dev->input;//bma150数据的输入设备指向轮询输入设备的输入设备 return 0; }
4.1 初始化bma150输入设备
static void bma150_init_input_device(struct bma150_data *bma150,struct input_dev *idev) { idev->name = BMA150_DRIVER; idev->phys = BMA150_DRIVER "/input0"; idev->id.bustype = BUS_I2C; idev->dev.parent = &bma150->client->dev; idev->evbit[0] = BIT_MASK(EV_ABS); //绝对位移事件 input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);//x轴位移 input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);//y轴位移 input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);//z轴位移 }
解析5 输入设备打开关闭方式
打开
static int bma150_open(struct bma150_data *bma150) { int error; error = pm_runtime_get_sync(&bma150->client->dev); if (error < 0 && error != -ENOSYS) return error; /* * See if runtime PM woke up the device. If runtime PM * is disabled we need to do it ourselves. */ if (bma150->mode != BMA150_MODE_NORMAL) { error = bma150_set_mode(bma150, BMA150_MODE_NORMAL);//设置正常模式 if (error) return error; } return 0; }
关闭
static void bma150_close(struct bma150_data *bma150) { pm_runtime_put_sync(&bma150->client->dev); if (bma150->mode != BMA150_MODE_SLEEP) bma150_set_mode(bma150, BMA150_MODE_SLEEP);//设置睡眠模式 }
打开关闭bma150都调用bma150_set_mode函数来设置模式
static int bma150_set_mode(struct bma150_data *bma150, u8 mode) { int error; error = bma150_set_reg_bits(bma150->client, mode, BMA150_WAKE_UP_POS, BMA150_WAKE_UP_MSK, BMA150_WAKE_UP_REG);//(0x15寄存器0位) if (error) return error; error = bma150_set_reg_bits(bma150->client, mode, BMA150_SLEEP_POS, BMA150_SLEEP_MSK, BMA150_SLEEP_REG);//(0x0a寄存器0位) if (error) return error; if (mode == BMA150_MODE_NORMAL) msleep(2); bma150->mode = mode; //记录住模式 return 0; }