Tel: 137-2421-1742 E-mail: paul@rdbuy.cn
ICP備案證書號:粵ICP備12082730號-1
共執行 65 個查詢,用時 0.017707 秒,在線 201 人,Gzip 已啟用,占用內存 2.686 MB
Powered by ECShop v4.0.1
本文以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上報給操作系統。
由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, 因而不會有沖突。
1, gsensor硬件,負責獲取gsensor傳感器所處的空間狀態信息,存放于fifo中,供主控使用,不同的硬件平臺,數據準備好后,告知主控的方式及主控獲取數據的方式略有不同。
2, 告知主控的方式:gsensor作為傳感器,本身無法區分哪些數據是應該上報的,哪些數據是無效的,它只能接受主控的控制,以主控主動查詢為主;
3, 主控獲取數據的方式:通過ahb, i2c, spi,usb等方式獲取都是可能的。以下以一種典型的硬件連接為例,描述gsensor 傳感器,gsensor ic, 主控之間的連接關系;
3.1 gsensor硬件連接
Gsensor在硬件上,只有i2c連接,這些連接信息,需要事先告知驅動,從而從指定的設備上讀取數據;這些連接信息,通過sysconfig1描述,在驅動中使用;
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.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函數中,主要完成了以下幾件事:
下面對以上這些工作做詳細解釋,分配數據內存空間就不講了
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子系統注冊,又三個步驟:
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)方法:
當然_ATTR不是獨生子女,他還有一系列的姊妹__ATTR_RO宏只有讀方法,__ANULL>