尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32定时器驱动SG90舵机:PWM参数计算与Proteus仿真详解
前阵子有朋友给我看一个STM32F103C8T6的舵机控制demo卡在Proteus仿真里舵机怎么都不动。代码检查了几遍引脚也对着原理图核了好几轮最后发现是定时器PWM的时序参数全算错了。这种问题在刚接触舵机控制的时候特别典型——不是硬件难而是对PWM的周期、脉宽、计数器时钟这三者之间的关系没有彻底吃透。这篇文章我打算完整梳理一遍SG90舵机的控制原理、STM32F103C8T6用定时器输出PWM的配置方法、标准库代码怎么写再到Proteus仿真里如何搭工程、加载hex、看波形。内容不多但每一步我都会把“为什么这么做”讲清楚按着步骤走完你就能在自己的电脑上复现一个从0°转到180°的SG90仿真。1. 先交代一下为什么我选了“Proteus SG90 STM32F103C8T6”这套组合以前在学校实验里调舵机多数人的第一选择是Arduino。理由也简单Servo库把底层PWM全部封装好几行代码就能让舵机转动。但到了真实项目里往往会遇到一个非常现实的问题团队指定用STM32F103C8T6作为主控工程师手里可能只有最小系统板示波器不一定有舵机也不一定有多余的更不愿意为了验证一个PWM输出先去打板、焊线、接电源。这时候Proteus仿真就成了性价比最高的验证手段。我用Proteus做过不少仿真不管是51还是STM32它都能在一个虚拟环境里把外设模型、时钟、时序全部跑起来。对舵机控制来说Proteus能直观看到舵机指针随着PWM占空比变化而转动还能用虚拟示波器观察输出波形非常适合验证代码逻辑和参数计算是否正确。虽然它没法完全替代实物调试比如带载能力、实际力矩、机械阻尼这些在仿真里是不存在的但至少能把“时序对不对”“角度映射对不对”这类核心问题在硬件之前解决掉。SG90虽然是个很小的舵机但它内部包含了电机、减速齿轮、控制电路和电位器反馈是一个完整的闭环系统。用PWM控制它看起来简单但里面涉及到的技术点非常典型定时器频率计算、预分频系数、自动重载值、比较值、角度线性映射。这些东西放在STM32F103C8T6上做一遍以后再去控制无刷电调、云台电机、机械臂关节思路都是一样的。所以这篇文章适合谁看你不是那种只想要“复制粘贴就能跑”的代码的人而是想搞清楚参数为什么这么算、引脚为什么这么连、仿真为什么这么搭。如果你正好在学STM32定时器或者在准备某个Proteus课程设计那这篇内容应该能帮你省掉不少折腾的时间。2. SG90的控制原理20ms周期、脉宽与角度的线性映射SG90是市面上最常见的小型模拟舵机工作电压一般在4.8V到6V之间。它的控制信号规格写得很清楚需要50Hz的PWM信号也就是周期固定为20ms。实际决定舵机角度的不是这个20ms周期而是周期内高电平所占据的宽度。这里必须强调一个概念——我们关心的是脉冲宽度而不是笼统的“占空比”。虽然占空比和脉宽本质相关但在计算时大家习惯直接用微秒数因为这样更好理解。舵机内部有一个参考脉冲发生器和一个电位器反馈电路两者会形成一组内部脉冲。外部输入的控制脉宽与内部参考脉宽进行比较一旦存在差值控制电路就会驱动电机转动直到差值归零。这其实就是一个典型的闭环位置控制外部给定目标内部反馈实际位置电机去消除误差。理解了这个闭环过程就能明白为什么舵机对PWM脉冲的稳定性要求很高——如果脉冲宽度频繁跳变舵机会一直在“追赶”不断变化的目标表现出来就是抖动、来回摆动。关于脉宽和角度的对应关系我常用的标定方式如下表目标角度高电平脉宽假设计数频率1MHz时的比较寄存器值0°500us50045°1000us100090°1500us1500135°2000us2000180°2500us2500中间的任意角度按线性关系计算。比如需要45°脉宽就是500us加上45度在0到180之间所占的比例乘以2000us得到1000us。这个0.5ms到2.5ms的标定范围是我用SG90时比较习惯的取值。也有些人用1ms到2ms对应0°到180°这样做计算更简便但SG90在部分极限角度上的线性区间更接近0.5到2.5ms的范围。两者没有绝对对错关键是一旦选定映射关系代码里的换算公式就要保持一致。那为什么要用定时器硬件PWM而不直接用GPIO翻转加延时很多人一开始都会想我把引脚拉高延时1.5ms再拉低延时18.5ms不就是一个90°的PWM吗功能上确实能转但这种实现方式存在一个致命问题——所有的延时都不能被打断。只要系统里有串口中断、定时器中断、按键扫描哪怕只是一个短暂的中断服务函数脉冲宽度就会发生偏移舵机就会抖动。硬件PWM则完全不同定时器在后台持续输出脉冲CPU全程不参与脉冲生成哪怕主循环在跑复杂算法PWM的周期和脉宽依然纹丝不动。这也是STM32F103C8T6舵机控制里“为什么用定时器”最根本的答案。3. 定时器PWM参数计算PSC、ARR、CCR背后的完整推导STM32F103C8T6内部有多个定时器TIM1是高级定时器TIM2、TIM3、TIM4是通用定时器。这个实验里我选的是TIM2的通道1输出引脚对应PA0。为什么不选TIM1因为高级定时器除了PWM输出之外还附带互补输出、刹车输入、死区插入等一大堆功能对舵机这种应用来说完全用不上配置反而增加出错概率。TIM2是通用定时器结构清晰标准库调起来也简单。3.1 定时器时钟从哪里来为什么这里是72MHz要算PWM参数先要确认定时器工作的输入时钟频率。正常情况下标准库的SystemInit函数会把STM32F103C8T6配置成72MHz主频。TIM2挂在APB1总线上这里有个非常容易踩坑的细节当APB1预分频系数不为1时定时器时钟会被自动倍频最终TIM2的输入时钟依然是72MHz而不是APB1总线上的36MHz。很多人的PWM频率计算出错就是栽在这个地方。他们看到APB1是36MHz就直接拿36MHz去算定时器结果周期整整翻了一倍。所以在你动手算参数之前先确认你的工程系统时钟确实是72MHz。标准库工程里如果宏定义中配置了SYSCLK_FREQ_72MHzSystemInit会把时钟设置好如果没配置系统可能跑在8MHz或者其他频率后面的所有定时器参数都会跟着出错。3.2 三个关键寄存器的计算步骤定时器计算涉及三个寄存器PSC预分频器、ARR自动重载寄存器、CCR捕获比较寄存器。第一步选PSC。我希望让计数频率是一个好用的整数这样可以简化角度到计数值的换算。如果定时器输入时钟是72MHz我把PSC设为71那么实际计数频率是72MHz / (71 1) 1MHz也就是说计数器每计数一次对应的时间是1us。之所以要加1是因为预分频器的值是0到65535实际分频系数等于PSC1。这个细节在算参数时特别容易忘。第二步选ARR。舵机的PWM周期是20ms在1MHz计数频率下一个周期需要用掉20000个计数。因此ARR1等于20000寄存器里写的就是19999。代码里经常能看到TIM_Period 19999这样的赋值如果你反过来推它对应的就是20ms周期。第三步更新角度。PWM1模式下计数器从0向上计数计数值小于CCR时引脚输出有效电平大于等于CCR后引脚翻转一直保持到计数器溢出回落。因为计数频率是1MHz所以CCR的数值直接就可以理解为高电平微秒数。当我们需要1.5ms脉宽时CCR就填1500整个换算关系非常直观。角度到CCR的换算公式可以写成CCR 500 angle * 2000 / 180这个公式里的500对应0°脉宽2000是2500us减500us的总跨度后面再除以180是为了把比例摊到0到180度上。举两个例子验证一下角度为0时CCR等于500角度为180时CCR等于500加2000等于2500角度为90时CCR等于1500和前面表格完全吻合。3.3 如果主频不是72MHz怎么调整不是所有工程的主频都是72MHz。比如有人用的外部晶振不是8MHz或者启动文件里的时钟宏定义被改过系统主频可能是64MHz、48MHz甚至36MHz。这种情况下只要保证“计数频率换算后的微秒值”不变就行。比如系统时钟是64MHz想把计数频率仍做成1MHzPSC就设63。如果PSC已经是一个固定值那也没关系关键是ARR要满足周期等于20ms。比如计数频率不是1MHz而是500KHz一个20ms周期需要的计数就是10000ARR就设9999。角度对应CCR的换算也会随之变成脉宽乘以0.5。我的建议是尽量把计数频率凑成1MHz或者100KHz这类好算的整数后面做映射时会轻松很多调试时心算都能算出结果。4. 标准库初始化代码逐段讲解直接可以搬进Keil工程我用的是STM32标准外设库3.5版这是目前网上教程资料最丰富的版本。下面把工程里最核心的代码分块讲解你可以直接复制到自己的工程里只要时钟是72MHz参数就不用改。4.1 GPIO复用推挽配置TIM2的通道1默认映射在PA0所以GPIO初始化是这样的void Servo_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); }注意GPIO模式必须是复用推挽GPIO_Mode_AF_PP不是通用推挽GPIO_Mode_Out_PP。如果错配成通用推挽定时器的PWM信号不会正确映射到引脚上PA0可能始终是低电平或者高电平在Proteus仿真里最典型的现象就是舵机完全不动。同时要把AFIO时钟打开虽然这里没用到引脚重映射但标准库规范里配置复用功能时打开它是个好习惯。4.2 定时器和比较模块初始化void Servo_TIM_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Prescaler 71; // 72MHz / (711) 1MHz TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period 19999; // 20ms周期 TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 1500; // 初始高电平1.5ms对应90度 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_Cmd(TIM2, ENABLE); }这段配置里有两个新手最容易漏掉的地方。第一TIM_Pulse赋的是初始比较值1500对应90°位置这样上电后舵机首先会回到一个可预测的中间角度方便观察。做实际项目时我建议把初始值设成500也就是0°防止设备上电瞬间舵机猛甩到一侧造成机械冲击。第二TIM_Cmd(TIM2, ENABLE)必须调用否则定时器不会开启。配置了比较输出结构体不代表定时器已经开始工作。我看到过不少代码初始化函数写完了却看不到这一行仿真时波形死活出不来。4.3 角度设置函数与主循环void Servo_SetAngle(uint16_t angle) { uint16_t compare; if (angle 180) angle 180; compare 500 (uint32_t)angle * 2000 / 180; TIM_SetCompare1(TIM2, compare); }这里的角度值是整型0到180。为什么中间要把angle强转成uint32_t再乘2000因为angle本身是uint16_t最大65535而180乘以2000等于360000已经超出16位整数的表示范围。如果不用32位中间量参与计算编译器可能产生错误的截断结果。这种小细节在C语言里非常隐蔽仿真时角度经常跳到奇怪的值很多时候就是这个原因。主函数就简单了int main(void) { Delay_Init(); Servo_GPIO_Config(); Servo_TIM_Config(); while (1) { Servo_SetAngle(0); Delay_ms(1000); Servo_SetAngle(90); Delay_ms(1000); Servo_SetAngle(180); Delay_ms(1000); } }Delay_ms可以用SysTick实现也可以用最朴素的for循环延时。Proteus仿真里对延时的精度要求并不高只要保证每次角度变化之间有足够的时间让舵机转到目标位置即可。建议延迟时间不低于300ms太短的话舵机机械上跟不上仿真里就会看到指针来回乱摆。5. Proteus仿真工程搭建从新建工程到加载hex文件全程Proteus仿真STMF103C8T6舵机控制版本选择上建议用8.13以上我自己用的是8.15。老版本对STM32F103C8T6的外设模型支持不完整有些甚至加载不了hex文件折腾半天可能只是版本问题。5.1 新建工程与元件查找打开Proteus新建项目时选择Schematic Capture不需要生成PCB。进入原理图界面后从左侧工具栏点Component Mode再点P按钮打开元件库搜索。在搜索框里输入STM32F103C8T6找到后双击放置到画布。接着搜索SG90舵机如果你的Proteus版本元件库自带SG90直接添加如果没有搜索Servo或者Motor-Servo类元件替代。用通用舵机模型不影响本次PWM逻辑验证关键是舵机的信号输入引脚要接对。5.2 MCU属性与晶振频率设置放置好STM32F103C8T6之后双击打开属性框里面有一项Crystal Frequency默认值有些版本是8MHz有些是12MHz需要手动确认。标准库SystemInit默认使用8MHz外部晶振然后通过PLL倍频到72MHz所以这里必须填8MHz。如果你使用的不是8MHz晶振或者修改过HSE_VALUE那这一栏要同步改。别忘了给舵机接电源和地。舵机控制信号接PA0舵机VCC接一个5V电源端子GND和MCU的GND连在一起。Proteus仿真中电源可以通过左侧Power Terminal工具放置VCC和GND也可以直接用电源探针重点是逻辑关系要正确别让信号线和电源线混在同一个网络里。5.3 加载hex文件并启动仿真在Keil里编译工程之前先确认Output选项卡里勾选了Create HEX File。不勾选的话编译后只有.axf文件Proteus是加载不了程序的。编译完成后回到Proteus双击STM32F103C8T6芯片在Program File一栏填入Keil生成的.hex文件路径或者点击文件选择按钮手动选择。设置完成后点击左下角的运行按钮仿真就会启动。如果一切正常应该能看到SG90的指针在0°、90°、180°三个位置之间反复摆动。如果你需要确认波形可以在PA0引脚上接一台虚拟示波器。Proteus左下角仪器面板里有OSCILLOSCOPE双击放置把探头连到PA0的网络上运行后暂停就能看到标准的50Hz PWM波形高电平宽度分别对应500us、1500us和2500us。6. 实测中踩过的坑以及我的完整排查链路这个过程看似简单实际做的时候坑不少。我把自己踩过的几个典型问题整理出来并附上完整的排查顺序希望能帮你少走弯路。6.1 坑一Proteus一直报错找不到hex文件这个问题几乎每次都发生在Keil配置上。Options for Target - Output页面里有一个Create HEX File选项默认是不勾选的。很多人用J-Link调试习惯了只关心能不能在线调试根本不注意有没有生成hex文件。Proteus加载程序文件的机制和调试器完全不同它需要的是一个独立的hex文件没有它就是跑不起来。所以第一次编译前先把这一项勾上。6.2 坑二PA0引脚没有任何PWM波形输出舵机纹丝不动这种问题不能一上来就怀疑硬件建议按下面的顺序排查。第一步确认GPIO模式。PA0是否配置成GPIO_Mode_AF_PP如果错误配成GPIO_Mode_Out_PP引脚不会输出PWM。第二步确认定时器时钟是否使能标准库里要调用RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE)漏掉这行RCC使能寄存器写了也白写。第三步确认TIM_Cmd(TIM2, ENABLE)有没有调用。第四步确认初始化完成后主函数里有没有调用角度设置函数。如果初始比较值保持默认而PWM又没有启动输出依然是低电平。全部排查完再到Proteus里看PA0波形不要一上来就怀疑模型有问题。STM32F103C8T6的Proteus模型虽然不算完美但PWM这种基本功能是稳定可靠的。6.3 坑三输出波形周期变成40ms或者舵机乱抖这个坑百分之百是定时器时钟频率算错了。当你按72MHz算好参数结果输出频率变成25Hz而不是50Hz时基本可以确定你误把APB1总线时钟36MHz当成了定时器输入时钟。前面说过TIM2挂载在APB1上当APB1分频系数大于1时定时器时钟会自动倍频回72MHz。如果你在预分频计算时把PSC设为35那么计数频率是36MHz除以36等于1MHz吗表面上看也是1MHz但实际定时器接收到的时钟是72MHz按PSC35算出来的真实计数频率是2MHz。于是20ms周期对应40000个计数ARR还是19999实际周期就被拉长到40ms舵机会表现为转速异常或者抖动。解决方法是回到代码里确认PSC等于71而不是35。如果你实在拿不准当前定时器时钟可以在Proteus仿真里直接看波形的高电平时长和周期做反推这是最直观的验证方式。6.4 坑四Proteus仿真正常但实物上舵机不动仿真和实物之间最大的差异是电气特性。STM32F103C8T6的引脚输出高电平是3.3V而SG90舵机的控制信号线需要参考电源电压。多数SG90在3.3V高电平下可以正常识别因为TTL高电平阈值大约在2V左右3.3V已经算可靠高电平。但部分舵机的控制板设计比较保守会对输入电平有更严格要求这时就不能直接用MCU引脚去驱动需要加一级三极管电平转换或者使用3.3V到5V的转换电路把PWM信号幅值抬上去。另外实物SG90的电源一定要单独考虑不要直接从STM32最小系统板上的3.3V引脚取电。舵机空载电流可能几十毫安堵转时能达到几百毫安甚至更高直接从MCU的LDO取电很容易把板子拉复位。正确做法是使用5V电源给舵机供电STM32和舵机共地即可。我在实际调试中养成了一个习惯不管仿真跑得多顺上实物之前一定先用虚拟示波器确认PA0的PWM波形频率是50Hz左右再确认高电平时间是否等于目标脉宽。这两点验证通过舵机基本上就能动了。如果波形都不正常先别急着怀疑舵机回头把定时器参数和GPIO配置重新过一遍。搞嵌入式就是这样大多数问题不是出在玄学而是出在某个理所当然的细节上。把每个参数都当成变量去验证反而能更快找到问题。
RELATED

相关推荐

【老王架构专栏】OpenClaw 一键部署平台盘点:TaoToken 统一 Key 接入与 config.toml 配置骨架

【老王架构专栏】OpenClaw 一键部署平台盘点:TaoToken 统一 Key 接入与 config.toml 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/28 7:21:00
SpringBoot+Vue医院就诊管理系统开发实践与选型解析

SpringBoot+Vue医院就诊管理系统开发实践与选型解析

"SpringBootVue的医院就诊管理系统"这类题目,在毕业设计和中小型医疗信息化项目里几乎是绕不开的选题。它既不单纯是CRUD堆积,又不像大厂高并发医疗平台那样复杂,正好卡在"能完整落地"和"有业务深度"之间。我用…

📅 2026/9/28 7:21:00
构建基于 AMD 显卡的高性价比大模型推理集群:TaoToken 统一 API 接入与 ROCm 配置实战

构建基于 AMD 显卡的高性价比大模型推理集群:TaoToken 统一 API 接入与 ROCm 配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/28 7:16:00
MORE NEWS

更多资讯

📰

React Native异步状态更新与渲染机制全面解析

我先跟你说个特别真实的场景:RN 项目里调完setState,紧接着下一行打印this.state,结果拿到的还是旧数据。你以为是代码写错了,查了半天,发现不是 bug,是机制。状态更新是异步的,渲染是 React 自…

📰

Eclipse怎么做网页免费工具全解析:备案别花冤枉钱

Eclipse怎么做网页免费工具全解析:备案别花冤枉钱 备案流程一头雾水?很多人第一反应是找代办,结果一问多少钱,从几百到几千都有,心里没底。其实,对于用 Eclipse…

📰

浪网站制作对比评测:告别拖延,3招搞定技术选型

浪网站制作对比评测:告别拖延,3招搞定技术选型 改个按钮颜色,建站公司让你等一周?这种憋屈谁受得了? 别骂了,先看看你的网站是用什么技术堆的。很多老板不懂技术,只懂扔需求,结果被外包坑得底掉。今天咱们不整虚的,直接上硬菜,通过 对比评测…

📰

做网站需要提供什么条件?避开被黑挂马坑,选对哪家好

做网站需要提供什么条件?避开被黑挂马坑,选对哪家好 网站被黑挂马不知道怎么办?别慌,先自查。很多老板找建站公司,问“做网站需要提供什么条件”,结果只给了个Logo和几段文字,上线没三天,网站变成赌博广告,百度也搜不到,找服务商推诿,找技术不…

📰

小项目开发sop流程

文章目录从零开始做项目:一份完整的个人项目开发流程指南(以贪吃蛇为例)一、立项二、可行性分析技术可行性要分析什么?🌰 实战例子:开发一个贪吃蛇三、需求分析四、功能流程图五、产品原型图六、架构搭建为…

📰

S905L3SB盒子刷机指南:安卓9.0线刷固件+当贝桌面纯净版集成

如果你手里有一台运营商送的IPTV盒子,芯片方案是晶晨S905L3SB,那大概率你和我一样,拿到手没几天就被它自带桌面里的广告和推荐位烦得不行。开机先放十几秒广告,切个频道又弹个充值页面,想装个第三方App还被各种限制卡住…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬