成人伊人|一艘游轮一百个女的一个男的|绿巨人污|韩国三级电影网址|国产深夜福利

當前位置: 首頁 > 傳感器常見問題 > 如何編寫加速度傳感器linux驅動(bma250)

相關商品

瀏覽歷史

如何編寫加速度傳感器linux驅動(bma250)
傳感器驅動 / 2012-12-22

 

 1 Gsensor 驅動概述

  本文以Bma250驅動為例子,詳細介紹Gsensor設計的一個模板。

 gsensor驅動在系統中的層次如下圖所示:

 

圖中包含三個部分:hardware, driver, input:

 

1, Hardware:其實我們可以認為Gsensor也是一個I2C設備。整個Gsensor芯片分為兩部分,一個是sensor傳感器,另一個是controller控制器,用于將sensor掛載在linux系統的I2C上。驅動程序則通過I2C與Gsensor做通信。

 

2,Gsensor Driver:是駐留于操作系統中,為gsensor hardware服務的一個內核模塊;它將gsensor hardware采集到的原始數據,進行降噪,濾波,獲得當前平板的空間狀態,并按照操作系統的要求,將這些信息通過input core上報給操作系統。

 

3,Input core: 是linux為簡化設備驅動程序開發,而開發的一個內核子系統;發給input core的數據將提供給操作系統使用。

 

實際使用時,驅動按照一定的時間間隔,通過數據總線,獲取gsensor hardware采集到的數據,并按照操作系統的要求,將這些信息通過input core上報給操作系統。

 

2 Gsensor驅動設計要求

 

由gsensor驅動在系統中的層次,上有Input core,下有I2C,驅動需要通過I2C采集信息,并準確及時的上報數據至input core。驅動上報的數據,是被input core管理并被上層使用的,應符合input core和上層應用框架的要求;

 

2.1符合Input輸入子系統的設計規范

 

1, 接口:Gsensor驅動,在設計上,不應自行決定是否上報,上報頻率等,應提供接口,供上層應用控制驅動的運行和數據上報:包括使能控制Enable, 上報時延delay等;通常通過sysfs文件系統提供,這部分實現,遵循標準的linux規范;

 

2,上報數據的方式:或者提供接口供上層訪問(eg: ioctl),或者掛接在系統子系統上,使用系統子系統的接口,供上層使用(eg: input core);

 

3,讀取數據的支持:應滿足讀取數據的要求,進行相應的配置;本文以i2c總線為例,簡要說明在A1x平臺上,配置總線傳輸相關信息;

 

2.2 I2c總線的配置

 

要使用i2c總線進行數據傳輸,需注冊i2c driver,創建i2c-client,以便使用i2c-adapter進行數據傳輸;

要成功注冊i2c driver有兩種方式:

1,使用i2c_register_board_info:此方式,需要在系統啟動時,進行相關信息的注冊,不利于模塊化開發,現已不推薦;目前,在2.6內核上,還支持此方式,在3.0上已不再支持;

2,  使用detect方式:在模塊加載時,進行檢測,在條件成立時,注冊i2c設備相關信息,創建i2c-client,并注冊i2c driver,執行probe操作;

 

需要說明的是,此兩種方式可共存,目前2.6就是這樣的;在共存時,以i2c_register_board_info信息為更高優先級,在i2c_register_board_info已經占用設備的前提下,內核發現設備被占用,不會執行detect, 因而不會有沖突。

 

3 gsensor模塊硬件說明

 

1,  gsensor硬件,負責獲取gsensor傳感器所處的空間狀態信息,存放于fifo中,供主控使用,不同的硬件平臺,數據準備好后,告知主控的方式及主控獲取數據的方式略有不同。

2,   告知主控的方式:gsensor作為傳感器,本身無法區分哪些數據是應該上報的,哪些數據是無效的,它只能接受主控的控制,以主控主動查詢為主;

3,  主控獲取數據的方式:通過ahb, i2c, spi,usb等方式獲取都是可能的。以下以一種典型的硬件連接為例,描述gsensor 傳感器,gsensor ic, 主控之間的連接關系;

3.1 gsensor硬件連接

 

Gsensor在硬件上,只有i2c連接,這些連接信息,需要事先告知驅動,從而從指定的設備上讀取數據;這些連接信息,通過sysconfig1描述,在驅動中使用;

 

4 驅動設計

 

4.1 支持模組列表

 

在A1x平臺上支持Gsensor列表如下:

支持的

模組

Chip ID

寄存器

Chip ID

I2C地址

I2C設備注冊

名稱

unuse_name

bma250

0x00

3

0x18

bma250

bma250

bma222

0x00

3

0x08

bma250

bma222

bma150

0x00

2

0x38

bma250

bma150

kxtik-1004

0x0f

0x05

0x0f

kxtik

kxtik

kxtj9-1005

0x0f

0x08

0x0f

kxtik

kxtik

dmard06

0x0f

0x06

0x1c

dmard06

dmard06

mma7660

0x4c

mma7660

mma7660

mma8452

0x0d

0x2a

SA0 = 0: 0x1c

mma8452

mma8452c

SA0 = 1: 0x1d

Mma8452d

afa750

0x37

0x3d or 0x3c

0x3d

afa750

afa750

mxc6225

0x15

mxc622x

mxc622x


4.2 Gsensor配置

 

在A1x的方案中,Gsensor的配置在sys_config1.fex文件中:

 [gsensor_para]

gsensor_used             = 1          //是否使用gsensor

gsensor_name             = "bma250"  //名稱

gsensor_twi_id           = 1           //使用哪組I2C

gsensor_twi_addr         = 0x18        // I2C設備地址(7位地址)

 

4.3 關鍵數據結構

 

4.3.1 i2c_driver

static struct i2c_driver bma250_driver = {

              .class = I2C_CLASS_HWMON,

              .driver = {

                             .owner    = THIS_MODULE,

                             .name      = SENSOR_NAME,

              },

              .id_table   = bma250_id,

              .probe                    = bma250_probe,

              .remove                 = bma250_remove,

              .address_list           = u_i2c_addr.normal_i2c,

};

在驅動程序中,靜態初始化i2c_driver結構體給bma250_driver變量,該變量完成gsensor驅動的主要工作,匹配設備名,設備的偵測等,文件操作結構體如上所示。

 

4.3.2 bma250_data

 

struct bma250_data {

              struct i2c_client *bma250_client;

              atomic_t delay;

              atomic_t enable;

              unsigned char mode;

              struct input_dev *input;

              struct bma250acc value;

              struct mutex value_mutex;

              struct mutex enable_mutex;

              struct mutex mode_mutex;

              struct delayed_work work;

              struct work_struct irq_work;

#ifdef CONFIG_HAS_EARLYSUSPEND

              struct early_suspend early_suspend;

#endif

};

代表了gsensor驅動所需要的信息的集合,用于幫助實現對采樣信息的處理。

 

4.3.3 delayed_work

 

struct delayed_work {

              struct work_struct work;

              struct timer_list timer;

};

Delayed_work一般用來觸發定時的操作,在定時時間到后,完成指定操作,通過不斷的觸發定時操作,實現按照一定頻率的執行指定操作;

 

4.3.4 bma250acc

 

struct bma250acc{

              s16         x,

                             y,

                             z;

} ;

用于記錄采樣時獲得的信息。

 

5 驅動流程解析

 

5.1模塊加載

 

static struct i2c_driver bma250_driver = {

              .class = I2C_CLASS_HWMON,

              .driver = {

                             .owner    = THIS_MODULE,

                             .name      = SENSOR_NAME,

              },

              .id_table   = bma250_id,

              .probe                    = bma250_probe,         //注冊完成時調用

              .remove                 = bma250_remove,

              .address_list           = u_i2c_addr.normal_i2c,   //IIC地址

};

static int __init BMA250_init(void)

{            

              if(gsensor_fetch_sysconfig_para()){       //解析sys_config1.fex文件

                             printk("%s: err.\n", __func__);

                             return -1;

              }

 

              bma250_driver.detect = gsensor_detect;

              ret = i2c_add_driver(&bma250_driver);   //開始向IIC注冊

}

static void __exit BMA250_exit(void)

{

              i2c_del_driver(&bma250_driver);

}

module_init(BMA250_init);

module_exit(BMA250_exit);

  內核加載驅動模塊的時候將調用到BMA250_init()方法

n         初始化了i2c_driver結構體給bma250_driver變量,將用于將設備注冊到IIC。關鍵在于結構體中的probe()方法,注冊完成的時候將調用

n         調用gsensor_fetch_sysconfig_para()解析sys_config1.fex文件,讀取到IIC的地址,并賦值給u_i2c_addr.normal_i2c。

n         調用i2c_add_driver開始向IIC注冊driver,完成注冊后將調用bm250_probe()方法

 

5.2 bma250初始化工作-probe

 

static int bma250_probe(struct i2c_client *client,

                             const struct i2c_device_id *id)

{

              int err = 0;

              int tempvalue;

              struct bma250_data *data;

    ……

              data = kzalloc(sizeof(struct bma250_data), GFP_KERNEL); //為bma250_data結構體申請內存

             

              tempvalue = 0;

              tempvalue = i2c_smbus_read_word_data(client, BMA250_CHIP_ID_REG);

 

              if ((tempvalue&0x00FF) == BMA250_CHIP_ID) {

                             printk(KERN_INFO "Bosch Sensortec Device detected!\n" \

                                                         "BMA250 registered I2C driver!\n");

              } else if ((tempvalue&0x00FF) == BMA150_CHIP_ID) {

                             printk(KERN_INFO "Bosch Sensortec Device detected!\n" \

                                                         "BMA150 registered I2C driver!\n");

              }

    ……

              i2c_set_clientdata(client, data); //將設備驅動的私有數據連接到設備client中

              data->bma250_client = client;

              mutex_init(&data->value_mutex);

              mutex_init(&data->mode_mutex);

              mutex_init(&data->enable_mutex);

              bma250_set_bandwidth(client, BMA250_BW_SET);

              bma250_set_range(client, BMA250_RANGE_SET);

 

              INIT_DELAYED_WORK(&data->work, bma250_work_func);//創建工作隊列

              bma_dbg("bma: INIT_DELAYED_WORK\n");

              atomic_set(&data->delay, BMA250_MAX_DELAY);

              atomic_set(&data->enable, 0);

              err = bma250_input_init(data);  //向Input子系統注冊

    …...

              err = sysfs_create_group(&data->input->dev.kobj, //創建sysfs接口

                                                                                       &bma250_attribute_group);

}

在bma250_probe函數中,主要完成了以下幾件事:

  •        為驅動私有數據結構體bma250_data分配內存空間
  •        讀取IIC chip id
  •        將設備驅動的私有數據(bma250_data)連接到設備client(i2c_client)中
  •        創建工作隊列
  •       將bma250驅動注冊到linux input子系統
  •      創建sysfs接口

下面對以上這些工作做詳細解釋,分配數據內存空間就不講了

 

5.2.1 讀取IICchip id

 

在4.1的列表中,我們可以看到bma250的chip ID寄存器為0x00,chip ID的值為3。而上面代碼有兩個宏的定義:

#define BMA250_CHIP_ID_REG  0x00

#define BMA250_CHIP_ID       3

調用IIC接口i2c_smbus_read_word_data()讀取IIC上bma250的chip id,返回的值tempvalue=3的時候,說明是正確的!

 

5.2.2 初始化工作隊列

 

先提一個問題,為什么要創建工作隊列?在前面的介紹中我們知道,sensor傳感器獲取數據后,將數據傳給controller的寄存器中,供主控去查詢讀取數據。所以這里創建的工作隊列,就是在一個工作者線程,通過IIC不斷的去查詢讀取controller上的數據。

工作隊列的作用就是把工作推后,交由一個內核線程去執行,更直接的說就是如果寫了一個函數,而現在不想馬上執行它,想在將來某個時刻去執行它,那用工作隊列準沒錯.大概會想到中斷也是這樣,提供一個中斷服務函數,在發生中斷的時候去執行,沒錯,和中斷相比,工作隊列最大的好處就是可以調度可以睡眠,靈活性更好。

上面代碼中我們看到INIT_DELAYED_WORK(&data->work, bma250_work_func),其實是一個宏的定義,在include/linux/workqueue.h中。bma250_work_func()就是我們定義的功能函數,用于查詢讀取Sensor數據的,并上報Input子系統,代碼如下:

static void bma250_work_func(struct work_struct *work)

{

              struct bma250_data *bma250 = container_of((struct delayed_work *)work,

                                           struct bma250_data, work);

              static struct bma250acc acc;

              unsigned long delay = msecs_to_jiffies(atomic_read(&bma250->delay)); //延時時間

 

              bma250_read_accel_xyz(bma250->bma250_client, &acc); //讀取Sensor數據

              input_report_abs(bma250->input, ABS_X, acc.x);

              input_report_abs(bma250->input, ABS_Y, acc.y);

              input_report_abs(bma250->input, ABS_Z, acc.z);

              bma_dbg("acc.x %d, acc.y %d, acc.z %d\n", acc.x, acc.y, acc.z);

              input_sync(bma250->input);

              mutex_lock(&bma250->value_mutex);

              bma250->value = acc;

              mutex_unlock(&bma250->value_mutex);

              schedule_delayed_work(&bma250->work, delay); //設定delay時間后再次執行這個函數

}

我們調用INIT_DELAYED_WORK()宏初始化了工作隊列之后,那么什么時候將執行我們定義的功能函數bma250_work_func()呢?那么只需要調用schedule_delayed_work()即可在delay時間后執行功能函數。

在驅動設計中,我們在Sensor使能函數中調用schedule_delayed_work()開始啟動工作隊列,調用cancel_delayed_work_sync()停止工作隊列。而我們在上面的功能函數bma250_work_func()中也調用了schedule_delayed_work(),這樣功能函數將被重復調用,也就可以按照一個設定的頻率查詢讀取Sensor數據了。然后通過input系統提供的接口函數input_report_abs(),向input系統報告新的數據。

5.2.3向input系統注冊

Gsensor作為一個輸入設備,按照linux設計標準,需要將Gsensor驅動注冊到Input子系統中,注冊代碼如下:

static int bma250_input_init(struct bma250_data *bma250)

{

              struct input_dev *dev;

              int err;

 

       dev = input_allocate_device();

              if (!dev)

                             return -ENOMEM;

              dev->name = SENSOR_NAME;

              dev->id.bustype = BUS_I2C;

 

              input_set_capability(dev, EV_ABS, ABS_MISC);

              input_set_abs_params(dev, ABS_X, ABSMIN_2G, ABSMAX_2G, 0, 0);

              input_set_abs_params(dev, ABS_Y, ABSMIN_2G, ABSMAX_2G, 0, 0);

              input_set_abs_params(dev, ABS_Z, ABSMIN_2G, ABSMAX_2G, 0, 0);

              input_set_drvdata(dev, bma250);

       err = input_register_device(dev);

 

              bma250->input = dev;

}

就是由上面的代碼,完成了Gsensor驅動向Input子系統注冊,又三個步驟:

  • n         申請一個新的input設備,即為一個input_dev申請內存空間
  • n         設置input設備支持的數據類型
  • n         向input系統注冊
  •  

5.2.4 創建sysfs 接口

 

為什么要創建sysfs接口?在驅動層創建了sysfs接口,HAL層通過這些sysfs接口,對Sensor進行操作,如使能、設置delay等。

5.2.4.1 sysfs接口函數的建立DEVICE_ATTR  

說道sysfs接口,就不得不提到函數宏 DEVICE_ATTR,原型在<include/linux/device.h> :

#define DEVICE_ATTR(_name, _mode, _show, _store) \

struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

函數宏DEVICE_ATTR內封裝的是__ATTR(_name,_mode,_show,_stroe)方法:

  • n         _show:表示的是讀方法,
  • n         _stroe表示的是寫方法。

當然_ATTR不是獨生子女,他還有一系列的姊妹__ATTR_RO宏只有讀方法,__ANULL>

 

 

用戶評論(共0條評論)

  • 暫時還沒有任何用戶評論
總計 0 個記錄,共 1 頁。 第一頁 上一頁 下一頁 最末頁
用戶名: 游客
E-mail:
評價等級:
評論內容: