DSP eQEP正交编码

1 eQEP 介绍

1.1 正交编码器 QEP 简介

光电编码器是集光、机和电技术于一体的数字化传感器,通过光电转换将输

出轴上的机械几何位移量转换成脉冲或者数字量的传感器,可以高精度测量被测

物的转角或直线位移量,是目前应用最多的传感器之一。它具有分辨率高、精度

高、结构简单、体积小、使用可靠、性价比高等优点。在数控机床、机器人、雷

达、光电经纬仪、伺服系统等诸多领域中得到了广泛的应用。典型的光电编码器

主要由码盘、检测光栅、光电转换电路(包括光源、光敏器件、信号转换电路)、

机械部件等组成,如下图所示:

 

一般来说,根据光电编码器产生脉冲的方式不同,可以分为增量式、绝对式

以及复合式 3 大类。按编码器运动部件的方式来分,可以分为旋转式和直线式两 种。由于直线式运动可以借助机械连接转变为旋转式运动,反之亦然。因此,只 有在那些结构形式和运动方式都有利于直线式光电编码器的场合才使用。旋转式 光电编码器容易做成全封闭型式,易于实现小型化,传感长度较长,具有较长的环境适用能力,因而在实际工业生产中应用较广。

增量式光电编码器的特点是每产生一个输出脉冲信号就对应一个增量位移,但是不能通过输出脉冲区别出在哪个位置上的增量。它能够产生与位移增量等值 的脉冲信号,其作用是提供一种对连续位移量离散化或增量化以及位移变化(速度)的传感方法,它是相对于某个基准点的相对位置增量,不能直接检测出轴的 绝对位置信息。一般来说,增量式光电编码器输出 A、B 两相互差 90 度电度角的脉冲信号(即所谓的两组正交输出信号),从而可方便的判断出旋转方向。同时还有用作参考零位的 Z 相标志(指示)脉冲信号,码盘每旋转一周,只发出一个标志信号。标志脉冲通过用来指示机械位置或者对积累量清零。

绝对式光电编码器是利用自然二进制、循环二进制(格雷码)、二-十进制等方式进行光电转换的。绝对式光电编码器与增量式光电编码器不同之处在于圆 盘上透光、不透光的线条图形,绝对光电编码器可有若干编码,根据读出码盘上 的编码,检测绝对位置。它的特点是:可以直接读出角度坐标的绝对值,没有累 计误差,电源断开后位置信息不会丢失,编码器的精度取决于位数,最高运转速 度比增量式光电编码器高。

混合式光电编码器,就是在增量式光电编码器的基础上增加了一组用于检测 永磁伺服电机磁极位置的码道。它输出两组信息:一组信息用于检测磁极位置,带有绝对信息功能,另一组完全同增量式编码器的输出信息。 由于绝对编码器在位置定位方面明显的优于增量式编码器,已经越来越多的 应用在工控定位中。测速需要可以无限累加测量,目前增量型编码器在测速应用 方面仍处于无可取代的主流位置。F28335 中的 eQEP 模块主要针对的是增量型的编码器。

(1)判别电机转速方向的基本原理

增量式编码器一般安装在电机或者其他旋转机构的轴上,在码盘旋转过程中,输出两个型号称为 QEPA 和 QEPB,两路型号相差 90 度,这就是所谓的正交信号,当电机正转时,脉冲信号 A 的相位超前脉冲信号 B 的相位 90 度,此时逻辑电路处理后可形成高电平的方向信号 Dir。当电机反转时,脉冲信号 A 的相位滞后脉冲信号 B 的相位 90 度,此时逻辑电路处理后的方向信号 Dir 为低电平。

因此根据超前和滞后的关系可以确定电机的旋转方向。输出波形如下所示:

 

(2)电机测速的基本原理

可以利用定时器/计数器配合光电编码器的输出脉冲信号来测量电机的转速。具体的测速方法有 M 法、T 法和 M/T 法 3 种。

M 法又称为测频法,其测速原理是在规定的检测时间 Tc 内,对光电编码器输出的脉冲信号计数的测速方法,例如光电编码器是 N 线的,则每旋转一周可以有4N 个脉冲,因为两路脉冲的上升沿与下降沿正好是编码器信号 4 倍频。现在假设检测时间是 Tc,计数器记录的脉冲数是 M1,则电机每分钟的转速为:

n=15M1/NTc

在实际的测量中,时间 Tc 内的脉冲个数不一定正好是整数,而且存在最大半个脉冲的误差。如果要求测量的误差小于规定的范围,比如说是小于百分之一,那么 M1 就应该大于 50.在一定的转速下要增大检测脉冲数 M1 以减小误差,可以增大检测时间 Tc,但考虑到实际的应用中检测时间很短,例如伺服系统中的测 量速度用于反馈控制,一般应在 0.01s 以下。由此可见,减小测量误差的方法是采用高线数的光电编码器。M 法测速适用于测量高转速,因为对于给定的光电编码器线数 N,测量时间 Tc 条件下,转速越高,计数脉冲 M1 越大,误差也就越大。

T 法也称之为测周法,该测速方法是在一个脉冲周期内对时钟信号脉冲进行计数的方法。例如时钟频率为 Fclk,计数器记录的脉冲数为 M2,光电编码器是N 线的,每周输出 4N 个脉冲,那么电机的每分钟的转速为:

n=15Fclk/NM2

为了减小误差,希望尽可能的记录较多的脉冲数,因此 T 法测速适用于低速运行的场合。但转速太低,一个编码器输出脉冲的时间太长,时钟脉冲会超过计数器最大计数值而产生溢出。另外,时间太长也会影响控制的快速性。与 M 法测速一样,选用线数较多的光电编码器可以提高对电机转速测量的快速性与进度。

M/T 法测速是将 M 法和 T 法两种方法结合在一起使用,在一定的时间范围内同时对光电编码器输出的脉冲个数 M1 和 M2 进行计数,则电机每分钟的转速为:

n=15M1Fclk/NM2

实际工作时,在固定的 Tc 时间内对光电编码器的脉冲计数,在第一个光电编码器上升沿定时器开始定时,同时开始记录光电编码器和时钟脉冲数,定时器定时 Tc 时间到,对光电编码器的脉冲数停止计数,而在下一个光电编码器的上升沿到来时,时钟脉冲才停止记录。采用 M/T 法既具有 M 法测速的高速优点,又具有 T 法测速的低速的优点,能够覆盖较广的转速范围,测量的精度也较高在 电机的控制中有着十分广泛的应用。

1.2 F28335 eQEP 模块简介

F28335 有两路 eQEP 模块,每个模块有 4 个管脚,分别是:QEPA/XCLK、QEPB/XDIR、eQEPI 和 QPES。其中前两个引脚被使用在正交时钟模式或者直接计数模式。

(1)正交时钟模式

正交编码器提供两路相位差为 90 度的脉冲,相位关系决定了电机旋转方向信息,脉冲的个数可以决定电机的绝对位置信息。超前或者顺时钟旋转时,A 路信号超前 B 路信号,滞后或者逆时针旋转时,B 路信号超前 A 路信号。正交编码器使用这两路输入引脚可以产生正交时钟和方向信号。

(2)直接计数模式

在直接计数模式中,方向和时钟信号直接来自外部,此时 QEPA 引脚提供时钟输入,QEPB 引脚提供方向输入。

后两个引脚 eQEPI 是索引或者起始标记脚,正交编码器使用索引信号来确定一个绝对的起始位置,此引脚直接与正交编码器的索引输出端相连,当此信号到来时,可以将位置计数器复位清零,也可以初始化或者锁存位置计数器的值。

QEPS 是锁存输入引脚,锁存引脚输入的主要作用是当规定时间信号到来时,初始或者锁存位置计数器的值,该引脚通常和传感器或者限制开关连接,用于通知电机是否达到预定位置。

eQEP 主要包含以下几个功能单元:

--通过 GPIO MUX 寄存器编程锁定 QEPA 或者 QEPB 功能。

--正交解码单元(QDU)。

--位置计数器和位置计算控制单元(PCCU)。

--正交边沿捕获单元,用于低速测量(QCAP)。

--用于速度/频率测量的时基单元(UTIME)。

--用于检测的看门狗模块。

QEP 模块内部结构图如下:

 

下面我们就来介绍 eQEP 结构框图,通过介绍,大家就能够清楚了解它的功能及使用。

(1)正交解码单元(QDU)

正交解码单元结构框图如下:

 

1.位置计数器输入模式

在正交计数模式下,正交解码模块产生位置计数器时钟信号和方向信号,位置计数器的计数模式由 QDECCTL 寄存器中的 QSRC 位决定,主要有如下 4 种模式:

--正交-计数模式

--直接-计数模式

--向上-计数模式

--向下-计数模式

下面分别介绍下这几种模式:

A.正交-计数模式

在正交-计数模式下,正交解码器产生方向信号和时钟信号送给位置计数器。通过确定 QEPA 和 QEPB 两个脉冲信号哪一个超前,来解码出旋转方向逻辑,并且将此方向逻辑更新到 QEPSTS 的 QDF 位。下图是方向解码逻辑的真值表以及状态机。

 

TMS320F2833X 系列 DSP 中的 QEP 还有相位错误检测机制,当 QEPA 和 QEPB信号同步到来时,QEP 中的相位错误标志会置位,而且会申请中断。 QEP 中的解码模块会对 QEPA 和 QEPB 脉冲的上升沿以及下降沿进行计数,所以最后解码的时钟频率将会是实际输入 QEPA 或者 QWPB 的 4 倍。

相位错误标识:在正常操作条件下,正交输入 QEPA 与 QEPB 在相位上相差 90度,边沿信号是不会同时到达的,当同时检测到两者的边沿信号时,那么 QFLG 寄存器的相位错误标志位(PHE)将置位。上图中状态机的虚线就表示产生的相 位错误的可能。

反向计数:在正常正交计数模式操作时,QEPA 输入送到正交解码器的 QA 输入,QEPB 输入送到正交解码器的 QB 输入。反向计数通过设置 QDECCTLL 寄存器的 SWAP 位被使能,此时正交解码器的输入取反,从而计数方向也取反。

B.方向计数模式

有些位置编码器提供了方向和时钟输出代替正交输出,如下图所示,这种情况,可以使用方向计数模式。此时 QEPA 只能作为时钟输入,QEPB 只能作为方向输入。如果方向为高时,那么计数器会在 QEPA 输入的上升沿时递增计数,当方向为低时,那么计数器会在 QEPA 输入的上升沿自动递减计数。

C.递增计数模式

计数器的方向信号被硬件规定为递增计数,此时位置计数器根据 QDECCTL 中的 XCR 位规定,对 QEPA 信号计数或者 2 倍关系计数。

D,递减计数模式

计数器的方向信号被硬件规定为递增计数,此时位置计数器根据 QDECCTL 中的 XCR 位规定,对 QEPA 信号计数或者 2 倍关系计数。

2.QEP 输入极性选择

每个 QEP 输入都可以通过 QDECCTL 寄存器的 8~15 位决定极性。

3.位置比较同步输出

增强型的 QEP 包括一个位置比较单元,主要用于产生位置比较同步信号。当位置计数器的值与位置比较寄存器 QPOSCMP 的值相等时,可以把 QEPI 或者 QEPS配置为输出引脚,用于产生同步信号输出。具体配置可以查看QDECCTL[SOEN]和 QDECCTL[SPSEL]寄存器位。

(2)位置计数和控制单元(PCCU)

位置计数和控制单元提供了两个配置寄存器 QEPCTL 和 QPOSCTL。用于设置位置计数器操作模式、位置计数器初始化/锁存模式和产生同步信号的位置比较逻 辑。

1.位置计数操作模式

位置计数器的值可以有不同的方式进行捕获。在有些系统中,位置计数器连续累加计数,位置计数器按照当前的参考位置提供计数信息。比如在安装正交编码器的打印机中,通过将打印机头移动到起始位置从而使计数器复位,即打印机头在起始位置时为参考位置。而在有些系统中,位置计数器通过索引脉冲实现每次索引脉冲到来时,位置计数器就自动复位,位置计数器提供了相对于索引脉冲的相对位置信息。位置计数器可以配置为如下几种模式:

--在索引事件到来时,位置计数器复位。

--在计数值达到设定的最大值时,位置计数器复位。

--第一个索引事件到来时,位置计数器复位。

--在定时事件到达时,位置计数器复位。

上述所有的操作模式,位置计数器都会在上溢时复位到 0,在下溢时复位到

QPOSMAX 最大值。

①在索引事件到来时,位置计数器复位[QEPCTL[PCRM]=00]。

如果索引事件发生在正向运动时,那么位置计数器在下一个 QEP 时钟复位到0,如果索引事件发生在反向运动时,那么位置计数器在下一个 QEP 时钟复位到QPOSMAX 最大设定值。

第一个索引标识为在第一个索引边沿后的正交边沿。EQEP 外设记录了第一个 索引标识(QEPSTS[FIMF])的发生以及第一个索引事件标识方向(QEPSTS[FIDF]), 它也记录了第一个索引标识的正交边沿,从而可使这个相关的正交转换用于索引事件的复位操作。比如在正向运动过程中,第一次复位操作发生在 QEPB 的下降沿,那么所有后来的复位必须分配在正向运动中的 QEPB 的下降沿或者反向运动中的 QEPB 的上升沿,如下图所示:

 

在每次索引事件发生时,位置计数器的值锁存到 QPOSIL 寄存器中吗,方向信号更新到 QEPSTS[QDLF]中。在这种情况下,如果锁存的值不是 0 或者 QPOSMAX 值时,位置计数错误标志和错误中断标志会置 1。在索引事件被标识时,位置计 数器错误标志(QEPSTS[PCEF])被更新并且错误中断标识(QFLG[PCE])将被置位,只有通过软件才能清楚这两个错误标识。

在这种模式下,索引事件锁存配置 QEPCTL[IEL]位被忽略,位置计数器错误标识以及中断标识只有在索引事件复位模式下产生。

②在计数值达到设定最大值时,位置计数器复位[QEPCTL[PCRM]=01]。

如果位置计数器达到 QPOSMAX 时,那么在正向运动过程中,位置计数器在下

一个 EQEP 时钟时置 0,并且位置计数器上溢标识位置位;如果位置计数器等于 0,而且在反向运动的条件下,那么位置计数器在 QEP 的下一个时钟复位到 QPOSMAX,且位置计数器下溢标识位置位,如下图所示:

 

③第一个索引事件发生时,位置计数器复位[QEPCTL[PCRM]=10]。

如果第一个索引事件发生在正向运动时,位置计数器会在下一个 QEP 时钟复位为 0,;如果第一个索引事件发生在反向运动时,位置计数器会在下一个 QEP时钟复位为 QPOSMAX。注意只有在第一个索引事件发生时,位置计数器才会复位,而后面的索引事件不会使位置计数器复位。

④在定时(单位时间)事件达到时,位置计数器复位[QEPCTL[PCRM]=11]。

在此种模式下,事件发生时 QPOSCNT 的值会锁存到 QPOSLAT 寄存器中,然后位置计数器会复位到 0 或者 QPOSMAX,取决于 QDECCTL[QSRC]设置的方向模式。这种模式对测量频率很有效。

2.位置计数器锁存

QEP 的索引和标记输入引脚输入事件发生时,将位置计数器的值锁存到 QPOSILAT 和 QPOSSLAT 中。

A.索引事件锁存

在某些应用中,并不要求在每次索引事件到来时复位位置计数器,而是要求在 32 位模式(QEPCTL[PCRM]=01 和 QEPCTL[PCRM]=10)下,操作位置计数器。

在这种情况下,EQEP 的位置计数器可以在如下事件配置锁存并且在每次索引事件标识时,方向信息被记录到 QEPSTS[QDLF]中。

①上升沿锁存(QEPCTLP[IEL]=01)

②下降沿锁存(QEPCTLP[IEL]=10)

③索引事件标识锁存(QEPCTLP[IEL]=11)

这个作为错误检查机制很有用,可以用来检查位置计数器在索引事件之间能否正确累加。比如,1000 线的编码器当按照相同方向运动时,在两个索引事件之间应当有 4000 个脉冲,进行累加 4000 次。

位置计数器的值锁存到 QPOSILAT 寄存器中时,索引事件中断标志位 (QFLG[IEL])被置位。当 QEPCTL=0 时,索引事件锁存配置位(QEPCTZ[IEL])被忽略。

上升沿锁存(QEPCTLP[IEL]=01):在输入索引事件的每次上升沿到来时,位置计数器的值(QPOSCNT)锁存到 QPOSILAT 寄存器中。

下降沿锁存(QEPCTLP[IEL]=10):在输入索引事件的每次下降沿到来时, 位置计数器的值(QPOSCNT)锁存到 QPOSILAT 寄存器中。

索引事件标志锁存/软件索引标识(QEPCTLP[IEL]=11):第一个索引标识在第 一 个 索 引 边 沿 后 的 正 交 边 沿 。 EQEP 外 设 记 录 了 第 一 个 索 引 标 识 (QEPSTS[FIMF])的发生以及第一个索引事件标识方向(QEPSTS[FIDF]),它也记录了第一个索引标识的正交边沿,从而可使这个相关的正交转换可以用于位置 计数器锁存(QEPCTL[IEL]=11)。

下图为应用索引事件标识的位置计数器锁存时序图

 

B,选择事件锁存

位置计数器的值在选择输入信号的上升沿被锁存到 QPOSSLAT 寄存器中,并且 QEPCTL[SE1]位被清零。如果 QEPCTL[SE1]位被置位,位置计数器的值在正向运动时,选择输入信号的上升沿进行锁存,反向运动时,选择输入信号的下降沿进行锁存。如下图所示:

 

当位置计数器的值锁存到寄存器后,选择事件锁存中断标志(QFLG[SE1]) 被置位。

3,位置计数器初始化

位置计数器可以由以下事件进行初始化:

--索引事件

--选择事件

--软件初始化

①索引事件初始化(IEI):QEPI 索引输入在索引输入上升沿或者下降沿的时候可以触发位置计数器的初始化。如果 QEPCTL[IEI]=10,那么位置计数器在索 引 输 入 上 升 沿 时 会 初 始 化 为 寄 存 器 QPOSINIT 的 值 , 相 反 , 如 果 QEPCTL[IEI]=11,位置计数器的值在输入索引的下降沿初始化。当位置计数器初 始化为 QPOSINIT 的值后,索引事件初始化中断位(QFLG[IEI])被置位。

②选择事件初始化(SEI):如果 QEPCTL[IEI]=10,那么位置计数器在选择输入上升沿时会初始化为寄存器 QPOSINIT 的值。如果 QEPCTL[IEI]=11,位置计数器的值在正转时会在选择输入的上升沿初始化为 QPOSINIT 的值,否则在反转时,在选择输入的下降沿初始化。当位置计数器初始化为 QPOSINIT 的值后,选择事件初始化中断位(QFLG[IEI])被置位。

③软件初始化(SWI):将 QEPCTL(SWI)位设置为 1 时,位置计数器由软件初始化,而 QEPCTL(SWI)位在初始化后会自动清除。

(3)位置比较单元

QEP 模块包含一个位置比较单元,用于产生同步信号输出或者产生匹配中断。

其原理框图如下

 

位置比较寄存器 QPOSCMP 有影子寄存器,影子寄存器模式可以通过 QPOSCTL[PSSHDW]使能或者禁止。如果影子寄存器模式禁止时,CPU 直接写到有效位置比较寄存器即可。

在影子寄存器模式使能时,可以配置位置比较单元(QPOSCTL[PCLOAD])位以将影子寄存器内的值在下列事件来临时装入有效寄存器并且在装载完成以后,会产生位置比较就绪中断位(QFLG[PCR])。

--比较匹配装载

--位置计数器清零装载

当位置计数器的值(QPOSCNT)与工作位置比较寄存器(QPOSCMP)的值匹配时,位置比较位(QFLG[PCM])被置位,并且会产生位置比较同步可调宽脉冲以触发外部设备。

例如:假设 QPOSCM=2,在正向计数时,位置比较单元在 EQEP 位置计数器的

1~2 转换过程中产生一个位置比较事件;而在反向计数时,位置计数器 3~2 转换过程中产生一个位置比较事件,如下图所示:

 

位置比较的脉冲扩展逻辑在位置比较匹配时可以输出一个可编程的位置比较同步脉冲,当前一个位置比较脉冲仍在工作并且产生新的位置比较匹配时,脉冲扩展器会根据新的位置比较事件延伸脉冲宽度,如下图所示:

 

(4)边沿捕获单元

QEP 模块包括一个集成的边沿捕获单元来测量转速,如下图所示:

 

其中针对慢速和高速,此单元提供了两种方式进行计算。

①低速测量公式:

 

低速测量思想就是在设定编码器脉冲数时,计量在此脉冲数内所消耗的时间,从而计算出速度,即 T 法。具体工作过程如下:

捕获时钟(QCTMR)以系统时钟分频后的时基作基准运行,在每个单位位置事件发生时,QCTMR 的值会自动加载到捕获周期寄存器 QCPRD 中,之后捕获时钟

自动清零,同时 QEPSTS 中的 UPEVNT 标志位会置 1,表明有新值锁存到 QCPRD 寄存器,通知 CPU 进行操作。下图为低速速度测量的时序,此种测量方式,只有在下面两个条件满足时才是正确的。

 

条件 1:单元位置事件之间不能超过捕获时钟的最大值,不能超过 65535。

条件 2:没有换向发生。

如果上述条件不满足,定时器上溢时,捕获单元的错误标志位 QEPSTS[COEF]会置 1,如果两个单元位置事件发生方向变化,则状态寄存器 QEPSTS[COEF]错误标识被置位。所以该种方式只适用于低速的时候。

②高速测量公式:

 

高速测量思想是在设定的单位时间里面,计量采集到的脉冲数,即可确定速度值,即 M 法。

捕获时钟寄存器和捕获周期寄存器可以配置成如下事件发生时,会把值锁存到 QCTMRLAT 和 QPPRDLAT 中。

事件 1:CPU 读 QPOSCNT 寄存器时。

事件 2:单位时间事件发生时。

如果 QEPCTL[QCLM]位被清零了,那么当 CPU 读取位置计数器时,捕获时钟和捕获周期寄存器的值分别被锁存到 QCTMRLAT 和 QPPRDLAT 寄存器中。EQEP 边沿捕获单元时序图如下:

 

如果 QEPCTL[QCLM]位置 1,那么当单位时间事件发生时,位置计数器、捕获时钟寄存器和捕获周期寄存器的值分别锁存到 QPOSLAT、QCTMRLAT 和 QCPRDLAT中。

(5)eQEP 看门狗单元

QEP 模块包括一个 16 位的看门狗计数器,用来监测正交编码器脉冲状态。若有正交脉冲到来,看门狗计数器可以复位,如果正交编码脉冲没有到来,当看门狗计数器的值与其周期寄存器的值匹配时,可以产生中断信号给 CPU。该看门狗单元可以通过软件使能或者禁止。看门狗模块原理框图如下:

 

(6)eQEP 定时器基准单元

QEP 模块还包括了一个 32 位定时器(QUTMR),由 SYSCLKOUT 提供时钟,可以产生用于速度计算的周期性中断。当定时器(QUTMR)与周期寄存器(QUPRD)匹配时,单位超时中断(QFLG[UTO])被置位。

一个单位事件超时时,EQEP 可以配置为锁存位置计数器、捕捉定时器和捕捉周期值,从而用这些锁存的值来计算速度。定时器的原理框图如下:

 (7)eQEP 中断结构

EQEP 模块中断的工作原理框图如下:

 QEP 一共可以产生 11 个中断事件,分别是 PCE、PHE、QDC、WTO、PCU、PCO、 PCR、PCM、SEL、IEL、UTO。这些中断可以通过中断控制寄存器 QEINT 来设置使能或者禁止。如果每个中断都使能,那么中断源会产生中断脉冲送给 PIE 模块,进而送给 CPU。这些中断标志可以通过中断清除寄存器 QCLR 来清除。而且还可以通过中断强制寄存器来软件强制中断事件,这样对系统测试比较有利。

1.3 eQEP 相关寄存器介绍

查阅对应芯片手册

2 eQEP 配置步骤

(1)使能 eQEP1 外设时钟

要使用 eQEP1 外设则需开启相应时钟,开启 eQEP1 外设时钟代码如下:

EALLOW; // This is needed to write to EALLOW protected registers

SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 1; // eQEP1

EDIS; 

(2)初始化 GPIO 为 eQEP1 功能,即选择 GPIO 复用功能

(3)eQEP1 外设相关参数设置,包括 QEP 计数模式、自由运行、QEP 位置计

数器在索引事件复位、单位事件使能、QEP 模块使能等。

要想使用 eQEP1 模块检测外部信号脉冲频率或者电机旋转速度,除了使能

eQEP1 时钟和配置 GPIO 外,还需要对其进行寄存器相关参数的设置。比如 QEP

计数模式、自由运行、QEP 位置计数器在索引事件复位、单位事件发生时,加载

计数值、初始化最大位置值、单位位置分频、单位事件使能、QEP 模块使能。下

面我们就以本章 eQEP1 初始化配置为例进行介绍,我们开发板上使用的是eQEP1。

(4)编写中断函数。

3 硬件设计

本实验使用到硬件资源如下:

(1)D1 指示灯

(2)ePWM1A

(3)eQEP1

4 软件设计

(1)eQEP_freqcal


#if (CPU_FRQ_150MHZ)
  #define FREQCAL_DEFAULTS {\
		234,200,10000,0,0,\
		0,0,0,\
		(void (*)(long))FREQCAL_Init,\
        (void (*)(long))FREQCAL_Calc }
#endif
#if (CPU_FRQ_100MHZ)
  #define FREQCAL_DEFAULTS {\
		313,200,10000,0,0,\
		0,0,0,\
		(void (*)(long))FREQCAL_Init,\
        (void (*)(long))FREQCAL_Calc }
#endif

void  FREQCAL_Init(void)
{
    #if (CPU_FRQ_150MHZ)
	  EQep1Regs.QUPRD=1500000;			// Unit Timer for 100Hz at 150MHz SYSCLKOUT
	#endif
    #if (CPU_FRQ_100MHZ)
	  EQep1Regs.QUPRD=1000000;			// Unit Timer for 100Hz at 100MHz SYSCLKOUT
	#endif

	EQep1Regs.QDECCTL.bit.QSRC=2;		// Up count mode (freq. measurement)
	EQep1Regs.QDECCTL.bit.XCR=0;        // 2x resolution (cnt falling and rising edges)

	EQep1Regs.QEPCTL.bit.FREE_SOFT=2;
	EQep1Regs.QEPCTL.bit.PCRM=00;		// QPOSCNT reset on index evnt
	EQep1Regs.QEPCTL.bit.UTE=1; 		// Unit Timer Enable
	EQep1Regs.QEPCTL.bit.QCLM=1; 		// Latch on unit time out
	EQep1Regs.QPOSMAX=0xffffffff;
	EQep1Regs.QEPCTL.bit.QPEN=1; 		// QEP enable

	#if (CPU_FRQ_150MHZ)
	  EQep1Regs.QCAPCTL.bit.UPPS=2;   	// 1/4 for unit position at 150MHz SYSCLKOUT
	#endif
	#if (CPU_FRQ_100MHZ)
	  EQep1Regs.QCAPCTL.bit.UPPS=3;   	// 1/8 for unit position at 100MHz SYSCLKOUT
	#endif

	EQep1Regs.QCAPCTL.bit.CCPS=7;		// 1/128 for CAP clock
	EQep1Regs.QCAPCTL.bit.CEN=1; 		// QEP Capture Enable

}

void FREQCAL_Calc(FREQCAL *p)
{
     unsigned long tmp;
   	 _iq newp,oldp;


//**** Freq Calcultation using QEP position counter ****//
// Check unit Time out-event for speed calculation:
// Unit Timer is configured for 100Hz in INIT function

// For a more detailed explanation of the calculation, read
// the description at the top of this file

	if(EQep1Regs.QFLG.bit.UTO==1)                  // Unit Timeout event
	{
		/** Differentiator	**/
	 	newp=EQep1Regs.QPOSLAT;                    // Latched POSCNT value
		oldp=p->oldpos;

    	if (newp>oldp)
      		tmp = newp - oldp;                     // x2-x1 in v=(x2-x1)/T equation
    	else
      		tmp = (0xFFFFFFFF-oldp)+newp;

		p->freq_fr = _IQdiv(tmp,p->freqScaler_fr); // p->freq_fr = (x2-x1)/(T*10KHz)
		tmp=p->freq_fr;

		if (tmp>=_IQ(1))          // is freq greater than max freq (10KHz for this example)?
	 		p->freq_fr = _IQ(1);
		else
	 		p->freq_fr = tmp;

		p->freqhz_fr = _IQmpy(p->BaseFreq,p->freq_fr); 	// Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
		                                                // p->freqhz_fr = (p->freq_fr)*10kHz = (x2-x1)/T

		// Update position counter
    	p->oldpos = newp;
		//=======================================

		EQep1Regs.QCLR.bit.UTO=1;					// Clear interrupt flag
	}

//**** Freq Calcultation using QEP capture counter ****//
	if(EQep1Regs.QEPSTS.bit.UPEVNT==1)              // Unit Position Event
	{
		if(EQep1Regs.QEPSTS.bit.COEF==0)            // No Capture overflow
			tmp=(unsigned long)EQep1Regs.QCPRDLAT;
		else							            // Capture overflow, saturate the result
			tmp=0xFFFF;

		p->freq_pr = _IQdiv(p->freqScaler_pr,tmp);  // p->freq_pr = X/[(t2-t1)*10KHz]
		tmp=p->freq_pr;

		if (tmp>_IQ(1))
	 		p->freq_pr = _IQ(1);
		else
	 		p->freq_pr = tmp;

		p->freqhz_pr = _IQmpy(p->BaseFreq,p->freq_pr); 	// Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
	                                                    // p->freqhz_pr =( p->freq_pr)*10kHz = X/(t2-t1)
		EQep1Regs.QEPSTS.all=0x88;					    // Clear Unit position event flag
												     	// Clear overflow error flag
	}


}
/*-----------------------------------------------------------------------------
Define the structure of the FREQCAL Object
-----------------------------------------------------------------------------*/
typedef struct {
                Uint32 freqScaler_pr;   // Parameter : Scaler converting 1/N cycles to a GLOBAL_Q freq (Q0) - independently with global Q
                Uint32 freqScaler_fr;   // Parameter : Scaler converting 1/N cycles to a GLOBAL_Q freq (Q0) - independently with global Q
                Uint32 BaseFreq;        // Parameter : Maximum Freq
                _iq freq_pr;            // Output :  Freq in per-unit using capture unit
				int32 freqhz_pr;        // Output: Freq in Hz, measured using Capture unit
                Uint32 oldpos;
                _iq freq_fr;            // Output : Freq in per-unit using position counter
                int32 freqhz_fr; 	    // Output: Freq in Hz, measured using Capture unit
                void (*init)();     	// Pointer to the init funcion
                void (*calc)();    		// Pointer to the calc funtion
                }  FREQCAL;

void InitEQep1Gpio(void)
{
   EALLOW;

/* Enable internal pull-up for the selected pins */
// Pull-ups can be enabled or disabled by the user.
// This will enable the pullups for the specified pins.
// Comment out other unwanted lines.

//    GpioCtrlRegs.GPAPUD.bit.GPIO20 = 0;   // Enable pull-up on GPIO20 (EQEP1A)
//    GpioCtrlRegs.GPAPUD.bit.GPIO21 = 0;   // Enable pull-up on GPIO21 (EQEP1B)
//    GpioCtrlRegs.GPAPUD.bit.GPIO22 = 0;   // Enable pull-up on GPIO22 (EQEP1S)
//    GpioCtrlRegs.GPAPUD.bit.GPIO23 = 0;   // Enable pull-up on GPIO23 (EQEP1I)

    GpioCtrlRegs.GPBPUD.bit.GPIO50 = 0;   // Enable pull-up on GPIO50 (EQEP1A)
    GpioCtrlRegs.GPBPUD.bit.GPIO51 = 0;   // Enable pull-up on GPIO51 (EQEP1B)
    GpioCtrlRegs.GPBPUD.bit.GPIO52 = 0;   // Enable pull-up on GPIO52 (EQEP1S)
    GpioCtrlRegs.GPBPUD.bit.GPIO53 = 0;   // Enable pull-up on GPIO53 (EQEP1I)


// Inputs are synchronized to SYSCLKOUT by default.
// Comment out other unwanted lines.

//    GpioCtrlRegs.GPAQSEL2.bit.GPIO20 = 0;   // Sync to SYSCLKOUT GPIO20 (EQEP1A)
//    GpioCtrlRegs.GPAQSEL2.bit.GPIO21 = 0;   // Sync to SYSCLKOUT GPIO21 (EQEP1B)
//    GpioCtrlRegs.GPAQSEL2.bit.GPIO22 = 0;   // Sync to SYSCLKOUT GPIO22 (EQEP1S)
//    GpioCtrlRegs.GPAQSEL2.bit.GPIO23 = 0;   // Sync to SYSCLKOUT GPIO23 (EQEP1I)

    GpioCtrlRegs.GPBQSEL2.bit.GPIO50 = 0;   // Sync to SYSCLKOUT GPIO50 (EQEP1A)
    GpioCtrlRegs.GPBQSEL2.bit.GPIO51 = 0;   // Sync to SYSCLKOUT GPIO51 (EQEP1B)
    GpioCtrlRegs.GPBQSEL2.bit.GPIO52 = 0;   // Sync to SYSCLKOUT GPIO52 (EQEP1S)
    GpioCtrlRegs.GPBQSEL2.bit.GPIO53 = 0;   // Sync to SYSCLKOUT GPIO53 (EQEP1I)

/* Configure eQEP-1 pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be eQEP1 functional pins.
// Comment out other unwanted lines.

//    GpioCtrlRegs.GPAMUX2.bit.GPIO20 = 1;   // Configure GPIO20 as EQEP1A
//    GpioCtrlRegs.GPAMUX2.bit.GPIO21 = 1;   // Configure GPIO21 as EQEP1B
//    GpioCtrlRegs.GPAMUX2.bit.GPIO22 = 1;   // Configure GPIO22 as EQEP1S
//    GpioCtrlRegs.GPAMUX2.bit.GPIO23 = 1;   // Configure GPIO23 as EQEP1I

    GpioCtrlRegs.GPBMUX2.bit.GPIO50 = 1;   // Configure GPIO50 as EQEP1A
    GpioCtrlRegs.GPBMUX2.bit.GPIO51 = 1;   // Configure GPIO51 as EQEP1B
    GpioCtrlRegs.GPBMUX2.bit.GPIO52 = 1;   // Configure GPIO52 as EQEP1S
    GpioCtrlRegs.GPBMUX2.bit.GPIO53 = 1;   // Configure GPIO53 as EQEP1I


    EDIS;
}

FREQCAL freq=FREQCAL_DEFAULTS;

interrupt void prdTick(void);


#if (CPU_FRQ_150MHZ)
  #define CPU_CLK   150e6
#endif
#if (CPU_FRQ_100MHZ)
  #define CPU_CLK   100e6
#endif
#define PWM_CLK   5e3                 // If diff freq. desired, change freq here.
#define SP        CPU_CLK/(2*PWM_CLK)
#define TBCTLVAL  0x200E              // Up-down cnt, timebase = SYSCLKOUT


void EPwm1Setup(void)
{
    InitEPwm1Gpio();
	EPwm1Regs.TBSTS.all=0;
	EPwm1Regs.TBPHS.half.TBPHS=0;
	EPwm1Regs.TBCTR=0;

	EPwm1Regs.CMPCTL.all=0x50;        // Immediate mode for CMPA and CMPB
	EPwm1Regs.CMPA.half.CMPA =SP/2;
	EPwm1Regs.CMPB=0;

	EPwm1Regs.AQCTLA.all=0x60;        // EPWMxA = 1 when CTR=CMPA and counter inc
	                                  // EPWMxA = 0 when CTR=CMPA and counter dec
	EPwm1Regs.AQCTLB.all=0;
	EPwm1Regs.AQSFRC.all=0;
	EPwm1Regs.AQCSFRC.all=0;

	EPwm1Regs.DBCTL.all=0xb;          // EPWMxB is inverted
	EPwm1Regs.DBRED=0;
	EPwm1Regs.DBFED=0;

	EPwm1Regs.TZSEL.all=0;
	EPwm1Regs.TZCTL.all=0;
	EPwm1Regs.TZEINT.all=0;
	EPwm1Regs.TZFLG.all=0;
	EPwm1Regs.TZCLR.all=0;
	EPwm1Regs.TZFRC.all=0;

	EPwm1Regs.ETSEL.all=9;            // Interrupt when TBCTR = 0x0000
	EPwm1Regs.ETPS.all=1;	          // Interrupt on first event
	EPwm1Regs.ETFLG.all=0;
	EPwm1Regs.ETCLR.all=0;
	EPwm1Regs.ETFRC.all=0;

	EPwm1Regs.PCCTL.all=0;

	EPwm1Regs.TBCTL.all=0x0010+TBCTLVAL;			// Enable Timer
	EPwm1Regs.TBPRD=SP;

	EALLOW;  // This is needed to write to EALLOW protected registers
	PieVectTable.EPWM1_INT= &prdTick;
	EDIS;

	IER |= M_INT3;
	PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
	EINT;   // Enable Global interrupt INTM
	ERTM;   // Enable Global realtime interrupt DBGM

}



void EQEP1_Init(void)
{
	EALLOW;  // This is needed to write to EALLOW protected registers
	SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 1;  // eQEP1
	EDIS;

	InitEQep1Gpio();

	EPwm1Setup();

	freq.init();   // Initializes eQEP for frequency calculation in
						// FREQCAL_Init(void)function in Example_EPwmSetup.c
}

interrupt void prdTick(void) // Interrupts once per ePWM period
{
   freq.calc(&freq); // Checks for event and calculates frequency in FREQCAL_Calc(FREQCAL *p)
                     // function in Example_EPwmSetup.c
   // Acknowledge this interrupt to receive more interrupts from group 1
   PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
   EPwm1Regs.ETCLR.bit.INT=1;
}
void main()
{
	Uint16 i=0;


	InitSysCtrl();
	InitPieCtrl();
	IER = 0x0000;
	IFR = 0x0000;
	InitPieVectTable();

	LED_Init();
	TIM0_Init(150,200000);//200ms
	UARTa_Init(4800);

	EQEP1_Init();

	while(1)
	{

	}
}

 (2)eQEP_pos_speed

#if (CPU_FRQ_150MHZ)
  #define POSSPEED_DEFAULTS {0x0, 0x0,0x0,0x0,0x0,16776,2,0,0x0,\
        94,0,6000,0,\
        0,0,0,\
        (void (*)(long))POSSPEED_Init,\
        (void (*)(long))POSSPEED_Calc }
#endif
#if (CPU_FRQ_100MHZ)
  #define POSSPEED_DEFAULTS {0x0, 0x0,0x0,0x0,0x0,16776,2,0,0x0,\
        63,0,6000,0,\
        0,0,0,\
        (void (*)(long))POSSPEED_Init,\
        (void (*)(long))POSSPEED_Calc }
#endif

POSSPEED qep_posspeed=POSSPEED_DEFAULTS;
Uint16 Interrupt_Count = 0;

interrupt void prdTick(void);


#if (CPU_FRQ_150MHZ)
  #define CPU_CLK   150e6
#endif
#if (CPU_FRQ_100MHZ)
  #define CPU_CLK   100e6
#endif

#define PWM_CLK   5e3              // 5kHz (300rpm) EPWM1 frequency. Freq. can be changed here
#define SP        CPU_CLK/(2*PWM_CLK)
#define TBCTLVAL  0x200E           // up-down count, timebase=SYSCLKOUT


void EPwm1Setup(void)
{
    InitEPwm1Gpio();

    EALLOW;
	GpioCtrlRegs.GPADIR.bit.GPIO4 = 1;    // GPIO4 as output simulates Index signal
	GpioDataRegs.GPACLEAR.bit.GPIO4 = 1;  // Normally low
	EDIS;

	EPwm1Regs.TBSTS.all=0;
	EPwm1Regs.TBPHS.half.TBPHS =0;
	EPwm1Regs.TBCTR=0;

	EPwm1Regs.CMPCTL.all=0x50;     // immediate mode for CMPA and CMPB
	EPwm1Regs.CMPA.half.CMPA=SP/2;
	EPwm1Regs.CMPB=0;

	EPwm1Regs.AQCTLA.all=0x60;     // CTR=CMPA when inc->EPWM1A=1, when dec->EPWM1A=0
	EPwm1Regs.AQCTLB.all=0x09;     // CTR=PRD ->EPWM1B=1, CTR=0 ->EPWM1B=0
	EPwm1Regs.AQSFRC.all=0;
	EPwm1Regs.AQCSFRC.all=0;

	EPwm1Regs.TZSEL.all=0;
	EPwm1Regs.TZCTL.all=0;
	EPwm1Regs.TZEINT.all=0;
	EPwm1Regs.TZFLG.all=0;
	EPwm1Regs.TZCLR.all=0;
	EPwm1Regs.TZFRC.all=0;

	EPwm1Regs.ETSEL.all=0x0A;      // Interrupt on PRD
	EPwm1Regs.ETPS.all=1;
	EPwm1Regs.ETFLG.all=0;
	EPwm1Regs.ETCLR.all=0;
	EPwm1Regs.ETFRC.all=0;

	EPwm1Regs.PCCTL.all=0;

	EPwm1Regs.TBCTL.all=0x0010+TBCTLVAL; // Enable Timer
	EPwm1Regs.TBPRD=SP;

	EALLOW;  // This is needed to write to EALLOW protected registers
	PieVectTable.EPWM1_INT= &prdTick;
	EDIS;

	IER |= M_INT3;
	PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
	EINT;   // Enable Global interrupt INTM
	ERTM;   // Enable Global realtime interrupt DBGM

}



void EQEP1_Init(void)
{
	EALLOW;  // This is needed to write to EALLOW protected registers
	SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 1;  // eQEP1
	EDIS;

	InitEQep1Gpio();

	EPwm1Setup();

	qep_posspeed.init(&qep_posspeed);
}

interrupt void prdTick(void)                  // EPWM1 Interrupts once every 4 QCLK counts (one period)
{
	Uint16 i;
	// Position and Speed measurement
	qep_posspeed.calc(&qep_posspeed);

	// Control loop code for position control & Speed contol
	Interrupt_Count++;
	if (Interrupt_Count==1000)                 // Every 1000 interrupts(4000 QCLK counts or 1 rev.)
	{
		EALLOW;
		GpioDataRegs.GPASET.bit.GPIO4 = 1;     // Pulse Index signal  (1 pulse/rev.)
		for (i=0; i<700; i++){
	}
	GpioDataRegs.GPACLEAR.bit.GPIO4 = 1;
	Interrupt_Count = 0;                   // Reset count
	EDIS;
	}

	// Acknowledge this interrupt to receive more interrupts from group 1
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
	EPwm1Regs.ETCLR.bit.INT=1;
}

void main()
{
	Uint16 i=0;


	InitSysCtrl();
	InitPieCtrl();
	IER = 0x0000;
	IFR = 0x0000;
	InitPieVectTable();

	LED_Init();
	TIM0_Init(150,200000);//200ms
	UARTa_Init(4800);

	EQEP1_Init();

	while(1)
	{

	}
}

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

生成海报
点赞 0

时光话

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

暂无评论

发表评论

相关推荐

TMS320F280049学习笔记5_Analog-to-Digital Converter (ADC)

13.1简介 ADC模块是一个12位逐次逼近(SAR)风格的ADC。ADC由一个核心和一个封装器组成。其核心由模拟电路组成,包括通道选择MUX、采样保持(S/H)电路、逐次逼近电路、电压基准电路和其他模拟支持电路。封装器由配置和

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

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