zoukankan      html  css  js  c++  java
  • 增量式编码器专题

    编码器简介

    编码器(encoder)把角位移或直线位移转换成电信号,前者称为码盘,后者称为码尺。

    按照工作原理编码器可分为增量式和绝对式两类:

    ①增量式编码器: 将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。通常为A相、B相、Z相输出,A相、B相为相互延迟1/4周期的脉冲输出,根据延迟关系可以区别正反转,而且通过取A相、B相的上升和下降沿可以进行2或4倍频;Z相为单圈脉冲,即每圈发出一个脉冲。

    ②绝对式编码器: 每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。

    这里仅介绍旋转式增量编码器的使用。


    两种增量式编码器

    使用欧姆龙E6B2-CWZ6C 1000P/R编码器(输出A、B、Z三相)和ASLONG JGA25-371直流减速电机(带334线编码器),为使原理清晰,选用最常见的51单片机(IAP15F2K61S2)测试。

    欧姆龙E6B2-CWZ6C 1000P/R编码器外形如下: 
    l01

    参数表如下: 
    l02

    ASLONG JGA25-371直流减速电机外形如下(左边的两根黄线是电机引线,绿色和白色线是两组脉冲输出线,下面的未经减速的直流电机每转1周输出334个脉冲,用一根线就能测量转速,双脉冲可以判断旋转方向,红色的线接3V至5V电源给测速芯片供电,黑色线接地): 
    l03

    参数表为: 
    l04

    以该电机为例看一下增量式编码器的特性:

    接线如下(红黑鳄鱼夹为0~15V可调电源,为电机供电;排阵为5V电源为编码器测速元件供电;示波器探头连接到编码器的A、B输出相上) 
    l05

    电压加到11V,正转(左图)和反转(右图)波形如下: 
    l06

    可以看出,输出脉冲很稳定,正转时A相超前于B相1/4个周期,反转时B相超前于A相1/4周期。脉冲频率约为21.3kHz,又知道电机的转速比为21.3,减速前转一周输出334个脉冲,则电机的输出转速为: 
    l07


    编码器测位置、测转速、测角加速度原理

    测位置

    编码器每周的脉冲数是一定的,通过记录初始位置和转过的脉冲数可以计算转过的角度。当然,为了减小单片机的运算量,没必要换算成角度。如1000线的编码器输出500个脉冲,那么我们知道转了半圈,而没必要先换算成180度,再除以360度等于半圈。

    拥有固定起始位置的装置,可以用增量式编码器测角度。如倒立摆,其起始位置为自然下垂。然而,像舵机或者机器人的关节的角度测量有时并没有绝对位置,用增量式编码器就显得局限了(除非可以解决初始位置的问题),这种情况下用电位器或绝对式编码器是不错的选择。

    测转速

    增量式编码器有测频率(M法)和测周期(T法)以及两种结合的M/T法(高速时M法,低速时T法)。

    M法测速:记取一个采样周期Tc内旋转编码器发出的脉冲个数M来算出转速n。适用于转速较高、脉冲输出比较快的情况。原理用数学上说就是:位置的微分等于速度,角度的微分得到转速。计算公式如下(这里没有考虑减速比): 
    l08 
    式中:

    • n—转速,r/min;
    • Tc—采样周期,s;
    • M1—时间Tc内的脉冲个数;
    • Z—旋转编码器每转输出的脉冲个数。

    T法测速:测出编码器两个输出脉冲之间的时间间隔来计算出转速n。 
    由于Tc和Z为常数,所以n与M1成正比,故称M法测速。适用于转速较低、脉冲输出较慢的情况。计算公式如下: 
    l09 
    式中:

    • M2—编码器两个脉冲之间的时钟脉冲个数;
    • f0—时钟脉冲频率,Hz。

    测角加速度

    转速的微分为角加速度: 
    l10 
    式中

    • aω—角加速度;
    • nk—本次测量的转速;
    • nk-1—上一次的转速;
    • Tc—采样周期。

    单片机编程实现原理

    单片机对外唯一需要做的是捕获脉冲。

    脉冲捕获的方法

    ①定时器的计数器模式,来一个脉冲计一个数(传统的51单片机都有Timer0、Timer1,STC89C52和IAP15F2K61S2均有Timer2,STC12系列没有Timer2。STM32的定时器有一大堆,当然只当计数器浪费了,因为其可以配置为正交编码模式)。

    ②外部中断(一般都有INT0、INT1,有的有INT2、INT3等),如STC89C52有INT0、INT1,支持下降沿触发和低电平触发。IAPF2K61S2有INT0~4共5路外部中断,其中INT0和INT1支持上升沿或下降沿均可触发方式和仅下降沿触发方式、INT2、INT3和INT4仅支持下降沿触发模式。

    ③PCA (可编程计数器阵列Programmable Counter Array)脉冲捕获(STC12C5A有2路、STC12C56有4路、STC15F2K有3路。STM32的普通定时器即可实现脉冲捕获)。

    使用IAP15F2K61S2测10个编码器的速度

    使用一个定时器做时基定时器测速,剩余的2个定时器计数器+5路外部中断+3路PCA=10路编码器测速,再占用10个IO口可以判断编码器旋转方向。其中INT0、INT1、3路PCA是可以编码器2倍频的,下面会有解释。

    采样周期Tc

    因为编码器运算一次也是定时器中断一次,所以,采样周期Tc其实就是时基定时器的中断时间,或者中断时间的倍数。

    关于倍频

    1倍频:仅对编码器一相输出进行上升沿或下降沿捕获。则1000线编码器转1圈仅捕获1000个脉冲。

    2倍频:对编码器一相上升沿和下降沿均捕获,或者对两相的编码器进行一个边沿捕获(这个很少用),则1000线编码器转1圈可以捕获2000个脉冲。一般有的外部中断中和PCA支持上升沿、下降沿同时捕获。

    4倍频:对编码器A、B相的上升沿和下降沿均捕获。这样1000线的编码器转1圈可以捕获到4000个脉冲,精度大大提高。(对于STC12或者IAP15F2K61S2,使用两路PCA进行脉冲捕获,配置为上升下降均中断。对于STM32可以直接把定时器配置为正交编码接口,上下沿均采样,一个定时器搞定一个编码器)。

    由以上分析可知:

    对于拥有3个定时器、2个外部中断(贴片的有4个外部中断)的直插式STC89C52来说,仅测位置的话,可以1倍频测量多达5个编码器的位置。

    对于拥有6个定时器(含3路PCA)、5个外部中断的IAP15F2K61S2来说,仅测位置的话,可以1倍频测量多达11个编码器的位置。其中,3路PCA、2个外部中断支持上升沿和下降沿均捕获,可以配置成2倍频或4倍频测量。

    如果需要测量速度,则需要牺牲一个定时器作为时基,定时(转速公式中的采样周期Tc)中断来测速,如若中断中10ms查询一次,则Tc=0.01s。即每10ms计算一次速度。 
    当然,如果需要Z相的话,可能需要再牺牲一路。


    需要考虑的问题

    为什么要考虑中断优先级

    STC15W4K60S4系列单片机的中断优先级如下: 
    l11

    为什么要考虑中断优先级?假设使用中断优先级为7的PCA进行脉冲捕获,频率为25kHz,即40μs进一次PCA中断;此时,若又有一个优先级为1的定时器0中断,且其内部程序执行时间大于40μs,则会出现丢脉冲现象。所以,一般所有中断中的程序加起来的时间不宜超过最大的编码器脉冲输出频率的倒数。

    当然,对于进去就关总中断的流氓函数来说,其执行时间最好不要超过所有脉冲捕获的间隔。

    倍频是否一定好

    倍频可以获得更高的位置精度,如1000线的编码器1倍频的分辨率为360/1000=0.36度,2倍频的分辨率为360/2000=0.18度,4倍频的分辨率为360/4000=0.09度。这是很诱人的。

    但上节中已经介绍,所有中断中的程序加起来的时间不宜超过最大的编码器脉冲输出频率的倒数。2倍频则意味着这个时间缩短了一倍,频繁的中断会极大的影响CPU做其它工作。所以精度满足的情况下,没必要追求更高的倍频。

    51单片机的using

    对于51单片机(STM32请绕过),如我们写定时器1中断函数时,常写:void tm1_isr() interrupt 3 using 1。其中3是Timer1的中断优先级,那么using后的1呢?

    using是C51中的关键字,keil的help中给出了using的解释:

    In all members of the 8051 family, the first 32 bytes of DATA memory (0x00-0x1F) is grouped into 4 banks of 8 registers each. Programs access these registers as R0-R7. The register bank is selected by two bits of the program status word, PSW. 
    Register banks are useful when processing interrupts or when using a real-time operating system because the MCU can switch to a different register bank for a task or interrupt rather than saving all 8 registers on the stack. The MCU can then restore switch back to the original register bank before returning.

    所以,我们可以在中断函数分配寄存器组(using 0~3,主函数默认使用0,则其他中断可以使用寄存器组1~3)来省去压栈的时间,加快中断的切换速度。当然,using可以修饰任何函数,不过建议只用来修饰中断函数。同级中断设成同样的寄存器组。中断中调用的函数最好不要被中断意外的其他函数调用,否则会出现“重复调用”的警告。尽管可以用reentrant来修饰,但会占用大量堆栈空间。当然,也可以分成两个不同名的函数写。有时使用using会带来“莫名其妙”的错误,在中断中被调用的函数最好使用using指定与中断函数相同的寄存器组。因此,对using关键字的使用,如果没把握,宁可不用,交给编译系统自己去处理好了。


    编码器测速编程举例

    使用定时器的计数器模式测速度

    演示程序给出了定时器0作基准定时器,计数器1捕获脉冲的测速方法, 先设置定时器1的计数器模式(timer.c,注意不要使能定时器2的中断):

    void Timer_Init(unsigned int T_N100us)  //百微秒
    {
        unsigned int T_100us;
    
    //  AUXR |= 0x80;                       //定时器0为1T模式  //33.1776MHz下最多1.97ms
    //  T_100us = 65536-FOSC/10000*T_N100us;    //1T模式
        AUXR &= 0x7f;                       //定时器0为12T模式 //33.1776MHz下最多23.7ms
        T_100us = 65536-FOSC/12/10000*T_N100us;     //12T模式
    //    TMOD = 0x00;                      //设置定时器为模式0(16位自动重装载)
        TMOD= 0x40;                         //设置定时器为模式0(16位自动重装载),定时器1为计数器模式
        TL0 = T_100us;                      //初始化计时值
        TH0 = T_100us >> 8;
        TL1 = 0x01;
        TH1 = 0x01; 
        TR0 = 1;                            //定时器0开始计时
        TR1 = 1;                            //定时器1开始计数
        ET0 = 1;                            //使能定时器0中断
        EA = 1;     
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    然后在定时器0中断(interrupt.c)中判断速度(程序中得出1s输出的脉冲数Freq):

    //使用Aslong的JGA25-371直流减速电机:334线编码器,减速比为 21.3,12V额定电压,额定转速201rpm
        //那么额定转速下10ms输出脉冲数:201*21.3*334/60/100=238.3257个脉冲
        unsigned char ch,cl;
        static unsigned int temp=0;
        static unsigned int temp_1=0;   //上次的值
        cl=TL1; //先读低位(高位变得没那么快)
        ch=TH1;
        temp_1=temp;
        temp=ch*256+cl; //用左移怎么实现? ch<<8+cl
        //if(temp>=temp_1) Freq=(temp-temp_1)/5;            // *200/1000 kHz              //20kHz 每5ms 计100个数
        //else Freq=(65536-temp_1 + temp)/5;
        if(temp>=temp_1) Freq=(temp-temp_1)*100;            //1s的脉冲数,即频率
        else Freq=(65536-temp_1 + temp)*100;
  • 相关阅读:
    4个小时实现一个HTML5音乐播放器
    一款好看+极简到不行的HTML5音乐播放器-skPlayer
    操纵浏览器的历史记录
    基于jQuery查找dom的多种方式性能问题
    你真的了解console吗?
    关于overflow:hidden和bfc
    jQuery插件开发
    深入浅出jsonp
    jQuery.extend 函数详解
    [转] Hibernate一级缓存、二级缓存
  • 原文地址:https://www.cnblogs.com/mrchige/p/6239135.html
Copyright © 2011-2022 走看看