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