TIM编码器接口
看一下最终程序的现象啊。本小节只有一个程序,就是编码器接口测速。我们试一下,下载,看一下面包板。在这里我接了一个旋转编码器模块啊,这个代码和之前我们写的旋转编码器计次的代码,实现的功能基本都是一样的。
目前我们这个代码本质上也是旋转编码器计次,只不过这个代码是通过定时器的编码器接口来自动计次,而我们之前的代码是通过触发外部中断,然后在中断函数里手动进行计次。使用编码器接口的好处就是节约软件资源。如果使用外部中断来计次,那当电机高速旋转时,编码器每秒产生成千上万个脉冲,程序就得频繁进中断啊。然后进中断之后,完成的任务又只是简单的加一减一,是不是我们的软件资源就被这种简单而又低级的工作给占用了?所以对于这种需要频繁执行,操作又比较简单的任一般我们都会设计一个硬件电路模块来自动完成。那我们本节这个编码器接口就是用来自动给编码器进行计数的电路。如果我们每隔一段时间取一下计数值,就能得到编码器旋转的速度了。
那我们看一下程序,目前 OLED 显示的是 speed 速度,我们旋转试一下,向右慢速旋转,数值为正,计数比较小啊。向右快速旋转,计次就会增大。然后向左慢速旋转,数值为负,计次比较小。向左快速旋转,计次也是负向增大。然后停下来,速度就是 0。这就是编码器测速的实验现象。
使用定时器的编码器接口啊,再配合编码器,就可以测量旋转速度和旋转方向了。这里编码器测速一般应用在电机控制的项目上,使用 PWM 驱动电机,再使用编码器测量电机的速度,然后再用 PID 算法进行闭环控制。这是一个比较常见的使用场景啊。一般电机旋转速度比较高,会使用无接触式的霍尔传感器或者光栅进行测速啊。我们这里为了方便,就使用这个触点式的旋钮编码器来演示啊。电机旋转呢,就用人工旋转来模拟。当然实际使用的话,这个旋钮编码器和电机的霍尔光栅编码器都是一样的效果哈。

先看一下编码器接口的简介啊。首先编码器接口它的英文是 Encoder Interface, 直译过来就是编码器接口的意思啊。这个英文认识一下就行了。然后下一条就是编码器接口的工作流程概述了。
编码器接口可以接收增量编码器的信号,或者叫做正交编码器,都是一个意思啊。什么是正交编码器呢?等会我们会介绍啊。就是像这样,输出的两个方波信号相位相差 90 度,超前 90 度或者滞后 90 度,分别代表正转和反转,这就是正交编码器。

然后继续编码器接口可以根据编码器旋转产生的正交信号脉冲自动控制 CNT 自增或自减,从而指示编码器的位置、旋转方向和旋转速度。这就是编码器接口的工作流程,就是接收正交信号,自动执行 CNT 的自增或自减
最终的实验现象就是,这是一个编码器,它有两个输出,一个是 A 相,一个是 B 相。然后接入到 STM32 定时器的编码器接口。编码器接口自动控制定时器实际单元中的 CNT 计数器进行自增或自减。比如初始化之后, CNT 初始值为 0,然后编码器右转, CNT 就加加,右转产生一个脉冲, CNT 就加一次。比如右转产生 10 个脉冲后停下来,那么这个过程 CNT 就由 0 自增到 10 停下来。编码器左转 CNT 就减减,左转产生一个脉冲 CNT 减一次。比如我编码器在左转产生 5 个脉冲,那 CNT 就在原来 10 的基础上自减 5 停下来。
这个编码器接口啊,其实就是相当于一个带有方向控制的外部时钟。它同时控制着 CNT 的计数时钟和计数方向。这样的话, CNT 的值就表示了编码器的位置。如果我们每隔一段时间取一次 CNT 的值,再把 CNT 清零,是不是每次取出来的值就表示了编码器的速度啊?借用下上一小节测频法和测周法的知识点,这个编码器测速实际上就是测频法测正交脉冲的频率啊。 CNT 计次,然后每隔一段时间取一次计次,这就是测频法的思路,对吧?只不过这个编码器接口计时更高级,它能根据旋转方向,不仅能自增计次,还能自减计次,是一个带方向的测速啊。好,以上就是编码器接口的工作流程了

下一条,每个高级定时器和通用定时器都拥有一个编码器接口,这个编码器接口的资源还是比较紧张的啊。如果一个定时器配置成了编码器接口模式,那它基本上就干不了其他活了。我们这个 C8T6 芯片只有 TIM1234 四个定时器,所以最多只能接四个编码器,而且接完 4 个编码器就没有定时器可以用了啊。所以如果你编码器比较多的话,需要考虑一下这个资源够不够用。
不过实在不行的话,你还是可以用外部中断来接编码器的,这样就是用软件资源来弥补硬件资源了啊。所以这里也可以看出,硬件资源和软件资源是互补的,硬件资源越多,软件就会越轻松啊。硬件不够呢,那就软件来凑,对吧?比如 PWM, 我可以直接来个定时中断,然后在中断里手动计数,手动翻转电平。比如输入捕获,我可以来个外部中断,然后在中断里手动把 CNT 取出来放在变量里。比如编码器接口,我也可以来外部中断,然后在中断里手动自增或自减计数,这都可以实现功能。什么输出比较啊,输入捕获啊,编码器接口啊,都不需要。
但是这样就是消耗软件资源了。所以一般有硬件资源的情况下,我们可以优先使用硬件资源,这样节约下来的软件资源可以去干更重要的事情。
那继续往下看,编码器接口的两个输入引脚,借用了输入捕获的通道一和通道二。这个等会从结构框图就可以看出来啊。编码器的两个输入引脚就是每个定时器的 CH1 CH2 引脚, CH3 和 CH4 不能接编码器啊。

我们接下来来看一下正交编码器。
正交编码器一般可以测量位置或者带有方向的速度值啊。它一般有两个信号输出引脚,一个是 A 相,一个是 B 相。编码器的样子和结构呢,我们之前外部中断这里也介绍过啊。我们大概回顾一下,这里看一下图片。

第一个是只有一个光栅加红外对管的编码器,这只能输出一个方波信号,并不是正交编码器啊。第二个图就是我们套件使用的编码器,里面靠两个金属触点交替导通,可以输出 A 相和 B 相两个正交信号。是正交编码器。这里有四个引脚,上面两个是供电的正极和负极,下面两个是 A 相和 B 相的输出。第三个图是电机后面自带的一个编码器,电机旋转带动中间的磁铁旋转,两个霍尔传感器 90 度放置,最终输出 A 相和 B 相两个正交信号,是正交编码器。

下面这地方一般有六根线,最左和最右是直接接到电机的。然后是靠里一些的两根线是编码器电源,最中间的两根就是 A 相和 B 相的输出了。

接着最后一个图是单独的编码器元件,一般都是正交编码器哈,当然也有的不是,这个要看清商品的说明。接线的引脚一般有六个,两个是编码器电源,两个是 A B 相,一般还有个零位置的输出啊,也就是 Z 相。零位置就是编码器每转到一个固定位置时,输出一个脉冲,一般应用于位置测量哈,校正零位置用的。最后还这个引脚一般是空脚没有用到。这就是常见的编码器外观和工作原理。

那回到这里,当编码器的旋转轴转起来时, A 相和 B 相就会输出这样的方波信号。转的越快,这个方波的频率就越高。所以方波的频率就代表了速度。我们取出任意一相的信号来测频率,就能知道旋转速度了。但是只有一相的信号无法测量旋转方向,因为无论正转还是反转,它都是这样的方波。想要测量方向,还必须要有另一根线的辅助。比如我可以不要这个 B 相啊,在另一个方向输出脚,正转是高电平,反转是低电平。这是一种解决方案啊,但是这样的信号并不是正交信号。另一种解决方案就是我们本节所说的正交信号。当正转时, A 项提前 B 项 90 度,反转时, A 项滞后 B 项 90 度。但这个正转啊,是 A 项提前还是 A 项滞后,并不是绝对的,这只是一个极性问题啊。毕竟正转和反转的定义也是相对的。总之就是朝一个方向转时是 A 项提前,另一个方向是 A 项滞后。

那使用正交信号相比较单独定义一个方向引脚有什么好处呢?首先就是正交信号精度更高,因为 A B 项都可以计次,相当于计次频率提高了一倍。其次就是正交信号可以抗噪声,因为正交信号两个信号必须是交替跳变的,所以可以设计一个抗噪声电路。如果一个信号不变,另一个信号连续跳变,也就是产生了噪声啊,那这时计数值是不会变化的。这个等会会再介绍啊。

接着我们来看一下右边这个表,看一下正交信号如何计数和区分旋转方向呢?首先我们观察一下波形的特点,在正转的时候,第一个时刻 A 项上升沿,对应 B 项此时是低电平,也就是表里的第一行。继续第二个时刻, B 向上升沿,对应 A 项高电平,是表里的第三行。继续第三个时刻, A 向下降沿,对应 B 项高电平,是表里的第二行。最后是 B 向下降沿,对应 A 项低电平,是表里的第四行。再然后就是 A 向上升沿, B 项低电平,和第一个状态重复了。所以在正转的时候,我们总结了右边这个表。
当出现这些边缘时,对应另一项的状态是这四种。

那反转呢?我们看一下,第一个时刻, B 项上升沿对应 A 项低电平。第二个时刻 A 项上升沿对应 B 项高电平,第三个时刻 B 项下降沿对应 A 项高电平,第四个时刻 A 项下降沿对应 B 项低电平。然后把这四种状态也列个表,这里就可以发现,当 A B 项出现这些边缘时,对应另一项的状态

正转和反转正好是相反的。比如 A 项上升沿时,正转 B 项就是低电平,反转 B 项就是高电平。剩下的大家可以自己看一下啊,也都是相反的。
所以我们编码器接口的设计逻辑就是,首先把 A 项和 B 项的所有边缘作为计数器技术始终出现边缘信号时就计数自增或自减,然后到底是增还是减呢?这个计数的方向由另一项的状态来确定。当出现某个边缘时,我们判断另一项的高低电平,如果对应另一项的状态出现在上面这个表里,那就是正转,计数自增。反之,另一项的状态出现在下面这个表里,那就是反转,计数自减。这样就能实现编码器接口的功能了。这也是我们 STM32 定时器编码器接口的执行逻辑哈。那接下来我们先来看一下这个定时器的框图,看一下这个编码器接口的电路是如何设计的?


接下来我们先来看一下这个定时器的框图看一下这个编码器接口的电路是如何设计的,这里编码器接口处于定时器的这个位置啊,高级定时器和通用定时器都是一样的,每个定时器都只有一个编码器接口然后基本定时器是没有编码器接口的

我们来看一下这里编码器接口有两个输入端分别要接到编码器的 A 相和 B 相然后这里是两个网络标号分别写的是 TIM1FP1 和 TIM2FP2, 对应的就是这里 TIM1 的 FP1 另一个是这俩 TIM2 的 FP2 电路是这样连接的,可以看出哈这个编码器接口的两个引脚借用了输入捕获单元的前两个通道所以最终编码器的输入引脚啊就是定时器的 CH1 和 CH2 这两个引脚

信号的通路是 CH1 通过这里通向编码器接口 CH2 通过这里通向编码器接口, CH3 和 CH4 与编码器接口无关

其中 CH1 和 CH2 的输入捕获滤波器和边缘检测编码器接口也有使用哈,但是后面的是否交叉,预分频器和 CCR寄存器与编码器接口无关这就是编码器接口的输入部分

那编码器接口的输出部分啊其实就相当于从模式控制器了,去控制 CNT 的计数时钟和计数方向,简单来说这里的输出执行流程是按照我们之前总结的那个表,如果出现了边缘信号并且对应另一相的状态为正转,则控制 CNT 自增,否则控制 CNT 自减
注意在这里啊我们之前一直在使用的 72 兆赫兹内部时钟和我们在时基单元初始化时设置的计数方向并不会使用啊,因为此时计数时钟和计数方向都处于编码器接口托管的状态,计数器的自增和自减受编码器控制好这就是编码器接口的电路结构了

然后我们看一下这里我给出的一个编码器接口基本结构哈这个结构就比较清晰了

输入捕获的前两个通道通过 GPIO 口接入,编码器的 A B 相,然后通过滤波器和边缘检测极性选择产生 TIEFP1 和 TIRFP2 通向编码器接口,编码器接口通过预分频器控制 CNT 计数器的时钟,同时编码器接口还根据编码器的旋转方向控制 CNT 的计数方向,编码器正转时 CNT 自增编码器反转时 CNT 自减

另外这里 AR R也是有效的啊一般我们会设置 AR 为 65535 最大量程,这样的话利用补码的特性很容易得到负数啊,比如 CNT 初始为 0 我正转 CNT 自增 012 三四五六七等等,这都没问题但是我反转呢 CNT 自减 0 下一个数就是 65535 接着是 65534, 65533 等等这里负数不应该是-1-2 吗, 65535 是不是就出问题了但是没关系我们会做一个操作,直接把这个 16 位的无符号数转化为 16 位的有符号数,根据补码的定义这个 65535 就对应-1 啊 65534 就对应-2 ,65533 就对应-3 等等这样就可以直接得到负数非常方便,这是我们读出数据得到负数的一个小技巧啊

那到这里我们这个电路结构就了解差不多了,最后我们来看一些工作细节哈和两个小例子那先看看一下这里工作模式的这个表

这个表描述的就是我们刚才说的编码器接口的工作逻辑啊。这里, T1FP1 和 T2FP2 接的就是编码器的 A、 B 相。在 A 相和 B 相的上升沿或者下降沿触发计数。到底是向上计数还是向下计数呢?取决于边缘信号发生的这个时刻,另一项的电平状态,也就是这里的相对信号的电平。 T1FP1 对应 T2, T2FP2 对应 T1,就是另一项电平的意思啊。然后在这里,这个编码器还分了三种工作模式。分别是仅在 T1 计数,仅在 T2 计数,和 T1 T2 都计数。这三个模式是啥意思呢?

我们回到这里看一下。我们目前总结的是,这四种状态都是正转,都可以计次自增。下面这四种状态都是反转,都可以计次自减。这四种状态涉及了两个引脚,分别是 A 项上升沿, A 项下降沿, B 项上升沿, B 项下降沿。
如果这四种状态都执行自增或自减,就是 A 项和 B 项的边缘都计数,那就对应这里的第三种模式, TR1 和 TR2 都计数。当然这里我们还可以忽略一些边缘啊,比如我们可以仅在 A 项的上升沿和下降沿自增或自减,而 B 项的这两个状态忽略掉,不执行计数。或者仅在 B 项的上升沿和下降沿计数, A 项的边缘不管它。这样是不是也可以实现功能啊?只不过是计数的精度低了一些。看图比划一下就是,如果两个边缘都计数,计数的时刻就是这样。

如果仅在 A 项边缘计数,计数的时刻就是这样, B 项的边缘忽略掉啊。如果仅在 B 项边缘计数也是同理。

那这两种仅在一个边缘计数的模式,就对应这表里的前两种模式,仅在 T1 计数和仅在 T2 计数。
然后这个表右边什么时候自增,什么时候自减,和我们前面这里的表也是一样的。我们把这个表拿过来对比一下。这里假设 T1 接 A 项, T2 接 B 项。当 A B 项为左边这四个状态时,就是正转,计数器需要自增。我们看一下,左表第一个状态, A 项上升沿, B 项低电平,右表查一下, A 项上升沿。这里先看第三种模式啊,那对应另一项低电平,执行的是向上计数。然后左表第二个状态, A 项下降沿, B 项高电平,右表查一下, A 项下降沿, B 项高电平,也是向上计数。左表第三个状态, B 项上升沿。点, A 项高电平,右边查一下。 B 项上升沿 A 项高电平也是向上计数。左表第四个状态, B 项下降沿 A 项低电平,右表查一下, B 项下降沿 A 项低电平,还是向上计数。正好啊,这里正转的状态都是向上计数。

也可以把反转的状态拿过来比较一下,就对应另外的四个向下计数。

所以总结一句话就是,正转的状态都向上计数,反转的状态都向下计数。这就是编码器接口执行的逻辑。
那上面这两种模式应该也好理解啊,就是只在一项的边缘计数另一项的边缘忽略,也就是不计数。计数的逻辑啊,和最下面这个模式都是一样的。

一般情况下我们都会使用最下面这个模式啊,因为这个模式技术精度最高。上面这两个模式,如果你有这个需求的话,可以了解一下。这就是编码器接口的三种工作模式。
那接着我们再来看一下这个实例图,这个图里使用的就是两个引脚的边缘都计数的模式。执行的逻辑啊我放在这上面了。然后看一下,这里是 TIM1 和 TIM2 的时序信号,下面是计数器值的变化情况啊。第一个状态, TIM1 上升沿, TIM2 低电平,查表,上升沿低电平对应向上计数,所以这里计数器变高了一级。接着后面的这几个状态啊,大家可以查一下表,都是向上计数,这是正转,没问题。接着后面这个地方展示的就是正交编码器抗噪声的原理了。

在这里 T2 没有变化,但是 T1 却跳变了好几次,这不符合正交编码器的信号规律。正交信号两个输出交替变化,就像人走路一样,先迈左腿再迈右腿,再左腿再右腿。这里就是右腿没动,左腿连续走了好几步。显然这个左腿的动作是一个毛刺信号啊。而通过我们上面这个表的逻辑,就可以把这种一个腿没动另一个腿连走好几步的噪声滤掉。比如这里, T1 上升沿, T2 低电平,查表得向上计数,这里自增了。然后下一个状态, T1 下降沿, T2 还是低电平,查表就是向下计数,所以这里自减。然后继续, T1 上升沿, T2 低电平。自增,继续, T1 下降沿, T2 低电平,自减。所以你看这里如果出现了一个引脚不变,另一个引脚连续跳变多次的毛刺信号,计数器就会加减加减来回摆动,最终计数值呢还是原来那个数,并不受毛刺噪声的影响。这就是正交编码器抗噪声的原理。

然后继续往右看,这里是反转的波形,对照上表都查一下啊,都是向下计数,这里计数值就下降。然后 T1 不动, T2 多次跳变,计数值也是来回摆动过滤噪声啊。背后是正转向上计数,这就是编码器接口的一个实例哈。

这个图展示了什么时候向上计数,什么时候向下计数,以及正交编码器抗噪声的原理哈。
最后我们再来看一下这个实例,这个实例展示的是极性的变化对计数的影响。前面这个是两个引脚都不反向的图,后面这个是 T1 反向 T2 不反向的图哈。

T1 反向是什么意思呢?我们看一下这个图,这里 T1 和 T2 进来都会经过这个极性选择的部分,在输入补货模式下,这个极性选择是选择上升沿有效。选择下降沿有效的。

但是根据我们刚才的分析啊,编码器接口显然始终都是上升沿和下降沿都有效的哈。上升沿和下降沿都需要计数,对吧?所以在编码器接口模式下,这里就不再是边沿的极性选择了,而是高低电平的极性选择。如果我们选择上升沿的参数,就是信号直通过来,高低电平极性不反转。如果选择下降沿的参数,就是信号通过一个非门进来,高低电平极性反转。所以这里就会有两个控制极性的参数,选择要不要在这里加一个非门哈,反转一下极性。那对应这里如果两个信号都不反转就是这里的均不反向。如果把 T1 高低电平反转一下就是这里的 T1 反向。

分析的时候呢,这里图上画的是输入信号啊,如果你直接对照上面这个表,得到的计数方向就是错误的。比如第一个状态, T1 上升沿, T2 低电平,查表应该是向上计数啊,但是这里实际却是向下计数,所以 T1 反向之后,我们先这样,把 T1 高低电平取反,这才是反向后实际给编码器接口的电平,然后再查表,第一个状态应该是 T1 下降沿, T2 低电平,查表得到是向下计数,和这里是对应的哈。后续状态大家可以自行查表一一对照哈。

那 T1 反向之后,我们对照上一个图,显然这两个图的计数方向是相反的。这有什么用呢?
比如你接一个编码器,发现它数据的加减方向反了,你想要正转的方向,结果它自减了,你想要反转的方向,结果它自增了。这时就可以调整一下极性,把任意一个引脚反向,就能反转计数方向了。但如果想改变计数方向的话,我们还可以直接把 A, B 相两个引脚换一下,这也是可以的哈,有很多地方都可以调整极性,还是非常方便的。