零知IDE——基于STM32F407VET6和雨滴传感器的多界面TFT降雨监测显示系统

✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!

✔访问零知实验室,获取更多实战项目和教程资源吧!

www.lingzhilab.com

目录

一、硬件连接部分

[1.1 硬件清单](#1.1 硬件清单)

[1.2 接线方案表](#1.2 接线方案表)

[1.3 具体接线图](#1.3 具体接线图)

[1.4 连接实物图](#1.4 连接实物图)

二、核心代码解析

[2.1 初始化设置](#2.1 初始化设置)

[2.2 数据读取与映射](#2.2 数据读取与映射)

[2.3 防闪烁更新机制](#2.3 防闪烁更新机制)

[2.4 条形图表绘制](#2.4 条形图表绘制)

[2.5 完整代码](#2.5 完整代码)

三、项目结果演示

[3.1 操作流程](#3.1 操作流程)

[3.2 界面展示](#3.2 界面展示)

[3.3 视频演示](#3.3 视频演示)

四、雨滴传感器工作原理

[4.1 基本原理](#4.1 基本原理)

[4.2 工作过程](#4.2 工作过程)

[4.3 模数转换原理](#4.3 模数转换原理)

五、常见问题解答

[Q1: 为什么雨量百分比显示不正确?](#Q1: 为什么雨量百分比显示不正确?)

[Q2: 如何调整报警阈值?](#Q2: 如何调整报警阈值?)

[Q3: 历史数据图表不更新怎么办?](#Q3: 历史数据图表不更新怎么办?)


(1)项目概述

本项目基于STM32F407VET6主控芯片,结合雨滴传感器和240×240分辨率ST7789 TFT显示屏,开发了一套智能降雨监测系统。系统能够实时监测降雨情况,通过三种不同的UI界面展示数据,具备数据可视化、历史趋势分析和报警功能。

(2)项目功能及亮点

功能描述:数据映射关系处理,实时雨量百分比监测与显示

系统亮点:正确处理雨滴传感器的电阻特性,实现从模拟值到百分比的合理映射

一、硬件连接部分

1.1 硬件清单

组件 规格 数量
主控板 STM32F407VET6 1
雨滴传感器 模拟输出型 1
TFT显示屏 ST7789 240×240 1
报警LED 5mm LED 1
连接线 杜邦线 若干

1.2 接线方案表

根据代码定义的引脚分配:

组件 引脚功能 零知增强板引脚
雨滴传感器 模拟输入 A1
报警LED 数字输出 9
ST7789 片选信号 53
ST7789 复位信号 6
ST7789 数据/命令 7
ST7789 SPI时钟 52
ST7789 SPI数据 51

1.3 具体接线图

1.4 连接实物图

二、核心代码解析

2.1 初始化设置

cpp 复制代码
void setup() {
      pinMode(RAIN_SENSOR, INPUT);
      pinMode(ALERT, OUTPUT);
      Serial.begin(9600);
      
      // 初始化显示屏
      tft.init(240, 240);
      tft.setRotation(1);
      tft.fillScreen(BACKGROUND_COLOR);
      tft.setTextWrap(false);
      
      // 显示启动画面
      showStartupScreen();
      delay(2500);
      
      // 绘制初始界面
      drawCurrentPage();
      
      Serial.println("Rain Sensor with TFT Display Started");
  }

tft.setRotation(1)设置屏幕方向为横屏、tft.setTextWrap(false)禁止文本自动换行

2.2 数据读取与映射

cpp 复制代码
void readSensorData() {
      rawValue = analogRead(RAIN_SENSOR) / 4; // 适配4096的模拟值
      // 反转映射关系:无雨时电阻大,模拟值大,我们希望无雨时百分比小
      sensorValue = map(rawValue, 0, 1023, 100, 0); // 反转映射
      
      // 保存历史数据
      historyValues[historyIndex] = sensorValue;
      historyIndex = (historyIndex + 1) % 15;
      
      // 设置报警状态(阈值可调整)
      alertState = (sensorValue > 70); // 大于70%认为有雨(因为映射已反转)
  }

模拟值除以4适配4096分辨率ADC、使用map()函数实现数值范围映射

2.3 防闪烁更新机制

cpp 复制代码
void updateCurrentPage() {
    // 只有在数据变化时才更新显示,避免闪烁
    if (sensorValue != lastSensorValue || alertState != lastAlertState || rawValue != lastRawValue) {
        switch(currentPage) {
            case 0: updatePage1(); break;
            case 1: updatePage2(); break;
            case 2: updatePage3(); break;
        }
        
        // 更新上一次的值
        lastSensorValue = sensorValue;
        lastAlertState = alertState;
        lastRawValue = rawValue;
    }
    
    // 页面2(图表页面)需要持续更新
    if (currentPage == 1 && millis() - lastChartUpdate > 1000) {
        updatePage2Chart();
        lastChartUpdate = millis();
    }
}

数据变化检测避免不必要刷新、图表页面单独处理,保证实时性、时间戳控制刷新频率

2.4 条形图表绘制

cpp 复制代码
void updatePage2Chart() {
    // 计算条形图参数(居中显示,增加间隔)
    int barCount = 12;
    int barWidth = 10;
    int barSpacing = 6;
    int totalWidth = barCount * barWidth + (barCount - 1) * barSpacing;
    int startX = 130 - totalWidth / 2; // 居中计算
    
    // 绘制条形图
    for (int i = 0; i < barCount; i++) {
        int valueIndex = (historyIndex - barCount + i + barCount) % barCount;
        int barHeight = map(historyValues[valueIndex], 0, 100, 0, chartHeight);
        
        // 选择颜色 - 使用渐变效果
        uint16_t barColor;
        if (historyValues[valueIndex] > 70) {
            barColor = WARNING_COLOR; // 红色报警
        } else if (historyValues[valueIndex] > 40) {
            barColor = ST77XX_ORANGE; // 橙色警告
        } else {
            barColor = BAR_COLOR; // 蓝色正常
        }
        
        tft.fillRect(x, y, barWidth, barHeight, barColor);
    }
}

渐变颜色指示不同状态、时间轴标签显示

2.5 完整代码

cpp 复制代码
  #include <Adafruit_GFX.h>
  #include <Adafruit_ST7789.h>
  #include <SPI.h>

  // 引脚定义
  #define ST77XX_DARKGREY 0x7453
  #define RAIN_SENSOR A1
  #define ALERT 9
  #define TFT_CS   53
  #define TFT_RST   6
  #define TFT_DC    7

  // 创建显示屏对象
  Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

  // 变量定义
  int sensorValue = 0;
  int rawValue = 0;
  bool alertState = false;
  unsigned long lastDisplayUpdate = 0;
  unsigned long lastSensorRead = 0;
  unsigned long lastChartUpdate = 0;
  int currentPage = 0;
  const int TOTAL_PAGES = 3;
  const int PAGE_DURATION = 10000; // 页面切换时间8秒

  // 颜色定义
  #define BACKGROUND_COLOR ST77XX_BLACK
  #define TEXT_COLOR ST77XX_WHITE
  #define WARNING_COLOR ST77XX_RED
  #define NORMAL_COLOR ST77XX_GREEN
  #define BAR_COLOR ST77XX_BLUE
  #define ACCENT_COLOR ST77XX_CYAN
  #define GRID_COLOR 0x4208 // 深灰色

  // 页面切换动画相关
  int animationOffset = 0;
  bool isAnimating = false;
  unsigned long animationStartTime = 0;

  // 历史数据记录
  int historyValues[15] = {0};
  int historyIndex = 0;

  // 防止闪烁的变量
  int lastSensorValue = -1;
  bool lastAlertState = false;
  int lastRawValue = -1;

  void setup() {
      pinMode(RAIN_SENSOR, INPUT);
      pinMode(ALERT, OUTPUT);
      Serial.begin(9600);
      
      // 初始化显示屏
      tft.init(240, 240);
      tft.setRotation(1);
      tft.fillScreen(BACKGROUND_COLOR);
      tft.setTextWrap(false);
      
      // 显示启动画面
      showStartupScreen();
      delay(2500);
      
      // 绘制初始界面
      drawCurrentPage();
      
      Serial.println("Rain Sensor with TFT Display Started");
  }

  void loop() {
      unsigned long currentTime = millis();
      
      // 每800ms读取一次传感器数据
      if (currentTime - lastSensorRead >= 800) {
          readSensorData();
          lastSensorRead = currentTime;
      }
      
      // 每8秒切换页面
      if (currentTime - lastDisplayUpdate >= PAGE_DURATION) {
          switchToNextPage();
          lastDisplayUpdate = currentTime;
      }
      
      // 处理页面切换动画
      if (isAnimating) {
          handlePageAnimation();
      } else {
          // 更新当前页面数据(局部刷新)
          updateCurrentPage();
      }
      
      // 控制报警LED
      digitalWrite(ALERT, alertState ? HIGH : LOW);
  }

  void readSensorData() {
      rawValue = analogRead(RAIN_SENSOR) / 4; // 适配4096的模拟值
      // 反转映射关系:无雨时电阻大,模拟值大,我们希望无雨时百分比小
      sensorValue = map(rawValue, 0, 1023, 100, 0); // 反转映射
      
      // 保存历史数据
      historyValues[historyIndex] = sensorValue;
      historyIndex = (historyIndex + 1) % 15;
      
      // 设置报警状态(阈值可调整)
      alertState = (sensorValue > 70); // 大于70%认为有雨(因为映射已反转)

      Serial.print("Raw: ");
      Serial.print(rawValue);
      Serial.print(" - Rain Level: ");
      Serial.print(sensorValue);
      Serial.print("% - Alert: ");
      Serial.println(alertState ? "ON" : "OFF");
  }

  void switchToNextPage() {
      currentPage = (currentPage + 1) % TOTAL_PAGES;
      isAnimating = true;
      animationOffset = 240; // 从右侧开始
      animationStartTime = millis();
      
      // 重置上一次的值,确保新页面完全绘制
      lastSensorValue = -1;
      lastAlertState = !alertState;
      lastRawValue = -1;
  }

  void handlePageAnimation() {
      unsigned long currentTime = millis();
      unsigned long elapsed = currentTime - animationStartTime;
      
      // 动画持续时间400ms
      if (elapsed < 400) {
          // 计算动画偏移量(缓动效果)
          float progress = (float)elapsed / 400.0;
          animationOffset = 240 - (int)(240 * easeOutCubic(progress));
          
          // 绘制动画帧
          drawPageAnimation();
      } else {
          isAnimating = false;
          animationOffset = 0;
          drawCurrentPage(); // 最终绘制完整页面
      }
  }

  float easeOutCubic(float x) {
      return 1 - pow(1 - x, 3);
  }

  void drawPageAnimation() {
      tft.fillScreen(BACKGROUND_COLOR);
      
      // 绘制新页面(带偏移)
      switch(currentPage) {
          case 0: drawPage1(animationOffset); break;
          case 1: drawPage2(animationOffset); break;
          case 2: drawPage3(animationOffset); break;
      }
  }

  void drawCurrentPage() {
      switch(currentPage) {
          case 0: drawPage1(0); break;
          case 1: drawPage2(0); break;
          case 2: drawPage3(0); break;
      }
  }

  void updateCurrentPage() {
      // 只有在数据变化时才更新显示,避免闪烁
      if (sensorValue != lastSensorValue || alertState != lastAlertState || rawValue != lastRawValue) {
          switch(currentPage) {
              case 0: updatePage1(); break;
              case 1: updatePage2(); break;
              case 2: updatePage3(); break;
          }
          
          // 更新上一次的值
          lastSensorValue = sensorValue;
          lastAlertState = alertState;
          lastRawValue = rawValue;
      }
      
      // 页面2(图表页面)需要持续更新
      if (currentPage == 1 && millis() - lastChartUpdate > 1000) {
          updatePage2Chart();
          lastChartUpdate = millis();
      }
  }

  void showStartupScreen() {
      tft.fillScreen(ST77XX_BLACK);
      
      // 主标题
      tft.setTextColor(ST77XX_GREEN);
      tft.setTextSize(3);
      tft.setCursor(25, 80);
      tft.println("RAIN SENSOR");
      
      // 副标题
      tft.setTextColor(ST77XX_WHITE);
      tft.setTextSize(2);
      tft.setCursor(85, 120);
      tft.println("SYSTEM");
      
      // 进度条 - 粗进度条
      tft.drawRoundRect(40, 160, 160, 20, 10, ST77XX_WHITE);
      for(int i = 0; i <= 160; i += 8) {
          tft.fillRoundRect(40, 160, i, 20, 10, ST77XX_BLUE);
          delay(30);
      }
  }

  // 页面1: 简洁数据展示
  void drawPage1(int offset) {
      tft.fillScreen(BACKGROUND_COLOR);
      
      // 标题 - 左上角
      tft.setTextColor(ACCENT_COLOR);
      tft.setTextSize(2);
      tft.setCursor(10 + offset, 10);
      tft.println("RAIN LEVEL");
      
      // 分隔线
      tft.drawFastHLine(10 + offset, 35, 220, GRID_COLOR);
      
      // 绘制静态内容
      drawPage1Static(offset);
  }

  void drawPage1Static(int offset) {
      // 状态指示标签
      tft.setTextColor(TEXT_COLOR);
      tft.setTextSize(2);
      tft.setCursor(20 + offset, 160);
      tft.print("Raw: ");
      
      // 页面指示
      tft.setCursor(80 + offset, 220);
      tft.print("Page 1/");
      tft.print(TOTAL_PAGES);
  }

  void updatePage1() {
      // 清除数据区域
      tft.fillRect(10, 50, 220, 100, BACKGROUND_COLOR);
      
      // 大字体显示百分比
      tft.setTextColor(sensorValue > 70 ? WARNING_COLOR : TEXT_COLOR);
      tft.setTextSize(4);
      tft.setCursor(85, 70);
      tft.print(sensorValue);
      tft.setTextSize(2);
      tft.println("%");
      
      // 状态指示
      tft.setTextSize(2);
      tft.setCursor(75, 120);
      if (sensorValue > 70) {
          tft.setTextColor(WARNING_COLOR);
          tft.println("RAINING!");
      } else if (sensorValue > 30) {
          tft.setTextColor(ST77XX_YELLOW);
          tft.println("CLOUDY");
      } else {
          tft.setTextColor(NORMAL_COLOR);
          tft.println("CLEAR");
      }
      
      // 原始值显示
      tft.setTextColor(ACCENT_COLOR);
      tft.fillRect(50, 150, 100, 30, BACKGROUND_COLOR);
      tft.setTextSize(2);
      tft.setCursor(20, 160);
      tft.print("Raw: ");
      tft.print(rawValue);
  }

  // 页面2: 现代化图表展示
  void drawPage2(int offset) {
      tft.fillScreen(BACKGROUND_COLOR);
      
      // 标题 - 左上角
      tft.setTextColor(ACCENT_COLOR);
      tft.setTextSize(2);
      tft.setCursor(10 + offset, 10);
      tft.println("RAIN HISTORY");
      
      // 分隔线
      tft.drawFastHLine(10 + offset, 35, 220, GRID_COLOR);
      
      // 绘制图表框架
      drawChartFrame(offset);
      
      // 绘制静态标签
      tft.setTextColor(TEXT_COLOR);
      tft.setTextSize(1);
      tft.setCursor(10 + offset, 200);
      tft.print("Time");
      
      // 页面指示
      tft.setTextSize(2);
      tft.setCursor(80 + offset, 220);
      tft.print("Page 2/");
      tft.print(TOTAL_PAGES);
  }

  void drawChartFrame(int offset) {
      // 绘制坐标轴
      tft.drawFastVLine(30 + offset, 50, 140, TEXT_COLOR); // Y轴
      tft.drawFastHLine(30 + offset, 190, 190, TEXT_COLOR); // X轴
      
      // Y轴标签
      tft.setTextColor(TEXT_COLOR);
      tft.setTextSize(1);
      tft.setCursor(5 + offset, 45);
      tft.print("100%");
      tft.setCursor(10 + offset, 90);
      tft.print("75%");
      tft.setCursor(10 + offset, 135);
      tft.print("50%");
      tft.setCursor(10 + offset, 180);
      tft.print("25%");
      
      // X轴箭头
      tft.drawLine(220 + offset, 190, 215 + offset, 185, TEXT_COLOR);
      tft.drawLine(220 + offset, 190, 215 + offset, 195, TEXT_COLOR);
      
      // Y轴箭头
      tft.drawLine(30 + offset, 50, 25 + offset, 55, TEXT_COLOR);
      tft.drawLine(30 + offset, 50, 35 + offset, 55, TEXT_COLOR);
  }

  void updatePage2() {
      // 更新当前值显示
      tft.fillRect(120, 205, 100, 12, BACKGROUND_COLOR);
      tft.setTextColor(ACCENT_COLOR);
      tft.setTextSize(1);
      tft.setCursor(120, 205);
      tft.print("Now: ");
      tft.print(sensorValue);
      tft.print("%");
  }

  void updatePage2Chart() {
      // 计算条形图参数(居中显示,增加间隔)
      int barCount = 12;
      int barWidth = 10;
      int barSpacing = 6;
      int totalWidth = barCount * barWidth + (barCount - 1) * barSpacing;
      int startX = 130 - totalWidth / 2; // 居中计算
      int chartBottom = 190;
      int chartHeight = 130;
      
      // 清除图表区域(只清除条形区域,保留坐标轴)
      tft.fillRect(startX, 60, totalWidth + 5, chartHeight, BACKGROUND_COLOR);
      
      // 绘制条形图
      for (int i = 0; i < barCount; i++) {
          int valueIndex = (historyIndex - barCount + i + barCount) % barCount;
          int barHeight = map(historyValues[valueIndex], 0, 100, 0, chartHeight);
          int x = startX + i * (barWidth + barSpacing);
          int y = chartBottom - barHeight;
          
          // 选择颜色 - 使用渐变效果
          uint16_t barColor;
          if (historyValues[valueIndex] > 70) {
              barColor = WARNING_COLOR;
          } else if (historyValues[valueIndex] > 40) {
              barColor = ST77XX_ORANGE;
          } else {
              barColor = BAR_COLOR;
          }
          
          // 绘制条形(带阴影效果)
          tft.fillRect(x, y, barWidth, barHeight, barColor);
          
          // 条形顶部边框
          tft.drawFastHLine(x, y, barWidth, TEXT_COLOR);
          
          // 数值标签(只显示较高的条形)
          if (barHeight > 20) {
              tft.setTextColor(TEXT_COLOR);
              tft.setTextSize(1);
              tft.setCursor(x + 1, y - 10);
              tft.print(historyValues[valueIndex]);
          }
          
          // 时间标签(底部)
          if (i % 3 == 0) {
              tft.setTextColor(GRID_COLOR);
              tft.setTextSize(1);
              tft.setCursor(x - 3, chartBottom + 5);
              tft.print("-");
              tft.print(barCount - i);
          }
      }
  }

  // 页面3: 详细信息
  void drawPage3(int offset) {
      tft.fillScreen(BACKGROUND_COLOR);
      
      // 标题 - 左上角
      tft.setTextColor(ACCENT_COLOR);
      tft.setTextSize(2);
      tft.setCursor(10 + offset, 10);
      tft.println("SENSOR INFO");
      
      // 分隔线
      tft.drawFastHLine(10 + offset, 35, 220, GRID_COLOR);
      
      // 绘制静态内容
      drawPage3Static(offset);
  }

  void drawPage3Static(int offset) {
      // 静态标签
      tft.setTextColor(TEXT_COLOR);
      tft.setTextSize(2);
      tft.setCursor(5 + offset, 60);
      tft.print("Rain Level");
      
      tft.setCursor(5 + offset, 90);
      tft.print("Raw Value: ");
      
      tft.setCursor(5 + offset, 120);
      tft.print("Alert: ");
      
      tft.setCursor(5 + offset, 150);
      tft.print("Threshold: ");
      
      // 映射说明
      tft.setTextColor(GRID_COLOR);
      tft.setTextSize(1);
      tft.setCursor(5 + offset, 180);
      tft.println("Dry=Low%, Wet=High%");
      
      // 页面指示
      tft.setTextColor(ACCENT_COLOR);
      tft.setTextSize(2);
      tft.setCursor(80 + offset, 220);
      tft.print("Page 3/");
      tft.print(TOTAL_PAGES);
  }

  void updatePage3() {
      // 更新雨量百分比
      tft.fillRect(130, 60, 80, 20, BACKGROUND_COLOR);
      tft.setTextColor(sensorValue > 70 ? WARNING_COLOR : NORMAL_COLOR);
      tft.setTextSize(2);
      tft.setCursor(130, 60);
      tft.print(sensorValue);
      tft.println("%");
      
      // 更新原始值
      tft.fillRect(130, 90, 100, 20, BACKGROUND_COLOR);
      tft.setTextColor(ACCENT_COLOR);
      tft.setCursor(130, 90);
      tft.print(rawValue);
      tft.println("/1023");
      
      // 更新报警状态
      tft.fillRect(130, 120, 100, 20, BACKGROUND_COLOR);
      if (alertState) {
          tft.setTextColor(WARNING_COLOR);
          tft.setCursor(130, 120);
          tft.println("ACTIVE");
      } else {
          tft.setTextColor(NORMAL_COLOR);
          tft.setCursor(130, 120);
          tft.println("INACTIVE");
      }
      
      // 更新阈值信息
      tft.fillRect(130, 150, 80, 20, BACKGROUND_COLOR);
      tft.setTextColor(TEXT_COLOR);
      tft.setCursor(130, 150);
      tft.println(">70%");
  }

数据展示思维导图

三、项目结果演示

3.1 操作流程

①系统启动显示启动界面

②自动进入主监测界面(页面1)

③每10秒自动切换至下一页面

历史雨量数据柱状统计图模拟界面设计,根据时间戳显示雨量大小

④页面1: 实时雨量百分比显示、页面2: 历史数据趋势图表、页面3: 详细传感器信息

⑤零知IDE串口打印输出

串口输出示例:

Rain Sensor with TFT Display Started

Raw: 245 - Rain Level: 76% - Alert: ON

3.2 界面展示

页面1: 大字体显示当前雨量百分比和状态指示

页面2: 条形柱状图展示历史数据趋势

页面3: 详细的传感器参数和报警状态

3.3 视频演示

雨滴传感器的多界面TFT降雨监测显示系统

系统从启动到运行的完整流程,包括三种界面的自动切换、模拟降雨检测、报警触发等关键功能

四、雨滴传感器工作原理

4.1 基本原理

雨滴传感器基于电阻变化原理工作。传感器表面有交错排列的导电线,当雨水落到表面时,水分的导电性会在导线之间形成电阻通路

模拟电压输出特性:模拟雨量增大之后,导通电阻降低,输出电压下降

4.2 工作过程

(1)干燥状态: 导线间电阻极大(兆欧级),输出高电压

(2)湿润状态: 水分形成导电通路,电阻显著降低,输出电压下降

(3)雨量检测: 通过ADC读取电压值,映射为雨量百分比,雨量阈值超过70%亮红灯报警

4.3 模数转换原理

零知增强板的STM32F407VET6主控芯片内置12位ADC,将0-3.3V模拟电压转换为0-4095数字值:

五、常见问题解答

Q1: 为什么雨量百分比显示不正确?

A: 检查雨滴传感器的接线:

确保模拟输入引脚正确。调整map()函数的参数以适应具体传感器特性

Q2: 如何调整报警阈值?

A: 修改代码:

调整alertState = (sensorValue > 70);的阈值数值,根据实际需求设置合适的报警点

Q3: 历史数据图表不更新怎么办?

A: 检查historyValues数组的索引管理:

确保新数据正确覆盖旧数据。验证updatePage2Chart()函数的调用频率

相关推荐
轩情吖3 小时前
Qt常用控件之QTextEdit
开发语言·c++·qt·信号·qtextedit·多行输入框·桌面级开发
zhmc3 小时前
MCU的取指周期与等待周期以及预取指令机制
单片机·嵌入式硬件
奔跑吧邓邓子3 小时前
【C++实战㊹】解锁C++装饰器模式:实战与技巧全解析
c++·实战·装饰器模式
休息一下接着来3 小时前
C++ 装饰器模式
c++·设计模式·装饰器模式
bkspiderx3 小时前
C++设计模式之结构型模式:装饰器模式(Decorator)
c++·设计模式·装饰器模式
凌盛羽4 小时前
将Gowin高云FPGA仿真库导入Modelsim中并编译
单片机·fpga开发·仿真·modelsim·gowin
一枝小雨4 小时前
STM32启动流程全面解析:从上电复位到进入main函数
stm32·单片机·嵌入式·bootloader·启动流程·启动代码·中断向量
big\hero4 小时前
STM32智能加湿器
stm32·单片机·嵌入式硬件
_OP_CHEN4 小时前
C++:(四)类和对象(中)—— 构造、析构与重载
开发语言·c++·类和对象·构造函数·析构函数·运算符重载·日期类