STM32 PWM控制舵机

本文介绍了舵机的类型、工作原理,阐述了PWM的概念、频率、周期和占空比。详细说明了舵机与STM32的硬件连线,给出相关代码。作者在学习中遇到修改通道无法实现的问题,后续打算将舵机与超声波测距代码整合,制作基于STM32的感应垃圾桶。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

舵机简介

舵机是指在自动驾驶仪中操纵飞机舵面(操纵面)转动的一种执行部件。分有:①电动舵机,由电动机、传动部件和离合器组成。接受自动驾驶仪的指令信号而工作,当人工驾驶飞机时,由于离合器保持脱开而传动部件不发生作用。②液压舵机,由液压作动器和旁通活门组成。当人工驾驶飞机时,旁通活门打开,由于作动器活塞两边的液压互相连通而不妨碍人工操纵。此外,还有电动液压舵机,简称“电液舵机”。
在这里插入图片描述

舵机工作原理

舵机的主要组成部分为伺服电机,所谓伺服就是服从信号的要求而动作。在信号来之前,转子停止不动;信号来到之后,转子立即运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。
舵机接收的是PWM信号,当信号进入内部电路产生一个偏置电压,触发电机通过减速齿轮带动电位器移动,使电压差为零时,电机停转,从而达到伺服的效果。简单来说就是给舵机一个特定的PWM信号,舵机就可以旋转到指定的位置。

在这里插入图片描述
舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。
一般来说,舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。
在这里插入图片描述
舵机是通过高电平的占空比来调节输出轴的角度,舵机的驱动信号是50Hz的方波信号,每个周期为20ms,其中高电平的占比在0.5ms-2.5ms之间。
占空比 = t/T

t = 0.5ms——————-舵机会转到 0 °
t = 1.0ms——————-舵机会转到 45°
t = 1.5ms——————-舵机会转到 90°
t = 2.0ms——————-舵机会转到 135°
t = 2.5ms——————-舵机会转到 180°

因为舵机的驱动信号时50HZ,就能得到周期

T=  1/f=1/50=0.02s=20ms

因此可以计算出TIM_Period和TIM_Prescaler的值。
PWM周期为20ms = (7200*200)/72000000=0.02

什么是PWM

简介
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽
度的控制。STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出!
PWM的频率
是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);也就是说一秒钟PWM有多少个周期。
PWM的周期

T=1/f
周期=1/频率

根据舵机50Hz = 20ms 一个周期,如果频率为50Hz ,也就是说一个周期是20ms 那么一秒钟就有 50次PWM周期。
PWM占空比
是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,由于PWM周期为20ms,所以(以舵机会转动 45°为例),占空比就应该为1ms/20ms = 5%,所以TIM_SetCompare1的 TIMx 捕获比较 1 寄存器值就为200-200*5% = 190
单位: % (0%-100%)

硬件连线

舵机红线-----------5V
舵机棕线-----------GND
舵机黄线-----------PA6

代码

servo.c

#include "servo.h"

/*我这里采用通用计时器3*/
static void Servo_GPIO_Config()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	/*开启GPIO时钟*/
	RCC_APB2PeriphClockCmd(BASIC_GPIOA_CLK,ENABLE);
	/*配置GPIO*/
	GPIO_InitStructure.GPIO_Pin			= GPIOA_CH1_PIN;//TIM_CH1 
	GPIO_InitStructure.GPIO_Mode		= GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStructure.GPIO_Speed		= GPIO_Speed_50MHz;
	/*初始化*/
	GPIO_Init(GPIOA_CH1_PORT,&GPIO_InitStructure);
	

}
static void Servo_TIM2_Config()
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	/*开启定时器时钟*/
	RCC_APB1PeriphClockCmd(BASIC_TIM_CLK,ENABLE);
	
	/*
	TIM2时基单元配置
	重要配置:TIM_Prescaler(预分频值)TIM_Period(定时周期)
	将TIM_Period设置成999,则计数器会数1000个(TIM_Period+1)
	节拍为一个定时器的周期。这个和后面需要配置的TIM_Pulse共同
	控制着定时器输出波形的占空比。
	TIM_Prescaler用来指定TIM时钟的分频值。也就是说它是进一步来
	分频TIM clock的。	简单来说也就是定时器每一次数数的时间间隔是多少。
	*/
	
	/*配置TIM3,一般的驱动PWM信号都是周期20毫秒,频率为50HZ。所以我们设定周期为20ms*/
	
	
											/*时基结构体成员配置*/
	//自动重装载值寄存器的值
	TIM_TimeBaseInitStructure.TIM_Period					= BASIC_TIM_Period;
	//时钟预分频数
	TIM_TimeBaseInitStructure.TIM_Prescaler				=	BASIC_TIM_Prescaler;
	//时钟分频因子
	TIM_TimeBaseInitStructure.TIM_ClockDivision		= TIM_CKD_DIV1;
	//计数器计数模式,设置向上计数,
	TIM_TimeBaseInitStructure.TIM_CounterMode			= TIM_CounterMode_Up;
	/*初始化结构体*/
	TIM_TimeBaseInit(BASIC_TIM,&TIM_TimeBaseInitStructure);
	
	
									/*定时器输出比较结构体成员初始化*/
	TIM_OCInitStructure.TIM_OCMode				= TIM_OCMode_PWM2;
	// 输出使能
	TIM_OCInitStructure.TIM_OutputState		= TIM_OutputState_Enable;
	//TIM_OCInitStructure.TIM_OutputNState	= TIM_OutputNState_Enable;
	// 设置占空比大小,主要取主函数里设置占空比这边先设置成0
	TIM_OCInitStructure.TIM_Pulse					= 0;
	// 输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCPolarity		= TIM_OCPolarity_High;
	//TIM_OCInitStructure.TIM_OCNPolarity		=	TIM_OCNPolarity_High;
	// 输出通道空闲电平极性配置
	TIM_OCInitStructure.TIM_OCIdleState		= TIM_OCIdleState_Set;
	//TIM_OCInitStructure.TIM_OCNIdleState  = TIM_OCNIdleState_Reset;
	/*初始化结构体*/
	TIM_OC1Init(BASIC_TIM,&TIM_OCInitStructure);
	
	//使能TIMx在CCR2上的预装载寄存器
	TIM_OC1PreloadConfig(BASIC_TIM,TIM_OCPreload_Enable);
	
	// 使能计数器
	TIM_Cmd(BASIC_TIM, ENABLE);	
	// 主输出使能,当使用的是通用定时器时,这句不需要
	TIM_CtrlPWMOutputs(BASIC_TIM, ENABLE);

}


void initServo(void)
{
	Servo_GPIO_Config();
	Servo_TIM2_Config();
	
}



main.c


#include "led.h"
#include "delay.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "beep.h"
#include "servo.h"

 int main(void)
 {	
	u16 i;
	delay_init();
	LED_Init();	 
	LED0 = 0;
	LED1 = 0;
	initServo();
	LED1 = 1;
	

	while(1){
		delay_ms(300);
		TIM_SetCompare1(BASIC_TIM, 175); //对应180度
		delay_ms(300);
		TIM_SetCompare1(BASIC_TIM, 180); //对应135度
		delay_ms(300);
		TIM_SetCompare1(BASIC_TIM, 185); //对应90度
		delay_ms(300);
		TIM_SetCompare1(BASIC_TIM, 190); //对应45度
		delay_ms(300);
		TIM_SetCompare1(BASIC_TIM, 195); //对应0度
		for(i = 195; i > 174; i--){
			TIM_SetCompare1(BASIC_TIM, i);
		}
		delay_ms(100);
		for(i = 175; i < 195; i++){
			TIM_SetCompare1(BASIC_TIM, i);
		}
	} 
}

实现结果

在这里插入图片描述

总结

通过这次舵机学习,让我初步了解到了STM32 PWM调制相关的知识,期间还是遇到问题。我本次采用的是TIM3通道1,但是我修改代码改成TIM3通道2就不能实现了,希望好心人能帮我解答一下。
接下来打算配合昨天写的超声波测距代码,两者整合形成一个基于STM32的感应垃圾桶。

参考:https://blog.csdn.net/qq_42866708/article/details/113355329

<think>我们之前讨论了使用Arduino和树莓派控制LD-20MG舵机,现在用户询问如何使用STM32控制舵机。由于STM32没有像Arduino那样的Servo库,我们需要使用定时器产生PWM信号来控制舵机。 LD-20MG舵机控制参数: - 周期:20ms(频率50Hz) - 脉冲宽度范围:0.5ms(0°)到2.5ms(180°) 在STM32中,我们可以通过配置定时器(TIM)的PWM模式来产生这样的信号。以下是一个通用的步骤: 1. 选择定时器和通道:STM32有多个定时器,每个定时器有多个通道。选择一个可用的定时器和通道(例如TIM1的通道1)。 2. 配置定时器:设置定时器的时钟频率和预分频器,使得定时器的计数周期为20ms(即50Hz)。例如,如果系统时钟为72MHz,我们可以设置预分频器为72-1,这样定时器时钟为1MHz(1us计数一次)。然后设置自动重装载寄存器(ARR)为20000-1(因为20ms/1us=20000个计数)。 3. 配置PWM模式:设置通道为PWM模式1或2,并设置比较捕获寄存器(CCR)的值,该值决定脉冲宽度(高电平时间)。例如,0.5ms对应500个计数(因为1us计数一次),2.5ms对应2500个计数。 4. 启动定时器:使能定时器和通道。 下面是一个使用STM32 HAL库的示例代码(以STM32F103系列为例,使用TIM1通道1,信号输出引脚为PA8): 步骤: - 配置TIM1的通道1为PWM输出 - 计算并设置ARR和CCR的值 代码示例(使用CubeMX生成初始化代码后,在main函数中添加):</think>### STM32控制LD-20MG舵机示例 LD-20MG舵机使用标准PWM控制协议(周期$20\ \text{ms}$,脉宽$0.5\text{ms}-2.5\text{ms}$)。以下是基于STM32 HAL库的完整实现: #### 硬件配置 1. **接线**: - 舵机信号线 → STM32 PWM引脚(如PA8) - 舵机电源 → 外部6V/2A电源 - 共地连接 → STM32 GND与电源GND 2. **CubeMX设置**: - 选择TIMx(如TIM1) - 通道配置为PWM模式 - 时钟源:内部时钟 - 预分频器:`72-1`(假设系统时钟72MHz) - 自动重载值:`20000-1`(20ms周期) - 脉冲宽度:初始值`1500`(90°位置) ```c // main.c #include "stm32f1xx_hal.h" TIM_HandleTypeDef htim1; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM1_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); // 启动PWM HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 1500); // 初始90° while (1) { // 0°位置 (脉宽500us) __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 500); HAL_Delay(1000); // 90°位置 (脉宽1500us) __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 1500); HAL_Delay(1000); // 180°位置 (脉宽2500us) __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 2500); HAL_Delay(1000); } } // TIM1初始化(CubeMX生成) void MX_TIM1_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; htim1.Instance = TIM1; htim1.Init.Prescaler = 72-1; // 1MHz计数频率 htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 20000-1; // 20ms周期 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim1, &sClockSourceConfig); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig); sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 1500; // 初始脉宽 sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1); } ``` #### 关键参数计算 1. **定时器频率**: $$ \text{定时器频率} = \frac{\text{系统时钟}}{\text{预分频值}+1} $$ $$ f_{\text{TIM}} = \frac{72\ \text{MHz}}{72} = 1\ \text{MHz} \quad (1\ \mu\text{s}/\text{计数}) $$ 2. **脉宽设置**: $$ \text{CCR值} = \frac{\text{目标脉宽}(\mu\text{s})}{1\ \mu\text{s}} $$ - $0^\circ$ → CCR=500 - $90^\circ$ → CCR=1500 - $180^\circ$ → CCR=2500 #### 角度控制函数 ```c // 设置舵机角度(0-180°) void Set_Servo_Angle(TIM_HandleTypeDef *htim, uint32_t Channel, float angle) { angle = (angle > 180) ? 180 : (angle < 0) ? 0 : angle; // 限幅 uint32_t pulse = 500 + (angle / 180.0) * 2000; // 脉宽映射 __HAL_TIM_SET_COMPARE(htim, Channel, pulse); } ``` > **注意事项**: > 1. 使用`__HAL_TIM_SET_COMPARE()`直接修改CCR寄存器,避免重载周期冲突[^1] > 2. 舵机电源必须独立(峰值电流$1.2\ \text{A}$),STM32仅提供信号 > 3. 机械限位保护:软件限制角度范围`0°-180°` > 4. 消抖处理:角度变化后延迟$15\ \text{ms}$再执行下一步操作 #### 进阶功能 - **多路控制**:使用多定时器通道(如TIM1的CH1/CH2/CH3/CH4) - **闭环控制**:结合电位器反馈实现PID位置控制 - **通信接口**:通过UART接收角度指令(如`SERVO:90\n`) ---
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值