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:
    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:

    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;
    17.     ADCON0=0xC1;
    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;
    32.     ADCON0=0xC1 | kanal;
    33.     Delay(100);
    34.     GODONE=1;
    35.     while(GODONE);
    36.     return ADRESH;
    37. }
    38.  
    39.  
     
    Last edited: Feb 6, 2010
  2. russ_hensel

    Well-Known Member

    Jan 11, 2009
    818
    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.

    See: http://www.ladyada.net/wiki/openbench/fgen
     
  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;
    19.     ADCON0 = 0xC1;
    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. }
    [​IMG]

    Any ideas in terms of optimizing the code? :)
     
    Last edited: Feb 6, 2010
  4. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    764
    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
     
  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){
    27.                     Delay(ad8bit(0));
    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;
    37.    ADCON0=0xC1 | kanal;
    38.    GODONE=1;
    39.    while(GODONE);
    40.    return ADRESH;
    41. }
    42.  
    43. void Delay(int a) // Delay-function
    44. {
    45.     while(a>0)
    46.     {
    47.         asm("nop");
    48.         a--;
    49.     }
    50. }
    Short movie: http://www.youtube.com/watch?v=fp26ZWjcm4Q

    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!
     
Loading...