利用EQEP实现编码器的位置与转速测量

零 前言

研究了这么久的电机控制,DSP28335的EQEP模块实际上了解的并不是特别多,因为之前做实验都是用的师兄整的代码。所以这回我觉得好好研究一下这玩意,正好我手上现在有一种光电式的,还有一种磁编码器,一起整理一下。然后实现了一下相关的代码,完成了转速和位置的测量。

“生活可真是艰难呢”

一 光电编码器

1 E6C2CWZ6C型光电编码器

E6C2-CWZ6C输出引脚包含A、B、Z三相,以及5-24V电源与地,输出状态为NPN集电极开路输出,对应实物照片及输出回路如下:

当三极管导通时,集电极输出低电平;当三级管关断时,集电极输出悬空。因此,该编码器在工作时,需要外部添加上拉电阻,如下图所示。因此,这边我额外在在编码器输入信号线上增加了上拉电阻。

当然也可以采用光耦隔离的方式,如下图所示。这边我的另外一个控制板采用的是这种光耦隔离的方案,本文中暂不涉及。

此时,输出信号定义如下,注意动作图中的ON、OFF晶体管开通或关断状态:

光电编码器我见过的应用主要是在感应电机的控制过程中,只要三根线就够了。其他的常见编码器引脚信号还有:A、B、Z信号的反相信号,这个是为了消除共模噪声干扰的;U、V、W信号(120度相位差),这个用在BLDC控制上的,和霍尔位置传感器类似,可参考[1]这里不做详述;或者只有计数时钟和方向信号的,这个28335是有对应的控制方式的。

2 硬件连接

输入信号与地并联噪声滤除电容,然后经过两次反相器,一方面是增强信号能力,另外编码器供电采用5V,这个SN74LVC14供电是个3.3V,还起到电平转换的效果。后面直接连接入DSP接口即可。

二 磁传感编码器

1 MT6825磁编码器

目前常见的小型伺服永磁电机,经过拆解可以发现,其使用的主要是磁角位置编码器,使用的我这边看到使用的芯片型号为MagnTek的MT6825磁编码芯片,再配上一些差分芯片及控制芯片,其输出引脚包含了正反的A、B、Z以及U、V、W信号。按照我早年做机器人的经验,这种编码器在转动处没有直接的机械连接,因此在具有较强机械振动的应用场合,比滑动电阻式以及光电编码器更有优势。根据数据手册,磁铁与芯片之间的间隙最好不要超过3mm。下面的是示意图和实物照片。

下图给出了A与反向A信号的波形,以及A与B信号的波形。

2 硬件连接

由于采用差分的输出方式,所以通常需要经过滤波,然后接差分转单端芯片,比如IT的AM26C32,这个用了很多年了,以及后接反相器做电平转换(如上部分所述)。

三 基于28335 EQEP单元实现位置与转速测量

1 我的编码器到底有多少线以及位置的确定

位置计数与控制单元(Position Counter and Control Unit, PCCU)通过QEPCTL和QPOSCTL寄存器,来实现编码器的计数设置。其中的一个运行模式是,位置计数器可以在Index事件时清零(正转)或者置为设定最大值QPOSMAX(反转)。因为我记得我这个编码器大概有2500线,这样我就可以测试一下是不是这样。具体的寄存器定义,只要把[4]这个文档整明白了,问题不大。中文翻译可以看[5]。

设置代码,让他在Z信号时刻清零位置计数器

EQep1Regs.QEPCTL.bit.PCRM=00;

可以得到如下结果

果然,大概在10000的位置,位置计数器清零,所以我的编码器确实是2500线。另外在这个模式下,每一圈的z信号都会让他清零。

与此同时,采用这个模式还可以实现旋转位置的测量,只要将QPOSMAX设置为最大位置脉冲数减一即可,我这里是9999。这样,不管他怎么转,都是在0到9999这个范围里。

2 等时间长度采样法(也称M法)实现转速计算

计算公式如下

这种方法一般书里都描述对应于低速并不合适,也就是说如果编码器线数较低,他可能在一个采样周期转不过两根线。例如,考虑一个2500线的编码器,那么编码器转一周在28335内部经过四倍可以得到10000个脉冲,也就是一个脉冲间隔对应0.0001个圆周。这时候,如果用100Hz的等时间采样来计及编码器的计数,那么理论上最低速度是0.0001*100=0.01rps,即0.6rpm。我觉得这种分辨率对我来说已经足够了(200rpm左右),所以我就用这个方法就好了。

这边我再多描述一些,在eqep模块里,他有一个计数器模块,这个可以用来实现等时间的脉冲计数采样,也就是M法。另外,他还有一个捕捉模块,这个模块可以用来实现几个固定脉冲位置之间的时间采集,也就是T法。这里只用定时实现计数就完了。

28335的EQEP里面配有一个Unit Timer Base,他是一个SYSCLKOUT驱动的32bit计时器,并且还带有中断功能。当timer的计数器QUTMR等于设置的周期值QUPRD时,单位时间中断出现(QFLG[UTO]置位)。与此同时,设置周期值QUPRD恰好为单周总脉冲-1,并设计z信号清除QPOSCNT,即可实现每周清除一次位置计数器,并且位置计数器正好对应编码器的空间绝对位置。

3 利用首次z信号中断实现编码器绝对位置计算

这边我想实现一个从一开始编码器实际旋转,就确定的绝对位置功能。可是,在实际运行时,可能出现这样的现象,编码器开始运动时,经过不到半圈就遇到了0信号,此时对应的QPOSCNT清零。这就会出现一个问题,即编码器z信号定义的空间位置和我们以编码器起始转动的0位置不对应。当然,可以将编码器先转到z信号位置,然后再开始运转,但这种起始很明显不符合我们需要的逻辑,并且实现起来并不容易。

为了将这个问题剖析的更清楚,我试着绘制了如下正反转流程图,如果有人发现我哪里理解错了,务必告知,谢谢。

在正转启动的过程中,可以看到POSCNT从0开始累加,当达到某点(这里认为8000)的时候,z信号使得计数器清零,也就是说它实际上没有达到最大线数(这里是9999)。但是在后面的计数过程中,每次都能达到满值。这里将第一次遇到z信号前后的时刻描述为图中“采集POSCNT”,与之对应的“希望Theta”是我实际想要得到的计数。这里整了一个中间变量ThetaShift,在第一次z信号中断生成,并记录了z信号时刻的计数器值,只要在后面将希望Theta赋值为ThetaShift+POSCNT+1就行了。

反转这边我其实有一点迷的,如下图所示,刚开始POSCNT是0,但是在反转的下一线,立刻反向溢出变为最大值(也就是9999),并逐渐递减,直到z信号到达(这里假定减少到了8000),同样为了使得希望Theta能够继续降低,这边同样定义一个与正转一样的ThetaShift,并且连赋值都跟正转一样。但是,神奇的是,Thtea的幅值少了一个加一,为ThetaShift+POSCNT。

这种不和谐的确令人难受,所以我在实际写程序的时候,无论正反转,我都让他不加一,反正就1线的误差。当然好像可以根据正反转区分对待,但是在实验中还不如我统一的效果,所以这里就这样了。如果有人能帮我指出错我,那么谢谢了。

下面这个实验波形,一个通道是位置,一个通道是瞬时转速,是我用手转的。

最后测试一个大约500rpm旋转得到的电机正反转波形,如下图所示。看上去还不错。

4 代码

Eqep寄存器配置部分

	EQep1Regs.QUPRD=QEP_QUPRD;            // 中断周期 Unit Timer at 150 MHz SYSCLKOUT
	EQep1Regs.QDECCTL.bit.QSRC=00;		// 正交计数模式 QEP quadrature count mode
	EQep1Regs.QEPCTL.bit.FREE_SOFT=2;	// 仿真挂起时位置计数器继续运行 Unaffected by emulation suspend
	EQep1Regs.QEPCTL.bit.PCRM=00;		// 在z信号index到达时,位置计数器复位 Position Counter Reset on Unit Time out Event

	EQep1Regs.QEPCTL.bit.UTE=1; 		// 使能单位定时器 Enable unit timer
	EQep1Regs.QEPCTL.bit.QCLM=1; 		// 当单位时间事件发生时,锁存数据
	                                    // Latch on unit time out. Position counter, capture timer and capture period values are latched
	                                    // into QPOSLAT, QCTMRLAT and QCPRDLAT registers on unit time out.
	EQep1Regs.QPOSMAX=0x270F;           // 最大脉冲数减一
	EQep1Regs.QEPCTL.bit.QPEN=1; 		// QEP enable

	EQep1Regs.QDECCTL.bit.SWAP=0;       // 正交时钟交换禁止 改变输入信号的方向0-正常,1-反方向
										// Quadrature-clock inputs are not swapped
	//中断配置步骤-----1 中断使能寄存器
	EQep1Regs.QEINT.bit.UTO=1;			// 单位时间事件中断使能 Unit time out interrupt enable
	EQep1Regs.QEINT.bit.IEL=1;			// 索引时间锁存中断使能

	//中断配置步骤-----2 重映射中断服务函数
	// Interrupts that are used in this example are re-mapped to
	// ISR functions found within this file.
	EALLOW;
	PieVectTable.EQEP1_INT = &eqep1int_isr; //EQEP1
	EDIS;
	//中断配置步骤-----3 连接CPU中断Y
	IER |= M_INT5;
	//中断配置步骤-----4 连接Y中断里的第几位
	PieCtrlRegs.PIEIER5.bit.INTx1 = 1;		// Enable INT5.1 EQEP 优先级9.1

中断函数部分,这里说明一下,因为它这个所有EQEP的中断都会进到一个中断函数里,所以我这边用了两种中断,函数内需要判断一下。

	// 认为启动时遇到第一个z信号之前保持运动方向不变
	if (EQep1Regs.QFLG.bit.IEL==1 && qep1.ThetaSyncFlag==0) // 索引时间锁存中断标志
	{
		// 更新方向
		qep1.MechDirection = EQep1Regs.QEPSTS.bit.QDLF;

		// 因此利用一次z信号索引中断,获得角度偏差
		qep1.MechThetaShift = EQep1Regs.QPOSLAT * QEP_DEG_PER_PULSE;
		// 标志角度差建立,防止再次进入
		qep1.ThetaSyncFlag = 1;

		EQep1Regs.QCLR.bit.IEL = 1;					// Clear interrupt flag
		EQep1Regs.QCLR.bit.INT=1;                    //clear both UTO and INT

		EALLOW;
		EQep1Regs.QEINT.bit.IEL = 0;	// 索引时间锁存中断关闭
		EDIS;

	}

	if (EQep1Regs.QFLG.bit.UTO==1) // 单位时间锁存中断标志
	{
		qep1.TimeCounter++;
		qep1.MechDirection = EQep1Regs.QEPSTS.bit.QDF;	// 更新方向
		qep1.PosCounter = EQep1Regs.QPOSLAT;            // 更新脉冲个数
		// 更新角度位置
		qep1.MechTheta = qep1.MechThetaShift +QEP_DEG_PER_PULSE +qep1.PosCounter*QEP_DEG_PER_PULSE; // 更新角度

		if (qep1.MechDirection==1)
		{
			// 正转
			if (qep1.MechTheta>360.0)
				qep1.MechTheta = qep1.MechTheta - 360.0;
			if (qep1.MechTheta<0.0) 
				qep1.MechTheta = qep1.MechTheta + 360.0;
			// 如果正转情况下当前角度小于上一回合角度,说明正转溢出回零或周期内运动方向变化,这一步的转速不更新
			if (qep1.MechTheta >= qep1.MechThetaPre)
				qep1.MechSpeed = (qep1.MechTheta-qep1.MechThetaPre) / QEP_SAMPLE_TIME / 6.0;
		}
		else
		{
			// 反转
			if (qep1.MechTheta<0.0)
				qep1.MechTheta = qep1.MechTheta + 360.0;
			if (qep1.MechTheta>360.0) 
				qep1.MechTheta = qep1.MechTheta - 360.0;
			// 如果正转情况下当前角度小于上一回合角度,说明正转溢出回零或周期内运动方向变化,这一步的转速不更新
			if (qep1.MechTheta <= qep1.MechThetaPre)
				qep1.MechSpeed = (qep1.MechTheta-qep1.MechThetaPre) / QEP_SAMPLE_TIME / 6.0;
		}
		qep1.MechThetaPre = qep1.MechTheta;
	}

	EQep1Regs.QCLR.bit.IEL=1;				// Clear interrupt flag
	EQep1Regs.QCLR.bit.UTO=1;				// Clear interrupt flag
	EQep1Regs.QCLR.bit.INT=1;               //clear both UTO and INT

	// To receive more interrupts from this PIE group, acknowledge this interrupt
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP5;

哦对了,还有h文件的结构体定义

typedef struct {
			float32 MechTheta;      // 机械角度 (deg)
			float32 MechThetaShift; // 原始角度偏差 (deg)
			float32 MechThetaPre;   // 上一步的机械角度 (deg)
			float32 MechSpeed;      // 机械速度 (rpm)
            Uint32 PosCounter;      // 位置计数器
            Uint32 TimeCounter;		// 单位时间中断次数
            Uint16 MechRevolution;  // 转的圈数
			Uint16 MechDirection;   // 转动方向
			Uint16 ThetaSyncFlag;	// 角度差建立标志位
               }  QEP;

3 小结

这里,我总结了一下NPN集电极开路型的光电编码器,然后介绍了一下永磁伺服电机常用的磁编码器,再后面分析了28335的EQEP模块,最后给出了相关的实验代码。

4 参考文献
  • [1] 增量式编码器信号,https://blog.csdn.net/qlexcel/article/details/84136859
  • [2] M/T法测速,https://blog.csdn.net/abbing/article/details/3960288
  • [3]一文搞懂M法、T法测速原理,https://blog.csdn.net/hzh68128387/article/details/113433495
  • [4] Reference Guide TMS320x2833x, 2823x Enhanced Quadrature Encoder
  • Pulse (eQEP) Module [5] 手把手教你学DSP-基于TMS320F28335

版权声明:本文为CSDN博主「obotisr」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pikaliu/article/details/121916245

生成海报
点赞 0

obotisr

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

利用EQEP实现编码器的位置与转速测量

零 前言 研究了这么久的电机控制,DSP28335的EQEP模块实际上了解的并不是特别多,因为之前做实验都是用的师兄整的代码。所以这回我觉得好好研究一下这玩意,正好我手上现在有一种光电式的&#xff0

基于8051单片机实现电子时钟+数字秒表设计

概述 电子时钟是一种利用数字电路来显示秒、分、时的计时装置,与传统的机械钟相比,它具有走时准确、显 示直观、无机械传动装置等优点,因而得到广泛应用。随着人们生活环境的不断改善和美化,在许

汇编实现LED点亮

汇编点亮一个LED MCS-51单片机 也许C语言人人都会,但使用汇编语言就不一定了。 使用汇编语言点亮一个LED灯 程序: ORG 0000H MAIN:CLR P2.0 LJMP MAINEND 注释如下&#