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

【经验分享】STM32-增量式旋转编码器测量

[复制链接]
STMCU小助手 发布时间:2022-1-16 17:55
一、增量式旋转编码器1、简介
  • 编码器(encoder)是将信号(如比特流)或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号,前者称为码盘,后者称为码尺。
  • 按照读出方式编码器可以分为接触式和非接触式两种;
  • 按照工作原理编码器可分为增量式和绝对式两类。增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。绝对式编码器的每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。 
  • 旋转编码器是集光机电技术于一体的速度位移传感器。它将被测的角位移直接转换成数字信号(高速脉冲信号)。
  • 我们通常用的是增量型编码器,可将旋转编码器的输出脉冲信号直接输入给PLC,利用PLC的高速计数器对其脉冲信号进行计数,以获得测量结果。不同型号的旋转编码器,其输出脉冲的相数也不同,有的旋转编码器输出A、B、Z三相脉冲,有的只有A、B相两相,最简单的只有A相。A、B为相差90度的脉冲,Z相信号在编码器旋转一圈只有一个脉冲,通常用来做零点的依据,连接时要注意PLC输入的响应时间。旋转编码器还有一条屏蔽线,使用时要将屏蔽线接地,提高抗干扰性。
  • 信号输出有正弦波(电流或电压),方波(TTL、HTL),集电极开路(PNP、NPN),推拉式多种形式,其中TTL为长线差分驱动(对称A,A-;B,B-;Z,Z-),HTL也称推拉式、推挽式输出,编码器的信号接收设备接口应与编码器对应。信号连接—编码器的脉冲信号一般连接计数器、PLC、计算机,PLC和计算机连接的模块有低速模块与高速模块之分,开关频率有低有高。如单相联接,用于单方向计数,单方向测速。A.B两相联接,用于正反向计数、判断正反向和测速。A、B、Z三相联接,用于带参考位修正的位置测量。A、A-,B、B-,Z、Z-连接,由于带有对称负信号的连接,在后续的差分输入电路中,将共模噪声抑制,只取有用的差模信号,因此其抗干扰能力强,可传输较远的距离。对于TTL的带有对称负信号输出的编码器,信号传输距离可达150米。
  • 线数:旋转一周信号线输出脉冲数,也成为分辨率
    & x, W/ x9 P8 b5 S
  • + e5 ?- g/ E2 Z) {

    ( E$ p0 \& N  r" g5 H, ?
1248164-20180510192503629-93708335.png

1 J1 Y) X( q: G# h3 q
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)
  e4 f! s3 E5 L) `& ?6 L9 B
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙

! Y, a" R/ v+ y9 E
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V

' ?9 z7 R( y7 o3 o' `5 A
- V4 \# C/ N" a; K# C9 a" v$ M
二、硬件设计
[backcolor=rgba(255, 255, 255, 0.5)]   由于增量型编码器输出脉冲,可以直接接入PLC单片机,利用PLC高速计数器进行测量。将编码器接入STM32单片机需要一个定时器资源,并将定时器配置为编码器模式,稍后会简要介绍配置方法。根据传感器的输出方式,进而来判断是否需要进行信号预处理。对于集电极开路输出的传感器接入必须使用上拉电阻。注意STM32大多数引脚的输入电压都在3.3V左右,尽量不要过压。如果电压较大,最好加隔离电路,比如6N136光耦,类似下图
+ o( v0 g$ l: x7 A, ~+ f
1248164-20180510200405732-66175176.png
4 S( W! V( F4 K  ?
  我在这里并没有使用隔离电路,而是对传感器统一使用了5V供电,并在信号接入端人为加了3.3V上拉(上拉电阻1K),这种接口电路在我自己设计的采集板上统一留出了8个。编码器应用中并未使用Z相,仅使用了A、B两相进行编码器计数。按照线序说明接入STM32的定时器引脚,PA6、PA7对应TIM3的CH1、CH2通道,PB6、PB7对应TIM4的CH1、CH2通道。对于第二种传感器的推挽输出方式而言,实验证明这种接入电路也是可行的。(具体电路没有进行分析,只是图个方便。但是5*70%=3.5V应该是勉强可以直接接入stm32引脚的)
3 d) f- o5 u: I
1248164-20180510202458411-1027273337.png

1 V2 v1 D& i3 a' M/ a) y% L三、软件设计1、STM32定时器编码器模式简介
[backcolor=rgba(255, 255, 255, 0.5)]  参考官方文档,摘录几个要点:
  • 编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。
  • 两个输入TI1和TI2被用来最为增量编码器的接口,TI1FP1和TI2FP2是TI1和TI2在通过滤波器和极性控制后的信号,计数器由每次在TI1FP1和TI2FP2的有效跳变驱动,根据跳变顺序,产生计数脉冲和方向信号,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应设计
  • IC1、TI1概念理解:TI1是定时器输入通道,IC1是输入捕获通道(两者可以交错)。配置CCMR1寄存器的CC1S位可以指定IC1的映射方式。可配置滤波器和预分频器。6 S  g) D0 S. R/ }8 Y2 R+ S
    & S2 r: y: @& @9 ~  b, @+ c3 L
/ b4 l6 B+ @: K/ `! ]) u& G
1248164-20180510211213519-2095565993.png
" q9 \6 I0 |, s5 w: |: d
1248164-20180510210619647-836174267.png

/ d- {' i6 F* j. q  ~. p; s6 P
  • 计数方向与编码器信号的关系" D7 Q0 A# l; a- f+ X, [8 Q9 a8 a+ ~
    / s1 [  n! g0 p+ f5 [  F% {
5 s9 e4 r5 g* j/ y# H, ?1 N

* C6 ?2 t" c( |$ D- C 1248164-20180510211455433-476773874.png & G( R+ j* T2 ]% S# J/ e

3 _1 d8 X) M" p5 y9 k$ ?+ J
. u; Z+ N$ {) r* J  E# Q
2、软件实现
  1. 1 /********************************************& b$ G0 I3 G0 T( D  y2 Q# u
  2.   2 - P, m& B: A. d7 P/ ?/ w0 K" u
  3.   3 TIM3、TIM4定时器编码器模式配置
    / j- d9 ]- L+ Y6 d  ?5 }
  4.   4
    " K+ {, |# V) Z6 ?$ l
  5.   5 *********************************************/
    / B6 }! D) Z$ C  _5 C: _5 c, O9 _
  6.   6 % O+ g0 j: P* }  b6 B1 e( I
  7.   7 #include "sys.h"
    ) O2 A( `8 l/ h2 e7 I* P/ T7 S
  8.   8 #include "encoder.h"
    1 j: f& C7 i* I* |
  9.   9   Y$ k( b4 O; I2 {
  10. 10 int TIM3ITCount=0;//中断溢出次数: f2 A- w* U) A  v$ _
  11. 11 int TIM4ITCount=0;//中断溢出次数$ A5 i( j2 J  B& w
  12. 12
    . V  F' i5 h2 D% ^; c
  13. 13 void TIM3_IRQHandler(void)4 ^# U7 D7 d, g/ X8 O( _
  14. 14 {   
    ( J" d7 C4 A) |0 L6 g5 s' P* e
  15. 15     if(TIM3->SR&0x0001)//溢出中断
    - X, K! l4 W! Z3 v+ q% I0 B
  16. 16     {
    3 O/ @7 ]/ I0 u2 S0 V, i6 ^
  17. 17         
    0 i+ x3 P" y, @; s% |3 u/ h. H5 N
  18. 18     }   3 ]: J, }0 f* O* {
  19. 19     TIM3->SR&=~(1<<0);//清除标志位      w+ ]4 K1 b! c9 x- X" b3 J
  20. 20 }. A1 I& U: N. }9 L3 K; n# Y2 c% j; \
  21. 21 ! b( Q7 _" y& B: k
  22. 22 void TIM4_IRQHandler(void)9 G5 t/ j- }' y
  23. 23 {   
    " d; \5 Y5 p: h- p
  24. 24     if(TIM4->SR&0x0001)//溢出中断
    ) H% m$ G8 a+ I9 ]! U. Y# A% b
  25. 25     {0 {* T! i' E. C$ \2 p! y
  26. 26     }   
    + [; Q/ u; y; o* ~& s6 [) q
  27. 27     TIM4->SR&=~(1<<0);//清除标志位   
    ; \6 W, @, N4 ~7 w
  28. 28 }
    ' P  ~1 W! ]' C' F5 r/ q' a
  29. 29
    2 d7 g% q8 b  I- C* ~  ^
  30. 30 void TIM3_Encoder_Init()/ t9 L( R/ _9 Q
  31. 31 {
    5 o6 R0 k& J/ S9 q# N; U
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;$ {6 y+ U) y0 \2 J  L% y
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;
    " Y, a3 u  u2 U
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;
    0 A" K& T$ O+ v9 {( A) s
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;0 a( D7 M/ P/ [" w
  36. 36     
      k7 ~2 ?* x9 U! f7 f1 |7 i
  37. 37     //时钟使能. Q, [3 m# n  N( K; v, v" o
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟' f8 ]9 w) }" a6 `- Y/ b
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟# B3 \7 P9 X/ S( s% W! x0 S
  40. 40     3 P0 L/ A% \$ p6 }4 t$ Z
  41. 41     //GPIO配置
    " f3 }" N" b+ d4 n2 I* B* E2 @
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置' d( p' h% w2 I5 F1 G
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    5 }; @( @/ D% m9 o* o( u5 B
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    % m( `* {  |; f  N
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA6 v9 v1 @) J/ r7 K- D$ e8 D
  46. 46     " D9 }. I9 w; Z7 Z2 B' c
  47. 47     //定时器初始化5 g- ^$ n6 [. ]5 o2 y
  48. 48     TIM_DeInit(TIM3);
    ) v6 `9 T6 H# c9 f) i5 {
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);- N/ A& o% L" ~# T8 W
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值: ?' ]0 W* S1 {8 h) t6 z5 p
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    : u% Z' f% [! j  I
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    - I0 h+ ?& o$ k  E2 w! v' B1 |
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 4 g: I5 q, D9 `' Y2 P
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    8 X9 w8 ]. s: g1 n3 X' B6 x1 i" j  O
  55. 55     ) _- c  W/ e0 z3 e8 e
  56. 56     //编码器模式配置  ^8 @  ]- \* Y3 Z5 M: H& o+ H2 y
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);7 V# d& ^( Z% m; u! Z. S
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);
    ; t( _6 L, O6 M# B- ]. y
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;% k) b' \& s- `
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);
      H7 o" x. h& Z
  61. 61     
    $ a) X4 d% c) T& @2 b9 M' h' t
  62. 62     //初始化 NVIC 中断优先级分组
    * K4 F* L$ l6 ~1 M% c6 D) s( ?) q6 X
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断, F! w6 p1 j( }+ t# I; S
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级/ ?0 A8 i' i3 B  ~8 u9 M% M" u
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级2 ]3 a7 @: G3 j
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    8 H0 x( v5 M' {
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    - K( X0 x. V5 J' v& j
  68. 68     
    6 H- p" D7 a8 l& e! K
  69. 69     //中断配置. a8 M8 b& v& `
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);9 m. l- {1 R( |
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    4 f+ `. [/ [( i2 S  r  O* ]
  72. 72     5 z8 E/ f8 O4 k' w) X# I
  73. 73     //Reset counter. J7 n4 `/ Y( I0 E. R
  74. 74     TIM3->CNT = 0;' e  g& d5 ], y' Q% i5 m
  75. 75     //使能定时器
    0 S/ w* y+ A* F. q7 p4 M9 G. |
  76. 76     TIM_Cmd(TIM3, ENABLE);   : a4 Y3 |7 T# o, w8 ?" K
  77. 77 }7 P0 M+ E+ F! w+ U
  78. 78
    # {7 |# I) L6 R( |+ a$ S
  79. 79 void TIM4_Encoder_Init()
    8 Y% }4 U4 B; T4 n, K3 n+ ]
  80. 80 {
    ; S0 |) \' c3 H& q1 e
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    - n# l& P! }' p5 K3 X+ c
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;% I( r6 z: a+ U; H
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;
    % x8 z1 T8 o2 m: o6 c# Y6 a
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;  Q' w( l0 j* F+ r1 T0 b
  85. 85     
    5 v) |4 f# W. i& m% u
  86. 86     //时钟使能
    . b: ]9 L  K$ E5 C0 p
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟# m+ S; [8 b4 [1 F* G5 _! Z
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟
    : a" x5 B/ J# t' x7 G" O- i
  89. 89     . I/ ]2 g( H4 y8 m& G
  90. 90     //GPIO配置
    $ Q# K2 x! M: N6 N
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置
      T3 x# B, I5 s! z. C# n
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    7 U/ K% n$ l; R  _$ w6 R
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% M5 v. D5 j+ C( X5 D4 `2 v
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA8 p2 k4 ^  z3 [4 f4 ~/ K" K, ?- ^
  95. 95     
    ( d( r0 [# y7 n) |' M; C( R
  96. 96     //定时器初始化
    ( \: ~1 @6 ?7 ^; ^
  97. 97     TIM_DeInit(TIM4);
    $ U( p; `/ \2 e) c' E  i* d
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    3 U9 N& Y: U2 z5 Q2 u# G
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值
    & h( B, c0 p" }; d7 J
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频. o- t9 x4 p- a, W% e$ @
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;! }. E! ~- S; ^7 Z' f
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    5 S. i' o8 {3 U9 B& A
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 8 |# I/ n6 C- I: o7 M  C3 q
  104. 104     9 k7 A$ D* c1 \( }  M- K* Z
  105. 105     //编码器模式配置' j5 f+ G6 q, B: r9 Y
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    . w6 C5 k  F4 z7 \8 H' ]# Z& L
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);
    : E$ L: x+ m: x* }/ X4 N; C
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;! a+ u; B1 O5 n
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);
    & r  }1 ~. p+ X7 Z
  110. 110     
    ( o6 {3 E; i- Y8 u8 ?& p
  111. 111     //初始化 NVIC 中断优先级分组) @5 Z7 ?4 Y5 N; z7 z6 q% u
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断/ t8 N9 ~7 l0 p+ K6 G+ Q
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
    4 B7 J: \5 p( o' q% g
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级
    * W: T) u/ I, f4 z% q
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    / r) z9 `( D0 P1 i
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    ( ]  n' t8 Y  v. p$ [
  117. 117     - R- u! z9 ]3 `+ z; U7 _
  118. 118     //中断配置
    * A6 _9 h2 v3 B( x: r: w. _2 y, \
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);% n3 @. n1 }" u) A+ U  T7 y% E+ Y3 `
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    ! i, }3 D7 B/ Y
  121. 121     
    5 T6 ], O& p& C6 g% F
  122. 122     //Reset counter
    5 ]; Q2 a# m$ q
  123. 123     TIM4->CNT = 0;
    5 n6 e5 L, `- n' e& s
  124. 124     //使能定时器0 O$ N1 a% o  H( ^$ ~0 a! U/ L
  125. 125     TIM_Cmd(TIM4, ENABLE);   3 B0 N$ z3 h$ w' d/ P/ B7 `+ a1 @, V
  126. 126 }5 f! c: ]% s0 g9 C8 K4 P- O
  127. 4 Z4 \7 O0 _8 t/ p
  128. encoder.c
复制代码
$ G* D) \7 H2 `1 q0 p5 O  _  @
1 j% A: ?% P4 v# c% R0 r
3、注意事项
  • 更新中断可以不开。但是由于没有使用Z相信号,所以这里保留更新中断,实际上目前中断里什么也没做
  • 由于是双相下降沿计数,所以转一次会输出两个脉冲,转一周脉冲数为分辨率*2,注意处理。当然你也可以配置为其它计数倍数
  • 这里预装载65535,可改为线数*2,即可对转一周的位置进行严格编码
  • TIMx->CNT的值即为编码器的位置(并不一定是输出脉冲值,跟旋转方向和预装载值有关,增量编码器的零点是初始化自己定义的)
  • GPIO配置为浮空输入模式。对于集电极开路输出的编码器,如果你没有外部上拉,则使用内部上拉,配置为上拉输入模式即可。
    ( W) z& H4 T5 I7 c
    3 E  Q" Y( t5 E& b  F+ L

, a8 Y* d. B0 |- O! W3 ~
1 n0 l- `* B6 j
收藏 评论0 发布时间:2022-1-16 17:55

举报

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