检测流量数据的方法有很多种,这一次我们就是使用SDP800差压传感器来测量流量数据。所以在这一篇中,我们将讨论如何实现SDP800差压传感器的驱动,并使用它实现流量数据的检测。
1、功能概述
SDP800差压传感器系列是Sensirion为大批量应用设计的数字压差传感器系列。传感器测量空气和非腐蚀性气体的压力,具有极高的精度,没有偏移。该传感器覆盖的压力范围高达±500 Pa,并提供卓越的精度。其结构及引脚定义如下图所示:
SDP800系列差压传感器具有数字2线I2C接口,这使得它很容易直接连接到微处理器。在I2C总线上每一台设备都有一个地址,SDP800差压传感器不同的型号设备地址略有差异,具体如下表:
虽然I2C接口基本有规范的通讯格式,但不同的设备在通讯报文的设置上还是有一下差异。这里SDP800差压传感器其通讯报文的格式如下:
在这一报文格式中,除了地址和数据还有一个16位的命令。这些命令是厂商设定的,用于实现对SDP800差压传感器的各种操作。这里我们只列出数据获取的命令。
对于SDP800差压传感器操作命令还有很多如配置、复位等我们在此不作详述。
2、驱动设计与实现
我们已经简单的描述了SDP800差压传感器的基本情况。这一节我们将进一步考虑SDP800差压传感器的驱动设计与实现。
2.1、对象定义
首先我们来考虑SDP800差压传感器的对象定义。关于对象总是存在对象的属性和操作,SDP800差压传感器对象我们也从这两个方面来考虑。
我们先来分析一下SDP800差压传感器对象的属性问题。SDP800差压传感器采用I2C接口,所以设备地址必不可少,而且每一个地址都唯一标识一台设备,所以我们将其设定为对象的属性。此外,SDP800差压传感器的产品编号和产品序列号都是唯一标识SDP800差压传感器设备,所以我们也将其设定为属性。我们也希望记录设备的状态、测量的压力、温度以及差压系数等。这些两标识了SDP800差压传感器设备的状态,所以我们也将其作为对象的属性。
而对象的操作,SDP800差压传感器采用I2C接口,所以需要接收和发送数据、为了控制时序我们需要延时操作函数。而这些函数的实现都依赖于具体的软硬件平台,所以我们将它们设置为对象的操作,以便于通过回调函数来实现对象平台无关性。根据上述分析我们可以定义SDP800差压传感器的对象类型如下:
/* 定义SDP800对象类型 */typedef struct SDP800Object{uint8_t devAddress; //SDP800对象的地址uint8_t status; //SDP800状态信息uint8_t pn[4]; //SDP800对象的产品号uint8_t sn[8]; //SDP800对象的序列号float dpressure; //差压float temperature;//温度float dpFactor; //差压系数void (*Delayms)(volatile uint32_t nTime); //延时操作指针void (*Receive)(struct SDP800Object *sdp,uint8_t *rData,uint16_t rSize); //接收数据操作指针void (*Transmit)(struct SDP800Object *sdp,uint8_t *tData,uint16_t tSize); //发送数据操作指针}SDP800ObjectType;
有了对象类型,我们就可以获得对象变量,但对象变量需要初始化后才能进行各种操作,所以我们需要实现一个SDP800差压传感器对象变量初始化的函数。
/*SDP800对象初始化配置*/SDP800ErrorType Sdp800Initialization(SDP800ObjectType *sdp,//SDP800对象uint8_t i2cAddress, //设备地址SDP800Receive recieve,//接收函数指针SDP800Transmit transmit, //发送函数指针SDP800Delayms delayms//毫秒演示函数){SDP800ErrorType error=SDP800_ERROR_NONE;if((sdp==NULL)||(recieve==NULL)||(transmit==NULL)||(delayms==NULL)){return SDP800_ERROR_IVALID_PARAMETER;}sdp->Receive=recieve;sdp->Transmit=transmit;sdp->Delayms=delayms;sdp->temperature=0.0;sdp->dpressure=0.0;if((i2cAddress==0x25)||(i2cAddress==0x26)){sdp->devAddress=(i2cAddress<<1);}else if((i2cAddress==0x4A)||(i2cAddress==0x4C)){sdp->devAddress=i2cAddress;}else{sdp->devAddress=0;error|=SDP800_ERROR_IVALID_PARAMETER;}if(error==SDP800_ERROR_NONE){error|=Sdp800ReadSerialNumber(sdp);}return error;}
在初始化函数中,我们对对象的属性以及操作函数的指针变量都做了初始化,并读取了设备的序列号。
2.2、对象操作
我们定义了SDP800差压传感器的对像类型,也设计了对象变量的初始化函数。这一节我们来看一看我们所要实现的操作。
2.2.1、数据的获取
我们需要对SDP800差压传感器所做的首要操作就是获取测量数据。根据不同的命令,SDP800差压传感器可以做单次测量,也可以做连续测量。这里我们采用连续测量的方式。连续测量设计到三类操作:开启连续测量、读取测量数据以及结束连续测量。根据通讯命令及报文格式要求,我们实现数据连续读取的代码如下:
/*连续读取测量值*/SDP800ErrorType Sdp800ReadContinousMeasurement(SDP800ObjectType *sdp){SDP800ErrorType error=SDP800_ERROR_NONE;uint8_t rDatas[9];int16_t diffPressureTicks;int16_t temperatureTicks;uint16_t scaleFactorDiffPressure;sdp->Receive(sdp,rDatas,9);if((rDatas[0]==0xFF)&&(rDatas[1]==0xFF)&&(rDatas[2]==0xAC)&&(rDatas[3]==0xFF)&&(rDatas[4]==0xFF)&&(rDatas[5]==0xAC)&&(rDatas[6]==0xFF)&&(rDatas[7]==0xFF)&&(rDatas[8]==0xAC)){sdp->status=0;return SDP800_ERROR_ACK;}error|=CheckCRC8ForSDP800(&rDatas[0],2,rDatas[2]);error|=CheckCRC8ForSDP800(&rDatas[3],2,rDatas[5]);error|=CheckCRC8ForSDP800(&rDatas[6],2,rDatas[8]);if(error==SDP800_ERROR_NONE){diffPressureTicks=rDatas[0]*256+rDatas[1];temperatureTicks=rDatas[3]*256+rDatas[4];scaleFactorDiffPressure=rDatas[6]*256+rDatas[7];sdp->temperature=(float)temperatureTicks/200.0;sdp->dpFactor=(float)scaleFactorDiffPressure;sdp->dpressure=(float)diffPressureTicks/sdp->dpFactor;}return error;}/*启动连续测量*/SDP800ErrorType Sdp800StartContinousMeasurement(SDP800ObjectType *sdp,Sdp800TempCompType tempComp,Sdp800AveragingType averaging){SDP800ErrorType error=SDP800_ERROR_NONE;SDP800Command commands[2][2]={{COMMAND_START_MEASUREMENT_MF_AVERAGE,COMMAND_START_MEASUREMENT_MF_NONE},{COMMAND_START_MEASUREMENT_DP_AVERAGE,COMMAND_START_MEASUREMENT_DP_NONE}};switch (commands[tempComp][averaging]){case COMMAND_START_MEASUREMENT_MF_AVERAGE:{sdp->status=1;break;}case COMMAND_START_MEASUREMENT_MF_NONE:{sdp->status=2;break;}case COMMAND_START_MEASUREMENT_DP_AVERAGE:{sdp->status=3;break;}case COMMAND_START_MEASUREMENT_DP_NONE:{sdp->status=4;break;}default:{sdp->status=0;error=SDP800_ERROR_IVALID_PARAMETER;break;}}if(SDP800_ERROR_NONE==error){Sdp800WriteCommand(sdp,commands[tempComp][averaging]);sdp->Delayms(20);}if(SDP800_ERROR_NONE!=error){sdp->status=0;}return error;}/*停止连续测量*/SDP800ErrorType Sdp800StopContinousMeasurement(SDP800ObjectType *sdp){Sdp800WriteCommand(sdp,COMMAND_STOP_CONTINOUS_MEASUREMENT);return SDP800_ERROR_NONE;}
2.2.2、设备控制
有一些命令是用来实现对SDP800差压传感器的控制的,如设备的复位、休眠及各种配置。这里我们主要用到SDP800差压传感器的软件复位及休眠。
/*软件复位*/SDP800ErrorType Sdp800SoftReset(SDP800ObjectType *sdp){Sdp800WriteCommand(sdp,COMMAND_ENTER_SLEEP_MODE);// 等待 20 mssdp->Delayms(20); return SDP800_ERROR_NONE;}/*进入休眠模式*/SDP800ErrorType SDP800EnterSleepMode(SDP800ObjectType *sdp){Sdp800WriteCommand(sdp,COMMAND_ENTER_SLEEP_MODE);return SDP800_ERROR_NONE;}
3、驱动的使用
我们设计并实现了SDP800差压传感器的驱动程序。接下来,我们使用设计的驱动实现基于SDP800差压传感器传感器的流量检测。
3.1、声明并初始化对象
在前面我们已经定义了SDP800差压传感器对象类型。在这里,我们先声明一个SDP800差压传感器对象变量。
SDP800ObjectType sdp;
有了这个对象变量,我们还需要调用初始化函数对其进行实例化。初始化函数具有读个参数:
SDP800ObjectType *sdp,//SDP800对象uint8_t i2cAddress, //设备地址SDP800Receive recieve,//接收函数指针SDP800Transmit transmit, //发送函数指针SDP800Delayms delayms//毫秒演示函数
第一个参数是需要初始化的对象变量。第二个参数则是SDP800差压传感器的设备地址。而后面的三个参数则是函数指针,我们需要实现这三个函数,它们的原型定义如下:
//延时操作指针typedef void (*SDP800Delayms)(volatile uint32_t nTime);//接收数据操作指针typedef void (*SDP800Receive)(struct SDP800Object *sdp,uint8_t *rData,uint16_t rSize);//发送数据操作指针typedef void (*SDP800Transmit)(struct SDP800Object *sdp,uint8_t *tData,uint16_t tSize);
结合这三个函数的原型要求以及我们所使用平台的具体特点,我们实现这几个函数如下:
/*向DSP800下发指令,指令格式均为1个字节*/static void WriteToSDP(SDP800ObjectType *sdp,uint8_t *wData,uint16_t wSize){HAL_I2C_Master_Transmit(&hi2c2,sdp->devAddress,wData,wSize,1000);}/*从DSP800读取多个字节数据的值*/static void ReadFromSDP(SDP800ObjectType *sdp,uint8_t *rData,uint16_t rSize){HAL_I2C_Master_Receive(&hi2c2,sdp->devAddress,rData, rSize, 1000);}
我们实现了这些函数后,我们就可以将这些变量及函数作为初始化函数的参数来对SDP800差压传感器对象变量进行实例化,具体实现:
/*SDP800对象初始化配置*/SDP800ErrorType error=Sdp800Initialization(&sdp,//SDP800对象0x4A, //设备地址ReadFromSDP, //接收函数指针WriteToSDP, //发送函数指针HAL_Delay//毫秒延时函数);Sdp800StartContinousMeasurement(&sdp,SDP800_TEMPCOMP_MASS_FLOW,SDP800_AVERAGING_TILL_READ);
3.2、基于对象进行操作
初始化完成后,我们则可以使用该对象获取测量数据并计算流量值。具体实现如下:
if(sdp.status==0){Sdp800SoftReset(&sdp);Sdp800StartContinousMeasurement(&sdp,SDP800_TEMPCOMP_MASS_FLOW,SDP800_AVERAGING_TILL_READ);}else{Sdp800ReadContinousMeasurement(&sdp);}aPara.phyPara.temperature=sdp.temperature;dpFilter.newValue=sdp.dpressure;aPara.phyPara.dpressure=BandSmoothingFilter(&dpFilter);aPara.phyPara.dpressure=aPara.phyPara.dpressure<0.0?0.0:aPara.phyPara.dpressure;flow=sqrtf(aPara.phyPara.dpressure);flowFilter.newValue=Power3Polyfit(flow,aPara.phyPara.factorA,aPara.phyPara.factorB,aPara.phyPara.factorC,aPara.phyPara.factorD);aPara.phyPara.flowValue=BandSmoothingFilter(&flowFilter);aPara.phyPara.flowValue=aPara.phyPara.flowValue>0.25?aPara.phyPara.flowValue:0.0;
在这一事例中,我们读取了SDP800差压传感器的值,并根据差压数据计算了流量数据。并实现了修正、滤波以及小信号切除等处理。
4、应用总结
在这一篇中,我们设计并实现了SDP800差压传感器的驱动程序,而且使用我们设计的驱动实现了一个具体示例。事实上,这个示例是从我们的实际项目中提炼出来的,实际的使用效果良好。