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