找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 451|回复: 24
收起左侧

单片机旋转编码器旋转速度的问题请教一下

  [复制链接]
ID:680429 发表于 2024-4-7 09:08 | 显示全部楼层 |阅读模式
普通51单片机用旋转编码器控制数码管显示数字的增减,步进值固定为1(数码管显示的数字都是逐一增加,不出现跳数)。当旋转速度快时,显示数字加减就快;旋转速度慢时,显示数字加减就慢(比如同样旋转90°,时间为2s数字就加5,时间为1s就加10,且显示不出现跳数的情况)

请问各位大佬有没有实现这个功能的思路,谢谢。
回复

使用道具 举报

ID:857072 发表于 2024-4-7 11:09 来自手机 | 显示全部楼层
天问51的库函数搬来你看看,也可以自己去翻代码全开源的。


#ifndef _EC11_51_H_
#define _EC11_51_H_


//----------------编码器参数微调宏定义----------------//
#ifndef KEY_COUNT_DESHAKING
#define KEY_COUNT_DESHAKING      15      //按键消抖时间15
#endif
#ifndef KEY_COUNT_LONGT
#define KEY_COUNT_LONGT          1500    //长按按键判断时间1500
#endif
#ifndef KEY_COUNT_DUAL
#define KEY_COUNT_DUAL           180     //双击按键判断时间180
#endif
#ifndef KEY_LONG_REPEAT
#define KEY_LONG_REPEAT          200     //长按按键的回报率的倒数,即一直长按按键时响应的时间间隔200
#endif

//----------------重入缓存结构体----------------//
typedef struct tongdaoHC {
        unsigned char    EC11_A_dq;                         //EC11的A引脚读到的当前状态
        unsigned char    EC11_B_dq;                         //EC11的B引脚读到的当前状态
        unsigned char    EC11_D_dq;                         //EC11的D引脚读到的当前状态
        unsigned char    EC11_A_Last;                       //EC11的A引脚上一次的状态
        unsigned char    EC11_B_Last;                       //EC11的B引脚上一次的状态
        unsigned char    EC11_IN_Click;                                //EC11按键状态机动作标志
        unsigned char    EC11_j_km;                         //EC11判断转动快慢的计数器
        unsigned int     EC11_j_COUNT;                      //EC11按键动作计数器       
       
}tongdaoHC;

//----------------函数声明列表----------------//
unsigned char Encoder_EC11_Scan(tongdaoHC *HC);

//*******************************************************************/
//功能:扫描EC11旋转编码器的动作并动作分析
//形参:无
//返回:EC11动作返回 0无动作 1正转 2反转 3按着按键正转 4按着按键反转 5短按 6双击 7长按 8长按松开
//详解:对EC11旋转编码器的动作进行模式分析
//*******************************************************************/
unsigned char Encoder_EC11_Scan(tongdaoHC *HC)
{
    unsigned char ScanResult = 0;    //用于分析编码器动作的变量
        if(HC->EC11_j_km)
        HC->EC11_j_km--;
        if(!HC->EC11_A_dq && !HC->EC11_A_Last && !HC->EC11_B_dq && HC->EC11_B_Last)   //A相当前状态和上次状态都为低电平时,抓B相上次状态为高当前状态为低的下降沿。
                {                    
                        ScanResult = 1;     //正转
                }
        else if(!HC->EC11_A_dq && HC->EC11_A_Last && !HC->EC11_B_dq && !HC->EC11_B_Last)  //B相当前状态和上次状态都为低电平时,抓A相上次状态为高当前状态为低的下降沿。
                {
                        ScanResult = 2;     //反转
                }
                HC->EC11_A_Last = HC->EC11_A_dq;   //更新编码器上一个状态暂存变量
                HC->EC11_B_Last = HC->EC11_B_dq;   //更新编码器上一个状态暂存变量
               
    if(HC->EC11_D_dq == 0)          //如果EC11的按键按下,
    {
        if(ScanResult == 0)         //状态记录值为0表示EC11没有转动
                {
            ScanResult = 5;         //返回值为5
                }       
        else
        {
            if(ScanResult == 1)     //按下按键时候正转
                                {
                                        HC->EC11_IN_Click = 7;
                                        return 3;       //编码器按键按下并正转返回值为3
                                }       
            if(ScanResult == 2)     //按下按键时候反转
                                {
                                        HC->EC11_IN_Click = 7;
                                        return 4;       //编码器按键按下并反转返回值为4
                                }       
        }
    }
        else                         //如果EC11的按键没有按下,
        {
        if(ScanResult==1)
                return 1;                //编码器正转返回值为1
                else if(ScanResult==2)
                return 2;                //编码器反转返回值为2
        }               

        if(HC->EC11_IN_Click = 7)//有旋转动作等待释放
        {
                if(ScanResult==0) //按键被释放
                        {
                                HC->EC11_IN_Click = 0;   //按键状态机清零
                        }
        }               
        else           //没有旋转动作等待进入按键处理
        {
                if(HC->EC11_j_COUNT<3000)    //打开按键按下时间定时器
                HC->EC11_j_COUNT++;
                switch (HC->EC11_IN_Click) //状态机
                {
                   case 0://初始状态
                                if(ScanResult==5)
                                        {
                                                HC->EC11_IN_Click = 1;   //进入状态1有键按下
                                                HC->EC11_j_COUNT = 0; //复位计时器
                                        }
                        break;
                   case 1://有键按下状态
                                if(ScanResult==5 && HC->EC11_j_COUNT > KEY_COUNT_DESHAKING)//按下消抖时间结束
                                        {
                                                HC->EC11_IN_Click = 2;   //进入状态2按键以稳定按下。
                                        }
                        break;               
                   case 2://按键以稳定按下。
                  
                           if(HC->EC11_j_COUNT <KEY_COUNT_DUAL && ScanResult==0)//按键释放且计时小于180MS
                                   {
                                           HC->EC11_IN_Click = 3;//进入状态3按键双击等待状态。
                                           HC->EC11_j_COUNT = 0; //复位计时器
                                   }
                           else if(HC->EC11_j_COUNT >=KEY_COUNT_DUAL && ScanResult==5)//按键按下且计时大于180MS
                                   {
                                           HC->EC11_IN_Click = 5;//进入状态5按键长按等待状态。
                                   }
                        break;
                   case 3://按键双击等待状态。
                           if(HC->EC11_j_COUNT <KEY_COUNT_DUAL && ScanResult==5)//按键按下且计时重新计数小于180MS
                                   {
                                           HC->EC11_IN_Click = 4;//进入状态4按键双击二次按下状态。
                                           HC->EC11_j_COUNT = 0; //复位计时器
                                   }
                           else if(HC->EC11_j_COUNT >=KEY_COUNT_DUAL && ScanResult==0)//按键释放且计时重新计数大于180MS
                                   {
                                           HC->EC11_IN_Click = 0;   //按键状态机清零
                                           return 5;//单击返回5

                                   }
                        break;
                        case 4://按键双击二次按下状态。
                           if(HC->EC11_j_COUNT > KEY_COUNT_DESHAKING && ScanResult==0)//按下消抖时间结束且按键被释放
                                   {
                                           HC->EC11_IN_Click = 0;   //按键状态机清零
                                           return 6;        //双击返回键值6       
                                   }                           
                        break;
                        case 5://按键长按等待状态。
                           if(HC->EC11_j_COUNT <KEY_COUNT_LONGT && ScanResult==0)//按键被释放且计时小于1600MS
                                   {
                                           HC->EC11_IN_Click = 0;   //按键状态机清零
                                           return 5;//单击返回5
                                   }
                           else if(HC->EC11_j_COUNT >=KEY_COUNT_LONGT && ScanResult==5)//记录键值在按下键值表里且计时大于1600MS
                                   {
                                                HC->EC11_IN_Click = 6;//进入状态6按键长按时间到达状态。
                                                HC->EC11_j_COUNT = 0; //复位计时器
                                                return 7;//连续按下返回键值
                                   }                  
                        break;
                        case 6://按键长按时间以到达。
                           if(ScanResult==0) //按键被释放
                                   {
                                                HC->EC11_IN_Click = 0;   //按键状态机清零
                                                return 8;//连续按下松开返回键值
                                   }
                           else if(HC->EC11_j_COUNT >=KEY_LONG_REPEAT && ScanResult==5)//记录键值在按下键值表里大于200MS
                                   {
                                           HC->EC11_j_COUNT = 0; //复位计时器
                                                return 7;//连续按下返回键值
                                   }
                        break;
                }
        }       
        return 0;//没有按键返回0
}   

#endif
12~80TSX9$`AQJIHGRXKD6B.png
回复

使用道具 举报

ID:883242 发表于 2024-4-7 13:51 | 显示全部楼层
编码器的分辨率是多少?
回复

使用道具 举报

ID:857072 发表于 2024-4-7 18:45 | 显示全部楼层
Hephaestus 发表于 2024-4-7 13:51
编码器的分辨率是多少?

手调的编码器,一圈十几二十步的,又不是电机编码器还讲分辨率。。
回复

使用道具 举报

ID:213173 发表于 2024-4-7 18:53 | 显示全部楼层
如果发现正常手速旋转会丢数,大概率是程序写的有毛病。
回复

使用道具 举报

ID:213173 发表于 2024-4-7 20:36 | 显示全部楼层
a185980800 发表于 2024-4-7 18:45
手调的编码器,一圈十几二十步的,又不是电机编码器还讲分辨率。。

EC11或EC16,只要软件硬件配合的好,每秒转20圈的速度不影响正确计数和判断正反转,
回复

使用道具 举报

ID:1034262 发表于 2024-4-7 23:26 | 显示全部楼层
一般手动编码器是20脉冲一圈,假设转1圈耗时200ms(这个应该算很快的了),则10ms一个脉冲,对于单片机来说,10ms还是很慢的,所以做到+1没有问题。
回复

使用道具 举报

ID:1109793 发表于 2024-4-8 07:27 | 显示全部楼层
我在哪个帖子里面回过一种方法,比较直观,也是网上找到的。你搜索一下能找到。
回复

使用道具 举报

ID:883242 发表于 2024-4-8 07:59 | 显示全部楼层
a185980800 发表于 2024-4-7 18:45
手调的编码器,一圈十几二十步的,又不是电机编码器还讲分辨率。。

用中断,一只引脚做中断触发,发生中断查另一只引脚电平就能知道旋转方向。poll方式效率太低了。
回复

使用道具 举报

ID:88256 发表于 2024-4-8 08:00 | 显示全部楼层
我估计大家都理解错了楼主的问题,楼主的想法应该和这样类似的,假如屏幕显示97.5 ,慢速旋转步进是0.1 ,快速旋转并且脉冲数达到10个后,步进是1 ;如果持续快速旋转并且达到一定的脉冲数,步进数是10 。
回复

使用道具 举报

ID:680429 发表于 2024-4-8 08:59 | 显示全部楼层
hhdsdy 发表于 2024-4-8 08:00
我估计大家都理解错了楼主的问题,楼主的想法应该和这样类似的,假如屏幕显示97.5 ,慢速旋转步进是0.1 , ...

步进值是固定的都是1,但是比如旋转同样角度,快旋加5,慢旋只加1,而且快旋加5是0、1、2、3、4、5这样加上去,而不是0直接到5,把中间的1、2、3、4这几个数丢了
回复

使用道具 举报

ID:680429 发表于 2024-4-8 09:04 | 显示全部楼层
coody_sz 发表于 2024-4-7 23:26
一般手动编码器是20脉冲一圈,假设转1圈耗时200ms(这个应该算很快的了),则10ms一个脉冲,对于单片机来说 ...

现在能做到+1,但是就是快旋和慢旋没区别,有没有办法做到区分快、慢旋,并且显示上不丢数
回复

使用道具 举报

ID:680429 发表于 2024-4-8 09:06 | 显示全部楼层
wulin 发表于 2024-4-7 18:53
如果发现正常手速旋转会丢数,大概率是程序写的有毛病。

现在能正确计数,正反判断也正常,也不会丢数,就是区分不了快慢旋
回复

使用道具 举报

ID:680429 发表于 2024-4-8 09:08 | 显示全部楼层
xiaobendan001 发表于 2024-4-8 07:27
我在哪个帖子里面回过一种方法,比较直观,也是网上找到的。你搜索一下能找到。

好像没有找到帖子。。
回复

使用道具 举报

ID:857072 发表于 2024-4-8 13:06 来自手机 | 显示全部楼层
快慢转的判断就是建立一个用于计时的变量,然后在扫描的定时器中断里判断这个变量不为零就一直自减。然后在旋转的函数里判断这个值为零就是,慢转不为零就是快转,判断完以后给这个变量赋一个值,这个值就是你判断快慢转的间隔时间
回复

使用道具 举报

ID:1109793 发表于 2024-4-8 15:29 | 显示全部楼层
要快转+5还得一个一个加,那就是要在慢的时候5个信号加1了,这样就得提高编码器分辨率了。每个信号设置一次定时器,时间到达之前信号又来了说明是快的,否则就是慢的。
回复

使用道具 举报

ID:213173 发表于 2024-4-8 21:34 | 显示全部楼层
zjy597662225 发表于 2024-4-8 09:06
现在能正确计数,正反判断也正常,也不会丢数,就是区分不了快慢旋

楼主的意思是不是快转比慢转计数增/减量翻倍,但还要在数码管上呈现连续数字?
回复

使用道具 举报

ID:384109 发表于 2024-4-8 21:57 | 显示全部楼层
楼主还没搞懂旋转编码器的结构和原理
回复

使用道具 举报

ID:680429 发表于 2024-4-9 09:09 | 显示全部楼层
wulin 发表于 2024-4-8 21:34
楼主的意思是不是快转比慢转计数增/减量翻倍,但还要在数码管上呈现连续数字?

嗯,是这样的
回复

使用道具 举报

ID:996773 发表于 2024-4-9 09:15 | 显示全部楼层
楼主没懂旋转编码器是什么东东,估计也没用过,建议楼主拆一个旋转编码器看看,旋转90度?

单片机能知道它转了90度吗?
回复

使用道具 举报

ID:1109793 发表于 2024-4-9 09:21 | 显示全部楼层
hi等你 发表于 2024-4-9 09:15
楼主没懂旋转编码器是什么东东,估计也没用过,建议楼主拆一个旋转编码器看看,旋转90度?

单片机能知道 ...

只要知道了分辨率,当然可以知道度数啊。获得Z轴信号还能知道绝对角度呢。如果是绝对值编码器,那就更容易了。
回复

使用道具 举报

ID:996773 发表于 2024-4-9 13:15 | 显示全部楼层
xiaobendan001 发表于 2024-4-9 09:21
只要知道了分辨率,当然可以知道度数啊。获得Z轴信号还能知道绝对角度呢。如果是绝对值编码器,那就更容 ...

其实我也不懂分辨率,也不懂z信号,绝对角度我也不懂,但我会写,其实编码器两脚就相当于

两个位,01或10,转了之后变00,啥时候变00就是你的手速,搞个定时器,01时候开始计时,

定时时间短一些看看有没有到00,到了说明手速快,屏显加10或者寄存器加10去执行,如果还没

到00继续计时,又等了一会看看到了00了,说明手速慢,屏显加1或者寄存器加1,这种思路去

写的编码器程序丝滑无比
回复

使用道具 举报

ID:1109793 发表于 2024-4-9 14:10 | 显示全部楼层
hi等你 发表于 2024-4-9 13:15
其实我也不懂分辨率,也不懂z信号,绝对角度我也不懂,但我会写,其实编码器两脚就相当于

两个位,01 ...

你说的大概就是这样的吧,这个方法还是不错的。虽然我没有实际测试过。
  1. static uchar temp;
  2. temp <<= 2;
  3. if(sig_a)temp += 2;
  4. if(sig_b)temp += 1;
  5. switch(temp&0x0f){
  6.         case 2:case 4:case 11:case 13:
  7.         counter++;                        //脉冲数增加
  8.         break;
  9.         case 1:case 7:case 8:case 14:
  10.         counter--;                //脉冲数减少
  11.         break;
  12.         case 3:case 6:case 9:case 12:
  13.         break;
  14.         }
复制代码
回复

使用道具 举报

ID:213173 发表于 2024-4-12 07:17 | 显示全部楼层

用定时器判断A脉冲间距以判断转速,小于某转速只计A脉冲数,大于等于参考转速A、B脉冲都计数。就可以实现快速旋转编码器时计数值翻倍并在数码管上呈现连续数字。
回复

使用道具 举报

ID:680429 发表于 2024-4-23 13:40 | 显示全部楼层
wulin 发表于 2024-4-12 07:17
用定时器判断A脉冲间距以判断转速,小于某转速只计A脉冲数,大于等于参考转速A、B脉冲都计数。就可以实现 ...

好的我试试,谢谢大佬
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表