基于GD32VW553的SG90舵机PWM驱动与角度控制实战
基于GD32VW553的SG90舵机PWM驱动与角度控制实战最近在做一个机器人小项目需要用GD32VW553开发板控制舵机正好手头有最常见的SG90舵机。很多刚开始接触嵌入式控制的朋友可能对如何用单片机精确控制舵机角度有点摸不着头脑。其实原理并不复杂关键是要理解PWM信号和舵机角度之间的对应关系。今天我就以GD32VW553为例手把手带你实现SG90舵机的PWM驱动和角度控制。我会从硬件连接到软件配置再到代码编写一步步详细讲解保证你跟着做一遍就能完全掌握。1. 认识SG90舵机这个小家伙怎么工作在开始动手之前咱们先来了解一下今天的主角——SG90舵机。这可能是市面上最常见、最便宜的9克微型舵机了很多机器人、航模项目都会用到它。1.1 SG90的基本参数根据资料SG90有几个关键参数需要记住工作电压3V~7.2V。实际使用中用5V供电最稳定这也是大多数开发板都能提供的电压。控制方式PWM脉冲宽度调制。这是控制舵机的核心后面会详细讲。转动角度180度。注意市面上还有360度连续旋转的舵机那种只能控制转速不能控制角度。咱们用的是180度版本。转动速度大约0.18秒/60度。这个速度不算快所以如果你控制舵机角度变化太快它可能反应不过来。1.2 PWM控制原理舵机怎么知道要转多少度SG90舵机有三根线棕色线GND地线红色线VCC电源接5V黄色线信号线接MCU的PWM输出引脚舵机内部有个控制电路它通过检测信号线上的PWM脉冲宽度来判断要转到什么角度。PWM信号有几个关键参数周期通常是20ms频率50Hz脉宽高电平持续的时间这个时间决定了舵机角度具体的对应关系是这样的0.5ms脉宽→ 舵机转到0度位置1.0ms脉宽→ 舵机转到45度位置1.5ms脉宽→ 舵机转到90度位置中间位置2.0ms脉宽→ 舵机转到135度位置2.5ms脉宽→ 舵机转到180度位置你可以把脉宽想象成给舵机的指令脉宽越长舵机转的角度就越大。这个线性关系是咱们后面写代码的基础。2. 硬件连接把舵机接到GD32VW553上硬件连接很简单但一定要接对不然舵机可能不工作甚至损坏。2.1 引脚选择根据原始资料我们使用GD32VW553的PA1引脚来控制SG90舵机。为什么选这个引脚因为PA1是TIMER1的通道1TIMER1_CH1可以直接输出PWM信号。注意GD32VW553的很多引脚都有复用功能一定要查数据手册确认引脚支持PWM输出。PA1的复用功能1AF1就是TIMER1_CH1。2.2 接线方法按照下面的表格连接舵机线颜色开发板接口说明棕色线GND接地一定要接不然没有参考电平黄色线PA1信号线接TIMER1_CH1输出红色线5V0电源接开发板的5V输出连接时注意先断电再接线防止短路确保电源正负极不要接反如果舵机负载较重建议单独供电不要直接从开发板取电3. 软件驱动编写创建BSP层代码接下来是重头戏——写代码。咱们采用模块化的方式创建两个文件bsp_sg90.h和bsp_sg90.c把舵机相关的驱动都放在这里面。3.1 头文件定义bsp_sg90.h头文件主要定义引脚、定时器配置以及函数声明。#ifndef BSP_CODE_BSP_SG90_H_ #define BSP_CODE_BSP_SG90_H_ #include gd32vw55x.h #include systick.h /* 使能相关时钟的宏定义 */ #define Module_RCU_Enable() \ rcu_periph_clock_enable(RCU_GPIOA); \ rcu_periph_clock_enable(RCU_TIMER1); /* 定时器相关定义 */ #define BSP_PWM_TIMER_RCU RCU_TIMER1 // 定时器时钟 #define BSP_PWM_TIMER TIMER1 // 使用TIMER1 #define BSP_PWM_TIMER_CH TIMER_CH_1 // 使用通道1 /* GPIO引脚定义 - PA1对应TIMER1_CH1 */ #define BSP_PWM_RCU RCU_GPIOA #define BSP_PWM_PORT GPIOA #define BSP_PWM_PIN GPIO_PIN_1 #define BSP_PWM_AF GPIO_AF_1 // 复用功能1 /* 函数声明 */ void SG90_Init(void); // 舵机初始化 void Set_SG90_Servo_Angle(uint32_t angle); // 设置舵机角度 uint8_t Get_SG90_Servo_Angle(void); // 获取当前角度 #endif /* BSP_CODE_BSP_SG90_H_ */3.2 源文件实现bsp_sg90.c这是核心的实现部分我会逐段解释。#include bsp_sg90.h /* 全局变量记录当前舵机角度 */ uint8_t Servo_Angle 0;首先定义一个全局变量来记录当前角度这样我们就能随时知道舵机在什么位置。3.2.1 角度设置函数这个函数是整个驱动的核心它把角度值转换成PWM的脉宽。void Set_SG90_Servo_Angle(uint32_t angle) { /* 角度范围限制在0-180度 */ if(angle 180) { angle 180; } /* 更新当前角度 */ Servo_Angle angle; /* 关键计算将角度转换为PWM比较值 */ // 0.5ms对应的计数值 ≈ 500 // 2.5ms对应的计数值 ≈ 2500 float min_count 500.0f; // 0度对应的计数值 float max_count 2500.0f; // 180度对应的计数值 float range max_count - min_count; // 有效范围 /* 线性计算角度 - 计数值 */ float ServoAngle min_count (((float)angle / 180.0f) * range); /* 配置定时器通道的比较值改变PWM脉宽 */ timer_channel_output_pulse_value_config(BSP_PWM_TIMER, BSP_PWM_TIMER_CH, (uint32_t)ServoAngle); }这里有个重要的计算过程我解释一下定时器时钟配置后计数频率是1MHz后面会讲怎么配置的1MHz意味着每1us计数1次0.5ms 500us所以对应计数值5002.5ms 2500us所以对应计数值2500中间的角度就按比例线性计算3.2.2 角度读取函数这个函数很简单就是返回当前记录的角度值。uint8_t Get_SG90_Servo_Angle(void) { return Servo_Angle; }提示这个函数返回的是我们软件记录的角度不是从舵机实际读取的。舵机本身没有角度反馈功能所以我们需要自己记录设置的角度。3.2.3 初始化函数初始化函数比较长但每一步都很重要。我会分段解释。void SG90_Init(void) { /* 第一步使能相关时钟 */ Module_RCU_Enable(); // 使能GPIOA和TIMER1的时钟 /* 第二步配置GPIO引脚为复用功能 */ gpio_mode_set(BSP_PWM_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, BSP_PWM_PIN); gpio_output_options_set(BSP_PWM_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, BSP_PWM_PIN); gpio_af_set(BSP_PWM_PORT, BSP_PWM_AF, BSP_PWM_PIN); // 设置为AF1功能 /* 第三步配置定时器基本参数 */ timer_parameter_struct timer_initpara; /* 使能定时器时钟 */ rcu_periph_clock_enable(BSP_PWM_TIMER_RCU); /* 配置定时器时钟预分频器CK_TIMERx 2 x CK_APB1 2x80M 160MHz */ rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL2); /* 复位定时器到默认状态 */ timer_deinit(BSP_PWM_TIMER); /* 配置定时器参数 */ timer_initpara.prescaler 160 - 1; // 预分频值160MHz/160 1MHz timer_initpara.alignedmode TIMER_COUNTER_EDGE; // 边沿对齐模式 timer_initpara.counterdirection TIMER_COUNTER_UP; // 向上计数 timer_initpara.period 20000 - 1; // 自动重装载值周期20ms timer_initpara.clockdivision TIMER_CKDIV_DIV1; // 时钟分频 timer_initpara.repetitioncounter 0; // 重复计数器 /* 初始化定时器 */ timer_init(BSP_PWM_TIMER, timer_initpara); /* 第四步使能定时器 */ timer_enable(BSP_PWM_TIMER); /* 第五步配置定时器输出比较功能 */ timer_oc_parameter_struct timer_ocintpara; timer_ocintpara.ocpolarity TIMER_OC_POLARITY_HIGH; // 输出极性为高 timer_ocintpara.outputstate TIMER_CCX_ENABLE; // 使能输出 timer_channel_output_config(BSP_PWM_TIMER, BSP_PWM_TIMER_CH, timer_ocintpara); /* 第六步初始比较值设为0 */ timer_channel_output_pulse_value_config(BSP_PWM_TIMER, BSP_PWM_TIMER_CH, 0); /* 第七步配置为PWM模式0 */ timer_channel_output_mode_config(BSP_PWM_TIMER, BSP_PWM_TIMER_CH, TIMER_OC_MODE_PWM0); /* 第八步禁用影子寄存器立即更新 */ timer_channel_output_shadow_config(BSP_PWM_TIMER, BSP_PWM_TIMER_CH, TIMER_OC_SHADOW_DISABLE); /* 第九步使能自动重装载影子寄存器 */ timer_auto_reload_shadow_enable(BSP_PWM_TIMER); }这里有几个关键点需要理解时钟配置GD32VW553的APB1时钟是80MHz经过预分频器×2后定时器时钟变成160MHz。我们再通过预分频值160分频得到1MHz的计数频率。周期计算我们要产生20ms周期的PWM信号。计数频率1MHz 1,000,000次/秒20ms周期对应的计数值1,000,000 × 0.02 20,000所以自动重装载值设为20000-1因为从0开始计数脉宽范围前面说了0.5ms-2.5ms对应0-180度。0.5ms 500us → 计数值5002.5ms 2500us → 计数值2500这个范围在20ms周期内是合理的4. 主程序测试让舵机动起来驱动写好了现在来写主程序测试一下。#include gd32vw55x.h #include systick.h #include stdio.h #include main.h #include gd32vw553h_eval.h #include bsp_sg90.h int main(void) { /* 系统初始化 */ systick_config(); eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1); /* 初始化LED、串口等外设 */ gd_eval_led_init(LED1); gd_eval_com_init(EVAL_COM0); /* 打印系统信息 */ printf(\r\n Welcome to use the LC-GD32VW553-HMQ6 development board \r\n); /* 初始化SG90舵机 */ SG90_Init(); printf(\r\nSG90 Init Success\r\n); /* 测试1先转到180度位置等待1秒 */ Set_SG90_Servo_Angle(180); delay_1ms(1000); /* 测试2再转到0度位置等待1秒 */ Set_SG90_Servo_Angle(0); delay_1ms(1000); printf(\r\nSG90 Set Angle Success\r\n); /* 测试3让舵机从0度慢慢转到180度 */ uint8_t i 0; while(1) { Set_SG90_Servo_Angle(i); if(i 180) { i 0; } delay_1ms(10); // 每10ms变化1度 } }这个测试程序做了三件事初始化后先让舵机转到180度最右等待1秒再转到0度最左等待1秒最后进入循环让舵机从0度慢慢转到180度然后再从头开始5. 调试技巧和常见问题在实际项目中你可能会遇到一些问题这里分享几个我踩过的坑5.1 舵机不转动如果舵机完全不动按这个顺序检查电源问题用万用表量一下舵机电源脚有没有5V电压地线问题确保MCU和舵机共地信号线连接确认黄色线接到了PA1引脚代码问题检查定时器是否使能PWM输出是否配置正确5.2 舵机抖动或角度不准电源功率不足如果舵机负载较重开发板的5V输出可能带不动。可以给舵机单独供电但要确保和MCU共地。PWM频率不对SG90要求50Hz20ms周期如果频率偏差太大舵机可能工作不正常。脉宽范围不对确认计算是否正确0.5ms-2.5ms对应500-2500的计数值。5.3 角度有偏差有时候你会发现设置90度舵机可能停在85度或95度位置。这是因为舵机本身有机械误差电源电压影响电压低时扭矩小可能到不了指定位置负载影响如果舵机轴上有负载可能需要更大的扭矩解决方法在代码中做校准比如实际测试0度、90度、180度对应的准确计数值适当增加死区避免在极限位置卡住5.4 多个舵机控制如果你要控制多个舵机有几种方案每个舵机用一个定时器通道GD32VW553的定时器通常有4个通道可以控制4个舵机使用多个定时器如果需要更多舵机可以用多个定时器软件PWM如果对精度要求不高可以用GPIO模拟PWM但会占用CPU资源6. 实际项目应用建议在实际项目中控制舵机我还有几个经验分享平滑运动不要突然让舵机从0度跳到180度这样冲击很大。可以逐步改变角度比如每次变化1-2度中间加个小延时。电源处理如果控制多个舵机一定要用单独的电源模块供电不要直接从开发板取电。开发板的LDO电流有限多个舵机同时工作可能重启。机械保护舵机转到极限位置时不要长时间保持容易烧坏。可以在软件中设置软限位比如实际只让舵机在10-170度范围内运动。状态保存如果系统需要断电记忆可以把舵机角度保存到Flash中上电后恢复到之前的位置。错误处理增加超时检测如果舵机卡住或异常及时停止输出PWM防止持续堵转烧坏。通过这个实战项目你应该已经掌握了用GD32VW553控制SG90舵机的完整流程。从硬件连接到软件驱动再到实际调试每个环节我都尽量把原理讲清楚。在实际项目中你可能需要根据具体需求调整参数但基本的框架和思路是一样的。下次如果你要做机械臂、云台或者机器人关节就可以用这套代码作为基础快速实现舵机控制功能了。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2415584.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!