Problems with PWM & triangle/sine wave

Thread Starter

sebgus

Joined Feb 5, 2010
6
Hi everybody!

Been trying this afternoon to get a triangle/sine wave from my PIC by using the PWM-module as a D/A-converter.

Here is the setup:

A PIC16F887 running @ 8 MHz (internal clock)
PWM-module activated (prescaler set at 4, PR2 = 127) and a RC-circuit on the output.

Because it is running at 8 MHz with prescaler at 4 the period-time of the PWM would be:

T = (127 + 1) * 4 * (1/8000000) * 4 = 256 µs

The RC-circuit is chosen as following:

R = 10 kohm
C = 1 µF (electrolyte)

This means that the time-constant is 10ms, which should be enough?

All I get on the output is this:
http://www.youtube.com/watch?v=69ZuGRKI658

And with a other RC-circuit:
http://www.youtube.com/watch?v=EbwOcoxVFPk

What am I doing wrong?

Here is the code:

Rich (BB code):
#include <pic16F887.h>
#include	<math.h>

double x, j, y;

unsigned char ad8bit(unsigned char kanal);

void main() {
	PWM1CON=0x07;
	TRISD=0xFF;
	TRISC=0x00;
	PR2=0x7F;
	CCP1CON=0x0C;
	TMR2ON=1;
	T2CKPS0 = 1;
	T2CKPS1 = 0;
	ADCON0=0xC1;

	while(1){
		x=0;
		y=0;
		while(x<(2*3.14)){
		x = x+0.1;
		y=sin(x)+1;
		y = y*64;
		CCPR1L=(unsigned char)y;
	}
}

unsigned char ad8bit(unsigned char kanal){
	kanal<<=3;
	ADCON0=0xC1 | kanal;
	Delay(100);
	GODONE=1;
	while(GODONE);
	return ADRESH;
}
 
Last edited:

russ_hensel

Joined Jan 11, 2009
825
To obtain a nice triangular wave form, just feed two CLD like 1N5283 connected back to back, with your pwm frequency @ 50% duty cycle.

Alberto

Cool but does not play well with frequency variations. It is basically an integrator. Can also use an op amp integrator. Same amplitude issue.

If we knew more about the frequency and how busy the uc is we might give more solutions.

See: http://www.ladyada.net/wiki/openbench/fgen
 

Thread Starter

sebgus

Joined Feb 5, 2010
6
Managed to fix it.

The sin() function was too slow for any higher frequency to appear on my oscilloscope. Instead I used a lookup-table of a sine wave. Works perfectly!

The rewritten code:

Rich (BB code):
#include <pic16F887.h>

double Sinus(int b);

double y = 0;
int x = 0;


void main(){
	OSCCON = 0x75;
	PWM1CON = 0x07;
	TRISD = 0xFF;
	TRISC = 0x00;
	PR2 = 0xFF;
	CCP1CON = 0x0C;
	TMR2ON = 1;
	T2CKPS0 = 0;
	T2CKPS1 = 0;
	ADCON0 = 0xC1;

		while(1){
				while(x <= 16){
					y = 1.0+Sinus(x);
					CCPR1L = (unsigned char)(y*127);
					x++;
				}

				while(x >= 0){
					y = 1.0+Sinus(x);
					CCPR1L = (unsigned char)(y*127);
					x--;
				}

				while(x <= 16){
					y = 1.0-Sinus(x);
					CCPR1L = (unsigned char)(y*127);
					x++;
				}

				while(x >= 0){
					y = 1.0-Sinus(x);
					CCPR1L = (unsigned char)(y*127);
					x--;
				}
		}
}

double Sinus(int b){
	if (b == 0){
		return 0;
	}
	
	else
	if (b == 1){
		return 0.0980171403;
	}

	else
	if (b == 2){
		return 0.195090322;
	}

	else
	if (b == 3){
		return 0.2902846773;
	}
	
	else
	if (b == 4){
		return 0.3826834324;
	}

	else
	if (b == 5){
		return 0.4713967368;
	}
	
	else
	if (b == 6){
		return 0.555570233;
	}

	else
	if (b == 7){
		return 0.6343932842;
	}
	
	else
	if (b == 8){
		return 0.7071067812;
	}

	else
	if (b == 9){
		return 0.7730104534;
	}
	
	else
	if (b == 10){
		return 0.8314696123;
	}

	else
	if (b == 11){
		return 0.8819212643;
	}
	
	else
	if (b == 12){
		return 0.9238795325;
	}

	else
	if (b == 13){
		return 0.9569403357;
	}
	
	else
	if (b == 14){
		return 0.9807852804;
	}

	else
	if (b == 15){
		return 0.9951847267;
	}
	
	else
	if (b == 16){
		return 1;
	}
}


Any ideas in terms of optimizing the code? :)
 
Last edited:

atferrari

Joined Jan 6, 2004
4,769
Sine seems to be solved but holds your micro more or less busy with it, isn't it?

In a similar way, using a look up table or just a counter going up and down continuously will do the trick for triangles, hogging your micro even more...:confused:

If you want it analogic:

a) feed a capacitor with a constant current source
b) discharge the cap with a constant current sink.
c) GOTO a)

That's the way the XR2206 and ICL 8038 work.

Buena suerte
 

Markd77

Joined Sep 7, 2009
2,806
I'm not sure how C optimises and complies that but if you multiply everything in the table by 127 then you wouldn't have to use doubles or use multiply. I imagine you have program space to spare so you could enlarge the table. Again, not sure how that compiles, maybe a case statement would work better.
If you want even more speed and you don't need it to be PWM you could use 8 outputs to drive an DAC. I've even had success for (low quality) audio with a 4 bit DAC built from resistors and an op amp.
 

Thread Starter

sebgus

Joined Feb 5, 2010
6
Yeah, I thought I used a lookup-table but I had wrong there.

Now rewritten the code again, with a real table!

Using it as a simple frequency generator for now (sweeps 10-600 Hz). :)

Rich (BB code):
#include <pic16F887.h>

unsigned char ad8bit(unsigned char kanal);
void Delay(int a);

double y = 0;
int x = 0;

const unsigned char sine_table[64] = {127, 139, 152, 164, 176, 187, 198, 208, 217, 225, 233, 239, 244, 249, 252, 253, 254, 253, 252, 249, 244, 239, 233, 225, 217, 208, 198, 187, 176, 164, 152, 139, 127, 115, 102, 90, 78, 67, 56, 46, 37, 29, 21, 15, 10, 5, 2, 1, 0, 1, 2, 5, 10, 15, 21, 29, 37, 46, 56, 67, 78, 90, 102, 115}; // Sinewave lookup-table

void main(){
	OSCCON = 0x75;  	// Setting frequency to 8 MHz
	PWM1CON = 0x07;		// Sets the number of instruction cycles before the output is driven active
	TRISD = 0xFF;		// Set all D-ports to intputs
	TRISC = 0x00;		// Set all C-ports to outputs
	PR2 = 0xFF;		// Timer2s period-reg
	CCP1CON = 0x0C;		// Activate PWM
	TMR2ON = 1;		// Enable Timer2
	T2CKPS0 = 0;		// Prescaler = 1
	T2CKPS1 = 0;		// Prescaler = 1
	ADCON0 = 0xC1;		// Enable the A/D-converter

		while(1){
				x = 0;

				while(x<64){
					Delay(ad8bit(0));
					CCPR1L=sine_table[x];  // Sinewave output
					x++;
				}
		}
}

unsigned char ad8bit(unsigned char kanal) // A/D-converting
{
   kanal<<=3;
   ADCON0=0xC1 | kanal;
   GODONE=1;
   while(GODONE);
   return ADRESH;
}

void Delay(int a) // Delay-function
{
	while(a>0)
	{
		asm("nop");
		a--;
	}
}
Short movie: http://www.youtube.com/watch?v=fp26ZWjcm4Q

Any more ideas? :)
 
Last edited:

hgmjr

Joined Jan 28, 2005
9,027
Can you attach a schematic of your low-pass filter here in this thread? Or is it just the RC you mentioned in your initial post at the start of this thread.

hgmjr
 

Thread Starter

sebgus

Joined Feb 5, 2010
6
It's the simple RC-circuit as before.

C = 6.8 µF
R = 10 kohm

Eventually going to test more complex filters soon (Butterworth-filters), can return with some results&schematics then.

But first I have to fix a external crystal to extend the bandwidth of the generator. Don't want to add external IC's, want to keep it simple & cheap :)
 

hgmjr

Joined Jan 28, 2005
9,027
Have you considered reducing the capacitor or resistor values to move the rolloff of the lowpass filter out to a higher frequency? Remember that the triangle waveform contains some fairly high frequency components in it. If the filter rolls off the signal to soon it will round off the top and bottom of your triangle.

hgmjr
 

Thread Starter

sebgus

Joined Feb 5, 2010
6
Yeah, I have tested different RC-values to let through higher frequencies, but this code was "made for" sine waves. With triangle-waves you just increment the values linear, right? No look-up tables and such.

Anyway, bedtime now. More testing tomorrow!

Thanks guys :). This seems to be a nice forum to hang around on!
 
Top