STM32F103RCT6实战从零构建智能小车电机控制系统引言在嵌入式开发领域电机控制一直是工程师们必须掌握的核心技能之一。无论是智能小车、工业机器人还是自动化设备精准的电机调速都直接影响着整个系统的性能表现。STM32系列微控制器凭借其丰富的外设资源和稳定的性能成为电机控制领域的首选方案。本文将带您从零开始基于STM32F103RCT6芯片和CubeMX工具构建一个完整的带编码器反馈的PWM电机控制系统。不同于简单的配置教程我们将以智能小车项目为背景完整呈现从硬件选型、CubeMX配置、HAL库编程到实际调试的全过程。您将学习到如何正确连接电机驱动电路PWM信号生成与占空比调节技巧编码器信号采集与转速计算原理系统调试与性能优化方法1. 硬件准备与电路设计1.1 核心组件选型构建一个可靠的电机控制系统硬件选型是第一步。以下是我们的推荐配置组件类别推荐型号关键参数备注主控芯片STM32F103RCT672MHz主频256KB Flash48KB RAM性价比高资源丰富电机驱动TB6612FNG1.2A持续电流3.2A峰值支持双路H桥控制直流电机JGA25-3706V/3000RPM带AB相编码器11线增量式编码器电源系统18650锂电池组7.4V/2000mAh需配合5V稳压模块1.2 关键电路连接正确的硬件连接是系统稳定运行的基础。以下是核心连接示意图STM32F103RCT6 TB6612FNG 直流电机 ------------------- ------------------- ----------- | PA8 (TIM1_CH1) -----PWM----| PWMA | | | | | | | | 电机 | | PB6 (TIM4_CH1) ---A相------| AO1 |-----| 动力线 | | | | | | | | PB7 (TIM4_CH2) ---B相------| AO2 | | | | | | | | 编码器 | | GND ------------| GND |-----| 信号线 | ------------------- ------------------- -----------注意电机驱动板的VM引脚应接电池正极VCC接5V逻辑电源。编码器电源建议使用3.3V供电以确保信号兼容性。1.3 保护电路设计为防止电机干扰导致系统不稳定建议添加以下保护措施在电机电源输入端并联1000μF电解电容和0.1μF陶瓷电容编码器信号线上串联100Ω电阻并添加10nF滤波电容使用光耦隔离或专用电机驱动芯片避免反向电动势损坏MCU2. CubeMX工程配置2.1 基础环境搭建首先在CubeMX中创建新工程选择STM32F103RCT6芯片。关键配置步骤如下时钟配置启用HSE外部8MHz晶振系统时钟设为72MHzAPB1定时器时钟保持72MHz不要分频GPIO设置配置一个LED指示灯如PD2启用USART1用于调试信息输出// 生成的时钟配置代码片段 RCC_OscInitTypeDef RCC_OscInitStruct {0}; 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; HAL_RCC_OscConfig(RCC_OscInitStruct);2.2 PWM定时器配置我们使用TIM1的通道1生成PWM信号选择TIM1激活Channel1配置参数Prescaler: 71 (72MHz/(711) 1MHz)Counter Period: 999 (1MHz/1000 1kHz PWM频率)Pulse: 初始占空比设为0Mode: PWM mode 1Fast Mode: Disable提示对于直流电机控制1-10kHz的PWM频率是常见选择。频率过低会导致电机噪音过高则可能增加驱动损耗。2.3 编码器接口配置使用TIM4作为编码器接口选择TIM4设置为Encoder Mode配置Channel1和Channel2为Input Capture direct mode设置Encoder Mode为Encoder Mode TI1 and TI2调整Counter Period为6553516位最大值启用TIM4全局中断// 编码器初始化代码 TIM_Encoder_InitTypeDef sConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; sConfig.EncoderMode TIM_ENCODERMODE_TI12; sConfig.IC1Polarity TIM_ICPOLARITY_RISING; sConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler TIM_ICPSC_DIV1; sConfig.IC1Filter 0; sConfig.IC2Polarity TIM_ICPOLARITY_RISING; sConfig.IC2Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler TIM_ICPSC_DIV1; sConfig.IC2Filter 0; HAL_TIM_Encoder_Init(htim4, sConfig);3. HAL库编程实现3.1 电机控制核心逻辑在main.c中添加以下用户代码/* Private variables ---------------------------------------------------------*/ int32_t encoder_count 0; int32_t motor_rpm 0; uint16_t pwm_duty 0; uint8_t motor_direction 0; /* Private function prototypes -----------------------------------------------*/ void Motor_SetSpeed(int16_t speed); void System_DebugOutput(void); /* USER CODE BEGIN 2 */ // 启动外设 HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIM_Encoder_Start(htim4, TIM_CHANNEL_ALL); HAL_TIM_Base_Start_IT(htim2); // 10ms定时中断 // 初始设置 Motor_SetSpeed(0); // 电机初始停止 /* USER CODE END 2 */3.2 定时器中断处理在定时器回调函数中实现速度计算和控制/* USER CODE BEGIN 4 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint32_t debug_counter 0; if(htim-Instance TIM2) // 10ms定时中断 { // 读取编码器计数值 int16_t current_count (int16_t)TIM4-CNT; TIM4-CNT 0; // 计数器清零 // 计算转速(RPM) // 编码器分辨率: 11线×4倍频44计数/转 // 采样周期10ms: 1分钟60000ms → 6000采样周期 motor_rpm (current_count * 6000) / 44; // 调试输出 debug_counter; if(debug_counter 50) // 500ms输出一次 { System_DebugOutput(); debug_counter 0; } } } /* USER CODE END 4 */3.3 电机控制函数实现精准的PWM调速函数/** * brief 设置电机速度和方向 * param speed: -1000~1000, 负值表示反转 */ void Motor_SetSpeed(int16_t speed) { // 限制速度范围 speed (speed 1000) ? 1000 : (speed -1000) ? -1000 : speed; // 设置方向 if(speed 0) { motor_direction 0; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 方向控制引脚 } else { motor_direction 1; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); speed -speed; // 取绝对值 } // 设置PWM占空比 pwm_duty (uint16_t)((speed * htim1.Init.Period) / 1000); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, pwm_duty); }4. 系统调试与优化4.1 串口调试输出添加调试信息输出函数方便观察系统状态void System_DebugOutput(void) { char buffer[128]; int len snprintf(buffer, sizeof(buffer), RPM:%6d PWM:%4d Dir:%d CNT:%d\r\n, motor_rpm, pwm_duty, motor_direction, encoder_count); HAL_UART_Transmit(huart1, (uint8_t*)buffer, len, HAL_MAX_DELAY); // LED闪烁指示系统运行 HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2); }4.2 PID速度控制为实现更精准的速度控制可以引入PID算法/* PID控制器结构体 */ typedef struct { float Kp, Ki, Kd; float integral; float prev_error; } PID_Controller; /* PID初始化 */ void PID_Init(PID_Controller* pid, float Kp, float Ki, float Kd) { pid-Kp Kp; pid-Ki Ki; pid-Kd Kd; pid-integral 0; pid-prev_error 0; } /* PID计算 */ float PID_Calculate(PID_Controller* pid, float setpoint, float measurement) { float error setpoint - measurement; pid-integral error; float derivative error - pid-prev_error; pid-prev_error error; return pid-Kp * error pid-Ki * pid-integral pid-Kd * derivative; } /* 在定时中断中调用 */ void Speed_Control_Update(int16_t target_rpm) { static PID_Controller speed_pid; static uint8_t pid_inited 0; if(!pid_inited) { PID_Init(speed_pid, 0.8f, 0.05f, 0.1f); // 需根据实际调整参数 pid_inited 1; } float output PID_Calculate(speed_pid, target_rpm, motor_rpm); Motor_SetSpeed((int16_t)output); }4.3 常见问题排查遇到电机不转或转速异常时可按以下步骤排查电源问题测量电机驱动板供电电压检查STM32与驱动板共地信号问题用示波器检查PWM输出波形确认编码器信号线连接正确软件问题检查定时器配置参数确认中断优先级设置合理验证速度计算算法正确性5. 项目进阶与扩展5.1 多电机协同控制在智能小车应用中通常需要控制两个电机实现差速转向typedef struct { TIM_HandleTypeDef* pwm_tim; uint32_t pwm_channel; GPIO_TypeDef* dir_port; uint16_t dir_pin; int16_t current_speed; } Motor_Controller; void Motor_Init(Motor_Controller* motor, TIM_HandleTypeDef* tim, uint32_t channel, GPIO_TypeDef* port, uint16_t pin) { motor-pwm_tim tim; motor-pwm_channel channel; motor-dir_port port; motor-dir_pin pin; motor-current_speed 0; HAL_TIM_PWM_Start(tim, channel); } void Motor_SetSpeed(Motor_Controller* motor, int16_t speed) { // 实现类似前面的速度控制 // ... }5.2 无线遥控集成通过蓝牙或2.4G模块接收遥控指令void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART2) // 假设USART2连接蓝牙模块 { uint8_t cmd uart_rx_buffer[0]; switch(cmd) { case F: // 前进 Motor_SetSpeed(motor_left, 500); Motor_SetSpeed(motor_right, 500); break; case B: // 后退 // ... } HAL_UART_Receive_IT(huart2, uart_rx_buffer, 1); } }5.3 运动控制算法实现更复杂的运动轨迹控制void Move_Forward(int16_t speed, uint32_t distance_mm) { // 重置编码器计数 encoder_left_count 0; encoder_right_count 0; // 根据轮径计算目标脉冲数 float wheel_circumference 3.1416f * WHEEL_DIAMETER; uint32_t target_counts (distance_mm * ENCODER_COUNTS_PER_REV) / wheel_circumference; // 启动电机 Motor_SetSpeed(motor_left, speed); Motor_SetSpeed(motor_right, speed); // 等待到达目标距离 while((encoder_left_count encoder_right_count) (2 * target_counts)) { // 可加入PID调节保持直线行驶 // ... } // 停止电机 Motor_Stop(motor_left); Motor_Stop(motor_right); }