查看: 1145|回复: 2

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

[复制链接]
  • TA的每日心情
    奋斗
    2019-5-24 16:20
  • 签到天数: 8 天

    [LV.3]偶尔看看II

    发表于 2019-4-16 17:28 | 显示全部楼层 |阅读模式
    • 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的电压上,竟然神奇般的工作正常了)。

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




    kittenblock中小学创客名师推荐的图形化编程软件

    #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[2];
        command[0] = 0x36;
        command[1] = 0x82;
        if (! readWordFromCommand(command, 2, 10, serialnumber, 3))
            return false;
    
        //  0x202f 指令: 获取版本号
        uint16_t featureset;
        command[0] = 0x20;
        command[1] = 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[2];
        command[0] = 0x20;
        command[1] = 0x03;
        return readWordFromCommand(command, 2, 10);
    }
    
    // 返回 6 字节,包括CRC 2字节
    boolean Adafruit_SGP30::IAQmeasure(void) {
        uint8_t command[2];
        command[0] = 0x20;
        command[1] = 0x08;
        uint16_t reply[2];
        if (! readWordFromCommand(command, 2, 12, reply, 2))
            return false;
        TVOC = reply[1];
        eCO2 = reply[0];
        return true;
    }
    
    // 0x2015 指令: 获取基准线
    // 返回 6 字节,包括CRC 2字节
    boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base) {
        uint8_t command[2];
        command[0] = 0x20;
        command[1] = 0x15;
        uint16_t reply[2];
        if (! readWordFromCommand(command, 2, 10, reply, 2))
            return false;
        *eco2_base = reply[0];
        *tvoc_base = reply[1];
        return true;
    }
    
    // 0x201e 指令:  设置基准线
    // 参数 6 字节,包括CRC两字节
    //
    boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
        uint8_t command[8];
        command[0] = 0x20;
        command[1] = 0x1e;
        command[2] = tvoc_base >> 8;
        command[3] = tvoc_base & 0xFF;
        command[4] = generateCRC(command+2, 2);
        command[5] = eco2_base >> 8;
        command[6] = eco2_base & 0xFF;
        command[7] = 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[5];
        command[0] = 0x20;
        command[1] = 0x61;
        command[2] = ah_scaled >> 8;
        command[3] = ah_scaled & 0xFF;
        command[4] = 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[replylen];
    #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[i * 3 + 2], HEX);
    #endif
            if (crc != replybuffer[i * 3 + 2])
                return false;
            // success! store it
            readdata = replybuffer[i*3];
            readdata <<= 8;
            readdata |= replybuffer[i*3 + 1];
    #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;
    }
    




    Sensors_SGP30_Datasheet_EN.zip

    988.44 KB, 下载次数: 43

    SGP30芯片手册

    STM32-I2C.pdf

    553 KB, 下载次数: 26

    I2C手册

    SGP30.tar

    1.02 KB, 下载次数: 40

    SGP30代码(不包含main函数的调用)

    该用户从未签到

    发表于 2019-5-6 09:13 | 显示全部楼层
    学习了,正在为sgp30这个芯片资料少而发愁,非常感谢楼主指导,马上准备买个芯片试试看!

    该用户从未签到

    发表于 2019-5-6 09:34 | 显示全部楼层
    请教您sgp30连接arduino的问题,我准备买的是sgp30升级版即供电电压1.8-5v的,连接到5varduino,都是用5v供电,请问两个板子之间数据线连接还需要电平转换吗?
    您需要登录后才可以回帖 登录 | 立即注册  

    本版积分规则

    热门推荐

    全国机器人等级考试配件购买
    全国机器人等级考试配件购
    本人想自己教小孩机器人等级考试,需要购买机器人等级考试的配件,可是淘宝上的太
    【Arduino】108种传感器系列实验(24)---水位传感器模块
    【Arduino】108种传感器系
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    【Arduino】108种传感器系列实验(96)---433M无线发射接收模块
    【Arduino】108种传感器系
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    【Arduino】108种传感器系列实验(93)---CSR8350蓝牙音乐模块
    【Arduino】108种传感器系
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    【Arduino】108种传感器系列实验(89)--- 4*4位 RGB LED 全彩模块
    【Arduino】108种传感器系
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
    快速回复 返回顶部 返回列表