news 2026/4/16 14:47:55

用STM32F103C8T6+L298N做个简易闭环?Proteus里模拟电位器控制电机转速

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用STM32F103C8T6+L298N做个简易闭环?Proteus里模拟电位器控制电机转速

STM32F103C8T6与L298N构建闭环电机调速系统的Proteus仿真实践

在嵌入式系统开发中,理解"感知-控制"的闭环概念是迈向实际应用的关键一步。本文将带您通过STM32F103C8T6微控制器和L298N电机驱动模块,构建一个完整的电位器控制电机转速的闭环系统,并在Proteus仿真环境中验证其动态性能。这个项目不仅适合嵌入式初学者掌握基础外设配置,也为理解工业控制中的PID算法奠定了实践基础。

1. 系统架构设计与核心组件

闭环控制系统由三个基本环节构成:传感器输入、控制器处理和执行器输出。在本项目中,滑动变阻器作为模拟量输入设备,STM32负责信号处理和控制逻辑,L298N则驱动电机完成物理输出。这种结构在工业自动化、机器人控制等领域有着广泛应用原型价值。

核心硬件选型考量

  • STM32F103C8T6:Cortex-M3内核,72MHz主频,具备12位ADC和高级定时器,性价比极高的"蓝色药丸"开发板核心
  • L298N双H桥驱动:最大46V供电电压,2A持续电流输出,内置续流二极管,支持PWM调速
  • 10KΩ线性电位器:作为模拟量输入设备,电压变化范围0-3.3V
  • 直流电机模型:Proteus中选用MOTOR-DC组件,参数设置为12V/2000rpm

注意:实际硬件搭建时,务必在电机两端并联104电容抑制电刷火花干扰,同时为L298N配备足够散热片。

系统工作原理框图如下:

[电位器] → [STM32 ADC] → [PWM算法] → [L298N] → [电机] ↑ | |______________________________________| 转速反馈(可选)

2. Proteus仿真环境搭建

Proteus 8.9及以上版本提供了完善的STM32仿真支持,配合VS Code或Keil MDK可实现代码级调试。新建工程时选择"STM32F103C8"器件,注意与C8T6的引脚兼容性。

关键元器件搜索关键词

  • 微控制器:STM32F103C8
  • 电机驱动:L298N
  • 显示设备:OLED12864-I2C
  • 调试工具:DC VOLTMETERDIGITAL OSCILLOSCOPE

电路连接要点:

  1. 电位器中间引脚接PA0(ADC1_IN0),两端分别接3.3V和GND
  2. L298N的IN1-IN4接PA4-PA7,ENA/ENB接PA2/PA3(TIM2_CH3/CH4)
  3. OLED的SCL/SDA分别接PB6/PB7(I2C1)
// 元器件参数设置示例 #define POTENTIOMETER POT-HG // 10KΩ线性电位器 #define MOTOR MOTOR-DC // 12V/2000rpm直流电机 #define DRIVER L298N // 双H桥驱动模块

常见仿真问题排查

  • ADC采样值不稳定:在ADC输入引脚添加0.1uF滤波电容
  • 电机不转动:检查L298N的VS(供电)和VSS(逻辑)电压是否正常
  • OLED无显示:确认I2C上拉电阻(4.7KΩ)已添加

3. STM32固件开发详解

采用模块化编程思想,将系统功能分解为硬件抽象层(HAL)和应用逻辑层。使用STM32标准外设库而非HAL库,便于理解底层寄存器操作。

3.1 ADC采样配置

12位ADC以55.5周期采样时间平衡速度和精度,采用单次转换模式节省功耗:

void ADC1_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 12MHz ADC时钟 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStruct); ADC_InitTypeDef ADC_InitStruct; ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; ADC_InitStruct.ADC_ScanConvMode = DISABLE; ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStruct); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_Cmd(ADC1, ENABLE); // ADC校准流程 ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); } uint16_t Get_ADC_Value(void) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); }

3.2 PWM生成与电机控制

TIM2通道3/4产生互补PWM信号,通过改变占空比实现调速:

void PWM_Init(uint16_t arr, uint16_t psc) { GPIO_InitTypeDef GPIO_InitStruct; TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_OCInitTypeDef TIM_OCInitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // PA2/PA3作为TIM2_CH3/CH4输出 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); TIM_TimeBaseStruct.TIM_Period = arr; // 自动重装载值 TIM_TimeBaseStruct.TIM_Prescaler = psc; // 预分频系数 TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct); // PWM1模式配置 TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC3Init(TIM2, &TIM_OCInitStruct); TIM_OC4Init(TIM2, &TIM_OCInitStruct); TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM2, ENABLE); TIM_Cmd(TIM2, ENABLE); } void Set_Motor_Speed(uint8_t motor, uint16_t speed) { switch(motor) { case 1: TIM_SetCompare3(TIM2, speed); break; case 2: TIM_SetCompare4(TIM2, speed); break; } }

4. 闭环控制算法实现

将ADC采样值映射为PWM占空比是系统的核心逻辑。采用线性变换算法,当电位器旋至中点时电机停止,顺时针/逆时针旋转分别加速:

#define ADC_MAX 4095 // 12位ADC最大值 #define PWM_MAX 1000 // 根据TIM2的ARR值设定 void Control_Loop(void) { static uint16_t adc_val, pwm_duty; adc_val = Get_ADC_Value(); // 死区处理(中间10%区域) if(adc_val > (ADC_MAX*0.55) || adc_val < (ADC_MAX*0.45)) { // 线性映射:ADC[2250,4095] -> PWM[0,1000] if(adc_val > ADC_MAX/2) { pwm_duty = (uint32_t)(adc_val - ADC_MAX/2) * PWM_MAX / (ADC_MAX/2); Set_Motor_Speed(1, pwm_duty); Set_Motor_Direction(1, FORWARD); } // ADC[0,2250] -> PWM[1000,0] else { pwm_duty = PWM_MAX - (uint32_t)adc_val * PWM_MAX / (ADC_MAX/2); Set_Motor_Speed(1, pwm_duty); Set_Motor_Direction(1, BACKWARD); } } else { Set_Motor_Speed(1, 0); // 停止电机 } OLED_ShowNum(2, 1, adc_val, 4); // 显示ADC值 OLED_ShowNum(3, 1, pwm_duty, 4); // 显示PWM值 OLED_ShowString(4, 1, adc_val > ADC_MAX/2 ? "FWD " : "REV "); }

性能优化技巧

  1. 添加软件滤波:采用滑动平均法处理ADC采样值
    #define FILTER_LEN 5 uint16_t ADC_Filter(uint16_t new_val) { static uint16_t buf[FILTER_LEN] = {0}; static uint8_t index = 0; uint32_t sum = 0; buf[index++] = new_val; if(index >= FILTER_LEN) index = 0; for(uint8_t i=0; i<FILTER_LEN; i++) sum += buf[i]; return sum / FILTER_LEN; }
  2. 非线性映射:使用查表法或指数曲线实现更符合人机交互的调速感受
  3. 速度反馈:通过编码器或霍尔传感器实现真正的闭环控制

5. 系统调试与性能分析

Proteus的虚拟示波器是验证系统行为的利器。添加电压探针监测以下关键点:

  1. PA0引脚电压(电位器输出)
  2. PA2/PA3引脚PWM波形
  3. 电机两端电压

典型调试流程

  1. 静态测试:确认ADC采样值与电位器位置呈线性关系
  2. 开环测试:固定PWM占空比,观察电机转速是否稳定
  3. 闭环测试:旋转电位器,检查系统响应速度和超调量

测量数据记录表:

电位器位置ADC值PWM占空比电机转速(rpm)
最小00%0
25%10240%0
50%20480%0
75%307250%980
最大4095100%1950

动态响应改进方案

  • 增加加速度限制:避免PWM占空比突变导致电机电流冲击
  • 加入死区补偿:消除电机启动静摩擦影响
  • 实现PID控制:通过Proteus的模拟示波器观察阶跃响应曲线,调整P/I/D参数
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 14:46:30

Formily终极指南:高性能JSON Schema驱动的跨端表单解决方案

Formily终极指南&#xff1a;高性能JSON Schema驱动的跨端表单解决方案 【免费下载链接】formily &#x1f4f1;&#x1f680; &#x1f9e9; Cross Device & High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/V…

作者头像 李华
网站建设 2026/4/16 14:46:29

League Akari:英雄联盟客户端的终极自动化工具箱

League Akari&#xff1a;英雄联盟客户端的终极自动化工具箱 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power &#x1f680;. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 在英雄联盟的竞技体验中&#xff…

作者头像 李华
网站建设 2026/4/16 14:44:05

第 10 课:列表页的异步状态怎么设计

第 10 课&#xff1a;列表页的异步状态怎么设计 这一课非常实战。 因为真实项目里的列表页&#xff0c;几乎都不是一打开就“天然有数据”的。 它们通常都要经历这样一个过程&#xff1a; 页面先挂载发起请求等待返回再决定显示表格、空状态还是错误提示 所以这节课要解决…

作者头像 李华
网站建设 2026/4/16 14:35:25

Android蓝牙状态监听实战:从广播接收器到Handler的完整实现

Android蓝牙状态监听实战&#xff1a;从广播接收器到Handler的完整实现 在移动应用开发中&#xff0c;蓝牙功能的状态管理一直是个既基础又关键的环节。想象一下这样的场景&#xff1a;用户打开健身APP准备连接智能手环&#xff0c;却发现界面始终显示"设备未连接"&a…

作者头像 李华