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

【经验分享】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米。
  • 线数:旋转一周信号线输出脉冲数,也成为分辨率3 w% F' u8 {: o" k$ R
  • * O/ u6 n) R* P* g  i, ^

    & T5 |: p- t9 Y
1248164-20180510192503629-93708335.png

0 l* F$ y7 C. @
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)
  _9 O, M5 e5 i1 w
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙

$ o" _% X* d! L: X
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V

% g/ d& I, W- @& ~  F

6 D1 x2 g4 X  G- f6 j) l3 S! T8 z二、硬件设计
  由于增量型编码器输出脉冲,可以直接接入PLC单片机,利用PLC高速计数器进行测量。将编码器接入STM32单片机需要一个定时器资源,并将定时器配置为编码器模式,稍后会简要介绍配置方法。根据传感器的输出方式,进而来判断是否需要进行信号预处理。对于集电极开路输出的传感器接入必须使用上拉电阻。注意STM32大多数引脚的输入电压都在3.3V左右,尽量不要过压。如果电压较大,最好加隔离电路,比如6N136光耦,类似下图

! _8 I  ?; d# F- i
1248164-20180510200405732-66175176.png

+ m- b7 U4 p5 j4 H+ V
  我在这里并没有使用隔离电路,而是对传感器统一使用了5V供电,并在信号接入端人为加了3.3V上拉(上拉电阻1K),这种接口电路在我自己设计的采集板上统一留出了8个。编码器应用中并未使用Z相,仅使用了A、B两相进行编码器计数。按照线序说明接入STM32的定时器引脚,PA6、PA7对应TIM3的CH1、CH2通道,PB6、PB7对应TIM4的CH1、CH2通道。对于第二种传感器的推挽输出方式而言,实验证明这种接入电路也是可行的。(具体电路没有进行分析,只是图个方便。但是5*70%=3.5V应该是勉强可以直接接入stm32引脚的)
0 T# s+ S" _3 n; M0 W1 c
1248164-20180510202458411-1027273337.png

, K* G. u  \& K$ x7 M三、软件设计1、STM32定时器编码器模式简介
  参考官方文档,摘录几个要点:
  • 编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。
  • 两个输入TI1和TI2被用来最为增量编码器的接口,TI1FP1和TI2FP2是TI1和TI2在通过滤波器和极性控制后的信号,计数器由每次在TI1FP1和TI2FP2的有效跳变驱动,根据跳变顺序,产生计数脉冲和方向信号,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应设计
  • IC1、TI1概念理解:TI1是定时器输入通道,IC1是输入捕获通道(两者可以交错)。配置CCMR1寄存器的CC1S位可以指定IC1的映射方式。可配置滤波器和预分频器。
    2 X2 p; u" x$ N. [
    9 v0 B8 n" ^7 h% [7 K7 p4 S3 I# ]
, l5 u4 M% _: k5 a9 V
1248164-20180510211213519-2095565993.png
9 d' F9 ]6 A5 b, R$ e
1248164-20180510210619647-836174267.png

$ x5 H: T/ K& c4 E) V
  • 计数方向与编码器信号的关系
    " H( A1 t5 Z/ l3 I& \

    3 x: y( k) o* h3 b

+ F# B5 e) q2 s/ |9 w, b1 l! D

& p" P6 W9 u. x 1248164-20180510211455433-476773874.png
) i% }7 G2 c3 K$ N+ @$ z- _
; f4 U* w# C' I) `, l! w' Z

1 b) U8 T# u4 s. f2 F* j
2、软件实现
  1. 1 /********************************************
    + Q; `* V- i! q9 b" @$ i( C8 c
  2.   2 * y9 U4 T$ \) _, x! T$ J2 Q
  3.   3 TIM3、TIM4定时器编码器模式配置
    6 Y! K; ^4 y# ]  `" z: Y% m# z6 a
  4.   4 ) V/ ^8 A5 Y  ~8 @) ]1 f( ]4 a1 n
  5.   5 *********************************************/
    . S6 }: b5 @1 b+ ~
  6.   6 5 Z$ c; \) o4 C4 |5 x5 _! r! Z- Y
  7.   7 #include "sys.h"3 \' b8 Z1 g* K
  8.   8 #include "encoder.h"7 x; R8 l8 T4 g6 [; q6 |/ q" C
  9.   9
    " T8 @& @! W  s8 }3 Y# ~9 H3 y6 V
  10. 10 int TIM3ITCount=0;//中断溢出次数% P( P9 R  w$ X$ E
  11. 11 int TIM4ITCount=0;//中断溢出次数
    5 ?/ d0 G' N2 y5 i3 m# B5 u
  12. 12
    . b" k# N, o) r8 W: {
  13. 13 void TIM3_IRQHandler(void)
    % Q* b1 }8 F2 a5 g! L. J
  14. 14 {   6 d9 ]' Q# W- o3 }" j
  15. 15     if(TIM3->SR&0x0001)//溢出中断
    4 t4 N' n6 G. w/ B! D
  16. 16     {
    # N( L& x9 ?. _6 p2 ?  g
  17. 17         " h, r6 ]/ L( `7 Z# V( B1 N, h
  18. 18     }   
    9 l# T) P/ u0 y, g/ `% P" P
  19. 19     TIM3->SR&=~(1<<0);//清除标志位   
    ; y# x0 B6 P0 c% ]3 _% V) y
  20. 20 }
    " a0 a% }- f# }: o  G& m7 j( F( h3 M
  21. 21
    - i9 A' G% J& M  f
  22. 22 void TIM4_IRQHandler(void)$ z1 B& l' T9 d- I& \+ I
  23. 23 {   : n& i; @1 X1 }# N3 o' P
  24. 24     if(TIM4->SR&0x0001)//溢出中断
    8 G! K7 I; m5 v  w# @- W8 o! H
  25. 25     {$ R8 t, p7 q" F
  26. 26     }   
    2 @, ?' ?) P& ^) j
  27. 27     TIM4->SR&=~(1<<0);//清除标志位    1 Q7 C% w3 `; @$ Y+ p% A
  28. 28 }" z( t9 t% c$ A1 f+ _5 I
  29. 29 ( u% D9 C7 F( W3 ?
  30. 30 void TIM3_Encoder_Init()# R" h+ b. U# M. z8 A
  31. 31 {
    ( V& e2 `  e) e- W) w8 h0 w* q
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    * Q8 E9 g. p1 q& e8 W
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;; R% Z. @& r2 T' J
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;
    9 M9 m  s, w; }% t& p, e  e
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;
    ( H; r# O. B6 B) K
  36. 36     
    $ n- f+ `% g0 p5 u4 w
  37. 37     //时钟使能' ^5 J0 s5 s! M. `
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟2 J) n2 K. G4 K9 X$ R% v# N
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟* @8 ?* u; P# S3 P8 ]' J& `
  40. 40     
    ; p0 d6 e  N8 N
  41. 41     //GPIO配置5 Q& ?1 P* E" P/ z# K! }7 H
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置
    ; `; Z7 y! J% |" e  ~
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入6 r4 j! V% E6 k* u0 ^; [
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ' E% L$ I0 h% P3 z3 z3 t
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA
    / u, k5 F8 S6 F4 s$ Y% W
  46. 46     % A- o) X" H0 N# U+ d
  47. 47     //定时器初始化
    " X: m/ @! _* K- ^3 [% e5 H
  48. 48     TIM_DeInit(TIM3);
    : \# v* N  g. ]
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);! O  T0 |4 i  ^+ q; q" S" q& o
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值
    # |0 ]+ Y; e. F/ M9 l3 s# a
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频9 C# Y) x6 S) G7 `" V) d1 N
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    / c6 E1 @  j+ S
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    2 h4 T& X8 I) e% i  Z) I
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 6 i6 j# W. ]7 X6 V
  55. 55     
    ) k; U1 B& \8 R/ a2 ^
  56. 56     //编码器模式配置
    7 q! S- e$ s5 V# D$ c1 H
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);+ p& _9 s0 M% |% O
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);
    & O6 x1 Y* \0 ?% @6 n/ z
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;3 B9 z2 I! u5 L' Y
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);
    % n1 [) ~; H' ~* e. J
  61. 61     ) L8 W: I+ o# L7 y3 ]$ s6 P& s
  62. 62     //初始化 NVIC 中断优先级分组" v2 s7 z( e/ ]$ @) E2 n) ~" A/ b0 p( B
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断0 j9 R" x, O# U& K+ A. X
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级% T# y1 [( e. g& E
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
    ; F: d+ u) N5 u' `  I: `( |
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能  Y2 H: Q5 h8 d8 v& l3 i4 q
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC% h' ^2 y! w! H( ~
  68. 68     , R; T, T7 K6 B
  69. 69     //中断配置$ S8 T2 T) `% W: l; p! x
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    5 R$ y- n9 l  D% W" I; a
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);6 v* [% W8 F2 n, H
  72. 72     
      t; _' K6 M1 t
  73. 73     //Reset counter
    8 ^: ^5 z0 v/ D, k8 Y) D6 W/ M
  74. 74     TIM3->CNT = 0;
    9 d% y7 d( v! n
  75. 75     //使能定时器2 m. I$ v6 Q5 T6 m# T5 U; @
  76. 76     TIM_Cmd(TIM3, ENABLE);   # ]7 I3 C7 y7 z9 N
  77. 77 }
    ) i+ [% _6 o% O2 ?% e, c# v' U
  78. 78 . B; `5 c% z1 V, [  b. S% ~
  79. 79 void TIM4_Encoder_Init()
    , t+ b! N" A* d; N# I2 l
  80. 80 {
    , T! h) J! D! Y) C
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    4 f1 K2 c6 S* ?$ S
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;
    $ Z4 w, A+ L% _: N; m+ o
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;
    5 [8 j7 j' \5 G  s7 S
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;1 `5 F( l  U/ i& J* v" }
  85. 85     
    1 I, p0 a0 O7 I- d0 F; _
  86. 86     //时钟使能3 j: c% |1 ]$ l2 r, h
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟# f2 n" H( q- e
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟
    2 O7 a% s9 Q8 y0 l& @
  89. 89     ; F7 P% z) T: E5 [) D
  90. 90     //GPIO配置0 {( L5 [# ]% L5 Y5 A) Y
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置
    2 r# d' l/ `0 {! H% y) W9 u( {3 I
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入" D, I6 f* j6 w) }% c9 K5 o
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;0 V, G& q# q+ d$ K, l# k
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA- B" r& P: k* U. ^* _) E' t
  95. 95     
    ) V& |, m/ V( S) E  E
  96. 96     //定时器初始化: L8 k$ G- Q6 Q2 f1 z7 [2 ?8 ?
  97. 97     TIM_DeInit(TIM4);
    ' {4 F5 X6 N8 d  M1 W( I
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);2 a0 _, e0 H+ R. m2 Z$ ^1 l2 Z
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值
    1 `+ p1 N) o# O' r( _
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    ! T" F# y- }9 V( R
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    % d1 h8 F# f8 p; F, _% I- J  K- n
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    $ W/ f7 T4 p0 m% H
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 2 b$ a! U# f5 h
  104. 104     
    # _- ]. H# q; j4 S
  105. 105     //编码器模式配置
    ; m5 o& W1 X  {/ b' [! w! Z
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    4 S& O  T5 |6 F& ?
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);
    3 x/ D, G1 r0 e
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;; u8 q" E: ~/ x
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);' o0 H$ g- a" G$ h& h7 c  _  d& v
  110. 110     * L2 k4 H" n  ^1 P5 J
  111. 111     //初始化 NVIC 中断优先级分组4 V% R# _4 s% o4 I
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断: y; o: k/ |# |# o$ R1 @% H- T! N
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级' s8 I5 Q7 }0 d$ n! O( b  \
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级% @7 L& p7 C, M% H" S8 V
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
      q& F/ G0 ^( ]7 C% Y
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC2 L9 N% l( U; z# o5 h! r
  117. 117     
    6 r" W2 z" w/ c- V: R! u
  118. 118     //中断配置9 [6 t8 `& V* A5 [# P
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);
    $ c9 `4 a2 Z. h, [
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);8 E- S% f! A% w- o% p. a5 a
  121. 121     & `- t* ?+ s: _+ p7 a, \
  122. 122     //Reset counter8 M4 |8 T* U/ B
  123. 123     TIM4->CNT = 0;
    3 i8 B! y) q; r5 u. C: m' K& q
  124. 124     //使能定时器2 N; G7 _+ b1 Y4 w( ^: ?
  125. 125     TIM_Cmd(TIM4, ENABLE);   8 L- d7 T/ G1 j8 S* O
  126. 126 }
    $ b% t% v( W9 x* F; b5 g) M3 h
  127. 1 C3 z& ~8 a; [
  128. encoder.c
复制代码

7 G1 K7 p  a% m& r
# B& w' o1 t5 K0 S% V! T& p
3、注意事项
  • 更新中断可以不开。但是由于没有使用Z相信号,所以这里保留更新中断,实际上目前中断里什么也没做
  • 由于是双相下降沿计数,所以转一次会输出两个脉冲,转一周脉冲数为分辨率*2,注意处理。当然你也可以配置为其它计数倍数
  • 这里预装载65535,可改为线数*2,即可对转一周的位置进行严格编码
  • TIMx->CNT的值即为编码器的位置(并不一定是输出脉冲值,跟旋转方向和预装载值有关,增量编码器的零点是初始化自己定义的)
  • GPIO配置为浮空输入模式。对于集电极开路输出的编码器,如果你没有外部上拉,则使用内部上拉,配置为上拉输入模式即可。
    ! E! H( O" N' z, h$ f

    ' H- u' X6 T0 q2 H6 Q3 M8 d
: [: k  [' s/ o, H2 |% I- b

  P2 M1 f- p# C+ Z7 m
收藏 评论0 发布时间:2022-1-16 17:55

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版