接下来就是进行基本的驱动设计,这里我们使用最常用的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