Sensirion SCD4x 二氧化碳传感器,STM32 I2C读取CO2浓度,温度,湿度值

目录:

  • 一、描述
  • 二、部分函数代码
  • 1.开始周期测量。
  • 2.停止周期测量。
  • 3.读取传感器数据。
  • 4.读取传感器序列号。
  • 5.传感器自检。
  • 6.传感器恢复出厂设置。
  • 7.传感器软件复位。
  • 8 查询传感器数据是否就绪
  • 9.主机发送指令
  • 9.1 发送指令,不带参数和CRC
  • 9.2 发送指令,带参数和CRC
  • 9.3 发送指令,并读取一个16位数据
  • 10 十六进制转ASCII
  • 11 CRC计算
  • 三、传感器指令定义
  • 一、描述

    SCD4xSensirion公司的微型二氧化碳传感器。
    采用内置的SHT4x 湿度和温度传感器实现片上信号补偿。

    Sensirion官网: https://sensirion.com/
    SCD4x传感器:https://sensirion.com/products/catalog/?filter_series=7d9d4a77-bd13-4545-8e68-f8e03c184ddd

    基本参数:

    尺寸 10.1 x 10.1 x 6.5 mm3
    输出范围 0 ppm – 40’000 ppm
    宽电压 2.4 – 5.5 V
    高精度 ±(40 ppm + 5 %)
    低功耗运行时 < 0.4 mA avg. @ 5 V, 1 meas. / 5 minutes
    通信协议 I2C

    传感器集成SHT4x温湿度传感器可同时输出温度湿度数据。

    接口定义:

    I2C地址0x62

    I2C指令协议
    见下图,SCD4x具有四种不同的I2C命令类型, “read I2C sequences”, “write I2C sequences”,“send I2C
    command
    ” and “send command and fetch result” sequences.

    二、部分函数代码

    文中所参考的数据文档:“SCD40_SCD41_dataSheet.pdf”可去官网下载

    1.开始周期测量。

    1.具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.5.1”
    2.信号更新间隔为5秒

    bool SCD4x_StartPeriodicMeasurement(void)
    {
    	bool success = 0;
      if (periodic_Measurements_AreRunning)
      {
        #if SCD4x_ENABLE_DEBUGLOG
         printf("SCD4x_StartPeriodicMeasurement: periodic measurements are already running");
        #endif // if SCD4x_ENABLE_DEBUGLOG
        return (true);  //Maybe this should be false?
      }
    
      success = SCD4x_sendCommand(SCD4x_COMMAND_START_PERIODIC_MEASUREMENT);
      if (success)
        periodic_Measurements_AreRunning = true;
      return (success);
    }
    

    2.停止周期测量。

    1.具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.5.3”
    2.传感器在发出 STOP_PERIONAL_MEASURATION 命令后等待 500毫秒 后才会对其他命令作出响应。

    bool SCD4x_stopPeriodicMeasurement(uint16_t delayTime)
    {
      bool success = SCD4x_sendCommand(SCD4x_COMMAND_STOP_PERIODIC_MEASUREMENT);
    	
    	if(success)
    	{
    		periodic_Measurements_AreRunning = false;
    		if (delayTime > 0)
        		delay1ms(delayTime);
    		return true;
    	}
      if (delayTime > 0)
        delay1ms(delayTime);
    	
      return (false);
    }
    

    3.读取传感器数据。

    1.具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.5.2”
    2.每个信号更新间隔只能读出一次测量数据,因为读数时缓冲区被清空.
    3.如果缓冲区中没有可用数据,传感器将返回 NACK

    bool SCD4x_readMeasurement(uint16_t *co2, float *temperature, float *humidity, uint8_t *Error)
    {
    	uint8_t error = 0;
    	uint8_t buffer[10] = {0};
    
      if (SCD4x_getDataReadyStatus() == false) // get_data_ready_status. see "SCD40_SCD41_Datasheet.pdf" 3.8.2
        return (false);
    
    	SCD4x_SensorData.tempCO2.unsigned16 = 0;
    	SCD4x_SensorData.tempHumidity.unsigned16 = 0;
    	SCD4x_SensorData.tempTemperature.unsigned16 = 0;
    
    	IIC_Start();
        IIC_Send_Byte( (SCD4x_ADDRESS<<1)&(~0x01)); //I2C Address + write
    	IIC_Wait_Ack();
        IIC_Send_Byte(SCD4x_COMMAND_READ_MEASUREMENT >> 8);   //MSB
    	IIC_Wait_Ack();
        IIC_Send_Byte(SCD4x_COMMAND_READ_MEASUREMENT & 0xFF); //LSB
    	error = IIC_Wait_Ack();
    	
      if ( error )
        return (false); //Sensor did not ACK
    
    	error = 0;
        delay1ms(1); //Datasheet specifies this,  command execution time
    
    	IIC_Start();
        IIC_Send_Byte( (SCD4x_ADDRESS<<1)|(0x01)); //I2C Address + read
    	IIC_Wait_Ack();
    	buffer[0] = IIC_Read_Byte(1); //read data and ack
    	buffer[1] = IIC_Read_Byte(1);
    	buffer[2] = IIC_Read_Byte(1);
    	buffer[3] = IIC_Read_Byte(1);
    	buffer[4] = IIC_Read_Byte(1);
    	buffer[5] = IIC_Read_Byte(1);
    	buffer[6] = IIC_Read_Byte(1);
    	buffer[7] = IIC_Read_Byte(1);
    	buffer[8] = IIC_Read_Byte(0); //read data and no ack
    	IIC_Stop();
    
    	uint8_t foundCrc = 0;
        uint8_t bytesToCrc[2]={0};
        for (uint8_t x = 0; x < 9; x++)
        {
          switch (x)
          {
          case 0:
          case 1:
            SCD4x_SensorData.tempCO2.bytes[x == 0 ? 1 : 0] = buffer[x]; // Store the two CO2 bytes in little-endian format
            bytesToCrc[x] = buffer[x]; // Calculate the CRC on the two CO2 bytes in the order they arrive
            break;
          case 3:
          case 4:
            SCD4x_SensorData.tempTemperature.bytes[x == 3 ? 1 : 0] = buffer[x]; // Store the two T bytes in little-endian format
            bytesToCrc[x % 3] = buffer[x]; // Calculate the CRC on the two T bytes in the order they arrive
            break;
          case 6:
          case 7:
            SCD4x_SensorData.tempHumidity.bytes[x == 6 ? 1 : 0] = buffer[x]; // Store the two RH bytes in little-endian format
            bytesToCrc[x % 3] = buffer[x]; // Calculate the CRC on the two RH bytes in the order they arrive
            break;
          default: // x == 2, 5, 8
            //Validate CRC
            foundCrc = SCD4x_computeCRC8(bytesToCrc, 2); // Calculate what the CRC should be for these two bytes
            if (foundCrc != buffer[x]) // CRC check error
            {
              #if SCD4x_ENABLE_DEBUGLOG		
              #endif // if SCD4x_ENABLE_DEBUGLOG
    		  if(x==2)
    			{error |= 0B1;} // CO2
    		  if(x==5)
    			{error |= 0B10;} // Temperature
    		  if(x==8)
    			{error |= 0B100;} // Humidity
            }
            break;
          }
        }
    
      if (error)
      {
    	*Error = error;
        #if SCD4x_ENABLE_DEBUGLOG
        	printf("SCD4x_readMeasurement: encountered error reading SCD4x data.");
        #endif // if SCD4x_ENABLE_DEBUGLOG
        return (false);
      }
      // 返回解析后的数据
      *co2 = (uint16_t)SCD4x_SensorData.tempCO2.unsigned16; //ppm
      *temperature = -45 + (((float)SCD4x_SensorData.tempTemperature.unsigned16) * 175 / 65536);
      *humidity = ((float)SCD4x_SensorData.tempHumidity.unsigned16) * 100 / 65536;
    	
      return (true); //Success! 
    }
    

    4.读取传感器序列号。

    1.具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.9.2”
    2.从SCD4x获取9个字节。并将48位序列号转换为ASCII字符。.
    3.读出序列号可用于识别芯片和验证传感器。

    bool SCD4x_getSerialNumber(char *serialNumber)
    {
    	uint8_t error = 0;
    	uint8_t Crc = 0;
    	
      if (periodic_Measurements_AreRunning)
      {
        #if SCD4x_ENABLE_DEBUGLOG
          printf("SCD4x_getSerialNumber: periodic measurements are running. Aborting");
        #endif // if SCD4x_ENABLE_DEBUGLOG
        return (false);
      }
    
      IIC_Start();
      IIC_Send_Byte( (SCD4x_ADDRESS<<1)&(~0x01)); //I2C Address + write
      IIC_Wait_Ack();
      IIC_Send_Byte(SCD4x_COMMAND_GET_SERIAL_NUMBER >> 8);   //MSB
      IIC_Wait_Ack();
      IIC_Send_Byte(SCD4x_COMMAND_GET_SERIAL_NUMBER & 0xFF); //LSB
      error = IIC_Wait_Ack();
    	
      if ( error )
        return (false); //Sensor did not ACK
    
      delay1ms(1); //Datasheet specifies this
    
      IIC_Start();
      IIC_Send_Byte( (SCD4x_ADDRESS<<1)|(0x01)); //I2C Address + read
      error = IIC_Wait_Ack();
    
      if (error==0)
      {
        uint8_t bytesToCrc[2];
        int digit = 0;
        for (uint8_t  x = 0; x < 9; x++)
        {
          uint8_t incoming = IIC_Read_Byte(1); //read data and ack
    	  if(x==8)
    		{
    			incoming = IIC_Read_Byte(0);  //read data and no ack
    			IIC_Stop();
    		}
          switch (x)
          {
          case 0: // The serial number arrives as: two bytes, CRC, two bytes, CRC, two bytes, CRC
          case 1:
          case 3:
          case 4:
          case 6:
          case 7:
            serialNumber[digit++] = SCD4x_convertHexToASCII(incoming >> 4); // Convert each nibble to ASCII
            serialNumber[digit++] = SCD4x_convertHexToASCII(incoming & 0x0F);
            bytesToCrc[x % 3] = incoming;
            break;
          default: // x == 2, 5, 8
            Crc = SCD4x_computeCRC8(bytesToCrc, 2); // Calculate what the CRC should be for these two bytes
            if (Crc != incoming) // Does this match the CRC byte from the sensor?
            {
              #if SCD4x_ENABLE_DEBUGLOG
    
              #endif // if SCD4x_ENABLE_DEBUGLOG
              error = true;
            }
            break;
          }
          serialNumber[digit] = 0; // NULL-terminate the string
        }
      }
    
      if (error)
      {
        #if SCD4x_ENABLE_DEBUGLOG
          printf("SCD4x_readSerialNumber: encountered error reading SCD4x data.");
        #endif // if SCD4x_ENABLE_DEBUGLOG
        return (false);
      }
    
      return (true); //Success!
    }
    

    5.传感器自检。

    1.具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.9.3”
    2.执行传感器自检指令,该指令需要 10秒 的执行时间。

    bool SCD4x_performSelfTest(void)
    {
      if (periodic_Measurements_AreRunning)
      {
        #if SCD4x_ENABLE_DEBUGLOG
    			
        #endif // if SCD4x_ENABLE_DEBUGLOG
        return (false);
      }
    
      uint16_t response;
    
      #if SCD4x_ENABLE_DEBUGLOG
        printf("SCD4x_performSelfTest: delaying for 10 seconds...");
      #endif // if SCD4x_ENABLE_DEBUGLOG
    
      bool success = SCD4x_readRegister(SCD4x_COMMAND_PERFORM_SELF_TEST, &response, 10000);
    
      return (success && (response == 0x0000)); // word[0] = 0 → no malfunction detected
    }
    

    6.传感器恢复出厂设置。

    1.具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.9.4”
    2.PERFORM_FACTORY_RESET 命令用于重置存储在 EEPROM 中的所有配置设置。
    3. 执行时间 1200ms

    bool SCD4x_performFactoryReset(uint16_t delayTime)
    {
      if (periodic_Measurements_AreRunning)
      {
        #if SCD4x_ENABLE_DEBUGLOG
    
        #endif // if SCD4x_ENABLE_DEBUGLOG
        return (false);
      }
    
      bool success = SCD4x_sendCommand(SCD4x_COMMAND_PERFORM_FACTORY_RESET);
      if (delayTime > 0)
        delay1ms(delayTime);
      return (success);
    }
    

    7.传感器软件复位。

    1.具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.9.5”
    2. Reinit 命令通过从 EEPROM 重新加载用户设置来重新初始化传感器。
    3. 执行此命令前,传感器必须 停止周期测量
    4. 指令执行时间 20ms

    bool SCD4x_reInit(uint16_t delayTime)
    {
      if (periodic_Measurements_AreRunning)
      {
        return (false); // 需停止周期测量
      }
    
      bool success = SCD4x_sendCommand(SCD4x_COMMAND_REINIT);
      if (delayTime > 0)
        delay1ms(delayTime);
      return (success);
    }
    

    8 查询传感器数据是否就绪

    bool SCD4x_getDataReadyStatus(void)
    {
      uint16_t response;
      bool success = SCD4x_readRegister(SCD4x_COMMAND_GET_DATA_READY_STATUS, &response, 1);
    
      if (success == false)
        return (false);
    
      // 0 至 11 bit 是 0 --> data not ready
      //else → data ready for read-out
      if ((response & 0x07ff) == 0x0000)
        return (false);
      return (true);
    }
    

    9.主机发送指令

    下图为传感器IIC通信时的不同指令类型
    具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.3”
    IIC指令类型

    9.1 发送指令,不带参数和CRC

    “send I2C command sequence”

    bool SCD4x_sendCommand(uint16_t command)
    {
      uint8_t error = 0;
    	
      IIC_Start();
      IIC_Send_Byte((SCD4x_ADDRESS<<1)&(~0x01));
      IIC_Wait_Ack();
      IIC_Send_Byte(command >> 8);   //MSB
      IIC_Wait_Ack();
      IIC_Send_Byte(command & 0xFF); //LSB
      error = IIC_Wait_Ack();
      IIC_Stop();
    	
      if ( error )
        return (false); //Sensor did not ACK
    
      return (true);
    }
    

    9.2 发送指令,带参数和CRC

    “write I2C sequence”

    bool SCD4x_sendCommand_CmdData_CRC(uint16_t command, uint16_t arguments)
    {
      uint8_t data[2];
      uint8_t error = 0;
      data[0] = arguments >> 8;
      data[1] = arguments & 0xFF;
      uint8_t crc = SCD4x_computeCRC8(data, 2); //Calc CRC on the arguments only, not the command
    
      IIC_Start();
      IIC_Send_Byte( (SCD4x_ADDRESS<<1)&(~0x01) );
      IIC_Wait_Ack();
      IIC_Send_Byte(command >> 8);     //MSB
      IIC_Wait_Ack();
      IIC_Send_Byte(command & 0xFF);   //LSB
      IIC_Wait_Ack();
      IIC_Send_Byte(arguments >> 8);   //MSB
      IIC_Wait_Ack();
      IIC_Send_Byte(arguments & 0xFF); //LSB
      IIC_Wait_Ack();
      IIC_Send_Byte(crc); //CRC
      error = IIC_Wait_Ack();
      IIC_Stop();
    	
      if ( error )
        return (false); //Sensor did not ACK
    
      return (true);
    }
    

    9.3 发送指令,并读取一个16位数据

    “read I2C sequence”
    如果 IIC_Wait_Ack(); 返回0,且 CRC 校验正确,此函数返回true

    bool SCD4x_readRegister(uint16_t registerAddress, uint16_t *response, uint16_t delayTime)
    {
    	uint8_t error = 0;
    	uint8_t crc = 0;
    	uint8_t data[2] = {0};
    	
      IIC_Start();
      IIC_Send_Byte( (SCD4x_ADDRESS<<1)&(~0x01) ); //I2C Address + write
      IIC_Wait_Ack();
      IIC_Send_Byte(registerAddress >> 8);   //MSB
      IIC_Wait_Ack();
      IIC_Send_Byte(registerAddress & 0xFF); //LSB
      error = IIC_Wait_Ack();
      IIC_Stop();
    	
      if ( error )
        return (false); //Sensor did not ACK
    
      delay1ms(delayTime);
    	
      IIC_Start();
      IIC_Send_Byte( (SCD4x_ADDRESS<<1)|(0x01) ); //I2C Address + read
      IIC_Wait_Ack();
      data[0] = IIC_Read_Byte(1);
      data[1] = IIC_Read_Byte(1);
      crc = IIC_Read_Byte(0);
      IIC_Stop();  
    	
      *response = (uint16_t)data[0] << 8 | data[1];
      uint8_t expectedCRC = SCD4x_computeCRC8(data, 2);
        if (crc == expectedCRC) // Return true if CRC check is OK
          return (true);
      
      return (false);
    }
    

    10 十六进制转ASCII

    char SCD4x_convertHexToASCII(uint8_t digit)
    {
      if (digit <= 9)
        return ( (char)(digit + 0x30) );
      else
        return ( (char)(digit + 0x41 - 10) ); // Use upper case for A-F
    }
    

    11 CRC计算

    具体请阅读 “SCD40_SCD41_dataSheet.pdf”, 第“3.11”
    参考: http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html
    验证: http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
    x8+x5+x4+1 = 0x31

    uint8_t SCD4x_computeCRC8(uint8_t data[], uint8_t len)
    {
      uint8_t crc = 0xFF; //Init with 0xFF
    
      for (uint8_t x = 0; x < len; x++)
      {
        crc ^= data[x]; // XOR-in the next input byte
    
        for (uint8_t i = 0; i < 8; i++)
        {
          if ((crc & 0x80) != 0)
            crc = (uint8_t)((crc << 1) ^ 0x31);
          else
            crc <<= 1;
        }
      }
    	
      return crc; //No output reflection
    }
    

    三、传感器指令定义

    //The default I2C address for the SCD4x is 0x62.
    #define SCD4x_ADDRESS 0x62
    
    //Available commands
    
    //Basic Commands
    #define SCD4x_COMMAND_START_PERIODIC_MEASUREMENT              0x21b1
    #define SCD4x_COMMAND_READ_MEASUREMENT                        0xec05 // execution time: 1ms
    #define SCD4x_COMMAND_STOP_PERIODIC_MEASUREMENT               0x3f86 // execution time: 500ms
    
    //On-chip output signal compensation
    #define SCD4x_COMMAND_SET_TEMPERATURE_OFFSET                  0x241d // execution time: 1ms
    #define SCD4x_COMMAND_GET_TEMPERATURE_OFFSET                  0x2318 // execution time: 1ms
    #define SCD4x_COMMAND_SET_SENSOR_ALTITUDE                     0x2427 // execution time: 1ms
    #define SCD4x_COMMAND_GET_SENSOR_ALTITUDE                     0x2322 // execution time: 1ms
    #define SCD4x_COMMAND_SET_AMBIENT_PRESSURE                    0xe000 // execution time: 1ms
    
    //Field calibration
    #define SCD4x_COMMAND_PERFORM_FORCED_CALIBRATION              0x362f // execution time: 400ms
    #define SCD4x_COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED  0x2416 // execution time: 1ms
    #define SCD4x_COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED  0x2313 // execution time: 1ms
    
    //Low power
    #define SCD4x_COMMAND_START_LOW_POWER_PERIODIC_MEASUREMENT    0x21ac
    #define SCD4x_COMMAND_GET_DATA_READY_STATUS                   0xe4b8 // execution time: 1ms
    
    //Advanced features
    #define SCD4x_COMMAND_PERSIST_SETTINGS                        0x3615 // execution time: 800ms
    #define SCD4x_COMMAND_GET_SERIAL_NUMBER                       0x3682 // execution time: 1ms
    #define SCD4x_COMMAND_PERFORM_SELF_TEST                       0x3639 // execution time: 10000ms
    #define SCD4x_COMMAND_PERFORM_FACTORY_RESET                   0x3632 // execution time: 1200ms
    #define SCD4x_COMMAND_REINIT                                  0x3646 // execution time: 20ms
    
    //Low power single shot - SCD41 only
    #define SCD4x_COMMAND_MEASURE_SINGLE_SHOT                     0x219d // execution time: 5000ms
    #define SCD4x_COMMAND_MEASURE_SINGLE_SHOT_RHT_ONLY            0x2196 // execution time: 50ms
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » Sensirion SCD4x 二氧化碳传感器,STM32 I2C读取CO2浓度,温度,湿度值

    发表评论