文章目录[隐藏]
零 前言
研究了这么久的电机控制,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
暂无评论