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

基于STM32的PWM舵机控制经验分享

[复制链接]
攻城狮Melo 发布时间:2023-9-21 14:07
PWM舵机控制5 B& K! f) y8 R
6 F+ J# v( |( i; X* r* T* R* d( m
硬件清单
  • 180°舵机
  • 270°舵机
  • 360°舵机
  • STM/GD32F103C8T6核心板
  • 杜邦线
    ' w3 f/ Z0 Z2 U+ M
9 m: C2 g/ W$ G% m7 e
硬件接线
! y3 E0 C2 `' I' X

! n% i3 b$ c' S' |) ^6 t; V
(KJ87HQKEAR50GMANL~TDZV.png

0 }) {' a1 d/ e" r; P- z5 ~一、标准/连续旋转舵机( z$ i. ?- ^, a: F* r. ^. _
PWM舵机的控制原理基于脉宽调制技术。
: i& [6 F7 |- L3 u' H5 z3 P5 }1、标准舵机(180°/270°)
  • 具有固定的角度范围
  • 精确控制角度位置
    8 R) X  ?; r5 T* g* K
通过改变PWM信号来控制脉冲的高电平时间,从而来控制舵机旋转的角度2、连续旋转舵机(360°)
4 Y5 ~+ G3 O7 a9 A: J* r
6 N1 F0 x* o1 `- i
  • 控制旋转速度和方向1 n9 V: s% v! m* h8 ?2 u9 o
  • 实现无限制的连续旋转
      A) w9 L, n# d" r4 s" s
3 S" U# r* X: y

: g/ K& y' W0 n$ x通过改变PWM信号来控制脉冲的高电平时间,从而来控制舵机的速度和方向

. i+ I1 l' [+ F+ X) z0 S
1 Q5 v4 _. V  {, O2 k% I' m; Q! m- G3 ~二、舵机控制思路) }! c$ o) U2 ^( I- {* \
控制信号以固定频率的方波形式发送给舵机;这个方波的周期通常为20ms,即50Hz的频率;
' D/ [% g2 F% {0 N# k+ f) i- W在控制信号中,脉冲的高电平时间决定舵机旋转的角度或旋转速度和方向。+ \; B0 V9 {2 F0 H# Z

) ^4 I. f( M$ E6 G4 |# y5 Z
- P* z$ `" n( ]: s
@E`Y[AZ1G_FO2[ZDGV6E_A3.png / c  W* q* V) J$ ?4 f

* [5 x) P0 I. f% S  f. F5 ]
三、PWM实现思路
1、时钟使能
  1. //TIM3时钟使能
    1 \# _" y& f# |2 \! q
  2. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    1 w  T/ E$ Q$ E; X& t2 L* }% O
  3. //使能GPIOB端口时钟和AFIO时钟
    7 j1 m5 L6 [0 V; O, c4 V3 X) |
  4. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
复制代码
4 ~: @, G$ M* B8 D0 M( _
2、配置GPIO为复用功能及时钟使能
  1. // 配置GPIO为复用功能* X8 O! |# L. t$ z, N( x
  2. GPIO_InitTypeDef GPIO_InitStructure;
    8 L9 A2 |$ d0 N8 [: V
  3. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;  // 设置要配置的引脚
    . x8 c9 u, _4 s0 p5 d
  4. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 设置引脚模式为复用推挽模式
    * v+ w# s4 c2 }
  5. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 设置引脚的速度
    ) ?8 m, x5 O' U1 O2 }( m
  6. GPIO_Init(GPIOB, &GPIO_InitStructure);  // 初始化GPIO配置
    # u3 A  M' z8 L

  7.   r4 e" b$ R$ v( c) w
  8. // 禁用JTAG 启用SWD
    1 ]% I% h3 |9 U0 ]/ r! Z
  9. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    - Z. P# j/ }3 x6 [# i$ ^+ ~
  10. // 把TIM3_CH1功能部分映射到PB4引脚上
    & |: k  p  ?8 A
  11. GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
复制代码

+ ]4 Y* q. g9 A" ]7 N, b2 [+ y# H
由于使用的是PB4引脚,需要将TIM3_CH1功能部分映射到PB4引脚上

  y! f5 K; I3 ~! \% ]5 i- C
3、配置定时器3为PWM模式
  1. // 配置TIM3为PWM模式' M$ m  B, I, c1 G2 |7 `
  2. TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;) m# e* o7 Z% K' o3 T
  3. TIM_TimeBaseStructure.TIM_Period = 20000 - 1; // PWM周期,即计数周期20ms,PWM信号频率为50HZ3 K8 H& n. o1 T$ N6 O3 G/ F
  4. TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频系数,定时器时钟频率为72MHz/72=1MHz3 }5 ~' w, f6 R; s8 U5 U4 V
  5. TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分频
    : A$ H8 }2 A8 U9 Q
  6. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数模式为向上计数0 T) Z$ I* Z$ I& [
  7. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
复制代码
4 [. a3 {  j4 S3 C1 z
4、配置TIM3的通道1为PWM输出
  1. // 配置TIM3的通道1为PWM输出4 l$ k. r2 Y1 V. O* ?/ Q
  2. TIM_OCInitTypeDef  TIM_OCInitStructure;
    % \" `, N; @9 m; ^" K
  3. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
    6 h( p* o! T  @' Q- s, y9 k* a, |
  4. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 输出使能& u9 K. e5 S' s
  5. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性为高电平" |( G4 K5 ?* g, h' e
  6. TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比+ a0 x- J. `0 E% p* c
  7. TIM_OC1Init(TIM3, &TIM_OCInitStructure);
      g, G  N3 }  |* e6 [

  8. - _  N4 ^0 `2 `7 H! b: t
  9. TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); // 开启OC1预加载寄存器
      d. z$ D( P  \' a1 L: @5 ^
  10. TIM_ARRPreloadConfig(TIM3, ENABLE);// 使能自动重装载寄存器. W. i# U7 o1 k% n+ J0 F/ r% W
  11. // 使能TIM3定时器
    9 {$ K2 y# q- O  R" A4 B
  12. TIM_Cmd(TIM3, ENABLE);
复制代码

5 a) W* v5 E3 i/ c, H' h6 ?7 p
四、主要函数
此处代码以180°舵机演示
1、main.c
  1. if(Data_Flag == 1)9 b* \' Z, z# O
  2. {- N& c1 j3 i" }$ C
  3.    int Servo_Degree = strtol(Data_Buffer, NULL, 10);//将数组中的数据转换成整数+ b+ ~4 `' j" `# r
  4.    printf("%s\n",Data_Buffer);//打印数组中的字符串3 D. q/ D9 h! a1 v
  5.    printf("舵机目前角度 = %d°\n",Servo_Degree);//打印转化后的数据
    2 s1 b& |) F% \- r, D6 P7 |- ~
  6.    Degree_Conv_PWM(Servo_Degree);//控制舵机旋转角度( _' N, _3 m* m) U& ~2 `
  7.    Data_Flag = 0;//标志位重置  I- C3 c  e4 O) w8 o" I) T2 v
  8.    memset(Data_Buffer, 0, sizeof(Data_Buffer));//清除数组中的数据& f, w7 O" G& g
  9. }
复制代码
1 K# t' l8 t) k

7 P" p. _7 k* j3 c$ Z, j, l  D! ~使用轮询的方式判断是否接收到正确的数据,根据串口数据控制舵机运转
- h3 L* k9 {+ n% H7 z% N6 D3 n
2、PWM.c
  1. void Degree_Conv_PWM(int Servo_Degree)// 将接收的数据转换成PWM高电平脉宽* F( \4 `2 n( u" ]$ W! @) ]8 c7 x
  2. {
    ! f; v. W: R+ s$ Z9 Z* [
  3.     float PWM_Time;
    " u. T. J7 {! I# d
  4.     PWM_Time = (2000.0 * (Servo_Degree / 180.0)) + 500;
      J( b( R: U' S- p7 l
  5.     TIM_SetCompare1(TIM3, (int)PWM_Time);//定时器设置比较值函数和通道有关 时间参数单位是us2 d1 s5 W# k. P; A0 v) b6 ^
  6. }
复制代码
" |. `; G, K1 \9 i4 J: R/ b
9 k4 s( W, c$ x# O+ E5 W
使用强制类型转换是为了保证精度,最后传递给定时器一个整数比较值

4 @; @7 h/ B* C4 o" U  K; d2 ^8 i) T
3、Usart.c
  1. int fputc(int ch, FILE *f)// printf()函数重定向
    , p# \7 T  @: R5 ]# i; @( V9 J
  2. {
    ( D% R; h- D2 R# K% I
  3.     // 发送一个字符到USART1
    ' M# J7 T6 V" I. t0 ]7 l& f
  4.     USART_SendData(USART1, (uint16_t)ch);, _4 R4 g, i2 J" }+ @( ~
  5.     // 等待发送完成6 ~  N: o. U% `0 u% C" |
  6.     while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
      F2 x# L8 W9 c  _
  7.     return ch;
    1 D# X1 L! V: k; u3 Y! p
  8. }
    + q/ j+ j* d* z

  9. 7 j# P) [6 p. C1 \2 @3 O  \9 U
  10. void USART1_IRQHandler(void)//串口中断函数
    : J7 r( @9 D8 [" C. o! q& q
  11. {
    1 ^; k/ h$ v1 E
  12.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    0 ^& F5 H: v) m" W
  13.     {! {+ s% |  d3 s9 J1 g5 T7 |  |
  14.         Rx_Buffer[Rx_Index] = USART_ReceiveData(USART1);// 从串口接收数据到缓冲区
    ) z/ C' l! e  B7 l* A' w8 U4 n
  15.     if(Rx_Buffer[Rx_Index] == '[/align][b][b][font=Tahoma][size=3][color=#000000][b][b][font=Tahoma][size=3][color=#000000]; g- ~  k9 y# a( x8 h7 U
  16. [/color][/size][/font][/b][/b]) ?2 d# X- u+ C2 ^( m$ j
  17. Usart1:串口中断接收以“$”开头,“#”结尾的数据[/color][/size][/font][/b][/b]
    / k  K* P: r, i! \
  18. [align=left][b][b][b][b][font=Tahoma][size=3][color=#ff8c00]4、SysTick.c[/color][/size][/font][/b][/b][/b][/b][/align][align=left][code]void Delay_us(uint16_t us)5 D. m7 Y5 K# j+ `
  19. {
    ! {" \. I, x/ B" u
  20.     SysTick->CTRL =  0;// SysTick控制寄存器清零,停止SysTick定时器6 x' b0 ^, k& e. m7 _9 ^
  21.     SysTick->LOAD =  72*us - 1;// 设置SysTick加载寄存器的值,确定延时的时间7 b. A0 E3 Y( O, C/ d7 C* _8 i
  22.     SysTick->VAL =  0;// 将SysTick当前值寄存器清零8 m: J6 W0 l, K, U
  23.     SysTick->CTRL =  5;// 设置SysTick控制寄存器,使其开始倒数计数,并启用SysTick定时器2 c6 ?6 v) e# W' p* H; u0 s9 A
  24.     while((SysTick->CTRL & 0x00010000) == 0);// 等待SysTick定时器倒数完成
    & u* G0 I1 e2 f7 U: \
  25.     SysTick->CTRL =  0;// 再次将SysTick控制寄存器清零,停止SysTick定时器
    0 X. H, K. c6 T* y
  26. }
复制代码
使用系统滴答定时器来控制延时" ?+ l5 e& c; o+ n
五、实践
通过串口发送指令控制舵机(180°舵机演示)
1、UartAssist
微信图片_20230921140518.png
注意:串口、波特率、以及数据格式等
* B- H# H: P9 F+ D' p' j* G- M" g9 M
2、现象
180°/270°舵机可以通过串口发送的指令控制舵机旋转的角度;
360°舵机可以通过串口发送的指令控制舵机旋转的方向和速度。
6 d0 D+ q3 z# @! Y0 ?3 G( I8 }
转载自: W-ML
如有侵权请联系删除
! T0 S4 D# J; D& X2 i7 L9 U
/ t! v  v- Q: R, ?, O; F2 |* z( O

- q% \( G# b/ \% L
- M* v( A, d  H% V0 R)// 如果数据是以$开头则开始存储数据
8 h9 j: m6 @5 ^( M$ j( x" R0 z7 w    {' x1 {+ `# Q/ h4 A5 C- e1 O
        memset(Rx_Buffer, 0, sizeof(Rx_Buffer));// 清空接收缓冲区% l8 O6 Q7 J) a6 C: t
        Rx_Buffer[0] = '* u$ |0 V3 y1 {# g# k
: b$ E- H( t0 }3 e
Usart1:串口中断接收以“$”开头,“#”结尾的数据
1 d" M: i2 V8 W% E# S3 R: n
4、SysTick.c
  1. void Delay_us(uint16_t us)
    & y8 |3 d1 H# H% K- C7 t% j. i
  2. {
    ' R8 X, g6 a* r) b, n+ l  b- k6 P
  3.     SysTick->CTRL =  0;// SysTick控制寄存器清零,停止SysTick定时器
    5 k/ x: c) w3 N! ?8 t! V
  4.     SysTick->LOAD =  72*us - 1;// 设置SysTick加载寄存器的值,确定延时的时间
    ! T6 |6 y+ i& x0 j( v
  5.     SysTick->VAL =  0;// 将SysTick当前值寄存器清零; Y& |3 a4 i! R$ b; Z
  6.     SysTick->CTRL =  5;// 设置SysTick控制寄存器,使其开始倒数计数,并启用SysTick定时器6 o  m8 o9 D1 C% F7 s( h- S4 F, {
  7.     while((SysTick->CTRL & 0x00010000) == 0);// 等待SysTick定时器倒数完成; S8 y' N8 c: \2 O# M, n" S
  8.     SysTick->CTRL =  0;// 再次将SysTick控制寄存器清零,停止SysTick定时器0 R+ o1 m: A. f' n
  9. }
复制代码
使用系统滴答定时器来控制延时
2 V: R3 M& x# k5 G$ v2 Q9 ?3 u
五、实践
通过串口发送指令控制舵机(180°舵机演示)
1、UartAssist
注意:串口、波特率、以及数据格式等
+ _0 @9 _; ~, P
2、现象
180°/270°舵机可以通过串口发送的指令控制舵机旋转的角度;
360°舵机可以通过串口发送的指令控制舵机旋转的方向和速度。

! h: l+ u' X8 A/ A+ `' R  W
转载自: W-ML
如有侵权请联系删除
2 F/ u* L4 @7 U  Y$ e8 V5 U1 Y
5 ]8 p) D3 n  u# l- }
4 D3 _' R6 ?  V+ C# p3 b2 r3 ]5 i( N: [
: w( ?; N) R  _; [
;
3 @. I) k7 G5 t3 }% {4 L        Rx_Index = 1;
( u7 B5 `% p2 k- v# w' W" S. P% u; H    }
) f! @+ @) `8 X3 n& J7 q* i- p6 H    else if(Rx_Buffer[Rx_Index] == '#' && Rx_Buffer[0] == '" U% U. `/ B1 r; c0 K* w2 |

2 o" Y. `* B1 |5 o- nUsart1:串口中断接收以“$”开头,“#”结尾的数据

# X$ G2 f* S) K- W
4、SysTick.c
[        DISCUZ_CODE_7        ]
使用系统滴答定时器来控制延时6 [5 C; a  W: Z9 U' o
五、实践
通过串口发送指令控制舵机(180°舵机演示)
1、UartAssist
注意:串口、波特率、以及数据格式等
. X3 i6 P. z+ J
2、现象
180°/270°舵机可以通过串口发送的指令控制舵机旋转的角度;
360°舵机可以通过串口发送的指令控制舵机旋转的方向和速度。

* ^4 q  ~' a( M
转载自: W-ML
如有侵权请联系删除
, l( G  O, }/ S% ~. [5 v
; w: E1 |! Y1 ]& M' o
: }) K* L2 R! d$ v3 V9 }

/ h. W% p8 p% V0 k6 S0 R)// 如果数据是以#结尾则结束数据存储并打印数据信息" w8 R5 Z# F) u, w, P0 H
    {9 ], V' g1 g$ p3 t0 j5 a
          printf("Received: %sundefined", Rx_Buffer);// 打印接收到的数据- m4 [& Z. L3 k  ]' d& h  l* F1 U/ |
          memcpy(Data_Buffer, Rx_Buffer + 1, 3);// 使用memcpy函数进行数组内容复制' }" P0 U; h/ |6 @! E' `
          memset(Rx_Buffer, 0, sizeof(Rx_Buffer));// 清空接收缓冲区3 F7 K" R! e4 Z! n" B
          Rx_Index = 0;// 重置索引
+ K/ a/ g, k( f' c- Y* P          Data_Flag = 1;// 数据标志位, h6 k  C" [9 O7 o" S
    }  C( J* L. D) I1 w3 F, d2 ^' ^
    else4 x7 M( s' T! w. B6 \
    {
5 S" [; [2 k2 e* O3 j# M2 v          Rx_Index++;// 增加索引,准备接收下一个字符& ]/ X. e. z3 R7 B- s
    }& }% i( _' d; f0 X' o0 r1 @* \9 ^
    }
; G( x1 B6 b4 ^) R$ F- x+ _1 ~}[/code]4 s+ s; f* G% l5 v$ W/ x: k4 V

5 Y+ I+ n# G: x* n+ V6 p. C8 tUsart1:串口中断接收以“$”开头,“#”结尾的数据

  K# y: [* z0 y3 ?
4、SysTick.c
[        DISCUZ_CODE_7        ]
使用系统滴答定时器来控制延时
* u$ Q6 a, g; e& \. V8 G
五、实践
通过串口发送指令控制舵机(180°舵机演示)
1、UartAssist
注意:串口、波特率、以及数据格式等
' {7 k0 T# O7 v
2、现象
180°/270°舵机可以通过串口发送的指令控制舵机旋转的角度;
360°舵机可以通过串口发送的指令控制舵机旋转的方向和速度。

! i; O, A& W/ N" [
转载自: W-ML
如有侵权请联系删除
, V4 t1 U. L9 d( K+ ~5 }! E8 D2 R
5 P* M7 t( {) z% O  U
% T- R; H9 G1 P8 x4 y
, f4 L# |9 J& S
! h: }9 k$ e4 J
赞 收藏 评论0 发布时间:2023-9-21 14:07

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版