Update repo

This commit is contained in:
2026-08-30 23:04:35 -07:00
parent 749dab5721
commit ce65a0f59a
14950 changed files with 4408250 additions and 1 deletions
@@ -0,0 +1,33 @@
<projectSpec>
<project
name="eqep_ex1_freq_cal"
device="TMS320F28377D"
cgtVersion="22.6.0.LTS"
products="C2000WARE"
outputFormat="ELF"
launchWizard="False"
linkerCommandFile=""
enableSysConfigTool="true"
sysConfigBuildOptions="--product ${C2000WARE_ROOT}/.metadata/sdk.json --device F2837xD"
>
<configuration name="CPU1_RAM" compilerBuildOptions="--opt_level=off -I${PROJECT_ROOT}/device -I${C2000WARE_DLIB_ROOT} -I${C2000WARE_IQMATH_ROOT}/c28/include -v28 -ml -mt --define=DEBUG --cla_support=cla1 --float_support=fpu32 --tmu_support=tmu0 --vcu_support=vcu2 --define=CPU1 --diag_warning=225 --diag_suppress=10063 --display_error_number --diag_suppress=16002" linkerBuildOptions="--entry_point code_start --stack_size=0x100 " />
<configuration name="CPU1_FLASH" compilerBuildOptions="--opt_level=off -I${PROJECT_ROOT}/device -I${C2000WARE_DLIB_ROOT} -I${C2000WARE_IQMATH_ROOT}/c28/include -v28 -ml -mt --define=DEBUG --cla_support=cla1 --float_support=fpu32 --tmu_support=tmu0 --vcu_support=vcu2 --define=CPU1 --define=_FLASH --diag_warning=225 --diag_suppress=10063 --display_error_number --diag_suppress=16002" linkerBuildOptions="--entry_point code_start --stack_size=0x100 " />
<pathVariable name="C2000WARE_IQMATH_ROOT" path="../../../../../../libraries/math/IQmath/" scope="project" />
<pathVariable name="C2000WARE_DLIB_ROOT" path="../../../../driverlib/" scope="project" />
<pathVariable name="C2000WARE_ROOT" path="../../../../../../" scope="project" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/include/driverlib.h" targetDirectory="device" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/include/device.h" targetDirectory="device" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/source/device.c" targetDirectory="device" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/targetConfigs/TMS320F28377D.ccxml" targetDirectory="targetConfigs" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/cmd/2837xD_RAM_IQMATH_lnk_cpu1.cmd" targetDirectory="" applicableConfigurations="CPU1_RAM"/>
<file action="copy" path="../../../../../../device_support/f2837xd/common/cmd/2837xD_FLASH_IQMATH_lnk_cpu1.cmd" targetDirectory="" applicableConfigurations="CPU1_FLASH"/>
<file action="copy" path="../../../../../../device_support/f2837xd/common/source/F2837xD_CodeStartBranch.asm" targetDirectory="device" />
<file action="link" path="../../../../driverlib/ccs/Debug/driverlib.lib" targetDirectory="" />
<file action="copy" path="../../../../driverlib/" targetDirectory="device" excludeFromBuild="True"/>
<file action="link" path="../../../../../../libraries/math/IQmath/c28/lib/IQmath_fpu32.lib" targetDirectory="" />
<file action="copy" path="../eqep_ex1_calculation.c" targetDirectory="" />
<file action="copy" path="../eqep_ex1_calculation.h" targetDirectory="" />
<file action="copy" path="../eqep_ex1_freq_cal.c" targetDirectory="" />
<file action="copy" path="../eqep_ex1_calculation.xls" targetDirectory="" />
</project>
</projectSpec>
@@ -0,0 +1,33 @@
<projectSpec>
<project
name="eqep_ex2_pos_speed"
device="TMS320F28377D"
cgtVersion="22.6.0.LTS"
products="C2000WARE"
outputFormat="ELF"
launchWizard="False"
linkerCommandFile=""
enableSysConfigTool="true"
sysConfigBuildOptions="--product ${C2000WARE_ROOT}/.metadata/sdk.json --device F2837xD"
>
<configuration name="CPU1_RAM" compilerBuildOptions="--opt_level=off -I${PROJECT_ROOT}/device -I${C2000WARE_DLIB_ROOT} -I${C2000WARE_IQMATH_ROOT}/c28/include -v28 -ml -mt --define=DEBUG --cla_support=cla1 --float_support=fpu32 --tmu_support=tmu0 --vcu_support=vcu2 --define=CPU1 --diag_warning=225 --diag_suppress=10063 --display_error_number --diag_suppress=16002" linkerBuildOptions="--entry_point code_start --stack_size=0x100 " />
<configuration name="CPU1_FLASH" compilerBuildOptions="--opt_level=off -I${PROJECT_ROOT}/device -I${C2000WARE_DLIB_ROOT} -I${C2000WARE_IQMATH_ROOT}/c28/include -v28 -ml -mt --define=DEBUG --define=_FLASH --cla_support=cla1 --float_support=fpu32 --tmu_support=tmu0 --vcu_support=vcu2 --define=CPU1 --diag_warning=225 --diag_suppress=10063 --display_error_number --diag_suppress=16002" linkerBuildOptions="--entry_point code_start --stack_size=0x100 " />
<pathVariable name="C2000WARE_IQMATH_ROOT" path="../../../../../../libraries/math/IQmath/" scope="project" />
<pathVariable name="C2000WARE_DLIB_ROOT" path="../../../../driverlib/" scope="project" />
<pathVariable name="C2000WARE_ROOT" path="../../../../../../" scope="project" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/include/driverlib.h" targetDirectory="device" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/include/device.h" targetDirectory="device" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/source/device.c" targetDirectory="device" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/targetConfigs/TMS320F28377D.ccxml" targetDirectory="targetConfigs" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/cmd/2837xD_RAM_IQMATH_lnk_cpu1.cmd" targetDirectory="" applicableConfigurations="CPU1_RAM" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/cmd/2837xD_FLASH_IQMATH_lnk_cpu1.cmd" targetDirectory="" applicableConfigurations="CPU1_FLASH" />
<file action="copy" path="../../../../../../device_support/f2837xd/common/source/F2837xD_CodeStartBranch.asm" targetDirectory="device" />
<file action="link" path="../../../../driverlib/ccs/Debug/driverlib.lib" targetDirectory="" />
<file action="copy" path="../../../../driverlib/" targetDirectory="device" excludeFromBuild="True"/>
<file action="link" path="../../../../../../libraries/math/IQmath/c28/lib/IQmath_fpu32.lib" targetDirectory="" />
<file action="copy" path="../eqep_ex2_calculation.c" targetDirectory="" />
<file action="copy" path="../eqep_ex2_calculation.h" targetDirectory="" />
<file action="copy" path="../eqep_ex2_pos_speed.c" targetDirectory="" />
<file action="copy" path="../eqep_ex2_calculation.xls" targetDirectory="" />
</project>
</projectSpec>
@@ -0,0 +1,260 @@
//#############################################################################
//
// FILE: eqep_ex1_calculation.c
//
// TITLE: Frequency Measurement Using eQEP (Calculations)
//
// This file includes the eQEP frequency calculation function called by
// eqep_ex1_freq_cal.c. The frequency calculation steps performed by
// FreqCal_calculate() are described below:
//
// 1. This program calculates: **freqHzFR**
//
// freqHzFR or v = (x2 - x1) / T -Equation 1
//
// If max/base freq = 10kHz:
//
// 10kHz = (x2 - x1) / (2 / 100Hz) -Equation 2
// max (x2 - x1) = 200 counts = freqScalerFR
//
// Note: T = 2 / 100Hz. 2 is from (x2 - x1) / 2 because QPOSCNT counts 2 edges
// per cycle.
//
// If both sides of Equation 2 are divided by 10 kHz, then:
//
// 1 = (x2 - x1) / [10kHz * (2 / 100Hz)] where [10kHz * (2 / 100Hz)] = 200
//
// Because (x2 - x1) must be < 200 (max), (x2 - x1) / 200 < 1 for all
// frequencies less than max.
//
// freqFR = (x2 - x1) / 200
// = (x2 - x1) / [10kHz * (2 / 100Hz)] -Equation 3
//
// To get back to original velocity equation, Equation 1, multiply Equation 3
// by 10 kHz:
//
// freqHzFR = 10kHz * (x2 - x1) / [10kHz * (2 / 100Hz)]
// = (x2 - x1) / (2 / 100Hz) -Final Equation
//
// 2. **min freq** = 1 count / (2 / 100Hz) = 50 Hz
//
// 3. **freqHzPR**
//
// freqHzPR or v = X / (t2 - t1) -Equation 4
//
// If max/base freq = 10kHz:
//
// 10kHz = (8 / 2) / T = 8 / 2T
//
// where 8 = prescaler selected by QCAPCTL.UPPS (unit time out every 8 edges)
// 2 = divide by 2 because QPOSCNT counts 2 edges per cycle
// T = time in seconds = t2 - t1 / (SYSCLKFREQ / 128)
// t2 - t1 = # of QCAPCLK cycles
// 1 QCAPCLK cycle = 1 / (SYSCLKFREQ / 128) = QCPRDLAT
//
// Note: 128 is the prescaler selected by QCAPCTL.CCPS
//
// So:
//
// 10 kHz = 8(SYSCLKFREQ / 128) / 2(t2 - t1)
// t2 - t1 = 8(SYSCLKFREQ / 128) / (10kHz * 2)
// = (SYSCLKFREQ / 128) / (2 * 10kHz / 8) -Equation 5
// = maximum (t2 - t1) = freqScalerPR
//
// Divide both sides by (t2 - t1) and:
//
// 1 = freqScalerPR / (t2 - t1)
// = [(SYSCLKFREQ / 128) / (2 * 10kHz / 8)] / (t2 - t1)
//
// Because (t2 - t1) must be < freqScalerPR (max), freqScalerPR / (t2 - t1) < 1
// for all frequencies less than max.
//
// freqPR = freqScalerPR / (t2 - t1)
// = [(SYSCLKFREQ / 128) / (2 * 10kHz / 8)] / (t2 - t1) -Equation 6
//
// Now within velocity limits, to get back to original velocity equation,
// Equation 1, multiply Equation 6 by 10 kHz:
//
// freqHzFR = 10kHz * [(SYSCLKFREQ / 128) / (2 * 10kHz / 8)] / (t2 - t1)
// = (SYSCLKFREQ / 128) * 8 / [2 * (t2 - t1)]
// = 8 / [2 * (t2 - t1) * QCPRDLAT] -Final Equation
//
// More detailed calculation results can be found in the
// eqep_ex1_calculation.xls spreadsheet included in the example folder.
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2013-2024 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//#############################################################################
//
// Included Files
//
#include "eqep_ex1_calculation.h"
//
// FreqCal_calculate - Function to calculate the frequency of the input signal using both the unit
// timer and the quadrature capture units.
//
// For a more detailed explanation of the calculations, read the description at
// the top of this file.
//
void
FreqCal_calculate(FreqCal_Object *p)
{
uint32_t temp;
_iq newPosCnt, oldPosCnt;
//
// **** Frequency calculation using eQEP position counter ****
//
// Check for unit time out event
//
if((EQEP_getInterruptStatus(EQEP1_BASE) & EQEP_INT_UNIT_TIME_OUT) != 0)
{
//
// Get latched POSCNT value
//
newPosCnt = EQEP_getPositionLatch(EQEP1_BASE);
oldPosCnt = p->oldPos;
if(newPosCnt > oldPosCnt)
{
//
// x2 - x1 in v = (x2 - x1) / T equation
//
temp = newPosCnt - oldPosCnt;
}
else
{
temp = (0xFFFFFFFF - oldPosCnt) + newPosCnt;
}
//
// p->freqFR = (x2 - x1) / (T * 10kHz)
//
p->freqFR = _IQdiv(temp, p->freqScalerFR);
temp=p->freqFR;
//
// Is freq greater than max freq (10kHz for this example)?
//
if(temp >= _IQ(1))
{
p->freqFR = _IQ(1);
}
else
{
p->freqFR = temp;
}
//
// Q0 = Q0 * GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
// p->freqHzFR = (p->freqFR) * 10kHz = (x2 - x1) / T
//
p->freqHzFR = _IQmpy(p->baseFreq,p->freqFR);
//
// Update old position counter value and clear unit time out flag
//
p->oldPos = newPosCnt;
p->FR_calc_count++;
if(p->FR_calc_count >= 2)
{
p->task_complete_FR = 1;
}
EQEP_clearInterruptStatus(EQEP1_BASE, EQEP_INT_UNIT_TIME_OUT);
}
//
// **** Frequency calculation using eQEP capture counter ****
//
// Check for unit position event
//
if((EQEP_getStatus(EQEP1_BASE) & EQEP_STS_UNIT_POS_EVNT) != 0)
{
//
// No capture overflow
//
if((EQEP_getStatus(EQEP1_BASE) & EQEP_STS_CAP_OVRFLW_ERROR) == 0)
{
temp = (uint32_t)EQEP_getCapturePeriodLatch(EQEP1_BASE);
}
else
{
//
// Capture overflow, saturate the result
//
temp = 0xFFFF;
}
//
// p->freqPR = X / [(t2 - t1) * 10kHz]
//
p->freqPR = _IQdiv(p->freqScalerPR, temp);
temp = p->freqPR;
if(temp > _IQ(1))
{
p->freqPR = _IQ(1);
}
else
{
p->freqPR = temp;
}
//
// Q0 = Q0 * GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
// p->freqHzPR = (p->freqPR) * 10kHz = X / (t2 - t1)
//
p->freqHzPR = _IQmpy(p->baseFreq, p->freqPR);
p->PR_calc_count++;
if(p->PR_calc_count >= 2)
{
p->task_complete_PR = 1;
}
//
// Clear unit position event flag and overflow error flag
//
EQEP_clearStatus(EQEP1_BASE, (EQEP_STS_UNIT_POS_EVNT |
EQEP_STS_CAP_OVRFLW_ERROR));
}
}
//
// End of File
//
@@ -0,0 +1,85 @@
//#############################################################################
//
// FILE: eqep_ex1_calculation.h
//
// TITLE: Frequency Measurement Using eQEP (Calculations)
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2013-2024 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//#############################################################################
#ifndef EQEP_EX1_CALCULATION_H
#define EQEP_EX1_CALCULATION_H
//
// Included Files
//
#include "IQmathLib.h"
#include "driverlib.h"
//
// Typedefs
//
typedef struct
{
uint32_t freqScalerPR; // Parameter: Scaler converting 1/N cycles to a
// GLOBAL_Q freq (Q0) - independently with global Q
uint32_t freqScalerFR; // Parameter: Scaler converting 1/N cycles to a
// GLOBAL_Q freq (Q0) - independently with global Q
uint32_t baseFreq; // Parameter: Maximum freq
_iq freqPR; // Output: Freq in per-unit using capture unit
int32_t freqHzPR; // Output: Freq in Hz, measured using Capture unit
uint32_t oldPos;
_iq freqFR; // Output: Freq in per-unit using position counter
int32_t freqHzFR; // Output: Freq in Hz, measured using Capture unit
bool task_complete_FR,task_complete_PR; // Output : calculation is done.
int16_t FR_calc_count,PR_calc_count; // Output : calculation is done.
} FreqCal_Object;
typedef FreqCal_Object *FreqCal_Handle;
//
// Function Prototypes
//
void FreqCal_calculate(FreqCal_Handle);
#endif // EQEP_EX1_CALCULATION_H
//
// End of File
//
@@ -0,0 +1,364 @@
//#############################################################################
//
// FILE: eqep_ex1_freq_cal.c
//
// TITLE: Frequency Measurement Using eQEP
//
//! \addtogroup driver_example_list
//! <h1>Frequency Measurement Using eQEP</h1>
//!
//! This example will calculate the frequency of an input signal using the eQEP
//! module. ePWM1A is configured to generate this input signal with a
//! frequency of 5 kHz. It will interrupt once every period and call the
//! frequency calculation function. This example uses the IQMath library to
//! simplify high-precision calculations.
//!
//! In addition to the main example file, the following files must be included
//! in this project:
//! - \b eqep_ex1_calculation.c - contains frequency calculation function
//! - \b eqep_ex1_calculation.h - includes initialization values for frequency
//! structure
//!
//! The configuration for this example is as follows
//! - Maximum frequency is configured to 10KHz (baseFreq)
//! - Minimum frequency is assumed at 50Hz for capture pre-scalar selection
//!
//! \b SPEED_FR: High Frequency Measurement is obtained by counting the
//! external input pulses for 10ms (unit timer set to 100Hz).
//! \f[ SPEED\_FR = \frac{Count\ Delta}{10ms} \f]
//!
//! \b SPEED_PR: Low Frequency Measurement is obtained by measuring time
//! period of input edges. Time measurement is averaged over 64 edges for
//! better results and the capture unit performs the time measurement using
//! pre-scaled SYSCLK.
//!
//! Note that the pre-scaler for capture unit clock is selected such that the
//! capture timer does not overflow at the required minimum frequency. This
//! example runs indefinitely until the user stops it.
//!
//! For more information about the frequency calculation see the comments at
//! the beginning of eqep_ex1_calculation.c and the XLS file provided with the
//! project, eqep_ex1_calculation.xls.
//!
//! \b External \b Connections \n
//! - Connect GPIO20/eQEP1A to GPIO0/ePWM1A
//!
//! \b Watch \b Variables \n
//! - \b freq.freqHzFR - Frequency measurement using position counter/unit
//! time out
//! - \b freq.freqHzPR - Frequency measurement using capture unit
//!
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2013-2024 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//#############################################################################
//
// Included Files
//
#include "driverlib.h"
#include "device.h"
#include "IQmathLib.h"
#include "eqep_ex1_calculation.h"
//
// Defines
//
#define TB_CLK DEVICE_SYSCLK_FREQ / 2 // Time base clock is SYSCLK / 2
#define PWM_CLK 5000 // We want to output at 5 kHz
#define PRD_VAL (TB_CLK / (PWM_CLK * 2)) // Calculate value period value
// for up-down count mode
// Base/max frequency is 10 kHz
#define BASE_FREQ 10000
// See Equation 5 in eqep_ex1_calculation.c
#define FREQ_SCALER_PR (((DEVICE_SYSCLK_FREQ / 128) * 8) / (2 * BASE_FREQ))
// See Equation 2 in eqep_ex1_calculation.c
#define FREQ_SCALER_FR ((BASE_FREQ * 2) / 100)
//
// Function Prototypes
//
void initEPWM(void);
void initEQEP(void);
__interrupt void epwmISR(void);
//
// Globals
//
FreqCal_Object freq =
{
FREQ_SCALER_PR, // freqScalerPR
FREQ_SCALER_FR, // freqScalerFR
BASE_FREQ, // baseFreq
0, 0, 0, 0, 0,0,0,0,0 // Initialize outputs to zero
};
//
// Main
//
void main(void)
{
//
// Initialize device clock and peripherals
//
Device_init();
//
// Disable pin locks and enable internal pullups.
//
Device_initGPIO();
//
// Initialize GPIOs for use as EPWM1A and EQEP1A
//
GPIO_setPinConfig(GPIO_0_EPWM1A);
GPIO_setPadConfig(0, GPIO_PIN_TYPE_STD);
GPIO_setPinConfig(GPIO_20_EQEP1A);
GPIO_setPadConfig(20, GPIO_PIN_TYPE_STD);
//
// Initialize PIE and clear PIE registers. Disables CPU interrupts.
//
Interrupt_initModule();
//
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
//
Interrupt_initVectorTable();
//
// Interrupts that are used in this example are re-mapped to ISR functions
// found within this file.
//
Interrupt_register(INT_EPWM1, &epwmISR);
//
// Setup ePWM1 to generate a 5 kHz signal to be an input to the eQEP
//
initEPWM();
//
// Enable interrupts required for this example
//
Interrupt_enable(INT_EPWM1);
//
// Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
//
EINT;
ERTM;
//
// Setup eQEP1, configuring the unit timer and quadrature capture units
//
initEQEP();
//
// Loop indefinitely
//
while (1)
{
if((freq.task_complete_FR == 1) && (freq.task_complete_PR == 1))
{
if(((freq.freqHzFR - PWM_CLK) < 50) &&
((freq.freqHzFR - PWM_CLK) > -50) &&
((freq.freqHzPR - PWM_CLK) < 50) &&
((freq.freqHzPR - PWM_CLK) > -50))
{
Example_PassCount++;
}
else
{
Example_Fail = 1;
}
}
}
}
//
// initEQEP - Function to configure eQEP1.
//
void
initEQEP(void)
{
//
// Configure the decoder for up-count mode, counting both rising and
// falling edges (that is, 2x resolution)
//
EQEP_setDecoderConfig(EQEP1_BASE, (EQEP_CONFIG_2X_RESOLUTION |
EQEP_CONFIG_UP_COUNT |
EQEP_CONFIG_NO_SWAP));
EQEP_setEmulationMode(EQEP1_BASE, EQEP_EMULATIONMODE_RUNFREE);
//
// Configure the position counter to reset on an index event
//
EQEP_setPositionCounterConfig(EQEP1_BASE, EQEP_POSITION_RESET_IDX,
0xFFFFFFFF);
//
// Enable the unit timer, setting the frequency to 100 Hz
//
EQEP_enableUnitTimer(EQEP1_BASE, (DEVICE_SYSCLK_FREQ / 100));
//
// Configure the position counter to be latched on a unit time out
//
EQEP_setLatchMode(EQEP1_BASE, EQEP_LATCH_UNIT_TIME_OUT);
//
// Enable the eQEP1 module
//
EQEP_enableModule(EQEP1_BASE);
//
// Configure and enable the edge-capture unit. The capture clock divider is
// SYSCLKOUT/128. The unit-position event divider is QCLK/8.
//
EQEP_setCaptureConfig(EQEP1_BASE, EQEP_CAPTURE_CLK_DIV_128,
EQEP_UNIT_POS_EVNT_DIV_8);
EQEP_enableCapture(EQEP1_BASE);
}
//
// initEPWM - Function to configure ePWM1 to generate a 5 kHz signal.
//
void
initEPWM(void)
{
//
// Disable the ePWM time base clock before configuring the module
//
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
//
// Set phase shift to 0 and clear the time base counter
//
EPWM_setPhaseShift(EPWM1_BASE, 0);
EPWM_setTimeBaseCounter(EPWM1_BASE, 0);
//
// Disable the shadow load; the load will be immediate instead
//
EPWM_disableCounterCompareShadowLoadMode(EPWM1_BASE,
EPWM_COUNTER_COMPARE_A);
EPWM_disableCounterCompareShadowLoadMode(EPWM1_BASE,
EPWM_COUNTER_COMPARE_B);
//
// Set the compare A value to half the period value, compare B to 0
//
EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, PRD_VAL/2);
EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_B, 0);
//
// Set action qualifier behavior on compare A events
// - EPWM1A --> 1 when CTR = CMPA and increasing
// - EPWM1A --> 0 when CTR = CMPA and decreasing
//
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_HIGH,
EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_LOW,
EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);
//
// Configure EPWM1B to be complementary to EPWM1A
//
EPWM_setDeadBandDelayPolarity(EPWM1_BASE, EPWM_DB_FED,
EPWM_DB_POLARITY_ACTIVE_LOW);
EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_FED, true);
EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_RED, true);
//
// Enable interrupt when the counter is equal to 0
//
EPWM_setInterruptSource(EPWM1_BASE, EPWM_INT_TBCTR_ZERO);
EPWM_enableInterrupt(EPWM1_BASE);
//
// Interrupt on first event
//
EPWM_setInterruptEventCount(EPWM1_BASE, 1);
//
// Set the time base clock prescaler to /1
//
EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1,
EPWM_HSCLOCK_DIVIDER_1);
//
// Set the period value; don't shadow the register
//
EPWM_setPeriodLoadMode(EPWM1_BASE, EPWM_PERIOD_DIRECT_LOAD);
EPWM_setTimeBasePeriod(EPWM1_BASE, PRD_VAL);
//
// Put the time base counter into up-down count mode
//
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP_DOWN);
//
// Sync the ePWM time base clock
//
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
}
//
// ePWM1 ISR- interrupts once per ePWM period
//
__interrupt void
epwmISR(void)
{
//
// Checks for events and calculates frequency.
//
FreqCal_calculate(&freq);
//
// Clear interrupt flag and issue ACK
//
EPWM_clearEventTriggerInterruptFlag(EPWM1_BASE);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3);
}
//
// End of File
//
@@ -0,0 +1,348 @@
//#############################################################################
//
// FILE: eqep_ex1_calculation.c
//
// TITLE: Position and Speed Measurement Using eQEP (Calculations)
//
// This file includes the eQEP position and speed calculation function called
// by eqep_ex2_pos_speed.c. The position and speed calculation steps performed
// by PosSpeed_calculate() are described below:
//
// 1. This program calculates: **thetaMech**
//
// thetaMech = QPOSCNT / mechScaler
// = QPOSCNT / 4000, where 4000 is the number of counts in 1 rev
// (4000 / 4 = 1000 line/rev quadrature encoder)
//
// Note this example uses mechScaler = 16776 which is _IQ26(1 / 4000)
//
// 2. This program calculates: **thetaElec**
//
// thetaElec = (pole pairs) * thetaMech = 2 * QPOSCNT / 4000 in this case
//
// 3. This program calculates: **speedRPMFR**
//
// speedRPMFR = [(x2 - x1) / 4000] / T -Equation 1
//
// Note (x2 - x1) = difference in number of QPOSCNT counts. Dividing (x2 - x1)
// by 4000 gives position relative to Index in one revolution.
//
// If base RPM = 6000 rpm:
// 6000 rpm = [(x2 - x1) / 4000] / 10ms -Equation 2
// = [(x2 - x1) / 4000] / (.01s * 1 min / 60 sec)
// = [(x2 - x1) / 4000] / (1 / 6000) min
//
// max (x2 - x1) = 4000 counts or 1 revolution in 10 ms
//
// If both sides of Equation 2 are divided by 6000 rpm, then:
// 1 = [(x2 - x1) / 4000] rev / [(1 / 6000) min * 6000rpm]
//
// Because (x2 - x1) must be < 4000 (max) for QPOSCNT increment,
// (x2 - x1) / 4000 < 1 for CW rotation, and because (x2 - x1) must be >- 4000
// for QPOSCNT decrement, (x2 - x1) / 4000 > -1 for CCW rotation
//
// speedFR = [(x2 - x1) / 4000] / [(1 / 6000) min * 6000rpm]
// = (x2 - x1) / 4000 -Equation 3
//
// To convert speedFR to RPM, multiply Equation 3 by 6000 rpm:
// speedRPMFR = 6000rpm * (x2 - x1) / 4000 -Final Equation
//
//
// 2. **min rpm ** = selected at 10 rpm based on CCPS prescaler options
// available (128 is greatest)
//
// 3. **speedRPMPR**
// speedRPMPR = X / (t2 - t1) -Equation 4
//
// where X = QCAPCTL [UPPS] / 4000 rev (position relative to Index in 1 rev)
//
// If max / base speed = 6000 rpm:
// 6000 = (32 / 4000) / [(t2 - t1) / (SYSCLKFREQ / 64)]
//
// where 32 = QCAPCTL[UPPS] (Unit timeout once every 32 edges)
//
// 32 / 4000 = position in 1 rev (position as a fraction of 1 revolution)
//
// t2 - t1 / (SYSCLKFREQ / 64), t2 - t1 = # of QCAPCLK cycles
//
// QCAPCLK cycle = 1 / (SYSCLKFREQ / 64)
// = QCPRDLAT
//
// So:
// 6000 rpm = [32(SYSCLKFREQ / 64) * 60s/min] / [4000(t2 - t1)]
//
// t2 - t1 = [32(SYSCLKFREQ / 64) * 60s/min]/(4000 * 6000rpm) -Equation 5
// = 250 CAPCLK cycles
// = maximum (t2 - t1) = speedScaler
//
// Divide both sides by (t2 - t1) and:
// 1 = 32 / (t2 - t1)
// = [32(SYSCLKFREQ / 64) * 60 s/min] / (4000 * 6000rpm) / (t2 - t1)
//
// Because (t2 - t1) must be < 250 for QPOSCNT, increment 250 / (t2 - t1) < 1
// for CW rotation, and because (t2 - t1) must be > -250 for QPOSCNT decrement
// 250 / (t2 - t1) > -1 for CCW rotation
//
// speedPR = 250 / (t2 - t1)
// = [32(SYSCLKFREQ / 64) * 60 s/min] / (4000 * 6000rpm) / (t2 - t1)
// -Equation 6
//
// To convert speedPR to RPM, multiply Equation 6 by 6000rpm:
// speedRPMFR = [32(SYSCLKFREQ / 64) * 60s/min] / [4000 * (t2 - t1)]
// = [(32 / 4000) rev * 60s/min] / [(t2 - t1)(QCPRDLAT)]
// -Final Equation
//
// More detailed calculation results can be found in the
// eqep_ex2_calculation.xls spreadsheet included in the example folder.
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2013-2024 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//#############################################################################
//
// Included Files
//
#include "eqep_ex2_calculation.h"
//
// Function to calculate the frequency of the input signal using both the unit
// timer and the quadrature capture units.
//
// For a more detailed explanation of the calculations, read the description at
// the top of this file.
//
void PosSpeed_calculate(PosSpeed_Object *p)
{
int32_t temp;
uint16_t pos16bVal, temp1;
_iq temp2, newPosCnt, oldPosCnt;
//
// **** Position calculation - mechanical and electrical motor angle ****
//
// Get the motor direction: -1 = CCW/reverse, 1 = CW/forward
//
p->directionQEP = EQEP_getDirection(EQEP1_BASE);
//
// Capture position once per QA/QB period
//
pos16bVal = (uint16_t)EQEP_getPosition(EQEP1_BASE);
//
// Raw theta = current pos. + ang. offset from QA
//
p->thetaRaw = pos16bVal + p->calAngle;
//
// The following lines calculate
//
// p->thetaMech ~= QPOSCNT / mechScaler [current cnt/(total cnt in 1 rev)]
//
// where mechScaler = 4000 cnts/revolution
//
temp = (int32_t)p->thetaRaw * (int32_t)p->mechScaler; // Q0 * Q26 = Q26
temp &= 0x03FFF000;
p->thetaMech = (int16_t)(temp >> 11); // Q26 -> Q15
p->thetaMech &= 0x7FFF;
//
// The following lines calculate p->elec_mech
//
p->thetaElec = p->polePairs * p->thetaMech; // Q0 * Q15 = Q15
p->thetaElec &= 0x7FFF;
//
// Check for an index occurrence
//
if((EQEP_getInterruptStatus(EQEP1_BASE) & EQEP_INT_INDEX_EVNT_LATCH) != 0U)
{
EQEP_clearInterruptStatus(EQEP1_BASE, EQEP_INT_INDEX_EVNT_LATCH);
}
//
// **** High Speed Calculation using QEP Position counter ****
//
// Check for unit timeout event
//
if((EQEP_getInterruptStatus(EQEP1_BASE) & EQEP_INT_UNIT_TIME_OUT) != 0)
{
//
// The following lines calculate position:
// (x2 - x1) / 4000 (position in 1 revolution)
//
pos16bVal = (uint16_t)EQEP_getPositionLatch(EQEP1_BASE);
temp = (int32_t)pos16bVal * (int32_t)p->mechScaler; // Q0 * Q26 = Q26
temp &= 0x03FFF000;
temp = (int16_t)(temp >> 11); // Q26 -> Q15
temp &= 0x7FFF;
newPosCnt = _IQ15toIQ(temp);
oldPosCnt = p->oldPos;
//
// POSCNT is counting down
//
if(p->directionQEP == -1)
{
if(newPosCnt > oldPosCnt)
{
//
// x2 - x1 should be negative
//
temp2 = -(_IQ(1) - newPosCnt + oldPosCnt);
}
else
{
temp2 = newPosCnt -oldPosCnt;
}
}
//
// POSCNT is counting up
//
else if(p->directionQEP == 1)
{
if(newPosCnt < oldPosCnt)
{
temp2 = _IQ(1) + newPosCnt - oldPosCnt;
}
else
{
//
// x2 - x1 should be positive
//
temp2 = newPosCnt - oldPosCnt;
}
}
if(temp2 > _IQ(1))
{
p->speedFR = _IQ(1);
}
else if(temp2 < _IQ(-1))
{
p->speedFR = _IQ(-1);
}
else
{
p->speedFR = temp2;
}
//
// Update the electrical angle
//
p->oldPos = newPosCnt;
//
// Change motor speed from pu value to rpm value (Q15 -> Q0)
// Q0 = Q0*GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
//
p->speedRPMFR = _IQmpy(p->baseRPM, p->speedFR);
//
// Clear unit time out flag
//
EQEP_clearInterruptStatus(EQEP1_BASE, EQEP_INT_UNIT_TIME_OUT);
}
//
// **** Low-speed computation using QEP capture counter ****
//
// Check for unit position event
//
if((EQEP_getStatus(EQEP1_BASE) & EQEP_STS_UNIT_POS_EVNT) != 0)
{
//
// No Capture overflow
//
if((EQEP_getStatus(EQEP1_BASE) & EQEP_STS_CAP_OVRFLW_ERROR) == 0)
{
temp1 = (uint32_t)EQEP_getCapturePeriodLatch(EQEP1_BASE);
}
else
{
//
// Capture overflow, saturate the result
//
temp1 = 0xFFFF;
}
//
// p->speedPR = p->speedScaler / temp1
//
p->speedPR = _IQdiv(p->speedScaler, temp1);
temp2 = p->speedPR;
if(temp2 > _IQ(1))
{
p->speedPR = _IQ(1);
}
else
{
p->speedPR = temp2;
}
//
// Convert p->speedPR to RPM
//
// Reverse direction = negative
//
if(p->directionQEP == -1)
{
//
// Q0 = Q0 * GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
//
p->speedRPMPR = -_IQmpy(p->baseRPM, p->speedPR);
}
//
// Forward direction = positive
//
else
{
//
// Q0 = Q0 * GLOBAL_Q => _IQXmpy(), X = GLOBAL_Q
//
p->speedRPMPR = _IQmpy(p->baseRPM, p->speedPR);
}
//
// Clear unit position event flag and overflow error flag
//
EQEP_clearStatus(EQEP1_BASE, (EQEP_STS_UNIT_POS_EVNT |
EQEP_STS_CAP_OVRFLW_ERROR));
}
}
@@ -0,0 +1,90 @@
//#############################################################################
//
// FILE: eqep_ex2_calculation.h
//
// TITLE: Position and Speed Measurement Using eQEP (Calculations)
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2013-2024 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//#############################################################################
#ifndef EQEP_EX2_CALCULATION_H
#define EQEP_EX2_CALCULATION_H
//
// Included Files
//
#include "IQmathLib.h"
#include "driverlib.h"
//
// Typedefs
//
typedef struct {
int16_t thetaElec; // Output: Motor electrical angle (Q15)
int16_t thetaMech; // Output: Motor mechanical angle (Q15)
int16_t directionQEP; // Output: Motor rotation direction (Q0)
int16_t thetaRaw; // Variable: Raw angle from timer 2 (Q0)
int16_t mechScaler; // Parameter: 0.9999 / total count,
// total count = 4000 (Q26)
int16_t polePairs; // Parameter: Number of pole pairs (Q0)
int16_t calAngle; // Parameter: Raw angular offset between encoder
// and Phase A (Q0)
uint32_t speedScaler; // Parameter: Scaler converting 1/N cycles to a
// GLOBAL_Q speed (Q0) - independently
// with global Q
_iq speedPR; // Output: Speed in per-unit
uint32_t baseRPM; // Parameter: Scaler converting GLOBAL_Q speed to
// rpm (Q0) speed - independently with
// global Q
int16_t speedRPMPR; // Output: Speed in rpm (Q0) - independently with
// global Q
_iq oldPos;
_iq speedFR; // Output: Speed in per-unit
int16_t speedRPMFR; // Output: Speed in rpm (Q0) - independently with
// global Q
} PosSpeed_Object;
typedef PosSpeed_Object *PosSpeed_Handle;
//
// Function Prototypes
//
void PosSpeed_calculate(PosSpeed_Handle);
#endif // EQEP_EX2_CALCULATION_H
@@ -0,0 +1,404 @@
//#############################################################################
//
// FILE: eqep_ex2_pos_speed.c
//
// TITLE: Position and Speed Measurement Using eQEP
//
//! \addtogroup driver_example_list
//! <h1> Position and Speed Measurement Using eQEP</h1>
//!
//! This example provides position and speed measurement using the
//! capture unit and speed measurement using unit time out of the eQEP module.
//! ePWM1 and a GPIO are configured to generate simulated eQEP signals. The
//! ePWM module will interrupt once every period and call the position/speed
//! calculation function. This example uses the IQMath library to simplify
//! high-precision calculations.
//!
//! In addition to the main example file, the following files must be included
//! in this project:
//! - \b eqep_ex2_calculation.c - contains position/speed calculation function
//! - \b eqep_ex2_calculation.h - includes initialization values for
//! position/speed structure
//!
//! The configuration for this example is as follows
//! - Maximum speed is configured to 6000rpm (baseRPM)
//! - Minimum speed is assumed at 10rpm for capture pre-scalar selection
//! - Pole pair is configured to 2 (polePairs)
//! - Encoder resolution is configured to 4000 counts/revolution (mechScaler)
//! - Which means: 4000 / 4 = 1000 line/revolution quadrature encoder
//! (simulated by ePWM1)
//! - ePWM1 (simulating QEP encoder signals) is configured for a 5kHz frequency
//! or 300 rpm (= 4 * 5000 cnts/sec * 60 sec/min) / 4000 cnts/rev)
//!
//! \b SPEEDRPM_FR: High Speed Measurement is obtained by counting the QEP
//! input pulses for 10ms (unit timer set to 100Hz).
//!
//! \b SPEEDRPM_FR = (Position Delta / 10ms) * 60 rpm
//!
//! \b SPEEDRPM_PR: Low Speed Measurement is obtained by measuring time period
//! of QEP edges. Time measurement is averaged over 64 edges for better results
//! and the capture unit performs the time measurement using pre-scaled SYSCLK.
//!
//! Note that the pre-scaler for capture unit clock is selected such that the
//! capture timer does not overflow at the required minimum frequency. This
//! example runs indefinitely until the user stops it.
//!
//! For more information about the position/speed calculation see the comments
//! at the beginning of eqep_ex2_calculation.c and the XLS file provided with
//! the project, eqep_ex2_calculation.xls.
//!
//! \b External \b Connections \n
//! - Connect GPIO20/eQEP1A to GPIO0/ePWM1A (simulates eQEP Phase A signal)
//! - Connect GPIO21/eQEP1B to GPIO1/ePWM1B (simulates eQEP Phase B signal)
//! - Connect GPIO23/eQEP1I to GPIO2 (simulates eQEP Index Signal)
//!
//! \b Watch \b Variables \n
//! - \b posSpeed.speedRPMFR - Speed meas. in rpm using QEP position counter
//! - \b posSpeed.speedRPMPR - Speed meas. in rpm using capture unit
//! - \b posSpeed.thetaMech - Motor mechanical angle (Q15)
//! - \b posSpeed.thetaElec - Motor electrical angle (Q15)
//!
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2013-2024 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//#############################################################################
//
// Included Files
//
#include "driverlib.h"
#include "device.h"
#include "IQmathLib.h"
#include "eqep_ex2_calculation.h"
//
// Defines
//
#define TB_CLK DEVICE_SYSCLK_FREQ / 2 // Time base clock is SYSCLK / 2
#define PWM_CLK 5000 // We want to output at 5 kHz
// (300 rpm)
#define PRD_VAL (TB_CLK / (PWM_CLK * 2)) // Calculate value period value
// for up-down count mode
// .9999 / 4000 converted to IQ26 fixed point format
#define MECH_SCALER 16776
// 2 pole pairs in this example
#define POLE_PAIRS 2
// Angular offset between encoder and Phase A
#define CAL_ANGLE 0
// See Equation 5 in eqep_ex2_calculation.c
#define SPEED_SCALER ((((uint64_t)32 * DEVICE_SYSCLK_FREQ / 64) * 60) / (24000000))
// Base/max rpm is 6000rpm
#define BASE_RPM 6000
//
// Function Prototypes
//
void initEPWM(void);
void initEQEP(void);
__interrupt void epwmISR(void);
//
// Globals
//
PosSpeed_Object posSpeed =
{
0, 0, 0, 0, // Initialize outputs to zero
MECH_SCALER, // mechScaler
POLE_PAIRS, // polePairs
CAL_ANGLE, // calAngle
SPEED_SCALER, // speedScaler
0, // Initialize output to zero
BASE_RPM, // baseRPM
0, 0, 0, 0 // Initialize outputs to zero
};
uint16_t interruptCount = 0;
//
// Main
//
void main(void)
{
//
// Initialize device clock and peripherals
//
Device_init();
//
// Disable pin locks and enable internal pullups.
//
Device_initGPIO();
//
// Initialize GPIO0 to ePWM1A, GPIO1 to ePWM1B, and GPIO4 as an output.
// They will be used to simulate incoming eQEP Phase A, Phase B, and Index
// signals respectively.
//
GPIO_setPinConfig(GPIO_0_EPWM1A);
GPIO_setPadConfig(0, GPIO_PIN_TYPE_STD);
GPIO_setPinConfig(GPIO_1_EPWM1B);
GPIO_setPadConfig(1, GPIO_PIN_TYPE_STD);
GPIO_setPinConfig(GPIO_2_GPIO2);
GPIO_setDirectionMode(2, GPIO_DIR_MODE_OUT);
GPIO_writePin(2, 0);
//
// Initialize GPIOs for use as EQEP1A, EQEP1B, and EQEP1I
//
GPIO_setPinConfig(GPIO_20_EQEP1A);
GPIO_setPadConfig(20, GPIO_PIN_TYPE_STD);
GPIO_setPinConfig(GPIO_21_EQEP1B);
GPIO_setPadConfig(21, GPIO_PIN_TYPE_STD);
GPIO_setPinConfig(GPIO_23_EQEP1I);
GPIO_setPadConfig(23, GPIO_PIN_TYPE_STD);
//
// Initialize PIE and clear PIE registers. Disables CPU interrupts.
//
Interrupt_initModule();
//
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
//
Interrupt_initVectorTable();
//
// Interrupts that are used in this example are re-mapped to ISR functions
// found within this file.
//
Interrupt_register(INT_EPWM1, &epwmISR);
//
// Setup ePWM1 to generate a 5 kHz signal to be an input to the eQEP
//
initEPWM();
//
// Enable interrupts required for this example
//
Interrupt_enable(INT_EPWM1);
//
// Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
//
EINT;
ERTM;
//
// Setup eQEP1, configuring the unit timer and quadrature capture units
//
initEQEP();
//
// Loop indefinitely
//
while(1)
{
;
}
}
//
// Function to configure eQEP1.
//
void initEQEP(void)
{
//
// Configure the decoder for quadrature count mode
//
EQEP_setDecoderConfig(EQEP1_BASE, (EQEP_CONFIG_1X_RESOLUTION |
EQEP_CONFIG_QUADRATURE |
EQEP_CONFIG_NO_SWAP));
EQEP_setEmulationMode(EQEP1_BASE, EQEP_EMULATIONMODE_RUNFREE);
//
// Configure the position counter to reset on an index event
//
EQEP_setPositionCounterConfig(EQEP1_BASE, EQEP_POSITION_RESET_IDX,
0xFFFFFFFF);
//
// Enable the unit timer, setting the frequency to 100 Hz
//
EQEP_enableUnitTimer(EQEP1_BASE, (DEVICE_SYSCLK_FREQ / 100));
//
// Configure the position counter to be latched on a unit time out
//
EQEP_setLatchMode(EQEP1_BASE, EQEP_LATCH_UNIT_TIME_OUT);
//
// Enable the eQEP1 module
//
EQEP_enableModule(EQEP1_BASE);
//
// Configure and enable the edge-capture unit. The capture clock divider is
// SYSCLKOUT/64. The unit-position event divider is QCLK/32.
//
EQEP_setCaptureConfig(EQEP1_BASE, EQEP_CAPTURE_CLK_DIV_64,
EQEP_UNIT_POS_EVNT_DIV_32);
EQEP_enableCapture(EQEP1_BASE);
}
//
// Function to configure ePWM1 to generate a 5 kHz signal.
//
void initEPWM(void)
{
//
// Disable the ePWM time base clock before configuring the module
//
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
//
// Set phase shift to 0 and clear the time base counter
//
EPWM_setPhaseShift(EPWM1_BASE, 0);
EPWM_setTimeBaseCounter(EPWM1_BASE, 0);
//
// Disable the shadow load; the load will be immediate instead
//
EPWM_disableCounterCompareShadowLoadMode(EPWM1_BASE,
EPWM_COUNTER_COMPARE_A);
EPWM_disableCounterCompareShadowLoadMode(EPWM1_BASE,
EPWM_COUNTER_COMPARE_B);
//
// Set the compare A value to half the period value, compare B to 0
//
EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, PRD_VAL/2);
EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_B, 0);
//
// Set action qualifier behavior on compare A events
// - EPWM1A --> 1 when CTR = CMPA and increasing
// - EPWM1A --> 0 when CTR = CMPA and decreasing
//
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_HIGH,
EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,
EPWM_AQ_OUTPUT_LOW,
EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);
//
// Set action qualifier behavior on compare B events
// - EPWM1B --> 1 when CTR = PRD and increasing
// - EPWM1B --> 0 when CTR = 0 and decreasing
//
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_B,
EPWM_AQ_OUTPUT_HIGH,
EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_B,
EPWM_AQ_OUTPUT_LOW,
EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
//
// Enable interrupt when the counter is equal to PRD
//
EPWM_setInterruptSource(EPWM1_BASE, EPWM_INT_TBCTR_PERIOD);
EPWM_enableInterrupt(EPWM1_BASE);
//
// Interrupt on first event
//
EPWM_setInterruptEventCount(EPWM1_BASE, 1);
//
// Set the time base clock prescaler to /1
//
EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1,
EPWM_HSCLOCK_DIVIDER_1);
//
// Set the period value; don't shadow the register
//
EPWM_setPeriodLoadMode(EPWM1_BASE, EPWM_PERIOD_DIRECT_LOAD);
EPWM_setTimeBasePeriod(EPWM1_BASE, PRD_VAL);
//
// Put the time base counter into up-down count mode
//
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP_DOWN);
//
// Sync the ePWM time base clock
//
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
}
//
// ePWM1 ISR--interrupts once every 4 QCLK counts (one period)
//
__interrupt void epwmISR(void)
{
uint16_t i;
//
// Position speed and measurement
//
PosSpeed_calculate(&posSpeed);
//
// Control loop for position control and speed control
//
interruptCount++;
if(interruptCount == 1000)
{
//
// Pulse index signal (1 pulse/rev)
//
GPIO_writePin(2, 1);
for(i = 0; i < 700; i++)
{
;
}
GPIO_writePin(2, 0);
}
//
// Clear interrupt flag and issue ACK
//
EPWM_clearEventTriggerInterruptFlag(EPWM1_BASE);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3);
}