发一个我的程序,旋转编码器解码,今天晚上写出来的
初步试验,可以用了,没有发现问题。/*旋转编码器解码程序
PC4,PC5为左右输入,内部上拉,公共脚接地。
1ms调用一次
//2007.2.23 yule
//修改:
*/
#define Knob_In_PIN PINC
#define Knob_In_PORT PORTC
#define Knob_In_DDR DDRC
#define Knob_In_L PC4
#define Knob_In_R PC5
//输出结果:0不动作;1左转一格;2右转一格。
enum direct {STOP,LEFT,RIGHT}D_OUT;
const uint8_t direct_left PROGMEM ={3,2,0,1,3};
const uint8_t direct_right PROGMEM ={3,1,0,2,3};
//初始化。
void Knob_Init(void)
{
Knob_In_DDR &= ~((1<<Knob_In_L)|(1<<Knob_In_R)); //输入
Knob_In_PORT |=(1<<Knob_In_L)|(1<<Knob_In_R); //上拉
}
//编码器解码
void knob_scan(void)
{
static uint8_t knob_ary;
static uint8_t knob_count=0;
uint8_t knob_value;
uint8_t update=0;
uint8_t i;
knob_value = Knob_In_PIN&((1<<Knob_In_L)|(1<<Knob_In_R)); //取端口C管脚信号
knob_value =knob_value>>4;
if(knob_value==knob_ary)
{
++knob_count;
if(knob_count==1)
{
knob_ary=knob_ary;
knob_ary=knob_ary;
update=1;
}
if(knob_count==0xff) //停留,不转动。
{
knob_count=2;
}
}
else
{
knob_ary=knob_value;
knob_count=0;
}
//如果转动产生新数据,判断方向。
//左转为 11 10 00 01 11 3 2 0 1 3
//右转为 11 01 00 10 11 3 1 0 2 3
if(update)
{
update=0;
for(i=0;i<4;i++)
{
if((knob_ary==pgm_read_byte(direct_left+i))
&&(knob_ary==pgm_read_byte(direct_left+i+1)))
{
D_OUT=LEFT;
}
else if((knob_ary==pgm_read_byte(direct_right+i))
&&(knob_ary==pgm_read_byte(direct_right+i+1)))
{
D_OUT=RIGHT;
}
else D_OUT=STOP;
}
}
} 大家瞧瞧,有没有需要修改的,多提些意见。 用中断不是更好,简单一点的做法是使用一路信号的固定边沿触发中断,中断发生时读另一路信号来判断方向,复杂一点的做法是两路信号的两个边沿都触发中断 ……
unsigned long DCounter = DCOUNTER_ZERO;
……
# define MotorDirection (PINB & 0x10)
……
/***********************************************************
*函数说明:外中断初始化函数 *
*说明: 使用外中断0和外中断1 下降沿触发 *
***********************************************************/
void ExternIntInit(void)
{
MCUCR = 0x0A;
GICR= 0xC0;
}
/***********************************************************
*函数说明:外中断0中断处理程序 *
***********************************************************/
void Int0Isr(void)
{
/*
if (MotorDirection)
{
DCounter ++;
}
else if (DCounter)
{
DCounter --;
}*/
asm("push r20")
asm("push r2");
asm("push r3");
asm("push r4");
asm("push r5");
asm("lds R2,_DCounter"); //Load direct from data space
asm("lds R3,_DCounter+1");//Load direct from data space
asm("lds R4,_DCounter+2");//Load direct from data space
asm("lds R5,_DCounter+3");//Load direct from data space
asm("ldi r20,1");
if (MotorDirection)
{
//DCounter ++;
asm("add r2,r20"); //Add without carry
asm("ldi r20,0");
asm("adc r3,r20"); //Add with carry
asm("adc r4,r20"); //Add with carry
asm("adc r5,r20"); //Add with carry
}
else
{
//DCounter --;
asm("sub r2,r20"); //Add without carry
asm("ldi r20,0");
asm("sbc r3,r20"); //Add with carry
asm("sbc r4,r20"); //Add with carry
asm("sbc r5,r20"); //Add with carry
}
asm("sts _DCounter,R2");
asm("sts _DCounter+1,R3");
asm("sts _DCounter+2,R4");
asm("sts _DCounter+3,R5");
asm("pop r5");
asm("pop r4");
asm("pop r3");
asm("pop r2");
asm("pop r20");
} 什么叫旋转编码器? 中断不好使!
扫描好些! 很多方法在于运用,运用得当才能发挥其效用,不知楼上是怎么用的中断而断定其不好用? int8 CodingsWitchPolling()//return 0=不变;-1=减;1=加//a先高=+
{
static bool aold,bold; //定义了两个变量用来储蓄上一次调用此方法是编码开关两引脚的电平
static bool st; //定义了一个变量用来储蓄以前是否出现了两个引脚都为高电平的状态
int8 tmp = 0;
if(CodingsWitch_A&&CodingsWitch_B) st = 1; //如果两个引脚都为高电平则把st置1
if(st) //如果st为1执行下面的步骤
{
if(CodingsWitch_A==0&&CodingsWitch_B==0) //如果当前编码开关的两个引脚都为底电平执行下面的步骤
{
if(bold) //如果上一次调用时编码开关b引脚为高说明编码开关在向加大的方向转
{
st = 0;
tmp++; //设返回值为1
}
if(aold) //如果上一次调用时编码开关a引脚为高说明编码开关在向减小的方向转
{
st = 0;
tmp--; //设返回值为-1
}
}
}
aold = CodingsWitch_A; //储蓄a到aold
bold = CodingsWitch_B; //储蓄b到bold
return tmp; //返回 0=不变;-1=减;1=加
}
我这个如何 我一直都用中断方式的啊 我用dewangyang 水滴的程序作了实验,发现旋钮转动较快时有漏读现象.请问多长时间调用一次程序才能不漏判. 一般都是用中断+滤波 我习惯使用扫描方式 扫描?开玩笑吧!如果单片机要处理的事情多一点的话,看你怎么扫描, 别人设计的硬件,用的是普通IO口我只能用扫描的方式.不知道怎么才能做的好点. mark 参考楼主的程序,编成了,谢谢。
我的这个编码器用在步进电机调速上面,0--700转/分.要求:转一下后,停一下再转,每格速度+1或者-1;连续迅速旋转编码器4圈以上时,每转一格+5或者-5,给人一种“加速”的感觉。请问这种加速的感觉如何利用编程实现呢?大家给个思路。谢谢 我一直用中断,很实时,不会丢失脉冲,单片机是MSP430 最近发现还是扫描比较好容易处理干扰。
中断方式不好弄,尤其是传输线路比较长,又没有屏蔽的情况,比如40米的普通电缆 旋转编码器的信号输出有A相,B相,Z相,其中Z为零脉冲,计数模块中不使用。计数模块使用A,B来进行计数。编码器结构以及计数原理如下:
结构:玻璃码盘,上排为A,下排为B,灰色的不透光,白色的透光。
编码器转动后经过码盘和光电二极管的作用可以得到如下的波形:
http://cache.amobbs.com/bbs_upload782111/files_13/ourdev_426534.JPG
±à???÷???í (原文件名:???ü??.JPG)
上图为编码器的工作原理,图中A相超前B相90度,如果转动方向相反,则A相滞后B相90度。B_A为B&A的组合,若A相超前B相90度设定为正向旋转,则编码器正向转动一个码的距离,B_A变化过程为01-11-10-00。
根据这个原理计数模块根据B_A变化过程来计数:
若当前B_A为00状态,下一个状态为01则计数器加1。
若当前B_A为00状态,下一个状态为10则计数器减1。
其余状态不符合编码器的工作原理,计数器的值不变。
此时计数器的结果是编码器实际移动一个码距离的4倍。由于编码器加工精度的原因,这个结果与实际的位置相比,不是非常准确。
通常的增量式光电编码器产生的周期性的方波信号理论的占空比是1:1,即50%。但是由于码盘加工精度,安装精度等原因的影响,在编码器匀速转动时产生信号的占空比不是1:1,是40%-60%,即信号占空比的误差是10%。
但是其信号的周期误差,即高电平与低电平时间总和的误差比较小,为1%。所以通常用编码器信号测量速度应该测量信号的整个周期来计算,这样测量的误差才较小。
只有把计数结果右移两位(除以4),得到的结果才是准确的编码器移动一个码的距离。 我通常用FPGA来实现编码器的计数,以前也用过单片机IO查询方式处理过,需要评估查询速度是否满足编码器的最大频率变化。 //编码器计数程序
void encoder_cnt(void)
{
uchar temp;
temp = PIND; //取端口D管脚信号
couch_clr = (temp & 0x08); //取编码器清零信号
if(couch_clr != false) //有编码器清零信号
{
couch_num = 0; //水平床码清零
}
else
{
if(encoder_cnt_en == false) //编码器计数模块没有启动
{
pr_couch_ba = temp & 0x03; //取编码器A、B相电平信号
}
else
{
couch_ba = temp & 0x03; //取编码器A、B相电平信号
if(pr_couch_ba == 0x00)
{
if(couch_ba == 0x01)
{
couch_num++; //水平床码加1
}
else if(couch_ba == 0x02)
{
couch_num--; //水平床码减1
}
}
else if(pr_couch_ba == 0x01)
{
if(couch_ba == 0x03)
{
couch_num++; //水平床码加1
}
else if(couch_ba == 0x00)
{
couch_num--; //水平床码减1
}
}
else if(pr_couch_ba == 0x02)
{
if(couch_ba == 0x00)
{
couch_num++; //水平床码加1
}
else if(couch_ba == 0x03)
{
couch_num--; //水平床码减1
}
}
else if(pr_couch_ba == 0x03)
{
if(couch_ba == 0x02)
{
couch_num++; //水平床码加1
}
else if(couch_ba == 0x01)
{
couch_num--; //水平床码减1
}
}
}
pr_couch_ba = couch_ba;
}
if(couch_num >= 0) //床码值大于等于0
{
couch_num_minus = 0; //床码值正标志
if(couch_num < 20000)
{
abs_couch_num = couch_num; //取绝对床码
}
}
else
{
couch_num_minus = 1; //床码值负标志
if(couch_num > -20000)
{
abs_couch_num = abs(couch_num); //取绝对床码
}
}
abs_couch_num = abs_couch_num >> 2;//床码计数结果除4
}
这时以前我写的IO查询方式的编码器计数函数。我个人认为还是扫描好些,若用中断需要AB相双沿触发中断。 MARK 这几天正要做闭环控制,谢谢! 学习了 记号. 进来学习了,最近买了100只编码器:) 主楼上面的 pgm_read_byte这个函数好像没有提供出来,应该是读状态需要动到的,否则,后面的for循环不知道怎么动作了 分析了这3个,感觉还是7楼的好用些啊 不错,有原理,有程序,学习了 旋转编码器是什么? 好,学习! 是光编码器吧 我也用的JANCO的 一个好几万 MARK 学习了 使用中断来作是比较实时,但要处理干扰。 MARK 学习了 MARK 标记一下 试验的如何了啊?关注 mark 支持23楼:我就是这么做的 感觉太复杂了,偶多年以来,一直是100us查询一次并两次采样相等才有效,相当于去抖,在A的下降沿时检测B是高还是低来决定左右,很可靠,用C的话,就几句语句就OK了 有用汇编做的编码开关程序吗,想学习使用。 mark 旋转编码器应该有A B 相加一组Z的啊 记号,谢谢
正要使用旋转编码器 mark 打地基 测量速度不怕丢失一个2个 测量位置就有跟随问题了 尤其是精度高,速度高的场合。没有外部逻辑配合 仅靠AVR ,速度还是不够。 用过一次,效果不理想, 今天也做回mark 党 【17楼】 qingzhong
积分:154
派别:
等级:------
来自:深圳
mark
2009-03-14,11:36:44 遇到个挖坟党。。。。。。 mark hao 最近用ALPS的大约10块左右,EC11系列的。 请问楼主旋转编码器的解码用的是那款芯片? 程序太长了。 7句就可以搞定。 X在3-100范围内加减。ALPS的EC11系列
///////////////////////RB中断入口RB4-RB7//////////////////////////////////////
#int_RB
void RB_isr(){
if (input(PIN_B4)!=input(PIN_B5)){//这里很重要,不然的话会加一个数又减一个数
rb_buf^=portb; //相同为0.相异为1
if(plus) if(x<101) x++; //断rb_buf的4和5来检测是RB4还是RB5中断.
if(sub)if(x>3) x--; //
}
while((input(PIN_B4)!=input(PIN_B5))); //等待B4电平=B5电平
rb_buf=input_b(); //读RB口,电平锁存退出中断程序.
CLEAR_INTERRUPT(int_RB);
}
////////////////////////////end///////////////////////////////////////////////// 受教!有个欧姆龙的正好玩玩 MARK 【42楼】 coody
积分:400
派别:
等级:------
来自:
感觉太复杂了,偶多年以来,一直是100us查询一次并两次采样相等才有效,相当于去抖,在A的下降沿时检测B是高还是低来决定左右,很可靠,用C的话,就几句语句就OK了
===================================================================================================================
这种方法我用在300块钱一个的编码器上好用,用在10块钱一个的上面很不好用,这几天正在搞 先用硬件消除颤动,去抖。或者加个滤波(?) 或者加个四分频电路(?)加触发器判别方向(?)
不然用带qei模块的mcu或者dsp试一试?
to楼上,上一个公司里用的是6块的(200pcs),taiwan产。烂。最后凑合凑合,加强了电路,改进了程序,总算能用了。
加速旋转时候的代码,好像不一样的。 mark 扫描,感觉还好,停在中间会自动退出,基本不会误判。
void Decode(void)
{
if((!Flag_End_Decode)&&Flag_Decode_End_Process)
{
if(In_Decode_Pin_L == In_Decode_Pin_R)
{
if(!Flag_End_Detected)
{
if(In_Decode_Pin_L)
{
Flag_Decode_Level_L = 0;
Flag_End_Detected = 1;
}
if(!In_Decode_Pin_L)
{
Flag_Decode_Level_L = 1;
Flag_End_Detected = 1;
}
}
}
else
{
if(Flag_Decode_Level_L)
{
if(In_Decode_Pin_L)
{
Flag_Decode_Add = 1;
Flag_Decode_Dec = 0;
Flag_End_Decode = 1;
Flag_Decode_End_Process = 0;
}
else if(In_Decode_Pin_R)
{
Flag_Decode_Add = 0;
Flag_Decode_Dec = 1;
Flag_End_Decode = 1;
Flag_Decode_End_Process = 0;
}
}
else
{
if(!In_Decode_Pin_L)
{
Flag_Decode_Add = 1;
Flag_Decode_Dec = 0;
Flag_End_Decode = 1;
Flag_Decode_End_Process = 0;
}
else if(!In_Decode_Pin_R)
{
Flag_Decode_Add = 0;
Flag_Decode_Dec = 1;
Flag_End_Decode = 1;
Flag_Decode_End_Process = 0;
}
}
}
}
} void Update_Vaule()
{
if(Flag_End_Decode)
{
Flag_End_Decode = 0;
Flag_End_Detected = 0;
if(Flag_Decode_Add)
{
Flag_Decode_Add = 0;
if(run_time < 60 )
{
run_time++;
}
}
if(Flag_Decode_Dec)
{
Flag_Decode_Dec = 0;
if(run_time > 1)
{
run_time--;
}
}
}
else
{
if(In_Decode_Pin_L == In_Decode_Pin_R)
{
Flag_Decode_End_Process = 1;
}
}
}
连着处理哈。。 mark 发一个 机械式旋转编码器 检测程序 用51写的
#define WA= PX.X
#define WB =Py.y
void encoder(void) interrupt 3
{
unsigned char i,j;
static unsigned char ta,tb;
TR1 = 0;
i = WA;
j = WB;
if((i^j)&&(ta&tb))
{
if((i == 1)&&(j ==0))
{
count++;
}
else if((i == 0)&&(j == 1))
{
count--;
}
}
ta = WA;
tb = WB;
count0++;
TH1 = (65536 - 2000)/256;
TL1 = (65536 - 2000)%256;
TR1 = 1;
} mark mark 楼主:未提供 pgm_read_byte 定义,程序有BUG,请补充 pgm_read_byte 的定义! mark !!! 这贴受教了
mark !!! 好贴 受教 俺是菜鸟,能不能给一个C的增量旋转编码器控制步进的,也就是输出PU和DR,谢谢! MARK,有意思 mark mark,用的时候再研究 mark mark 自己编的ec11编码器程序快速转动有反弹现象,进来学习一下。 mark
赶紧淘几个玩下 收藏 Mark 学习 hao mark 马克。 current = GET_WHEEL(); //Get the MCU's logic
if ( current == WHEEL_ANB )
{
lock = 0x01;
}
if ( lock ) //Get half resolvtion
{
if ( current == WHEEL_B )//org:0x10
{
wheel_count_out += 2;
if ( wheel_count_out >= WHEEL_END )
{
wheel_count_out = WHEEL_END;
}
lock = 0x00;
return;
}
if ( current == WHEEL_A )//org:0x20
{
wheel_count_out -= 2;
if ( wheel_count_out <= WHEEL_BEGIN )
{
wheel_count_out = WHEEL_BEGIN;
}
lock = 0x00;
} 回复【楼主位】yule
-----------------------------------------------------------------------
菜鸟 问题 你好 你能 告诉一下你使用什么软件下写的程序吗chaobaocai@163.com mark mark.. 编码器用的很多了,但都是用PLC来实现的。 mark mark 有空要好好看看,呵呵 mark 标题让我吓一跳,以为我的编码器用错了。 记号 mark mark can any of you make it more complicated? mark mark
页:
[1]
2