温室效应加剧、废气肆虐、冰川消融、岛国面临沉没危机,这一切的根源皆在于CO2的过量排放。为此,让一款精准的CO2监测装置成为家庭的常备之选,方能深刻体悟环境危机的迫在眉睫。这款装置不仅能揭示室外环境的严峻,更能洞察室内空气的品质,提醒你适时开窗通风,有效降低CO2浓度。
这款先进的CO2传感器模块,凭借AT指令,轻松读取CO2及TVOC数值,更配备两种灵活的门限模式供您选择。该模块集SGP30气体传感器与SHT20温湿度传感器于一身,全面监测总VOC信号(TVOC)、CO2等效信号、温度及湿度,数据详尽无遗。模块还贴心提供基线设置及湿度交叉灵敏度补偿功能,确保测量结果的准确无误。其应用范围广泛,无论是智能家居、家电还是物联网应用等领域,都能展现其卓越的性能与价值。
这款传感器的模块的通信方式是通过AT指令获取,基础就是串口通信,我们在STM32WBA55CG的STM32CubeMX的工程文
件中可以看到,USART1已经使用了,也就是咱们的板载STlink的虚拟串口连接的,所以我们需要用到USART2的进行通信,在正式的驱动之前我们先用调试串口继续进行一下测试与接受数据情况。
首先进行USART2配置:
其相关配置可以参考USART2,不过注意波特率要与SGP30模块保持一直,为9600,对应的引脚是PA11和PA12;
接下来就是程序中的串口处理了,由于我们需要使用AT指令进行控制,所以指令是不定长的,我们在接收数据的时候选取了接收中断和空闲中断两种方式进行,接收中断确保每一个数据的接收,空闲中断用于指示一条指令的结束,然后我们需要在程序中进行如下处理。
初始化阶段开启中断:
HAL_UART_Receive_IT(&huart2, (uint8_t *)RxBuff, 1); __HAL_UART_CLEAR_IDLEFLAG(&huart2); __HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
接收中断是通过HAL_UART_Receive_IT开启的,而空闲中断是通过比较原始的宏定义处理,HAL的处置中对它没有进一步包装;
接下来就是中断服务函数的处理:
void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ if( __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) == SET ) { __HAL_UART_CLEAR_IDLEFLAG(&huart2); Rx_Flag[0] = 1; Rx_Flag[1] = Rx_cnt; Rx_cnt=0; } /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ }
增加空闲中断处理,HAL_UART_IRQHandler中主要是接收和发送方面的处理;
然后是接收回调函数:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ UNUSED(huart); uint8_t Var =0; //临时变量 if (huart == &huart2) //判断中断来源(串口2) { Var = USART2->RDR; //一个给发送用(TDR),一个给接收用(RDR), RxBuff[Rx_cnt++] = Var; HAL_UART_Receive_IT(&huart2, &RxBuff[Rx_cnt], 1); //再开启接收中断 } }
这里需要注意,中断处理函数会将处理的中断关闭,我们需要每次都开启一下,Rx_cnt是不管增加的,来连续接收串口数据;
测试函数我们是在while中对收到的一串数据进行回传:
if( Rx_Flag[0]==0x01) { Rx_Flag[0]=0; HAL_UART_Transmit(&huart2,RxBuff,Rx_Flag[1],0xffff); }
HAL_UART_Transmit是阻塞时串口发送,效果如下:
上图可以看到,每一次收到了完整字符串后进行字符串的回传,也就是说可以完整的进行AT指令的发送和接收,收到一组完整字符串后进行一次处理就可以。
接下来就是SGP30模块驱动AT指令的实现了。
我们通过接收中断和空闲中断的结合实现不定长串口数据的接收和处理,我们目前的数据量比较少,这样处理是没有问题的,如果数据亮很多,那么就不建议这样了,最好采用空闲中断+DMA的方式进行,接下来我们看一下AT指令的发送与接收处理:
void App_ATCOM(void) { uint32_t data_cnt = 0; uint8_t j,k,a,z; for(uint8_t i=0;i<4;i++) { if(AT_Flag[i] == 1) { if(i==0) { HAL_UART_Transmit(&huart2,AT_T,6,0xffff); } else if(i==1) { HAL_UART_Transmit(&huart2,AT_H,6,0xffff); } else if(i==2) { HAL_UART_Transmit(&huart2,AT_CO2,8,0xffff); } else if(i==3) { HAL_UART_Transmit(&huart2,AT_TVOC,9,0xffff); } AT_Flag[i] = 2; AT_cnt = i; } } if( Rx_Flag[0]==0x01) { Rx_Flag[0]=0; if(AT_cnt == 0 && AT_Flag[AT_cnt] == 2) { Data_T = (RxBuff[3]-0x30)*100+(RxBuff[4]-0x30)*10+(RxBuff[6]-0x30); AT_Flag[AT_cnt] = 0; AT_cnt++; AT_Flag[AT_cnt] = 1; } else if(AT_cnt == 1 && AT_Flag[AT_cnt] == 2) { Data_H = (RxBuff[3]-0x30)*100+(RxBuff[4]-0x30)*10+(RxBuff[6]-0x30); AT_Flag[AT_cnt] = 0; AT_cnt++; AT_Flag[AT_cnt] = 1; } else if(AT_cnt == 2 && AT_Flag[AT_cnt] == 2) { Data_CO2 = 0; for( j=0;RxBuff[5+j] != 0x0D;j++) { if(j==0) data_cnt = 1; else data_cnt *= 10; } for( k=0;k<j+1;k++) { Data_CO2 += (RxBuff[5+k]-0x30)*data_cnt; data_cnt /=10; } AT_Flag[AT_cnt] = 0; AT_cnt++; AT_Flag[AT_cnt] = 1; } else if(AT_cnt == 3 && AT_Flag[AT_cnt] == 2) { Data_TVOC = 0; for( a=0;RxBuff[6+a] != 0x0D;a++) { if(a==0) data_cnt = 1; else data_cnt *= 10; } for( z=0;z<a+1;z++) { Data_TVOC += (RxBuff[6+z]-0x30)*data_cnt; data_cnt /=10; } AT_Flag[AT_cnt] = 0; } } }
总体的处理程序都在这里进行,顺序发送和处理,开启在定时器中进行,每2秒进行一次采集,在线查看数据处理如下: