一、系统架构设计
┌─────────────────────────────────────────────────────────────┐
│ STM32 速度控制系统 │
├─────────────────────────────────────────────────────────────┤
│ 上位机/按键 │ 目标速度设定 │ PID控制器 │ PWM输出 │
│ 输入指令 │ (RPM/Hz) │ 算法处理 │ 驱动电机 │
├─────────────┼──────────────┼────────────┼────────────┤
│ • 串口指令 │ • 速度设定 │ • 比例项 │ • TIMx │
│ • 按键调整 │ • 加减速曲线 │ • 积分项 │ • 通道配置 │
│ • 旋钮输入 │ • 方向控制 │ • 微分项 │ • 死区补偿 │
└─────────────────────────────────────────────────────────────┘
二、代码
2.1 主程序:main.c
c
/**
* @file main.c
* @brief STM32 速度控制器 - PWM + PID 无级调速
* @platform STM32F103/F407 系列
* @author AI Assistant
* @date 2024
*/
#include "stm32f10x.h"
#include "pid.h"
#include "motor_control.h"
#include "encoder.h"
#include "usart.h"
#include "timer.h"
// 系统参数
#define SYSTEM_CLOCK 72000000 // 系统时钟 72MHz
#define PWM_FREQUENCY 20000 // PWM频率 20kHz
#define CONTROL_PERIOD 10 // 控制周期 10ms
#define MAX_SPEED 3000 // 最大速度 3000 RPM
#define MIN_SPEED 50 // 最小速度 50 RPM
// 全局变量
volatile uint16_t target_speed = 0; // 目标速度 (RPM)
volatile uint16_t actual_speed = 0; // 实际速度 (RPM)
volatile uint8_t system_status = 0; // 系统状态
volatile uint8_t direction = 1; // 方向:1正转,0反转
// PID控制器实例
PID_Controller speed_pid;
int main(void) {
// 系统初始化
System_Init();
// PID参数初始化(根据实际电机调整)
PID_Init(&speed_pid, 2.5f, 0.8f, 0.1f, 1000.0f, 0.0f, 1000.0f);
// 启动系统
Motor_Enable();
while(1) {
// 主循环处理
System_Process();
// 等待控制周期
Delay_ms(CONTROL_PERIOD);
}
}
/**
* @brief 系统初始化
*/
void System_Init(void) {
// 中断优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 初始化各个模块
USART_Init(115200); // 串口初始化
Encoder_Init(); // 编码器初始化
Motor_Init(); // 电机驱动初始化
Timer_Init(CONTROL_PERIOD); // 控制定时器初始化
// 默认参数设置
target_speed = 500; // 默认500 RPM
direction = 1; // 正转
printf("STM32 Speed Controller Initialized!\r\n");
printf("Target Speed: %d RPM\r\n", target_speed);
}
/**
* @brief 系统主处理逻辑
*/
void System_Process(void) {
static uint8_t status_counter = 0;
// 1. 读取实际速度(通过编码器)
actual_speed = Encoder_GetSpeed();
// 2. 执行PID控制
float pid_output = PID_Calculate(&speed_pid, (float)target_speed, (float)actual_speed);
// 3. 应用PID输出到PWM
Motor_SetPWM(pid_output, direction);
// 4. 状态上报(每秒一次)
if(++status_counter >= (1000 / CONTROL_PERIOD)) {
status_counter = 0;
Report_Status();
}
}
/**
* @brief 上报系统状态
*/
void Report_Status(void) {
printf("Status: Target=%d RPM, Actual=%d RPM, PWM=%.1f%%, Dir=%s\r\n",
target_speed, actual_speed,
(speed_pid.output / 1000.0f) * 100.0f,
direction ? "Forward" : "Reverse");
}
2.2 PID控制器:pid.h / pid.c
c
/**
* @file pid.h
* @brief PID控制器头文件
*/
#ifndef __PID_H
#define __PID_H
#include "stm32f10x.h"
// PID控制器结构体
typedef struct {
float kp; // 比例系数
float ki; // 积分系数
float kd; // 微分系数
float integral; // 积分项
float prev_error; // 上一次误差
float max_output; // 最大输出限制
float min_output; // 最小输出限制
float dead_zone; // 死区
float output; // PID输出
uint8_t enable; // 使能标志
} PID_Controller;
// PID函数声明
void PID_Init(PID_Controller* pid, float kp, float ki, float kd,
float max_out, float min_out, float dead_zone);
float PID_Calculate(PID_Controller* pid, float target, float feedback);
void PID_Reset(PID_Controller* pid);
void PID_SetParameters(PID_Controller* pid, float kp, float ki, float kd);
void PID_SetOutputLimits(PID_Controller* pid, float max_out, float min_out);
#endif /* __PID_H */
c
/**
* @file pid.c
* @brief PID控制器实现
*/
#include "pid.h"
#include "math.h"
/**
* @brief PID控制器初始化
* @param pid PID控制器指针
* @param kp 比例系数
* @param ki 积分系数
* @param kd 微分系数
* @param max_out 最大输出限制
* @param min_out 最小输出限制
* @param dead_zone 死区
*/
void PID_Init(PID_Controller* pid, float kp, float ki, float kd,
float max_out, float min_out, float dead_zone) {
pid->kp = kp;
pid->ki = ki;
pid->kd = kd;
pid->max_output = max_out;
pid->min_output = min_out;
pid->dead_zone = dead_zone;
pid->integral = 0.0f;
pid->prev_error = 0.0f;
pid->output = 0.0f;
pid->enable = 1;
}
/**
* @brief PID计算函数
* @param pid PID控制器指针
* @param target 目标值
* @param feedback 反馈值
* @return PID输出值
*/
float PID_Calculate(PID_Controller* pid, float target, float feedback) {
if(!pid->enable) {
return 0.0f;
}
// 计算误差
float error = target - feedback;
// 死区处理
if(fabs(error) < pid->dead_zone) {
error = 0.0f;
}
// 比例项
float proportional = pid->kp * error;
// 积分项(带限幅)
pid->integral += pid->ki * error;
// 积分限幅(抗积分饱和)
if(pid->integral > pid->max_output) {
pid->integral = pid->max_output;
} else if(pid->integral < pid->min_output) {
pid->integral = pid->min_output;
}
// 微分项
float derivative = pid->kd * (error - pid->prev_error);
pid->prev_error = error;
// PID输出
pid->output = proportional + pid->integral + derivative;
// 输出限幅
if(pid->output > pid->max_output) {
pid->output = pid->max_output;
} else if(pid->output < pid->min_output) {
pid->output = pid->min_output;
}
return pid->output;
}
/**
* @brief 重置PID控制器
* @param pid PID控制器指针
*/
void PID_Reset(PID_Controller* pid) {
pid->integral = 0.0f;
pid->prev_error = 0.0f;
pid->output = 0.0f;
}
/**
* @brief 设置PID参数
* @param pid PID控制器指针
* @param kp 比例系数
* @param ki 积分系数
* @param kd 微分系数
*/
void PID_SetParameters(PID_Controller* pid, float kp, float ki, float kd) {
pid->kp = kp;
pid->ki = ki;
pid->kd = kd;
PID_Reset(pid);
}
/**
* @brief 设置输出限制
* @param pid PID控制器指针
* @param max_out 最大输出
* @param min_out 最小输出
*/
void PID_SetOutputLimits(PID_Controller* pid, float max_out, float min_out) {
pid->max_output = max_out;
pid->min_output = min_out;
}
2.3 电机控制:motor_control.h / motor_control.c
c
/**
* @file motor_control.h
* @brief 电机控制头文件
*/
#ifndef __MOTOR_CONTROL_H
#define __MOTOR_CONTROL_H
#include "stm32f10x.h"
// 电机控制引脚定义
#define MOTOR_PWM_PORT GPIOA
#define MOTOR_PWM_PIN GPIO_Pin_8 // TIM1_CH1
#define MOTOR_DIR_PORT GPIOA
#define MOTOR_DIR_PIN GPIO_Pin_0
#define MOTOR_EN_PORT GPIOA
#define MOTOR_EN_PIN GPIO_Pin_1
// 电机状态
#define MOTOR_FORWARD 1
#define MOTOR_REVERSE 0
#define MOTOR_ENABLE 1
#define MOTOR_DISABLE 0
// 函数声明
void Motor_Init(void);
void Motor_Enable(void);
void Motor_Disable(void);
void Motor_SetDirection(uint8_t dir);
void Motor_SetPWM(float duty_cycle, uint8_t dir);
void Motor_EmergencyStop(void);
#endif /* __MOTOR_CONTROL_H */
c
/**
* @file motor_control.c
* @brief 电机控制实现
*/
#include "motor_control.h"
#include "timer.h"
// PWM参数
static uint16_t pwm_period = 0;
static uint16_t pwm_pulse = 0;
/**
* @brief 电机控制初始化
*/
void Motor_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE);
// 2. 配置GPIO
// PWM输出引脚
GPIO_InitStructure.GPIO_Pin = MOTOR_PWM_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MOTOR_PWM_PORT, &GPIO_InitStructure);
// 方向和使能引脚
GPIO_InitStructure.GPIO_Pin = MOTOR_DIR_PIN | MOTOR_EN_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(MOTOR_DIR_PORT, &GPIO_InitStructure);
// 3. 配置定时器1(高级定时器)
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 默认1kHz PWM
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 72MHz/72 = 1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
// 4. 配置PWM输出
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
// 5. 使能定时器
TIM_Cmd(TIM1, ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
// 6. 保存PWM周期
pwm_period = 1000 - 1;
// 7. 默认状态
Motor_Disable();
Motor_SetDirection(MOTOR_FORWARD);
printf("Motor Control Initialized!\r\n");
}
/**
* @brief 使能电机
*/
void Motor_Enable(void) {
GPIO_SetBits(MOTOR_EN_PORT, MOTOR_EN_PIN);
}
/**
* @brief 禁用电机
*/
void Motor_Disable(void) {
GPIO_ResetBits(MOTOR_EN_PORT, MOTOR_EN_PIN);
}
/**
* @brief 设置电机方向
* @param dir 方向:MOTOR_FORWARD 或 MOTOR_REVERSE
*/
void Motor_SetDirection(uint8_t dir) {
if(dir == MOTOR_FORWARD) {
GPIO_SetBits(MOTOR_DIR_PORT, MOTOR_DIR_PIN);
} else {
GPIO_ResetBits(MOTOR_DIR_PORT, MOTOR_DIR_PIN);
}
}
/**
* @brief 设置电机PWM
* @param duty_cycle PWM占空比 (0.0 - 100.0)
* @param dir 方向
*/
void Motor_SetPWM(float duty_cycle, uint8_t dir) {
// 限制占空比范围
if(duty_cycle > 100.0f) duty_cycle = 100.0f;
if(duty_cycle < 0.0f) duty_cycle = 0.0f;
// 设置方向
Motor_SetDirection(dir);
// 计算PWM脉冲
pwm_pulse = (uint16_t)((duty_cycle / 100.0f) * (pwm_period + 1));
// 设置PWM
TIM_SetCompare1(TIM1, pwm_pulse);
}
/**
* @brief 紧急停止
*/
void Motor_EmergencyStop(void) {
Motor_Disable();
TIM_SetCompare1(TIM1, 0);
printf("Emergency Stop Activated!\r\n");
}
2.4 编码器接口:encoder.h / encoder.c
c
/**
* @file encoder.h
* @brief 编码器接口头文件
*/
#ifndef __ENCODER_H
#define __ENCODER_H
#include "stm32f10x.h"
// 编码器参数
#define ENCODER_PPR 1000 // 编码器每转脉冲数
#define ENCODER_TIMER TIM2 // 使用TIM2作为编码器接口
#define ENCODER_SAMPLE_TIME 10 // 采样时间 10ms
// 函数声明
void Encoder_Init(void);
uint16_t Encoder_GetSpeed(void);
int32_t Encoder_GetPosition(void);
void Encoder_Reset(void);
#endif /* __ENCODER_H */
c
/**
* @file encoder.c
* @brief 编码器接口实现
*/
#include "encoder.h"
static volatile int32_t encoder_count = 0;
static volatile uint16_t last_count = 0;
static volatile uint16_t current_count = 0;
/**
* @brief 编码器初始化
*/
void Encoder_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置GPIO (PA0-TIM2_CH1, PA1-TIM2_CH2)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置定时器基础
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(ENCODER_TIMER, &TIM_TimeBaseStructure);
// 4. 配置编码器接口模式
TIM_EncoderInterfaceConfig(ENCODER_TIMER, TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
// 5. 配置输入捕获
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x06; // 滤波
TIM_ICInit(ENCODER_TIMER, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInit(ENCODER_TIMER, &TIM_ICInitStructure);
// 6. 使能定时器
TIM_Cmd(ENCODER_TIMER, ENABLE);
printf("Encoder Initialized! PPR=%d\r\n", ENCODER_PPR);
}
/**
* @brief 获取电机速度 (RPM)
* @return 速度值 (RPM)
*/
uint16_t Encoder_GetSpeed(void) {
static uint32_t last_time = 0;
uint32_t current_time = Get_SystemTime();
// 每10ms计算一次速度
if(current_time - last_time >= ENCODER_SAMPLE_TIME) {
last_time = current_time;
// 读取当前计数值
current_count = TIM_GetCounter(ENCODER_TIMER);
// 计算脉冲差(考虑溢出)
int16_t pulse_diff;
if(current_count >= last_count) {
pulse_diff = current_count - last_count;
} else {
pulse_diff = (0xFFFF - last_count) + current_count + 1;
}
// 更新上次计数值
last_count = current_count;
// 计算速度 (RPM)
// 速度 = (脉冲差 / 采样时间) * 60秒 / 每转脉冲数
float speed = ((float)pulse_diff / (ENCODER_SAMPLE_TIME / 1000.0f)) * 60.0f / ENCODER_PPR;
return (uint16_t)speed;
}
return 0;
}
/**
* @brief 获取编码器位置
* @return 位置值
*/
int32_t Encoder_GetPosition(void) {
return (int32_t)TIM_GetCounter(ENCODER_TIMER);
}
/**
* @brief 重置编码器
*/
void Encoder_Reset(void) {
TIM_SetCounter(ENCODER_TIMER, 0);
encoder_count = 0;
last_count = 0;
current_count = 0;
}
2.5 定时器中断:timer.h / timer.c
c
/**
* @file timer.h
* @brief 定时器中断头文件
*/
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h"
// 定时器定义
#define CONTROL_TIMER TIM3
#define SYSTEM_TIMER TIM4
// 函数声明
void Timer_Init(uint16_t period_ms);
void SysTick_Init(void);
uint32_t Get_SystemTime(void);
void Delay_ms(uint32_t ms);
#endif /* __TIMER_H */
c
/**
* @file timer.c
* @brief 定时器中断实现
*/
#include "timer.h"
static volatile uint32_t system_time = 0;
/**
* @brief 控制定时器初始化
* @param period_ms 定时周期 (ms)
*/
void Timer_Init(uint16_t period_ms) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 2. 配置定时器
TIM_TimeBaseStructure.TIM_Period = period_ms * 10 - 1; // 10kHz计数
TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 72MHz/7200 = 10kHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(CONTROL_TIMER, &TIM_TimeBaseStructure);
// 3. 配置中断
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 4. 使能更新中断
TIM_ITConfig(CONTROL_TIMER, TIM_IT_Update, ENABLE);
// 5. 启动定时器
TIM_Cmd(CONTROL_TIMER, ENABLE);
printf("Control Timer Initialized! Period=%dms\r\n", period_ms);
}
/**
* @brief SysTick初始化
*/
void SysTick_Init(void) {
// 配置SysTick为1ms中断
if(SysTick_Config(SystemCoreClock / 1000)) {
while(1); // 配置失败
}
}
/**
* @brief 获取系统时间
* @return 系统时间 (ms)
*/
uint32_t Get_SystemTime(void) {
return system_time;
}
/**
* @brief 延时函数
* @param ms 延时毫秒数
*/
void Delay_ms(uint32_t ms) {
uint32_t start = system_time;
while((system_time - start) < ms);
}
/**
* @brief SysTick中断处理函数
*/
void SysTick_Handler(void) {
system_time++;
}
/**
* @brief 控制定时器中断处理函数
*/
void TIM3_IRQHandler(void) {
if(TIM_GetITStatus(CONTROL_TIMER, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(CONTROL_TIMER, TIM_IT_Update);
// 在这里可以添加周期性的控制逻辑
// 注意:避免在中断中进行耗时操作
}
}
2.6 串口通信:usart.h / usart.c
c
/**
* @file usart.h
* @brief 串口通信头文件
*/
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include <stdio.h>
// 串口定义
#define DEBUG_USART USART1
#define BAUDRATE 115200
// 函数声明
void USART_Init(uint32_t baudrate);
void USART_SendChar(char ch);
void USART_SendString(char* str);
void USART_Printf(char* format, ...);
#endif /* __USART_H */
c
/**
* @file usart.c
* @brief 串口通信实现
*/
#include "usart.h"
#include <stdarg.h>
/**
* @brief 串口初始化
* @param baudrate 波特率
*/
void USART_Init(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // TX
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // RX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置串口
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(DEBUG_USART, &USART_InitStructure);
// 4. 使能串口
USART_Cmd(DEBUG_USART, ENABLE);
printf("USART Initialized! Baudrate=%d\r\n", baudrate);
}
/**
* @brief 发送单个字符
* @param ch 字符
*/
void USART_SendChar(char ch) {
USART_SendData(DEBUG_USART, (uint8_t)ch);
while(USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
}
/**
* @brief 发送字符串
* @param str 字符串指针
*/
void USART_SendString(char* str) {
while(*str) {
USART_SendChar(*str++);
}
}
/**
* @brief 格式化输出
* @param format 格式化字符串
* @param ... 可变参数
*/
void USART_Printf(char* format, ...) {
char buffer[256];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
USART_SendString(buffer);
}
// 重定向printf到串口
int _write(int fd, char* ptr, int len) {
for(int i = 0; i < len; i++) {
USART_SendChar(ptr[i]);
}
return len;
}
三、高级功能扩展
3.1 速度斜坡控制
c
/**
* @file ramp_control.c
* @brief 速度斜坡控制(S曲线)
*/
typedef struct {
uint16_t target_speed; // 目标速度
uint16_t current_speed; // 当前速度
uint16_t acceleration; // 加速度 (RPM/s)
uint16_t deceleration; // 减速度 (RPM/s)
uint32_t last_update_time; // 上次更新时间
uint8_t ramp_enable; // 斜坡使能
} Ramp_Controller;
/**
* @brief 速度斜坡更新
* @param ramp 斜坡控制器指针
* @param target 目标速度
* @return 斜坡调整后的速度
*/
uint16_t Ramp_Update(Ramp_Controller* ramp, uint16_t target) {
uint32_t current_time = Get_SystemTime();
uint32_t time_diff = current_time - ramp->last_update_time;
if(!ramp->ramp_enable) {
return target;
}
// 计算速度变化量
float speed_change = (float)ramp->acceleration * (time_diff / 1000.0f);
// 加速过程
if(target > ramp->current_speed) {
ramp->current_speed += (uint16_t)speed_change;
if(ramp->current_speed > target) {
ramp->current_speed = target;
}
}
// 减速过程
else if(target < ramp->current_speed) {
speed_change = (float)ramp->deceleration * (time_diff / 1000.0f);
ramp->current_speed -= (uint16_t)speed_change;
if(ramp->current_speed < target) {
ramp->current_speed = target;
}
}
ramp->last_update_time = current_time;
return ramp->current_speed;
}
3.2 看门狗保护
c
/**
* @file watchdog.c
* @brief 独立看门狗保护
*/
void Watchdog_Init(void) {
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_256); // 32kHz/256 = 125Hz
IWDG_SetReload(625); // 625/125 = 5秒超时
IWDG_ReloadCounter();
IWDG_Enable();
}
void Watchdog_Feed(void) {
IWDG_ReloadCounter();
}
参考代码 stm32速度控制器 www.youwenfan.com/contentcsu/60235.html
四、调试与优化
4.1 PID参数整定步骤
-
先调比例(P):
- 将Ki、Kd设为0
- 逐渐增加Kp,直到系统出现轻微振荡
- 将Kp减小到振荡值的60-70%
-
再调积分(I):
- 保持Kp不变
- 逐渐增加Ki,消除稳态误差
- 注意不要引起积分饱和
-
最后调微分(D):
- 保持Kp、Ki不变
- 适当增加Kd,改善动态响应
- 微分太大会放大噪声
4.2 性能优化
c
// 1. 使用DMA传输PWM数据(减少CPU负载)
void PWM_DMA_Config(void) {
// 配置DMA将PID计算结果直接传输到PWM寄存器
}
// 2. 使用硬件死区保护
void PWM_DeadZone_Config(void) {
// 配置互补PWM的死区时间,防止上下桥臂直通
}
// 3. 速度前馈控制
float PID_WithFeedForward(PID_Controller* pid, float target, float feedback) {
float pid_output = PID_Calculate(pid, target, feedback);
float feedforward = target * 0.1f; // 前馈系数
return pid_output + feedforward;
}
4.3 故障保护机制
c
// 过流保护
if(Get_MotorCurrent() > MAX_CURRENT) {
Motor_EmergencyStop();
printf("Over Current Protection Activated!\r\n");
}
// 堵转保护
if(actual_speed < 10 && target_speed > 100) {
stall_counter++;
if(stall_counter > 10) {
Motor_EmergencyStop();
printf("Stall Protection Activated!\r\n");
}
}
五、实际应用示例
5.1 通过串口设置速度
c
// 串口接收中断中解析速度指令
void USART1_IRQHandler(void) {
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
char ch = USART_ReceiveData(USART1);
if(ch >= '0' && ch <= '9') {
speed_input = speed_input * 10 + (ch - '0');
}
else if(ch == 's') { // 设置速度命令
target_speed = speed_input;
speed_input = 0;
printf("Set Target Speed: %d RPM\r\n", target_speed);
}
else if(ch == 'd') { // 方向切换
direction = !direction;
printf("Direction Changed: %s\r\n", direction ? "Forward" : "Reverse");
}
}
}