三軸加速度傳感器bma150驅動解析

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;
}










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章