Bosch Sensortec的BMA250是為電子消費市場設計的一款數字輸出的低功耗三軸加速度傳感器,BMA250加速度傳感器2mmX2mm的小型封裝和數字接口,使其滿足眾多消費電子制造商的需求,尤其是在便攜式手持設備上。BMA250加速度傳感器具有從±2g到±16g四個可編程的測量范圍,提供應用程序設計者更多的開發彈性,較高的測量精度,其十位的數據可提供最高精確度小于4mg
BMA250支持兩種操作模式:
1)流數據模式:加速數據直接讀出通過傳感器的數字接口和計算系統μController、應用處理器或基帶處理器。
2)中斷發動機模式:加速數據計算已經在BMA250的集成,可編程中斷發動機。根據可編程設置綜合中斷發動機BMA250的信號發生一定的通過傳感器事件的兩個中斷pin。相應的寄存器的BMA250可以很容易地設置和讀出通過數字傳感器接口。
重力感應器BMA250源代碼執行分析
重力傳感器是根據壓電效應的原理來工作的。
所謂的壓電效應就是 “對于不存在對稱中心的異極晶體加在晶體上的外力除了使晶體發生形變以外,還將改變晶體的極化狀態,在晶體內部建立電場,這種由于機械力作用使介質發生極化的現象稱為正壓電效應 ”。
重力傳感器就是利用了其內部的由于加速度造成的晶體變形這個特性。由于這個變形會產生電壓,只要計算出產生電壓和所施加的加速度之間的關系,就可以將加速度轉化成電壓輸出。當然,還有很多其它方法來制作加速度傳感器,比如電容效應,熱氣泡效應,光效應,但是其最基本的原理都是由于加速度產生某個介質產生變形,通過測量其變形量并用相關電路轉化成電壓輸出。
BMA250E
10位,數字型,三軸加速度傳感器,運動觸發,中斷控制
主要特點:小封裝,數字接口,可編程功能,板上FIFO,板上中斷控制,低功耗。
I2C接口,2個中斷Pin,電壓范圍1.2to3.6V
加速度范圍: 2g/4g/8g/16g
動力觸發中斷信號產生:新數據,檢測任何運動,單輸出和雙輸出,方位識別,flat detection,無運動檢測。低功耗,喚醒時間短,先進的系統電源管理
Vdd是內部塊的主電源
Vddio是分成的電源供應Pin用于支持接口和內部塊
電源模式:
有六種電源模式,除了普通模式支持這個設備的操作外,還有其他的五種節能模式:深度睡眠模式,睡眠模式,標準模式,低功耗模式一和低功耗模式二。
電源打開后就是普通模式。在deep-suspnd模式下,設備接近于最低功耗。只有接口保持活動。沒有數據請求被響應,配置寄存器is lost.
OffsetCompensation:慢速補償,快速補償,快速補償,在線校準
Non-volatile memory:三種寄存器:hardwired,volatile,non-volatile
BMA250_driver.c
BMA250_driver.c代碼分析:
1、static int bma250_smbus_read_byte(struct i2c_client *client,
unsigned char reg_addr, unsigned char *data)
static int bma250_smbus_write_byte(struct i2c_client *client,
unsigned char reg_addr, unsigned char *data)
兩個函數分別調用
i2c_smbus_read_byte_data(client, reg_addr);
i2c_smbus_write_byte_data(client, reg_addr, *data);
而這兩個函數都調用i2c_smbus_xfer,函數原型為:
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
兩個調用時就是參數char read_write這個參數有了改變,讀時參數為I2C_SMBUS_READ,寫時參數為:I2C_SMBUS_WRITE,其他參數不變。參數 int protocol表示使用的協議。
在i2c-core.c中通過EXPORT_SYMBOL(i2c_smbus_read_byte_data);把這個函數導出來,然后在其他文件中使用。
2、static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR, bma250_mode_show, bma250_mode_store);
static DEVICE_ATTR(value, S_IRUGO, bma250_value_show, NULL);
……;
static DEVICE_ATTR(update, S_IRUGO|S_IWUSR, NULL, bma250_update_store);
static DEVICE_ATTR(selftest, S_IRUGO|S_IWUSR, bma250_selftest_show, bma250_selftest_store);
完成了DEVICE_ATTR函數宏的填充,下面就需要創建接口了
static struct attribute *bma250_attributes[]= {
&dev_attr_range.attr,
……
&dev_attr_offset_filt_z.attr,
&dev_attr_orientation.attr,
NULL
};
當想要實現的接口名字是orientation的時候,需要實現結構體static struct attribute *bma250_attributes[]
其中成員變量的名字必須是&dev_attr_orientation.attr,然后再封裝
static struct attribute_group bma250_attribute_group = {
.attrs = bma250_attributes
};
再利用sysfs_create_group(&data-》input-》dev.kobj,&bma250_attribute_group);創建接口。通過以上的幾個步驟,就可以在adb shell 終端查看到接口了。當我們將數據 echo 到接口中時,在上層實際上完成了一次 write 操作,對應到 kernel ,調用了驅動中的 “store”。同理,當我們cat 一個 接口時則會調用 “show” 。到這里,只是簡單的建立了 android 層到 kernel 的橋梁,真正實現對硬件操作的,還是在 “show” 和 “store” 中完成的。這些接口也是調試接口。
程序執行過程:
板子信息:
struct bma250acc{
s16 x,
y,
z;
} ;
struct bma250_data {/*kernel\lc1810\include\linux\Bmc.h*/
struct i2c_client *bma250_client;//bma250設備
atomic_t delay; //延遲
atomic_t enable; //使能
unsigned char mode; //工作模式
struct input_dev *input; //input設備
struct bma250acc value; //上報的坐標值
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
atomic_t selftest_result;
int orientation;
};
/*kernel\lc1810\arch\arm\mach-comip\board-lc1810.c*/
#if defined(CONFIG_SENSORS_BMA250)
static struct bma250_data bmc_bma250_data = {
.orientation = 0,
};
#endif
填充i2c_board_info:
static struct i2c_board_infocomip_i2c1_board_info[] = {
#if defined(CONFIG_SENSORS_BMA250)
{
I2C_BOARD_INFO(“bma250”, 0x18),
.platform_data = &bmc_bma250_data,
},
#endif
}
1、module_init(BMA250_init);
module_exit(BMA250_exit);
static int __init BMA250_init(void)
{
return i2c_add_driver(&bma250_driver);//加載驅動程序bma250_driver
}
其中,驅動程序為:
static struct i2c_driver bma250_driver = {
.driver = {
.owner = THIS_MODULE,
.name = SENSOR_NAME,
},
.id_table = bma250_id,//id_table是該驅動所支持的I2C設備的ID表
.probe = bma250_probe,/* bus-》match成功后調用 */
.remove = bma250_remove,
};
2、bma250_probe(struct i2c_client *client,const struct i2c_device_id *id)函數的執行:
1)、利用i2c_check_functionality(client-》adapter, I2C_FUNC_I2C)檢查I2C設備適配器工作是否正常。
2)、給bma250_data分配空間并清零。
3)、利用i2c_smbus_read_word_data(client, BMA250_CHIP_ID_REG);檢測bma250的ID號是否匹配,匹配繼續,否則失敗。
4)、讓client的data指向新分配的空間,初始化data.
5)、互斥訪問:利用
bma250_set_bandwidth(client, BMA250_BW_SET);
bma250_set_range(client, BMA250_RANGE_SET);設置帶寬和范圍。
用INIT_DELAYED_WORK(&data-》work, bma250_work_func);動態初始化一個隊列的work,并且和隊列處理函數bma250_work_func()綁定在一起。過一段延遲后處理work。
而在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);
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);
input_sync(bma250-》input);
bma250-》value = acc;
printk(“_______[%s]: acc.x=%d,y=%d,z=%d\n”,__func__,acc.x,acc.y,acc.z);
schedule_delayed_work(&bma250-》work, delay);
}
首先是一段延遲msecs_to_jiffies(atomic_read(&bma250-》delay)),然后通過
bma250_read_accel_xyz(bma250-》bma250_client, &acc)讀取sensor的xyz的值,再通過input_report_abs()函數把這三個點報上去。再通過schedule_delayed_work()延遲一段時間后調度執行一個具體的任務,執行的任務將會被掛入Linux系統提供的workqueue。
INIT_DELAYED_WORK()和schedule_delayed_work()函數是成對出現的。
6)、通過bma250_input_init(data)函數初始化bma250_input。初始化時先通過input_allocate_device()分配空間。在設置報點范圍,然后注冊設備,讓bma250_input指向它。
7)、通過sysfs_create_group()函數創建sysfs接口。
8)、初始化bma250電源管理的掛起和打開。
static void bma250_early_suspend(struct early_suspend *h)
{
struct bma250_data *data =container_of(h, struct bma250_data, early_suspend);
mutex_lock(&data-》enable_mutex);
if (atomic_read(&data-》enable) == 1) {
bma250_set_mode(data-》bma250_client, BMA250_MODE_SUSPEND);
cancel_delayed_work_sync(&data-》work);
}
mutex_unlock(&data-》enable_mutex);
}
互斥的設置bma250工作模式是suspend,然后執行cancel_delayed_work_sync()函數,對一個延遲執行的工作來說,這個函數的作用是在這個工作還未執行的時候就把它給取消掉。
工作時:static void bma250_late_resume(struct early_suspend *h)
{
struct bma250_data *data =
container_of(h, struct bma250_data, early_suspend);
mutex_lock(&data-》enable_mutex);
if (atomic_read(&data-》enable) == 1) {
bma250_set_mode(data-》bma250_client, BMA250_MODE_NORMAL);
schedule_delayed_work(&data-》work,
msecs_to_jiffies(atomic_read(&data-》delay)));
}
mutex_unlock(&data-》enable_mutex);
}resume時首先互斥把工作模式設為普通工作模式,然后調用schedule_delayed_work()函數延遲一段時間后調用工作隊列中的work進行處理。
工作者線程(events),或者說worker threads,更確切的說,這些應該是缺省的工作者線程。而與工作者線程相關的一個概念就是工作隊列,或者叫work queue.events這么一個線程,它其實和內核線程一樣,有事情就處理,沒事情就睡眠,也是一個死循環,而schedule_delayed_work()的作用就是喚醒這個線程,確切的說,是先把自己的這個struct work_struct插入workqueue_struct這個隊列里,然后喚醒昏睡中的events.然后events就會去處理,要是有延時,那么它就給安排延時以后執行,要是沒有延時,或者設了延時為0,那就趕緊開始執行。
9)、初始化后通過函數bma250_set_mode(client, BMA250_MODE_SUSPEND);把sensor設置為suspend狀態。如果出現錯誤就bma250_input_delete(data);釋放設備。
至此,bma250_probe執行完畢,sensor初始化完畢。
評論
查看更多