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

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

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

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

! n3 k2 g9 {( x* ~9 o0 i

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

1 c* A3 e# j# H" m$ c6 [/ C
舵机的主要组成部分为伺服电机,所谓伺服就是服从信号的要求而动作。在信号来之前,转子停止不动;信号来到之后,转子立即运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。

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

% p4 F* x' i: K3 B
舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。

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

: ^! @1 |' z1 @- Y+ ]9 m+ }


6 ]/ u( W* E# e! z+ A8 _" j

20180729163323156.png

+ t, d% c8 v+ p- Q$ q, x

& y, Q4 [9 M% u
那么我们如何使用stm32给舵机输入信号,让它听从我们的指挥呢?

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


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

8 j' ]% k" d- S& u7 V- p, L7 q  V

  1. <font color="#000000"><font size="3" face="Tahoma">( D* ]: R3 w3 h1 u' {
  2. static void TIM_GPIO_Config(void)$ J6 ~; c1 L, k1 p6 U5 ~
  3. {1 d- Q  o, F# U2 G. v- r
  4.         GPIO_InitTypeDef GPIO_InitStructure;; K- @  s- _1 S0 U/ h. @! A

  5. 6 y9 Y* Y  U' b2 {3 x
  6.   // 输出比较通道 GPIO 初始化; X9 s  Q7 L* O- [
  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    9 Q) ~: H; `) N0 V
  8.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;& D5 m3 W/ M( K8 `9 V
  9.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;# L  u* k  g5 b5 H7 `
  10.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    % p: Y, n. p3 C* x5 n
  11.     GPIO_Init(GPIOA, &GPIO_InitStructure);2 ?" Z, @# K2 Q& b- G0 @  h: T

  12. 2 L$ l2 G# _2 c3 ?8 U
  13.   // 输出比较通道互补通道 GPIO 初始化
    1 l- u, e7 Z% \$ f: h: [) u
  14.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);) X7 O0 J4 {( ?: |# s# s. ^6 E( W
  15.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13;
    $ H$ N" I9 }3 P
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    , p" T- [( K4 c8 f, f. V% \0 l
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    # O# l- v4 g& z
  18.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    9 W$ j8 G. ~' h4 L
  19.         " f% N: e* C/ |% m5 {: G% h) T
  20.         // BKIN引脚默认先输出低电平7 B7 f  c/ O6 G5 U' ^2 ?4 y
  21.         GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);! \, J+ k6 \# v* ~2 @
  22. }6 Y& \; `% d/ O, {

  23. & h8 K% Q) b+ ^  `, y9 m% A
  24. static void Advance_TIM_Config(void)
    % e% p; y6 s2 T  }/ f
  25. {
    ( H4 ~' ?" W9 q7 Q' I
  26.           // 开启定时器时钟,即内部时钟CK_INT=72M
      J+ T; [9 S, F  {/ y( }  v
  27.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    : K/ n$ L/ @; h* J$ `

  28. ; Q  O) M0 ^1 F9 j3 ~2 M4 z
  29. /*--------------------时基结构体初始化-------------------------*/7 V5 O2 `: x- l4 F# E2 a3 H
  30.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    # [+ @) e3 A) B5 P- {4 g$ l' R: {, G
  31.         // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断: e. G7 P6 z8 q* v/ x" ~5 E, c% x
  32.         TIM_TimeBaseStructure.TIM_Period= (200-1);        
    & b1 w% h) ]. D9 S6 J
  33.         // 驱动CNT计数器的时钟 = Fck_int/(psc+1)
    9 P$ `! t, F3 P+ Y' Q) y
  34.         TIM_TimeBaseStructure.TIM_Prescaler= (7200-1);        4 G) J6 M3 y  v' `; `( M8 [
  35.         // 时钟分频因子 ,用于配置死区时间,没用到,随意
    * R$ H2 M% |; e: G4 h! }
  36.         TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;                4 U& i) u4 `# R8 {. L, V8 U
  37.         // 计数器计数模式,设置为向上计数: V/ @8 g  [* b, F* T& h
  38.         TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;               
    * r/ \, r, [) ]. E
  39.         // 重复计数器的值,没用到,可以随意设置
    : ~2 f, T1 u2 k
  40.         TIM_TimeBaseStructure.TIM_RepetitionCounter=0;        " u, W8 m2 h% `1 E( F& M
  41.         // 初始化定时器
    " e% ~; I( B( [
  42.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);4 W& c4 w, _5 K0 `
  43. 9 q6 ?+ V8 n, O) k5 Y1 O# q
  44.         /*--------------------输出比较结构体初始化-------------------*/               
      {% h/ r$ t6 G  d8 p4 V. P& i
  45.         TIM_OCInitTypeDef  TIM_OCInitStructure;7 K9 Z" E+ A) m: H9 ]! i# o4 O3 K
  46.         // 配置为PWM模式2
    - X1 e& q+ w& C  a* V1 d' u
  47.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;2 a8 }4 S: _; b! Q) J1 @0 V
  48.         // 输出使能
    6 l+ l8 u8 C1 L( p
  49.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;* x% c& C3 ~7 a4 w( b
  50.         // 互补输出使能
    + B5 C+ q  e7 p% I$ e$ C
  51.         TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 4 ~5 a7 [0 f1 v; P0 t
  52.         // 设置占空比大小# ]; A/ N- Q5 [) ]) \- l$ j: G
  53.         TIM_OCInitStructure.TIM_Pulse = 0;
    4 o  U4 G! X# H
  54.         // 输出通道电平极性配置
    , ]/ n. b/ L) R' I2 E
  55.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    # A& h4 m2 A0 |! [
  56.         // 互补输出通道电平极性配置
    $ f- k& ]: m$ Y
  57.         TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;' ?: c3 v# i( p
  58.         // 输出通道空闲电平极性配置# g. [6 \  ]9 j7 p3 y
  59.         TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;6 |+ Y, P% x& I" j, T; z
  60.         // 互补输出通道空闲电平极性配置
    & ~& v7 ~  A2 f1 R% ]
  61.         TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
    $ ~- d1 A* t; `3 }' W7 b+ \  i9 S8 v9 N' a
  62.         TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);" K* s' `/ ^! G: M
  63.         TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    - e& N7 S, [( c) x3 P
  64.         , b5 A, S- A$ D
  65.         // 使能计数器
    - o8 Q* Y2 n; n% {. ^
  66.         TIM_Cmd(TIM1, ENABLE);        
    6 _# n8 N5 |" A% R
  67.         // 主输出使能,当使用的是通用定时器时,这句不需要, M' w! |0 n1 C# S. |
  68.         TIM_CtrlPWMOutputs(TIM1, ENABLE);4 Y5 L- u2 k7 _/ |
  69. }4 o5 [1 q/ M3 ]7 J9 R3 k" H

  70. 7 d: `5 b- k6 J+ V" K; l7 Q
  71. void TIM_Init(void)3 R" Y) W( l+ G$ |5 X
  72. {
    + ^' P/ ~, R9 G7 L* D5 y* u6 _
  73.         TIM_GPIO_Config();
    * ~) M) a& k4 u; f) K/ `" a
  74.         Advance_TIM_Config();' a$ V. p3 `7 W( \3 D! O
  75. }</font></font>
复制代码


) F6 d4 D1 g; {8 m; q0 Z

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

5 D1 E! z7 I6 i& l! B. N

在初始化完成之后,就可以在main函数中实现信号的输出了。
$ V0 u) _3 r( T7 K) t
! a* c; t* H2 a9 q5 O) o7 u( j7 G/ _

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


( r, s5 A# j$ L  I' E  u) P/ |1 u

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

9 \! p, M* F' U

  1. <font color="#000000"><font size="3" face="Tahoma">) Q4 o/ l1 g/ Y# S7 M) }
  2. #include "stm32f10x.h"" s- \. u4 b1 \. k# e* p4 F
  3. #include "bsp_Advance_tim.h"+ Y: V  j8 f: `: P- o
  4. #include "delay.h"
    0 R, I5 z) i  I0 U

  5. * I4 l* c/ D6 v! X
  6. int main(void)
    . |& ]- e: h" }) X( w& g
  7. {
    1 w$ F4 c2 }$ t
  8.         int delay_time;
    , C; {+ ^. |* ^5 P2 e
  9.         delay_init(); //延时函数初始化  N' m$ [# C" [2 w" z* }
  10.         TIM_Init(); //定时器初始化9 @" L- f8 ~& P8 c5 ?- N5 t
  11.         2 O3 Q1 S7 \4 I+ H% |2 n% S3 k- C$ J
  12.         delay_time = 500;. [- g8 m- Q2 g5 M  L( Y
  13.         while(1)
    0 D4 w* O) E6 T7 `* o4 _2 ~
  14.         {
    / T4 t# |0 M# S% D
  15.                 delay_ms(delay_time);3 f4 y$ t) s; L
  16.                 TIM_SetCompare1(ADVANCE_TIM, 175); //对应180度
    * N- _( T: N# L: S, [1 {9 F8 n$ y) }
  17.         delay_ms(delay_time);! I! H) c$ w  L5 e- y- f
  18.                 TIM_SetCompare1(ADVANCE_TIM, 180); //对应135度5 [% m+ P  m, B4 \, g0 r
  19.         delay_ms(delay_time);9 Z& t' H' ]% [" e  B3 C
  20.                 TIM_SetCompare1(ADVANCE_TIM, 185); //对应90度) o+ I; p# S% l& [) ~  m
  21.         delay_ms(delay_time);
    , W& m: @+ }3 m2 t" F5 L
  22.                 TIM_SetCompare1(ADVANCE_TIM, 190); //对应45度
    4 I' i3 W$ `, c; s7 `
  23.         delay_ms(delay_time);
    2 }* d* H( d# W0 f( C
  24.                 TIM_SetCompare1(ADVANCE_TIM, 195); //对应0度: C% R7 ^( T0 V) _* y( B0 H! u
  25.         }9 _8 W' Z% Q! l; i% t& f7 j* U3 x3 z
  26. }</font></font>
复制代码

9 I8 n% [) Q  S; i6 L0 {

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


% {0 n# o& L' a

在这补充一点如何连线:可以用杜邦线将舵机的电源与stm32的5v或3.3v引脚连接,将地线与stm32的GND连接,将舵机的信号线与stm32的PWM信号输出引脚连接。
0 I, ^0 ]- n" F, [" s( B- J! e
3 {2 U  o, Q7 g, ]: m. ~  O
5 s- t/ ^: \2 c/ @  L' ]' y( p" w: B$ T6 d
收藏 评论0 发布时间:2020-9-15 10:52

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版