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

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

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

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

: n1 O3 y5 W& X1 A

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


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


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


- v0 r) v* R: v' {) t/ U2 D- G舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。


: }$ g# ~+ O2 I7 `4 o- ^; ^一般来说,舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。如下图。


' l6 n/ _; F/ c! z3 t


7 C+ L# G3 V" [9 K

20180729163323156.png

% e% t, }' K& S3 @- A4 f( M


+ D: g+ t4 e' M2 R" E那么我们如何使用stm32给舵机输入信号,让它听从我们的指挥呢?

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

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

# {: p% V8 X0 r$ Z4 J8 l

  1. <font color="#000000"><font size="3" face="Tahoma">: o$ A; `2 U: K9 p
  2. static void TIM_GPIO_Config(void)
    + K' ^$ Q' z* B* z0 q: w
  3. {! Z9 v3 S9 C1 x% T
  4.         GPIO_InitTypeDef GPIO_InitStructure;% L! q1 t" R8 O1 ~3 [% Z% ]

  5. : Y8 q4 Y  u9 s( `7 k5 k' J
  6.   // 输出比较通道 GPIO 初始化
      M/ N& W9 D+ p. Z1 K1 @
  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    . j5 U5 z  R- p- F/ `
  8.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;; H) i. S0 h* Q  P* u7 E# u
  9.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;) [% Z$ W, G) E# S5 H7 P
  10.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ; q9 m9 Q1 T  ], Z6 I4 O! F9 P/ T
  11.     GPIO_Init(GPIOA, &GPIO_InitStructure);0 }6 C+ c) o: q# P' D/ d7 b
  12. 8 n6 g; |+ e8 l% X1 f
  13.   // 输出比较通道互补通道 GPIO 初始化- L( D. ]! Q+ z2 W8 v8 D
  14.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    9 U" `% B! f3 ?/ o' I. i; [
  15.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13;
    8 Y; W& d% ~- ~& p; g$ c6 |/ ^3 I
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    5 O: D8 J9 O+ }6 n
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    7 g4 A5 z8 b3 N$ C
  18.     GPIO_Init(GPIOB, &GPIO_InitStructure);, D: y; u- e/ K" h0 \7 f
  19.         
    , J: e% S/ S9 D& o
  20.         // BKIN引脚默认先输出低电平* m5 U$ k+ R' G; u) D
  21.         GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);/ i' b6 f; @$ N+ ]' g" N" |0 c
  22. }. u3 r  k7 w, h- R4 K
  23. ) N& K3 A/ a2 a7 V+ ^5 `
  24. static void Advance_TIM_Config(void)
    % Z" q+ K1 N1 `; ^8 C
  25. {
    , D1 d6 a2 @: Y! r3 J( D3 @- g
  26.           // 开启定时器时钟,即内部时钟CK_INT=72M
    ; n8 [* Q7 I$ Z8 E+ }6 ~
  27.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    % l4 y5 j% j8 k6 [. F" g  M$ ?$ G  l
  28. 4 S/ R/ _0 |, b+ C4 k
  29. /*--------------------时基结构体初始化-------------------------*/& ?& d% j+ r: [/ v- O" O# ]3 n
  30.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    % F& Z5 h9 L  C, g( g
  31.         // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断. o+ s2 E3 ^* L4 l% G* ]
  32.         TIM_TimeBaseStructure.TIM_Period= (200-1);        
    : O+ `; q  U7 Y/ C* x$ J  x' g. s
  33.         // 驱动CNT计数器的时钟 = Fck_int/(psc+1)
    + D$ `& n2 c) z5 y8 S
  34.         TIM_TimeBaseStructure.TIM_Prescaler= (7200-1);        1 I$ L' M2 b& e) H
  35.         // 时钟分频因子 ,用于配置死区时间,没用到,随意; j5 b: e& Z) R
  36.         TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;                0 v8 k) R, ?# f
  37.         // 计数器计数模式,设置为向上计数& B- L' O3 H$ ?
  38.         TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;               
    3 ^% I& a& r  d2 T( d$ p
  39.         // 重复计数器的值,没用到,可以随意设置. i* x7 `( R; H$ S* n, C8 ~
  40.         TIM_TimeBaseStructure.TIM_RepetitionCounter=0;        
    * U7 h& Z3 W" E0 }( y
  41.         // 初始化定时器6 o7 U) ~3 Q$ ]  t8 K6 m+ Y' v- r' o
  42.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);1 n2 m0 Y+ e( @( x

  43. / j9 d* O/ J  K, ?' q3 v
  44.         /*--------------------输出比较结构体初始化-------------------*/               
    $ p1 T. v7 T. H4 [
  45.         TIM_OCInitTypeDef  TIM_OCInitStructure;0 t0 A$ [( d7 D- Y* T6 S6 a: J
  46.         // 配置为PWM模式2
    & C9 {! ?" p+ g" M3 |5 d6 S
  47.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
    7 m6 j3 r' Q/ D5 J1 N
  48.         // 输出使能4 o4 J( b$ V7 a6 v6 K
  49.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;4 T) u/ P' Z; ]  Z6 L+ o/ U
  50.         // 互补输出使能
    5 |3 d: p% c7 Q* A
  51.         TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
    ; u/ L2 C( S, X
  52.         // 设置占空比大小
    $ l( @' b3 X7 p) ?2 r
  53.         TIM_OCInitStructure.TIM_Pulse = 0;" ?' B. ]9 f" J/ U
  54.         // 输出通道电平极性配置7 v7 x( g% A8 V( a3 [3 d
  55.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;# @5 Q" V+ D1 [* m8 d3 e9 ~% _
  56.         // 互补输出通道电平极性配置$ m) s# G) W6 x, x3 O* n
  57.         TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
    ( c1 l# I% ]5 H
  58.         // 输出通道空闲电平极性配置
    2 `5 }# C! s0 ?( F: A; D1 [7 c: |
  59.         TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    + [% {& f% p* E
  60.         // 互补输出通道空闲电平极性配置8 I* ]' e5 ^4 H# U! w
  61.         TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;- c3 E1 x. l2 m1 P
  62.         TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);, p' w( j% N% w: ^1 V
  63.         TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    ' x: w5 G" \  x. b. T+ X+ `
  64.         ' M( l5 K0 y" h0 u$ A1 l: i- x- y
  65.         // 使能计数器
    6 g& M: [3 M: A* u0 N9 i) ?
  66.         TIM_Cmd(TIM1, ENABLE);        - n8 i4 T: f# _( [  e' S$ g
  67.         // 主输出使能,当使用的是通用定时器时,这句不需要0 s3 n$ H# r+ G% d
  68.         TIM_CtrlPWMOutputs(TIM1, ENABLE);: H4 C& f% h. S' Q
  69. }
    2 z6 E) G& R; c
  70. . f3 h! B" z4 ~% r" }* F# j
  71. void TIM_Init(void)
    5 i) N: X; }8 p$ c2 |4 ?
  72. {" m, R6 u* q, D/ Z" T( z. k
  73.         TIM_GPIO_Config();7 ~/ a9 Y: K3 j9 B6 D% _. J
  74.         Advance_TIM_Config();4 |/ n' a8 S' ^) Z0 y" Q0 {
  75. }</font></font>
复制代码

. d$ g, t. y$ N8 O+ o

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

5 ?- b2 r' D  U$ }

在初始化完成之后,就可以在main函数中实现信号的输出了。
2 b7 `: R3 A" H6 ]6 A0 h6 A" U
* o8 O2 z6 V7 M7 [4 S; n/ f' y* Y. p. V0 n" M

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


/ ]7 B7 X1 K, ~* f& Q

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

+ d7 z/ c! y9 B8 t

  1. <font color="#000000"><font size="3" face="Tahoma">
    . Q- v! ?  q% Z* a* y! n- k! U
  2. #include "stm32f10x.h"! i! f! X, k  n) B: w
  3. #include "bsp_Advance_tim.h"
    . F5 z+ Z3 d0 t8 N
  4. #include "delay.h", Q" j, X  h0 y
  5. " _. i( z- O$ ~
  6. int main(void)/ [1 `3 g# c2 O) A+ l
  7. {
    : e4 C' `+ e% Q8 E
  8.         int delay_time;
    8 S' D" @: \& f4 g. S
  9.         delay_init(); //延时函数初始化
    / n# T4 V1 o/ W- c
  10.         TIM_Init(); //定时器初始化
    4 o( @  l3 S  Y- B/ U, x' `0 g
  11.         
    : F( B0 S# d7 D
  12.         delay_time = 500;
    : U( w1 v$ h, m9 Q+ u0 e* l  E
  13.         while(1)
      \& N3 z  ]  b! }# ~
  14.         {
    % @. ?. n2 g  C& ]- A( v. I6 ]
  15.                 delay_ms(delay_time);
    * A  k- u" v9 c/ |7 F
  16.                 TIM_SetCompare1(ADVANCE_TIM, 175); //对应180度# X/ F* x3 ?% l6 |
  17.         delay_ms(delay_time);7 X3 }4 w) o6 Y3 F6 E# t
  18.                 TIM_SetCompare1(ADVANCE_TIM, 180); //对应135度
    + K: {  K) X/ a6 n- p+ e5 r
  19.         delay_ms(delay_time);! ^; O3 G; A- d" T) f9 ^
  20.                 TIM_SetCompare1(ADVANCE_TIM, 185); //对应90度
    4 W1 K1 V/ X) R* v7 }+ V( \
  21.         delay_ms(delay_time);
    , D3 j' z! L  q/ ^# c; {
  22.                 TIM_SetCompare1(ADVANCE_TIM, 190); //对应45度; r/ H7 F3 c8 u$ ]
  23.         delay_ms(delay_time);
    - M' q( ?5 q! n( Q
  24.                 TIM_SetCompare1(ADVANCE_TIM, 195); //对应0度. w8 A1 X! `# l5 O7 w* Q
  25.         }8 Y) b- v, ^; T* H7 X  X
  26. }</font></font>
复制代码


# f! ^5 v% S. _3 W

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


# L9 M( g$ F2 U% ~, v8 n# v

在这补充一点如何连线:可以用杜邦线将舵机的电源与stm32的5v或3.3v引脚连接,将地线与stm32的GND连接,将舵机的信号线与stm32的PWM信号输出引脚连接。7 d+ j6 B6 ?2 Y* w* f+ o
, w! _( n8 @" }4 c8 x8 o0 h
( v! ^6 @! Y( |+ D' u5 n& Y

6 P. G( o1 m: @, x
收藏 评论0 发布时间:2020-9-15 10:52

举报

0个回答

所属标签

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