stm32控制舵机旋转到不同角度

本文介绍如何使用STM32F103VE开发板控制舵机,并提供了一种实现PWM信号输出的方法。详细解释了舵机工作原理及STM32定时器配置过程。

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

最近学习了stm32,就想用它来控制舵机,然后写下这篇文章分享给大家,如果有理解不到位的地方欢迎大家指正。(我使用的是stm32f103ve型号的开发板,即使和你的型号不同,也有参考价值)

想要控制舵机的转动,首先你得知道舵的工作原理。


舵机的主要组成部分为伺服电机,所谓伺服就是服从信号的要求而动作。在信号来之前,转子停止不动;信号来到之后,转子立即运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。
舵机接收的是PWM信号,当信号进入内部电路产生一个偏置电压,触发电机通过减速齿轮带动电位器移动,使电压差为零时,电机停转,从而达到伺服的效果。简单来说就是给舵机一个特定的PWM信号,舵机就可以旋转到指定的位置。
舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。
一般来说,舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。如下图。


那么我们如何使用stm32给舵机输入信号,让它听从我们的指挥呢?


想要输出PWM信号自然就得用上TIM定时器,而基本定时器没有PWM信号的输出功能,所以只能选用通用定时器和高级定时器。对于初始化这些外设无非也就是那些套路,我总结为如下几点:1、开启该外设的时钟2、配置初始化结构体(如果有对应的GPIO还需要初始化该GPIO)3、调用结构体初始化函数4、该使能的使能
对于TIM来说初始化结构体有两个,分别是时基结构体和输出比较结构体,除此之外还需要做的是先选择具体开启哪条输出通道,我选择的是TIM1(高级定时器)的CH1(通道一),对应的GPIO是PA8。我还初始化了通道一的互补通道PB13,为了更加方便测试。下面是初始化部分的代码。

static void TIM_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

  // 输出比较通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

  // 输出比较通道互补通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	// BKIN引脚默认先输出低电平
	GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);
}

static void Advance_TIM_Config(void)
{
	  // 开启定时器时钟,即内部时钟CK_INT=72M
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);

/*--------------------时基结构体初始化-------------------------*/
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
	TIM_TimeBaseStructure.TIM_Period= (200-1);	
	// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= (7200-1);	
	// 时钟分频因子 ,用于配置死区时间,没用到,随意
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;		
	// 计数器计数模式,设置为向上计数
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;		
	// 重复计数器的值,没用到,可以随意设置
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;	
	// 初始化定时器
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

	/*--------------------输出比较结构体初始化-------------------*/		
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	// 配置为PWM模式2
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
	// 输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// 互补输出使能
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 
	// 设置占空比大小
	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(ADVANCE_TIM, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
	
	// 使能计数器
	TIM_Cmd(TIM1, ENABLE);	
	// 主输出使能,当使用的是通用定时器时,这句不需要
	TIM_CtrlPWMOutputs(TIM1, ENABLE);
}

void TIM_Init(void)
{
	TIM_GPIO_Config();
	Advance_TIM_Config();
}

在代码中要特别注意的是时基结构体的TIM_Period(自动重装载寄存器值,简称arr)和TIM_Prescaler(预分频寄存器值,简称psc),因为这两个决定了输出PWM信号的周期。具体的周期计算公式为:周期=(arr+1)*(psc+1)/CLK。其中CLK为计数器的时钟频率,我的是72MHZ,也就是72000000。最后计算结果单位为秒,结果为0.02s,也就是20ms。这样的配置就是为了让输出的PWM信号达到前面说到的舵机要求的20ms周期。

在初始化完成之后,就可以在main函数中实现信号的输出了。

前面说过,在周期20ms的PWM信号中,不同的脉宽对应舵机不同的转动角度,在0.5ms-2.5ms间有效,因此我们可以在main函数中配置几个不同的脉宽。要注意的是stm32并不直接配置脉宽,而是通过配置占空比来配置脉宽的。

配置占空比有个重要的函数TIM_SetCompare1(),具体用法如果不懂可以去看手册。main函数代码如下。

#include "stm32f10x.h"
#include "bsp_Advance_tim.h"
#include "delay.h"

int main(void)
{
	int delay_time;
	delay_init(); //延时函数初始化
	TIM_Init(); //定时器初始化
	
	delay_time = 500;
	while(1)
	{
		delay_ms(delay_time);
		TIM_SetCompare1(ADVANCE_TIM, 175); //对应180度
        delay_ms(delay_time);
		TIM_SetCompare1(ADVANCE_TIM, 180); //对应135度
        delay_ms(delay_time);
		TIM_SetCompare1(ADVANCE_TIM, 185); //对应90度
        delay_ms(delay_time);
		TIM_SetCompare1(ADVANCE_TIM, 190); //对应45度
        delay_ms(delay_time);
		TIM_SetCompare1(ADVANCE_TIM, 195); //对应0度
	}
}

如果在定时器初始化时TIM_OCInitStructure.TIM_OCMode配置的是PWM1模式那么main中的占空比就依次为25、20、15、10、5。在你们自己试验时,可以将占空比设置成各种不同的值,看看有什么不同的效果。

在这补充一点如何连线:可以用杜邦线将舵机的电源与stm32的5v或3.3v引脚连接,将地线与stm32的GND连接,将舵机的信号线与stm32的PWM信号输出引脚连接。

stm32控制舵机旋转到不同角度完整代码

### STM32 控制舵机沿矩形轨迹运动 为了使STM32控制舵机按照预定的矩形路径移动,需要编写相应的初始化设置以及定时更新PWM信号来改变舵机角度。下面提供了一个简单的C语言代码示例用于说明这一过程。 #### 初始化配置 首先,在主函数之前定义必要的头文件导入、宏定义和全局变量声明: ```c #include "stm32f1xx_hal.h" #define SERVO_MIN_PULSEWIDTH 0.5 // ms, 对应最小角度(通常为0度) #define SERVO_MAX_PULSEWIDTH 2.5 // ms, 对应最大角度(通常为180度) TIM_HandleTypeDef htim3; uint16_t servo_angle = 90; // 初始中间位置 ``` 接着是对硬件资源的具体配置部分,这里假设使用的是TIM3作为PWM输出端口,并且连接到了PA6引脚上: ```c static void MX_TIM3_Init(void){ __HAL_RCC_TIM3_CLK_ENABLE(); TIM_OC_InitTypeDef sConfigOC; htim3.Instance = TIM3; htim3.Init.Prescaler = 79; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 999; HAL_TIM_PWM_Init(&htim3); sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = (SERVO_MIN_PULSEWIDTH / 20 * htim3.Init.Period); sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); } ``` 上述代码片段完成了对定时器TIM3及其通道1(即PA6)的相关参数设定工作[^1]。 #### 舵机控制逻辑 接下来就是核心算法——让舵机沿着指定路线运行的部分。考虑到题目中的需求是要形成一个矩形闭合回路,则可以通过四个角点之间的顺序切换达到目的。具体来说就是在每次到达一个新的顶点之后暂停一段时间再继续前进至下一个目标方位直到完成整个循环为止。 ```c void move_servo_to(uint16_t angle) { uint32_t pulse_width_us = ((angle*(SERVO_MAX_PULSEWIDTH-SERVO_MIN_PULSEWIDTH)/180)+SERVO_MIN_PULSEWIDTH)*1000/20; __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,pulse_width_us*htim3.Init.Period/(SystemCoreClock/1000)); } int main() { /* ...其他初始化... */ while (1) { for(int i=0;i<4;++i){ switch(i%4){ case 0://左下->右下 move_servo_to(0); break; case 1://右下->右上 move_servo_to(90); break; case 2://右上->左上 move_servo_to(180); break; default://左上->左下 move_servo_to(90); } HAL_Delay(2000);//等待两秒后再到下一侧边 } } } ``` 这段程序会使得舵机依次经过四次动从而构成完整的矩形边界扫描动作。
评论 98
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值