PM2.5检测 -- PMS7003 开发程序

昨天看了一天的 PMS7003 传输协议。然后网上搜索了一下,仅有的几篇文章讲代码怎么写的。

参看:DIY 空气质量检测表

参看:Dust Sensor - PMS 5003/6003/7003

参看:MartyMacGyver/PMS7003-on-Particle

参看:PMS7003 PM2.5测试仪,带SHT20温湿度 STM32源码

参看:(SKU:SEN0177)PM2.5激光粉尘传感器


但是你有没有发现,这几篇文章要么是在 QT 上实现的、要么用的是 Arduino 开发板、要么是单片机。

对于我现在要在 DM368 板子上实现,好像没什么卵用...  现在真的有点懵逼。

还算好的是,前辈有留下一点,之前的PM2.5采集器的源码。仅供参看...   没办法,只能一点一点来做。

一、首先看一下 PMS7003 传输协议 

默认波特率:9600bps 校验位:无 停止位:1位 
协议总长度:32字节

字节数据
起始符10x42
起始符20x4D
帧长度高八位帧长度=2x13+2(数据+校验位)
帧长度低八位
数据1高八位数据1表示PM1.0浓度(CF=1,标准颗粒物)单位μg/m3
数据1低八位
数据2高八位数据2表示PM2.5浓度(CF=1,标准颗粒物)单位μg/m3
数据2低八位
数据3高八位数据3表示PM10浓度(CF=1,标准颗粒物)单位μg/m3
数据3低八位
数据4高八位数据4表示PM1.0浓度(大气环境下)单位μg/m3
数据4低八位
数据5高八位数据5表示PM2.5浓度(大气环境下)单位μg/m3
数据5低八位
数据6高八位数据6表示PM10浓度 (大气环境下)单位μg/m3
数据6低八位
数据7高八位数据7表示0.1升空气中直径在0.3um以上颗粒物个数
数据7低八位
数据8高八位数据8表示0.1升空气中直径在0.5um以上颗粒物个数
数据8低八位
数据9高八位数据9表示0.1升空气中直径在1.0um以上颗粒物个数
数据9低八位
数据10高八位数据10表示0.1升空气中直径在2.5um以上颗粒物个数
数据10低八位
数据11高八位数据11表示0.1升空气中直径在5.0um以上颗粒物个数
数据11低八位
数据12高八位数据12表示0.1升空气中直径在10um以上颗粒物个数
数据12低八位
数据13高八位版本号
数据13低八位错误代码
数据和校验高八位校验码=起始符1+起始符2+……..+数据13低八位
数据和校验低八位
注:标准颗粒物质量浓度值是指用工业金属颗粒物作为等效颗粒进行密度换算得到的质量浓度值,适用于工业生产车间等环境。大气环境颗粒物质量浓度值以空 气中主要污染物为等效颗粒进行密度换算,适用于普通室内外大气环境。


输出结果
主要输出为单位体积内各浓度颗粒物质量以及个数,其中颗粒物个数的单位体积为 0.1 升,质量浓度单位为:微克/立方米。
输出分为主动输出和被动输出两种状态。
传感器上电后默认状态为主动输出,即传感器主动向主机发送串行数据,时间间隔为 200~800ms,空气中颗粒物浓度越高,时间间隔越短。主动输出又分为两种模式:平稳模式和快速模式。在空气中颗粒物浓度变化较小时,传感器输出为平稳模式,即每三次输出同样的一组数值,实际数据更新周期约为 2s。当空气中颗粒物浓度变化较大时,传感器输出自动切换为快速模式,每次输出都是新的数值,实际数据更新周期为 200~800ms。

二、PMS7003 传输协议代码讲解  (重点)

(1)源码

//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Demo: interfacing a Plantower PMS7003 air quality sensor to a Particle IoT microcontroller
/*
    Copyright (c) 2016 Martin F. Falatic
    
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    
        http://www.apache.org/licenses/LICENSE-2.0
    
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


int incomingByte = 0; // for incoming serial data

const int MAX_FRAME_LEN = 64;
char frameBuf[MAX_FRAME_LEN];
int detectOff = 0;
int frameLen = MAX_FRAME_LEN;
bool inFrame = false;
char printbuf[256];

const bool DEBUG = false;


uint16_t calcChecksum = 0;

struct PMS7003_framestruct {
    uint8_t  frameHeader[2];
    uint16_t frameLen = MAX_FRAME_LEN;
    uint16_t concPM1_0_CF1;
    uint16_t concPM2_5_CF1;
    uint16_t concPM10_0_CF1;
    uint16_t concPM1_0_amb;
    uint16_t concPM2_5_amb;
    uint16_t concPM10_0_amb;
    uint16_t rawGt0_3um;
    uint16_t rawGt0_5um;
    uint16_t rawGt1_0um;
    uint16_t rawGt2_5um;
    uint16_t rawGt5_0um;
    uint16_t rawGt10_0um;
    uint8_t  version;
    uint8_t  errorCode;
    uint16_t checksum;
} thisFrame;


void setup() {
    Serial.begin(57600);
    delay(1000);
    Serial.println("-- Initializing...");
}


bool pms7003_read() {
    //  Particle.publish("PMS7003", printbuf, 60, PRIVATE);
    // send data only when you receive data:
    Serial.println("-- Reading PMS7003");
    Serial1.begin(9600);
    bool packetReceived = false;
    while (!packetReceived) {
        if (Serial1.available() > 32) {
            int drain = Serial1.available();
            if (DEBUG) {
                Serial.print("-- Draining buffer: ");
                Serial.println(Serial1.available(), DEC);
            }
            for (int i = drain; i > 0; i--) {
                Serial1.read();
            }
        }
        if (Serial1.available() > 0) {
            if (DEBUG) {
                Serial.print("-- Available: ");
                Serial.println(Serial1.available(), DEC);
            }
            incomingByte = Serial1.read();
            if (DEBUG) {
                Serial.print("-- READ: ");
                Serial.println(incomingByte, HEX);
            }
            if (!inFrame) {
                if (incomingByte == 0x42 && detectOff == 0) {
                    frameBuf[detectOff] = incomingByte;
                    thisFrame.frameHeader[0] = incomingByte;
                    calcChecksum = incomingByte; // Checksum init!
                    detectOff++;
                }
                else if (incomingByte == 0x4D && detectOff == 1) {
                    frameBuf[detectOff] = incomingByte;
                    thisFrame.frameHeader[1] = incomingByte;
                    calcChecksum += incomingByte;
                    inFrame = true;
                    detectOff++;
                }
                else {
                    Serial.print("-- Frame syncing... ");
                    Serial.print(incomingByte, HEX);
                    if (DEBUG) {
                    }
                    Serial.println();
                }
            }
            else {
                frameBuf[detectOff] = incomingByte;
                calcChecksum += incomingByte;
                detectOff++;
                uint16_t val = frameBuf[detectOff-1]+(frameBuf[detectOff-2]<<8);
                switch (detectOff) {
                    case 4:
                        thisFrame.frameLen = val;
                        frameLen = val + detectOff;
                        break;
                    case 6:
                        thisFrame.concPM1_0_CF1 = val;
                        break;
                    case 8:
                        thisFrame.concPM2_5_CF1 = val;
                        break;
                    case 10:
                        thisFrame.concPM10_0_CF1 = val;
                        break;
                    case 12:
                        thisFrame.concPM1_0_amb = val;
                        break;
                    case 14:
                        thisFrame.concPM2_5_amb = val;
                        break;
                    case 16:
                        thisFrame.concPM10_0_amb = val;
                        break;
                    case 18:
                        thisFrame.rawGt0_3um = val;
                        break;
                    case 20:
                        thisFrame.rawGt0_5um = val;
                        break;
                    case 22:
                        thisFrame.rawGt1_0um = val;
                        break;
                    case 24:
                        thisFrame.rawGt2_5um = val;
                        break;
                    case 26:
                        thisFrame.rawGt5_0um = val;
                        break;
                    case 28:
                        thisFrame.rawGt10_0um = val;
                        break;
                    case 29:
                        val = frameBuf[detectOff-1];
                        thisFrame.version = val;
                        break;
                    case 30:
                        val = frameBuf[detectOff-1];
                        thisFrame.errorCode = val;
                        break;
                    case 32:
                        thisFrame.checksum = val;
                        calcChecksum -= ((val>>8)+(val&0xFF));
                        break;
                    default:
                        break;
                }
    
                if (detectOff >= frameLen) {
                    sprintf(printbuf, "PMS7003 ");
                    sprintf(printbuf, "%s[%02x %02x] (%04x) ", printbuf,
                        thisFrame.frameHeader[0], thisFrame.frameHeader[1], thisFrame.frameLen);
                    sprintf(printbuf, "%sCF1=[%04x %04x %04x] ", printbuf,
                        thisFrame.concPM1_0_CF1, thisFrame.concPM2_5_CF1, thisFrame.concPM10_0_CF1);
                    sprintf(printbuf, "%samb=[%04x %04x %04x] ", printbuf,
                        thisFrame.concPM1_0_amb, thisFrame.concPM2_5_amb, thisFrame.concPM10_0_amb);
                    sprintf(printbuf, "%sraw=[%04x %04x %04x %04x %04x %04x] ", printbuf,
                        thisFrame.rawGt0_3um, thisFrame.rawGt0_5um, thisFrame.rawGt1_0um,
                        thisFrame.rawGt2_5um, thisFrame.rawGt5_0um, thisFrame.rawGt10_0um);
                    sprintf(printbuf, "%sver=%02x err=%02x ", printbuf,
                        thisFrame.version, thisFrame.errorCode);
                    sprintf(printbuf, "%scsum=%04x %s xsum=%04x", printbuf,
                        thisFrame.checksum, (calcChecksum == thisFrame.checksum ? "==" : "!="), calcChecksum);
                    Serial.println(printbuf);
                    Particle.publish("Data1", printbuf, 60, PRIVATE);
                    packetReceived = true;
                    detectOff = 0;
                    inFrame = false;
                }
            }
        }
    }
    Serial1.end();
    return (calcChecksum == thisFrame.checksum);
}

void loop () {
    if (!pms7003_read()) {
        delay(4000);
    }
}

(2)讲解

这篇文章对于 PMS7003 传输协议写的不错哦,我们来讲一下。

《1》定义数组和结构体

这里面主要看一下,完全参照的传输协议定义的串口数据的结构体

《2》串口配置

不用看这个,因为我们用的是 485 通信,里面自有它那一套串口配置的方式,下面会讲到的。

《3》传输协议代码如何写

重点来了哦

然后这里可以复习一下大小端、位操作和分支跳转语句的基础知识。


三、快速学习驱动程序基本框架

需要写传感器的驱动程序,这里快速的浏览一遍。
(之前以为单独写驱动呢,现在看来直接用 485 串口编程的即可,这部分只做参考学习。
驱动程序的基本框架
#include <linux/init.h>
#include <linux/module.h>
static int helloword_init (void)  //入口函数  成功返回0  static限定作用域 
{
printf (“hello,world!\n”);
return 0;
}
static void helloworld_exit (void) //出口函数
{
printf (“goodbye,world!\n”);

}
module_init (helloworld_init);
module_exit (helloworld_exit);
MODULE_LICENSE (“GPL”);  //GPL 开源
Makefile 编写
obj-m += helloworld.o #将helloworld.c 
#编译生成最终的二进制可执行文件helloworld.ko
all:
	make -C /opt/kernel SUBDIRS=$(PWD) modules
#到内核/opt/kernel源码中进行make编译,然后告诉内核,在你的源码以外还有一个目录/opt/drivers/day01/1.0,在这个目录下有一个.c文件需要你进行编译,要把它编译生成.ko文件
clean:
	make -C /opt/kernel SUBDIRS=$(PWD) clean
内核程序操作命令
insmod  加载
rmmod   卸载
lsmod   查看
内核提供的GPIO操作库函数
  gpio_request:申请资源
  gpio_free:释放资源
  gpio_direction_output:配置工作模式为输出,输出某个状态
  gpio_direction_input:配置工作模式为输入
  gpio_get_value:获取状态
  gpio_set_value:设置状态
设备号的操作
申请
    alloc_chrdev_region(设备号,0, 次设备号的个数,设备名称);
释放
    unregister_chrdev_region(设备号,次设备号的个数);
实现一个字符设备驱动的编程步骤
定义初始化字符设备对象
    cdev_init
注册字符设备对象到内核
    cdev_add   
卸载字符设备对象
    cdev_del
linux内核描述字符设备使用的数据结构
struct cdev {
   		dev_t dev; //保存申请的设备号
   		unsigned int count;//设备的个数
   		const struct file_operations *ops;
//给字符设备驱动赋予操作硬件的方法,并且将这些方法最终提供给用户
   };	
linux内核字符设备硬件操作接口
struct file_operations {
.owner  = THIS_MODULE,
.open = led_open, //打开设备
.release = led_close, //关闭设备
.read = led_read, //读设备
.write = led_write, //写设备
.unlocked_ioctl = led_ioctl, //向设备发送命令并且能够进行读写操作
......
};
设备文件创建函数
struct class *cls; //创建设备类指针

  //定义初始化设备类(长树枝,树枝名叫tarena)
  //结果是在/sys/class目录下生成一个tarena目录,存放创建设备文件所需的原材料
cls = class_create(THIS_MODULE, "tarena");

  //创建设备文件(长苹果)
  //结果是在/dev/生成设备文件
device_create(cls, NULL, 申请的设备号,NULL,s设备文件名);

  //删除设备文件(采摘苹果)
device_destroy(cls, 申请的设备号);

  //删除设备类(砍树枝)
class_destroy(cls);
混杂设备驱动的编程步骤
1.定义初始化混杂设备对象
	  struct file_operations led_fops = {
	  	...
	  };
	  
	  struct miscdevice led_misc = {
	  		.minor = MISC_DYNAMIC_MINOR,
	  		.name = "myled",
	  		.fops = &led_fops
	  };    

  2.向内核注册混杂设备对象,一旦注册完毕,内核就有一个真实的混杂设备驱动
    misc_register(&led_misc);

  3.从内核卸载混杂设备对象
    misc_deregister(&led_misc);

四、UART 接口 (重点)

上文中有提到,用的 485 通信,然后看一下它所接的 GPIO 口是哪个。

然后之前写过一篇关于 485 串口编程的文章,可供参考。

参看:UNIX再学习 -- RS485 串口编程

这个很有必要哦,里面的串口编程,很实用。然后只需要将其改为只接收即可。

然后具体的更改代码,这里我就不再重复。


编译执行:
编译:
arm-none-linux-gnueabi-gcc com.c -o com -pthread

tftp 到文件系统中:
tftp -g -r com 192.168.2.xx

设置权限:
chmod 777 com

手动加载设备:
mknod /dev/pio c 203 0

执行
./com
这里看一下,输出的结果。


然后通过 校验码=起始符1+起始符2+……..+数据13 来验证第一行的数据。校验码是 02 81 说明是正确的。 

五、开发程序

最后是传输协议开发程序,只看 read 读取部分即可。其他的没啥可讲的。

#include <fcntl.h>     //文件控制定义
#include <stdio.h>     //标准输入输出定义
#include <stdlib.h>     //标准函数库定义
#include <unistd.h>    //Unix标准函数定义 
#include <errno.h>     //错误好定义
#include <termios.h>   //POSIX终端控制定义
#include <sys/ioctl.h>   //ioctl函数定义
#include <string.h>     //字符操作
#include <sys/types.h>  
#include <sys/stat.h> 
#include <pthread.h>
#include <sys/time.h>
int fd_gpio;

struct termios newtio, oldtio;
typedef struct {
        int  pin_idx;
        int  pin_dir;
        int  pin_sta;
} davinci_gio_arg;

typedef enum {
        AT91PIO_DIR_OUT = 0,
        AT91PIO_DIR_INP 
} davinci_gio_dir;
//驱动判断输入输出模式

davinci_gio_arg arg;

#define DEV_PIO_LED "/dev/pio"
// 需要手动添加设备号 mknod /dev/pio c 203 0
#define PIO_NUM 47
// 47pin 为控制输入输出方向引脚
#define DEV_UART	"/dev/ttyS1"
// /dev/ttyS1 为串口设备

#define IOCTL_PIO_SETDIR	1		//set gpio direct
#define IOCTL_PIO_GETDIR	2		//get gpio direct
#define IOCTL_PIO_SETSTA	3		//set gpio status
#define IOCTL_PIO_GETSTA	4		//get gpio status

//保存信息
int log_init( const char *strFileName )
{
	int fdLog = -1;

	if( -1 == (fdLog = open( strFileName,  O_CREAT|O_TRUNC ) ) )
	{
	}
	close( fdLog );
}

int log_out( const char *strFileName, const char * szLog )
{
	int fdLog = -1;

	if( -1 == (	fdLog = open( strFileName,  O_CREAT|O_WRONLY|O_APPEND ) ) )
	{
		printf( "LOG (%s) open error!\n", strFileName );
		return -1;
	}

	write( fdLog, szLog, strlen( szLog ) );

	close( fdLog );

	return 0;
}

//配置串口
/* 参数说明:fd 设备文件描述符,nspeed 波特率,nbits 数据位数(7位或8位),
            parity 奇偶校验位('n'或'N'为无校验位,'o'或'O'为偶校验,'e'或'E'奇校验),
		    nstop 停止位(1位或2位)
  成功返回1,失败返回-1。 
*/
int set_com_opt( int fd, int nspeed, int nbits, char parity, int nstop )
{
	char szTmp[128];
//打印配置信息
	sprintf( szTmp, "set_com_opt - speed:%d,bits:%d,parity:%c,stop:%d\n", 
				nspeed, nbits, parity, nstop );
    
	log_out( "./485.log", szTmp );
	//保存并测试现在有串口参数设置,在这里如果串口号等出错,会有相关的出错信息 
	if( tcgetattr( fd, &oldtio ) != 0 )
	{
	
	sprintf( szTmp, "SetupSerial 1" );

	log_out( "./485.log", szTmp );

		perror( "SetupSerial 1" );
		return -1;
	}

    //修改输出模式,原始数据输出
	bzero( &newtio, sizeof( newtio ));
	newtio.c_cflag &=~(OPOST);

	//屏蔽其他标志位
	newtio.c_cflag |= (CLOCAL | CREAD );
	newtio.c_cflag &= ~CSIZE;
	
    //设置数据位
	switch( nbits )
	{
	case 7:
		newtio.c_cflag |= CS7;
		break;
	case 8:
		newtio.c_cflag |= CS8;
		break;
	default:
		perror("Unsupported date bit!\n");
		return -1;
	}
	
	//设置校验位
	switch( parity )
	{
	case 'n':
	case 'N':  //无奇偶校验位
		newtio.c_cflag &= ~PARENB;
		newtio.c_iflag &= ~INPCK;
		break;
	case 'o':
	case 'O':	 //设置为奇校验
		newtio.c_cflag |= ( PARODD | PARENB );
		newtio.c_iflag |= ( INPCK | ISTRIP );
		break;
	case 'e':
	case 'E':  //设置为偶校验
		newtio.c_iflag |= ( INPCK |ISTRIP );
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;
		break;
	default:
		perror("unsupported parity\n");
		return -1;
	}

	//设置停止位
	switch( nstop )	
	{
	case 1:	
		newtio.c_cflag &= ~CSTOPB;
		break;
	case 2:
		newtio.c_cflag |= CSTOPB;
		break;
	default :
		perror("Unsupported stop bit\n");
		return -1;
	}

	//设置波特率
	switch( nspeed )
	{
	case 2400:
		cfsetispeed( &newtio, B2400 );
		cfsetospeed( &newtio, B2400 );
		break;
	case 4800:
		cfsetispeed( &newtio, B4800 );
		cfsetospeed( &newtio, B4800 );
		break;	
	case 9600:
		cfsetispeed( &newtio, B9600 );
		cfsetospeed( &newtio, B9600 );
		break;	
	case 115200:
		cfsetispeed( &newtio, B115200 );
		cfsetospeed( &newtio, B115200 );
		break;
	case 460800:
		cfsetispeed( &newtio, B460800 );
		cfsetospeed( &newtio, B460800 );
		break;
	default:	
		cfsetispeed( &newtio, B9600 );
		cfsetospeed( &newtio, B9600 );
		break;
	}

	//设置等待时间和最小接收字符
	newtio.c_cc[VTIME] = 0;  
	newtio.c_cc[VMIN] = 0;   
//VTIME=0,VMIN=0,不管能否读取到数据,read都会立即返回。

//输入模式
	newtio.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
//设置数据流控制
	newtio.c_iflag &= ~(IXON|IXOFF|IXANY); //使用软件流控制
//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
	tcflush( fd, TCIFLUSH ); 
//激活配置 (将修改后的termios数据设置到串口中)
	if( tcsetattr( fd, TCSANOW, &newtio ) != 0 )
	{
	sprintf( szTmp, "serial set error!\n" );

	log_out( "./485.log", szTmp );
		perror( "serial set error!" );
		return -1;
	}

	log_out( "./485.log", "serial set ok!\n" );
	return 1;
}

//打开串口并返回串口设备文件描述
int open_com_dev( char *dev_name )
{
	int fd;
		char szTmp[128];

	log_init( "./485.log" );
	if(( fd = open( dev_name, O_RDWR|O_NOCTTY|O_NDELAY)) == -1 )
	{

		perror("open\n");
		//printf("Can't open Serial %s Port!\n", dev_name );
		sprintf( szTmp, "Can't open Serial %s Port!\n", dev_name );
		
		log_out( "./485.log", szTmp );

		return -1;
	}

	sprintf( szTmp, "open %s ok!\n", dev_name );
	log_out( "./485.log", szTmp );

	if(fcntl(fd,F_SETFL,0)<0)
	{
		printf("fcntl failed!\n");
	}
	//printf("Open %s ok\n",dev_name );
	return fd;
}

//单片机数据收发
void* task(void* p)
{
	char buf[64];
	char frameBuf[64];
	int detectOff = 0;
	int res = 0, nread = 0;

	int m_pm1_factory;
	int m_pm25_factory;
	int m_pm10_factory;
	int m_pm1_outdoor;
	int m_pm25_outdoor;
	int m_pm10_outdoor;

	int m_count03;
	int m_count05;
	int m_count1;
	int m_count25;
	int m_count5;
	int m_count10;

	unsigned short m_length;
	unsigned short m_version;
	unsigned short m_errorno;

	while (1) {

	arg.pin_sta = 0;   //设为低电平 接收态  
	ioctl(fd_gpio, IOCTL_PIO_SETSTA, &arg);
	int fd_r=open_com_dev( DEV_UART );
	if( fd_r < 0 )
	{
		printf( "open UART device error! %s\n", DEV_UART );
	}
	else
		set_com_opt(fd_r, 9600,8,'n',1);

	//执行select
	fd_set rd;  
	FD_ZERO(&rd);  
	FD_SET(fd_r, &rd);  
	if ((res = select (fd_r+1,&rd, NULL, NULL, NULL) )< 0)
	{
		perror ("read err");
		exit (-1);
	}

	memset (buf, 0, sizeof (buf));
	if (FD_ISSET (fd_r, &rd))
	{
		//接收数据 8 8 2 
		int res1 = 0;
		int val = 0;
		int calcChecksum = 0;
		int checksum = 0;
		int i = 0;
		while ((nread = read(fd_r, buf, 1)) > 0)
		{
			//printf ("%02X ",*buf);
			//frameBuf[detectOff] = buf[0];
			memcpy (frameBuf+detectOff, buf, 1);
			//calcChecksum += *buf;
			detectOff++;

			if (frameBuf[0] == 0x42 && frameBuf[1] == 0x4d)
			{
				
			//	m_length = frameBuf[3]+(frameBuf[2]<<8);
				m_pm1_factory = frameBuf[5]+(frameBuf[4]<<8);
				m_pm25_factory = frameBuf[7]+(frameBuf[6]<<8);
				m_pm10_factory = frameBuf[9]+(frameBuf[8]<<8);
				m_pm1_outdoor = frameBuf[11]+(frameBuf[10]<<8);
				m_pm25_outdoor = frameBuf[13]+(frameBuf[12]<<8);
				m_pm10_outdoor = frameBuf[15]+(frameBuf[14]<<8);
			//	m_count03 = frameBuf[17]+(frameBuf[16]<<8);
			//	m_count05 = frameBuf[19]+(frameBuf[18]<<8);
			//	m_count1 = frameBuf[21]+(frameBuf[20]<<8);
			//	m_count25 = frameBuf[23]+(frameBuf[22]<<8);
			//	m_count5 = frameBuf[25]+(frameBuf[24]<<8);
			//	m_count10 = frameBuf[27]+(frameBuf[26]<<8);
			//	m_version = frameBuf[28];
			//	m_errorno = frameBuf[29];
			//	checksum = frameBuf[31]+(frameBuf[30]<<8);
			//	calcChecksum -= ((checksum>>8)+(checksum&0xFF));
			}

			//退出循环, 这里有点疑问
			if (detectOff == 32)
			{
				printf ("\n");
				printf ("pm1_factory = %d ug/m3\npm25_factory = %d ug/m3\npm10_factory = %d ug/m3\n", m_pm1_factory, m_pm25_factory, m_pm10_factory);
				printf ("pm1_outdoor = %d ug/m3\npm25_outdoor = %d ug/m3\npm10_outdoor = %d ug/m3\n",m_pm1_outdoor,m_pm25_outdoor,m_pm10_outdoor);
			//	printf ("m_count03 = %02X\nm_count05 = %02X\nm_count1 = %02X\nm_count25 = %02X\nm_count5 = %02X\nm_count10 = %02X\n",
					//	m_count03, m_count05, m_count1, m_count25, m_count5, m_count10);
			//	printf ("m_length = %d\n", m_length);
			//	printf ("m_version = %s\n", m_version);
			//	printf ("m_errorno = %s\n", m_errorno);
			//	printf("checksum = %02X %s calcChecksum = %02X",checksum, (calcChecksum == checksum ? "==" : "!="), calcChecksum);

			//	for (i = 0;i<32;i++)
			//	{
			//		printf ("%02X ", frameBuf[i]);
			//	}
			//	printf ("\n");
				memset (frameBuf, 0, sizeof (frameBuf));
				detectOff = 0;
				break;
			}
		}
	}
	close (fd_r); 
	usleep (200000);
	}
}

int main (void)
{

	int error = 0, error1 = 0;
	arg.pin_idx = PIO_NUM;        
	arg.pin_dir = AT91PIO_DIR_OUT;
	//打开/dev/pio设备
	fd_gpio = open(DEV_PIO_LED, O_RDWR); 
	if(fd_gpio < 0)
	{
		perror("fd_gpio open err");
		exit (-1);
	}

	pthread_t tid;
	//创建线程
	if ((error = pthread_create (&tid, NULL, task, NULL)) < 0)
	{
		perror ("pthread_cread error");
		return -1;		
	}
	
	//等待线程结束
	pthread_join (tid, NULL);
	//关闭设备	
	close (fd_gpio);
	return 0;
}

测试结果:




  • 12
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聚优致成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值