你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

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

[复制链接]
STMCU 发布时间:2020-9-15 10:52

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


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


舵机的主要组成部分为伺服电机,所谓伺服就是服从信号的要求而动作。在信号来之前,转子停止不动;信号来到之后,转子立即运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。


舵机接收的是PWM信号,当信号进入内部电路产生一个偏置电压,触发电机通过减速齿轮带动电位器移动,使电压差为零时,电机停转,从而达到伺服的效果。简单来说就是给舵机一个特定的PWM信号,舵机就可以旋转到指定的位置。


舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。


一般来说,舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。如下图。



20180729163323156.png



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


想要输出PWM信号自然就得用上TIM定时器,而基本定时器没有PWM信号的输出功能,所以只能选用通用定时器和高级定时器。对于初始化这些外设无非也就是那些套路,我总结为如下几点:1、开启该外设的时钟2、配置初始化结构体(如果有对应的GPIO还需要初始化该GPIO)3、调用结构体初始化函数4、该使能的使能


对于TIM来说初始化结构体有两个,分别是时基结构体和输出比较结构体,除此之外还需要做的是先选择具体开启哪条输出通道,我选择的是TIM1(高级定时器)的CH1(通道一),对应的GPIO是PA8。我还初始化了通道一的互补通道PB13,为了更加方便测试。下面是初始化部分的代码。


  1. <font color="#000000"><font size="3" face="Tahoma">
  2. static void TIM_GPIO_Config(void)
  3. {
  4.         GPIO_InitTypeDef GPIO_InitStructure;

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

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

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

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

  39.         /*--------------------输出比较结构体初始化-------------------*/               
  40.         TIM_OCInitTypeDef  TIM_OCInitStructure;
  41.         // 配置为PWM模式2
  42.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
  43.         // 输出使能
  44.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  45.         // 互补输出使能
  46.         TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
  47.         // 设置占空比大小
  48.         TIM_OCInitStructure.TIM_Pulse = 0;
  49.         // 输出通道电平极性配置
  50.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  51.         // 互补输出通道电平极性配置
  52.         TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
  53.         // 输出通道空闲电平极性配置
  54.         TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
  55.         // 互补输出通道空闲电平极性配置
  56.         TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
  57.         TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
  58.         TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
  59.         
  60.         // 使能计数器
  61.         TIM_Cmd(TIM1, ENABLE);        
  62.         // 主输出使能,当使用的是通用定时器时,这句不需要
  63.         TIM_CtrlPWMOutputs(TIM1, ENABLE);
  64. }

  65. void TIM_Init(void)
  66. {
  67.         TIM_GPIO_Config();
  68.         Advance_TIM_Config();
  69. }</font></font>
复制代码


在代码中要特别注意的是时基结构体的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函数代码如下。


  1. <font color="#000000"><font size="3" face="Tahoma">
  2. #include "stm32f10x.h"
  3. #include "bsp_Advance_tim.h"
  4. #include "delay.h"

  5. int main(void)
  6. {
  7.         int delay_time;
  8.         delay_init(); //延时函数初始化
  9.         TIM_Init(); //定时器初始化
  10.         
  11.         delay_time = 500;
  12.         while(1)
  13.         {
  14.                 delay_ms(delay_time);
  15.                 TIM_SetCompare1(ADVANCE_TIM, 175); //对应180度
  16.         delay_ms(delay_time);
  17.                 TIM_SetCompare1(ADVANCE_TIM, 180); //对应135度
  18.         delay_ms(delay_time);
  19.                 TIM_SetCompare1(ADVANCE_TIM, 185); //对应90度
  20.         delay_ms(delay_time);
  21.                 TIM_SetCompare1(ADVANCE_TIM, 190); //对应45度
  22.         delay_ms(delay_time);
  23.                 TIM_SetCompare1(ADVANCE_TIM, 195); //对应0度
  24.         }
  25. }</font></font>
复制代码


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


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



收藏 评论0 发布时间:2020-9-15 10:52

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版