空气质量是现代日常生活中人们所关注的事情,也是生存环境好坏的一种体现。其中粉尘数量监测更是空气质量检测中最常见的对象,在我们的检测设备中也经常会有这种需求。检测手段也是多种多样,这一篇中,我们就来讨论使用PM3003S 激光粉尘传感器实现粉尘测量。
1、功能概述
PM3003S 激光粉尘传感器模块采用光学散射原理,可精确检测并计算单位体积内空气中不同粒径的悬浮颗粒物的个数,内置四方独有的尘源智能识别,配以流量稳定的气泵,可实现颗粒物PM1.0、PM2.5、PM10、TSP质量浓度的实时输出。
PM3003S 激光粉尘传感器模块接口采用TTL电平。通讯报文包括起始符、长度、命令符、数据以及校验和。具体的数据格式如下。
PM3003S 激光粉尘传感器模块有4中命令:读取粉尘仪测量结果,功能码0x0B;开启或关闭粉尘仪测量,功能码0x0C;读取或读取粉尘仪校准系数,功能码0x07;读取粉尘仪编码,功能码0x1F。具体如下表;
PM3003S 激光粉尘传感器模块具有6个通道,可以输出PM10,PM2.5,PM1.0以及TSP等。
2、驱动设计与实现
PM3003S 激光粉尘传感器模块驱动的设计我们依然采用基于对象的方式来实现。在实现之前,我们需要考虑一下PM3003S 激光粉尘传感器模块对象的具体内容。
2.1、对象定义
首先我们需要考虑对象类型的定义。关于PM3003S 激光粉尘传感器模块对象我们从对象的属性及方法两个方面来考虑。
对于PM3003S 激光粉尘传感器模块的属性,其6个通道的粉尘测量值,状态值、报警值、PM1.0、PM2.5、PM10以及TSP等表示了PM3003S 激光粉尘传感器模块的工作状态,多以我们将其设定为属性。同样的矫正系数表示PM3003S 激光粉尘传感器模块的配置状态、传感器编码标识了PM3003S 激光粉尘传感器模块的唯一性,我们也将其设计为属性。
再来看PM3003S 激光粉尘传感器模块的方法。在这里我们并不实现PM3003S 激光粉尘传感器模块对象的全部方法,我们只考虑哪些对具体平台依赖性较大的方法。对于PM3003S 激光粉尘传感器模块只有读写操作是与平台相关的,所以我们将其作为对象的方法。于是我们可以设计PM3003S 激光粉尘传感器模块对象类型如下:
/*定义PM3003S对象类型*/
typedef struct PM3003S {
uint8_t status;
uint8_t alarm;
uint16_t sensorCode[5];
uint32_t pm1d0;
uint32_t pm2d5;
uint32_t pm10;
uint32_t tsp;
uint32_t up0d3um;
uint32_t up0d5um;
uint32_t up1d0um;
uint32_t up2d5um;
uint32_t up5d0um;
uint32_t up10um;
float dustFactor;
void (*SendCommand)(uint8_t *cmd,uint16_t rSize);
}PM3003ObjectType;
对于一个对象来说我们不能直接使用其对对象变量,二是需要先对其进行初始化配置才可以使用。所以我们还需要实现PM3003S 激光粉尘传感器模块对象的初始化函数。
/*对象初始化配置*/
PM3003SErrorType Pm3003sInitialization(PM3003ObjectType *pm,Pm3003sSendCommandType send)
{
if((pm==NULL)||(send==NULL))
{
return PM3003S_PARAMETER_ERROR;
}
pm->SendCommand=send;
pm->status=0;
pm->alarm=0;
for(int i=0;i<5;i++)
{
pm->sensorCode[i]=0;
}
pm->pm1d0=0;
pm->pm2d5=0;
pm->pm10=0;
pm->tsp=0;
pm->up0d3um=0;
pm->up0d5um=0;
pm->up1d0um=0;
pm->up2d5um=0;
pm->up5d0um=0;
pm->up10um=0;
pm->dustFactor=0.0;
return PM3003S_NO_ERROR;
}
2.2、对象操作
我们已经设计了PM3003S激光粉尘传感器模块对象的类型,并且实现了对象变量的初始化函数。接下来我们将实现PM3003S激光粉尘传感器模块的各种操作。
2.2.1、获取测量结果
我们使用PM3003S激光粉尘传感器模块的根本目的就是获取测量数据。根据前面的描述,获取测量数据的功能码为0x0B,所以根据协议格式可实现如下:
/*读取测量结果*/
void Pm3003sReadMeasureResult(PM3003ObjectType *pm)
{
uint8_t cmd[]={0x11,0x02,0x0B,0x07,0xDB};
pm->SendCommand(cmd,5);
}
2.2.2、启停粉尘仪
我们也可以使用命令启动或停止PM3003S激光粉尘传感器模块,所需功能码为0x0C,根据命令格式我们可以实现停止或启动PM3003S 激光粉尘传感器模块的函数如下:
/*停止粉尘测量*/
void Pm3003sTurnoffDustMeasurement(PM3003ObjectType *pm)
{
uint8_t cmd[]={0x11,0x03,0x0C,0x01,0x1E,0xC1};
pm->SendCommand(cmd,6);
}
/*开启粉尘测量*/
void Pm3003sTurnonDustMeasurement(PM3003ObjectType *pm)
{
uint8_t cmd[]={0x11,0x03,0x0C,0x02,0x1E,0xC0};
pm->SendCommand(cmd,6);
}
2.2.3、操作粉尘系数
我们也可以使用命令读取或者设置PM3003S激光粉尘传感器模块的粉尘校准系数,所需功能码为0x0C,根据命令格式我们可以实现读取或者设置PM3003S 激光粉尘传感器模块粉尘校准系数的函数如下:
/*读取粉尘校准系数*/
void Pm3003sGetDustCalibrationFactor(PM3003ObjectType *pm)
{
uint8_t cmd[]={0x11,0x01,0x07,0xE7};
pm->SendCommand(cmd,4);
}
/*设置粉尘校准系数*/
void Pm3003sSetDustCalibrationFactor(PM3003ObjectType *pm,uint8_t data)
{
uint8_t cmd[]={0x11,0x02,0x07,0x00,0x00};
cmd[3]=data;
cmd[4]=CalculateChecksum(cmd,4);
pm->SendCommand(cmd,5);
}
2.2.4、读取编码
每一台PM3003S激光粉尘传感器模块都有一个唯一的编码,我们可以使用相应的功能吗读取。根据协议格式,我们可以编写相应的读取PM3003S激光粉尘传感器模块唯一编码的函数如下:
/*查询传感器编码*/
void Pm3003sQueryingSensorCode(PM3003ObjectType *pm)
{
uint8_t cmd[]={0x11,0x01,0x1F,0xCF};
pm->SendCommand(cmd,4);
}
3、驱动的使用
我们基于对象设计了PM3003S激光粉尘传感器模块的驱动,接下来我们考虑如何使用这一驱动。
3.1、声明并初始化对象
与以往一样,首先使用前门定义的PM3003S激光粉尘传感器模块对象类型生命一个对象变量,具体如下:
PM3003ObjectType pm3003s;
对于声明的对象变量我们需要使用初始化函数先对其进行初始化才可使用。初始化函数有2个变量,第一个变量是我们需要初始化的对象变量,第二个变量则是发送函数的指针,其函数原型定义如下:
/*定义发送函数指针类型*/
typedef void (*Pm3003sSendCommandType)(uint8_t *cmd,uint16_t rSize);
我们这个示例实在STM32平台实现的,使用的是STM32的HAL库,所以我们的实现如下:
/*发送数据*/
static void SendBytes(uint8_t *instruction,uint16_t length)
{
/*关闭中断*/
__HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE);
pmiRxLength=0;
HAL_UART_Transmit(&huart2,instruction,length,100);
/*启用串口接收中端*/
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
}
有了上述准备我们就可以对对象变量进行初始化了。使用初始化函数,将对应的参数传给初始化函数即可,具体实现如下:
if(Pm3003sInitialization(&pm3003s,SendBytes)!=PM3003S_NO_ERROR)
{
Error_Handler();
}
3.2、基于对象进行操作
对象初始化完成后,我们就可以基于对象对PM3003S激光粉尘传感器模块进行相应的操作了。在我们这个实例中,我们设计了一个60秒的周期获取测量数据的操作。每一个周期的前10秒以干净空气对通道进行清洁,接下来的50秒钟,我们每秒钟获取一侧数据。具体实现如下:
if(pmiRxLength>=5)
{
if(Pm3003sParsingMessage(&pm3003s,pmiRxBuffer,pmiRxLength)!=PM3003S_INCOMPLETE_ERROR)
{
pmiRxLength=0;
}
else
{
errorCount++;
if(errorCount>150)
{
pmiRxLength=0;
errorCount=0;
}
}
}
if(pmiRxLength==0)
{
/*操作过程判断*/
Pm3003sOperation();
}
/*操作过程判断*/
static void Pm3003sOperation(void)
{
if(aPara.phyPara.sSecond<10) //清洗
{
if(pm3003s.status != 0x01) //停止粉尘测量
{
Pm3003sTurnoffDustMeasurement(&pm3003s);
}
else if(((uint8_t)aPara.phyPara.dustFactor)!=pm3003s.dustFactor)//设置粉尘系数
{
Pm3003sSetDustCalibrationFactor(&pm3003s,(uint8_t)aPara.phyPara.dustFactor);
readRequst=1;
}
else if(readRequst==1)//读取粉尘系数
{
Pm3003sGetDustCalibrationFactor(&pm3003s);
readRequst=0;
}
else if(Pm3003sSelfCheck())
{
Pm3003sQueryingSensorCode(&pm3003s);
}
}
else if(aPara.phyPara.sSecond>=10) //调整
{
if(pm3003s.status != 0x02) //启动粉尘测量
{
Pm3003sTurnonDustMeasurement(&pm3003s);
}
else
{
Pm3003sReadMeasureResult(&pm3003s);
}
}
}
4、应用总结
在本篇中,我们设计并实现了PM3003S激光粉尘传感器模块的驱动程序,同样史记与对象的方式实现的。同时我们设计了一个测试用例,对驱动程序进行了测试,结果如预期。事实上,我们这个测试用例是从我们的一个实际项目中接选出来的,而在这个实际的产品项目中我们也是的这套驱动程序。