Problems with PWM & triangle/sine wave

Discussion in 'Embedded Systems and Microcontrollers' started by sebgus, Feb 5, 2010.

1. sebgus Thread Starter New Member

Feb 5, 2010
6
0
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:

And with a other RC-circuit:

What am I doing wrong?

Here is the code:

Code ( (Unknown Language)):
1. #include <pic16F887.h>
2. #include    <math.h>
3.
4. double x, j, y;
5.
6. unsigned char ad8bit(unsigned char kanal);
7.
8. void main() {
9.     PWM1CON=0x07;
10.     TRISD=0xFF;
11.     TRISC=0x00;
12.     PR2=0x7F;
13.     CCP1CON=0x0C;
14.     TMR2ON=1;
15.     T2CKPS0 = 1;
16.     T2CKPS1 = 0;
18.
19.     while(1){
20.         x=0;
21.         y=0;
22.         while(x<(2*3.14)){
23.         x = x+0.1;
24.         y=sin(x)+1;
25.         y = y*64;
26.         CCPR1L=(unsigned char)y;
27.     }
28. }
29.
30. unsigned char ad8bit(unsigned char kanal){
31.     kanal<<=3;
33.     Delay(100);
34.     GODONE=1;
35.     while(GODONE);
37. }
38.
39.

Last edited: Feb 6, 2010
2. russ_hensel Distinguished Member

Jan 11, 2009
820
47

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.

3. sebgus Thread Starter New Member

Feb 5, 2010
6
0
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:

Code ( (Unknown Language)):
1. #include <pic16F887.h>
2.
3. double Sinus(int b);
4.
5. double y = 0;
6. int x = 0;
7.
8.
9. void main(){
10.     OSCCON = 0x75;
11.     PWM1CON = 0x07;
12.     TRISD = 0xFF;
13.     TRISC = 0x00;
14.     PR2 = 0xFF;
15.     CCP1CON = 0x0C;
16.     TMR2ON = 1;
17.     T2CKPS0 = 0;
18.     T2CKPS1 = 0;
20.
21.         while(1){
22.                 while(x <= 16){
23.                     y = 1.0+Sinus(x);
24.                     CCPR1L = (unsigned char)(y*127);
25.                     x++;
26.                 }
27.
28.                 while(x >= 0){
29.                     y = 1.0+Sinus(x);
30.                     CCPR1L = (unsigned char)(y*127);
31.                     x--;
32.                 }
33.
34.                 while(x <= 16){
35.                     y = 1.0-Sinus(x);
36.                     CCPR1L = (unsigned char)(y*127);
37.                     x++;
38.                 }
39.
40.                 while(x >= 0){
41.                     y = 1.0-Sinus(x);
42.                     CCPR1L = (unsigned char)(y*127);
43.                     x--;
44.                 }
45.         }
46. }
47.
48. double Sinus(int b){
49.     if (b == 0){
50.         return 0;
51.     }
52.
53.     else
54.     if (b == 1){
55.         return 0.0980171403;
56.     }
57.
58.     else
59.     if (b == 2){
60.         return 0.195090322;
61.     }
62.
63.     else
64.     if (b == 3){
65.         return 0.2902846773;
66.     }
67.
68.     else
69.     if (b == 4){
70.         return 0.3826834324;
71.     }
72.
73.     else
74.     if (b == 5){
75.         return 0.4713967368;
76.     }
77.
78.     else
79.     if (b == 6){
80.         return 0.555570233;
81.     }
82.
83.     else
84.     if (b == 7){
85.         return 0.6343932842;
86.     }
87.
88.     else
89.     if (b == 8){
90.         return 0.7071067812;
91.     }
92.
93.     else
94.     if (b == 9){
95.         return 0.7730104534;
96.     }
97.
98.     else
99.     if (b == 10){
100.         return 0.8314696123;
101.     }
102.
103.     else
104.     if (b == 11){
105.         return 0.8819212643;
106.     }
107.
108.     else
109.     if (b == 12){
110.         return 0.9238795325;
111.     }
112.
113.     else
114.     if (b == 13){
115.         return 0.9569403357;
116.     }
117.
118.     else
119.     if (b == 14){
120.         return 0.9807852804;
121.     }
122.
123.     else
124.     if (b == 15){
125.         return 0.9951847267;
126.     }
127.
128.     else
129.     if (b == 16){
130.         return 1;
131.     }
132. }

Any ideas in terms of optimizing the code?

Last edited: Feb 6, 2010
4. atferrari AAC Fanatic!

Jan 6, 2004
2,663
783
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...

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

5. Markd77 Senior Member

Sep 7, 2009
2,803
594
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.

6. sebgus Thread Starter New Member

Feb 5, 2010
6
0
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).

Code ( (Unknown Language)):
1. #include <pic16F887.h>
2.
3. unsigned char ad8bit(unsigned char kanal);
4. void Delay(int a);
5.
6. double y = 0;
7. int x = 0;
8.
9. 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
10.
11. void main(){
12.     OSCCON = 0x75;      // Setting frequency to 8 MHz
13.     PWM1CON = 0x07;     // Sets the number of instruction cycles before the output is driven active
14.     TRISD = 0xFF;       // Set all D-ports to intputs
15.     TRISC = 0x00;       // Set all C-ports to outputs
16.     PR2 = 0xFF;     // Timer2s period-reg
17.     CCP1CON = 0x0C;     // Activate PWM
18.     TMR2ON = 1;     // Enable Timer2
19.     T2CKPS0 = 0;        // Prescaler = 1
20.     T2CKPS1 = 0;        // Prescaler = 1
21.     ADCON0 = 0xC1;      // Enable the A/D-converter
22.
23.         while(1){
24.                 x = 0;
25.
26.                 while(x<64){
28.                     CCPR1L=sine_table[x];  // Sinewave output
29.                     x++;
30.                 }
31.         }
32. }
33.
34. unsigned char ad8bit(unsigned char kanal) // A/D-converting
35. {
36.    kanal<<=3;
38.    GODONE=1;
39.    while(GODONE);
41. }
42.
43. void Delay(int a) // Delay-function
44. {
45.     while(a>0)
46.     {
47.         asm("nop");
48.         a--;
49.     }
50. }

Any more ideas?

Last edited: Feb 7, 2010
7. hgmjr Moderator

Jan 28, 2005
9,030
214
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

8. sebgus Thread Starter New Member

Feb 5, 2010
6
0
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

9. hgmjr Moderator

Jan 28, 2005
9,030
214
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

10. sebgus Thread Starter New Member

Feb 5, 2010
6
0
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!