25 Ekim 2017 Çarşamba

Generating Sinewave using PWM

I found two references for this project Roman Black's 1kHz generator pages and Microchip app note AN1523. Both ideas are the same to create a PWM pulse train such that pulse width will be proportional to sinewave amplitude. A filter required to create the sine wave. I used Roman Black's simple filter instead of AN1523 Salen Key Filter.

I have 40 samples on a single 1kHz=1000us sinewave. so  sample period will be 1000us/40=25us. 25us will be TMR2 period.

Circuit works fine and creates a sinewave but the period was 6ms I used Roman Black table by changing TMR2 period to 20us  and 50 samples, but period was 9ms for this case. I checked 50usx20 samples and noticed that same pulse repeats 3 times then changes to new duty cycle. It means that during ISR processing (changing CCPR2L value) 3 to 6 cycle passes before changing the duty cycle. Maybe we can avoid this using higher freq external oscillator. 25MHz external clock decreased the period to 4ms.

Thus I decreased the frequency to 100Hz and 20 samples. It makes 500us timer2 period. I can reach to this range by using 1:16 prescaler. 20 sample values are the same as for 50usx20 table. Note that table values are related to number of samples and duty range you want to work. Finally, I modified the filter RC values to have 100Hz=1/(2*pi*R*C)


Proteus: PIC24k20 PWM output RC1 is connected to the filter.



1Khz was not achivable with this setup but 100Hz was ok with some ripples. We can play with the filter to reduce ripples.





MCC: Internal 16MHz oscillator was used. CCP2 is selected for PWM operation. CCP2 uses TMR2. Timer2 period is adjusted to 25us. 1:1 pre and post scalers are selected. we want to use PWM between 10-90% duty cycle. 




XC8: First we will create a sinevalue array for 40 samples. I have prepared using an excel file. I added +1 to have positive sinewave amplitude:

1+sin(2.pi.f.t) 
f=1000hz  
t=0,25us,50us...975us 


we can check maximum value of CCPR2L by setting duty cycle 100% and press GENERATE, check pwm2.c CCPRL value. For our example it was 99. Thus we can use the table as it is. I changed duty cycle by setting CCPR2L. Another method is to use PWM2_LoadDutyValue() function but I noticed that it created some spikes probably due to processing of another function inside ISR. We have to finish our job inside ISR as quick as possible to catch next interrupt otherwise we may miss some cycles.   I also checked with AN1523 sine table which has different numbers and it works fine, too. 

Roman Black changes duty cycle inside main.c within a while loop. This did not work for me. Thus I put this function inside timer2 ISR tmr2.c  



void TMR2_DefaultInterruptHandler(void){
    // add your TMR2 interrupt custom code
    // or set custom function using TMR2_SetInterruptHandler()

     const unsigned char sinevalue[20] = {
        50,62,74,82,88,90,88,82,74,62,50,38,26,18,12,10,12,18,26,38
    };
   
    static unsigned char pwmstep=0;   

    CCPR2L=sinevalue[pwmstep];
    pwmstep=(pwmstep+1) % 20;
       
}

download the example

Hiç yorum yok:

Yorum Gönder