Jump to content
brainwash

Starting a DDS project for Stellaris Launchpad

Recommended Posts

Hello all,

 

My first post here, but maybe because I am running into some problems.

I started a small project on the stellaris launchpad with the goal to do digital signal synthesis using the "PWM" module. Now it only does the sine function but the basic idea is to provide a low-pass filtered output for sine, triangle and other non-square signals and raw pwm output for square waves.

 

The plan: precompute as many points of the required output as to avoid aliasing (currently hardcoded to 255), load them into a table, set up the timer0_A interrupt on match, set up the timer period to the number of available samples and start the timer. Inside the interrupt I load the new match value and advance to the next LUT position.

 

Look-up table is generated fine, the sine wave is outputted at full frequency but I ALWAYS get a glitch inside the generated output. The glitch causes the output to go high for one or two pulses, but I don't know how I can pinpoint it without the LP filter in place. In the attached picture you see two glitches, the result of me playing around with different settings.

It is repetitive which means that if I set the LUT to contain two sines the glitch will show up twice even though the periodic timer has only run once. The LUT is correct, I debugged the data and graphed the result. Adding or removing code inside the ISR causes the glitch to move to another part making it sometimes invisible. It is only visible at signals less than two thirds of the output amplitude.

I've also compiled a release version and it shows the same issues.

 

This is my first ARM project so sorry if somehow I miss the obvious.

 

Later edit: LPF is just a simple RC network with a 100nF cap and a trimmer, wired in air; I thought maybe the breadboard or some components were inducing some behaviour. Also, the glitch is present at all MPU speeds I tried. This way I found out you can even overclock the part to at least 120MHz and the timers still seem to run fine.

Is there any open-source protocol for signal generators like there is one for logic analyzers (sump)? Writing a GUI and everything looks like reinventing the wheel.

 

post-1352-14264605232095_thumb.jpg

 

dds_pwm.zip

Share this post


Link to post
Share on other sites

Thanks, I'll take a look at the source code to see if there is anything different. It does not seem to match my style because I'm calling TimerMatchSet with a new value on each timer interrupt to be able to get the samples out as fast as possible.

 

To explain it simply: I'm getting a glitch with the code above no matter what settings I use. Samples are correct, timing seems correct.

Something seems to block the PWM output on high for a cycle whenever the timer reaches full count.

Share this post


Link to post
Share on other sites

Don't know if it is relevant but have you tried disabling (or not enabling) all the other timer interrupts?

 

 

I am not sure from the documentation what the various interrupts do in PWM mode, but since you clear all timer interrupts, but

only  handle one type might want to be sure that it isn't something to do with one interrupt keeping you from seeing another

one.

Share this post


Link to post
Share on other sites

The problem is that I cannot find proper documentation for the StellarisWare api. The MPU has the proper datasheet but the API calls are obfuscated so I have no idea what's happening behind the scenes.

Take for example: 

 

 

TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);

What does the function do? How it expands to, what registers does it touch? Is it setting both GPTMCFG and GPTMTAMR? Sure, I can look at the assembly and try to figure out by matching addresses and whatnot, but I should not need to do this.

 

 

Back to my code, I disabled all interrupts except the CAPA_EVENT, but it's acting the same.

 

I'll just paste the relevant code here:

 

 

 

IntEnable(INT_TIMER0A);
TimerIntEnable(TIMER0_BASE, TIMER_CAPA_EVENT);
TimerEnable(TIMER0_BASE, TIMER_A);
IntMasterEnable();

And then the ISR:

 

 


void Timer0IntHandler(void) {
TimerMatchSet(TIMER0_BASE, TIMER_A,  gSeriesData[currentCycle]);
 
unsigned long ulIntFlags = TimerIntStatus(TIMER0_BASE, true);
TimerIntClear(TIMER0_BASE, ulIntFlags);// acknowledge timer0A capture match
 
// if(ulIntFlags & TIMER_CAPA_EVENT) {
if (currentCycle >= SERIES_LENGTH-1){
currentCycle = 0;
}else{
currentCycle++;
}
// }else{
// ????
// }
}

The interesting thing are the commented lines. If I uncomment them (so that the else code does nothing anyway) I get a stable glitch. If in the 'if' condition I just put '1==1' I get the same stable glitch. With branch condition commented out I get a glitch RANDOMLY, but at the same stable position inside the waveform.

 

At this point I thing of either scratching the project completely or giving up interrupt handling and just doing stupid while loops, I've spent at least 10h looking at this stuff and trying different things.

Share this post


Link to post
Share on other sites

Just after I was going to delete everything I went back to the original post explaning PWM and in the comments sections I found this strange config line:

 

HWREG(TIMER0_BASE + TIMER_O_TAMR) |= (TIMER_TAMR_TAMRSU | TIMER_TAMR_TAPLO | TIMER_TAMR_TAILD);

 

After adding this in the timer setup code the generated waveform is now glitchless! "TAPLO" means PWM timer legacy operation.

Share this post


Link to post
Share on other sites

Just after I was going to delete everything I went back to the original post explaning PWM and in the comments sections I found this strange config line:

 

HWREG(TIMER0_BASE + TIMER_O_TAMR) |= (TIMER_TAMR_TAMRSU | TIMER_TAMR_TAPLO | TIMER_TAMR_TAILD);

 

After adding this in the timer setup code the generated waveform is now glitchless! "TAPLO" means PWM timer legacy operation.

Thanks for coming back and clearing it up. I'm sure this will help a ton of hair-pulling.

Share this post


Link to post
Share on other sites

Just in case anyone else wants to embark on something similar I'm attaching the latest code - since I'm not working on it (for now) anymore. The best practical frequency you can get from this setup lies below 20kHz so it's better to go with a R2R ladder approach. I'l share the code when I will go with that setup.

 

This code does not have the code artifacts anymore and can be modified to generate accurate sine waves (or other functions) at the desired frequency. Currently the parameter "user_frequency" is fixed at 10Khz, but with very few changes you can modify the program to read the frequency from the USART port.

 

Also there is a compiler option to overclock to ~106Mhz - just for the sake of demonstation. I haven't actually measured any PLL output but the output it is indeed proportionally faster. However the frequency scaling is off and operating a device outside its parameters will certainly not fare well.

 

One thing to note for newbies (like me) is that the ISR will run happily even when it skips interrupts causing a lower frequency of the output - without any warning. To experience this just lower the value DEFAULT_MAX_LEVEL to half and check the output frequency.

 

dds_pwm.c

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×