diff --git a/docs/doc/framework/gan.md b/docs/doc/framework/gan.md index 7f37fab..186432e 100644 --- a/docs/doc/framework/gan.md +++ b/docs/doc/framework/gan.md @@ -3,191 +3,222 @@ 多数嵌入式操作系统对传感器的抽象采用以物理传感器设备为中心的方式,在应用开发时无法绕过传感器设备的多样性,进而增加了传感器应用的开发难度和周期。这种抽象带来的缺陷在面对一些可以同时采集多种物理量的传感器(如温湿度传感器)时尤为突出,因为应用开发者不得不考虑每种传感器设备的采集数据的能力。 XiUOS的传感器框架以用户为中心,采用了以物理量为中心的抽象方式,在开发应用时只需要考虑所需感知的物理量种类,无需把重点放在实际采集物理量的传感器设备。这种抽象方式有效地隐藏了底层传感器设备的硬件细节,对外提供统一的数据采集接口,可以简化传感器应用与驱动的开发。为实现以物理量为中心的抽象,传感器框架对传感器设备进行两层抽象: -* 一个物理传感器测量一种物理量的能力(ability)被抽象为一个xs_SensorQuantity结构 -* 一个物理传感器本身被抽象为一个xs_SensorDevice结构 +* 一个物理传感器测量一种物理量的能力(ability)被抽象为一个SensorQuantity结构 +* 一个物理传感器本身被抽象为一个SensorDevice结构 -其中xs_SensorQuantity被设计为可以采用类似面向对象的方法、针对不同物理量扩展其数据成员与接口,从而为众多不同性质的物理量实现统一的管理架构。在应用开发的过程中只需要使用对应物理量的xs_SensorQuantity实例,无需关心传感器的硬件细节,从而实现数据采样功能与底层硬件的解耦。 +其中SensorQuantity被设计为可以采用类似面向对象的方法、针对不同物理量扩展其数据成员与接口,从而为众多不同性质的物理量实现统一的管理架构。在应用开发的过程中只需要使用对应物理量的SensorQuantity实例,无需关心传感器的硬件细节,从而实现数据采样功能与底层硬件的解耦。 -从关联关系上来看,一个xs_SensorQuantity对应一个xs_SensorDevice,一个xs_SensorDevice对应一个或多个xs_SensorQuantity。例如,对于一个可以测量温度与湿度的传感器设备,该设备唯一对应一个xs_SensorDevice结构,而该设备测量温度与湿度的能力分别对应一个xs_SensorQuantity结构。两种数据结构的具体定义如下。 +从关联关系上来看,一个SensorQuantity对应一个SensorDevice,一个SensorDevice对应一个或多个SensorQuantity。例如,对于一个可以测量温度与湿度的传感器设备,该设备唯一对应一个SensorDevice结构,而该设备测量温度与湿度的能力分别对应一个SensorQuantity结构。两种数据结构的具体定义如下。 ## 1. XiUOS传感器框架的关键数据结构定义和解析 -* struct xs_SensorQuantity结构 +* struct SensorQuantity结构 + ```c -struct xs_SensorQuantity { - const char name[XS_NAME_MAX]; /* name of the sensor quantity instance */ - enum xs_SensorQuantityType type; /* type of data the sensor collects, such as CO2 concentration */ - struct xs_SensorDevice *sdev; /* corresponding sensor device */ - struct XS_DOUBLE_LINKLIST_NODE link; /* link list node */ +struct SensorQuantity { + char *name; + enum SensorQuantityType type; + struct SensorQuantityValue value; + struct SensorDevice *sdev; + + int32 (*ReadValue)(struct SensorQuantity *quant); + + struct SysDoubleLinklistNode quant_link; + struct SysDoubleLinklistNode link; }; ``` -name成员是一个可读的名字,用于唯一标识一个xs_SensorQuantity结构。 -type成员表示该xs_SensorQuantity可测量的物理量,用一个枚举变量表示: +name成员是一个可读的名字,用于唯一标识一个SensorQuantity结构。 + +type成员表示该SensorQuantity可测量的物理量,用一个枚举变量表示: + ```c -enum xs_SensorQuantityType { - XS_SENSOR_QUANTITY_CO2 = 0, /* CO2 concentration */ - XS_SENSOR_QUANTITY_TEMP, /* temperature */ - XS_SENSOR_QUANTITY_HUMI, /* humidity */ +enum SensorQuantityType { + SENSOR_QUANTITY_CO2 = 0, + SENSOR_QUANTITY_TEMP, + SENSOR_QUANTITY_HUMI, + SENSOR_QUANTITY_HCHO, + SENSOR_QUANTITY_CO, + SENSOR_QUANTITY_PM, + SENSOR_QUANTITY_VOICE, /* ...... */ - XS_SENSOR_QUANTITY_END, + SENSOR_QUANTITY_END, }; ``` -sdev成员表示该xs_SensorQuantity所属的xs_SensorDevice结构,其具体定义在下文给出。 +value成员记录与该SensorQuantity相关的值,包括结果的小数位数、采集历史的最大值和最小值、国家标准的最大值和最小值、最近一次采集的值。取值时需要注意处理小数点,用变量的值除以小数位数乘以10。如果某一个值不存在,则为SENSOR_QUANTITY_VALUE_ERROR。 -最后,在系统中每种物理量的xs_SensorQuantity被分别组织成不同双链表,如二氧化碳浓度xs_SensorQuantity链表、温度xs_SensorQuantity链表等,使用的链表节点即为link成员。 - -* struct xs_SensorDevice结构 ```c -struct xs_SensorDevice { - const char name[XS_NAME_MAX]; /* name of the sensor device */ - struct xs_SensorProductInfo info; /* sensor model info, such as vendor name and model name */ - struct xs_SensorOps ops; /* filesystem-like APIs for data transferring */ - struct xs_SensorInterface interface; /* physical interface for transferring data */ - struct XS_DOUBLE_LINKLIST_NODE link; /* link list node */ +#define SENSOR_QUANTITY_VALUE_ERROR ((uint32)0xffffffff) + +struct SensorQuantityValue { + uint8 decimal_places; /* The decimal place of the result */ + uint32 last_value; /* The last read value, if it does not exist, is SENSOR_QUANTITY_VALUE_ERROR */ + uint32 min_value; /* The minimum read value, if it does not exist, is SENSOR_QUANTITY_VALUE_ERROR */ + uint32 max_value; /* The maximum read value, if it does not exist, is SENSOR_QUANTITY_VALUE_ERROR */ + uint32 min_std; /* The minimum standard value, if it does not exist, is SENSOR_QUANTITY_VALUE_ERROR */ + uint32 max_std; /* The maximum standard value, if it does not exist, is SENSOR_QUANTITY_VALUE_ERROR */ }; ``` -name成员记录传感器设备在系统中的名字,用于唯一标识一个xs_SensorDevice结构 -info成员记录传感器设备的一些属性信息,包括传感器的能力ability、厂家名vendor与型号product_model,其中ability用一个位图表示该传感器设备可以测量的物理量: +sdev成员表示该SensorQuantity所属的SensorDevice结构,其具体定义在下文给出。 + +ReadValue指向SensorQuantity的解析函数,根据每个传感器模块的协议不同而不同,解析结果会存入value成员的last_value中。 + +最后,在系统中每种物理量的SensorQuantity会注册到两个链表中,quant_link注册到所属的SensorDevice下,link注册到对应的物理量链表,如二氧化碳浓度SensorQuantity链表、温度SensorQuantity链表等。 + +* struct SensorDevice结构 ```c -#define XS_SENSOR_ABILITY_CO2 ((uint32_t)(1 << XS_SENSOR_QUANTITY_CO2)) -#define XS_SENSOR_ABILITY_TEMP ((uint32_t)(1 << XS_SENSOR_QUANTITY_TEMP)) -#define XS_SENSOR_ABILITY_HUMI ((uint32_t)(1 << XS_SENSOR_QUANTITY_HUMI)) +struct SensorDevice { + char *name; /* Name of sensor */ + struct SensorProductInfo *info; /* Sensor model info */ + struct SensorDone *done; + int fd; /* File descriptor */ + int status; /* Sensor work mode */ + uint8 buffer[SENSOR_RECEIVE_BUFFSIZE]; /* Buffer for read data */ + + int ref_cnt; /* Reference count */ + DoubleLinklistType quant_list; /* Sensor quantity link */ + struct SysDoubleLinklistNode link; /* Sensors link node */ +}; +``` + +name成员记录传感器设备在系统中的名字,用于唯一标识一个SensorDevice结构 + +info成员记录传感器设备的一些属性信息,包括传感器的采集能力ability、厂家名vendor name与产品型号model name,其中ability用一个位图表示该传感器设备可以测量的物理量: + +```c +#define SENSOR_ABILITY_CO2 ((uint32_t)(1 << SENSOR_QUANTITY_CO2)) +#define SENSOR_ABILITY_TEMP ((uint32_t)(1 << SENSOR_QUANTITY_TEMP)) +#define SENSOR_ABILITY_HUMI ((uint32_t)(1 << SENSOR_QUANTITY_HUMI)) +#define SENSOR_ABILITY_HCHO ((uint32_t)(1 << SENSOR_QUANTITY_HCHO)) +#define SENSOR_ABILITY_CO ((uint32_t)(1 << SENSOR_QUANTITY_CO)) +#define SENSOR_ABILITY_PM ((uint32_t)(1 << SENSOR_QUANTITY_PM)) +#define SENSOR_ABILITY_VOICE ((uint32_t)(1 << SENSOR_QUANTITY_VOICE)) /* ...... */ -struct xs_SensorProductInfo { - uint32_t ability; /* bitwise OR of XS_SENSOR_ABILITY_XXX */ - const char *vendor; - const char *product_model; +struct SensorProductInfo { + uint32_t ability; /* Bitwise OR of sensor ability */ + const char *vendor_name; + const char *model_name; }; ``` -ops成员包含统一的、类似文件系统的API,用于对传感器进行实际的数据读写。在使用一个传感器前后需要打开(open)/关闭(close)该传感器,read、write分别用与从传感器接收数据与向传感器发送数据,ioctl用于配置传感器属性(如波特率): +done成员包含统一的、类似文件系统的API,用于对传感器进行实际的数据读写。在使用一个传感器前后需要打开(open)/关闭(close)该传感器,read、write分别用与从传感器接收数据与向传感器发送数据,ioctl用于配置传感器工作模式(如主动上报或一问一答): + ```c -struct xs_SensorOps { - int (*open)(struct xs_SensorDevice *sdev); - void (*close)(struct xs_SensorDevice *sdev); - int (*read)(struct xs_SensorDevice *sdev, void *buf, size_t len); - int (*write)(struct xs_SensorDevice *sdev, const void *buf, size_t len); - int (*ioctl)(struct xs_SensorDevice *sdev, int cmd, void *arg); +struct SensorDone { + int (*open)(struct SensorDevice *sdev); + int (*close)(struct SensorDevice *sdev); + x_size_t (*read)(struct SensorDevice *sdev, size_t len); + int (*write)(struct SensorDevice *sdev, const void *buf, size_t len); + int (*ioctl)(struct SensorDevice *sdev, int cmd); }; ``` -interface成员表示用于与传感器进行通信的总线设备: -```c -struct xs_SensorInterface { - xs_device_t bus_device; -}; +fd成员表示用于与传感器进行通信的设备的文件描述符。 + +status成员标识传感器的工作模式: + ``` +/* Sensor quantity report mode */ +#define SENSOR_DEVICE_PASSIVE 0x00 +#define SENSOR_DEVICE_ACTIVE 0x01 +``` + +buffer成员用来存放从传感器中读取到的数据。 + +ref_cnt成员记录SensorDevice被打开的次数,当该值为0时传感器将被关闭。 + +quant_list链表中注册了该传感器支持采集的物理量。 最后,系统中所有注册过的传感器设备被组织成一个双链表,即link成员。 ## 2. XiUOS传感器框架驱动开发 -以二氧化碳传感器为例。传感器框架针对每个具体的物理量将xs_SensorQuantity进行扩充,采用类似面向对象的手段添加其他必要成员,如: +以二氧化碳传感器ZG09为例。实现SensorDone中的API,具体实现细节取决于传感器型号,无法实现的API可以置为NONE: + ```c -struct xs_SensorQuantityCo2 { - struct xs_SensorQuantity parent; /* inherit from xs_SensorQuantity */ - - uint32_t (*read_concentration)(struct xs_SensorQuantityCo2 *quant); - - uint32_t value_last; /* last measured value */ - uint32_t value_min; /* minimum measured value */ - uint32_t value_max; /* maximum measured value */ - uint32_t std_min; /* national standard: minimum */ - uint32_t std_max; /* national standard: maximum */ -}; -``` - -实现xs_SensorOps中的数据通信API,具体实现细节取决于传感器型号,无法实现的API可以置为NULL: -```c -struct xs_SensorOps co2_example_ops = { - .open = co2_example_open; - .close = co2_example_close; - .read = co2_example_read; - .write = NULL; - .ioctl = co2_example_ioctl; -}; -``` - -实现xs_SensorQuantityCo2中的read_concentration接口,该接口用于读取当前空气中的二氧化碳浓度。在实现过程中可以使用xs_SensorOps中的接口与传感器进行通信。最后,将传感器设备添加到传感器框架。分别填充xs_SensorDevice与对应物理量的xs_SensorQuantity结构(二氧化碳即为xs_SensorQuantityCo2),并依次使用xs_SensorDeviceRegister和xs_SensorQuantityRegister函数将其注册到传感器框架: -```c -int xs_SensorDeviceRegister(struct xs_SensorDevice *sdev); -int xs_SensorQuantityRegister(struct xs_SensorQuantity *quant); - -extern struct xs_SensorOps co2_example_ops; -extern uint32_t co2_example_read_concentration(struct xs_SensorQuantityCo2 *quant); - -/* declare xs_SensorDevice and xs_SensorQuantityCo2 objects */ -struct xs_SensorDevice co2_example_sdev; -struct xs_SensorQuantityCo2 co2_example_quant; - -void register_co2_sensor() +static struct SensorDone done = { - /* initialize and register the xs_SensorDevice object */ - memset(&co2_example_sdev, 0, sizeof(xs_SensorDevice)); + SensorDeviceOpen, + NONE, + SensorDeviceRead, + NONE, + SensorDeviceIoctl, +}; +``` - co2_example_sdev.name = "sensor1"; - co2_example_sdev.info.ability |= XS_SENSOR_ABILITY_CO2; - co2_example_sdev.info.vendor = "xxx"; - co2_example_sdev.info.product_model = "yyy"; - co2_example_sdev.ops = &co2_example_ops; - co2_example_sdev.interface.bus_device = xs_DeviceFind("uart1"); +实现SensorQuantity中的ReadValue接口,该接口会解析出当前空气中的二氧化碳浓度。在实现过程中可以使用SensorDone中的接口与传感器进行通信。最后,将传感器设备添加到传感器框架。分别填充SensorDevice与对应物理量的SensorQuantity结构,并依次使用SensorDeviceRegister和SensorQuantityRegister函数将其注册到传感器框架: - xs_SensorDeviceRegister(&co2_example_sdev); +```c +static struct SensorDevice zg09; +static void SensorDeviceZg09Init(void) +{ + zg09.name = SENSOR_DEVICE_ZG09; + zg09.info = &info; + zg09.done = &done; - /* initialize and register the xs_SensorQuantity object */ - memset(&co2_example_quant, 0, sizeof(xs_SensorQuantityCo2)); + SensorDeviceRegister(&zg09); +} - co2_example_quant.parent.name = "co2_1"; - co2_example_quant.parent.type = XS_XS_SENSOR_QUANTITY_CO2; - co2_example_quant.parent.sdev = &co2_example_sdev; - co2_example_quant.read_concentration = co2_example_read_concentration; +static struct SensorQuantity zg09_co2; - xs_SensorQuantityRegister((struct xs_SensorQuantity *)&co2_example_quant); +int Zg09Co2Init(void) +{ + SensorDeviceZg09Init(); + + zg09_co2.name = SENSOR_QUANTITY_ZG09_CO2; + zg09_co2.type = SENSOR_QUANTITY_CO2; + zg09_co2.value.decimal_places = 0; + zg09_co2.value.max_std = 1000; + zg09_co2.value.min_std = 350; + zg09_co2.value.last_value = SENSOR_QUANTITY_VALUE_ERROR; + zg09_co2.value.max_value = SENSOR_QUANTITY_VALUE_ERROR; + zg09_co2.value.min_value = SENSOR_QUANTITY_VALUE_ERROR; + zg09_co2.sdev = &zg09; + zg09_co2.ReadValue = QuantityRead; + + SensorQuantityRegister(&zg09_co2); + + return 0; } ``` ## 3. XiUOS传感器框架的使用实例 -传感器应用开发者使用传感器框架提供的API操作传感器,传感器API可以分为通用API与物理量特有API。通用API用于传感器的获取、打开与关闭,物理量特有API用于传感器的数据采样。以二氧化碳传感器为例: +以二氧化碳传感器为例,编译之前在menuconfig中打开Applications --> Using sensor apps --> Using sensor CO2 apps --> Using sensor ZG09 apps,main函数会调用框架初始化函数FrameworkInit初始化“感”框架并将CO2物理量注册到框架中,传感器应用开发者使用传感器框架提供的API操作传感器: + ```c -/* generic API: find a sensor quantity instance by its name */ -struct xs_SensorQuantity *xs_SensorQuantityFind(const char *name); +/* API: find a sensor quantity instance by its name and type */ +struct SensorQuantity *SensorQuantityFind(const char *name, enum SensorQuantityType type); -/* generic API: open/close a sensor quantity instance */ -int xs_SensorQuantityOpen(struct xs_SensorQuantity *quant); -void xs_SensorQuantityClose(struct xs_SensorQuantity *quant); +/* API: open/close a sensor quantity instance */ +int SensorQuantityOpen(struct SensorQuantity *quant); +int SensorQuantityClose(struct SensorQuantity *quant); -/* CO2 API: get current CO2 concentration reading (in ppm unit) */ -uint32_t xs_SensorCo2Read(struct xs_SensorQuantityCo2 *quant); +/* API: get current CO2 concentration reading */ +int32 SensorQuantityRead(struct SensorQuantity *quant); ``` -在获取数据前需要先获取并打开要使用的传感器,传感器打开后可以随时对传感器数据进行读取,使用完毕必须关闭传感器。完整的使用过程示例如下: +在获取数据前需要先获取并打开要使用的物理量,打开后可以随时对传感器数据进行读取,使用完毕必须关闭传感器。完整的使用过程示例如下: + ```c -int main(int argc, char *argv[]) +void Co2Zg09(void) { - int ret; - struct xs_SensorQuantity *quant; - struct xs_SensorQuantityCo2 *co2_quant; + struct SensorQuantity *co2 = SensorQuantityFind(SENSOR_QUANTITY_ZG09_CO2, SENSOR_QUANTITY_CO2); + SensorQuantityOpen(co2); + printf("CO2 : %d ppm\n", SensorQuantityRead(co2)); + SensorQuantityClose(co2); +} - /* get the CO2 sensor quantity instance */ - quant = xs_SensorQuantityFind("co2_1"); - XS_ASSERT(quant->type == XS_SENSOR_QUANTITY_CO2); +int main(void) +{ + UserPrintf("Hello, world!\n"); + FrameworkInit(); - /* open the CO2 sensor quantity instance */ - co2_quant = (struct xs_SensorQuantityCo2 *)quant; - ret = xs_SensorQuantityOpen(quant); - XS_ASSERT(ret == XS_EOK); - - /* read CO2 concentration for 5 times, just for demonstration */ - for (int i = 0; i < 5; i++) - xs_kprintf("Current CO2 concentration is %u ppm\n", xs_SensorCo2Read(co2_quant)); - - xs_SensorQuantityClose(quant); + Co2Zg09(); return 0; }