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

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

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

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

) b2 ~8 f, d  o6 [$ ]" K9 f( _

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

' h* f* S8 D3 N$ w9 F# `& _* y
舵机的主要组成部分为伺服电机,所谓伺服就是服从信号的要求而动作。在信号来之前,转子停止不动;信号来到之后,转子立即运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。


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

4 A& K- I1 s' R% o6 y/ b
舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。

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


* X1 U& K- D: A, a; p) q1 ~


6 h8 T" S) G' \/ ]

20180729163323156.png

" j# m7 E9 B  ?

0 }( ^4 T: I3 t" J: b
那么我们如何使用stm32给舵机输入信号,让它听从我们的指挥呢?


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


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


, f7 ^9 j- M7 G

  1. <font color="#000000"><font size="3" face="Tahoma">+ g. M/ X1 N# M9 I* @: Z
  2. static void TIM_GPIO_Config(void)
    , o) g/ H# P, E& F0 T8 f! P7 b  G: g
  3. {/ w5 q3 }4 @* w* d( O& Y( \
  4.         GPIO_InitTypeDef GPIO_InitStructure;* z( p) E  @% [2 ?1 T% F% a* q

  5. # j' f+ k* m! x; e- V% m" s0 q
  6.   // 输出比较通道 GPIO 初始化+ w  y& [- }, a/ ?& b5 d
  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      N' B. ~4 C: {# v) w( z  Y
  8.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;  N% c6 E: m, j9 J9 ~
  9.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    , m4 s( E# {4 ]  `2 x" b/ x- {
  10.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    6 O9 _% z5 Y/ {# _
  11.     GPIO_Init(GPIOA, &GPIO_InitStructure);
    . y: w) s1 ]+ p9 K! S* r; Y

  12. 0 I. p5 J4 x4 x1 K+ }
  13.   // 输出比较通道互补通道 GPIO 初始化. f2 c/ j9 k. P
  14.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);% E3 k& L0 x8 G; p5 z: A/ h* c* q" o
  15.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13;0 A* G, h) J: \$ U
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    5 M3 k0 _* r: R# \% l
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    & y8 \3 u7 N1 z& B2 N
  18.     GPIO_Init(GPIOB, &GPIO_InitStructure);4 }& d3 R9 o# G" p8 b) A: S
  19.         ) d8 c" W$ L' u
  20.         // BKIN引脚默认先输出低电平3 U: R! e- N! q. \4 E8 K5 ^% H' J2 K$ W
  21.         GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);
    4 O- o/ i( L/ o! L+ C
  22. }$ ^) |6 k1 @& m" U% A- M9 p% T& G. B

  23. / c+ |3 u) W/ v7 W9 I  u
  24. static void Advance_TIM_Config(void)  Z8 [' G. d1 `4 m' Z% S
  25. {) @2 i. c0 p7 K  z; L5 M7 j9 S
  26.           // 开启定时器时钟,即内部时钟CK_INT=72M
    - T; `) M& d7 x2 _, @4 D
  27.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    . M/ v$ ?0 C5 E2 Z
  28. 0 ?# j5 y; Q; W0 c( @- S
  29. /*--------------------时基结构体初始化-------------------------*/
    % I) j) ?! K) y9 s/ ~
  30.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    1 P) Z! `/ J2 r5 n9 ]# L
  31.         // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断# a1 F- E8 W4 {+ S2 M" o
  32.         TIM_TimeBaseStructure.TIM_Period= (200-1);        & h- Z+ L3 U/ b" f! j
  33.         // 驱动CNT计数器的时钟 = Fck_int/(psc+1)
    : o* N2 [1 B1 G
  34.         TIM_TimeBaseStructure.TIM_Prescaler= (7200-1);        6 X& Y$ A: R$ s" c
  35.         // 时钟分频因子 ,用于配置死区时间,没用到,随意
    + x* R9 B  P4 ~! Z+ ^& g
  36.         TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;               
    1 f! ~* A# |% O% D3 m' p
  37.         // 计数器计数模式,设置为向上计数: D$ k5 e0 O9 M8 a/ v, s+ z! D
  38.         TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;                ! j  P2 h4 l! z) _# ?3 \' I
  39.         // 重复计数器的值,没用到,可以随意设置
    6 o, G; K0 N4 b' g! `
  40.         TIM_TimeBaseStructure.TIM_RepetitionCounter=0;        % j9 G8 i0 z+ B8 F8 h
  41.         // 初始化定时器
    & j! j+ w3 p+ i1 v! s
  42.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
    ; D* W9 p# d# O. w& i

  43. , M7 q, z6 U0 S2 j8 Y* M' x
  44.         /*--------------------输出比较结构体初始化-------------------*/                % L& q, T4 c5 q
  45.         TIM_OCInitTypeDef  TIM_OCInitStructure;
    - p0 r, ?8 D- E" }$ A, R
  46.         // 配置为PWM模式2
    ) v$ f) P/ D: ^% d
  47.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
    1 _' t' s# W+ E6 `; p- d
  48.         // 输出使能; f- Q( w+ Z( o5 e3 o
  49.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    ; M- d! z: L2 L/ F/ _  f, ]
  50.         // 互补输出使能
    % D( \6 p# g" ^2 K/ k
  51.         TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 8 G( r- {4 J: i' \0 ^) b, Y
  52.         // 设置占空比大小
    , G9 {3 ~* y  t& J8 E6 e
  53.         TIM_OCInitStructure.TIM_Pulse = 0;6 R3 q0 C. S/ a4 F
  54.         // 输出通道电平极性配置
    / r# _- N% R% j5 g7 k
  55.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;: [! D) L* I2 e/ O2 B0 C
  56.         // 互补输出通道电平极性配置
    5 G' n1 V5 y9 k( f
  57.         TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;  Q& d, i% @- y$ j: j* T; j* E; W
  58.         // 输出通道空闲电平极性配置2 _7 {! p7 O4 ^1 @; }/ ]
  59.         TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    - z" G$ `+ U$ D. n7 y& z3 S0 ]8 k
  60.         // 互补输出通道空闲电平极性配置
    : @: t( m9 r7 K
  61.         TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
    / I+ o& v; h( k) x! O8 w
  62.         TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
    & D' F: [3 C4 t6 \7 P8 x; e' z' v
  63.         TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    3 G" d# U/ u' K$ {5 H* }, `( O
  64.         2 h9 f4 B9 x) x4 e8 _
  65.         // 使能计数器9 P) `1 L& X* P4 T
  66.         TIM_Cmd(TIM1, ENABLE);        
    2 A0 r" N2 o+ v3 V* Q+ [
  67.         // 主输出使能,当使用的是通用定时器时,这句不需要. l1 U+ |! d- I4 J+ O8 q
  68.         TIM_CtrlPWMOutputs(TIM1, ENABLE);' k2 J& m9 {( u" x5 }' [' N
  69. }' D% ?. n  l1 D# N# R4 D6 C
  70. . T) l& g% |  f; `# \, n4 M
  71. void TIM_Init(void)$ G5 N; N, P+ J# B* v
  72. {
    ( O8 d+ L3 `6 U0 i
  73.         TIM_GPIO_Config();; l) d( ]0 P5 |3 U+ E  E# ^( n( X% X
  74.         Advance_TIM_Config();
    0 B# s1 L1 s' `- M3 y
  75. }</font></font>
复制代码

$ [5 X  B+ P, j8 \) d4 V

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

3 u& K- R( N$ M5 H2 L1 r" _

在初始化完成之后,就可以在main函数中实现信号的输出了。9 I" V2 U, [) F# W4 Q+ I( M
6 k0 P! T' P% S  ]! {; H  s
3 O' e& _6 Y6 j8 U; [

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

! ]9 Y# H, G) r. I6 Q% R4 e* z

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

' L, o2 R' H6 n9 J: }7 t" ~- O5 H

  1. <font color="#000000"><font size="3" face="Tahoma">- j9 t$ B5 o; n. [) ~
  2. #include "stm32f10x.h"
    # S# F7 x+ k# I! s" x
  3. #include "bsp_Advance_tim.h"
    : K: d6 c8 r) F, e9 f6 h6 j
  4. #include "delay.h"+ I  ^2 M2 I. q9 V

  5. " K3 Z2 B9 V% d
  6. int main(void)% N3 }- l: [6 e1 a, {' |; [
  7. {
    , l) x8 B2 v" t' [5 B$ h1 @% @6 K
  8.         int delay_time;4 d% ]" K* q  O8 }9 h. Y
  9.         delay_init(); //延时函数初始化
    + z6 b5 J! R0 u9 A
  10.         TIM_Init(); //定时器初始化
    ( V5 b: B. k% ~# p( X
  11.         
    0 }6 C! d5 M% f* B
  12.         delay_time = 500;
    2 |& G! c7 X- n  L% H; V$ k
  13.         while(1), c) [  o! i2 Y# F1 @9 F
  14.         {
    0 C5 j" ?+ ~1 }- \
  15.                 delay_ms(delay_time);' ]' ]# z0 a  J# Z5 e7 i8 Q
  16.                 TIM_SetCompare1(ADVANCE_TIM, 175); //对应180度0 v$ |. G: u+ ^9 Z  I2 \
  17.         delay_ms(delay_time);
    ; Q( K1 E4 g- I- J2 M9 C
  18.                 TIM_SetCompare1(ADVANCE_TIM, 180); //对应135度
    5 @9 h7 r* e9 Q6 n
  19.         delay_ms(delay_time);6 s, s% I2 [  D6 T0 C) ]
  20.                 TIM_SetCompare1(ADVANCE_TIM, 185); //对应90度5 _/ R* e6 i) b
  21.         delay_ms(delay_time);
    4 c$ M" Z2 c; r! S$ c
  22.                 TIM_SetCompare1(ADVANCE_TIM, 190); //对应45度/ `* A; I5 o9 [4 _1 e9 ~) H8 L
  23.         delay_ms(delay_time);
    7 G8 R" i" G! ~  I: @
  24.                 TIM_SetCompare1(ADVANCE_TIM, 195); //对应0度
    2 [1 p& J9 U: {, I5 ~+ |* w
  25.         }
    8 B( T# f. r3 O! {# {( w6 D& F
  26. }</font></font>
复制代码


2 A2 h. A& h( H- C0 t3 Q8 s  {

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


8 o8 U. A5 Z3 Y3 ]$ {

在这补充一点如何连线:可以用杜邦线将舵机的电源与stm32的5v或3.3v引脚连接,将地线与stm32的GND连接,将舵机的信号线与stm32的PWM信号输出引脚连接。
0 x  n6 g3 i5 T) S# {# W$ ~3 t
  X, t. W$ I# u& H
9 H" N/ q* U+ j3 h1 P8 B* n7 t! A! H8 _, Y
收藏 评论0 发布时间:2020-9-15 10:52

举报

0个回答

所属标签

相似分享

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