切换到宽版
  • 1290阅读
  • 20回复

[C51]请老师帮忙看个旋转编码开关程序 [复制链接]

上一主题 下一主题
离线ycx2002
 

发帖
370
M币
321
专家
0
粉丝
4
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit PINA=P1^0;                   //旋转编码开关脚位
sbit PINB=P1^1;                  //旋转编码开关脚位
sbit P2_1=P2^1;    //数码管
sbit P2_2=P2^2;    //数码管
sbit P2_3=P2^3;    //数码管
uchar x;
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,
                            0x99,0x92,0x82,
                            0xf8,0x80,0x90};
void Delayms(uchar xms)
{
    uchar i,j;
    for(i=xms;i>0;i--)
    for(j=110;j>0;j--);
}
void Display(uchar temp)
{
   uchar bai,shi,ge;
    bai=temp/100;  
    shi=temp%100/10;
    ge=temp%10;  

   P0=(table[bai]);
   P2_1 = 0;
   Delayms(5);
   P2_1 = 1;

   P0=table[shi];
   P2_2 = 0;
   Delayms(5);
   P2_2 = 1;

   P0=table[ge];
   P2_3 = 0;
   Delayms(5);
   P2_3 = 1;
  
}

/******************************************************************************/
void encoder()
{
    
static bit flag=0,turn_right=0,turn_left=0;
    
    

    if((PINA)&&(PINB))
        flag=1;
    if(PINA!=PINB)
    {
        turn_left=PINA;    
        turn_right=PINB;  
    }
    if(flag)
    {
        if((PINA==0)&&(PINB==0))
        {    
            flag=0;
            if(turn_left==0)
                x--;
            if(turn_right==0)
                x++;
        }
    }

}

void main()
{
    
    while(1)
    {
        encoder();
        Display(x);
    }
}


这是个51旋转编码开关的程序,编码开关检测程序用到了静态局部变量,问题就出在这里,如果不用静态局部变量直接声明位变量bit flag=0,turn_right=0,turn_left=0;按照原理也是说的通的,因为每次执行到这部分right和left都被重新赋值了:
    
if(PINA!=PINB)
    {
        turn_left=PINA;    
        turn_right=PINB;  
    }
用仿真芯片是可以通过的!
但实际放到板子上测试只显示零,调节旋转开关也没反应。但是在检测函数里使用static bit 或把bit flag=0,turn_right=0,turn_left=0;设置为全局变量就一切正常了。关于静态局部变量的意义我也知道,为什么会这样?
请老师详细解惑。。。。感谢!
离线876192514

发帖
208
M币
993
专家
1
粉丝
11
只看该作者 1楼 发表于: 07-13
用支持 跳变触发的外部中断[或者外部中断+定时器(做上升沿检测)] 检测AB相 相同+1 不同-1
本帖最近打赏记录:共2条打赏M币+20专家+1
發騷友 专家 +1 歡迎探討 07-13
xiaomage M币 +20 谢谢专家,有例程最好 07-13
离线xiaomage

发帖
737
M币
3707
专家
5
粉丝
34
只看该作者 2楼 发表于: 07-13
谢谢专家,有例程最好
离线小茅

发帖
2206
M币
1549
专家
6
粉丝
30
只看该作者 3楼 发表于: 07-13
查了一下,可以A下降沿触发中断,然后中断的时候读取B的状态,当然任意一个用边沿触发中断后然后读取另一个的状态都可以判断方向
本帖最近打赏记录:共1条打赏专家+1
發騷友 专家 +1 歡迎探討 07-13
离线la45088d1

发帖
941
M币
1209
专家
8
粉丝
26
只看该作者 4楼 发表于: 07-13
这么说吧,静态变量和临时变量是存储在不同的物理空间里的。
静态变量,初始化时赋值,然后只要你不改动,一直维持原样,一般放在内存的某个单元。
临时变量,每一次调用如果不初始化值不固定,当所在函数执行完生存期结束,一般这个变量只是CPU的某个通用寄存器,函数返回后接下来的操作会破坏这个值。而下次再进入时,这个变量所对应的通用寄存器值也是不确定的。
离线huaweiwx

发帖
1273
M币
3355
专家
57
粉丝
240
只看该作者 5楼 发表于: 07-13
给你一个完整的示例:rotateEncode.ino
  1. /* EXIT rotaryEncoder template
  2.    huaweiwx@sina.com 2018.6.16
  3. */
  4. #define CLK  PA2
  5. #define DAT  PA3
  6. #define SELE  PA4
  7. #define LED LED_BUILTIN
  8. int COUNT = 0;
  9. void setup()
  10. {
  11.   Serial.begin(115200);
  12.   pinMode(CLK, INPUT);
  13.   pinMode(DAT, INPUT);
  14.   pinMode(SELE, INPUT);
  15.   pinMode(LED, OUTPUT);
  16.   attachInterrupt(CLK, RoteStateChanged, FALLING);
  17.   // attachInterrupt(SELE, SELEState, FALLING);
  18. }
  19. void loop()
  20. {
  21.   if  (!(digitalRead(SELE)))
  22.   {
  23.     COUNT = 0;
  24.     Serial.println("STOP COUNT = 0");
  25.     delay (2000);
  26.   }
  27.   Serial.println(COUNT);
  28. }
  29. //-------------------------------------------
  30. void RoteStateChanged() //When CLK  FALLING READ DAT
  31. {
  32.   if  (digitalRead(DAT)) // When DAT = HIGH IS FORWARD
  33.   {
  34.     COUNT++;
  35.     digitalWrite(LED, HIGH);
  36.   }
  37.   else                   // When DAT = LOW IS BackRote
  38.   {
  39.     COUNT--;
  40.     digitalWrite(LED, LOW);
  41.   }
  42. }

这是一个完整的示例,串口显示当前正转或反转的步数,按下编码器选择开关请0;
A B 两相 分别定义为 CLK 和DAT;
line 23 设定CLK  下降沿触发中断;
line 39 开始是中断服务程序,对于CLK的下降沿,对应DAT高电平就是正转,否则反转,正反转的次数记录在全局变量COUNT中,同时LED指示这个状态;就这么简单;
离线ycx2002

发帖
370
M币
321
专家
0
粉丝
4
只看该作者 6楼 发表于: 07-13
回 la45088d1 的帖子
la45088d1:这么说吧,静态变量和临时变量是存储在不同的物理空间里的。
静态变量,初始化时赋值,然后只要你不改动,一直维持原样,一般放在内存的某个单元。
临时变量,每一次调用如果不初始化值不固定,当所在函数执行完生存期结束,一般这个变量只是CPU的某个通用寄存器,函数返回后接下 .. (2018-07-13 09:22) 回 la45088d1 的帖子

可即使使用普通的位变量原理也是说得通的,进去编码开关函数后先给三个位变量赋值,然后判断条件重新赋值,这个值在函数体内部是有效的。经赋值并运算后退出查询函数。一切都对。为啥在板子上跑就不行呢
离线ycx2002

发帖
370
M币
321
专家
0
粉丝
4
只看该作者 7楼 发表于: 07-13
回 876192514 的帖子
876192514:用支持 跳变触发的外部中断[或者外部中断+定时器(做上升沿检测)] 检测AB相 相同+1 不同-1 (2018-07-13 03:55) 回 876192514 的帖子

跑题了,我想知道这个程序到底问题出在哪?
离线ycx2002

发帖
370
M币
321
专家
0
粉丝
4
只看该作者 8楼 发表于: 07-13
回 小茅 的帖子
小茅:查了一下,可以A下降沿触发中断,然后中断的时候读取B的状态,当然任意一个用边沿触发中断后然后读取另一个的状态都可以判断方向 (2018-07-13 08:27) 回 小茅 的帖子

但是我贴出来的这个查询法在查询函数中为什么用普通的位变量就不行呢?
离线lyy-cy

发帖
310
M币
53
专家
8
粉丝
22
只看该作者 9楼 发表于: 07-13


这个程序是不断读取编码器A和B的电平,并且记录,需要根据之前的状态和现在的状态两组数据来决定
是否改变全局变量X,和如何改变。若是用局部变量不能记录之前状态,程序没错,硬件工作原理就不对
了。永远 x==0  ;





  1. void encoder()
  2. {    
  3. static bit flag=0,turn_right=0,turn_left=0;
  4.     if((PINA)&&(PINB))
  5.         flag=1;
  6.     if(PINA!=PINB)
  7.     {
  8.         turn_left=PINA;    
  9.         turn_right=PINB;  
  10.     }
  11.     if(flag)
  12.     {
  13.         if((PINA==0)&&(PINB==0))
  14.         {    
  15.             flag=0;
  16.             if(turn_left==0)
  17.                 x--;
  18.             if(turn_right==0)
  19.                 x++;
  20.         }
  21.     }
  22. }





[ 此帖被lyy-cy在2018-07-13 11:48重新编辑 ]