9.5 KiB
感 - 传感器框架
多数嵌入式操作系统对传感器的抽象采用以物理传感器设备为中心的方式,在应用开发时无法绕过传感器设备的多样性,进而增加了传感器应用的开发难度和周期。这种抽象带来的缺陷在面对一些可以同时采集多种物理量的传感器(如温湿度传感器)时尤为突出,因为应用开发者不得不考虑每种传感器设备的采集数据的能力。
XiUOS的传感器框架以用户为中心,采用了以物理量为中心的抽象方式,在开发应用时只需要考虑所需感知的物理量种类,无需把重点放在实际采集物理量的传感器设备。这种抽象方式有效地隐藏了底层传感器设备的硬件细节,对外提供统一的数据采集接口,可以简化传感器应用与驱动的开发。为实现以物理量为中心的抽象,传感器框架对传感器设备进行两层抽象:
- 一个物理传感器测量一种物理量的能力(ability)被抽象为一个xs_SensorQuantity结构
- 一个物理传感器本身被抽象为一个xs_SensorDevice结构
其中xs_SensorQuantity被设计为可以采用类似面向对象的方法、针对不同物理量扩展其数据成员与接口,从而为众多不同性质的物理量实现统一的管理架构。在应用开发的过程中只需要使用对应物理量的xs_SensorQuantity实例,无需关心传感器的硬件细节,从而实现数据采样功能与底层硬件的解耦。
从关联关系上来看,一个xs_SensorQuantity对应一个xs_SensorDevice,一个xs_SensorDevice对应一个或多个xs_SensorQuantity。例如,对于一个可以测量温度与湿度的传感器设备,该设备唯一对应一个xs_SensorDevice结构,而该设备测量温度与湿度的能力分别对应一个xs_SensorQuantity结构。两种数据结构的具体定义如下。
1. XiUOS传感器框架的关键数据结构定义和解析
- struct xs_SensorQuantity结构
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 */
};
name成员是一个可读的名字,用于唯一标识一个xs_SensorQuantity结构。
type成员表示该xs_SensorQuantity可测量的物理量,用一个枚举变量表示:
enum xs_SensorQuantityType {
XS_SENSOR_QUANTITY_CO2 = 0, /* CO2 concentration */
XS_SENSOR_QUANTITY_TEMP, /* temperature */
XS_SENSOR_QUANTITY_HUMI, /* humidity */
/* ...... */
XS_SENSOR_QUANTITY_END,
};
sdev成员表示该xs_SensorQuantity所属的xs_SensorDevice结构,其具体定义在下文给出。
最后,在系统中每种物理量的xs_SensorQuantity被分别组织成不同双链表,如二氧化碳浓度xs_SensorQuantity链表、温度xs_SensorQuantity链表等,使用的链表节点即为link成员。
- struct xs_SensorDevice结构
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 */
};
name成员记录传感器设备在系统中的名字,用于唯一标识一个xs_SensorDevice结构
info成员记录传感器设备的一些属性信息,包括传感器的能力ability、厂家名vendor与型号product_model,其中ability用一个位图表示该传感器设备可以测量的物理量:
#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 xs_SensorProductInfo {
uint32_t ability; /* bitwise OR of XS_SENSOR_ABILITY_XXX */
const char *vendor;
const char *product_model;
};
ops成员包含统一的、类似文件系统的API,用于对传感器进行实际的数据读写。在使用一个传感器前后需要打开(open)/关闭(close)该传感器,read、write分别用与从传感器接收数据与向传感器发送数据,ioctl用于配置传感器属性(如波特率):
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);
};
interface成员表示用于与传感器进行通信的总线设备:
struct xs_SensorInterface {
xs_device_t bus_device;
};
最后,系统中所有注册过的传感器设备被组织成一个双链表,即link成员。
2. XiUOS传感器框架驱动开发
以二氧化碳传感器为例。传感器框架针对每个具体的物理量将xs_SensorQuantity进行扩充,采用类似面向对象的手段添加其他必要成员,如:
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:
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函数将其注册到传感器框架:
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()
{
/* initialize and register the xs_SensorDevice object */
memset(&co2_example_sdev, 0, sizeof(xs_SensorDevice));
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");
xs_SensorDeviceRegister(&co2_example_sdev);
/* initialize and register the xs_SensorQuantity object */
memset(&co2_example_quant, 0, sizeof(xs_SensorQuantityCo2));
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;
xs_SensorQuantityRegister((struct xs_SensorQuantity *)&co2_example_quant);
}
3. XiUOS传感器框架的使用实例
传感器应用开发者使用传感器框架提供的API操作传感器,传感器API可以分为通用API与物理量特有API。通用API用于传感器的获取、打开与关闭,物理量特有API用于传感器的数据采样。以二氧化碳传感器为例:
/* generic API: find a sensor quantity instance by its name */
struct xs_SensorQuantity *xs_SensorQuantityFind(const char *name);
/* generic API: open/close a sensor quantity instance */
int xs_SensorQuantityOpen(struct xs_SensorQuantity *quant);
void xs_SensorQuantityClose(struct xs_SensorQuantity *quant);
/* CO2 API: get current CO2 concentration reading (in ppm unit) */
uint32_t xs_SensorCo2Read(struct xs_SensorQuantityCo2 *quant);
在获取数据前需要先获取并打开要使用的传感器,传感器打开后可以随时对传感器数据进行读取,使用完毕必须关闭传感器。完整的使用过程示例如下:
int main(int argc, char *argv[])
{
int ret;
struct xs_SensorQuantity *quant;
struct xs_SensorQuantityCo2 *co2_quant;
/* get the CO2 sensor quantity instance */
quant = xs_SensorQuantityFind("co2_1");
XS_ASSERT(quant->type == XS_SENSOR_QUANTITY_CO2);
/* 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);
return 0;
}