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

【经验分享】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米。
  • 线数:旋转一周信号线输出脉冲数,也成为分辨率
    7 g+ p2 w- t# L5 g( _
  • 1 d. ?3 S5 `! z7 {

    * Y* @( v4 d4 W3 I
1248164-20180510192503629-93708335.png

* g" F' D: f+ i2 P/ ~  q% F5 Z4 t
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)

9 ^# i, J8 v3 N1 Z3 V1 H
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙

6 G! ], u, G8 `& G' o" i
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V

" Q5 ^( o2 m" R& Y8 \! u" n
* I6 g+ k( R( t2 t
二、硬件设计
[backcolor=rgba(255, 255, 255, 0.5)]   由于增量型编码器输出脉冲,可以直接接入PLC单片机,利用PLC高速计数器进行测量。将编码器接入STM32单片机需要一个定时器资源,并将定时器配置为编码器模式,稍后会简要介绍配置方法。根据传感器的输出方式,进而来判断是否需要进行信号预处理。对于集电极开路输出的传感器接入必须使用上拉电阻。注意STM32大多数引脚的输入电压都在3.3V左右,尽量不要过压。如果电压较大,最好加隔离电路,比如6N136光耦,类似下图
" ^  b. X+ r0 h# L: e* }& X
1248164-20180510200405732-66175176.png
( X1 L) w) Q7 h0 J) F0 `
  我在这里并没有使用隔离电路,而是对传感器统一使用了5V供电,并在信号接入端人为加了3.3V上拉(上拉电阻1K),这种接口电路在我自己设计的采集板上统一留出了8个。编码器应用中并未使用Z相,仅使用了A、B两相进行编码器计数。按照线序说明接入STM32的定时器引脚,PA6、PA7对应TIM3的CH1、CH2通道,PB6、PB7对应TIM4的CH1、CH2通道。对于第二种传感器的推挽输出方式而言,实验证明这种接入电路也是可行的。(具体电路没有进行分析,只是图个方便。但是5*70%=3.5V应该是勉强可以直接接入stm32引脚的)
' K( t$ E  e2 J6 _9 o( k0 O7 A
1248164-20180510202458411-1027273337.png

0 X9 x  s! t% E: p- ~' d- P% D0 D三、软件设计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的映射方式。可配置滤波器和预分频器。
    2 [7 B7 N) z9 n! ^% j+ z

    . a# W1 I' g5 a
; {  }* g! |  w- ?2 D
1248164-20180510211213519-2095565993.png
- k0 p: a+ ?6 h* A% p
1248164-20180510210619647-836174267.png
9 E9 W0 `1 L' S7 m
  • 计数方向与编码器信号的关系0 v! j( G% g& z' @/ q: m, f
    9 Y+ w& o- q# V5 e/ R
: w% K9 p6 M" ]7 N5 Q$ z% p# C

5 i! T9 R- x: ^% z 1248164-20180510211455433-476773874.png
# c! e' b' V( [, m: q3 ^/ p8 E! E8 m! X

. p8 B, w1 ]  s6 k1 _5 [
2、软件实现
  1. 1 /********************************************1 v; _' p- {7 q% f% k
  2.   2
    " w# `: D" U. }. |& e" f6 k4 i5 u
  3.   3 TIM3、TIM4定时器编码器模式配置* I: ?7 c. y/ c7 n
  4.   4
      W# P* ^3 F/ I: u1 X% S
  5.   5 *********************************************/
    4 f! X# \% ^1 ~. I$ W, H0 n7 _
  6.   6   }; k; A9 ?" A% ~9 f7 D
  7.   7 #include "sys.h"! D3 \) d, W* g: s+ e8 p4 e5 S
  8.   8 #include "encoder.h"
    ' Q$ k: X5 b6 t3 S  i0 k; {
  9.   9
      c4 B) x  X* W# [' L4 X; |$ i
  10. 10 int TIM3ITCount=0;//中断溢出次数
    / n$ o/ H$ G8 p. G7 F6 B: R4 F, c
  11. 11 int TIM4ITCount=0;//中断溢出次数. C' q: @" v! Y
  12. 12 ; j% W; q: E* b/ @
  13. 13 void TIM3_IRQHandler(void)7 O* G$ |2 T: D3 [" \: e
  14. 14 {   
    - ^1 ?9 z3 P# p$ ^- w
  15. 15     if(TIM3->SR&0x0001)//溢出中断
    5 ^! _" D2 k7 s/ {1 ^+ S0 S
  16. 16     {) J) }7 L; r; H
  17. 17         
    & J: {# q& V. u! `7 Q6 n! Z
  18. 18     }   
    ; W3 i) O) S0 A1 v, t! l4 j
  19. 19     TIM3->SR&=~(1<<0);//清除标志位   
    5 v/ P% i0 M/ g% ~! N+ J& y
  20. 20 }
    * \) \9 G6 Q  f
  21. 21
    1 v2 m1 O# u4 E& J$ l1 |7 j
  22. 22 void TIM4_IRQHandler(void)
    . k3 n" U4 F  O- q0 e
  23. 23 {   6 U! B1 i2 d  o% x, p
  24. 24     if(TIM4->SR&0x0001)//溢出中断! o  ^; A; L" Z* |
  25. 25     {
    % n; s: @* p& {; A6 \$ J
  26. 26     }   
    , H9 J- V  C& B' I3 F; v) g
  27. 27     TIM4->SR&=~(1<<0);//清除标志位   
    4 [0 y  m# F: A
  28. 28 }
    ! U6 ^: Z: \2 X4 j+ I4 A! d7 [
  29. 29
    9 g! T$ i& f, O7 }
  30. 30 void TIM3_Encoder_Init()% [6 D" P" r7 y/ k
  31. 31 {, y- @' q7 v& z% f% ~
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;% V) I" |% S4 ]5 j. u" {& _  s
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;
    ! M  ]' G; ?& [3 g; q
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;6 S( L: r, C! c/ ~* @
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;1 e# {- _/ O2 J# K( {
  36. 36     1 N' y! B4 N5 i" K
  37. 37     //时钟使能) w6 O4 X; I2 `3 N# H. c" @! [
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟
    2 a. q7 ~# o& R. p& f/ N+ M
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟
    . v/ N7 U+ j$ |% J; ^' l
  40. 40     
    7 i1 u1 g4 O- Z  B! e+ @1 S6 E
  41. 41     //GPIO配置8 O: }. {6 }- ~2 z  u9 r. ~
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置3 M' W5 |, x/ [  M5 _# ]4 K0 P
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    ; R: p$ G( k$ [+ H# W( }: H; L
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;& L) p+ o8 o$ z7 ~
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA9 S5 Q  p" g: E6 ?6 ?
  46. 46     
    ! `, w: |) p2 u, @
  47. 47     //定时器初始化! h0 f2 q7 T/ @0 f" J) @3 b
  48. 48     TIM_DeInit(TIM3);
    8 @# ~% A, M; o3 J
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);; {! H  d$ g) ~) T: q' N
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值+ ~' o+ j1 |( M6 K" u2 N6 m( Y
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    2 g& w- g  g, U1 u
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    8 _) p2 ]7 t. V' |2 G' q2 n
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    8 V2 I1 C. |1 T2 N7 g
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    , C- @8 t2 X5 n  c& [
  55. 55     
    6 f3 U4 [# V9 \& }/ _4 n; f
  56. 56     //编码器模式配置* I4 b+ ?" n1 c# p7 _' m
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);( s* d, G1 t5 e* I, f' {& r
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);8 Z% w; E- S2 N; [3 Y
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    + y  I5 w6 [5 x4 X: K- f! ^
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);
    7 y- x, `2 `% J
  61. 61     ' H9 H$ r6 z" [( @
  62. 62     //初始化 NVIC 中断优先级分组3 U/ A; i+ Y1 T) Z
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断+ E, M, B/ H" f5 L1 H7 O& B; C. c
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
    / O9 W6 x* O8 }; L) K& d
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
    . R3 V4 t+ F% a& R, X" _$ d
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能- [6 h- A; r% x2 h5 o3 R
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    , }' n( q' G4 H8 ]  C% Q
  68. 68     # H+ _5 y9 m) J6 f2 J, p1 }( j
  69. 69     //中断配置
    & X: q/ R6 v8 ]2 @3 A, U- p2 k* F
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);2 n0 m! W4 Y( P% H
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);$ W: p5 b5 p7 A( [5 o
  72. 72     
    ) K5 b8 i9 q6 f/ k& S
  73. 73     //Reset counter/ P2 @0 o1 U5 b  O/ S' a9 ^- z
  74. 74     TIM3->CNT = 0;# {2 k$ O3 }* x- W' a' h5 s0 H
  75. 75     //使能定时器2 p3 u" S0 C! B2 R+ W3 Y; a* q) ]
  76. 76     TIM_Cmd(TIM3, ENABLE);   9 a7 E/ w- i; [+ C
  77. 77 }/ }7 L4 B+ Z6 l# |9 a5 T1 m7 j
  78. 78   K/ E0 |& }6 A7 n" g5 A/ ~
  79. 79 void TIM4_Encoder_Init()0 G# U" n+ d* Y  y+ z
  80. 80 {
      n5 ~' }; o& Z" ?" l) B9 V) y
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;+ n7 h; M! P$ |9 x3 ~
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;' `' O8 i' ]7 w- E( Z
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;
    - j8 I. Q8 z9 s* R! |8 F- \
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;
    , @5 q! ^' Y$ ?: @- }2 J
  85. 85     
    3 B: T4 p) Q! o! V9 V: d
  86. 86     //时钟使能
    1 L6 u- E. F) p# |1 x7 h8 X
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟) @5 V5 \1 R' f+ _
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟
    " e, e1 ~' c/ U9 d6 @
  89. 89     
    - h) I% Z2 y% r4 r
  90. 90     //GPIO配置
    + x0 X- b5 h; `! k5 q+ `' e
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置
    - t+ }! d# t/ c& Y- J& Z0 r2 S# y
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入" g  i; k' Y, C& ?# p0 m
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: J* ]- r( U; ~0 D8 |8 Q
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA* v7 O3 A. k! Y$ p% r7 r* v4 _' U! `
  95. 95     
    4 b; d$ l6 c4 s0 j0 i( e  F
  96. 96     //定时器初始化8 @2 A- u, m& o. l- z. b
  97. 97     TIM_DeInit(TIM4);- T" R# l8 j$ C: R% Y2 p! A
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    % e; _. e9 \' H+ C4 K
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值* D# ]% W6 H: H9 i. ^
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频) f: g$ p) i- I* ~( m5 ?! N7 x
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;. r/ i& N& j7 N
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    5 C8 h1 P2 {$ Y  Y6 h8 c
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 1 N7 X9 M8 ^5 X6 e
  104. 104     
    : L: k3 y, i; [1 L
  105. 105     //编码器模式配置
    6 k3 m) G" r1 E- g# [: Z
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);* \: c  a/ G2 v8 L3 S6 v
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);: b! c6 E! H- r) Z  M+ ^
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;1 B. b0 R( ~7 m/ h" y' v) W
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);
      d6 z# b  R* T/ s' O3 [+ `: o
  110. 110     
    5 c# T# z8 F3 _/ ?# J. G3 }1 _
  111. 111     //初始化 NVIC 中断优先级分组
      I: B6 t: |; D/ P
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断! \5 P0 Q! n9 p
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级  O* p' b, c; M# M4 _# R1 ?$ O
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级! k- P  h, {: m9 b1 ]
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    / B2 U: A  q  r1 W; E
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    ; M9 _6 u# R9 {- [, o
  117. 117     
    2 u. S3 g! ^# f0 P  t
  118. 118     //中断配置
    % N9 D/ i" D7 |, z+ j; Q
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);# }$ a- c. w% K( A. {5 w
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
      O* t+ v6 O' f- A* t
  121. 121     - _0 {9 }. l( \5 |$ {3 |
  122. 122     //Reset counter, O4 I7 Q" K/ ^5 B8 l9 p
  123. 123     TIM4->CNT = 0;
    $ o! V6 a" U+ J
  124. 124     //使能定时器( q% L: W9 u( ~% R3 b2 ]7 m- V% _
  125. 125     TIM_Cmd(TIM4, ENABLE);   + W2 d3 Z/ {/ Q: d8 d9 ~# P) x
  126. 126 }
    2 M2 C" w' r0 t' Z

  127.   G& V3 M3 m  n0 V$ [/ y  T; S8 O
  128. encoder.c
复制代码

2 R4 C6 |4 |* ~0 l
* `: h0 v7 k7 w
3、注意事项
  • 更新中断可以不开。但是由于没有使用Z相信号,所以这里保留更新中断,实际上目前中断里什么也没做
  • 由于是双相下降沿计数,所以转一次会输出两个脉冲,转一周脉冲数为分辨率*2,注意处理。当然你也可以配置为其它计数倍数
  • 这里预装载65535,可改为线数*2,即可对转一周的位置进行严格编码
  • TIMx->CNT的值即为编码器的位置(并不一定是输出脉冲值,跟旋转方向和预装载值有关,增量编码器的零点是初始化自己定义的)
  • GPIO配置为浮空输入模式。对于集电极开路输出的编码器,如果你没有外部上拉,则使用内部上拉,配置为上拉输入模式即可。' _1 [$ m; ?" k
    / g- a$ k7 I4 I' D: r' @/ t

/ h* b1 ?3 D. [2 E" g
8 l8 z0 J! i( y8 i
收藏 评论0 发布时间:2022-1-16 17:55

举报

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