【Let'sdo第四期-液体流量检测仪DIY】过程帖03 驱动模块&流量检测
我们接线成功后,先测试是否能驱动继电器
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10); //继电器 Hal_Delay(1000);//延时时间尽量长,不要频繁开关继电器
在while(1)里测试这段代码,如果继电器1S吸合,1S断开,那就是驱动成功了!
如果没有驱动成功,请先检查接线,尤其是供电部分~
检测外部中断的脉冲信号 - 检测流量传感器的数据
流量传感器输出的是脉冲信号,需要在抽水泵工作的时候,有水流经过,这个传感器才会产生脉冲,然后我们需要计数这个脉冲,我使用外部中断的方式来累积计数
具体接线可以查看上一篇文章哦~代码如下
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_2) { flow_cnt ++; } }
流量检测
流量检测的思路
累积流量:用按键控制继电器的开关,在OLED上显示脉冲计数,
实际测试时候,打开继电器,抽水1000ml,得到一个计数值x1,
多次测量x2,x3,取x1,x2,x3的平均值x,x/1000 = 每毫升水的计数值,
这个数值可以定义成一个宏,供电电压不同,这个数值不同,数值需要自己实测!!!
我的数据是这样的:
#define CNT_Flow_1ML 355.357f
瞬时流量:我取的20ms的计数值,计数值*50 = 1S的计数值,然后用这个值除以CNT_Flow_1ML,得到1S的流量,单位是ml/S
我的思路应该没有问题,比较巧妙,而且没有用到定时器,又偷个懒哈哈哈
裸机代码
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "OLED.h" #include "myoldkey.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ uint32_t OLED_Tick; uint32_t Func_Tick; uint32_t flow_cnt; uint32_t key_tick; float Moment_Flow = 1.2f; // 瞬间流量 float Cumulative_Flow = 0.0f; // 累计流量 int8_t textnum; uint16_t TargetFlow = 0; //目标水量 - 100ML uint32_t Target_CNT = 0; void OLED_Show_Func(void); void JDQ_Func(void); void key_proc(void); /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ // CNT:164157 --- 650ML #define CNT_Flow_1ML 355.357f //#define CNT_Flow_1ML 533.295f /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM3_Init(); /* USER CODE BEGIN 2 */ OLED_Init(); OLED_Clear(); OLED_ShowChinese(8*6, 0, "得捷"); OLED_Printf(0, 16, OLED_8X16, " Waiting... "); OLED_Update(); HAL_Delay(1800); OLED_Clear(); HAL_TIM_Base_Start_IT(&htim3); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET); // 继电器 off /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { OLED_Show_Func(); // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET); // 继电器 on // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET); // 继电器 off JDQ_Func(); key_proc(); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; 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_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ void OLED_Show_Func(void) { if (uwTick - OLED_Tick <= 200) return; OLED_Tick = uwTick; OLED_Clear(); char tempStr[20]; OLED_ShowChinese(8 * 2, 16 * 0, "瞬间流量"); OLED_ShowChar(8 * 10, 16 * 0 + 8, ':', OLED_6X8); // : // sprintf(tempStr, "%d %d ", flow_cnt, TargetFlow); sprintf(tempStr, " %.1f mL/s %d ", Moment_Flow, TargetFlow); OLED_Printf(8 * 4, 16 * 1 + 5, OLED_6X8, tempStr); OLED_ShowChinese(8 * 2, 16 * 2, "累计流量"); OLED_ShowChar(8 * 10, 16 * 2 + 8, ':', OLED_6X8); // 显示: Cumulative_Flow = (float)flow_cnt / CNT_Flow_1ML; sprintf(tempStr, " %.2f mL", Cumulative_Flow); OLED_Printf(8 * 4, 16 * 3 + 5, OLED_6X8, tempStr); OLED_Update(); } void JDQ_Func(void) { static uint32_t Last_CNT; //上次计数 static uint32_t error_Flow; //前后两次的流量差 if (uwTick - Func_Tick <= 20) //如果要修改这个20ms,就需要重新计算瞬间流量 return; Func_Tick = uwTick; Target_CNT = TargetFlow*CNT_Flow_1ML; if (flow_cnt <= TargetFlow*CNT_Flow_1ML) // 当前小于目标,未到,开继电器 { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET); // 继电器 on } else if (flow_cnt >= TargetFlow*CNT_Flow_1ML) //如果当前计数值 >= 目标计数值,关闭继电器 { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET); // 继电器 off } error_Flow = flow_cnt - Last_CNT; //前后两次的流量差 20ms Moment_Flow = (float)(error_Flow * (1000/20)) / CNT_Flow_1ML; //计算瞬间流量 // error_Flow * (1000/20) === 20ms的流量*50, 等于1S的流量 Last_CNT = flow_cnt; } void key_proc(void) { if (uwTick - key_tick <= 10) return; key_tick = uwTick; if (Key[1].short_flag == 1) { Key[1].short_flag = 0; TargetFlow += 100; //目标值+=100 // HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10); //继电器 // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET); // 继电器 on } } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_2) { flow_cnt ++; } } /* 定时器回调函数 1ms */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint32_t tim3_cnt; static uint8_t keyscan_cnt; if (htim->Instance == TIM3) { if (tim3_cnt ++ >= 500) { // LED T HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // LED tim3_cnt = 0; } if (keyscan_cnt ++ >= 20) { key_serv_long(); keyscan_cnt = 0; } } } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */
以上就是本篇文章的全部内容了
如果有问题,欢迎大佬指正哦~虚心接受