接下来就是进行基本的驱动设计,这里我们使用最常用的STM32作为主要的控制器件,我们在第二章的时候主要使用的是就是arduino进行的快速验证,可是并不知道所以然,会用还要能把它集成到其他系统,其中STM32作为目前使用应用范围最广的MCU,明显是本次驱动验证最好的载体。
我们通过快速验证以及详细介绍,实际上驱动的设计也是基本水到渠成的,本次我们就以周期性测量来进行基本的驱动设计,主要包括接口适配,命令函数的适配,再到具体测量的实现。
我们先通过测量实现基本的通信验证,这里我们通过get_serial_number和get_sensor_variant来进行验证,用于证明传感器的身份以及验证接口的准确性。
首先是接口的适配,主要是发送命令和数据接收,分别对应IIC的发送和接受:int SCD4x_Send_Commmand(uint16_t command)
{
    uint8_t CommandData[2] = {0};
    int result;
    
    CommandData[0] = command >> 8 & 0xff;
  CommandData[1] = command & 0xff;
    result = HAL_I2C_Master_Transmit(&hi2c1,(SCD41_ADDRESS << 1), CommandData, 2, 1000);
    
    if (result!=HAL_OK)
        return -1; //error
    else
        return 0; //Success
}
int SCD4x_Read_Response(uint8_t ndata,uint8_t *data)
{
    // (2 bytes status + 1 byte CRC)*n
    int result;
    result = HAL_I2C_Master_Receive(&hi2c1, (SCD41_ADDRESS << 1), data, 3*ndata, 1000);
    
    if (result!=HAL_OK)
        return -1; //error
    else
        return 0; //Success
}这里我们需要注意,命令是个16位的数据,我们需要拆分一下,接受的数据也是通过16位的,不过不管是命令还是数据都是通过8位的方式传输的,这里只是做了一下接口的适配,如果后面想要通过模拟IIC的方式只要修改这里就好了,接受数据没有在这里进行处理,实际上可以直接在这里进行CRC的判断以及数据合成,不过我没有放到这里,在具体的命令中进行的处理。需要做一下8位的CRC验证:
uint8_t sensirion_common_generate_crc(const uint8_t* data, uint16_t count)
{
    uint8_t crc = CRC8_INIT;
    for (uint16_t i = 0; i < count; ++i) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; ++j) {
            if (crc & 0x80)
                crc = (crc << 1) ^ CRC8_POLYNOMIAL;
            else
                crc = (crc << 1);
        }
    }
    return crc;
}接下来就是具体的命令了,这里主要做的是传感器和设备的识别:
void SCD4x_Init(void)
{
    stop_periodic_measurement();
    HAL_Delay(500); 
    get_serial_number();
    get_sensor_variant();
    start_periodic_measurement();
}
int get_serial_number(void)
{
    uint16_t command = GET_SERIAL_NUMBER_CMD;
    uint8_t response[9]; // 3 data words + 3 CRC bytes
    int result;
    
    // Send command
    result = SCD4x_Send_Commmand(command);
    if (result != HAL_OK) {
        return result;
   }
    HAL_Delay(1); 
    // Read response
    result = SCD4x_Read_Response(9,response);
    if (result != HAL_OK) {
            return result;
    }
    
    SCD4x.Number[0] = (response[0] << 8) | response[1];  
    SCD4x.Number[1] = (response[3] << 8) | response[4];  
    SCD4x.Number[2] = (response[6] << 8) | response[7];  
    
    return result;
}
int get_sensor_variant(void)
{
        uint16_t command = GET_SERIAL_VARIANT_CMD;
        uint8_t response[3] = {0}; // 2 bytes status + 1 byte CRC
    int result;
        
    // Send command
    result = SCD4x_Send_Commmand(command);
    if (result != HAL_OK) {
        return result;
    }
        // Wait for sensor to process
    HAL_Delay(1); 
    // Read response
    result = SCD4x_Read_Response(3,response);
    if (result != HAL_OK) {
        return result;
    }
    // Verify CRC
    if (sensirion_common_generate_crc(response, 2) != response[2]) {
        return -1;
    }
    // Extract the 16-bit status
    SCD4x.Variant = (response[0] << 8) | response[1];
        return 0;
}结果如下:

接下来是周期性测量用到的函数:
int start_periodic_measurement(void)
{
    uint16_t command = START_PERIODIC_MEASUREMENT_CMD;
  int result;
    result = SCD4x_Send_Commmand(command);
    return result;
}
int stop_periodic_measurement(void)
{
  uint16_t command = STOP_PERIODIC_MEASUREMENT_CMD;
  int result;
    result = SCD4x_Send_Commmand(command);
    return result;
}
/* Function to check if measurement data is ready */
int get_data_ready_status(void)
{
        uint16_t command = GET_DATA_READ_STATUS_CMD;
        uint8_t response[3] = {0}; // 2 bytes status + 1 byte CRC
    int result;
        
    // Send command
    result = SCD4x_Send_Commmand(command);
    if (result != HAL_OK) {
        return result;
    }
        // Wait for sensor to process
    HAL_Delay(1); 
    // Read response
    result = SCD4x_Read_Response(3,response);
    if (result != HAL_OK) {
        return result;
    }
    // Verify CRC
    if (sensirion_common_generate_crc(response, 2) != response[2]) {
        return -1;
    }
    // Extract the 16-bit status
    uint16_t word = (response[0] << 8) | response[1];
    // Check if data is ready (status[10:0] != 0)
    if ((word & 0x07FF) == 0) {
        return -1;
    } else {
        return 0;
    }
}
int read_measurement(void)
{
    uint16_t command = READ_MEASUREMENT_CMD;
    uint8_t response[9]; // 3 data words + 3 CRC bytes
    uint16_t word[3]={0};
    int result=0;
    if(get_data_ready_status()==-1)
        return -1;
    // Send command
  result = SCD4x_Send_Commmand(command);
  if (result != HAL_OK) {
      return result;
  }
    
    // Read response
    result = SCD4x_Read_Response(9,response);
    if (result != HAL_OK) {
            return result;
    }
    word[0] = (response[0] << 8) | response[1];  // CO2
    word[1] = (response[3] << 8) | response[4];  // Temperature
    word[2] = (response[6] << 8) | response[7];  // Humidity
    SCD4x.co2    = word[0];
    SCD4x.temp    = -45 + 175*word[1]/ pow(2,16)-1;
    SCD4x.rh     = 100*word[2]/ pow(2,16)-1;
    printf("co2=%f \n",SCD4x.co2);
    printf("temp=%f \n",SCD4x.temp);
    printf("rh = %f\n",SCD4x.rh);
    return 0;
}        效果如下:
这个是一开始检测的数据,需要一定的稳定时间,反应还是挺快的,我离开一段时间后,通风的环境下就小了很多:

通风状态下有人的CO2测试数据:

PS:本次使用的STM32作为本次的主控,keil作为开发环境,不知道为什么这个IIC还是有时候会出现问题,不知道这个和传感器时候有关系,采用的IIC标准速率进行的采集,一旦调整优化等级就会出现问题,只能用O3等级,但凡是调整到O0或者再加点数据通信就会出现暂停的情况(这个是通过keil在线调试定位的点),听说过进入错误中断的或者死循环的,这种情况还是第一次遇到,大家如果有什么好方法或者解决办法可以一起讨论一下,或者IIC只能用IO口模拟才是最稳定的。
        整体的效果显示,通过OLED进行显示:
【EEPW测评-SCD41评估板】 https://www.bilibili.com/video/BV1PmxgzHEyS/?share_source=copy_web&vd_source=2a202874768d99b0acaa1aceb9a9b93e

 
					
				 
						
 
			
			
			
						
			 我要赚赏金
 我要赚赏金 STM32
STM32 MCU
MCU 通讯及无线技术
通讯及无线技术 物联网技术
物联网技术 电子DIY
电子DIY 板卡试用
板卡试用 基础知识
基础知识 软件与操作系统
软件与操作系统 我爱生活
我爱生活 小e食堂
小e食堂

