文章目录
-
- 一、方案整体设计与原理说明
-
- 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驱动模块接线
| PB0 | IN1 | 控制电机正反转(升窗) |
| PB1 | IN2 | 控制电机正反转(降窗) |
| PB2 | ENA | 电机使能(接PWM,控制转速) |
| 5V | 12V供电端 | 仅给L298N逻辑电路供电 |
| GND | GND | 共地 |
(2)L298N与车窗电机接线
| OUT1 | 电机引脚1 | 电机正转/反转 |
| OUT2 | 电机引脚2 | 电机正转/反转 |
| 12V | 12V电源正极 | 电机供电 |
| GND | 12V电源负极 | 共地 |
(3)霍尔传感器与STM32接线
| VCC | 3.3V | 传感器供电 |
| GND | GND | 共地 |
| OUT | PA0 | 脉冲信号输出(接定时器通道) |
(4)雨滴传感器与STM32接线
| VCC | 5V | 传感器供电 |
| GND | GND | 共地 |
| AO | PA1 | 模拟信号输出(接ADC通道) |
三、软件开发环境搭建
3.1 环境准备
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 代码编译与烧录
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的接线 |
网硕互联帮助中心






评论前必须登录!
注册