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] PIC16f887 how to achieve 60HZ pwm frequency

Status
Not open for further replies.

Tamim Neak

Junior Member level 1
Junior Member level 1
Joined
Feb 15, 2015
Messages
18
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Visit site
Activity points
137
Hi all,

I want to use a PIC 16F887 to generate a 60Hz SPWM signal for a full bridge. I want to know what Timer2 prescalar value, PR2 and (external or internal) crystal oscillator to use. Thanks for the help.
 

I did but didn't know what duty cycle to use and all the values i tried give me errors.
 

As a good measure, you could define a half sine wave with a power-of-two points of resolution, for example 32 divisions per half bridge yields a good shape. It means that you should generate a table for pickup values to load duty-cycle register containing on that case: 32/2 points ( 1/4 sine wave ). Therefore, the required rate of PWM period in that case is 60*4*16=3840Hz.

Back to the link, You need apply for instance 20.000.000 Hz to the 1st textbox, 100% to the 3rd textbox, but to the 2nd textbox, you´ll need to perform the following calculus:
  • sin(16*90o/16)*100%
  • sin(15*90o/16)*100%
  • sin(14*90o/16)*100%
  • sin(13*90o/16)*100%
  • sin(12*90o/16)*100%
  • sin(11*90o/16)*100%
  • sin(10*90o/16)*100%
  • sin(9*90o/16)*100%
  • sin(8*90o/16)*100%
  • sin(7*90o/16)*100%
  • sin(6*90o/16)*100%
  • sin(5*90o/16)*100%
  • sin(4*90o/16)*100%
  • sin(3*90o/16)*100%
  • sin(2*90o/16)*100%
  • sin(1*90o/16)*100%
  • sin(0*90o/16)*100%

Each one of these duty cycles will generate a new value that must be updated to PWM register. Do not forget that you need to activate Timer interrupt to give the proper timing to store each new value.
 

I have been using the following post by Mr. Tahmid
https://tahmidmc.blogspot.ca/2013/02/demystifying-use-of-table-pointer-in.html
and i have made the following calculations for my case can someone go over it and tell me if i am correct.

1/60hz =0.01667 0.01667/2 = 8.33e-3 taking pr2= 249 and oscillator frequency 8MHz and prescalar =1 ((250*4*1)*32)/8e6 = 4e-3

8.33e-3/4e-3 = 2.083 in the code Tahmid uses 2^11 =2048 pointer shift so u havent changed that

but i have changed the set_freq to 2048/2 = 1024
 

The frequency 2.048Hz that you selected for the sample rate above, for 60Hz provide a required value of 34,13 points per each sine wave full cycle, what means only 8,53 samples for each 1/4 cycle, yielding a poor resolution in my opinion, depending on the target application.

Anyway, putting these values into the calculator that I posted above, and supplying 100% of duty cycle as full scale, result on something not at all too different from what you found:
  • PR2 = 243
  • Prescaler Value = 4
 

Full-bridge.png

In the code in the forward path when Q1 is high Q4 is toggled on and off. Is it possible to make Q3 do the opposite of Q4. i mean when Q4 is on Q3 should be switched off and when Q4 on it should be switched off. Is it a good idea to do so and if it is how would i do it?
 

I did´t understand exactly your doubt, but the working principle of a full-bridge topology consists of the alternate conducting of the switches placed diagonally in opposite (e.g. Q1/Q4 or Q3/Q2), what means that the current flowing through L1 will have different polarity on that operation.
 

i mean when Q4 is on Q3 should be switched off and when Q4 on it should be switched off. Is it a good idea to do so and if it is how would i do it?

If you turn off both Q3 and Q4, you are likely to have high-voltage spikes. This is characteristic of an inductor when you switch it off while it is carrying current, and cause it to see high impedance.

To avoid this, it is a good idea to have Q3 conduct when Q4 is off (and vice-versa).

Notice the body diode may do this automatically. Then this allows the inductor to see low impedance.

In my mind that is a lot to ask of the diode, however. Some kind of snubbing network may be wise.
 

I am sorry i made a mistake what i meant was that in the positive half of the cycle when Q1 is high and Q4 is switching, is it possible to send to Q3 the inverse signal of Q4. Q4on==Q3 off and Q4 0ff == Q3 on.
Same thing for the negative half of the cycle when Q3 is high and Q2 is switching, make Q1 do the inverse of Q2 so Q2 on ==Q1 off and Q2 off == Q1 on.

This is because i am concerned about the situation when Q1 is on and Q4 is off. Q1 will be floating. Well this cause any problems?
 

i am concerned about the situation when Q1 is on and Q4 is off. Q1 will be floating. Well this cause any problems?

In order to you better get the understanding of the circuit, should to analyse separately the driving of the switches, and the switches operation on the topology itself. Concerning to the driving of Q1, the gate is referred to its source, which has a stable higher voltage driving circuit what ensures its biasing even if Q4 is open. Once Q1 driver must have a gate resistor that will provide the reference to the source, Q1 will not float.
 
Thank a lot. I changed the pwm frequency to 16KHz and using the website i got PR2= 124 and prescalar 1
Can you please go over my code and let me know if you see any problems. your suggestions are more than welcome.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
unsigned char sin_table[64]={0, 6, 12, 18, 24, 30, 36, 42, 48, 53, 59, 64, 69, 74, 79, 84, 88, 93, 97, 100, 104, 107, 110, 113, 115, 118, 120, 121, 123, 124, 124, 125, 125, 125, 124, 124, 123, 121, 120, 118, 115, 113, 110, 107, 104, 100, 97, 93, 88, 84, 79, 74, 69, 64, 59, 53, 48, 42, 36, 30, 24, 18, 12, 6};
 
unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
unsigned int TBL_temp;
unsigned char DUTY_CYCLE;
 
void interrupt(){
if (TMR2IF_bit == 1){
TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
if (TBL_POINTER_NEW < TBL_POINTER_OLD){
CCP1CON.P1M1 = ~CCP1CON.P1M1; //Reverse direction of full-bridge
}
TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 10;
DUTY_CYCLE = TBL_POINTER_SHIFT;
CCPR1L = sin_table[DUTY_CYCLE];
TBL_POINTER_OLD = TBL_POINTER_NEW;
TMR2IF_bit = 0;
}
}
 
void main() {
SET_FREQ = 1024;
TBL_POINTER_SHIFT = 0;
TBL_POINTER_NEW = 0;
TBL_POINTER_OLD = 0;
DUTY_CYCLE = 0;
ANSEL = 0; //Disable ADC
CM1CON0 = 0; //Disable Comparator
CM2CON0 = 0;
TRISC.F2 = 0;
TRISD.F5 = 0;
 TRISD.F6 = 0;
 TRISD.F7 = 0;
 TRISC.F0 = 1;
 TRISC.F1 = 1;
 PORTC.F2 = 0;
 PORTD.F5 = 0;
 PORTD.F6 = 0;
 PORTD.F7 = 0;
 
PR2 = 0b01111100;
CCP1CON = 0b01001100; // duty lowest bits + PWM mode
TMR2IF_bit = 0;
T2CON = 4; //TMR2 on, prescaler and postscaler 1:1
while (TMR2IF_bit == 0);
TMR2IF_bit = 0;
TRISC = 0;
TMR2IE_bit = 1;
GIE_bit = 1;
PEIE_bit = 1;
 
while(1);
}

 

Now you have a code containing something about 128 resolution points per full-cycle what seems a quite reasonable amount of samples to synthesize a sin waveform with a good shape and lower harmonic components due to quantization.

As you probably may have noticed, the main() is empty; all duty-cycle update operation occurs in the Timer interrupt vector. Note that in the main program, can not be performed any continuous processing exceeding 1/128 seconds or ~ 7ms which even insomuch restricted is still able to run thousands of instructions if you are running with a crystal of 20MHz.

What I did in the drive that developed in the past, was to create within the main() a code structure that makes the allocation of tasks alternately within a "thread" ( excuse the word used incorrectly). In other words, you must separate each part of your original program into several parts

Bellow follow is a model of what I'm talking about, on what was not yet filled with the program routines, but has just the 2 routines responsible for change the direction of PWM_counter variable ( the same was done on line 11 of your code ):


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void LoadPWM ( void )
{
   if ( direction== UP) PWM_counter++    ;
      else                     PWM_counter--    ;
   switch ( PWM_counter )
      {
      case 0  : LevelMin() ; set_pwm1_duty ( 0 )                 ; break ;
      case 1  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 2  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 3  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 4  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 5  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 6  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 7  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 8  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 9  :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 10 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 11 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 12 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 13 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 14 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 15 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 16 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 17 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 18 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 19 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 20 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 21 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 22 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 23 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 24 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 25 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 26 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 27 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 28 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 29 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 30 :                 set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      case 31 : LevelMax(); set_pwm1_duty ( Duty[PWM_counter] ) ; break ;
      }



Remark:
The code above is slightly different from that was proposed by the website from which you took your code. In my case, I did not update the PWM duty-cycle in the interrupt vector, but I configured only one flag there, that would enable this change in main(), in my case.
 
when Q1 is high and Q4 is switching, is it possible to send to Q3 the inverse signal of Q4. Q4on==Q3 off and Q4 0ff == Q3 on.
Same thing for the negative half of the cycle when Q3 is high and Q2 is switching, make Q1 do the inverse of Q2 so Q2 on ==Q1 off and Q2 off == Q1 on.

This can be done by using Pmos at the high side.

Here is a simulation which demonstrates an easy way (theoretically) to achieve this. (I am not saying it is the optimum way.)

Clock signals are high during one-half of the 50 Hz cycle. This keeps the Nmos turned on, but the Pmos shut off.

Notice current flows upward through the Nmos at times. It allows the inductor to continue to propel current during gaps.

H-bri N&Pmos SPWM opamps 24V ind-res load.png

I made the load partly inductive, partly resistive. It mimics how a waveform might appear through the transformer primary.
 

I am trying to make the program run only if portb.f0 = 1; but so far i have been unsuccessful. Please forgive my limited knowledge in programming. any help regarding this is appreciated. Thank you.
 

i went over it and couldnt find anything. Can you please be more specific? Thank you
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top