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

【经验分享】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米。
  • 线数:旋转一周信号线输出脉冲数,也成为分辨率
    # a+ C! V5 m% j

  • * H7 |; O8 X# i: V; O
    : B: R) r2 D$ a1 L8 z  Y
1248164-20180510192503629-93708335.png

# j2 z* t* A  `0 x
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)

( K+ e  ]- ?* `. K' U! K% F0 G
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙

" G! b2 b, S# s& {3 R5 `
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V

$ Y' z4 T3 E2 ]/ H0 G' b

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

5 h! `& h/ }4 R  P4 _3 }
1248164-20180510200405732-66175176.png

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

& {: |6 i: B" \9 K3 X$ ]
1248164-20180510202458411-1027273337.png
6 w/ X! B, I. d+ `7 A
三、软件设计1、STM32定时器编码器模式简介
  参考官方文档,摘录几个要点:
  • 编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。
  • 两个输入TI1和TI2被用来最为增量编码器的接口,TI1FP1和TI2FP2是TI1和TI2在通过滤波器和极性控制后的信号,计数器由每次在TI1FP1和TI2FP2的有效跳变驱动,根据跳变顺序,产生计数脉冲和方向信号,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应设计
  • IC1、TI1概念理解:TI1是定时器输入通道,IC1是输入捕获通道(两者可以交错)。配置CCMR1寄存器的CC1S位可以指定IC1的映射方式。可配置滤波器和预分频器。8 q+ ?  M6 h' V# n# c

    / F9 l$ ^) S$ G3 Q& l

: J- w/ q2 M9 K9 ]
1248164-20180510211213519-2095565993.png
& g- W& I" R# \% S
1248164-20180510210619647-836174267.png

1 A' x# b+ \: ^" _
  • 计数方向与编码器信号的关系  |, G2 @( V; K' x' W2 k" n

    ' |+ ^1 `9 l% x' q' A2 i4 |! j2 `: W5 g

$ c& K' G* i* j

' j. [/ G5 R3 x 1248164-20180510211455433-476773874.png : X+ p) Y: B) \

8 D5 a: i. O% x- c  _" D

( g, ^& {$ O  t& }/ N1 t
2、软件实现
  1. 1 /********************************************
    ( a4 R9 \+ o0 _
  2.   2
    # M7 h# k2 A) q, V( H5 t0 [
  3.   3 TIM3、TIM4定时器编码器模式配置
    + S4 r2 v% F: z& ^3 q: e( V
  4.   4
    ) E6 `' D+ c, n  U% r2 i5 j6 r& [4 U
  5.   5 *********************************************/
    % m3 |. E+ @( {/ I$ [  @+ r) ]. B
  6.   6
    , r5 Y- A% [) K9 U
  7.   7 #include "sys.h"
    . v6 n0 y3 W/ A7 v7 E& j
  8.   8 #include "encoder.h"& h# p: u  `& e7 n- a
  9.   9
    % P6 s% Y" ?& m+ E( i5 P
  10. 10 int TIM3ITCount=0;//中断溢出次数
    - x# N2 y& t) H! z8 E5 J
  11. 11 int TIM4ITCount=0;//中断溢出次数
    7 M" ~; n# ~' C1 w
  12. 12
    , y5 G# Y5 l- H
  13. 13 void TIM3_IRQHandler(void)) _3 {' S. j! w1 y
  14. 14 {   4 O. q" z6 O1 N: U& M) V0 D$ e8 K
  15. 15     if(TIM3->SR&0x0001)//溢出中断$ U, T; f/ o* k  v; ~$ }
  16. 16     {
      Y, V  v9 n$ [4 m0 r
  17. 17         9 g. i) X* |5 c) ~( i& X+ Z
  18. 18     }   
    & c4 G7 b; t' Q0 w
  19. 19     TIM3->SR&=~(1<<0);//清除标志位   
    8 f& P' @  [' i  o
  20. 20 }
    " X  r. P. ]0 m( H
  21. 21 " G- I5 H  ~- \
  22. 22 void TIM4_IRQHandler(void)  s& M+ r, M/ t7 \# Y  M1 k# R
  23. 23 {   9 o0 I$ H- S4 x  k
  24. 24     if(TIM4->SR&0x0001)//溢出中断  r0 V6 M' L* t5 c3 z7 L' ]3 L
  25. 25     {
    0 z: y) I: r4 s6 _
  26. 26     }   
      l4 T2 U) T4 `( G: {' o
  27. 27     TIM4->SR&=~(1<<0);//清除标志位   
    7 |1 Y* T& @- v% q
  28. 28 }4 g9 y! ^" U1 P
  29. 29
    / c' g4 X* X* {( y0 R' e5 f
  30. 30 void TIM3_Encoder_Init()
    8 J' T1 u5 P- U2 z2 b
  31. 31 {8 J: ~, l& o4 s9 t2 \
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;0 Q5 q! J5 U) K7 L7 P
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;+ p6 z% w3 j9 T4 ~1 l. U
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;+ Z$ R9 i# L7 `) C2 j& F0 P
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;! I) U0 T8 c& }# l4 c
  36. 36     $ C' Y/ f5 J+ o! O2 C# K! G
  37. 37     //时钟使能# k! @/ {& ]9 t
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟% {* j+ n2 H! L- v" `
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟- J4 r! L* K$ p- Z0 @0 g
  40. 40     
    7 F1 G2 c/ D" V; u& o
  41. 41     //GPIO配置
    0 k% _) }# W# w6 e' `& y
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置
    9 s- E+ _, s( W1 S$ ~4 a' m8 V
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入7 C+ A$ M, W, Z1 \- }% [
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ' P: l3 ^! }  P% U
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA) m( O; s- v( K8 B' t: Q
  46. 46     
    " p( Z9 K( }) Q0 B
  47. 47     //定时器初始化3 |# D) O' D1 c
  48. 48     TIM_DeInit(TIM3);$ \5 x, S. c( f* I; \( g
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);2 F1 V9 w$ E4 m1 P
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值# Y. Y1 A) v2 |: V
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频2 v; w# I2 J( W- {+ l; Y; K1 c& [$ s, Z
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    8 l5 a9 j9 m7 H
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    ! L9 D7 j8 p. W4 Q' D6 H1 ~6 p
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    # T! a1 L+ |* R/ J3 Q1 e2 P
  55. 55     ; n( ^9 W* [+ \$ f+ b3 q
  56. 56     //编码器模式配置
    6 r5 x+ L' }" M" x1 P
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    4 p( X, f5 n2 S! l  H$ c) `3 c
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);
    : w, _) G8 v/ Q2 \) y8 F
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;7 U" _; T) x8 g3 z) ^
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);& t- H5 w2 g0 I& y! i2 R9 |
  61. 61     
    6 C% r7 [( U9 D" Q& }
  62. 62     //初始化 NVIC 中断优先级分组8 ~  {! k6 @! E  K; u2 Q
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断
    ) p, @# |, {( ]1 h! P
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
    ! `; x. e& ?2 C6 v  M
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
    ! |+ N1 t) k- U/ _
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    $ \; r- w2 d4 ^5 p  z1 h
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC( Z+ |$ w# Q  b+ g1 Q4 W
  68. 68     
    ! C: I! g: ]& Q6 {6 }8 U0 ?
  69. 69     //中断配置0 E  T. e1 A  I0 b
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    3 f3 `- T4 b) ?5 u3 ^2 L! e
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    ' j5 t. R+ A+ U9 D, h
  72. 72     
    " r- e1 ]- \9 L" v
  73. 73     //Reset counter# `0 }' G& u, O& c: d
  74. 74     TIM3->CNT = 0;
    , Y3 V4 M9 p/ f8 N( H
  75. 75     //使能定时器
    / d& s1 g) U2 Q" S+ i. g0 m
  76. 76     TIM_Cmd(TIM3, ENABLE);   
      ~" y) r, a: D$ K% H+ {7 P. R& P
  77. 77 }
      h1 y; H0 E# c. W$ k6 p
  78. 78
    + [- _% b9 m" a8 D/ Z
  79. 79 void TIM4_Encoder_Init()
    ; c8 s! o% d4 G; V9 E& o
  80. 80 {/ @- e7 c+ e3 v, l4 u5 L9 A& V  E# R6 Q
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;/ F( f9 B# c7 l! [0 f
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;3 s$ y* ?. b3 d8 F
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;. d( h. s+ |$ a+ T3 \2 m
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;
    2 `  I, M& O3 E; @" d2 F  X
  85. 85     
    3 h" P9 _- O/ d
  86. 86     //时钟使能% U# W4 e0 |1 g: W% ]
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟
    4 h) T1 X9 B" S: n
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟" ]  o8 V3 ?" L# K4 M* |$ P7 U3 H
  89. 89     
    : y1 q5 M: y: ^
  90. 90     //GPIO配置' S2 B$ A- C/ P1 Z, T1 i% r
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置
      |3 R; O/ N) l! G7 x$ M+ _
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入* q5 S6 ~1 [% r9 D# ^# {
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;* ~5 I  {4 K: D$ n2 }% r# N
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA
    - s" h( V0 O( M1 \' t
  95. 95     : o4 c4 }* ]; W) r0 N! _
  96. 96     //定时器初始化
    0 {  g* V+ t, s. k# A3 ?
  97. 97     TIM_DeInit(TIM4);9 |7 k  p, ~  _/ U! k7 e
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);7 q2 l% N' v0 {0 x
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值
    " b: Z2 c0 s  s: O
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    $ H7 r& [, ?" s, I& Q+ M3 \
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;4 ^+ |! n0 K! F9 j* G" I
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    ; f# N& x  y% G( S8 D6 Y: R0 |* v" y
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
    5 i5 [2 L. d( M0 N1 [
  104. 104     
    3 Y  T2 h+ H( M3 W+ @+ @5 l* t
  105. 105     //编码器模式配置4 t( Z9 B$ z+ F9 W0 l
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    ) S. t- ~" x4 |4 _
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);$ o0 _1 z- J2 O( z: }$ m
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;4 E! i/ h( Z5 x8 l* ~
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);" [+ W1 b$ }$ n4 f6 g9 g
  110. 110     
    . M6 b9 K# A. t3 N7 V) {
  111. 111     //初始化 NVIC 中断优先级分组
    ! ^( K1 D5 A  `! ^( ]
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断
    , ~# \5 R5 |9 s7 V2 g& `2 v7 h
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
    0 B1 m8 {% `- S4 [  H/ p5 z4 I6 n$ p
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级
    9 T3 I$ N5 M; Q( Z9 j$ }
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能- f# R- O4 O( ^5 ~
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    : F5 N7 g, h* [0 J: I) ^  q
  117. 117     - D- e3 w4 c& ~0 i  W
  118. 118     //中断配置8 S) k: H! s3 u
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);. E* D& C# W0 K; h/ S4 T" `
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    * h$ X1 U% ]; s
  121. 121     
    2 @5 f1 w0 T/ C
  122. 122     //Reset counter
    5 w8 f) Q$ M' ~
  123. 123     TIM4->CNT = 0;1 `$ w/ v1 p7 r, \) k8 J2 |
  124. 124     //使能定时器6 |. ^- I1 \9 X0 c0 V! p( f
  125. 125     TIM_Cmd(TIM4, ENABLE);   4 _6 Q1 J5 A6 {, c$ y  k4 X
  126. 126 }
    1 C9 C9 l6 f; }

  127. . x& O8 k( h1 t2 C4 N4 s& C
  128. encoder.c
复制代码
, o  v% i' L3 J

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

    7 x' J  v. h8 O! L' @0 x' f
/ z0 M/ S& I4 f! O

: U- \7 m% b' H7 R( f
收藏 评论0 发布时间:2022-1-16 17:55

举报

0个回答

所属标签

相似技术帖

官网相关资源

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