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