stm32HAL库onenet平台数据实时获取实例--PH值获取与上传

发布时间:2026/7/10 2:15:45

stm32HAL库onenet平台数据实时获取实例--PH值获取与上传 文章目录概要整体架构流程PH值的感知PH值的上传小结概要在电赛之中常常会涉及到单片机与网络平台的实时交互而交互又分为上行链路交互与下行链路的交互其中上行链路的交互最主要的莫过于数据的实时上传今天我就来分享一下如何使用stm32F407ZGT6的HAL库与onenet平台实现上行链路数据的实时上传。整体架构流程一.感知层使用PH4502C模块PH测量模块的核心技术点如下1PH测量使用ADC1接口采用的是DMA回调这样的设计首先可以节省CPU的算力一旦启动 DMAADC 的转换结果会自动通过硬件通路存入指定的内存缓冲区adc_dma_buf完全无需 CPU 介入转换过程极大提升了系统响应速度。2采用的是DMA回调循环模式DMA 配置为循环模式后当PH_SAMPLE_COUNT个样本采集完成后DMA 指针会自动重置到缓冲区起始位置实现“只留最新数据”的循环覆盖。3采用高效的均值滤波由于 DMA 在后台持续运行主程序调用Sensor_Get_PH函数时缓冲区中始终存有最新的PH_SAMPLE_COUNT次采样样本只需进行简单的累加求平均即可在不阻塞 ADC 转换的前提下获取稳定的 PH 值。二.通信层采用的是EC20模块连接onenet平台重点是要将获取到的PH值上传到onenet平台已知数据上传的JSON格式如下{ id: 123, params: { ph: %.2f } }PH值的感知一感知层配置1PH-4502C接口配置2DMA配置在整体流程架构中介绍本项目采取的是DMA循环回调模式并采取均值滤波获取稳定的PH值具体操作流程如下图所示首先是设置DMA循环模式第一步设置哪个接口上面IN0IN1IN2.......代表的是ADC的多个通道由于我们接的是PA4代表的是ADC1的4通道第二步是在下方的Configuration配置之中设置ADC的配置打开DMA Settings点击下方的Add选择DMA模式在下方的配置之中设置为Circle模式这时我们烧录到单片机之中会产生如下的效果由于我们是将所测得的数据储存到adc_dma_buf之中这时如果仅仅只是进行第一步和第二步测试最终只能接收到一个数据这个数据没有进行实时刷新并且经过均值滤波之后测出的PH值为14。那么为什么会产生这样的现象呢首先产生一个数据的原因是没有设置Number Of Conversion这是只产生一个数据的原因Number Of Conversion 是定义 ADC 扫描序列物理长度的参数在代码中我设置了每16个数据进行采样采样后取均值我错误地将Number Of Conversion设为 1这意味着 ADC 完成 1 次通道转换后就认为“序列结束”了因此只出现了1个数据。其次一个原因就是未开启连续转换模式Continuous Conversion Mode导致最终数据无法如果没有开启Continuous Conversion ModeADC 在完成用户设置的Number Of Conversion次数后会进入休眠状态 (Idle)此时它不再向 DMA 发出任何搬运请求。基于上述分析可知嵌入式开发可以总结为驱动-硬件-搬运”三方握手模型即在驱动HAL层开启 ADC 与 DMA 的搬运开关在硬件 (ADC)产生模拟信号转数字信号的“脉冲”搬运 (DMA)搬运数据流扫描序列物理长度在硬件层产生连续转换模式在搬运层之中展现这样可以使得DMA进行连续搬运所接收到的数据。开启上述功能便可以收集到实时更新的数据并且能够使用均值滤波进行实时更新最终效果如下所示3PH处理代码实例代码Sensor.h #ifndef __SENSOR_H #define __SENSOR_H #include main.h #include ec20.h #define PH_SAMPLE_COUNT 16 // DMA 循环采样次数(循环采样实时覆盖 //外部调用变量 extern ADC_HandleTypeDef hadc1; //函数声明 void Sensor_Init(void);//ADC1初始化 float Sensor_Get_PH(void);//ADC1获取PH值 #endif代码Sensor.c #include ec20.h #include Sensor.h #include stdio.h #include string.h uint16_t adc_dma_buf[PH_SAMPLE_COUNT];//设置DMA缓存区 /*DMA初始化*/ void Sensor_Init(void) { memset(adc_dma_buf,0,sizeof(adc_dma_buf)); HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_dma_buf, PH_SAMPLE_COUNT); } /*获取并解析PH值*/ float Sensor_Get_PH(void) { uint32_t sum0; float avg_adc0; float voltage0; float ph_val0; for(int i0;iPH_SAMPLE_COUNT;i)//对所采取的数据进行累加目的是为了求平均值(均值滤波 { sumadc_dma_buf[i]; } avg_adc (float)sum / PH_SAMPLE_COUNT; // avg_adc(float)adc_dma_buf[0]; voltage avg_adc * (3.3f / 4095.0f); // 假设参考电压为 3.3V ph_val 7.0f ((2.5f - voltage) / 0.05916f);//ph值计算公式 return ph_val; }代码main.c int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init(); MX_ADC1_Init(); //初始化步骤 Sensor_Init(); //以下属于通信模块部分// while (1) { //每5s取出我的PH值 if(HAL_GetTick()-last_ph_tick10000) { HAL_Delay(500); g_sys_data.phSensor_Get_PH(); last_ph_tickHAL_GetTick(); } ////////后续通信上报流程/////////// } /** * brief System Clock Configuration * retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM 4; RCC_OscInitStruct.PLL.PLLN 168; RCC_OscInitStruct.PLL.PLLP RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ 4; if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_5) ! HAL_OK) { Error_Handler(); } } } /* USER CODE END 4 */其中在main函数之中是不需要使用回调函数的因为数据被 DMA 硬件直接搬运到预设的adc_dma_buf数组中。由于 DMA 是由硬件自动循环触发的数组里的数据时刻保持更新。对比USART接口数据是“突发性”到达的你无法预测它何时到来因此必须依赖中断/回调函数来“通知”CPU“有新数据了快来取”PH值的上传ec20.c /*数据上报函数*/ void EC20_Publish_Property(char *json_params) { char pub_cmd[128];//用于发送JSON的前缀 char full_payload[512];//用于发送JSON的格式 sprintf(full_payload,{\id\:\123\,\params\:%s},json_params);//full_payload是原有格式和后面两个东西拼接而成的 sprintf(pub_cmd, ATQMTPUBEX0,0,0,0,\$sys/z263G74j50/Ship_01/thing/property/post\,%d\r\n, (int)strlen(full_payload));//动态指令用sprint,部分变化最后拼接 HAL_UART_Transmit(huart1,(uint8_t*)pub_cmd,strlen(pub_cmd),100);//发送AT指令行 HAL_Delay(200); HAL_UART_Transmit(huart1,(uint8_t*)full_payload,strlen(full_payload),500);//发送JSON的完整格式 }最后一行是拼接的核心代码具体的交互如下述main.c代码main.c上报代码 if(HAL_GetTick()-last_report_tick10000)//每10s上报一次 { char param_buf[256]; // 打包PH数据符合OneNet物模型格式 sprintf(param_buf, {\ph_val\:{\value\:%.2f}}, current_ph);//合并JSON EC20_Publish_Property(param_buf);//上传JSON last_report_tick HAL_GetTick(); }最后在onenet平台产生的数据如下图所示小结本项目实现了PH值的测量采用了DMA的循环模式使得上传的PH数据更具实时性同时采用均值滤波使得PH值数据更具准确性在数据上传方面使用JSON格式并最终实现了实时上传。

相关新闻