【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 */以上就是本篇文章的全部内容了
如果有问题,欢迎大佬指正哦~虚心接受
我要赚赏金
