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

【经验分享】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 ~( g2 r" S( ?  n

  • 9 ?! T; q) ~$ ]3 }5 r# U3 m1 m2 E
    + p  a$ s. r, X( W
1248164-20180510192503629-93708335.png

4 y% M1 w8 K* v9 w! `) S
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)

8 w" J; e8 @1 r1 g+ f$ N
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙
0 z, j) U" w0 ?; f
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V
, Z* F& e6 _9 ^/ l% t

! S/ H4 X9 U9 z! g! v$ A7 E/ m! D二、硬件设计
  由于增量型编码器输出脉冲,可以直接接入PLC单片机,利用PLC高速计数器进行测量。将编码器接入STM32单片机需要一个定时器资源,并将定时器配置为编码器模式,稍后会简要介绍配置方法。根据传感器的输出方式,进而来判断是否需要进行信号预处理。对于集电极开路输出的传感器接入必须使用上拉电阻。注意STM32大多数引脚的输入电压都在3.3V左右,尽量不要过压。如果电压较大,最好加隔离电路,比如6N136光耦,类似下图
: i, ?3 v- [% P  F7 L7 K" v
1248164-20180510200405732-66175176.png
( o5 x( X4 c# n& 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引脚的)

! m/ \. z3 o. o8 f4 f, i5 ~- \
1248164-20180510202458411-1027273337.png
. u' `" U1 g9 h& M( U7 ?3 n
三、软件设计1、STM32定时器编码器模式简介
  参考官方文档,摘录几个要点:
  • 编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。
  • 两个输入TI1和TI2被用来最为增量编码器的接口,TI1FP1和TI2FP2是TI1和TI2在通过滤波器和极性控制后的信号,计数器由每次在TI1FP1和TI2FP2的有效跳变驱动,根据跳变顺序,产生计数脉冲和方向信号,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应设计
  • IC1、TI1概念理解:TI1是定时器输入通道,IC1是输入捕获通道(两者可以交错)。配置CCMR1寄存器的CC1S位可以指定IC1的映射方式。可配置滤波器和预分频器。( u2 u/ \$ B* a
    + l" I0 G5 b. f% k4 }
5 |6 p. G. F! S* v  B
1248164-20180510211213519-2095565993.png
: R* R! H9 o2 x9 w8 M+ p" R
1248164-20180510210619647-836174267.png
) @0 |) D6 A* r
  • 计数方向与编码器信号的关系
    # ]3 q4 U  e  |
    9 H! U" c' S' k) j
% Q6 k# u. W& |/ V$ Z1 i( r! w
% n& ?4 j" F9 U& w
1248164-20180510211455433-476773874.png
, v6 |! m6 l/ N8 e: S  I- S  s! Z+ V- d. y' m

2 L( H, q" L% D7 V4 |" N- }2 v
2、软件实现
  1. 1 /********************************************
    , w3 j: F$ b, E% H" @6 f# ^
  2.   2
    & ~7 O, ]* E+ _$ p" y
  3.   3 TIM3、TIM4定时器编码器模式配置; o0 L( N4 C9 y+ r0 r
  4.   4
    2 F2 C  X8 G; b# O% O& i
  5.   5 *********************************************/* h( o4 V" u, N6 \/ c
  6.   6 0 y9 ]  l" |% ^
  7.   7 #include "sys.h"
    # }7 R2 A+ a+ k6 M! G3 c
  8.   8 #include "encoder.h"$ Z9 n7 V! @+ F" @. b
  9.   9 & I! m5 [& R' H: f# {
  10. 10 int TIM3ITCount=0;//中断溢出次数+ B0 ]. A5 |9 ^6 s' e  o. w, T
  11. 11 int TIM4ITCount=0;//中断溢出次数" u5 E' x, U( W$ u
  12. 12 5 q6 Z! W9 \( g9 \5 ?
  13. 13 void TIM3_IRQHandler(void)9 T, h$ Q( ]$ C1 @; i( e
  14. 14 {   ! O8 D7 [* h9 H3 J( q% X* a* p
  15. 15     if(TIM3->SR&0x0001)//溢出中断
    ) F( G$ K# k1 o4 f7 ]: V
  16. 16     {
    % W" s# U* o& r  q7 ~9 M; H
  17. 17         ' R. k* v0 a( U! v: Z
  18. 18     }   $ K% x9 I* b, h
  19. 19     TIM3->SR&=~(1<<0);//清除标志位    $ m- Y9 m$ d5 f7 a" s
  20. 20 }
    . Z3 k& o1 S# ~: _0 r
  21. 21
    $ [. E% I+ |0 F4 k& n$ M8 b. T
  22. 22 void TIM4_IRQHandler(void)
    4 N; h1 U& Z5 i' ^
  23. 23 {   
    " }) ?4 d, j* f. {) B
  24. 24     if(TIM4->SR&0x0001)//溢出中断
    & k3 q2 B7 d0 {( |% U; I
  25. 25     {
    # x% {9 i. l: J! I: ~7 t7 E
  26. 26     }   
    ' T9 B5 h( _# D; {, I/ t: O
  27. 27     TIM4->SR&=~(1<<0);//清除标志位   
    * r# W5 o3 ~  I" ?7 @7 p
  28. 28 }; I6 i4 D" {/ s4 b
  29. 29 & O( \) n% m8 b8 N$ R3 X1 Y" D
  30. 30 void TIM3_Encoder_Init()
    1 Q# g7 i# W$ [- H
  31. 31 {2 q! U# ]! M$ b7 }
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    6 K' p* @* r/ v: v9 K" Z3 P/ S: {
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;
    : l( h$ z1 |+ g' Q
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;5 F) w# _" y5 [& Q6 G
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;
    + m7 {; \( O+ v. o: a
  36. 36     
    ' O; h, P$ [. C  j4 z
  37. 37     //时钟使能
    , W2 c1 x# @0 \; T. s* o
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟2 U, w( c$ `% ?$ w) T- y
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟6 u( q/ e1 o& F0 \% D6 X: ^4 T, h
  40. 40     
    * k4 _. o. x! W5 \0 P& I4 N
  41. 41     //GPIO配置
    - N' ~7 V( `5 x+ W
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置8 W8 A" k( O7 q1 B! S
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入( D$ F$ d6 {$ r& C  Q9 J: E
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;& v3 m9 h! I# H+ E7 Z7 T
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA
    2 @- v1 O9 e3 ~" c  F0 f* l
  46. 46     - l  q; M! X9 l( z: V
  47. 47     //定时器初始化
    0 j2 h: j' a. i9 o$ C+ R, K/ A
  48. 48     TIM_DeInit(TIM3);
    % [5 M0 R+ s+ e
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);2 @( c! t( G4 U& M
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值. s  F) _$ E3 Y2 K) W( P
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    ' c$ U* }4 |' i! ~
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
    $ L: r" l: @' M' W
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 2 V9 d+ `. N& P6 s! K2 d
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 0 ~  B% Q( \  _7 m1 P" c% F- y
  55. 55     
    / a4 T3 N4 d" n. I/ s; `) Q
  56. 56     //编码器模式配置( u: k. l* s7 _
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);0 l- P: |! N8 y9 A5 G! C6 `& J' [
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);' \2 b8 N- {0 ]8 }" P
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    1 N  ^' L) k* E4 L: x" R
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);7 e% h* c" d7 t- g. _
  61. 61     + l. k7 J2 T9 \; q" y
  62. 62     //初始化 NVIC 中断优先级分组
    $ }. X0 D0 |# ^. S6 Y3 D7 f# l
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断1 q* e7 A8 ^+ t
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
    ; T! l& b/ Z0 g- n& ?
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级! Z/ P# T: [/ z& ]" M5 }1 v! b
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能8 ~! J# B3 f5 C' j
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC$ i9 A6 o+ G3 k; X0 r5 a( N
  68. 68     , |  D# X- i1 {1 ?) g1 q5 l
  69. 69     //中断配置
      v4 U. t! z. u, J3 ?- @! U: U
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    ! u) v( k7 _8 ~' Y4 W( }& o) W
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);. \( `2 p& @- p* m
  72. 72     1 |3 W' }$ Y4 d) Q2 c
  73. 73     //Reset counter4 `: L8 C% Q+ ^7 E$ i  T
  74. 74     TIM3->CNT = 0;
    . [# `" @8 o$ J- t# O3 M, P4 J3 B6 o
  75. 75     //使能定时器3 q! v9 P6 U. o+ H: z6 m
  76. 76     TIM_Cmd(TIM3, ENABLE);   " e" K( q1 m  y  j
  77. 77 }
      h( h4 [8 |  B' j
  78. 78
    0 U( u& }" _; j/ F
  79. 79 void TIM4_Encoder_Init()
    ! f/ D" Q' r# g2 f( e1 H
  80. 80 {
    ( e. b6 v7 {: N
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    6 n# O/ S1 n, _: M
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;  G# c6 I; I% e5 D% e! C+ E
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;* d. n+ a% _8 r7 p
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;+ q1 L# V7 y0 k/ S" c1 B9 X2 e
  85. 85     % q6 N$ }, q, y2 m& z4 Y9 V
  86. 86     //时钟使能* l5 a; _+ l7 \( b% R
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟% ~# V* O7 o/ F+ M# m
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟
    8 f( w' _8 V, |, a; v" u8 {  K
  89. 89     
    * `9 R; W8 i; H0 n! t& c/ ?! n4 F
  90. 90     //GPIO配置
    - L6 ^8 O4 Z# [; u( F. C
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置
    / |% x2 n, ]3 W; Y* i, w+ N7 t5 U
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入9 @$ ^/ Y. k- [: U+ q
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  ?- g  o% g% a& P9 F8 U
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA
    & t2 g$ n' q  f4 N: q  B
  95. 95     " |+ s( p- W( w* f: e& V; k
  96. 96     //定时器初始化- M! c+ H# Q/ b, v" d
  97. 97     TIM_DeInit(TIM4);
    2 |0 t  s. ~$ J# u( p. q. W
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);: q3 L0 r. D* s. E2 s# [8 q' y+ f$ m
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值
    / ~5 p+ b: J5 W7 G' m& ^
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频5 b+ q3 f( V* w5 Q( j3 J" P
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;. O( u+ k3 [+ a2 C$ G
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   {) L7 j. P: d$ y# H/ n
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); . B* M5 S: z1 e$ E' U, |  P
  104. 104     - |' B0 @% g, v) Z% G/ J
  105. 105     //编码器模式配置! W: V2 u% W5 O! I
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    8 L. X: m, g; C3 J& {  B8 }6 L
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);3 w' G5 {6 r. J9 a$ x1 \- Z9 q
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    9 U$ k6 X% P! d8 D
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);3 E& N' c  ~; b, I% M9 f
  110. 110     1 j% r+ H7 r% e" l7 i
  111. 111     //初始化 NVIC 中断优先级分组
    1 ^' a# J9 U* _( D8 @! s
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断8 p2 V' v) a* a- |7 I. j# Q
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级9 r, c, a( {8 X" j
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级7 D& Y& N) k8 k1 l$ `# {
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能/ [5 b* q8 A  O1 K% }6 ]
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    : t0 v/ o. `! z. w9 n! U9 S
  117. 117     9 \0 \  E" `5 s' N5 O& S
  118. 118     //中断配置
    , q6 q" u; q4 R4 s
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);) S  t7 J8 D0 J9 j4 ~. d
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);8 ^4 d- v9 @7 z$ }: F) U0 g% b- t
  121. 121     
    5 R' p4 i* P, x( |+ ?
  122. 122     //Reset counter
    - u3 |6 X) h& |7 e2 t0 W
  123. 123     TIM4->CNT = 0;3 P( P9 g7 j" y9 U4 d( z
  124. 124     //使能定时器
    $ ^5 ]- v% M- u$ [! n; ~
  125. 125     TIM_Cmd(TIM4, ENABLE);   
    2 X9 Y% n7 t6 a( @; y: q* E. _
  126. 126 }  n! g3 }8 t4 ]9 h: p

  127. ( d: p6 r3 ^* t* T0 C0 D
  128. encoder.c
复制代码
! F7 W0 p0 G8 x0 p
1 Q$ R  f( V0 X6 _* \: Z
3、注意事项
  • 更新中断可以不开。但是由于没有使用Z相信号,所以这里保留更新中断,实际上目前中断里什么也没做
  • 由于是双相下降沿计数,所以转一次会输出两个脉冲,转一周脉冲数为分辨率*2,注意处理。当然你也可以配置为其它计数倍数
  • 这里预装载65535,可改为线数*2,即可对转一周的位置进行严格编码
  • TIMx->CNT的值即为编码器的位置(并不一定是输出脉冲值,跟旋转方向和预装载值有关,增量编码器的零点是初始化自己定义的)
  • GPIO配置为浮空输入模式。对于集电极开路输出的编码器,如果你没有外部上拉,则使用内部上拉,配置为上拉输入模式即可。
    / \. {" G* \! U1 D. r
      Z6 M/ p0 Q- {5 R3 v4 z
$ ]' P# B4 N( W. [, Q/ G2 t
- S+ D/ Y0 S3 r3 _
收藏 评论0 发布时间:2022-1-16 17:55

举报

0个回答

所属标签

相似分享

官网相关资源

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