arduino开发SGP30模块测二氧化碳浓度-Arduino中文社区 - Powered by Discuz! Archiver

programer11 发表于 2019-4-16 17:28

arduino开发SGP30模块测二氧化碳浓度


[*]SGP30模块是目前位置接触的比较麻烦的模块,首先芯片手册就比较烦人,没有中文翻译,(文末附加了芯片手册),而且SGP30使用的是I2C通信,所以这就需要了解I2C的通信原理,(文末附加了I2C手册),再者,SGP30模块有较多的模式,需要通过I2C将执行的指令传给模块,才可以选择对应的模式,得到自己想要的结果。每个传输的指令都是由两个字节构成,每个数据的返回也是由两个字节的数据构成,而且还要加一个字节的CRC校验码,(CRC校验来验证数据是否传输正确,芯片生成一个校验码传输给主机,所以必须写一个CRC校验算法,来对两个CRC码进程对比,以此来证明数据传输的正确性)。
[*]I2C的通信过程,不用自己写,arduino库集成了<Wire.h>来实现,这也省了一部分工作,但是这样也就感觉没有了开发这个模块的乐趣,不过可以看看<Wire>的源码来膜拜一下大神们维护的库。附上源码路径方便查看:/arduino/hardware/arduino/avr/libraries/Wire



[*]最重要的一点是SGP30的电气特性,工作电压是1.62v-1.92v(如果没记错的),这个工作电压跟板子上的电压完全不匹配啊,不过问题不大,可以自己链接5v-1.8v的稳压芯片,(但是,焊好稳压芯片,接好电路,烧好代码之后,却发现模块不工作,索性把稳压芯片去掉,将模块接到3.3v的电压上,竟然神奇般的工作正常了)。


[*]完成之后,发现一个道理,贵的模块果然工作稳定。




#include "Adafruit_SGP30.h"

//#ifdef I2C_DEBUG

Adafruit_SGP30::Adafruit_SGP30() {
}

boolean Adafruit_SGP30::sgp_begin(TwoWire *theWire) {
    _i2caddr = SGP30_I2CADDR_DEFAULT;
    if (theWire == NULL) {
      _i2c = &Wire;
    } else {
      _i2c = theWire;
    }

    // 初始化wire库,并且加入到i2c网络,主机只能调用一次
    _i2c->begin();


    // 0x3682 指令:获取序列号
    uint8_t command;
    command = 0x36;
    command = 0x82;
    if (! readWordFromCommand(command, 2, 10, serialnumber, 3))
      return false;

    //0x202f 指令: 获取版本号
    uint16_t featureset;
    command = 0x20;
    command = 0x2F;
    if (! readWordFromCommand(command, 2, 10, &featureset, 1))
      return false;
    //Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
    // 0x0020 版本号
    if (featureset != SGP30_FEATURESET)
      return false;
    if (! IAQinit())
      return false;

    return true;
}


//0x2003 指令: 初始化空气质量
//
boolean Adafruit_SGP30::IAQinit(void) {
    uint8_t command;
    command = 0x20;
    command = 0x03;
    return readWordFromCommand(command, 2, 10);
}

// 返回 6 字节,包括CRC 2字节
boolean Adafruit_SGP30::IAQmeasure(void) {
    uint8_t command;
    command = 0x20;
    command = 0x08;
    uint16_t reply;
    if (! readWordFromCommand(command, 2, 12, reply, 2))
      return false;
    TVOC = reply;
    eCO2 = reply;
    return true;
}

// 0x2015 指令: 获取基准线
// 返回 6 字节,包括CRC 2字节
boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base) {
    uint8_t command;
    command = 0x20;
    command = 0x15;
    uint16_t reply;
    if (! readWordFromCommand(command, 2, 10, reply, 2))
      return false;
    *eco2_base = reply;
    *tvoc_base = reply;
    return true;
}

// 0x201e 指令:设置基准线
// 参数 6 字节,包括CRC两字节
//
boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
    uint8_t command;
    command = 0x20;
    command = 0x1e;
    command = tvoc_base >> 8;
    command = tvoc_base & 0xFF;
    command = generateCRC(command+2, 2);
    command = eco2_base >> 8;
    command = eco2_base & 0xFF;
    command = generateCRC(command+5, 2);

    return readWordFromCommand(command, 8, 10);
}

//0x2061 指令: 设置温度补偿
//参数 3 字节 包括 CRC
boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
    if (absolute_humidity > 256000) {
      return false;
    }

    uint16_t ah_scaled = (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
    uint8_t command;
    command = 0x20;
    command = 0x61;
    command = ah_scaled >> 8;
    command = ah_scaled & 0xFF;
    command = generateCRC(command+2, 2);

    return readWordFromCommand(command, 5, 10);
}


boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delayms, uint16_t *readdata, uint8_t readlen)
{
    uint8_t data;

    // 开始传输数据,发送一个i2c开始字符
    _i2c->beginTransmission(_i2caddr);

#ifdef I2C_DEBUG
    Serial.print("\t\t-> ");
#endif

    for (uint8_t i=0; i<commandLength; i++) {
      // 向从机发送数据
      _i2c->write(command);
#ifdef I2C_DEBUG
      Serial.print("0x"); Serial.print(command, HEX); Serial.print(", ");
#endif
    }
#ifdef I2C_DEBUG
    Serial.println();
#endif
    // 结束发送
    _i2c->endTransmission();

    delay(delayms);

    if (readlen == 0)
      return true;

    //replylen : 从从机请求到的数据的字节数
    //sgp30 芯片手册得知: 每个数据由两个字节组成,并且附加一个字节的CRC校验
    uint8_t replylen = readlen * (SGP30_WORD_LEN +1);

    // requestFrom : 主设备请求从设备一个字节,这个字节可以被主设备用 read() 和 available() 接受
    // 参数      : 从设备地址 请求得到的数量
    // 返回值      : 从从机接受到的字节数目

    if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
      return false;
    uint8_t replybuffer;
#ifdef I2C_DEBUG
    Serial.print("\t\t<- ");
#endif
    for (uint8_t i=0; i<replylen; i++) {
      //read() : 用于接受 requestFrom 请求的数据
      replybuffer = _i2c->read();
#ifdef I2C_DEBUG
      Serial.print("0x"); Serial.print(replybuffer, HEX); Serial.print(", ");
#endif
    }

#ifdef I2C_DEBUG
    Serial.println();
#endif

    for (uint8_t i=0; i<readlen; i++) {
      uint8_t crc = generateCRC(replybuffer+i*3, 2);
#ifdef I2C_DEBUG
      Serial.print("\t\tCRC calced: 0x"); Serial.print(crc, HEX);
      Serial.print(" vs. 0x"); Serial.println(replybuffer, HEX);
#endif
      if (crc != replybuffer)
            return false;
      // success! store it
      readdata = replybuffer;
      readdata <<= 8;
      readdata |= replybuffer;
#ifdef I2C_DEBUG
      Serial.print("\t\tRead: 0x"); Serial.println(readdata, HEX);
#endif
    }
    return true;
}

//generateCRC 进行数据的校验
//data: 数据
//datalen : 数据字长
uint8_t Adafruit_SGP30::generateCRC(uint8_t *data, uint8_t datalen) {
    // calculates 8-Bit checksum with given polynomial
    uint8_t crc = SGP30_CRC8_INIT;

    for (uint8_t i=0; i<datalen; i++) {
      crc ^= data;
      for (uint8_t b=0; b<8; b++) {
            if (crc & 0x80)
                crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
            else
                crc <<= 1;
      }
    }
    return crc;
}




bh4wjy 发表于 2019-5-6 09:13

学习了,正在为sgp30这个芯片资料少而发愁,非常感谢楼主指导,马上准备买个芯片试试看!

bh4wjy 发表于 2019-5-6 09:34

请教您sgp30连接arduino的问题,我准备买的是sgp30升级版即供电电压1.8-5v的,连接到5varduino,都是用5v供电,请问两个板子之间数据线连接还需要电平转换吗?

yidongzhao 发表于 2022-8-21 16:44

本帖最后由 yidongzhao 于 2022-8-21 16:45 编辑

你好,我在使用这个模块的时候,相同环境下两次数据差别很大,呼气后的值变化也很大。请问会是什么方面的原因呢?大神能帮忙分析一下吗?第一次是C:\Users\Lenovo\Desktop\数据\2\shot.png;第二次是C:\Users\Lenovo\Desktop\数据\2\shot2.png

XlinliY.Zhang 发表于 2022-8-22 13:58

yidongzhao 发表于 2022-8-21 16:44
你好,我在使用这个模块的时候,相同环境下两次数据差别很大,呼气后的值变化也很大。请问会是什么方面的原 ...

传感器是有响应时间的
页: [1]
查看完整版本: arduino开发SGP30模块测二氧化碳浓度