云计算百科
云计算领域专业知识百科平台

场景实战:基于STM32的汽车车窗自动防夹与雨滴感应控制解决方案解析

文章目录

    • 一、方案整体设计与原理说明
      • 1.1 核心原理
      • 1.2 整体架构流程图
    • 二、硬件选型与接线
      • 2.1 核心硬件清单
      • 2.2 详细接线说明
        • (1)STM32与L298N驱动模块接线
        • (2)L298N与车窗电机接线
        • (3)霍尔传感器与STM32接线
        • (4)雨滴传感器与STM32接线
    • 三、软件开发环境搭建
      • 3.1 环境准备
      • 3.2 STM32CubeMX初始化配置步骤
        • 步骤1:新建工程
        • 步骤2:配置RCC时钟
        • 步骤3:配置GPIO引脚
        • 步骤4:配置ADC
        • 步骤5:配置定时器
        • 步骤6:生成代码
    • 四、核心代码编写
      • 4.1 头文件与全局变量定义(main.h)
      • 4.2 外设初始化代码(main.c)
      • 4.3 核心功能函数(main.c续)
    • 五、代码烧录与调试
      • 5.1 代码编译与烧录
      • 5.2 硬件调试步骤
        • 步骤1:电源调试
        • 步骤2:雨滴传感器调试
        • 步骤3:霍尔传感器调试
        • 步骤4:防夹功能调试
      • 5.3 常见问题与解决
    • 六、功能扩展建议
    • 总结

一、方案整体设计与原理说明

你需要实现的是基于STM32的汽车车窗控制系统,核心包含两大功能:车窗自动防夹和雨滴感应自动关窗。本方案以STM32F103C8T6最小系统板为核心控制器,通过霍尔传感器检测车窗电机转速实现防夹判断,通过雨滴传感器检测雨量触发自动关窗,同时配合直流电机驱动模块控制车窗升降,全程采用模块化编程思路,确保零基础小白也能一步步落地实现。

1.1 核心原理

  • 自动防夹原理:车窗升降过程中,霍尔传感器实时采集电机转动的脉冲信号,当车窗遇到障碍物时,电机转速骤降,脉冲频率低于设定阈值,控制器立即反转电机,实现防夹保护。
  • 雨滴感应原理:雨滴传感器通过检测玻璃表面的湿度变化输出模拟电压信号,STM32的ADC采集该信号,当电压低于设定阈值(检测到雨滴),控制器触发车窗自动关闭流程。
  • 硬件控制逻辑:STM32通过GPIO口控制L298N电机驱动模块,实现车窗电机的正转(升窗)、反转(降窗)、停止;通过外部中断或定时器捕获霍尔传感器脉冲;通过ADC通道采集雨滴传感器信号。

1.2 整体架构流程图

以下是系统整体工作流程的Mermaid流程图,采用深色底、白色字体,排版美观且逻辑清晰:

#mermaid-svg-WLpkJmpHA5WKywpi{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WLpkJmpHA5WKywpi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WLpkJmpHA5WKywpi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WLpkJmpHA5WKywpi .error-icon{fill:#552222;}#mermaid-svg-WLpkJmpHA5WKywpi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WLpkJmpHA5WKywpi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WLpkJmpHA5WKywpi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WLpkJmpHA5WKywpi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WLpkJmpHA5WKywpi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WLpkJmpHA5WKywpi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WLpkJmpHA5WKywpi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WLpkJmpHA5WKywpi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WLpkJmpHA5WKywpi .marker.cross{stroke:#333333;}#mermaid-svg-WLpkJmpHA5WKywpi svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WLpkJmpHA5WKywpi p{margin:0;}#mermaid-svg-WLpkJmpHA5WKywpi .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-WLpkJmpHA5WKywpi .cluster-label text{fill:#333;}#mermaid-svg-WLpkJmpHA5WKywpi .cluster-label span{color:#333;}#mermaid-svg-WLpkJmpHA5WKywpi .cluster-label span p{background-color:transparent;}#mermaid-svg-WLpkJmpHA5WKywpi .label text,#mermaid-svg-WLpkJmpHA5WKywpi span{fill:#333;color:#333;}#mermaid-svg-WLpkJmpHA5WKywpi .node rect,#mermaid-svg-WLpkJmpHA5WKywpi .node circle,#mermaid-svg-WLpkJmpHA5WKywpi .node ellipse,#mermaid-svg-WLpkJmpHA5WKywpi .node polygon,#mermaid-svg-WLpkJmpHA5WKywpi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WLpkJmpHA5WKywpi .rough-node .label text,#mermaid-svg-WLpkJmpHA5WKywpi .node .label text,#mermaid-svg-WLpkJmpHA5WKywpi .image-shape .label,#mermaid-svg-WLpkJmpHA5WKywpi .icon-shape .label{text-anchor:middle;}#mermaid-svg-WLpkJmpHA5WKywpi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WLpkJmpHA5WKywpi .rough-node .label,#mermaid-svg-WLpkJmpHA5WKywpi .node .label,#mermaid-svg-WLpkJmpHA5WKywpi .image-shape .label,#mermaid-svg-WLpkJmpHA5WKywpi .icon-shape .label{text-align:center;}#mermaid-svg-WLpkJmpHA5WKywpi .node.clickable{cursor:pointer;}#mermaid-svg-WLpkJmpHA5WKywpi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WLpkJmpHA5WKywpi .arrowheadPath{fill:#333333;}#mermaid-svg-WLpkJmpHA5WKywpi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WLpkJmpHA5WKywpi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WLpkJmpHA5WKywpi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WLpkJmpHA5WKywpi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WLpkJmpHA5WKywpi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WLpkJmpHA5WKywpi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WLpkJmpHA5WKywpi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WLpkJmpHA5WKywpi .cluster text{fill:#333;}#mermaid-svg-WLpkJmpHA5WKywpi .cluster span{color:#333;}#mermaid-svg-WLpkJmpHA5WKywpi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WLpkJmpHA5WKywpi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WLpkJmpHA5WKywpi rect.text{fill:none;stroke-width:0;}#mermaid-svg-WLpkJmpHA5WKywpi .icon-shape,#mermaid-svg-WLpkJmpHA5WKywpi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WLpkJmpHA5WKywpi .icon-shape p,#mermaid-svg-WLpkJmpHA5WKywpi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WLpkJmpHA5WKywpi .icon-shape rect,#mermaid-svg-WLpkJmpHA5WKywpi .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WLpkJmpHA5WKywpi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WLpkJmpHA5WKywpi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WLpkJmpHA5WKywpi :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-WLpkJmpHA5WKywpi .darkStyle>*{fill:#2c3e50!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .darkStyle span{fill:#2c3e50!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .darkStyle tspan{fill:#ffffff!important;}#mermaid-svg-WLpkJmpHA5WKywpi .startEnd>*{fill:#e74c3c!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .startEnd span{fill:#e74c3c!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .startEnd tspan{fill:#ffffff!important;}#mermaid-svg-WLpkJmpHA5WKywpi .decision>*{fill:#3498db!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .decision span{fill:#3498db!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .decision tspan{fill:#ffffff!important;}#mermaid-svg-WLpkJmpHA5WKywpi .process>*{fill:#27ae60!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .process span{fill:#27ae60!important;stroke:#ffffff!important;stroke-width:2px!important;color:#ffffff!important;fontSize:14px!important;fontFamily:Arial!important;}#mermaid-svg-WLpkJmpHA5WKywpi .process tspan{fill:#ffffff!important;}

系统上电初始化

GPIO/ADC/定时器配置

读取雨滴传感器信号

检测到雨滴?

触发自动关窗指令

检测车窗控制指令

升窗指令?

控制电机正转升窗

降窗指令?

控制电机反转降窗

霍尔传感器采集脉冲

脉冲频率低于阈值?

立即停止电机+反转0.5秒

车窗到位?

停止电机

二、硬件选型与接线

2.1 核心硬件清单

硬件名称型号/规格数量作用
STM32控制器 STM32F103C8T6最小系统板 1 核心控制单元
直流电机驱动模块 L298N 1 驱动车窗升降电机
车窗直流电机 12V 汽车车窗专用电机 1 执行车窗升降
霍尔传感器模块 3144霍尔开关 1 检测电机转速
雨滴传感器模块 模拟量输出型 1 检测雨量
电源模块 12V转5V/3.3V 1 给控制器和传感器供电
杜邦线/面包板 公对公/公对母 若干 硬件接线
12V电源适配器 12V 2A 1 给电机和驱动模块供电

2.2 详细接线说明

(1)STM32与L298N驱动模块接线
STM32引脚L298N引脚说明
PB0 IN1 控制电机正反转(升窗)
PB1 IN2 控制电机正反转(降窗)
PB2 ENA 电机使能(接PWM,控制转速)
5V 12V供电端 仅给L298N逻辑电路供电
GND GND 共地
(2)L298N与车窗电机接线
L298N引脚车窗电机引脚说明
OUT1 电机引脚1 电机正转/反转
OUT2 电机引脚2 电机正转/反转
12V 12V电源正极 电机供电
GND 12V电源负极 共地
(3)霍尔传感器与STM32接线
霍尔传感器引脚STM32引脚说明
VCC 3.3V 传感器供电
GND GND 共地
OUT PA0 脉冲信号输出(接定时器通道)
(4)雨滴传感器与STM32接线
雨滴传感器引脚STM32引脚说明
VCC 5V 传感器供电
GND GND 共地
AO PA1 模拟信号输出(接ADC通道)

三、软件开发环境搭建

3.1 环境准备

  • 下载并安装STM32CubeMX(版本6.0以上):用于图形化配置STM32外设,生成初始化代码。
  • 下载并安装Keil MDK-ARM(版本5.36以上):用于编写、编译和下载代码到STM32。
  • 安装STM32F103C8T6的器件库:在Keil中添加对应的Device Pack。
  • 准备ST-Link下载器:用于将编译后的代码烧录到STM32最小系统板。
  • 3.2 STM32CubeMX初始化配置步骤

    步骤1:新建工程
    • 打开STM32CubeMX,点击“New Project”,搜索“STM32F103C8T6”,选择对应型号后点击“Start Project”。
    • 弹出的“Project Manager”界面中,暂不修改,点击“OK”。
    步骤2:配置RCC时钟
    • 左侧菜单栏选择“RCC”,在“High Speed Clock (HSE)”中选择“Crystal/Ceramic Resonator”(外部晶振)。
    • 点击顶部“Clock Configuration”,将系统时钟配置为72MHz(HSE=8MHz,PLL倍频9倍)。
    步骤3:配置GPIO引脚
    • 左侧菜单栏选择“GPIO”:
      • PB0:设置为“Output Push Pull”(推挽输出),命名为“IN1”;
      • PB1:设置为“Output Push Pull”(推挽输出),命名为“IN2”;
      • PB2:设置为“Alternate Function Push Pull”(复用推挽输出),用于PWM输出,命名为“ENA”;
      • 其余GPIO默认配置。
    步骤4:配置ADC
    • 左侧菜单栏选择“ADC1”:
      • 模式选择“Independent Mode”(独立模式);
      • 通道配置:PA1设置为“IN1”,采样时间选择“239.5 Cycles”(提高采样精度);
      • 开启ADC连续转换模式。
    步骤5:配置定时器
    • 左侧菜单栏选择“TIM2”:
      • 模式选择“Counter Mode”为“Up Counter”;
      • 预分频器(Prescaler)设置为71(系统时钟72MHz,分频后1MHz);
      • 自动重装值(ARR)设置为65535;
      • 通道1(PA0)设置为“Input Capture direct mode”(输入捕获模式),用于捕获霍尔传感器脉冲。
    步骤6:生成代码
    • 点击顶部“Project Manager”:
      • 设置项目名称(如“Window_Control_System”);
      • 选择项目路径(非中文路径);
      • 工具链/IDE选择“MDK-ARM”,版本选择“V5”;
      • 点击“Code Generator”,勾选“Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral”;
      • 点击“Generate Code”,生成初始化代码后用Keil MDK打开。

    四、核心代码编写

    4.1 头文件与全局变量定义(main.h)

    #ifndef __MAIN_H
    #define __MAIN_H

    #include "stm32f1xx_hal.h"

    /* 引脚定义 */
    #define IN1_PIN GPIO_PIN_0
    #define IN1_GPIO_PORT GPIOB
    #define IN2_PIN GPIO_PIN_1
    #define IN2_GPIO_PORT GPIOB
    #define ENA_PIN GPIO_PIN_2
    #define ENA_GPIO_PORT GPIOB

    /* 阈值定义 */
    #define RAIN_THRESHOLD 1500 // 雨滴传感器阈值(ADC值,越小雨量越大)
    #define PULSE_THRESHOLD 50 // 防夹脉冲频率阈值(Hz)
    #define WINDOW_POSITION_PULSE 200 // 车窗到位脉冲数

    /* 全局变量 */
    extern uint32_t hall_pulse_count; // 霍尔脉冲计数
    extern uint32_t pulse_frequency; // 脉冲频率
    extern uint16_t rain_adc_value; // 雨滴传感器ADC值
    extern uint8_t window_status; // 车窗状态:0-停止,1-升窗,2-降窗

    /* 函数声明 */
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_ADC1_Init(void);
    static void MX_TIM2_Init(void);
    void Motor_Control(uint8_t cmd); // 电机控制函数
    void Hall_Pulse_Capture(void); // 霍尔脉冲捕获处理
    void Rain_Detect(void); // 雨滴检测函数
    void Anti_Pinch_Check(void); // 防夹检测函数

    #endif /* __MAIN_H */

    4.2 外设初始化代码(main.c)

    #include "main.h"

    /* 全局变量定义 */
    uint32_t hall_pulse_count = 0;
    uint32_t pulse_frequency = 0;
    uint16_t rain_adc_value = 0;
    uint8_t window_status = 0;

    /* TIM2句柄 */
    TIM_HandleTypeDef htim2;
    /* ADC句柄 */
    ADC_HandleTypeDef hadc1;

    /* 系统时钟配置 */
    void SystemClock_Config(void)
    {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

    /* 配置外部晶振HSE */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    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();
    }

    /* 配置系统时钟 */
    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();
    }
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
    Error_Handler();
    }
    }

    /* ADC1初始化 */
    static void MX_ADC1_Init(void)
    {
    ADC_ChannelConfTypeDef sConfig = {0};

    /* ADC基础配置 */
    hadc1.Instance = ADC1;
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
    hadc1.Init.ContinuousConvMode = ENABLE; // 连续转换模式
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 1;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
    Error_Handler();
    }

    /* 配置ADC通道1(PA1) */
    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
    Error_Handler();
    }

    /* 启动ADC转换 */
    HAL_ADC_Start(&hadc1);
    }

    /* TIM2初始化(输入捕获) */
    static void MX_TIM2_Init(void)
    {
    TIM_IC_InitTypeDef sConfigIC = {0};

    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 71; // 分频后1MHz
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 65535;
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
    {
    Error_Handler();
    }

    /* 配置TIM2通道1为输入捕获 */
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
    sConfigIC.ICFilter = 0;
    if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
    {
    Error_Handler();
    }

    /* 启动输入捕获中断 */
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
    /* 开启TIM2中断 */
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    }

    /* GPIO初始化 */
    static void MX_GPIO_Init(void)
    {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* 使能GPIO时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    /* 配置IN1(PB0)、IN2(PB1)为输出 */
    GPIO_InitStruct.Pin = IN1_PIN|IN2_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* 配置ENA(PB2)为复用输出(PWM) */
    GPIO_InitStruct.Pin = ENA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(ENA_GPIO_PORT, &GPIO_InitStruct);

    /* 初始状态:电机停止 */
    HAL_GPIO_WritePin(IN1_GPIO_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_GPIO_PORT, IN2_PIN, GPIO_PIN_RESET);
    }

    /* 错误处理函数 */
    void Error_Handler(void)
    {
    __disable_irq();
    while (1)
    {
    // 出错时可添加LED闪烁提示
    }
    }

    #ifdef USE_FULL_ASSERT
    void assert_failed(uint8_t *file, uint32_t line)
    {
    }
    #endif /* USE_FULL_ASSERT */

    4.3 核心功能函数(main.c续)

    /* 电机控制函数:cmd=0-停止,1-升窗(正转),2-降窗(反转) */
    void Motor_Control(uint8_t cmd)
    {
    switch(cmd)
    {
    case 0: // 停止
    HAL_GPIO_WritePin(IN1_GPIO_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_GPIO_PORT, IN2_PIN, GPIO_PIN_RESET);
    window_status = 0;
    break;
    case 1: // 升窗
    HAL_GPIO_WritePin(IN1_GPIO_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_GPIO_PORT, IN2_PIN, GPIO_PIN_RESET);
    // 配置TIM2通道2为PWM输出(ENA引脚),占空比80%
    TIM_OC_InitTypeDef sConfigOC = {0};
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 4000; // 占空比=4000/5000=80%
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
    window_status = 1;
    break;
    case 2: // 降窗
    HAL_GPIO_WritePin(IN1_GPIO_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_GPIO_PORT, IN2_PIN, GPIO_PIN_SET);
    // 配置PWM占空比80%
    TIM_OC_InitTypeDef sConfigOC2 = {0};
    sConfigOC2.OCMode = TIM_OCMODE_PWM1;
    sConfigOC2.Pulse = 4000;
    sConfigOC2.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC2.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC2, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
    window_status = 2;
    break;
    default:
    Motor_Control(0);
    break;
    }
    }

    /* 霍尔脉冲捕获中断处理函数 */
    void TIM2_IRQHandler(void)
    {
    HAL_TIM_IRQHandler(&htim2);
    }

    /* 输入捕获回调函数 */
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    static uint32_t last_capture_time = 0;
    uint32_t current_capture_time = 0;

    if(htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    {
    // 获取当前捕获值
    current_capture_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
    // 计算脉冲周期,转换为频率(Hz)
    if(current_capture_time > last_capture_time)
    {
    pulse_frequency = 1000000 / (current_capture_time last_capture_time);
    }
    last_capture_time = current_capture_time;
    hall_pulse_count++; // 脉冲计数+1
    }
    }

    /* 雨滴检测函数:读取ADC值并判断 */
    void Rain_Detect(void)
    {
    // 读取ADC转换值
    if(HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
    {
    rain_adc_value = HAL_ADC_GetValue(&hadc1);
    }

    // 检测到雨滴,触发自动关窗
    if(rain_adc_value < RAIN_THRESHOLD && window_status != 1)
    {
    Motor_Control(1); // 启动升窗
    }
    }

    /* 防夹检测函数:判断脉冲频率是否低于阈值 */
    void Anti_Pinch_Check(void)
    {
    // 仅在升窗/降窗时检测
    if(window_status == 1 || window_status == 2)
    {
    // 脉冲频率低于阈值,触发防夹
    if(pulse_frequency < PULSE_THRESHOLD)
    {
    Motor_Control(0); // 停止电机
    HAL_Delay(100); // 延时100ms
    Motor_Control(2); // 反转降窗0.5秒
    HAL_Delay(500);
    Motor_Control(0); // 停止电机
    hall_pulse_count = 0; // 重置脉冲计数
    }

    // 车窗到位(脉冲数达到阈值)
    if(hall_pulse_count >= WINDOW_POSITION_PULSE)
    {
    Motor_Control(0); // 停止电机
    hall_pulse_count = 0; // 重置脉冲计数
    }
    }
    }

    /* 主函数 */
    int main(void)
    {
    /* 初始化HAL库 */
    HAL_Init();

    /* 配置系统时钟 */
    SystemClock_Config();

    /* 初始化外设 */
    MX_GPIO_Init();
    MX_ADC1_Init();
    MX_TIM2_Init();

    /* 主循环 */
    while (1)
    {
    Rain_Detect(); // 雨滴检测
    Anti_Pinch_Check(); // 防夹检测
    HAL_Delay(10); // 主循环延时10ms,降低CPU占用
    }
    }

    五、代码烧录与调试

    5.1 代码编译与烧录

  • 在Keil MDK中打开生成的工程,将上述代码补充到对应文件中。
  • 点击“Build”按钮编译代码,确保无错误(0 Error)、无警告(0 Warning)。
  • 连接ST-Link下载器到STM32最小系统板的SWD接口,同时连接电脑USB。
  • 在Keil中点击“Download”按钮,将编译后的代码烧录到STM32中。
  • 5.2 硬件调试步骤

    步骤1:电源调试
    • 给STM32供电(5V),给L298N驱动模块供电(12V),确保所有硬件通电后无发烫、短路现象。
    • 测量STM32引脚输出:未触发指令时,PB0、PB1应为低电平,ENA引脚无PWM输出。
    步骤2:雨滴传感器调试
    • 用喷壶向雨滴传感器喷水,观察STM32的PA1引脚ADC值变化(可通过串口打印,需添加串口代码),正常情况下ADC值应随雨量增大而减小。
    • 当ADC值低于RAIN_THRESHOLD时,应触发车窗升窗动作。
    步骤3:霍尔传感器调试
    • 手动转动车窗电机,观察PA0引脚是否有脉冲信号输出(可用示波器或逻辑分析仪检测),同时查看hall_pulse_count变量是否递增。
    • 正常情况下,电机转速越快,pulse_frequency值越大。
    步骤4:防夹功能调试
    • 启动升窗指令,在车窗升降过程中用手阻挡车窗,观察电机是否立即停止并反转0.5秒,验证防夹功能是否生效。
    • 若防夹不生效,可调整PULSE_THRESHOLD阈值(根据实际电机转速调整)。

    5.3 常见问题与解决

    问题现象可能原因解决方法
    电机不转 L298N使能端未接PWM、接线错误 检查ENA引脚接线,确保PWM输出正常
    防夹功能不触发 霍尔传感器安装位置不当、阈值过高 调整霍尔传感器与电机的距离,降低阈值
    雨滴检测无反应 ADC通道配置错误、传感器未供电 检查ADC初始化代码,测量传感器供电
    电机反转方向错误 IN1/IN2接线反了 调换IN1/IN2与STM32的接线

    六、功能扩展建议

  • 添加手动控制按键:在STM32上扩展GPIO口,连接物理按键,实现手动升窗、降窗、暂停功能。
  • 添加车窗位置显示:通过OLED屏幕显示当前车窗位置(基于霍尔脉冲计数)。
  • 添加串口调试:在代码中添加USART初始化和打印函数,实时输出ADC值、脉冲频率、车窗状态等信息,方便调试。
  • 低功耗优化:在无操作时,将STM32切换到睡眠模式,降低系统功耗,适配汽车电瓶供电场景。
  • 总结

  • 本方案以STM32F103C8T6为核心,通过霍尔传感器检测电机转速实现防夹,通过雨滴传感器实现自动关窗,硬件接线清晰、代码模块化,零基础小白可按步骤落地。
  • 核心逻辑通过Mermaid流程图清晰展示,代码中关键阈值(如雨滴ADC阈值、防夹脉冲频率阈值)可根据实际硬件调整,确保适配不同型号的电机和传感器。
  • 调试时需分步验证电源、传感器、电机控制各模块,优先解决硬件接线和供电问题,再调试软件逻辑,可快速定位并解决问题。
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » 场景实战:基于STM32的汽车车窗自动防夹与雨滴感应控制解决方案解析
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!