第13课 旋转编码器与外部中断的效率

我是潘,曾经是个工程师。这是为 Ardui.Co 制作的 “Arduino 公开课” 系列的入门教程。上一课介绍了霍尔传感器的测速原理,现在将继续通过外部中断,来读取旋转编码器的信息。同时我们利用两个程序去分析中断的效率。有任何疑问请在评论区提出,我会逐一回答。

编码器外形很像电位器,但它的输出的是两路脉冲信号,通过程序对比来判断顺时针、逆时针以及增减量是多少。人机交互中,编码器可以精确控制输出,每一个脉冲信号代表一个调整信号,而且它的旋转没有圈数限制,使用灵活、不容易磨损。

常见的旋转编码器,每圈有16步到1024步等多种型号,配合旋转编码器上的按键,可以复位到初始状态,即从0开始计数。现在我们使用的是一圈20步机械编码器,其抖动较大,程序中需要去抖处理,但日常使用足够了,而在高精度场合可以使用光栅型的。

旋转编码器模块有5个引脚,分别是VCC, GND, Switch(中轴复位按键)、编码端A、编码端B(有些产品会将A、B端标记为CLK、DT,完全等效)。

这类旋转编码器模块一般在 A、B 两端内置了上拉电阻,而单个元件的则需要自己接上拉电阻。

我们不深究期内部原理,只要知道,机械旋转编码器有类似齿轮缺口与感应的机制(光学型为光栅感应),旋转时,A、B两路脉冲将输出不同的讯号,正转与逆转得到不同的一串讯号:


来源:Arduino 官方网站

程序设计逻辑是:
当 B 信号输出低电平,A 输出高电平,则为顺时针,反之为逆时针;或者:
当 B 信号输出高电平,A 输出高电平,则为逆时针,反之为顺时针。逻辑图如下:

看出规律吗?当A、B信号反转时,顺时针;当A、B信号一样时,逆时针。

接线方式很简单,只要将模块 Vcc、GND、A、B 分别接电源和D2、D3,(如果是单个元件,A、B 分别接 10K 拉电阻),通过下面程序测试一下:

打开串口监视器旋转编码器,可以看到数字按每步±1变化。对于机械编码器,我们加入了 5ms 防抖延时。另外,A、B两个端口是可以互换的,但互换后旋转方向会反转。

这个程序看似很完美,不过,占用了两个宝贵的中断端口,尤其是Uno,就没办法做其他中断处理了。实际上,完全可以用其他数字端口来操作,但我一般不建议这样做:

encoderALast 作用是延时,确保每一步只增减 1。 如果 A 处于LOW状态,说明刚结束一个循环。第二个循环中,if 判断先不执行,同时由于 digitalRead(encoderPinA) 永远能读出 HIGH (上拉电阻),所以 encoderALast 又会被赋予 HIGH。直到第三个循环就可以执行了下一个步进了。

简单来讲,encoderALast 确保每隔一个 loop() 才执行一个步进,由于 loop 时间很短,所以旋转时不会感到延时的存在,但又能防止一个步进促发多次增减。

回到刚才的问题,为什么可以通过数字端口实现,我们却要用宝贵的中断端口呢?看看官方教程解释

Using interrupts to read a rotary encoder is a perfect job for interrupts because the interrupt service routine (a function) can be short and quick, because it doesn’t need to do much.

“中断很适合旋转编码器,因为中断过程短且块,这过程中无需要做太多其他事情。”

具体含义是,中断能够释放CPU的资源。对于Arduino来说,每次loop()、对每个数字端口的扫描、监测都会消耗非常有限的CPU资源。如果不采用中断,CPU要不断扫两个数字端口,判断描编码器是否有输入,效率极为低下。

而采用中断,CPU可以不管输入状态,而进行其他任务,只有当旋转编码器发出中断请求时才响应。所以,在第一个程序中,如果不需要在屏幕上输出结果,loop() 完全可以置空,或者加入其他更重要的内容。

外部中断就介绍到这里,以后我们会经常用到。后面我们会深入 Arduino 内部,介绍内部中断的原理和使用。

Leave a Reply

Your email address will not be published. Required fields are marked *