ESP8266之旋转编码器与四位数码管模块显示圈数

@fsw  February 3, 2020

模块:NodeMCU
SDK版本:ESP8266_RTOS_SDK-3.0
开发环境:wsl+vscode
外设:增量式光电编码器,四位共阳数码管模块

很久没用8266了,最近做了个绕线计数器,手头只有一块NodeMCU,使用旋转编码器(某宝36块的那个)与数码管显示模块(某宝四块八)。

编码器:

编码器统一为外径38,轴6的,脉冲600 ,电压5~24V宽电压 ,输出方式 ,线长2m。电压默认5-24v,默认是AB信号。8266不像stm32有硬件编码接口,所以只能采用软件的方式实现。首先我们来看编码器的正转与反转波形图。

编码器脉冲图.png

一个周期内的波形变化即可判断正转与反转,假设高电平为1,低电平为0,

正转时信号A与B按照“11-10-00-01-11-10-00-01....”变化
反转时信号A与B按照“11-01-00-10-11-01-00-10....”变化

若将A与B按照二进制相加得到变化顺序如下:

正转:3-2-0-1-3-2-0-1....
反转:3-1-0-2-3-1-0-2....

这样我们就只需要读取A与B两个信号高低电平就好了,正转与反正的变化顺序都是不一样的,总结下思路

1.初始化GPIO,设置为输入读取电平
2.记住初始状态encOld,一个周期内四次变化,记住上电时的那次,以后每个周期以此为开始
3.读取实时状态encNow,如果没有变化表示编码器没有转动
4.一个周期有四次状态变化,只要判断连续的三次符合正转或者反转的状态变化,即返回相应的值
5.主函数中while循环并记录下每一个脉冲,600个脉冲为一圈,处理数据

下面是编码器代码

#include <stdio.h>
#include <string.h>

#include "driver/gpio.h"

#include "encoder.h"
#include "gpio.h"

//A为低电平,B增高,A为高电平时B降低为正转
//A为低电平时B降低,A为高电平时B增高为正转

#define pinA    14         //编码器信号A
#define pinB    12         //编码器信号B

static int encA=0,encB=0;
static int enc0=0,enc1=0;
static int encOld=0,encX=0,encY=0;
int encNow;

unsigned char Encoder(void)
{

    encY=encNow;
    encA=gpio_get_level(pinA);
    encB=gpio_get_level(pinB);
    if(enc0==0)
    {
        encOld=(encA?2:0)+(encB?1:0);//记住初次使用时状态
        enc0=1;
    }
    encNow=(encA?2:0)+(encB?1:0);//根据两个IO当前状态组合成16进制的0|1|2|3
    if(encY==encNow) return(0);//如果相同就没有转动
//11-10-00-01是正转
//11-01-00-10是反转
    if((encOld==3&&encNow==2)||(encOld==2&&encNow==0)||(encOld==0&&encNow==1)||(encOld==1&&encNow==3))encX=encNow;
    if((encOld==3&&encX==2&&encNow==0)||(encOld==2&&encX==0&&encNow==1)||(encOld==0&&encX==1&&encNow==3)||(encOld==1&&encX==3&&encNow==2))
    {
        
        encX=0;
        return('R');//正转返回R
    }

    if((encOld==3&&encNow==1)||(encOld==1&&encNow==0)||(encOld==0&&encNow==2)||(encOld==2&&encNow==3))encX=encNow;
    if((encOld==3&&encX==1&&encNow==0)||(encOld==1&&encX==0&&encNow==2)||(encOld==0&&encX==2&&encNow==3)||(encOld==2&&encX==3&&encNow==1))
    {
        encX=0;
        return('L');//反转返回L
    }
    return(0);//无效返回0
}

数码管模块:

四位数码管模块采用2片595驱动数码管,需要单片机3路IO口,根据数码管动态扫描原理进行显示。店家给的例程只有51单片机和Ardiuno的,不过驱动原理很简单,需要的IO口也很少,稍微改改就能用了,这里只贴了数码管部分,还需要初始化GPIO

四位数码管的三个IO口为SCLK,RCLK,DIO,分别作用为打入信号,时钟脉冲信号,和串行数据输入信号

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/gpio.h"

#include "display.h"
#include "gpio.h"

#define GPIO_BLUE_LED 2
#define DIO 5         //串行数据输入
#define RCLK 4        //时钟脉冲信号,上升沿有效
#define SCLK 0         //打入信号,上升沿有效

unsigned char LED[8];         //用于LED的8位缓存

unsigned char  LED_0F[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xbf,0X00};
//数码管显示0123456789AbCdEF-
unsigned char  LED_1F[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//带小数点的数字0.1.2.3.4.5.6.7.8.

//显示
void LED4_Display (void)
{
    unsigned char  *led_table;          // 查表指针
    unsigned char i;
    //显示第1位
    led_table = LED_0F + LED[0];
    i = *led_table;

    LED_OUT(i);            
    LED_OUT(0x01);        

    gpio_set_level(RCLK,0);
    gpio_set_level(RCLK,1);

    //显示第2位,带小数点
    led_table = LED_1F + LED[1];
    i = *led_table;

    LED_OUT(i);        
    LED_OUT(0x02);        

    gpio_set_level(RCLK,0);
    gpio_set_level(RCLK,1);
    //显示第3位
    led_table = LED_0F + LED[2];
    i = *led_table;

    LED_OUT(i);            
    LED_OUT(0x04);    

    gpio_set_level(RCLK,0);
    gpio_set_level(RCLK,1);
    //显示第4位
    led_table = LED_0F + LED[3];
    i = *led_table;

    LED_OUT(i);            
    LED_OUT(0x08);        

    gpio_set_level(RCLK,0);
    gpio_set_level(RCLK,1);
}

void LED_OUT(unsigned char X)
{
    unsigned char i;
    for(i=8;i>=1;i--)
    {
        if (X&0x80)
        {
            gpio_set_level(DIO,1);
        } 
        else 
        {
            gpio_set_level(DIO,0);
        }
        X<<=1;
        gpio_set_level(SCLK,0);
        gpio_set_level(SCLK,1);
    }
}

使用的时候先给LED[]赋值,即要显示的数字,例如LED[0]=6,第四个数码管就会显示6,把函数LED4_Display ();放到while里面循环或者RTOS任务里面定时执行即可。

存在的问题:
转速过快会丢失脉冲导致不准,不过我这里的转速下是准确的,够用了。(买的时候以为精度越高越好,这下坑了自己

还有while循环中不要使用printf,这样会拖慢cpu速度。

阅读量:260

添加新评论