外设驱动库开发笔记51:SDP800差压传感器驱动
检测流量数据的方法有很多种,这一次我们就是使用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 ms
sdp->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差压传感器的驱动程序,而且使用我们设计的驱动实现了一个具体示例。事实上,这个示例是从我们的实际项目中提炼出来的,实际的使用效果良好。
欢迎关注:
如果阅读这篇文章让您略有所得,还请点击下方的【好文要顶】按钮。
当然,如果您想及时了解我的博客更新,不妨点击下方的【关注我】按钮。
如果您希望更方便且及时的阅读相关文章,也可以扫描上方二维码关注我的微信公众号【木南创智】