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

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

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

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

5 H) }' E5 |' j1 [) ]

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


0 y* b3 E! m% t0 ?* }* ]) b: O* l舵机的主要组成部分为伺服电机,所谓伺服就是服从信号的要求而动作。在信号来之前,转子停止不动;信号来到之后,转子立即运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。

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


# @" U; m& I7 T; d舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。


; X; Y' X1 z% [$ F1 j1 S/ E一般来说,舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。如下图。


5 t3 J1 i  B8 {3 v! m

) N5 Q  l' v) C4 a( |* L/ q* a

20180729163323156.png


# a7 N  K4 I' f2 x3 J: N0 f% z


: o. \$ `& R5 W. d那么我们如何使用stm32给舵机输入信号,让它听从我们的指挥呢?


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


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

4 f9 _1 t: }4 O1 b& }

  1. <font color="#000000"><font size="3" face="Tahoma">. L+ P7 Z3 I4 V8 {( c0 p9 R
  2. static void TIM_GPIO_Config(void)+ v$ a1 a$ }- t, X/ y) {5 m
  3. {
    & i9 X6 w3 i, C9 C
  4.         GPIO_InitTypeDef GPIO_InitStructure;" R% @+ k6 M4 a

  5. , ^8 R% x9 Z; B! R1 J3 `3 h; d6 i
  6.   // 输出比较通道 GPIO 初始化
    ! X3 G' u- Q; ]: n- l2 m$ k
  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    , g1 q* b* {' ]1 p% R5 X
  8.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;
    4 m' k2 K8 O: P- G$ @5 N+ Y3 W
  9.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    : q) i( T! X8 }
  10.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;7 y% K. g. d( _* D8 p
  11.     GPIO_Init(GPIOA, &GPIO_InitStructure);" T& u4 C2 \& O5 f- I3 m( J
  12. 3 k& h% Q/ K: t: a$ I
  13.   // 输出比较通道互补通道 GPIO 初始化
    8 D. J% f4 H6 G" `0 y
  14.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);: v5 B# e4 G0 n. f/ B( [% _
  15.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13;
    * w2 {2 [+ h# V
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    1 Z# X/ r, @  x$ \: t& h
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;1 x  M; C; R* Q: u
  18.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    5 H9 q% {1 Q8 _6 J2 x$ H" c
  19.         
    # p% J1 K" R8 x+ L5 @
  20.         // BKIN引脚默认先输出低电平, E: J. \: c: _% g/ U; _/ ?: ~
  21.         GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);' N. {! O3 n; k& q( }- x
  22. }
    5 T7 S$ J/ N3 Z: b7 Z* N" _/ I* P
  23. ! {4 t5 v2 a2 G
  24. static void Advance_TIM_Config(void)* V9 w  G% I0 l9 ?$ }: D
  25. {
    ' ~6 k0 A: W, Q
  26.           // 开启定时器时钟,即内部时钟CK_INT=72M
    , A' n$ g3 w- ~) O- y9 f' {& |
  27.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);7 @+ k/ ?( B: F

  28. ! q5 ]( ~2 ?' [7 }! G
  29. /*--------------------时基结构体初始化-------------------------*/
    & f& P4 E# U: v
  30.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;5 K; ]9 k! @  J  l! O
  31.         // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    + b/ |( r' C3 o4 N1 T
  32.         TIM_TimeBaseStructure.TIM_Period= (200-1);        , ]: r  k' _8 Y: h) ?
  33.         // 驱动CNT计数器的时钟 = Fck_int/(psc+1)- ?" k; `7 U  x8 c! o
  34.         TIM_TimeBaseStructure.TIM_Prescaler= (7200-1);        
    % o* z8 x1 D: e& k  P
  35.         // 时钟分频因子 ,用于配置死区时间,没用到,随意, T, \6 _) T$ u: X0 @
  36.         TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;               
    5 a% n* S2 m7 H" U, {" |
  37.         // 计数器计数模式,设置为向上计数
    & `& p) q5 j! x/ ^/ y4 |- j
  38.         TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;                4 n! c4 \2 i' z! X) r5 I  j
  39.         // 重复计数器的值,没用到,可以随意设置
    " v0 D; @1 f" C# M1 W
  40.         TIM_TimeBaseStructure.TIM_RepetitionCounter=0;        
    4 k) G3 N4 A1 Y0 O4 c5 N1 Z
  41.         // 初始化定时器
    & a: ?! x) Q7 ]0 ^* u- d
  42.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);+ y1 O- E) u2 r6 _  e) P# f

  43. 6 r+ M# W. `( a/ I  a0 m9 a& ~
  44.         /*--------------------输出比较结构体初始化-------------------*/               
    ! ^' Q% @7 N! v" G  \7 K# v; G1 _
  45.         TIM_OCInitTypeDef  TIM_OCInitStructure;
    # h4 @. _/ F4 S% k; @3 N) N4 o
  46.         // 配置为PWM模式2
    0 ]* H1 g, }3 ?
  47.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;. V7 {- c9 b0 [4 w
  48.         // 输出使能9 e4 s: i- G9 o% B6 [) H
  49.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;. k; B" g1 m3 P4 J4 p
  50.         // 互补输出使能
    - a7 y5 B/ `: V  A5 y
  51.         TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 6 k, F6 `  m8 t
  52.         // 设置占空比大小# H5 e* n' N) o
  53.         TIM_OCInitStructure.TIM_Pulse = 0;
    6 }: |8 _5 W" @) v
  54.         // 输出通道电平极性配置
      \5 Z7 r# ?0 H4 Q& m
  55.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;/ W/ r1 ?0 z0 K' {' R5 [6 T2 n
  56.         // 互补输出通道电平极性配置" u% z" R+ Y' Y8 I
  57.         TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;* P5 w! u/ \) L5 ?2 Z7 d
  58.         // 输出通道空闲电平极性配置
    5 J+ s/ h3 L+ n! i; k$ W- V1 x
  59.         TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;6 I  m6 W7 I- l' I: ^. z3 ~
  60.         // 互补输出通道空闲电平极性配置
    * j9 {3 i" J) x, v& J6 c
  61.         TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;; J" y5 g/ o$ C
  62.         TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
    ! [8 ?) P) |" @7 ]) C
  63.         TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);8 W7 b7 j2 {$ V7 h
  64.         3 U+ Z) t/ o9 i- w: p/ }$ V/ K3 f
  65.         // 使能计数器
    & v: ]  m  U4 N, t
  66.         TIM_Cmd(TIM1, ENABLE);        
    : i$ y5 R6 H/ h; {) J8 @
  67.         // 主输出使能,当使用的是通用定时器时,这句不需要
    / {# l8 ^4 I* w4 c! n
  68.         TIM_CtrlPWMOutputs(TIM1, ENABLE);) g8 ]0 @3 `! `+ n  P5 J/ \
  69. }
    - P! ]2 ?2 y) l2 B: P. X* ]. `
  70. . W3 _$ j+ J; a+ B/ J0 h
  71. void TIM_Init(void)
    9 i  A- |2 l  z3 f1 w& U+ O# {
  72. {
    / E+ R9 y$ y. @, _& m& g9 ^* i5 |
  73.         TIM_GPIO_Config();
    6 m7 [8 w! W. m/ p
  74.         Advance_TIM_Config();
    1 ~" v8 J$ k7 W  ]( q4 N
  75. }</font></font>
复制代码

) ]1 v4 l" A8 x8 o* p2 f  z) y

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


5 p& `  Y) l2 Z

在初始化完成之后,就可以在main函数中实现信号的输出了。) I/ M# H/ k0 C4 ?! n9 c  L( i8 @
$ z! W/ q- ^& i, r/ i
  X3 x3 m3 D9 t5 s$ A

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


. `% u/ l. E& l3 c0 r0 j5 M1 y5 r

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


/ _7 t( [- m' z& }$ U

  1. <font color="#000000"><font size="3" face="Tahoma">- I  f& m: S& z. Q# |% @( m1 D) l& p7 c
  2. #include "stm32f10x.h"
    & p8 p) c- O; U
  3. #include "bsp_Advance_tim.h"! p- c+ I6 l" Q3 y5 M$ _/ O6 Z
  4. #include "delay.h"  d5 K9 |( |2 j1 f0 U

  5. 1 e, F1 Z: G2 n0 o
  6. int main(void), w0 L  Q7 \. d6 e- T( [
  7. {
    4 i! s" a4 [1 W0 _
  8.         int delay_time;$ {' y5 a' J6 I
  9.         delay_init(); //延时函数初始化8 L% t' B5 ]7 U* Z% v1 X$ |2 g
  10.         TIM_Init(); //定时器初始化9 g% G0 Z+ Z& G
  11.         
    , u8 L9 s/ m$ j6 d; y* M
  12.         delay_time = 500;. y7 \2 n% q8 h) O, e/ b$ V5 H
  13.         while(1)3 D0 ]. j. q. k; X
  14.         {0 |6 r# y- X/ }/ b  g* h- q
  15.                 delay_ms(delay_time);, N% V+ p. H8 ]. \' J
  16.                 TIM_SetCompare1(ADVANCE_TIM, 175); //对应180度
    4 ^" a. q3 I( A3 ^4 C
  17.         delay_ms(delay_time);
    3 E% U% F% E) @- g* Z/ n6 e
  18.                 TIM_SetCompare1(ADVANCE_TIM, 180); //对应135度9 q' {( N, K8 d6 d2 C: ?
  19.         delay_ms(delay_time);
    7 X5 d& d  p2 B" S
  20.                 TIM_SetCompare1(ADVANCE_TIM, 185); //对应90度
    8 x7 p" W- w0 S
  21.         delay_ms(delay_time);
    8 S6 {3 t! c7 t8 ~  b4 d( G
  22.                 TIM_SetCompare1(ADVANCE_TIM, 190); //对应45度
    8 ^" w5 \) A6 p; @) q  N
  23.         delay_ms(delay_time);
    ! ~8 d, g! J! H; u7 }
  24.                 TIM_SetCompare1(ADVANCE_TIM, 195); //对应0度; }1 J" Y0 L+ r6 l( Y/ A2 w
  25.         }
    7 g/ u- p8 f* \# r4 A8 P/ Z
  26. }</font></font>
复制代码

) _- w0 Z9 C0 ?) U0 o

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


6 H* S. s0 s: Z1 |  W5 U" P

在这补充一点如何连线:可以用杜邦线将舵机的电源与stm32的5v或3.3v引脚连接,将地线与stm32的GND连接,将舵机的信号线与stm32的PWM信号输出引脚连接。
: h) V, M' Z- h
* |  c" G5 h" F' ]: I& N) d1 k# A; m
- t5 j. G, \8 N4 K/ \
; R& C( w4 `: u- ~- N& L
收藏 评论0 发布时间:2020-9-15 10:52

举报

0个回答

所属标签

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