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

【经验分享】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米。
  • 线数:旋转一周信号线输出脉冲数,也成为分辨率
    & \6 j0 @) n2 o2 q

  • ( m" Y; c" i9 G
    0 X$ y7 x+ R& {, O; ?3 b1 K
1248164-20180510192503629-93708335.png

  ^/ s  ]- _8 T) y: ]$ H, G" G( T
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)
  S+ ~6 p5 L8 d/ N0 b) D6 z8 v8 ]: r
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙

( m) \8 V( `" X  s
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V
3 C8 Y* d9 m8 b3 T0 i& F% v: E
$ @/ j6 U5 e. b6 X9 i/ K  v( c: e( Z
二、硬件设计
  由于增量型编码器输出脉冲,可以直接接入PLC单片机,利用PLC高速计数器进行测量。将编码器接入STM32单片机需要一个定时器资源,并将定时器配置为编码器模式,稍后会简要介绍配置方法。根据传感器的输出方式,进而来判断是否需要进行信号预处理。对于集电极开路输出的传感器接入必须使用上拉电阻。注意STM32大多数引脚的输入电压都在3.3V左右,尽量不要过压。如果电压较大,最好加隔离电路,比如6N136光耦,类似下图

6 c! e; K5 C! R/ a- S" [$ z5 T; O9 d0 g
1248164-20180510200405732-66175176.png

1 G7 t4 S( m' n" [
  我在这里并没有使用隔离电路,而是对传感器统一使用了5V供电,并在信号接入端人为加了3.3V上拉(上拉电阻1K),这种接口电路在我自己设计的采集板上统一留出了8个。编码器应用中并未使用Z相,仅使用了A、B两相进行编码器计数。按照线序说明接入STM32的定时器引脚,PA6、PA7对应TIM3的CH1、CH2通道,PB6、PB7对应TIM4的CH1、CH2通道。对于第二种传感器的推挽输出方式而言,实验证明这种接入电路也是可行的。(具体电路没有进行分析,只是图个方便。但是5*70%=3.5V应该是勉强可以直接接入stm32引脚的)
) [9 X* L+ C, U$ [
1248164-20180510202458411-1027273337.png
  _/ A. F, h) H& U" z& o' S
三、软件设计1、STM32定时器编码器模式简介
  参考官方文档,摘录几个要点:
  • 编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。
  • 两个输入TI1和TI2被用来最为增量编码器的接口,TI1FP1和TI2FP2是TI1和TI2在通过滤波器和极性控制后的信号,计数器由每次在TI1FP1和TI2FP2的有效跳变驱动,根据跳变顺序,产生计数脉冲和方向信号,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应设计
  • IC1、TI1概念理解:TI1是定时器输入通道,IC1是输入捕获通道(两者可以交错)。配置CCMR1寄存器的CC1S位可以指定IC1的映射方式。可配置滤波器和预分频器。
    3 \+ u; s, S1 h1 R
    * D- K& q- u- a8 U; L' ]
. }8 R( S& e) h; f
1248164-20180510211213519-2095565993.png

9 [6 M- Y" D; }5 a9 W. t! r0 t/ @
1248164-20180510210619647-836174267.png
7 E* r9 p1 o6 M# F+ N1 v; V
  • 计数方向与编码器信号的关系
    + J8 m: a  L5 O& E" Q
    # O/ k7 f5 ~4 O- ^/ \, }
0 v6 l/ b. p4 d  C2 K  e" N

  {+ e" ^- }% r. n8 y; e4 Y5 Q$ N 1248164-20180510211455433-476773874.png
$ D2 Z, V2 m* |/ o: y: \5 t/ ^5 Y7 l
. M8 U$ U- m6 ~; N

. K1 |- U$ M5 J3 K7 d
2、软件实现
  1. 1 /********************************************$ q0 `. R" D, e7 E! B+ K
  2.   2 ) ~9 t  \$ o. i* n
  3.   3 TIM3、TIM4定时器编码器模式配置( ~) J  n* [' ?) [5 d9 E
  4.   4   h1 V4 R9 J( T# L
  5.   5 *********************************************/$ J) i# W8 o. ~( H; ]1 R! G
  6.   6
    ! b  h: w6 p1 d6 x2 Z, l0 t0 p) k
  7.   7 #include "sys.h"
      r' G5 J8 D1 [6 p! O
  8.   8 #include "encoder.h"
    1 g# A; j# u: i& ?
  9.   9   K- u  ~  V3 y1 c$ G
  10. 10 int TIM3ITCount=0;//中断溢出次数# b8 P9 m  d  c
  11. 11 int TIM4ITCount=0;//中断溢出次数; M) i9 Z3 M% V, Q2 x
  12. 12 4 {8 @- u' N+ A1 @
  13. 13 void TIM3_IRQHandler(void)
    - }) Z2 ~' C8 D8 {$ C
  14. 14 {   ) V3 i1 f" N( w; ~- k4 S0 K6 ]- \, \
  15. 15     if(TIM3->SR&0x0001)//溢出中断
    , L( S! H6 n9 q2 o7 [
  16. 16     {/ [5 D0 b) c% Y
  17. 17         3 Q7 Z5 N5 P( H7 o
  18. 18     }   
    % R( ~7 M) K/ |; n7 _% g) p# ?
  19. 19     TIM3->SR&=~(1<<0);//清除标志位   
    7 d$ \- y* j0 Z( u
  20. 20 }& C2 ~7 t# h3 n
  21. 21
    4 B( l0 O9 j; H
  22. 22 void TIM4_IRQHandler(void)
    ) _, g/ ^' r/ I$ H7 }- t! e: H: Z
  23. 23 {   
    # N  ^9 d! c2 k
  24. 24     if(TIM4->SR&0x0001)//溢出中断8 W6 L, X# D: I+ [  n
  25. 25     {
    % F0 K: M: b1 _/ H" Y7 u
  26. 26     }   
    8 z# ~, P8 x6 l/ J, V2 l7 F
  27. 27     TIM4->SR&=~(1<<0);//清除标志位   
    * N! K( j1 f- C
  28. 28 }
    1 m8 @- I# b3 W; M8 a
  29. 29 7 ~& |' C  Y2 @. |- R4 Q
  30. 30 void TIM3_Encoder_Init()8 \3 I  x% e& z0 l" M
  31. 31 {
    % e- n' s0 d  g% n. ]
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    3 p, d3 t( T& T9 Z4 Y4 ?, N5 V4 Q
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;% w$ w" o7 _6 g
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;! q+ M( e7 K2 Q% H% X# L1 |, N4 x
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;
    3 H7 ^) ]5 H. ?9 s
  36. 36     2 u! s( ]% C7 d9 B& c( z2 p
  37. 37     //时钟使能& p+ b" z3 ?, H% b4 J
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟
    * x5 H/ |# b" F; o
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟, K0 E# O9 F2 j. m
  40. 40     
    2 b. @0 ]( B8 A7 o
  41. 41     //GPIO配置
    * w) ~6 v  T# _
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置
    3 E4 b5 |  A5 E
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入9 V7 U- F8 ~/ ~- Y# C" O9 Z
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: O* O' N1 i6 a5 {: u' M
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA
      N4 z, \6 O: o. t3 ^
  46. 46     * \# m* Y2 n7 \8 ~5 k7 \
  47. 47     //定时器初始化
    * h9 Z) \6 J$ k
  48. 48     TIM_DeInit(TIM3);
    : [+ m- x2 B- `% b- D* A9 N
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);( ~: ], T' l. M" h6 ^% X+ U" j
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值/ r' d$ K2 d! K/ j8 K$ ^
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    / a- z& [# P3 X% {6 A/ a
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    / i0 Q! |; G3 j5 P
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 9 L  Y) w& @' U6 k- I$ ]6 w4 B
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 7 f2 U3 H, v& O! r
  55. 55     
    $ z. S( L, i( I
  56. 56     //编码器模式配置' B+ V3 Z: Z- B" `
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);7 c- q4 c3 y' O8 I  s3 T$ x& y1 F
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);
    ) S2 h. U" L) c, n% n
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;8 K3 F/ Q6 Q: r6 d4 O3 C  n2 \. a6 t
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);+ @" v, \, Z) N' G, b& M7 J( x
  61. 61     3 S9 d: o) n1 {2 H" k
  62. 62     //初始化 NVIC 中断优先级分组
    * {4 c& z, K; I6 L' @5 ]' W7 p
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断' r, ~, Q& g) B" Z( i7 S3 }+ K$ L
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级$ L" h3 a: z2 F! D' f; a
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级8 k! k( ^' a2 k
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能& Q' Q0 `; z8 H
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    / B+ e  I7 Y7 r
  68. 68     ( X  {7 D" s! w" Q, M2 U4 `& a% F/ v
  69. 69     //中断配置# V5 L5 o) q  p2 J5 v8 P2 h
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    / H8 {  F$ ^! E7 b4 A1 [
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);) B& O% W/ E: k1 Y' p
  72. 72     0 L  X4 T" A( \4 p/ Q0 D
  73. 73     //Reset counter
    8 x4 I9 c! w8 j3 ^' H
  74. 74     TIM3->CNT = 0;" N9 y' J8 t% p$ r5 ?
  75. 75     //使能定时器
    ( W' p( U) r  _6 Q" c. u
  76. 76     TIM_Cmd(TIM3, ENABLE);   
    3 n* Y1 e4 {, K$ J! g% g( j* `" U
  77. 77 }
    $ o. R' f# W. h' v# z# ^+ j6 s' }
  78. 78 9 F8 y' h$ S  b. n  W8 U9 B
  79. 79 void TIM4_Encoder_Init()
      }5 n# U4 S+ R
  80. 80 {8 m' v# w' X: h, s+ i+ P! ?* I
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;) a2 r4 [: Z3 @0 l! a0 ]
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;
    % s  q2 e3 i6 z. W
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;
    ' U2 ~( h3 M5 G1 ?7 i1 l  ]* c: x
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;* t3 E6 K8 x9 H1 A7 s
  85. 85     
    * c3 |  ^+ h: j/ M/ ]+ z- {
  86. 86     //时钟使能1 p, F  M- n# Q0 Z8 F
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟6 K' }! t1 z2 y6 o% l
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟
    7 x* v2 h# V" j9 L
  89. 89     
    ; B4 k+ y" W% i4 g
  90. 90     //GPIO配置
    2 Q% ^0 U6 s6 G( Z9 y
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置3 B2 L4 q, x4 k+ \( h' o
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    ! T3 q" z# g8 p( L, j
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% t% u' n0 R" o: M( t; ^( O
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA
    ' O# c. V( m/ l# V; Z5 r
  95. 95     9 |3 E3 k) e9 ?
  96. 96     //定时器初始化
    , r2 s# G1 ?6 O& J) W
  97. 97     TIM_DeInit(TIM4);
      N) R" v$ S$ l2 X9 W
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);1 L+ Y% n- C9 F- f  I" s
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值/ r* i/ Y1 a) I3 M& s% j- e
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    5 ^; `: `, h7 r! c+ @3 r
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    $ |9 P+ D2 J; p8 N7 Z) M
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    + U) T, D- X/ \% B$ N
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); - }( ?) f2 H8 a" x
  104. 104     
    9 Q7 f; p6 f3 {4 x
  105. 105     //编码器模式配置2 q! I0 I& ?  R( O" C: i: @
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    . _  x* h: h$ R9 c8 j, j
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);* n" n# \- Y1 b, Z$ I, y6 \9 A
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    . [( A6 E7 e7 A) ]+ M
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);- N) j7 ]2 W: N; Z
  110. 110     . C) \8 a) U5 q; Z( e( g
  111. 111     //初始化 NVIC 中断优先级分组
    & b( s: a1 e2 [/ e
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断
    ' O0 q. `1 f3 r0 B4 j1 \- n. i
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
    / O& J! L, t0 C7 O# n
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级) V+ U% {* H2 H0 W
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    " v" n# D. W# ]
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    " [6 X# N  R6 ~& x" M" g
  117. 117     
    * Y- R5 S: V7 M. K
  118. 118     //中断配置# L; K% C/ m8 g: A: m8 J  B
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);
    - b: x; H$ j* Q9 ]4 C( r
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);! y; H- N" G( O# ]# Q
  121. 121     
    5 g1 z/ H# c# e' G6 u2 j6 p
  122. 122     //Reset counter
    " l+ u4 P! p& G, O9 q7 V$ g
  123. 123     TIM4->CNT = 0;# t- j9 Y1 p  M* [8 `
  124. 124     //使能定时器
    + n  P1 M# u4 L# D1 K7 Z0 S6 @- t9 o: M
  125. 125     TIM_Cmd(TIM4, ENABLE);   
    + `" N- B# [' t
  126. 126 }
    & G: ]2 e3 }2 w" z( N. w  `
  127. ( v- G  |( u  N: K
  128. encoder.c
复制代码

$ {1 W" r# Q0 k* Y4 [; R: R

+ P# `0 ~0 c- j( p) c$ i& M* L  P3、注意事项
  • 更新中断可以不开。但是由于没有使用Z相信号,所以这里保留更新中断,实际上目前中断里什么也没做
  • 由于是双相下降沿计数,所以转一次会输出两个脉冲,转一周脉冲数为分辨率*2,注意处理。当然你也可以配置为其它计数倍数
  • 这里预装载65535,可改为线数*2,即可对转一周的位置进行严格编码
  • TIMx->CNT的值即为编码器的位置(并不一定是输出脉冲值,跟旋转方向和预装载值有关,增量编码器的零点是初始化自己定义的)
  • GPIO配置为浮空输入模式。对于集电极开路输出的编码器,如果你没有外部上拉,则使用内部上拉,配置为上拉输入模式即可。
    : R* e. m4 h1 T, Y

    % P, g7 I6 W6 Y, P/ ?2 b. C

( t5 @- j: E3 f4 t# v$ g4 Z4 R. o" Y& V* \  u
收藏 评论0 发布时间:2022-1-16 17:55

举报

0个回答

所属标签

相似分享

官网相关资源

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