用STM32 CubeMX HAL库玩转SG90:180度舵机和360度舵机代码一键生成教程

张开发
2026/4/11 18:50:13 15 分钟阅读

分享文章

用STM32 CubeMX HAL库玩转SG90:180度舵机和360度舵机代码一键生成教程
STM32 CubeMX HAL库驱动SG90舵机实战从图形配置到多模式控制在嵌入式开发领域舵机控制一直是机器人、自动化设备中的基础技能。传统开发方式需要手动配置寄存器、计算分频系数不仅耗时还容易出错。而现代开发工具链如STM32CubeMX配合HAL库让开发者能够通过图形化界面快速生成初始化代码将精力集中在应用逻辑而非底层配置上。1. 开发环境搭建与CubeMX基础配置1.1 硬件准备清单在开始之前请确保准备好以下硬件组件STM32开发板如Nucleo-F103RB兼容大多数STM32系列SG90舵机180度或360度版本5V外接电源模块建议输出电流≥1A杜邦线若干USB转TTL模块可选用于调试输出注意切勿直接使用开发板的3.3V引脚为舵机供电这会导致扭矩不足或工作异常。务必使用独立5V电源且共地处理。1.2 CubeMX工程创建打开STM32CubeMX点击New Project选择对应芯片型号如STM32F103RB在Pinout Configuration界面进行以下关键设置系统时钟配置为72MHz根据具体芯片调整启用TIM2的Channel1作为PWM输出配置USART1用于调试输出可选// CubeMX自动生成的时钟配置代码示例部分 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);1.3 PWM定时器参数计算SG90舵机标准控制信号参数频率50Hz周期20ms脉宽范围180度舵机0.5ms-2.5ms360度舵机相同范围但含义不同在CubeMX中配置TIM2参数Prescaler 72 - 1 // 72MHz/72 1MHz计数器时钟 Counter Period 20000 - 1 // 1MHz下20000个计数20ms Pulse 1500 - 1 // 初始占空比1.5ms中立位置2. 180度舵机角度精确控制2.1 控制原理深度解析180度定位舵机将PWM脉宽线性映射到机械角度0.5ms → 0度1.5ms → 90度2.5ms → 180度实际项目中需要考虑的死区补偿和机械误差修正// 脉宽到角度的转换函数带死区补偿 uint16_t AngleToPulse(float angle) { const float min_pulse 500; // 0.5ms in us const float max_pulse 2500; // 2.5ms in us const float dead_zone 50; // 机械死区补偿 // 限制角度范围并加入补偿 angle fmaxf(0, fminf(180, angle)) dead_zone; // 线性映射计算 return (uint16_t)(min_pulse (max_pulse-min_pulse)*(angle/180.0f)); }2.2 HAL库驱动实现CubeMX生成代码后添加控制函数// 更新舵机角度 void Servo_SetAngle(TIM_HandleTypeDef *htim, uint32_t Channel, float angle) { uint16_t pulse AngleToPulse(angle); __HAL_TIM_SET_COMPARE(htim, Channel, pulse); } // 示例让舵机从0度平滑移动到180度 void Servo_SweepDemo(TIM_HandleTypeDef *htim, uint32_t Channel) { for(float angle 0; angle 180; angle 5) { Servo_SetAngle(htim, Channel, angle); HAL_Delay(100); // 100ms步进间隔 } }2.3 实际应用中的优化技巧运动平滑处理添加加速度曲线避免机械冲击// 二次缓动函数实现平滑移动 float EaseInOutQuad(float t) { return t 0.5 ? 2*t*t : 1-powf(-2*t2,2)/2; } void Servo_SmoothMove(TIM_HandleTypeDef *htim, uint32_t Channel, float start, float end, uint16_t duration_ms) { const uint8_t steps 20; for(uint8_t i 0; i steps; i) { float progress EaseInOutQuad((float)i/steps); float current start (end-start)*progress; Servo_SetAngle(htim, Channel, current); HAL_Delay(duration_ms/steps); } }多舵机同步控制利用多个定时器通道或DMA位置反馈检测通过ADC读取电位器电压部分高级舵机支持3. 360度连续旋转舵机速度控制3.1 工作模式本质差异360度舵机虽然使用相同的PWM信号但内部去除了位置反馈脉宽 1.5ms逆时针旋转值越小速度越快脉宽 1.5ms停止脉宽 1.5ms顺时针旋转值越大速度越快速度控制参数对照表脉宽(ms)旋转方向速度等级0.5CCW最大1.0CCW中等1.5停止-2.0CW中等2.5CW最大3.2 速度控制代码实现// 设置旋转速度(-100到100) void Servo_SetSpeed(TIM_HandleTypeDef *htim, uint32_t Channel, int8_t speed) { speed (int8_t)fmaxf(-100, fminf(100, speed)); // 限幅 // 映射到脉宽范围 uint16_t pulse; if(speed 0) { pulse 1500 (speed * 10); // 1500-2500us } else { pulse 1500 (speed * 10); // 500-1500us } __HAL_TIM_SET_COMPARE(htim, Channel, pulse); } // 示例速度渐变测试 void Servo_SpeedRampDemo(TIM_HandleTypeDef *htim, uint32_t Channel) { // 加速阶段 for(int8_t s 0; s 100; s 10) { Servo_SetSpeed(htim, Channel, s); HAL_Delay(200); } // 减速到反转 for(int8_t s 100; s -100; s - 10) { Servo_SetSpeed(htim, Channel, s); HAL_Delay(200); } }3.3 闭环控制进阶通过外部编码器实现简单PID控制typedef struct { float Kp, Ki, Kd; float integral; float prev_error; } PID_Controller; float PID_Update(PID_Controller *pid, float setpoint, float actual) { float error setpoint - actual; 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 Servo_PIDControl(TIM_HandleTypeDef *htim, uint32_t Channel, float target_rpm, float current_rpm) { static PID_Controller pid {0.5, 0.01, 0.1, 0, 0}; float control PID_Update(pid, target_rpm, current_rpm); Servo_SetSpeed(htim, Channel, (int8_t)control); }4. 工程优化与调试技巧4.1 电源噪声处理方案舵机运行时产生的电流突变会导致电压波动常见解决方案对比方法成本效果实现难度大容量电解电容低一般简单LC滤波电路中好中等独立电源供电高优秀复杂开关电源模块中良好中等推荐电路配置[5V电源] → [470μF电解电容] → [100nF陶瓷电容] → [舵机] ↑ [10Ω电阻] [100μF电容]组成RC滤波4.2 实时监控实现利用串口打印调试信息// 重定向printf到串口 int __io_putchar(int ch) { HAL_UART_Transmit(huart1, (uint8_t*)ch, 1, HAL_MAX_DELAY); return ch; } // 在控制循环中添加 printf(Pulse: %uus, Angle: %.1f°\r\n, TIM2-CCR1, (TIM2-CCR1-500)/2000.0f*180.0f);4.3 异常处理机制增强鲁棒性的代码结构HAL_StatusTypeDef Servo_SafeSetAngle(TIM_HandleTypeDef *htim, uint32_t Channel, float angle) { if(htim-State ! HAL_TIM_STATE_READY) { return HAL_ERROR; } if(angle 0 || angle 180) { return HAL_ERROR; } uint16_t pulse AngleToPulse(angle); __HAL_TIM_SET_COMPARE(htim, Channel, pulse); return HAL_OK; }5. 项目实战云台控制系统5.1 双轴云台硬件连接构建一个由两个180度舵机组成的监控云台水平轴TIM2 Channel1 → PA0垂直轴TIM3 Channel1 → PA6电源共用5V 2A电源模块5.2 三维坐标转换将空间坐标转换为舵机角度typedef struct { float x, y, z; // 目标位置坐标 } Point3D; void PointToAngles(Point3D target, float *pan, float *tilt) { // 计算水平转角pan *pan atan2f(target.y, target.x) * 180.0f / M_PI; // 计算俯仰角tilt float dist sqrtf(target.x*target.x target.y*target.y); *tilt atan2f(target.z, dist) * 180.0f / M_PI; // 限制在舵机有效范围 *pan fmaxf(0, fminf(180, *pan 90)); // 转换为0-180范围 *tilt fmaxf(30, fminf(150, *tilt 90)); // 限制俯仰范围 }5.3 主控制逻辑实现void Main_Loop(void) { // 初始化位置 Servo_SetAngle(htim2, TIM_CHANNEL_1, 90); // 水平居中 Servo_SetAngle(htim3, TIM_CHANNEL_1, 90); // 垂直居中 while(1) { Point3D target GetTargetPosition(); // 从传感器获取目标 float pan, tilt; PointToAngles(target, pan, tilt); // 平滑移动到新位置 Servo_SmoothMove(htim2, TIM_CHANNEL_1, __HAL_TIM_GET_COMPARE(htim2,TIM_CHANNEL_1), pan, 500); Servo_SmoothMove(htim3, TIM_CHANNEL_1, __HAL_TIM_GET_COMPARE(htim3,TIM_CHANNEL_1), tilt, 500); HAL_Delay(100); } }在完成基础功能后可以进一步添加蓝牙/WiFi远程控制接口预设位置记忆功能自动扫描模式障碍物检测保护

更多文章