
SG90舵机PWM控制实战基于STM32的180度角度精准驱动与代码移植详解最近在做一个机械臂的小项目用到了SG90这款舵机。很多刚开始玩机器人和航模的朋友第一次接触舵机控制时可能会被PWM、占空比这些概念绕晕。其实用STM32控制舵机旋转到指定角度原理并不复杂关键在于理解那个“神秘”的PWM信号。今天我就结合手头的代码带大家从零开始一步步实现用STM32的定时器产生PWM信号精准控制SG90舵机在0到180度之间任意旋转。咱们这篇教程的目标很明确让你彻底搞懂舵机控制原理并把手里的驱动代码成功移植到自己的STM32工程里最终让舵机动起来。无论你是做机器人关节、航模舵面还是自动门开关这个基础技能都非常有用。1. 认识你的舵机SG90规格与控制原理在写代码之前咱们得先了解手里的“兵器”。SG90是一款非常常见的微型舵机价格便宜性能也足够应付很多DIY项目。1.1 关键参数与选型提醒根据厂家资料SG90有几个核心参数需要记住驱动电压3V ~ 7.2V。常用5V供电用开发板的5V输出或者外接一个稳压模块都行。工作扭矩1.6KG/CM。这个扭矩不算大驱动个小机械爪、摄像头云台没问题但带不动太重的负载。控制方式PWM脉冲宽度调制。这是核心后面会详细讲。转动角度180度。这里有个大坑要注意市面上还有一种360度连续旋转的舵机那种是没法控制角度的只能控制旋转速度和方向。买的时候一定要分清楚咱们今天讲的是180度位置舵机。另外SG90的转速不算快大概是0.18秒/60度。这意味着如果你发送角度控制指令的间隔太短比如小于0.2秒就换一个角度舵机可能还没转到指定位置就收到了新指令动作会不连贯。做平滑运动时需要考虑这个速度。1.2 舵机为什么听PWM的话你可以把舵机想象成一个“听话的电机”它内部有一套精密的反馈控制系统。我们通过一根信号线告诉它该转到什么位置。传递这个“命令”的语言就是PWM信号。SG90能听懂一种固定的“语言格式”信号周期T固定为20毫秒ms。也就是说每隔20ms我们需要发送一个脉冲。脉冲宽度t这个宽度决定了舵机的角度。它在一个周期20ms内的高电平持续时间。0.5ms高电平 → 舵机转到0度1.0ms高电平 → 舵机转到45度1.5ms高电平 → 舵机转到90度中间位置2.0ms高电平 → 舵机转到135度2.5ms高电平 → 舵机转到180度简单来说我们要用STM32产生一个周期20ms的PWM波然后通过改变每个周期里高电平的持续时间0.5ms到2.5ms之间就能线性控制舵机在0-180度内转动。提示这里的0.5ms-2.5ms是典型值不同厂家、不同批次的舵机可能有微小差异如果发现角度不准可以在这个范围附近微调。2. 代码移植与工程搭建理解了原理接下来就是动手环节。我这里有一份已经写好的驱动代码bsp_sg90.c和bsp_sg90.h咱们的任务是把它移植到你的STM32工程中并做必要的修改。2.1 文件准备与放置首先你需要拿到这两个驱动文件。如果还没有可以根据文章末尾的链接下载。移植步骤和添加其他外设驱动比如DHT11温湿度传感器是一样的在你的工程目录下例如Drivers/BSP文件夹新建一个SG90文件夹。将bsp_sg90.c和bsp_sg90.h这两个文件复制进去。在IDE如Keil、IAR的工程管理器中将bsp_sg90.c添加到你的项目源文件组里。确保在需要调用舵机功能的源文件如main.c中包含了头文件#include bsp_sg90.h。2.2 关键宏定义修改必做这是移植中最容易出错的一步。原始代码的bsp_sg90.h文件里用宏定义指定了使用哪个定时器和哪个GPIO引脚。你必须根据自己开发板的硬件连接来修改这些宏原始头文件中的关键定义如下#define RCC_SIG RCC_AHB1Periph_GPIOA #define PORT_SIG GPIOA #define GPIO_SIG GPIO_Pin_6 #define GPIO_SIG_SOURCE GPIO_PinSource6 #define AF_SIG GPIO_AF_TIM3 #define RCC_SIG_TIMER RCC_APB1Periph_TIM3 #define BSP_SIG_TIMER TIM3 // 定时器你需要修改的地方GPIO_SIG这个定义了具体的引脚号。原文用的是GPIO_Pin_6即PA6。你的舵机信号线接在哪个引脚这里就改成哪个。比如接在PB8就改为GPIO_Pin_8。PORT_SIG这个定义了引脚所在的端口。如果接在PB8这里就要改为GPIOB。RCC_SIG对应的GPIO端口时钟使能宏。如果端口改为GPIOB这里应改为RCC_AHB1Periph_GPIOB对于F1系列可能是RCC_APB2Periph_GPIOB具体看你的标准库版本。BSP_SIG_TIMER选择使用的定时器。PA6是TIM3的通道1所以这里用TIM3。如果你换用了其他有PWM输出功能的引脚一定要查芯片数据手册确认该引脚对应的定时器和通道并同步修改这里的定时器宏以及下面的复用功能AF号。注意STM32的引脚复用功能是固定的不是任意引脚都能输出任意定时器的PWM。一定要查阅你所用芯片的《数据手册》或《引脚复用映射表》来确认。3. 驱动代码深度解析文件放好了宏定义也改对了现在咱们来深入看看代码到底是怎么工作的。理解了代码以后自己写或者调试起来就心里有底了。3.1 初始化函数SG90_Init(void)这个函数负责配置定时器产生我们需要的PWM信号。核心是计算两个参数预分频值Prescaler和自动重装载值Period。我们的目标是产生50Hz的PWM波因为周期 T1/f1/500.02s20ms。假设系统主频是84MHz原文代码基于此计算过程在代码注释里写得很清楚// 频率f 系统时钟 / ( (prescaler1) * (period1) ) // 频率f 84,000,000 / (8400 * 200) 50hz // 周期T 1/f 1/50 0.02S 20ms TIM_TimeBaseStructure.TIM_Period 200-1; // 自动重装载值 ARR TIM_TimeBaseStructure.TIM_Prescaler 8400-1; // 预分频值 PSCTIM_Period设置为199即200-1。这个值决定了PWM一个周期的“计数刻度”总数这里是200。TIM_Prescaler设置为8399即8400-1。这个值将系统时钟分频降低计数频率。配置好时基后接着设置PWM输出模式模式1高电平有效并使能定时器和通道输出。这样硬件上就会在指定的引脚我们之前宏定义的PA6输出一个默认占空比的50Hz PWM波了。3.2 角度设置函数Set_Servo_Angle(unsigned int angle)这是控制舵机转动的核心函数。输入一个0-180的角度值函数会自动计算出对应的PWM脉宽并更新到定时器的比较寄存器中。计算原理我们已经知道PWM周期对应200个计数刻度因为Period199。0.5ms脉宽对应0度2.5ms脉宽对应180度。首先将角度angle线性映射到脉宽时间0.5ms ~ 2.5ms脉宽(ms) 0.5 (angle / 180.0) * 2.0然后计算这个脉宽时间对应的计数刻度值计数刻度 (脉宽 / 20.0) * 200合并一下公式就得到了代码里的计算式ServoAngle (unsigned int)((0.5 (angle / 180.0) * 2.0) / 20.0 * 200);最后调用TIM_SetCompare1(BSP_SIG_TIMER, ServoAngle);将这个计算值写入定时器的通道1比较寄存器CCR1。定时器就会自动调整输出波形的占空比了。注意函数开头对角度做了限制if(angle180) angle180;这是一个很好的编程习惯防止输入错误值导致计算异常。3.3 头文件bsp_sg90.h头文件除了我们修改的宏定义主要就是三个函数的声明。确保在你的main.c里包含了这个头文件才能调用这些函数。4. 实战验证让舵机动起来代码移植和解析都完成了最后一步就是写个主程序来验证一下。下面是一个简单的测试例程它会让舵机先转到180度再回到0度然后开始在0到180度之间循环扫描。#include board.h #include bsp_uart.h #include stdio.h #include bsp_sg90.h // 包含舵机驱动头文件 int main(void) { int angle 0; // 定义一个角度变量 board_init(); // 开发板基础初始化系统时钟、GPIO等 uart1_init(115200U); // 初始化串口用于调试打印可选 SG90_Init(); // 初始化舵机PWM // 测试两个极限位置 Set_Servo_Angle(180); // 转到180度 delay_ms(1000); // 等待1秒让舵机到位 Set_Servo_Angle(0); // 转到0度 delay_ms(1000); // 等待1秒 // 循环扫描从0度逐步增加到180度 while(1) { Set_Servo_Angle(angle); // 设置当前角度 angle; // 角度加1 if( angle 180 ) // 如果超过180度归零 { angle 0; } delay_ms(10); // 延时10ms控制扫描速度 } }上电后的效果你会看到舵机先快速转到最右180度停顿一下再转到最左0度然后开始缓慢地从左向右匀速转动到达最右后立刻跳回最左如此循环。调试小技巧舵机不转首先检查接线。舵机一般有三根线棕色GND、红色VCC5V、橙色信号线。确保电源和地线接对信号线接在了你代码里定义的GPIO引脚上。舵机抖动或吱吱叫很可能是PWM频率不对。用示波器或者逻辑分析仪测量一下信号引脚输出的波形看周期是不是20ms50Hz高电平时间是否随角度变化在0.5ms-2.5ms之间。角度不准可以微调Set_Servo_Angle函数里的计算公式。比如如果转到90度时实际位置偏左可以尝试将公式中的0.5ms基准值稍微调大一点试试。希望这篇教程能帮你打通STM32控制SG90舵机的任督二脉。实际项目中你可能需要控制多个舵机原理都是一样的只需要使用不同定时器的不同通道即可。多动手试试遇到问题查查手册、测测波形嵌入式开发的乐趣就在于此。