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

【经验分享】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米。
  • 线数:旋转一周信号线输出脉冲数,也成为分辨率
    5 F6 ~0 s/ [. H$ I- \* Z

  • 6 y% Q! ]) m. S- z
    7 `# i. V0 s3 O
1248164-20180510192503629-93708335.png
% @( ]5 s: r) X; A! _5 H% a6 {
1248164-20180510195028614-102457577.png
2、型号
此篇文章使用的旋转编码器型号有两种。
传感器(增量式旋转编码器)
! R& p8 S! h+ }* v
型号
供电电压
输出方式
分辨率
品牌/厂家
输出电平
S20-1000型拉线位移传感器
5-24V
NPN集电极开路
400
Omron/欧姆龙

' s2 s6 z( t# m
DT100E40R1024-HI
增量型大孔径编码器
5-30V
HTL(推挽式)
1024
冬莅自动化技术(上海)有限公司
www.shdongli.cn
高:VCC*70%
低:1V

3 S7 ~+ O+ F& m. v' T

- S( s7 n+ V: a0 k* u二、硬件设计
  由于增量型编码器输出脉冲,可以直接接入PLC单片机,利用PLC高速计数器进行测量。将编码器接入STM32单片机需要一个定时器资源,并将定时器配置为编码器模式,稍后会简要介绍配置方法。根据传感器的输出方式,进而来判断是否需要进行信号预处理。对于集电极开路输出的传感器接入必须使用上拉电阻。注意STM32大多数引脚的输入电压都在3.3V左右,尽量不要过压。如果电压较大,最好加隔离电路,比如6N136光耦,类似下图
+ K2 `. ^" v* l- @1 [3 q
1248164-20180510200405732-66175176.png
8 Z: j$ H8 j; I" y0 f8 O1 A: f
  我在这里并没有使用隔离电路,而是对传感器统一使用了5V供电,并在信号接入端人为加了3.3V上拉(上拉电阻1K),这种接口电路在我自己设计的采集板上统一留出了8个。编码器应用中并未使用Z相,仅使用了A、B两相进行编码器计数。按照线序说明接入STM32的定时器引脚,PA6、PA7对应TIM3的CH1、CH2通道,PB6、PB7对应TIM4的CH1、CH2通道。对于第二种传感器的推挽输出方式而言,实验证明这种接入电路也是可行的。(具体电路没有进行分析,只是图个方便。但是5*70%=3.5V应该是勉强可以直接接入stm32引脚的)
. m9 T2 \1 h  C5 W8 S
1248164-20180510202458411-1027273337.png
( O2 d, }2 {/ `" _4 @$ r
三、软件设计1、STM32定时器编码器模式简介
  参考官方文档,摘录几个要点:
  • 编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。
  • 两个输入TI1和TI2被用来最为增量编码器的接口,TI1FP1和TI2FP2是TI1和TI2在通过滤波器和极性控制后的信号,计数器由每次在TI1FP1和TI2FP2的有效跳变驱动,根据跳变顺序,产生计数脉冲和方向信号,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应设计
  • IC1、TI1概念理解:TI1是定时器输入通道,IC1是输入捕获通道(两者可以交错)。配置CCMR1寄存器的CC1S位可以指定IC1的映射方式。可配置滤波器和预分频器。
    / ^, k8 V0 h& V3 X# n. r
    8 [! r( p, g" E

! B" w) C% L. j7 V
1248164-20180510211213519-2095565993.png

" {; d  H- y% U, ~8 m% @
1248164-20180510210619647-836174267.png
1 P- u  v2 w8 z& k5 X! d! W( p/ Z
  • 计数方向与编码器信号的关系7 R& m# d1 s1 u- e# V# {9 _
    # s- N+ [0 u4 @- j. P( _2 \& {
% W' z+ Z6 c/ e: w1 X
5 u" w% z; p4 L) [$ h9 J
1248164-20180510211455433-476773874.png 0 S4 v8 m9 [9 c0 B2 m
$ s" Y; X8 F% S1 Y! W9 ?, C

8 S, A; ]3 h% T$ Z  m
2、软件实现
  1. 1 /********************************************
    / {4 G. y# G2 T2 i, E) q
  2.   2
    * Q: a. W4 _# r
  3.   3 TIM3、TIM4定时器编码器模式配置: e- \6 g; n: t3 w% @
  4.   4
    ) a5 M! S  p* L9 t* s/ p$ u
  5.   5 *********************************************/3 [8 y' l: ?8 z/ R
  6.   6 ; |8 t& ~3 S* T) S% n9 y0 E
  7.   7 #include "sys.h"
    & J) q* [* M+ N
  8.   8 #include "encoder.h"
    : ]1 |0 J; ?4 {+ Z0 I) G
  9.   9
    . t$ P& M# S6 w% t
  10. 10 int TIM3ITCount=0;//中断溢出次数
    - u6 n. @) J; O" [8 v
  11. 11 int TIM4ITCount=0;//中断溢出次数- _" G: p/ E9 b4 \: x
  12. 12 ; F  P) j# o% N9 o7 k- e
  13. 13 void TIM3_IRQHandler(void)0 C/ |% x! c: {" C/ J
  14. 14 {   
    5 K) \3 J) \' Q8 j, Y
  15. 15     if(TIM3->SR&0x0001)//溢出中断3 A( B! m  D3 N- s) U( x& g3 w5 W
  16. 16     {
    1 i. }% b) H" W* n# d3 `7 _
  17. 17         % p: f+ N3 W% [6 v/ _* m. t
  18. 18     }   
    - r0 X7 L$ R  X/ D" R5 A4 _
  19. 19     TIM3->SR&=~(1<<0);//清除标志位    # [% x; C5 I- q* z7 f
  20. 20 }
    9 L9 n+ V. ^* J$ p
  21. 21 - ~' X. y& ]0 v% U; j
  22. 22 void TIM4_IRQHandler(void)  w2 U+ W# m' @; k
  23. 23 {   
    5 ^" a) _! D; a7 \% Z" q
  24. 24     if(TIM4->SR&0x0001)//溢出中断0 Y5 U! ^" y7 G
  25. 25     {
    ' ~* P9 u/ E- |4 a' t, W" |
  26. 26     }   
    ( X+ E9 v6 [! q5 }* q- c- w' X( s
  27. 27     TIM4->SR&=~(1<<0);//清除标志位   
    - J; Y5 {( o( |
  28. 28 }
    + w# }. F  D& U8 K' p
  29. 29 5 C; |  U- }" r1 L) z
  30. 30 void TIM3_Encoder_Init()! R, j( C' W( ]( W
  31. 31 {
    ) m) o1 e& F8 K1 r* q) A% e
  32. 32     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    ' M5 j2 s/ v# c
  33. 33     TIM_ICInitTypeDef TIM_ICInitStructure;; y4 C' I' D; v" _  P3 U% x3 f
  34. 34     GPIO_InitTypeDef GPIO_InitStructure;- U. ]3 Z6 H) P0 J8 i* ]! T
  35. 35     NVIC_InitTypeDef NVIC_InitStructure;
    7 F, O( b1 Z" e3 A( w9 n) ?3 H
  36. 36     
    " y1 X* e8 ^" g1 y7 b
  37. 37     //时钟使能3 n! n" r, z- d% _+ b
  38. 38     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能 TIM3 时钟
    1 _& _: E8 T+ f9 J( X
  39. 39     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟
    0 p' n$ p) j: V% S* d6 A
  40. 40     8 e9 G  W& l% Y& Z" b1 s3 R6 p
  41. 41     //GPIO配置7 y! A/ d: U7 F! k, ]
  42. 42     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7设置! t: t# u; h0 b* K1 S7 g, H; ^
  43. 43     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入8 u/ d# \! i3 l
  44. 44     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    4 m) l4 |) v$ ]' m8 M
  45. 45     GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA
    * T  H( M/ ]5 Q! z- D& b
  46. 46     
    + w4 `, B) |9 l; z
  47. 47     //定时器初始化
    8 t* v& g% e# M" R
  48. 48     TIM_DeInit(TIM3);% A  w; f% E4 o5 y- @
  49. 49     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    ' v# A  j% z, Q5 _+ M6 o4 J& X+ U+ o
  50. 50     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值0 F( p9 b& L8 g% A7 `" @
  51. 51     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频$ ]; w4 d# j7 C) o# g
  52. 52     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;7 C! @: @, D( m7 I; n6 ?" K% d
  53. 53     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; % }4 R$ h& B: G7 y& e
  54. 54     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); / g* R  Y1 p+ E) G9 b5 M
  55. 55     ; X5 p/ {  Y; r" T- J
  56. 56     //编码器模式配置8 e8 m2 i6 U0 }* a! H
  57. 57     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);: L  e0 l3 y/ H% c/ Q, `/ `+ _% |
  58. 58     TIM_ICStructInit(&TIM_ICInitStructure);( R; A( `4 R6 D: t) S; U) X
  59. 59     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    % z1 R# ?9 |/ _7 y8 j
  60. 60     TIM_ICInit(TIM3, &TIM_ICInitStructure);4 l2 N1 e) w/ C; F2 n
  61. 61     
    , x  I/ m4 F: p. W8 i( m3 a
  62. 62     //初始化 NVIC 中断优先级分组$ d  o5 p* E0 H: Y$ X# _
  63. 63     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断  R6 x$ N  F$ @: a
  64. 64     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级- ~" a) [& a  v, a% o
  65. 65     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
    ' Y1 ?, `8 ~$ U3 o- j3 I
  66. 66     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
    * S2 B' A" x! v; w' z! b$ g
  67. 67     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    2 w" y/ U% ^. i/ n* E/ ]
  68. 68       W, m: r2 f6 l' f1 u
  69. 69     //中断配置
    3 e7 n5 @) R% w( r2 e& u" J
  70. 70     TIM_ClearFlag(TIM3, TIM_FLAG_Update);& b3 p2 \' W; a- c3 u0 w/ H
  71. 71     TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);0 b! `  P7 B3 F+ t. {. N
  72. 72     ( v$ x  V. e, `! J  j6 U
  73. 73     //Reset counter
    % c3 S, V' F* G% g2 _4 Z% v0 U
  74. 74     TIM3->CNT = 0;2 s) X  J. d2 T, U7 g4 ~  ^
  75. 75     //使能定时器: L2 M4 u$ d2 Z9 Z/ w0 ?) O$ l
  76. 76     TIM_Cmd(TIM3, ENABLE);   
    * J; B  S0 n* K4 k6 }* h
  77. 77 }$ U2 d: H( e9 q6 ?9 n' A3 a% l  l
  78. 78
    - p1 T4 ~6 Q: e5 v. ^4 A
  79. 79 void TIM4_Encoder_Init()
    ! }" `6 z# i6 Q$ E! P5 M+ c& L4 N
  80. 80 {
    " f5 D9 X% q& n  A" Q
  81. 81     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;8 Z: H3 u1 _/ I3 }
  82. 82     TIM_ICInitTypeDef TIM_ICInitStructure;' }; R" |% W; N& {5 ~) q$ z3 R
  83. 83     GPIO_InitTypeDef GPIO_InitStructure;
    , V& J6 Z+ D! `: l* m# V6 _' _8 X
  84. 84     NVIC_InitTypeDef NVIC_InitStructure;7 x/ o8 P! r% G* Z2 t# O
  85. 85     % o. C+ n& Z, F; s
  86. 86     //时钟使能7 v4 P1 }" R' ?. F2 A7 {0 _
  87. 87     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //使能 TIM4 时钟) E; p! U$ J, ~* I5 C# v
  88. 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 时钟
    ' @( R# H" V) h0 F" U
  89. 89     . g8 c9 O* g. j3 J, B6 O; Z
  90. 90     //GPIO配置
    & z; |( o" \6 L7 d7 {/ o; C
  91. 91     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7设置% R2 p& P0 t: t, y# ~
  92. 92     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入% s3 C" ~1 M, {% b% k
  93. 93     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;( U0 o% p1 M1 }0 G
  94. 94     GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA8 f( x( Z7 ?$ m, `! A" r
  95. 95     
    6 q  n  Z0 o$ ~) b; j
  96. 96     //定时器初始化8 d! V8 o; K6 m' H+ z! N
  97. 97     TIM_DeInit(TIM4);. y2 V) j+ K2 C  s
  98. 98     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);; q$ d; f$ C8 B) i2 W& W" x
  99. 99     TIM_TimeBaseStructure.TIM_Period =65535;//预装载值
    9 |7 i: I. U8 ?% j5 p
  100. 100     TIM_TimeBaseStructure.TIM_Prescaler = 0;//预分频
    9 X1 o$ y2 c0 A' B. `
  101. 101     TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;4 A' S8 X! ?; A( z1 n
  102. 102     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    3 W, i; w3 n" C) H8 A5 |( ~
  103. 103     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
    . z3 R- u9 n0 z
  104. 104     8 F  e  G# o2 p3 U( }
  105. 105     //编码器模式配置
    - M, F0 N. \4 V% T; S& Y8 }; i  x
  106. 106     TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
    0 R0 m: t" l2 v. @
  107. 107     TIM_ICStructInit(&TIM_ICInitStructure);
    ! m, S. K3 I" t$ f0 @* X
  108. 108     TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
    4 f8 E% i5 L6 `1 w. Y& S
  109. 109     TIM_ICInit(TIM4, &TIM_ICInitStructure);
    + g! [/ G. b9 }1 n' ]* `
  110. 110     
    1 ]1 @6 Q8 b+ x* t  u* D# S
  111. 111     //初始化 NVIC 中断优先级分组, c7 T5 h5 B) w
  112. 112     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中断
    3 J" ]9 @3 G( |  J% }5 D7 ^) B( V
  113. 113     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
    1 a/ ~  P' m* C- C8 {
  114. 114     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 0 级0 J" W4 I* W: i
  115. 115     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能) }5 n: d% Q! @. G2 Q7 t+ b
  116. 116     NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
    8 }2 U# o3 q; p
  117. 117     + t# h. C, t. P3 \1 a( e
  118. 118     //中断配置
    ! B! _) }1 g9 S8 x. K& R
  119. 119     TIM_ClearFlag(TIM4, TIM_FLAG_Update);
    * N5 I; E1 ~0 e
  120. 120     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    ; T( ?5 W  M, B
  121. 121     / S9 s+ G$ j0 M; }4 Z" [2 K
  122. 122     //Reset counter) n- x# `4 T0 A8 A2 R: p
  123. 123     TIM4->CNT = 0;
    + c+ g; C. S9 S9 E  D, W
  124. 124     //使能定时器
    : T& Q) v# F4 R2 J% u
  125. 125     TIM_Cmd(TIM4, ENABLE);   
    # _" z2 a2 n5 W6 q( B
  126. 126 }
    4 E/ N0 e' ?4 u$ S1 ?+ `; ?

  127. 7 N7 j' z9 N+ C  v
  128. encoder.c
复制代码

$ J- ]  `5 A/ H

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

2 P0 _/ N! x& {( J- l% `# o4 X" F) ^# y  T
赞 收藏 评论0 发布时间:2022-1-16 17:55

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版