STM32驱动SG90舵机:从PWM原理到蓝牙远程控制实战
1. 认识SG90舵机与PWM控制第一次拿到SG90这个小家伙时我差点以为是个玩具电机。直到把它接上STM32看到它能精准地停在指定角度才意识到这玩意儿在机器人、智能家居里有多实用。SG90是一种微型舵机三根线分别接电源红色、地线棕色和信号线黄色。它的核心秘密就在于那根黄色信号线——通过PWM脉冲信号我们可以像操纵木偶一样控制它的转动角度。PWM脉冲宽度调制听起来高大上其实原理特别生活化。想象你在用老式水龙头接水快速开关水龙头如果开的时间长、关的时间短接到的水就多反之水就少。PWM也是这样通过调节高电平的持续时间比例占空比就能控制设备的工作状态。对于SG90来说它需要的是频率50Hz周期20ms的PWM信号其中高电平持续时间在0.5ms到2.5ms之间变化时舵机会对应转动0°到180°。这里有个新手容易混淆的点占空比和角度是线性对应的。具体对应关系如下表示高电平时间占空比舵机角度0.5ms2.5%0°1.0ms5%45°1.5ms7.5%90°2.0ms10%135°2.5ms12.5%180°我第一次调试时就犯了个错误以为占空比越大舵机转得越快。实际上SG90是位置舵机占空比决定的是目标位置而不是转速。比如给2.5%占空比舵机会直接转到0°并保持而不是持续旋转。2. STM32定时器配置实战要让STM32产生精准的PWM信号定时器是关键。我常用TIM3的通道2引脚映射到PB5。先说说配置要点时钟配置STM32F103的APB1总线时钟默认72MHz我们需要通过预分频降频。比如设置预分频值为7200-1实际分频后时钟频率变为10kHz每计数一次0.1ms。自动重装载值设为200-1这样PWM周期就是(200)*(0.1ms)20ms正好满足SG50要求。输出比较模式选择PWM模式1有效电平设为低电平。这里有个坑如果极性配置反了舵机会乱转。我第一次就栽在这里舵机像抽风一样乱抖。具体代码实现如下关键部分加注释void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_OCInitTypeDef TIM_OCInitStruct; // 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 配置PB5为复用推挽输出 GPIO_InitStruct.GPIO_Pin GPIO_Pin_5; GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStruct); // 定时器基础配置 TIM_InitStruct.TIM_Period 200-1; // 自动重装载值 TIM_InitStruct.TIM_Prescaler 7200-1; // 预分频值 TIM_InitStruct.TIM_ClockDivision TIM_CKD_DIV1; TIM_InitStruct.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_InitStruct); // PWM输出配置 TIM_OCInitStruct.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStruct.TIM_OCPolarity TIM_OCPolarity_Low; // 低电平有效 TIM_OC2Init(TIM3, TIM_OCInitStruct); // 通道2 TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE); }配置成功后用示波器看PB5引脚应该能看到稳定的20ms周期方波。如果没有信号先检查定时器和GPIO时钟是否开启引脚模式是否正确设置为复用推挽输出自动重装载值和预分频值计算是否正确3. 角度控制与蓝牙指令解析有了PWM信号接下来实现角度控制函数。根据之前的占空比表格我们需要将角度转换为比较寄存器的值。这里有个计算公式比较值 195 - (角度 / 180 * 20)比如要让舵机转到90°比较值 195 - (90/180*20) 185对应的控制函数如下void Set_Angle(uint8_t angle) { if(angle 180) angle 180; // 限制最大角度 uint16_t cmp 195 - (angle * 20 / 180); TIM_SetCompare2(TIM3, cmp); }现在加入蓝牙控制。我用的是HC-05模块通过串口接收手机指令。当收到字符1时让舵机转到180°收到0回归零度void USART2_IRQHandler() { if(USART_GetITStatus(USART2, USART_IT_RXNE)) { char cmd USART_ReceiveData(USART2); switch(cmd) { case 1: Set_Angle(180); break; // 转到180° case 0: Set_Angle(0); break; // 归零 case 9: Set_Angle(90); break; // 中间位置 } USART_ClearITPendingBit(USART2, USART_IT_RXNE); } }实际测试时发现一个问题如果快速发送多个角度指令舵机会出现卡顿。解决方法是在角度切换时加入延时case 1: for(int i0; i180; i10) { Set_Angle(i); delay_ms(50); // 每50ms转动10° } break;4. 完整工程与调试技巧把以上模块整合后完整的工程应该包含PWM初始化配置角度控制函数蓝牙串口中断处理必要的延时函数调试时我总结了几条经验舵机不转先检查供电是否足够建议5V/1A以上再用万用表量信号线电压角度不准用示波器测量PWM高电平时间是否精确0.5ms~2.5ms蓝牙无响应检查模块是否进入AT模式波特率是否匹配通常9600或115200代码优化将角度映射封装成函数避免硬编码数值最后分享一个实用技巧如果想实现慢速转动效果不要直接修改PWM频率会破坏舵机控制协议而是用for循环逐步改变角度值中间加小延时。就像这样// 平滑转动示例 void Smooth_Rotate(uint8_t target_angle) { uint8_t current Get_Current_Angle(); // 需要自己记录当前角度 int step (target current) ? 1 : -1; while(current ! target) { current step; Set_Angle(current); delay_ms(20); // 控制转动速度 } }这个项目最让我有成就感的部分是看到手机发送一个指令后舵机真的像被施了魔法一样转到指定位置。这种软硬件结合的实现方式正是嵌入式开发的魅力所在。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2467378.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!