第一个项目——智能垃圾桶

1. 项目背景与目标

本项目旨在使用 STM32F103C8T6 最小系统板,结合 超声波模块(HC-SR04)舵机按键OLED 显示屏,设计一个智能垃圾桶系统。该系统能够实现自动感知距离,并根据超声波测距值控制舵机开关,同时支持按键触发操作。系统还可以在 OLED 屏幕上显示实时的测距信息。

本项目的设计重点是:

  • 综合训练 定时器中断输入捕获PWM控制
  • 训练配置 外部中断(按键触发),并设置中断优先级。
  • 使用 OLED 显示屏输出调试信息,提供距离反馈。

2. 硬件选型与连接

2.1 硬件选型

  • 主控板:STM32F103C8T6 最小系统板
  • 显示模块:0.96 寸 OLED 显示屏
  • 超声波模块:HC-SR04,用于测量距离
  • 舵机:用于控制垃圾桶的开关
  • 按键:用于手动触发操作
  • 电路搭建:使用面包板进行电路搭建

2.2 硬件连接

OLED 屏幕 (0.96寸)

  • SCL(时钟)PB8
  • SDA(数据)PB9
  • GND(地) → 直接连接到电源地(PB6 暂空)
  • VCC(供电) → 直接连接到电源正极(PB7 暂空)

超声波测距模块 (HC-SR04)

  • VCC(供电)5V(从 ST-LINK 单独引出)
  • GND(地)GND
  • Trig(触发)PA11(推挽输出,同时用于 TIM1通道4 间接下降沿输入捕获)
  • Echo(回声)PA10(设置为 TIM1通道3 上升沿输入捕获)

舵机 (SG90)

  • VCC(供电)5V(与超声波模块共用 ST-LINK 5V 供电)
  • GND(地)GND
  • PWM信号未指定,请补充连接引脚信息

按键

  • 信号输入PB1(上拉输入)
  • GND(地)GND

3. 软件设计

3.1 使用 STM32CubeMX 配置

在 STM32CubeMX 中进行以下配置:

  • 定时器 1
    • 通道3 (PA10, Echo)上升沿输入捕获,启用 上升沿捕获中断
    • 通道4 (PA11, Trig)间接下降沿输入捕获
    • 时钟源Internal Clock
    • 预分频系数72 - 1(即 1MHz 计数频率,每 1us 计数+1)
    • 自动重装载值65535(最大计数时间 65.535ms,足够测 11m 以内的距离)
  • 定时器 2
    • 时钟源Internal Clock
    • 通道2 (PA1)PWM 生成预分频系数720 - 1(即 72MHz / 720 = 100kHz 计数频率,每 10µs 计数+1)
    • 向上计数模式自动重装载值(ARR)2000 - 1(周期 20000 µs,即 20ms,符合舵机信号标准)
  • 外部中断
    • 配置按键连接的 PB1 引脚为外部中断源,设置中断优先级。

3.2 主要功能实现

3.2.1 超声波测距与舵机控制

  • 使用定时器 1 的输入捕获功能来测量超声波回波的时延,从而计算物体的距离。
  • 当物体距离小于 5 cm 时,舵机会旋转至 40° 位置,同时 LED 灯亮起。
  • 在 2 秒后,舵机恢复至 0° 位置,LED 灯熄灭。
  • 用户按键触发时,舵机也会旋转至 40°,并点亮 LED。

3.2.2 中断管理与优先级

  • 超声波测距的输入捕获中断:设置定时器 1 的输入捕获中断,以便及时捕获回声信号并计算距离。
  • 按键中断:使用外部中断管理按键触发操作,按键触发时会优先处理,舵机旋转并点亮 LED。

3.3 代码解析

3.3.1 定时器 1 输入捕获中断

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  if(htim == &htim1 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4) {
    RisingEdgeTime = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
    FallingEdgeTime = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_4);
    Distance = (FallingEdgeTime - RisingEdgeTime) * 0.034 / 2;
  }
}

该函数用于捕获上升沿和下降沿的时间,功能是通过定时器输入捕获中断计算超声波测距。代码解释如下:

  • 判断是否为定时器 1 的捕获通道 4(即超声波模块的 Trig 触发口)触发的中断。
  • 读取上升沿和下降沿的计数值(通过定时器输入捕获功能获取)。
  • 计算距离
    距离公式为:$\text{Distance} = \frac{(\text{FallingEdgeTime} – \text{RisingEdgeTime}) \times 0.034}{2}$
  • 这里 0.034 是声音在空气中的传播速度(单位:厘米/微秒),除以 2 是因为超声波的传播是来回的。

3.3.2 外部中断处理(按键)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == Key1_Pin) {
    key1_flag = 1;
  }
}

判断是否为 PB1 引脚的中断(即按键触发的中断)。如果是,设置 key1_flag 为 1,表示按键已被触发,主程序可以根据此标志位进行进一步的操作。

3.3.3 PWM 控制舵机

__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 94);

通过定时器 2 设置 PWM 波形的占空比来控制舵机的旋转角度。

3.4 中断优先级设置

HAL_NVIC_SetPriority(TIM1_CC_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);

为定时器 1 捕获中断和按键中断设置了不同的优先级,确保按键事件能够高优先级响应。

4. 主函数逻辑

4.1 主函数代码

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  OLED_Init();

  HAL_TIM_Base_Start(&htim1); // 开启定时器1
  HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_3); // 开启通道3输入捕获 上升沿
  HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_4); // 开启通道4输入捕获中断,下降沿
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); // 开启定时器2的PWM输出

  OLED_ShowString(2, 1, "Distance:");
  OLED_ShowString(2, 15, "cm");

  while (1)
  {
    HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_SET); // 拉高触发引脚
    HAL_Delay(1);
    HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_RESET); // 拉低触发引脚
    __HAL_TIM_SET_COUNTER(&htim1, 0); // 清零定时器1计数器
    HAL_Delay(20);

    OLED_ShowNum(2, 11, (uint32_t)Distance, 3); // 显示距离,最大3位
    OLED_ShowString(2, 15, "cm");

    /* 1. 按键触发 (最高优先级) */
    if (key1_flag)
    {
        key1_flag = 0; // 清除标志位

        /* 舵机旋转到 40° */
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 94);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 点亮 LED
        HAL_Delay(2000);

        /* 舵机旋转回 0° */
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 50);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 熄灭 LED
        HAL_Delay(2000);
    }
    /* 2. 超声波触发 (低优先级) */
    else if (Distance < 5 && servo_state == 0)
    {
        servo_state = 1;
        servo_timer = HAL_GetTick(); // 记录触发时间

        /* 舵机旋转到 40° */
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 94);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 点亮 LED
    }
    else if (servo_state == 1 && (HAL_GetTick() - servo_timer >= 2000))
    {
        servo_state = 0;

        /* 舵机旋转回 0° */
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 50);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 熄灭 LED
    }

    HAL_Delay(500);
}

主函数中的初始化与启动

  • HAL_Delay(20): 延时 20ms,确保硬件初始化完成。
  • OLED_Init(): 初始化 OLED 显示屏。
  • HAL_TIM_Base_Start(&htim1): 启动定时器 1,用于超声波测距的输入捕获。
  • HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_3): 启动定时器 1 的 通道 3,用于捕获超声波回声的上升沿。
  • HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_4): 启动定时器 1 的通道 4,并使能输入捕获中断,用于捕获超声波回声的下降沿。
  • HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2): 启动定时器 2 的通道 2,输出 PWM 信号控制舵机。

主循环功能

  1. OLED 显示:
    • 显示“Distance: ”和“cm”提示信息。
    • 显示当前计算的距离值,单位为厘米。
  2. 超声波触发
    • Trig 引脚拉高 1ms,然后拉低,触发超声波模块发射脉冲。
    • 清除定时器计数器,确保每次测距从零开始。
  3. 按键触发(优先级最高):
    • 如果 key1_flag 为 1,表示按键被触发,舵机旋转到 40°,同时点亮 LED。
    • 舵机旋转后等待 2 秒,然后回到初始位置(0°)并熄灭 LED。
  4. 超声波触发条件(低优先级):
    • 如果测得的距离小于 5cm 且舵机状态为默认(servo_state == 0),舵机旋转到 40°,并点亮 LED。
    • 如果舵机已旋转并且触发时间超过 2 秒,舵机回到 0° 并熄灭 LED。
  5. 延时
    • 每次循环结束后延时 500ms,避免过于频繁的测量和操作。

总结

主循环负责以下任务:

  • 控制 OLED 显示距离。
  • 触发超声波测距。
  • 响应按键操作和距离变化,控制舵机旋转,并控制 LED 状态。

5. 实验现象

上一篇
下一篇