STM32HAL库驱动SCD40/SCD30驱动代码

SCD40是瑞士SENSIRION公司推出的一款微型CO2传感器,它基于光声传感原理,并结合了温湿度传感器SHTW2实现片上信号补偿。以下是SCD40的详细介绍:

一、产品特点

  1. 高精度:SCD40在特定范围内(如400-2000ppm)具有高精度的CO2浓度测量能力,精度可达±(40ppm+5%)。
  2. 小尺寸:其尺寸仅为10 x 10 x 7 mm³(或根据其他资料为10.1 x 10.1 x 6.5 mm³),便于集成到各种设备中。
  3. 多功能:除了测量CO2浓度外,还能同时测量温度和湿度,提供全面的环境参数监测。
  4. 自动校准:SCD40具有自动校准功能(ASC),可以在使用过程中自动保持测量准确性。
  5. 低功耗:支持低功耗模式,适合长时间运行和电池供电的应用场景。

二、技术原理

SCD40采用光声传感原理进行CO2浓度测量。当红外光通过含有二氧化碳的气流时,二氧化碳分子会吸收部分光线并产生热量,进而产生声波。传感器通过检测这些声波信号的强度来判断二氧化碳的浓度。此外,内置的SHTW2温湿度传感器能够实时测量环境温度和湿度,并通过片上信号补偿技术提高CO2浓度的测量精度。

三、应用场景

SCD40广泛应用于需要精确监测CO2浓度的场合,如:

  • 智能家居:用于智能通风系统、空气净化器等设备中,根据室内CO2浓度自动调节通风量或净化效率。
  • 农业大棚:监测大棚内的CO2浓度,为植物提供适宜的生长环境。
  • 工业检测:在工业生产过程中监测CO2排放情况,确保生产安全和环境合规。
  • 医疗领域:用于手术室、病房等场所的空气质量监测,保障医疗环境的洁净度。

四、使用注意事项

  1. 上电稳定:传感器上电后需要等待约1000ms才能进入空闲状态并接收命令。
  2. 校准操作:虽然SCD40具有自动校准功能,但在特定情况下(如长时间未使用或环境变化较大时)可能需要进行手动校准。
  3. 焊接时间:拆包后应在168小时内完成焊接,以避免湿度增加影响传感器性能。
  4. 接口通信:SCD40使用IIC接口进行通信,从机地址为0x62。在读取数据时需注意遵循相应的通信协议和时序要求。

综上所述,SCD40是一款性能优越、功能全面的微型CO2传感器,广泛应用于智能家居、农业、工业和医疗等领域。在使用过程中需注意遵循相关操作规范和维护要求以确保其长期稳定运行和测量准确性。

五、驱动代码

STM32采用任意IO口即可驱动,因为本人采用的是模拟iic接口,分别罗列了.C文件和h文件。如下为.c文件:

#include "i2c.h"
#include "delay.h"
//初始化IIC
void IIC_Init(void)
{			     
 GPIO_InitTypeDef GPIO_Initure;
 
	__HAL_RCC_GPIOB_CLK_ENABLE();   //使能GPIOD时钟
 
    /*
		SCL - PD6		SDA-PC1
	*/
    GPIO_Initure.Pin  	= GPIO_PIN_6;
    GPIO_Initure.Mode 	= GPIO_MODE_OUTPUT_PP;	//推挽输出
    GPIO_Initure.Pull 	= GPIO_PULLUP;        	//上拉
    GPIO_Initure.Speed 	= GPIO_SPEED_FREQ_HIGH;   	//快速
    HAL_GPIO_Init(GPIOB, &GPIO_Initure);
	
	
	  GPIO_Initure.Pin = GPIO_PIN_7;
    GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; 	//推挽输出
    GPIO_Initure.Pull = GPIO_PULLUP;        	//上拉
    GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;   	//快速
    HAL_GPIO_Init(GPIOB, &GPIO_Initure);
 
}
//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA(1);	  	  
	IIC_SCL(1);
	delay_us(4);
 	IIC_SDA(0);//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL(0);//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL(0);
	IIC_SDA(0);//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL(1); 
	IIC_SDA(1);//发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA(1);delay_us(1);	   
	IIC_SCL(1);delay_us(1);	 
	while(READ_SDAA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL(0);//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL(0);
	SDA_OUT() ;
	IIC_SDA(0);
	delay_us(2);
	IIC_SCL(1);
	delay_us(2);
	IIC_SCL(0);
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL(0);
	SDA_OUT();
	IIC_SDA(1);
	delay_us(2);
	IIC_SCL(1);
	delay_us(2);
	IIC_SCL(0);
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL(0);//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {       
      IIC_SDA((txd & 0x80) >> 7);			
//        IIC_SDA(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL(1);
		delay_us(2); 
		IIC_SCL(0);	
		delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL(0); 
        delay_us(2);
		IIC_SCL(1);
        receive<<=1;
        if(READ_SDAA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

uint8_t periodic_Measurements_AreRunning = 0;
scd4x_SensorData_t SCD4x_SensorData;

//Start periodic measurements. See "SCD40_SCD41_Datasheet.pdf" 3.5.1
//signal update interval is 5 seconds.
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);
}

// Stop periodic measurements. See "SCD40_SCD41_Datasheet.pdf" 3.5.3
// 传感器在发出 STOP_PERIONAL_MEASURATION 命令后等待500毫秒后才会对其他命令作出响应。
bool SCD4x_stopPeriodicMeasurement(uint16_t delayTime)
{
	
//	if (periodic_Measurements_AreRunning == 0)
//  {
//    return (false);
//  }

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

// Get 9 bytes from SCD4x. See 3.5.2
// Read sensor output.
// 每个信号更新间隔只能读出一次测量数据,因为读数时缓冲区被清空
// 如果缓冲区中没有可用数据,传感器将返回 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;
  delay_ms(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 |= 0x01;} // CO2
					if(x==5)
						{error |= 0x02;} // Temperature
					if(x==8)
						{error |= 0x04;} // 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! 
}



//Get 9 bytes from SCD4x. Convert 48-bit serial number to ASCII chars. See 3.9.2
//Returns true if serial number is read successfully
//Reading out the serial number can be used to identify the chip and to verify the presence of the sensor.
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

  delay_ms(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!
}



//Perform self test. Takes 10 seconds to complete. See 3.9.3
//The perform_self_test feature can be used as an end-of-line test to check sensor functionality
//and the customer power supply to the sensor.
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
}

//Peform factory reset. See 3.9.4
//The perform_factory_reset command resets all configuration settings stored in the EEPROM
//and erases the FRC and ASC algorithm history.
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)
    delay_ms(delayTime);
  return (success);
}

//Reinit. See 3.9.5
//The reinit command reinitializes the sensor by reloading user settings from EEPROM.
//Before sending the reinit command, the stop measurement command must be issued.
//If the reinit command does not trigger the desired re-initialization,a power-cycle should be applied to the SCD4x.
bool SCD4x_reInit(uint16_t delayTime)
{
  if (periodic_Measurements_AreRunning)
  {
    return (false); // 需停止周期测量
  }

  bool success = SCD4x_sendCommand(SCD4x_COMMAND_REINIT);
  if (delayTime > 0)
    delay_ms(delayTime);
  return (success);
}

//Sends just a command, no arguments, no CRC
// see "SCD40_SCD41_Datasheet.pdf 3.3", "send 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);
}

// Sends a command along with arguments and CRC
// see "SCD40_SCD41_Datasheet.pdf 3.3", "write 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);
}



// Read one uint16 data from SCD4x plus CRC. see "SCD40_SCD41_Datasheet.pdf 3.3", "read sequence"
// Returns true if IIC_Wait_Ack returns 0 _and_ the CRC check is valid
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

  delay_ms(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);
}

//Returns true when data is available. See 3.8.2
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);
}

//PRIVATE: Convert serial number digit to 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
}

//Given an array and a number of bytes, this calculate CRC8 for those bytes
//From: http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html
//Tested with: http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
//x^8+x^5+x^4+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
}


/*
			end
*?

h文件如下:

#ifndef _I2C_H_
#define _I2C_H_


#include "stm32g0xx.h"
#include "stdbool.h"



//IO方向设置
#define SDA_IN()  {GPIOB->MODER&=~(3<<(6*2));GPIOB->MODER|=0<<(6*2);}	//PC1输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(6*2));GPIOB->MODER|=1<<(6*2);} 	//PC1输出模式

#define IIC_SCL(n)		(n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET))//SCL
#define IIC_SDA(n)		(n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET))//SDA
#define READ_SDAA  		HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)//输入SDA





//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(u8 txd);			//IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号

void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);	


#define SCD4x_ENABLE_DEBUGLOG 0 //debug SCD4x

//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

typedef union
{
  int16_t signed16;
  uint16_t unsigned16;
} scd4x_int16_uint16_t; // Avoid any ambiguity casting int16_t to uint16_t

typedef union
{
  uint16_t unsigned16;
  uint8_t bytes[2];
} scd4x_uint16_Bytes_t; // Make it easy to convert 2 x uint8_t to uint16_t

typedef struct
{
	scd4x_uint16_Bytes_t tempCO2;
	scd4x_uint16_Bytes_t tempHumidity;
	scd4x_uint16_Bytes_t tempTemperature;
}scd4x_SensorData_t;

extern scd4x_SensorData_t SCD4x_SensorData; //save SCD4x sensor data

bool SCD4x_StartPeriodicMeasurement(void);
bool SCD4x_stopPeriodicMeasurement(uint16_t delayTime); // execution time: 500ms
bool SCD4x_readMeasurement(uint16_t *co2, float *temperature, float *humidity, uint8_t *Error);
bool SCD4x_getSerialNumber(char *serialNumber); //return 13 byte string

bool SCD4x_performSelfTest(void);  // executed time 10000 ms
bool SCD4x_performFactoryReset(uint16_t delayTime); // executed time 1200 ms
bool SCD4x_reInit(uint16_t delayTime);   // executed time 20 ms
bool SCD4x_getDataReadyStatus(void);  // executed time 1 ms

bool SCD4x_sendCommand(uint16_t command);
bool SCD4x_sendCommand_CmdData_CRC(uint16_t command, uint16_t arguments);
bool SCD4x_readRegister(uint16_t registerAddress, uint16_t *response, uint16_t delayTime);

char SCD4x_convertHexToASCII(uint8_t digit);
uint8_t SCD4x_computeCRC8(uint8_t data[], uint8_t len); //CRC8校验



//===========================================================================//

#endif
//========================== end of file ==================================//

IO方向设置不理解参考往期文章STM32驱动AHT10/AHT20/AHT30温湿度传感器源码-CSDN博客

这里有做说明 ,https://blog.csdn.net/man_pangpang/article/details/139071176?utm_source%20=%20uc_fansmsg

主要说明如何配置iic方向;

主程序如下:

..............
uint16_t CO2 = 0; //ppm
float temperature = 0; //摄氏度
float humidity = 0; //湿度 百分比
uint8_t error = 0;


int main(void)
{    
	u16 ikL=0;
    
.............
	IIC_Init();
//	init_scd40();
	SCD4x_stopPeriodicMeasurement(600);
	//SCD4x软件重启
	SCD4x_reInit(20);
	//开始周期测量
	SCD4x_StartPeriodicMeasurement(); 
  while (1)
  {		
		

		ikL++;
			if(ikL>10)
			{
				ikL=0;
				SCD4x_readMeasurement(&CO2, &temperature, &humidity, &error); 
		        //上句即可得到温度湿度以及二氧化碳浓度数据
			}

		delay_ms(1000);
  }
}


 创作不易,全部开源各位且行且珍惜,请各位履行点赞关注评论转发义务。

### STM32SCD40-D-R1传感器集成 对于STM32微控制器与SCD40-D-R1二氧化碳浓度传感器之间的交互,主要依赖于I²C通信协议实现数据交换。SCD40-D-R1是一款紧凑型、低功耗的CO₂气体浓度测量模块,适用于各种便携式设备和室内空气质量监测系统。 #### 驱动程序开发环境配置 为了便于开发者编写针对SCD40-D-R1的驱动代码,在基于STM32平台下推荐使用官方提供的HAL或LL作为底层接口支持。这些提供了丰富的API函数用于初始化外设以及处理中断事件等操作[^1]。 ```c // 初始化 I2C 接口 void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.Timing = 0x20909CEC; // 设置时钟频率和其他参数 if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } } ``` #### 数据读取流程 通过调用`HAL_I2C_Master_Transmit()`发送指令给SCD40-D-R1请求获取当前环境中的CO₂含量;随后利用`HAL_I2C_Master_Receive()`接收返回的数据包并解析其中的有效信息完成一次完整的采样过程。 ```c uint8_t cmd[2]; cmd[0] = SCD4X_CMD_MEASURE_CO2 >> 8; cmd[1] = SCD4X_CMD_MEASURE_CO2 & 0xFF; if(HAL_I2C_Master_Transmit(&hi2c1, SCD4X_ADDR<<1, cmd, sizeof(cmd), HAL_MAX_DELAY)!= HAL_OK){ /* Transfer error in transmission process */ } HAL_Delay(5); // 等待传感器响应时间 uint8_t data[9]; // 假定每次最多能接收到9字节长度的消息体 if(HAL_I2C_Master_Receive(&hi2c1, SCD4X_ADDR<<1, data, sizeof(data), HAL_MAX_DELAY)!= HAL_OK){ /* Transfer error in reception process */ } ``` #### 应用实例分析 实际应用场景中可以构建一个简单的智能家居控制系统,该系统能够实时检测房间内的空气品质状况并通过LCD显示屏向用户提供直观反馈。此外还可以连接Wi-Fi模组上传至云端服务器以便远程查看历史记录等功能扩展。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值