Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] Sine Wave generation using PWM and PIC16F72

Status
Not open for further replies.

djc

Advanced Member level 1
Joined
Jan 27, 2013
Messages
402
Helped
3
Reputation
6
Reaction score
2
Trophy points
1,298
Location
India
Activity points
4,554
Hi all,
Here is my code for sine wave generation using PWM. Controller is PIC16F72, mikroc and crystal is 8MHz.

Code:
static unsigned char i = 0,flag=1;
//=============== SINE WAVE LOOK UP TABLE ================//
 const unsigned char sine[50] ={ 52,57,62,66,70,74,77,80,82,84,85,86,86, 86,85,83,81,78,75,72,69,65,61,56,52, 48,44,39,35,31,28,25,22,19,17,15,14,14, 14,15,16,18,20,23,26,30,34,38,43,48};
 ///=============== SINE WAVE LOOK UP TABLE ================//
 void Port_setting(){
 TRISC = 0x00;
 PORTC = 0x00;
 }
 
 void Timer_setting(){
     //INTCON =0b11100000 ;           /*//0xc0;0xa0*/
     TMR0 = 100;          /*//Stop timer 0 during set up*/
     INTCON.GIE = 1;
     INTCON.PEIE = 1;
     INTCON.TMR0IE = 0;
     
     T0CS_bit = 0;        /*// use internal clock to trigger timer to count*/
     PSA_bit = 0;         /*// Use the prescaler to slow the timer down*/

     /*// prescaler
     // 111 = 1:256 Prescale value
     // 110 = 1:128 Prescale value
     // 101 = 1:64 Prescale value
     // 100 = 1:32 Prescale value
     // 011 = 1:16 Prescale value
     // 010 = 1:8 Prescale value
     // 001 = 1:4 Prescale value
     // 000 = 1:2 Prescale value*/
     PS0_bit = 1; //0             /*//for 16 bit timer 1/4 of clock freq   001*/
     PS1_bit = 1; //1
     PS2_bit = 1; //0
 
 }
 
  void interrupt(){
 if(INTCON.TMR0IF){
   PORTC.B3 = ~PORTC.B3;
  
   if(flag==1){
   i++;
   PWM1_Set_Duty(sine[i]);
   if ( i == 49 ) flag = 0;
   }
   if(flag==0){
   i--;
   PWM1_Set_Duty(sine[i]);
   if ( i == 0 ) flag = 1;
   }
  
   TMR0 = 100;                             //Timer set for 20ms interrupt         
   INTCON.TMR0IF = 0;
 }
 }
void main() {
     Port_setting();
     Timer_setting();
     PWM1_Init(20000);                  //PWM at 20KHz
     PWM1_Start();
     
     INTCON.TMR0IE = 1;

     while(1)
     {

     }
}
At the output i am using 1kOhm and 102 to filter. However i am not getting the sine wave. Can anybody please guide me as to where am i going wrong.

- - - Updated - - -

If i use PWM frequency as 5K instead of 20k, i get atlest some part of sine wave, means half of the positive half cycle. For 20Khz, wave shape is like saw tooth.
For 5K PWM frequency,wave goes up to half of the positive sine wave then comes back..and it goes on.I am not getting complete sine wave atlest for positive half cycle.
 

At the output i am using 1kOhm and 102 to filter.
102 means 1 nF? 1k*1nF = 1µs time constant = low pass cut off frequency 160 kHz. How should it filter 20 kHz pwm?

Secondly, why are you stepping through the sine table up and down alternatingly? Makes no sense. A sine full cycle should be repeated continuously, either up- or downwards.
 

Yes, it is 1 nF. Though it will not filter but at least some sine wave kind of shape should be there, right? However that is also not there. Ok i will update the interrupt routine. But result is still same. After the peak, waveform becomes flat.
Code:
static unsigned char i = 0,flag=1;
//=============== SINE WAVE LOOK UP TABLE ================//
 const unsigned char sine[50] ={ 52,57,62,66,70,74,77,80,82,84,85,86,86, 86,85,83,81,78,75,72,69,65,61,56,52, 48,44,39,35,31,28,25,22,19,17,15,14,14, 14,15,16,18,20,23,26,30,34,38,43,48};
 ///=============== SINE WAVE LOOK UP TABLE ================//
 void Port_setting(){
 TRISC = 0x00;
 PORTC = 0x00;
 }
 
 void Timer_setting(){
     //INTCON =0b11100000 ;           /*//0xc0;0xa0*/
     TMR0 = 100;          /*//Stop timer 0 during set up*/
     INTCON.GIE = 1;
     INTCON.PEIE = 1;
     INTCON.TMR0IE = 0;
     
     T0CS_bit = 0;        /*// use internal clock to trigger timer to count*/
     PSA_bit = 0;         /*// Use the prescaler to slow the timer down*/

     /*// prescaler
     // 111 = 1:256 Prescale value
     // 110 = 1:128 Prescale value
     // 101 = 1:64 Prescale value
     // 100 = 1:32 Prescale value
     // 011 = 1:16 Prescale value
     // 010 = 1:8 Prescale value
     // 001 = 1:4 Prescale value
     // 000 = 1:2 Prescale value*/
     PS0_bit = 1; //0             /*//for 16 bit timer 1/4 of clock freq   001*/
     PS1_bit = 1; //1
     PS2_bit = 1; //0
 
 }
 
  void interrupt(){
 if(INTCON.TMR0IF){
   PORTC.B3 = ~PORTC.B3;
  
    i++;
   PWM1_Set_Duty(sine[i]);
   if ( i == 49 ) i = 0;   
  
   TMR0 = 100;                             //Timer set for 20ms interrupt         
   INTCON.TMR0IF = 0;
 }
 }
void main() {
     Port_setting();
     Timer_setting();
     PWM1_Init(20000);                  //PWM at 20KHz
     PWM1_Start();
     
     INTCON.TMR0IE = 1;

     while(1)
     {

     }
Is this interrupt routine Ok? If i use higher values of capacitor, wave shape will change, it may be sawtooth but never be sine.:roll:
 

The sine table as well as TMR0 suggests that you are generating one complete cycle. Then what do you do toggling PORTC.B3 ? Do you use any AND gate to derive symmetrical pulse train ? When a complete cycle is generated, then what is use of toggling ? I have seen some sine tables that corresponds to one half cycle of sine wave. Toggling AND gate and following same sine table, direction of H Bridge reversed resulting in generation of complete one sine wave cycle. You may post the schematic for more clarity. I have tested one project using 16F72, AND gate and observed that the output is clear sinusoidal. Please note: I have filtered the output using 2.5uF/250V capacitor.
 

If i use higher values of capacitor, wave shape will change, it may be sawtooth but never be sine.
Yes and no. Yes, because a first order filter (simple RC) will always produce an exponential waveform, not a sine. You'll need a higher order, preferably LC or active RC filter for a steeper characteristic that only passes the fundamental wave. No because a first order filter with a suitable cut-off frequency, e.g. 1-2 kHz, will give you a waveform nearer to a sine wave.
 

Toggling of PIN is just to confirm that interrupt is occurring. Pardon me for my foolish thinking. I am doing it on development board. And one oscilloscope.
 

See, you are updating the sin values after a long 20mS. What is your intended frequency of sine wave ? Is it 50Hz ? Since you have set the interrupt precisely at 20mS, I thought you are designing it for 50Hz. Input of your query is insufficient. Provide all relevant information of your requirement to have a clear conception. So far ISR is concerned, it is OK.
 

If the timer interrupt is every 20mS (I haven't checked the math) it will move to the next point along the sine curve at 20mS rate, not create a complete cycle. 50 * 20mS = 1Hz sine wave after filtering.

Brian.
 
Yes, frequency of the sine wave is 50Hz. So Timer interrupt is set for 20ms. Now as i am using 8Mhz crystal, so while calculation i divided it by 4. Is it correct? coz when I test the interrupt frequency on oscilloscope, it shows 1Khz. I don't know how this is happening. Apart from this, as i am using library for PWM, so what frequency do i need to enter in it so that pulses should be properly fit into the timer interrupt.
 

Hello!

It seems that you are missing some basics.
1. You want a sine wave at 50 Hz, which means that one period will be 20 ms, can we agree on that?
2. You want to produce the sine wave by PWM. How many points per period will you use?
---> Example: if you tell me you want for instance 20 points per period, then you should update your
PWM 20 times per sine period, therefore 1000 times per second.
3. Now let's talk about the PWM. PWM is a square wave with a varying ratio. Now how many discrete
levels do you want? If you tell me that you want for example 1000 levels (corresponding to 10 bits),
then the PWM clock frequency should be at least 1 MHz.
4. Now the filter: your PWM frequency is 1 kHz, which means that the highest possible sine frequency
that you can generate will be 500 Hz (but in this case with 2 points per period). This 500 Hz and above
should be clearly removed. But you want a sine wave at 50 Hz, so 50 Hz should be there.
Now it depends on what you do. How much attenatuation is OK at 50 Hz. If it's a standard -3 dB, then
you have to define the attenuation at 500 Hz. If it's -20 dB, then an order 1 is fine because you have
1 decade (50 ~ 500 hz), -40 -> order 2, -60 -> order 3 and so on.

Dora.
 
For a frequency of 50Hz, one complete cycle will take 20mS. During this 20mS your sine value should be updated right from start to end. Since the sine table consists 50 values, each sine value will continue for 20mS/50 = 400uS. So design the interrupt accordingly considering the clock frequency i.e. 8MHz.
 

Hello!

For a frequency of 50Hz, one complete cycle will take 20mS. During this 20mS your sine value should be updated
right from start to end. Since the sine table consists 50 values, each sine value will continue for 20mS/50 = 400uS.

In the general case (for example with a variable frequency sine), there is no relation between the number of values
in your table and the timer update rate.

In fact the best device I know for that kind of implementation is a TI chip. MSP430F5172 (and similar). The reason
is that it has high frequency timers. The CPU frequency is 25MHz max (I think I'm right on this one), but the frequency
of the high resolution timer is 256 MHz. Therefore you can update it more frequently and therefore output higher
analog frequencies. But I'm sure other makers have the same kind of devices, so check it if you are more used
to Atmel, PIC and so on.

Dora.
 

In the general case (for example with a variable frequency sine), there is no relation between the number of values
in your table and the timer update rate.


As it is clear from the post that variable frequency sine is not approached, I tried merely to point out the actual pit hole in the code. It seems that the guy simply undertakes to develop the code to notice the output on oscilloscope.
 

Do we have to divide clock frequency by 4 in the PIC for timer related calculations?
 

Do we have to divide clock frequency by 4 in the PIC for timer related calculations?
That depends on the formula you are using, the ones in the data sheet refer to actual crystal frequency.

The problem you have is conceptual. You are correct in saying the period of a 50Hz sine wave is 20mS but not realizing the sine shape is built from several discrete levels. To create a complete cycle in 20mS, you have to step through all the 50 levels (PWM values) every 20mS so the interupt rate has to be 50 times faster than you have it in your code.

When you take into account the interrupt overhead and additional code to read the PWM value from the array and load it in the PWM register, you might find you are running out of time before the next interrupt is due. This is why faster processors or optimized code is usually used.

Brian.

- - - Updated - - -

Do we have to divide clock frequency by 4 in the PIC for timer related calculations?
That depends on the formula you are using, the ones in the data sheet refer to actual crystal frequency.

The problem you have is conceptual. You are correct in saying the period of a 50Hz sine wave is 20mS but not realizing the sine shape is built from several discrete levels. To create a complete cycle in 20mS, you have to step through all the 50 levels (PWM values) every 20mS so the interrupt rate has to be 50 times faster than you have it in your code.

When you take into account the interrupt overhead and additional code to read the PWM value from the array and load it in the PWM register, you might find you are running out of time before the next interrupt is due. This is why faster processors or optimized code is usually used.

This is the nearest you will get to 50Hz at 8MHz clock using TMR0:
Code:
' Timer0 Registers: 
' Prescaler=1:16; TMR0 Preset=56; Freq=2,475.24752Hz; Period=404.00 µs
OPTION_REG.T0CS = 0 ' bit 5 TMR0 Clock Source Select bit:0=Internal Clock (CLKO) / 1=Transition on T0CKI pin
OPTION_REG.T0SE = 0 ' bit 4 TMR0 Source Edge Select bit: 0=low/high / 1=high/low
OPTION_REG.PSA  = 0 ' bit 3 Prescaler Assignment bit: 0=Prescaler is assigned to the Timer0
OPTION_REG.PS2  = 0 ' bits 2-0  PS2:PS0: Prescaler Rate Select bits
OPTION_REG.PS1  = 1 
OPTION_REG.PS0  = 1 
TMR0 = 56            ' preset for timer register(2 cycle adjusted)

Brian.
 

Please note - Each instruction takes 4 cycles of clock. Now, your processor is running at 8MHz, which means 8000000 clock/second. So every second 8000000/4 = 2000000 instructions will be executed. on the contrary - each instruction will take 1000000uS/2000000 = .5 uS.

Without prescaller every TMR count will have .5 uS. If 1:4 prescaller is assigned to TMR0, then you shall have to load the TMR0 register with a value of 55 to get TMR0 overflown after 400 uS.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top