外设驱动库开发笔记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差压传感器的驱动程序,而且使用我们设计的驱动实现了一个具体示例。事实上,这个示例是从我们的实际项目中提炼出来的,实际的使用效果良好。

欢迎关注:

posted @ 2023-02-05 21:47  Moonan  阅读(170)  评论(0编辑  收藏  举报