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

【经验分享】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米。
  • 线数:旋转一周信号线输出脉冲数,也成为分辨率$ V+ W% w" B9 r  q5 W
  • & B: }8 V4 U7 l+ |# o

    2 v8 r% H: {! m2 G
1248164-20180510192503629-93708335.png

" L* Q- t; K; N; j* i! d( u; J
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)

$ }# I! R: f4 B! n3 K
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙

5 t% b7 h: P8 l0 C$ A
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V
  m& T: s2 o" j
3 W5 t7 S& x! \% U
二、硬件设计
[backcolor=rgba(255, 255, 255, 0.5)]   由于增量型编码器输出脉冲,可以直接接入PLC单片机,利用PLC高速计数器进行测量。将编码器接入STM32单片机需要一个定时器资源,并将定时器配置为编码器模式,稍后会简要介绍配置方法。根据传感器的输出方式,进而来判断是否需要进行信号预处理。对于集电极开路输出的传感器接入必须使用上拉电阻。注意STM32大多数引脚的输入电压都在3.3V左右,尽量不要过压。如果电压较大,最好加隔离电路,比如6N136光耦,类似下图

: D0 @, t5 }) M! t& `/ |
1248164-20180510200405732-66175176.png
3 f0 e! |4 N& U: C
  我在这里并没有使用隔离电路,而是对传感器统一使用了5V供电,并在信号接入端人为加了3.3V上拉(上拉电阻1K),这种接口电路在我自己设计的采集板上统一留出了8个。编码器应用中并未使用Z相,仅使用了A、B两相进行编码器计数。按照线序说明接入STM32的定时器引脚,PA6、PA7对应TIM3的CH1、CH2通道,PB6、PB7对应TIM4的CH1、CH2通道。对于第二种传感器的推挽输出方式而言,实验证明这种接入电路也是可行的。(具体电路没有进行分析,只是图个方便。但是5*70%=3.5V应该是勉强可以直接接入stm32引脚的)

2 X: F/ v' V6 g5 I* ?: L
1248164-20180510202458411-1027273337.png
8 A2 y; E4 k0 p( ?2 W
三、软件设计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的映射方式。可配置滤波器和预分频器。
    $ ^/ N% J& H& N2 h
    ! u: a( X# P4 }+ o& p% l% ^- i
& ~" U! _3 r' ^# t$ ]& c% D" \/ K
1248164-20180510211213519-2095565993.png
/ t6 [2 ~( k1 U* [0 z8 z6 H% y
1248164-20180510210619647-836174267.png

' w" z# q* f( ]5 E1 ^0 E
  • 计数方向与编码器信号的关系: @% Z. J* N. e+ B" M# Q
    , j3 Z7 T9 l% g, i* j! Y
0 A, A$ U( t% A5 y$ n5 B+ C/ B
( |" [. K$ e/ q' x! V$ I
1248164-20180510211455433-476773874.png 5 E5 i8 k: |5 {& E( k$ G
' P) B1 h2 W. B# G

3 A/ @+ [2 ?! H9 I
2、软件实现
  1. 1 /********************************************
    8 a- O3 B7 W. b& J+ E. Q
  2.   2 0 B& h, p5 v7 Y9 l( K' M1 y" N
  3.   3 TIM3、TIM4定时器编码器模式配置" R3 c. p( i9 Q& O. D1 e
  4.   4
    / C* ~4 N* T  z1 U; H6 l. Q
  5.   5 *********************************************/
    ; I, A3 [, Q4 S+ w6 I
  6.   6
    - U4 Q& i  S; C. p: a. ?9 V. F
  7.   7 #include "sys.h"
    ! m# T4 h" l8 x6 @7 S) n% O7 C. B
  8.   8 #include "encoder.h"
    1 e. v/ Z" `  z) |$ ^3 R
  9.   9
    ; ~1 d0 L9 ~$ z  c& x
  10. 10 int TIM3ITCount=0;//中断溢出次数
      [1 k# J# M( C* }* k7 B6 g% `
  11. 11 int TIM4ITCount=0;//中断溢出次数
    ( s1 [7 _( n$ w% j/ M
  12. 12
    ) S+ J5 h3 V" N- k6 z  k  a. r& ~  S
  13. 13 void TIM3_IRQHandler(void)
    % h2 J5 Y+ }. [8 \. P3 E
  14. 14 {   / Y# k) ?# e  B) i0 i% t, s
  15. 15     if(TIM3->SR&0x0001)//溢出中断
    1 A2 g* p2 U" s3 ]+ c+ s
  16. 16     {- R7 v9 x! J9 e. T" i, Z8 U# c
  17. 17         
    1 Q+ P1 l9 g( j( k
  18. 18     }   
    6 P, {' m3 D* n7 q7 u( @( k* ?
  19. 19     TIM3->SR&=~(1<<0);//清除标志位   
    3 x- [" X  z# \5 o( |
  20. 20 }
    - Q/ o$ d" i0 T6 Y! k8 o
  21. 21   }3 \1 K8 C1 U: R6 `7 B1 n  k7 u. p
  22. 22 void TIM4_IRQHandler(void)$ y! y/ K4 W6 D, l$ g* Q; w- q
  23. 23 {   
    1 N5 ~/ t" O9 l6 }+ u3 r  j
  24. 24     if(TIM4->SR&0x0001)//溢出中断
    ) A" B$ [1 ^' }, B' A
  25. 25     {: Y  m+ [) |$ z* Y3 q( M8 I3 ?
  26. 26     }   3 r9 v4 U3 _9 G1 S6 r, {7 d& |
  27. 27     TIM4->SR&=~(1<<0);//清除标志位   
    + V: X2 K  D1 i3 e: `9 |( a8 b5 l3 o
  28. 28 }
    - T. i1 ~% ~8 ~' m) G* ~+ {9 A
  29. 29
    # U3 L6 @2 k# P( h6 `6 I
  30. 30 void TIM3_Encoder_Init()  S& ^0 }8 j+ P2 ^- C& o" c
  31. 31 {3 V( d) R+ d. Q- T! ^
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    ! c! p' J" \% B$ A% l
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;
    : a! b. p0 `; R+ u6 C
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;0 V9 N& u5 A4 L! f2 n1 L+ z$ Z, x
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;0 m! V" |5 y8 a+ b/ l
  36. 36     + {2 K# X+ c; S9 s: @8 i! D
  37. 37     //时钟使能
    ' |; j) B$ D# H4 V
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟
    / D# z: O9 ]1 F3 B0 t' V# F
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟& |3 D/ {6 G& }4 L9 I4 q; _0 h. t3 L
  40. 40     3 w! V! U9 E# A% D* t4 b  Q
  41. 41     //GPIO配置
    7 {$ t( ~7 t& c; @
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置9 @) V& `1 |4 t8 r( ?: R
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入) \9 I+ H4 h, c6 O
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ Q: R& ?: d) R
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA& f( }3 [8 h! B
  46. 46     
    : O/ d2 ^3 t9 T; l4 a! h! @( A
  47. 47     //定时器初始化4 U; t6 c/ |* q( @
  48. 48     TIM_DeInit(TIM3);) A. l, ?9 f; j# F
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    " r) [) k, [! `6 r
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值" [  C: I1 p) p
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频5 i5 R+ f& l. [7 e
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;- D& ], n- `  h% v+ |
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; # b7 c. ^, p1 g8 f
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    9 m" ?" I' u! \$ C1 Q9 T+ K2 w
  55. 55     
    4 Z9 k! p/ Y& t' k; q& O" m8 [5 y
  56. 56     //编码器模式配置
    6 A% b$ {3 J+ t3 D* X/ h( h
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    9 X) z( G4 a0 f. _. s. U
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);
    ( k. N) K) C. c' @) V! i$ u
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    ! d) t& L0 w9 d& D! @
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);% f- b) H, F; P- A$ O5 u
  61. 61     / h+ r' S2 v  |5 b' e( f
  62. 62     //初始化 NVIC 中断优先级分组6 `; J1 r( m* [& v; d  b+ C  e: P" t
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断
    9 F/ u' N9 u6 W7 H% e9 H2 ^6 ^) M2 d
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级% a% j! K& e7 p4 @, c
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
    # M! K. }- M$ h$ R5 W& d" c3 W9 p  D+ `
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    7 `& Q$ V* m; s/ O5 O6 {: q
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC2 D4 M* i1 ^/ D% h) I
  68. 68     
    2 j1 {; w9 M7 C, r% X
  69. 69     //中断配置
    : I! P- X( s! w# z& e
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    5 q  k7 N( {8 f  X& }' x+ e
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    # Y* n6 o6 D5 h
  72. 72       }$ N9 q$ l+ a' U8 M$ {# F+ [
  73. 73     //Reset counter
    ( K9 p5 `4 x& r3 F
  74. 74     TIM3->CNT = 0;
    7 Q  ~1 o  @' V3 S" u
  75. 75     //使能定时器
    & U. X: H2 q8 G0 j- s& D( C+ V) R
  76. 76     TIM_Cmd(TIM3, ENABLE);   
    * ~+ a& n8 C4 l) c! u7 T+ ?7 H( X4 A
  77. 77 }
    * B4 P$ P5 G7 a3 c3 Z* {
  78. 78 / k6 U9 g! j) H/ g
  79. 79 void TIM4_Encoder_Init(), ~' g& O4 \% h) d0 z# T5 Z
  80. 80 {/ e' U" T8 `, e( L. |! _# ~
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;+ L5 d: |* Z) x8 C
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;
    ' S+ m* J! w; h4 r# C) }8 O
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;
    ; `& s6 d( C; q! ]$ x
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;# f8 G5 I2 }8 Q% y
  85. 85     ( q/ d% ~; a. s
  86. 86     //时钟使能
    2 O9 g& [& y9 D4 s7 U" z
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟
    / K. G! k& K1 m4 k' Q( |" o& ]
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟
    . H% r& h. O+ {9 c. o7 \' ~1 Y
  89. 89     / }2 w( U& `5 C1 V
  90. 90     //GPIO配置' J" z2 W# A' m  `; _
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置! v9 K3 t7 i* a( J+ a4 I
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入" o4 `2 a/ ]; P/ h% G5 _: o+ C% D
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;5 b7 O7 D) o) U3 g( B/ ?! h
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA' q4 }6 |8 K  Z3 ~) Q' @' y' Y  t
  95. 95     
    0 H6 J+ \: F* L- E
  96. 96     //定时器初始化
    ! z+ b' Y* f4 u) `
  97. 97     TIM_DeInit(TIM4);
    0 r) b7 A0 \: ?- c6 Z
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);& }7 ?5 b- c% W
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值# U: u$ }+ M/ n% L% y( m3 |) q
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频8 M. u) B# p5 E( d& @% v3 L7 v/ x+ l
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    2 F( |! [6 `5 \- t' e- R& M9 Z
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 7 s3 K/ ?. |' Y& [. I6 z
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); ( U" s: Q3 D& W5 h6 f1 \' U6 k
  104. 104     
    + A* n1 m, K4 `! _
  105. 105     //编码器模式配置
    % U, F; h& O0 I, L# z9 M
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    8 @5 I1 L! ?# ^7 r$ F
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);
    8 K9 e/ o$ l6 n
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    + B" u' q# y9 g
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);
    1 w: `) p8 O% `. P0 e- B$ O
  110. 110     2 h5 b/ Q( f) x1 |  X
  111. 111     //初始化 NVIC 中断优先级分组
      R& Q5 H7 n! e# J7 y$ E" y
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断) S$ m8 `0 O. b4 C
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级) \+ [) z  @1 C2 I* v. e
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级
    / C, f/ S' I. i0 J. V- \
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能/ {- H9 Y: Z; V! }
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    8 Z4 h7 A* n, `* J! g8 `
  117. 117     
    1 _8 g9 B, d( w; {
  118. 118     //中断配置2 `* {7 Q, |- J, T( y8 \: s6 g$ g' Z
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);
    / w0 s! H: q5 M) w2 E  j
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    0 L6 B) [# S- _: K  V0 l
  121. 121     
      f3 X) c5 k2 u* G! D+ g# x
  122. 122     //Reset counter: ?. t; @# h- C8 f9 `  j
  123. 123     TIM4->CNT = 0;
    ! \2 H7 j+ f" \2 I1 g( C% }% l  _$ M
  124. 124     //使能定时器; m4 G+ x, J. B+ R
  125. 125     TIM_Cmd(TIM4, ENABLE);   ! f. v7 i. J  G' m: t) g/ u3 o
  126. 126 }
    , z* e( O. l5 y, A3 u% _, g, J( f

  127. ; c; K" J# ^- N& e$ _! T& N7 k2 V3 Y
  128. encoder.c
复制代码

# w7 m5 F; {3 v' z% b- ?' @

" X. R% ^& A4 D; f' G$ x5 ]3、注意事项
  • 更新中断可以不开。但是由于没有使用Z相信号,所以这里保留更新中断,实际上目前中断里什么也没做
  • 由于是双相下降沿计数,所以转一次会输出两个脉冲,转一周脉冲数为分辨率*2,注意处理。当然你也可以配置为其它计数倍数
  • 这里预装载65535,可改为线数*2,即可对转一周的位置进行严格编码
  • TIMx->CNT的值即为编码器的位置(并不一定是输出脉冲值,跟旋转方向和预装载值有关,增量编码器的零点是初始化自己定义的)
  • GPIO配置为浮空输入模式。对于集电极开路输出的编码器,如果你没有外部上拉,则使用内部上拉,配置为上拉输入模式即可。
    7 h8 ~9 r- q0 f9 l, J9 l" W

    6 t2 s1 u2 C# H; k8 C- d

4 ~6 h1 d$ O; V. n6 e; B3 m0 l9 c) }  p6 t
收藏 评论0 发布时间:2022-1-16 17:55

举报

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