Gsensor驱动概述

article/2025/10/6 2:57:23

本文以Bma250驱动为例子,详细介绍Gsensor设计的一个模板。

 gsensor驱动在系统中的层次如下图所示:

linux <wbr>Gsensor<strong>驱动</strong>(bma250为例子)

图中包含三个部分:hardware,driver, input:

n        Hardware:其实我们可以认为Gsensor也是一个I2C设备。整个Gsensor芯片分为两部分,一个是sensor传感器,另一个是controller控制器,用于将sensor挂载在linux系统的I2C上。驱动程序则通过I2C与Gsensor做通信。

n        GsensorDriver:是驻留于操作系统中,为gsensor hardware服务的一个内核模块;它将gsensor hardware采集到的原始数据,进行降噪,滤波,获得当前平板的空间状态,并按照操作系统的要求,将这些信息通过input core上报给操作系统。

n        Input core: 是linux为简化设备驱动程序开发,而开发的一个内核子系统;发给input core的数据将提供给操作系统使用。

实际使用时,驱动按照一定的时间间隔,通过数据总线,获取gsensor hardware采集到的数据,并按照操作系统的要求,将这些信息通过input core上报给操作系统。

2Gsensor驱动设计要求

由gsensor驱动在系统中的层次,上有Input core,下有I2C,驱动需要通过I2C采集信息,并准确及时的上报数据至input core。驱动上报的数据,是被input core管理并被上层使用的,应符合input core和上层应用框架的要求;

2.1符合Input输入子系统的设计规范

n        接口:Gsensor驱动,在设计上,不应自行决定是否上报,上报频率等,应提供接口,供上层应用控制驱动的运行和数据上报:包括使能控制Enable,上报时延delay等;通常通过sysfs文件系统提供,这部分实现,遵循标准的linux规范;

n        上报数据的方式:或者提供接口供上层访问(eg:ioctl),或者挂接在系统子系统上,使用系统子系统的接口,供上层使用(eg: input core);

n        读取数据的支持:应满足读取数据的要求,进行相应的配置;本文以i2c总线为例,简要说明在A1x平台上,配置总线传输相关信息;

2.2I2c总线的配置

要使用i2c总线进行数据传输,需注册i2c driver,创建i2c-client,以便使用i2c-adapter进行数据传输;

要成功注册i2cdriver有两种方式:

n        使用i2c_register_board_info:此方式,需要在系统启动时,进行相关信息的注册,不利于模块化开发,现已不推荐;目前,在2.6内核上,还支持此方式,在3.0上已不再支持;

n        使用detect方式:在模块加载时,进行检测,在条件成立时,注册i2c设备相关信息,创建i2c-client,并注册i2c driver,执行probe操作;

 

需要说明的是,此两种方式可共存,目前2.6就是这样的;在共存时,以i2c_register_board_info信息为更高优先级,在i2c_register_board_info已经占用设备的前提下,内核发现设备被占用,不会执行detect, 因而不会有冲突。

3gsensor模块硬件说明

n        gsensor硬件,负责获取gsensor传感器所处的空间状态信息,存放于fifo中,供主控使用,不同的硬件平台,数据准备好后,告知主控的方式及主控获取数据的方式略有不同。

n        告知主控的方式:gsensor作为传感器,本身无法区分哪些数据是应该上报的,哪些数据是无效的,它只能接受主控的控制,以主控主动查询为主;

n        主控获取数据的方式:通过ahb, i2c,spi,usb等方式获取都是可能的。以下以一种典型的硬件连接为例,描述gsensor 传感器,gsensor ic,主控之间的连接关系;

3.1gsensor硬件连接

linux <wbr>Gsensor<strong>驱动</strong>(bma250为例子)

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.2Gsensor配置

在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_driverbma250_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;

#ifdefCONFIG_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_driverbma250_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 __initBMA250_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 __exitBMA250_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.2bma250初始化工作-probe

static intbma250_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函数中,主要完成了以下几件事:

n        为驱动私有数据结构体bma250_data分配内存空间

n        读取IIC chip id

n        将设备驱动的私有数据(bma250_data)连接到设备client(i2c_client)中

n        创建工作队列

n        将bma250驱动注册到linux input子系统

n        创建sysfs接口

下面对以上这些工作做详细解释,分配数据内存空间就不讲了

5.2.1 读取IICchip id

在4.1的列表中,我们可以看到bma250的chip ID寄存器为0x00,chip ID的值为3。而上面代码有两个宏的定义:

#defineBMA250_CHIP_ID_REG  0x00

#defineBMA250_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 voidbma250_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 intbma250_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_attributedev_attr_##_name = __ATTR(_name, _mode, _show, _store)

函数宏DEVICE_ATTR内封装的是__ATTR(_name,_mode,_show,_stroe)方法:

n        _show:表示的是读方法,

n        _stroe表示的是写方法。

当然_ATTR不是独生子女,他还有一系列的姊妹__ATTR_RO宏只有读方法,__ATTR_NULL等等:

n        对设备的使用       DEVICE_ATTR   

n        对驱动使用              DRIVER_ATTR

n        对总线使用             BUS_ATTR 

n        对类别 (class) 使用  CLASS_ATTR

对于DEVICE_ATTR(_name,_mode, _show, _store)的四个参数,分别是名称、权限位、读函数、写函数。其中读函数和写函数是读写功能函数的函数名。

如果你完成了DEVICE_ATTR函数宏的填充,下面就需要创建接口了。例如如下:

1)        

 staticDEVICE_ATTR(polling, S_IRUGO |S_IWUSR, show_polling, set_polling);
    static struct attribute*dev_attrs[] = {
          &dev_attr_polling.attr,
          NULL,
    };

 

  

2)       当你想要实现的接口名字是polling的时候,需要实现结构体structattribute *dev_attrs[]。其中成员变量的名字必须是&dev_attr_polling.attr。然后再封装:

       static struct attribute_group dev_attr_grp ={
           .attrs =dev_attrs,
       };

3)       在利用sysfs_create_group(&pdev->dev.kobj,&dev_attr_grp);创建接口

      通过以上简单的三个步骤,就可以在adb shell 终端查看到接口了。当我们将数据 echo 到接口中时,在上层实际上完成了一次 write 操作,对应到 kernel ,调用了驱动中的 “store”。同理,当我们cat 一个 接口时则会调用 “show” 。到这里,只是简单的建立了 android 层到 kernel 的桥梁,真正实现对硬件操作的,还是在 "show" 和 "store" 中完成的。

5.2.4.2 bma250驱动sysfs接口建立

Bma250驱动sysfs接口建立,按照5.2.4.1上面介绍的三个步骤来实现。

n        调用宏DEVICE_ATTR完成对功能函数的注册

    

static DEVICE_ATTR(delay,S_IRUGO|S_IWUSR|S_IWGRP,

                            bma250_delay_show, bma250_delay_store);

static DEVICE_ATTR(enable,S_IRUGO|S_IWUSR|S_IWGRP,

                            bma250_enable_show, bma250_enable_store);

 

static struct attribute*bma250_attributes[] = {

             &dev_attr_delay.attr,

             &dev_attr_enable.attr,

             NULL

};

   对于bma250Gsensor,我们需要用到的接口是对Gsensor使能和设置delay。设置delay的功能函数——读、写分别是bma250_delay_show、bma250_delay_store。使能的功能函数——读写分别是bma250_enable_show、bma250_enable_store。这里提到的四个函数,是需要在Gsensor驱动中实现的。

n        封装bma250_attributes数据结构

static structattribute_group bma250_attribute_group = {

             .attrs = bma250_attributes

};

n        真正创建接口

在bma250的初始化函数probe中——bma250_probe(),调用:

errsysfs_create_group(&data->input->dev.kobj,&bma250_attribute_group);

 

到此,完成了sysfs接口的创建,我们可以在根文件系统中看到/sys/class/input/input3/目录,在该目录下我们可以看到多个节点,其中就包含了enable和delay。我们以enable为例子,可以有两种方法完成对Gsensor的使能工作:

1)       直接使用shell命令

$cd/sys/class/input/input3

$echo 1 >enable

将1写到enable节点,那么将“1”作为参数调用到驱动的bma250_enable_store()方法,完成对Gsensor的使能工作。

2)       代码写设备节点

char buffer[20];

int len = sprintf(buffer,"%d\n", 1);

fd =open(“/sys/class/input/input3/enable”, O_RDWR);

write(fd, value,len)

 

5.3 读取并上报数据

在Android的HAL层,通过对/sys/class/input/input3/enable节点的写操作,使能Gsensor。调用到的方法是bma250_enable_store():

static ssize_tbma250_enable_store(struct device *dev,

                            struct device_attribute *attr,

                            const char *buf, size_t count)

{

             ……

                            bma250_set_enable(dev,data);

   ……

             return count;

}

static voidbma250_set_enable(struct device *dev, int enable)

{

             struct i2c_client *client = to_i2c_client(dev);

             struct bma250_data *bma250 = i2c_get_clientdata(client);

             int pre_enable =atomic_read(&bma250->enable);

 

             mutex_lock(&bma250->enable_mutex);

             if (enable) {

                            if (pre_enable ==0) {

                                          bma250_set_mode(bma250->bma250_client,

                                                                                                  BMA250_MODE_NORMAL);

                                          schedule_delayed_work(&bma250->work,

                                                        msecs_to_jiffies(atomic_read(&bma250->delay)));

                                          atomic_set(&bma250->enable,1);

                            }

                            

             }……     

}

我们看到了,在使能函数中,调用了schedule_delayed_work()开始工作队列,于是调用了功能函数bma250_work_func()

static voidbma250_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);

             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); //继续开始下一个工作队列

}

在上面代码中,调用bma250_read_accel_xyz()方法读取Gsensor的三个数据,然后调用Input系统的接口函数input_report_abs()进行数据的上报。可以看到,当读取一次数据后,继续调用schedule_delayed_work()开始下一个工作队列,由此,功能函数bma250_work_func()将会按照一个的频率被执行。

那么对于HAL层,将通过/dev/input/event3设备节点读取到Gsensor数据。到此,Gsensor驱动的工作流程完毕。应该很好理解吧!

驱动调试

1)确保硬件各个管脚的连接顺序正确;

2)上电,测试各个管脚信号的电压正常,只有在保证硬件正常的情况下,进行软件驱动调试,方可保证驱动能够正常工作(该处最容易被很多软件开发人员忽视,务必注意,方可节省大部分时间)

3)将串口打印信息打开,串口打印信息设置:在打包工具中的crane-win-v2\wboot\bootfs\linux目录下的params和paramsr两个文件中的语句的最后加入loglevel=9即可。gsensor驱动中所有的打印信息打开,查看驱动程序的配置信息读取状态以及I2C的初始化状态。

4)查看probe是否成功,如probe不成功,根据打印信息定位驱动的运行情况,是因为什么原因导致失败。

5)当probe成功之后,gsensor没反应,查看打印信息,是否enable,确保enable。

6)查看i2c通信状态,当串口打印信息显示i2C通信失败时,主要有以下两个原因,一是硬件上的,各个信号线接触不良,所以出现通信失败时,检查各引脚接触情况和电压情况。二是因为I2C的地址不正确导致,因为i2C地址为7位地址,所以可能是因为在配置的时候没有移位或者是主控IC有多个I2C地址,导致地址不匹配。在已知i2C地址的情况下,可以通过尝试的方法,进行I2C地址的匹配;在不知道I2C地址的情况下,可以通过扫描的方法查看在哪一个地址时,有应答,即可知道I2C通信地址,在将正确的地址填写sysconfig配置文件中即扫描i2c地址的示例代码如下所示:

static int goodix_iic_test(struct i2c_client *client)

{

      struct i2c_msg msg;

      int ret=-1;

      uint8_t data[0];

      int i;

      for(i =0; i<256;i++)

      {

      msg.flags = !I2C_M_RD;//写消息

      msg.addr = i;

      msg.len = 1;

      msg.buf =data;                  

      

      ret=i2c_transfer(client->adapter,&msg,1);

      if(ret == 1)

      {

        printk("IIC TEST OK addr = %x\n",i);

        break;

      }

      mdelay(1000);

   }

      return ret;

}

 

 

若以上两种方法都不能正确进行i2c传输,则打开i2c传输打印,查看传输打印状态,在编译服务器上,目录为workspace\exdroid\lichee\linux-2.6.36上,输入命令:

make ARCH=arm menuconfig ,选择Device Drivers->I2Csupport->I2C Hardware Bussupport->SUN4I_IIC_PRINT_TRANSFER_INFO,输入Y进入bus num id(accepatableinput:0,1,2)(new),输入数值,,若希望打印信息,数值对应相应的IIC号,gsensorIC用的是第二组,因此选择数值为2,若不希望打印信息,输入N退出保存即可。进行修改后,需要重新编译打包之后才能生效。

linux <wbr>Gsensor<strong>驱动</strong>(bma250为例子)

 

7)可使用adb工具查看驱动是否加载以及gsensor是否有反应,adb工具需要安装设备对应的驱动。使用adb shell工具查看驱动是否存在于机器中以及驱动是否已经加载,以及gsensor之后是否有反应。同时可以作为简单的调试工具,修改好的驱动PUSH到机器中,重启系统之后即可运行新的驱动,不用重新打包(配置文件内容除外)。使用到的命令如下所示:

adbshell   登录设备的shell

adb push xx.ko/drv     将触摸驱动通过adb工具push到机器中

cd   /drv  进入drv目录

ls   *.ko  查看机器中已经有了那些驱动

lsmod  查看系统中已经加载了那些模块

rmmod ** 卸载驱动(注:不用加后缀)

insmod **.ko  加载驱动

getevent 查看系统中已经注册了那些input设备(当触摸有效时,触摸屏幕,会有相应的打印信息)



http://chatgpt.dhexx.cn/article/abiyXdUs.shtml

相关文章

MTK平台Android Gsensor数据校准与数据获取

http://blog.csdn.net/morixinguan/article/details/76850600 上节,写WIFI MAC地址的时候我们已经知道,MTKAndroid系统的Gsensor校准的数据其实也是存储在NVRAM中的,Gsensor隶属于传感器系统架构。 接下来我们来看下Gsensor校准的基准图像: 那么如何来校准Gsensor的X,Y,Z三…

android g sensor,android gsensor 休眠震动唤醒功能怎么实现

一、唤醒源 设备休眠后&#xff0c;通过触发唤醒源使设备恢复正常工作模式。设备唤醒源有多种&#xff0c;对于Android设备常见的就有PowerKey、来电唤醒、Alarm唤醒等。 唤醒源的实现处于内核空间&#xff0c;本文重点讨论下PowerKey作为唤醒源的具体实现。 二、PowerKey唤醒源…

G-sensor 介绍

转自&#xff1a;http://blog.chinaunix.net/uid-29595319-id-4200772.html G-sensor G-sensor是加速度传感器&#xff0c;可以通过其来获得分别来自三个不同轴向上的加速度用以通知上层应用做出相应处理。 由于地球的引力作用&#xff0c;gsensor平放时&#xff0c;Z轴方向能…

G-sensor概述及常用概念整理【转】

本文转载自:http://www.jianshu.com/p/d471958189a0?nomobile=yesG 本文对G-sensor进行整理,先介绍G-sensor的一些基本概念,再具体讲解BOSCH、ST、ADI三家的G-sensor,其中BOSCH的G-sensor重点讲BMA222E,ST的G-sensor重点讲LIS2DH12,ADI的G-sensor具体讲ADXL362。 一、G-…

初次使用Fleck+redis订阅发布实现学习小demo

https://www.cnblogs.com/SupPilot/p/10396333.html 首先安装Fleck的程序包添加引用下面是客户端代码&#xff1a; 客户端代码&#xff1a; 运行效果&#xff1a; 遇到的问题&#xff1a; Q:通常每个套接字地址(协议/网络地址/端口)只允许使用一次 A:是因为本萌新把WebSocke…

core+Fleck+redis

1.新建core版控制台程序引用 2.初始化websocket class Program { //客户端url以及其对应的Socket对象字典 static IDictionary<string, IWebSocketConnection> dic_Sockets new Dictionary<string, IWebSocketConnection>(); static …

[c#]使用Fleck实现简单的WebSocket含兼容低版本IE

WebSocket是html5的一种协议,那么就表示要使用websocket客户端的浏览器就要支持html5。 对于不支持的使用flash去解决。 Fleck源码地址 服务端采用的是Fleck,Fleck的好处就是简单方便,作者已经进行了完整的封装,可以根据自己情况进行修改 兼容低版本IE 使用flash实现兼容低…

C#实现WebSocket(基于Fleck-服务端和WebSocketSharp-客户端)

C#实现WebSocket(基于Fleck-服务端和WebSocketSharp-客户端) 官网&#xff1a;https://github.com/sta/websocket-sharp 客户端 private void btn_Connect_Click_1(object sender, EventArgs e){Console.WriteLine("Websocket Demo");WebSocket web new WebSocket(…

C#通过fleck实现wss协议的WebSocket多人Web实时聊天(附源码)

前言 最近想做一个Web版的即时聊天为后面开发的各项功能做辅助&#xff0c;就需要浏览器与服务器能够实时通讯。而WebSocket这种双向通信协议&#xff0c;就很合适用来实现这种需求。 本篇文章主要解决C#如何实现WebSocket服务端和Javascript客户端基于wss协议的安全通信问题。…

Asp.Net Mvc基于Fleck开发的多人网页版即时聊天室

一、项目的核心说明 1、Fleck这个是实现websocket一个比较简单第三方组件&#xff0c;它不需要安装额外的容器。本身也就几个接口可供调用。 2、项目是基于.net framework 4.7.2 &#xff0c;在vs2019上开发的&#xff0c;没试过在低版本上运行。但是代码上没怎么用到新特性&…

winfrom+Fleck 上传文件

最近这个项目要做一个Winfrom上传Execl到服务器。 就想到之前用Fleck做的一个小功能&#xff0c;但是客户端Web端的。百度吧&#xff0c;看有没有办法&#xff0c;开始找到fw4 &#xff0c;但是要引用它的dll&#xff0c;是可以&#xff0c;继续找了会结果找到ClientWebSocket…

C#工作总结(一):Fleck的WebSocket使用

一.引子&#xff08;Foreword&#xff09; 最近公司里面要做窗体和网页交互的功能。网上找了一下资料&#xff0c;这里做一个简单的扩充和整理&#xff0c;部分内容可能是摘自其他博客&#xff0c;这里会注明出处和原文地址供大家和自己日后查阅。 二.基础知识&#xff08;Foun…

Fleck说明文档翻译

CSDN ZslLoveMiwa的博客 Fleck Fleck是一个在C#中的WebSocket服务器端的一个实现.。分支自Nugget项目, Fleck不需要任何的继承、容器亦或是需要增加的引用就可以使用。 实例 下面是一个把客户端网页发来的消息回发到客户端的服务器程序。 var server new WebSocketServer(&q…

fleck 客户端_C#中使用Fleck实现WebSocket通信简例

Fleck是一个开源的使用C#封装的WebSocket服务端工具库。 一、服务端搭建 这里选择在.net core 2.1框架下新建了一个控制台程序 在项目里使用NuGet引入Fleck包 NuGet引入Fleck包 其它方式也可以,最终引入项目里都是Fleck.dll Fleck.dll 二、服务端实例 Github上的简单例子: 简…

Fleck WebSocket使用

作为笔记存储. 最近公司有这方面的使用需求。在网上查了一些资料后。得到了想要的结果。以下记录摘抄至网上资料 1.首先,服务端。项目NuGet直接引用Fleck类库。 code: 1 //客户端url以及其对应的Socket对象字典2 IDictionary<string, IWebSocket…

车牌识别技术原理

车牌识别技术原理 车牌识别技术原理都是一样的&#xff0c;具体流程如下&#xff1a;图像捕捉与获取、车牌定位、字符分割字符识别、输出结果。不一样的是在上述环节中采用不同的技术&#xff0c;比如作为核心技术的车牌定位&#xff0c;就可能用到(1)自适应边界搜索法、(2)区…

python实现车牌识别

原始图象 代码 from hyperlpr import HyperLPR_plate_recognition import cv2 from PIL import ImageFont, ImageDraw, Image import numpy as npimage cv2.imread("car.jpg")#读入图片 resultHyperLPR_plate_recognition(image)#识别车牌 plate result[0][0] #…

基于形态学处理的车牌提取,字符分割和车牌识别算法matlab仿真

目录 1.算法仿真效果 2.MATLAB核心程序 3.算法涉及理论知识概要 4.完整MATLAB 1.算法仿真效果 matlab2022a仿真结果如下&#xff1a; 2.MATLAB核心程序 ........................................................................... figure(8);subplot(3,2,2),imshow(d…

车牌识别算法介绍与实践

汽车牌照自动识别整个处理过程分为预处理、边缘提取、车牌定位、字符分割、字符识别五大模块&#xff0c;其中字符识别过程主要由以下3个部分组成&#xff1a; ①正确地分割文字图像区域&#xff1b; ②正确的分离单个文字&#xff1b; ③正确识别单个字符。 用MATLAB软件编…

基于android的车牌识别程序,基于Android平台车牌识别算法,实现手机识别车牌

原标题:基于Android平台车牌识别算法,实现手机识别车牌 近年来,随着移动行业的爆发式发展,手机配置不断提高,基于手机平台的信息采集、图像处理、数据传输等方面的研究也成为了热点,这使得基于手机平台上的车牌识别成为可能。传统的车牌识别系统一般都基于固定的桌面平台…