首页 > 其他分享 >基于STM32设计的太阳能热水器

基于STM32设计的太阳能热水器

时间:2023-08-05 11:01:08浏览次数:44  
标签:HAL temperature liquid level STM32 热水器 ADC GPIO 太阳能

一、概述

本项目使用 STM32F103C8T6 微控制器作为核心处理器,结合多个传感器和执行器,实现了太阳能热水器的自动控制。通过对光照、温度、水位等各种参数的监测和分析,对水泵、电磁阀等设备进行自动控制,从而实现太阳能热水器的高效、安全、可靠运行。

基于STM32设计的太阳能热水器_初始化

二、硬件设计

(1)模块组成

太阳能热水器模块主要由以下几个部分组成:

  • 光敏传感器模块:用于检测阳光强度,反映太阳辐射强度和方向。
  • 温度传感器模块:用于检测太阳能集热器表面和水箱内的温度,并根据温度变化调整水泵、电磁阀等设备的运行状态。
  • 液位传感器模块:用于检测水箱内的液位,并根据液位高低控制水泵和电磁阀的启停。
  • 水泵模块:通过控制水泵的启停,实现水循环流动和充水功能。
  • 电磁阀模块:通过控制电磁阀的开关,实现热水器的放水和接水功能。

(2)硬件连接

其中,光敏传感器模块、温度传感器模块和液位传感器模块通过 ADC 接口与 STM32F103C8T6 微控制器进行连接;水泵模块和电磁阀模块则通过 GPIO 口控制。

连接方式如下:

  • 光敏传感器模块:将光敏传感器输出口与 ADC1 通道10 连接,并用一个电位器调整 ADC 的参考电压,使其范围在 0-3.3V 之间。
  • 温度传感器模块:将 DS18B20 温度传感器数据线与 GPIOA 的 PA8 引脚连接,并将 VCC 和 GND 分别接到 3.3V 和 GND。
  • 液位传感器模块:将液位传感器输出口与 ADC1 通道11 连接,并用一个电位器调整 ADC 的参考电压。
  • 水泵模块:将水泵正极接到 GPIOB 的 PB1 引脚,将负极接到电源的负极。
  • 电磁阀模块:将电磁阀正极接到 GPIOB 的 PB0 引脚,将负极接到电源的负极。

三、软件设计

3.1 任务分配

整个项目采用 FreeRTOS 系统进行开发,实现数数的监测和控制,开发以下几个任务:

  • 光敏传感器任务:定时读取光敏传感器输出口的电压值,并进行数据处理,得到当前的光照强度。
  • 温度传感器任务:定时向 DS18B20 温度传感器发送温度采样请求,接收并解析响应数据,得到当前的太阳能集热器表面温度和水箱内温度。
  • 液位传感器任务:定时读取液位传感器输出口的电压值,并进行数据处理,得到当前的水箱水位高度。
  • 控制任务:根据光照强度、温度和水位高度等参数,决定是否需要启动水泵或电磁阀等设备。

伪代码如下:

void Light_Sensor_Task(void)
{
    while (1)
    {
        voltage = ADC_Get_Voltage(); // 获取光敏传感器输出电压
        light_intensity = voltage * 100 / 3.3f; // 根据电压计算光照强度
        vTaskDelay(1000); // 延时 1s
    }
}

void Temperature_Sensor_Task(void)
{
    while (1)
    {
        DS18B20_Start_Conversion(); // 向温度传感器发送采样请求
        temperature1 = DS18B20_Read_Temperature(); // 读取太阳能集热器表面温度
        temperature2 = DS18B20_Read_Temperature(); // 读取水箱内温度
        vTaskDelay(1000); // 延时 1s
    }
}

void Water_Level_Sensor_Task(void)
{
    while (1)
    {
        voltage = ADC_Get_Voltage(); // 获取液位传感器输出电压
        water_level = voltage * 100 / 3.3f; // 根据电压计算水位高度
        vTaskDelay(1000); // 延时 1s
    }
}

void Control_Task(void)
{
    while (1)
    {
        if (light_intensity > THRESHOLD && temperature1 > THRESHOLD && water_level > THRESHOLD) // 如果各种参数均符合要求,则启动水泵和电磁阀
        {
            GPIO_SetBits(GPIOB, GPIO_Pin_1); // 启动水泵
            GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 关闭电磁阀
        }
        else // 否则关闭水泵,打开电磁阀,放水
        {
            GPIO_ResetBits(GPIOB, GPIO_Pin_1); // 关闭水泵
            GPIO_SetBits(GPIOB, GPIO_Pin_0); // 启动电磁阀
        }
        vTaskDelay(1000); // 延时 1s
    }
}

3.2 光敏传感器任务

/* 光敏传感器任务 */
void Light_Sensor_Task(void *pvParameters)
{
  uint16_t adc_value;

  while (1)
  {
    /* 读取 ADC 值并计算光照强度 */
    if (HAL_ADC_Start(&hadc1) == HAL_OK)
    {
      if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
      {
        adc_value = HAL_ADC_GetValue(&hadc1);
        light_intensity = adc_value * 3300 / 4096.0;
      }
    }

    vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1s
  }
}

在函数中,声明一个变量 adc_value 用于存储读取到的 ADC 值。使用 if 条件语句检查 ADC 是否成功启动,并且使用 HAL_ADC_PollForConversion() 函数判断当前转换是否完成,如果转换完成,就获取 ADC 值,并且通过简单的计算公式将 ADC 值转换为光照强度值,最后将结果存储在 light_intensity 变量中。

3.3 温度传感器任务

/* 温度传感器任务 */
void Temperature_Sensor_Task(void *pvParameters)
{
  float temperature;

  /* 初始化 DS18B20 */
  DS18B20_Init(&htim2, GPIOA, GPIO_PIN_10);

  while (1)
  {
    /* 读取温度值 */
    temperature = DS18B20_Read_Temperature();

    /* 将读取到的温度值存储在全局变量中 */
    current_temperature = temperature;

    vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1s
  }
}

在函数中,声明一个变量 temperature 用于存储读取到的温度值。然后,调用函数 DS18B20_Init() 初始化 DS18B20 温度传感器。使用 DS18B20_Read_Temperature() 函数读取温度值,并且将结果存储在 temperature 变量中。最后,将读取到的温度值存储在全局变量 current_temperature 中。

3.4 液位传感器任务

/* 液位传感器任务 */
void Liquid_Level_Sensor_Task(void *pvParameters)
{
  uint16_t adc_value;
  float voltage;

  /* 初始化液位传感器 GPIO 口 */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);

  while (1)
  {
    /* 读取 ADC 值并计算电压值 */
    if (HAL_ADC_Start(&hadc1) == HAL_OK)
    {
      if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
      {
        adc_value = HAL_ADC_GetValue(&hadc1);
        voltage = adc_value * 3.3 / 4096.0;
      }
    }

    /* 根据电压值计算液位高度 */
    if (voltage < 0.5)
    {
      liquid_level = 0.0;
    }
    else if (voltage > 2.5)
    {
      liquid_level = 100.0;
    }
    else
    {
      liquid_level = (voltage - 0.5) * 100.0 / 2.0;
    }

    vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1s
  }
}

在函数中,声明变量 adc_valuevoltage,分别用于存储读取到的 ADC 值和计算得到的电压值。使用 HAL_GPIO_WritePin() 函数初始化液位传感器 GPIO 口,将启用传感器的引脚设置为高电平。使用 if 条件语句检查 ADC 是否成功启动,并且使用 HAL_ADC_PollForConversion() 函数判断当前转换是否完成,如果转换完成,就获取 ADC 值,并且通过简单的计算公式将 ADC 值转换为电压值,并将结果存储在 voltage 变量中。

由于需要使用电压值计算液位高度,使用 if 条件语句检查电压是否小于低液位警戒电压 0.5V 或者大于高液位警戒电压 2.5V,如果是则分别将液位高度设置为 0% 或 100%,否则使用简单的线性关系计算液位高度。

3.5 控制任务

/* 控制任务 */
void Control_Task(void *pvParameters)
{
  float temperature_setpoint = 25.0; // 设定温度值
  float liquid_level_setpoint = 50.0;  // 设定液位高度值
  float temperature_error, liquid_level_error;
  float temperature_integral, liquid_level_integral;
  float temperature_derivative, liquid_level_derivative;
  float temperature_output, liquid_level_output;

  float kp_temperature = 0.5, ki_temperature = 0.1, kd_temperature = 0.05; // 温度 PID 参数
  float kp_liquid_level = 0.2, ki_liquid_level = 0.05, kd_liquid_level = 0.02; // 液位高度 PID 参数

  while (1)
  {
    /* 计算温度 PID 控制器输出 */
    temperature_error = temperature_setpoint - current_temperature;
    temperature_integral += temperature_error;
    temperature_derivative = temperature_error - last_temperature_error;
    temperature_output = kp_temperature * temperature_error + ki_temperature * temperature_integral + kd_temperature * temperature_derivative;
    last_temperature_error = temperature_error;

    /* 计算液位高度 PID 控制器输出 */
    liquid_level_error = liquid_level_setpoint - liquid_level;
    liquid_level_integral += liquid_level_error;
    liquid_level_derivative = liquid_level_error - last_liquid_level_error;
    liquid_level_output = kp_liquid_level * liquid_level_error + ki_liquid_level * liquid_level_integral + kd_liquid_level * liquid_level_derivative;
    last_liquid_level_error = liquid_level_error;

    /* 通过 PWM 控制加热器和水泵电机 */
    if (temperature_output > 0.0)
    {
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
      __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, (uint16_t)(temperature_output * 1000));
    }
    else
    {
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
      __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0);
    }

    if (liquid_level_output > 0.0)
    {
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
      __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (uint16_t)(liquid_level_output * 1000));
    }
    else
    {
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
      __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0);
    }

    vTaskDelay(pdMS_TO_TICKS(10)); // 延时 10ms
  }
}

在函数中:

(1)定义参数和变量,包括设定温度值、设定液位高度值、温度 PID 控制器的参数、液位高度 PID 控制器的参数等。使用 while 循环处理控制逻辑,循环开始时,计算温度 PID 控制器输出。

(2)计算当前误差,并将误差累积到积分项中。计算误差变化率,并使用 PID 参数计算出输出值,将结果存储在 temperature_output 中,并将当前误差存储在 last_temperature_error 中以便于下一次计算,计算液位高度 PID 控制器输出。

(3)根据控制器输出值通过 PWM 控制加热器和水泵电机的运行状态。如果输出值大于 0,则启用电机或加热器并设置对应的 PWM 占空比,否则关闭电机或加热器并将 PWM 占空比设为 0。

标签:HAL,temperature,liquid,level,STM32,热水器,ADC,GPIO,太阳能
From: https://blog.51cto.com/u_11822586/6974099

相关文章

  • STM32案例学习 GY-39环境监测传感器模块
    STM32案例学习GY-39环境监测传感器模块硬件平台野火STM32F1系列开发板正点STM32F1系列开发板STM32F103ZET6核心板GY-39环境监测传感器模块GY-39环境监测传感器模块GY-39是一款低成本,气压,温湿度,光强度传感器模块。工作电压3-5v,功耗小,安装方便。其工作原理是,MCU收集各种传感器......
  • STM32中SWD下载不进去的解决方法
    这是我第一次写自己的博客,希望以后写博客可以当做自己的个人习惯并坚持下去,作为技术分享,也欢迎各位大佬前来指正。本人本科学习的机械电子工程,了解机械制图、嵌入式编程、目前刚好学习了PCB制板,正在向着全栈工程师的目标进发(自嘲一波,各位见谅,哈哈)惨案现场以及经过第一次绘制PCB,......
  • STM32学习笔记
    目录时钟配置时钟配置时钟树系统时钟倍频到168MHzvoidSystemClock_Config(void){RCC_ClkInitTypeDefRCC_ClkInitStruct;RCC_OscInitTypeDefRCC_OscInitStruct;/*EnablePowerControlclock*/__HAL_RCC_PWR_CLK_ENABLE();/*Thevoltage......
  • iTOP-STM32MP157开发板一键烧写 QT 程序到开发板
    1根据上一小节设置好编译套件后,打开自己的qt工程,然后点击qtcreator里面的项目,把编译器切换成上一章节设置好的的编译器,如下图所示:2然后打开要编译的QT代码的pro文件,在里面添加以下代码,这俩行代码的意思是说把编译的可执行程序下载到开发板的/opt目录下并执行。target.pa......
  • STM32笔记
    STM32笔记SWD连接开发板什么是SWDSWD与JTAG同属调试接口,是芯片在设计之初就预制的对芯片进行开发调试并在开发板上预留的接口,JTAG接口目前开发板上的接口大多是20PIN的,与此对应的关系如下:DAPLink在JTAG接口连接只用连接3根线SWD、SWCLK、GND。对应关系为连接时要注意将......
  • stm32串口USART 硬件流控(转载)
    尊重原创,分享学习,内容来源:stm32串口USART硬件流控--学习笔记-国产零零柒-博客园(cnblogs.com)    流控的概念源于RS232这个标准,在RS232标准里面包含了串口、流控的定义。大家一定了解,RS232中的“RS”是RecommendStandard的缩写,即”推荐标准“之意,它并不像......
  • STM32 cubemx配置USART DMA传输
    (文章目录)前言本篇文章来讲解DMA的概念,并使用DMA来进行串口的数据收发。一、DMA概念DMA(DirectMemoryAccess,直接内存访问)是一种计算机系统的技术,允许外部设备(如硬盘驱动器、网络适配器或图形卡)直接与计算机内存进行数据传输,而不需要CPU的直接参与。这种直接的内存访问可以提......
  • 基于STM32设计的人体健康检测仪
    一、项目介绍当前文章介绍基于STM32设计的人体健康检测仪。设备采用STM32系列MCU作为主控芯片,配备血氧浓度传感器(使用MAX30102血氧浓度检测传感器)、OLED屏幕和电池供电等外设模块。设备可以广泛应用于医疗、健康等领域。可以帮助医生和病人更好地了解病情变化,提高治疗效果和生活质......
  • STM32采用主从计时器实现精确脉冲输出
         首先按前面所述的主从计时器要求配置好主从计时器,这是最基本的要求。主计时器负责设置脉冲输出的频率以及输出脉冲,从计数器所控制输出的脉冲数。具体过程是这样的,主进程启动主从计时器,从计时器通过主计时器输出的触发信号开始脉冲计数,当达到指定的计数值后,产生中......
  • stm32CubeIDE 串口UART+DMA+空闲中断接收不定长数据
    一、概述串口使用时,有时候会有接收不定长数据的需求,这时候用DMA+空闲中断的方式是最好的方法。二、cubeide的配置串口按照需求配置后,添加一个串口接收的DMA,如下图 三、代码编写部分1、在串口init函数 MX_UART4_Init(void)的末尾用户代码区域添加使能空闲中......