Continue to Site

Manual C code for TMS320F28335 microcontroller

ohad0262

Junior Member level 2
Junior Member level 2
Joined
Jan 16, 2025
Messages
20
Helped
0
Reputation
0
Reaction score
1
Trophy points
3
Activity points
256
Hello everyone!

I have written the following code using a previously working codebase that was originally designed for a different application but the same TI TMS320F28335 DSP.
Some of the code was written by me, some was provided by ChatGPT, I also asked the AI chat to add comments for better clarity of the code lines.

objective:​

The DSP receives a digital signal as an input from an XOR phase detector and provide a 50% duty cycle with varying frequency PWM as output.

The goal is to:
  1. Accurately measure the pulse width of the XOR output (which represents the phase difference between two square waves).
  2. Adjust the PWM output frequency of the DSP accordingly.

Example Calculation:

  • Initial PWM frequency = 200 kHz (Period = 5 µs)
  • If the XOR output is logic ‘1’ for 0.5 µs, this corresponds to a phase difference of (0.5 µs/5 µs)×360°=36°
  • In response, the PWM frequency should update to 200k+36k = 236 kHz.

Implementation:

To ensure precise measurement of the XOR pulse width, I am using the eCAP (Enhanced Capture) module of the DSP.

Questions:

🔹 This is my first time manually writing such a DSP code—could you please review it and let me know if it is suitable for the TMS320F28335?
🔹 Is anything missing or incorrect in my approach?
🔹 How can I validate my code before flashing it onto the DSP?

Any advice, feedback, or suggestions would be greatly appreciated! Thanks in advance! 😊

The code is below:

#include "DSP2833x_Device.h" // Include device-specific register definitions
#include "DSP2833x_SysCtrl.h" // Include system control functions and definitions
#include "DSP2833x_GlobalPrototypes.h" // Include global prototypes for the DSP
#include <string.h> // Include the string library for the memcpy function

//------------------------------------------------------------------------------
// External functions from TI's libraries
//------------------------------------------------------------------------------
extern void InitSysCtrl(void); // Function to initialize system control (clocks, PLL, etc.)
extern void InitPieCtrl(void); // Function to initialize the Peripheral Interrupt Expansion (PIE)
extern void InitPieVectTable(void); // Function to initialize the PIE vector table with default ISRs
extern void InitFlash(void); // Function to initialize flash wait states and other flash settings

//------------------------------------------------------------------------------
// Symbols defined by the linker for copying critical functions from Flash to RAM
//------------------------------------------------------------------------------
extern unsigned int RamfuncsLoadStart; // Start address of functions to be copied from flash to RAM
extern unsigned int RamfuncsLoadEnd; // End address of functions to be copied from flash to RAM
extern unsigned int RamfuncsRunStart; // Start address in RAM where functions will run

//------------------------------------------------------------------------------
// Function Prototypes for our custom functions
//------------------------------------------------------------------------------
void Gpio_select(void); // Function to configure GPIO pins for PWM and capture input
void Setup_ePWM1(void); // Function to configure ePWM1 for PWM output
void Setup_eCAP1(void); // Function to configure eCAP1 for capturing sensor pulses

interrupt void ecap1_isr(void); // Interrupt Service Routine for eCAP1 interrupts

//------------------------------------------------------------------------------
// Global Definitions and Variables
//------------------------------------------------------------------------------
#define SYSCLK 150000000UL // Define the system clock frequency as 150 MHz (unsigned long literal)
#define INITIAL_PWM_FREQ 200000UL // Define the initial PWM frequency as 200 kHz (unsigned long literal)
// Calculate the initial time–base period in counts:
// TBPRD = SYSCLK / PWM_frequency = 150,000,000 / 200,000 = 750
#define INITIAL_TBPRD 750 // Define the initial PWM period as 750 counts

// Global variable to store the updated PWM frequency for monitoring or further processing
volatile unsigned long g_newPwmFreq = INITIAL_PWM_FREQ; // Initialize with the base frequency (200 kHz)

//------------------------------------------------------------------------------
// Main function: entry point of the program
//------------------------------------------------------------------------------
void main(void)
{
InitSysCtrl(); // Initialize system control: clocks, PLL, watchdog, etc.

// Copy critical functions from Flash to RAM for faster execution (if required)
memcpy(&RamfuncsRunStart, // Destination: start address in RAM
&RamfuncsLoadStart, // Source: start address in flash
(unsigned long)&RamfuncsLoadEnd - (unsigned long)&RamfuncsLoadStart); // Number of bytes to copy
InitFlash(); // Initialize flash memory settings (e.g., wait states)

EALLOW; // Enable access to protected registers
SysCtrlRegs.WDCR = 0x00AF; // Configure the watchdog timer (e.g., enable with a specific prescaler)
EDIS; // Disable access to protected registers

DINT; // Disable all interrupts during system configuration

Gpio_select(); // Configure GPIO pins for PWM output and eCAP input

Setup_ePWM1(); // Set up ePWM1: configure PWM output at 200 kHz with 50% duty cycle

Setup_eCAP1(); // Set up eCAP1: configure capture module to record sensor pulse edges

InitPieCtrl(); // Initialize the PIE control registers for interrupt management

InitPieVectTable(); // Initialize the PIE vector table with default ISR addresses

EALLOW; // Enable protected register access for modifying the PIE vector table
PieVectTable.ECAP1_INT = &ecap1_isr; // Map the eCAP1 interrupt vector to our custom ISR function
EDIS; // Disable protected register access

PieCtrlRegs.PIEIER4.bit.INTx1 = 1; // Enable the PIE interrupt for eCAP1 (typically in PIE Group 4, INTx1)

IER |= M_INT4; // Enable CPU interrupt group 4 (for eCAP interrupts)

EINT; // Globally enable interrupts
ERTM; // Enable real-time interrupts

while(1) // Main loop: continuously run background tasks (if any)
{
// Currently empty because the processing is handled in the eCAP1 ISR.
}
}

//------------------------------------------------------------------------------
// Function: Gpio_select
// Purpose: Configure GPIO pins for PWM output and for eCAP input
//------------------------------------------------------------------------------
void Gpio_select(void)
{
EALLOW; // Enable access to protected registers

GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // Set GPIO0 to function 1 (ePWM1A) for PWM output
GpioCtrlRegs.GPAMUX1.bit.GPIO2 = 3; // Set GPIO2 to function 3 (eCAP1) for sensor capture input

EDIS; // Disable access to protected registers
}

//------------------------------------------------------------------------------
// Function: Setup_ePWM1
// Purpose: Configure ePWM1 to generate a PWM signal at 200 kHz with a constant 50% duty cycle
//------------------------------------------------------------------------------
void Setup_ePWM1(void)
{
EALLOW; // Enable access to protected registers

EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Set ePWM1 time–base to up–count mode
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading (no external sync)
EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1; // Set high-speed clock divider to 1 (no division)
EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1; // Set clock divider to 1 (no division)

EPwm1Regs.TBPRD = INITIAL_TBPRD; // Load the time–base period register with 750 (for 200 kHz PWM)

EPwm1Regs.CMPA.half.CMPA = INITIAL_TBPRD / 2; // Set compare value to 375 for 50% duty cycle

EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET; // At counter zero (ZRO event), set the PWM output high
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR; // When counter equals CMPA on the up–count (CAU event), clear the PWM output (go low)

EDIS; // Disable access to protected registers
}

//------------------------------------------------------------------------------
// Function: Setup_eCAP1
// Purpose: Configure eCAP1 to capture rising and falling edges of the sensor pulse
//------------------------------------------------------------------------------
void Setup_eCAP1(void)
{
EALLOW; // Enable access to protected registers

ECAP1Regs.ECAPCTL.bit.CAPLDEN = 1; // Enable shadow loading of capture registers for reliable latching
ECAP1Regs.ECAPCTL.bit.CONT_ONESHT = 0; // Set eCAP1 to continuous capture mode (not one-shot)
ECAP1Regs.ECAPCTL.bit.CAP_APWM = 0; // Ensure the module is in capture mode (not APWM mode)

ECAP1Regs.ECAPCTL.bit.CAP1POL = 0; // Set capture event 1 polarity to rising edge (signal goes high)
ECAP1Regs.ECAPCTL.bit.CAP2POL = 1; // Set capture event 2 polarity to falling edge (signal goes low)

ECAP1Regs.ECCTL2.bit.TSCTRSTOP = 0; // Stop the time–stamp counter (if running) to reset it
ECAP1Regs.ECCTL2.bit.TSCTRSTOP = 1; // Start the time–stamp counter

ECAP1Regs.ECCTL2.bit.REARM = 1; // Rearm the eCAP module to start a new capture sequence

ECAP1Regs.ECEINT.bit.CEVT2 = 1; // Enable interrupt for capture event 2 (falling edge event)

ECAP1Regs.ECCLR.all = 0xFFFF; // Clear any pending eCAP interrupts

EDIS; // Disable access to protected registers
}

//------------------------------------------------------------------------------
// Interrupt Service Routine: ecap1_isr
// Purpose: Process the captured rising and falling edge timestamps,
// calculate the pulse width and phase difference,
// compute the new PWM frequency, and update ePWM1 accordingly.
//------------------------------------------------------------------------------
interrupt void ecap1_isr(void)
{
Uint32 cap1, cap2, pulse_width; // Variables to hold timestamps and pulse width in counts
float phase_fraction, phase_deg; // Variables to hold the fraction of the period and phase in degrees
unsigned long updated_pwm_freq, new_TBPRD; // Variables to hold the updated PWM frequency and new TBPRD

cap1 = ECAP1Regs.CAP1; // Read the timestamp for capture event 1 (rising edge)
cap2 = ECAP1Regs.CAP2; // Read the timestamp for capture event 2 (falling edge)

if(cap2 > cap1) // Check that the falling edge timestamp is greater than the rising edge timestamp
{
pulse_width = cap2 - cap1; // Compute pulse width as the difference between falling and rising edge timestamps
}
else
{
pulse_width = 0; // If an error or rollover occurred, set pulse width to 0 (could add more handling here)
}

phase_fraction = (float)pulse_width / (float)EPwm1Regs.TBPRD; // Calculate the fraction of the PWM period the sensor is high

phase_deg = phase_fraction * 360.0; // Convert the high time fraction into a phase difference in degrees

updated_pwm_freq = INITIAL_PWM_FREQ + (unsigned long)(phase_deg * 1000.0); // Compute new PWM frequency:
// Base frequency (200 kHz) plus (phase in degrees * 1000)

new_TBPRD = SYSCLK / updated_pwm_freq; // Calculate the new PWM period (TBPRD) in counts: TBPRD = SYSCLK / updated_pwm_freq

EPwm1Regs.TBPRD = new_TBPRD; // Update ePWM1 TBPRD with the new period for the updated frequency
EPwm1Regs.CMPA.half.CMPA = new_TBPRD / 2; // Update compare value for a constant 50% duty cycle (half of new TBPRD)

g_newPwmFreq = updated_pwm_freq; // Store the new PWM frequency in a global variable for monitoring (optional)

ECAP1Regs.ECCLR.all = 0xFFFF; // Clear all eCAP interrupt flags by writing all ones
ECAP1Regs.ECCTL2.bit.REARM = 1; // Rearm the eCAP module to prepare for the next capture sequence

PieCtrlRegs.PIEACK.all = PIEACK_GROUP4; // Acknowledge the PIE interrupt for group 4 so that further interrupts are recognized
}
 
Last edited:

Example Calculation:

  • Initial PWM frequency = 200 kHz (Period = 5 µs)
  • If the XOR output is logic ‘1’ for 0.5 µs, this corresponds to a phase difference of (0.5 µs/5 µs)×360°=36°
  • In response, the PWM frequency should update to 200k+36k = 236 kHz.
Good luck with this.
I recommend to do some reasearch about how a PLL and how (different) phase detectors and how it´s feedback loop works.

From your math.
it seems you don´t understand
* that even if both frequencies are 100% equal .. there still may be a 36° deviation.
* that 0.5us ... may mean 36° leading ... or 36° lagging.
* 36° of phase misalignement ... have nothing to do with 36kHz at all.
* you can have 0.5us phase difference with a wide range of frequencies. Let´s say 200kHz reference and 100kHz ... 400kHz output frequency.

You may use paper and pencil ... or a simulation tool ... or even Excel to verify your idea.

****
Let´s do an example:
* 200kHz reference and 220kHz PWM frequency, starting at the same time.
--> do you agree you have to correct the frequency by -20kHz? ... I mean to get 200kHz which is in first place necessay to become synchronized.

After 5us you get a phase difference of 0.5us
after another 5us --> =10us you get a phase difference of 1us
15us --- 1.5us
20us --- 2.0us
25us --- ALL HIGH
30us --- 2.0us
35us --- 1.5us
40us --- 1.0us
45us --- 0.5us
50us --- ALL LOW

And in all these cases above
... with all the phase differences of 0us to 2.5us (it does not go beyond 2.5us)
... you always need to correct the frequency by -20kHz.
Since it´s always 200kHz vs 220kHz.

Klaus
 
Good luck with this.
I recommend to do some reasearch about how a PLL and how (different) phase detectors and how it´s feedback loop works.

From your math.
it seems you don´t understand
* that even if both frequencies are 100% equal .. there still may be a 36° deviation.
* that 0.5us ... may mean 36° leading ... or 36° lagging.
* 36° of phase misalignement ... have nothing to do with 36kHz at all.
* you can have 0.5us phase difference with a wide range of frequencies. Let´s say 200kHz reference and 100kHz ... 400kHz output frequency.

You may use paper and pencil ... or a simulation tool ... or even Excel to verify your idea.

****
Let´s do an example:
* 200kHz reference and 220kHz PWM frequency, starting at the same time.
--> do you agree you have to correct the frequency by -20kHz? ... I mean to get 200kHz which is in first place necessay to become synchronized.

After 5us you get a phase difference of 0.5us
after another 5us --> =10us you get a phase difference of 1us
15us --- 1.5us
20us --- 2.0us
25us --- ALL HIGH
30us --- 2.0us
35us --- 1.5us
40us --- 1.0us
45us --- 0.5us
50us --- ALL LOW

And in all these cases above
... with all the phase differences of 0us to 2.5us (it does not go beyond 2.5us)
... you always need to correct the frequency by -20kHz.
Since it´s always 200kHz vs 220kHz.

Klaus
Dear Klaus,
This code whole purpose is just to validate If i am able to capture the XOR edges, there is no control loop at this point.
The PWM frequency updating is just a way to get some kind of response that will tell me if the code is suitable for the DSP.

I am willing to feed the XOR with two square wave having a constant phase difference between them.
And to calculate the phase difference between them assuming their frequency does not change and is always 200 kHz.
The PWM should change to 236 kHz and stay that way till the rest of the experiment.

I will not change the period used in the calculation, even if the PWM frequency changes.
 


Write your reply...

LaTeX Commands Quick-Menu:

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top