PIC16f887 start and start pwm

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
hi all i have the following code and i am trying to make the pwm run only when a specified pin is high example PORTB.F0 = 1; so far i have been unsuccessful. can anyone help please?


Code:
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);
}
 

JohnInTX

Joined Jun 26, 2012
4,787
Without going through the code in detail, if the PWM is working as required I'd do something like:

1)Split off the PWM initialization and start up into its own self-contained function. Call it startPWM() or something like that..
2) Write a new function that turns off the PWM (timer off, interrupt enable off etc) called stopPWM().
Make sure these two functions work and control the PWM in a consistent manner with a smooth shutdown/startup.

For control, all you have to do is expand the while(1) at the end of the code with a loop that samples the input and controls the PWM accordingly:

Code:
void initPWM(void)
{
 // code to setup the timer, duty cycle, IO, table pointer init etc. Leaves PWM off but ready to run
}
void startPWM(void)
{
  // code to start the pre-configured PWM
}

void stopPWM(void)
{
 // code to stop the PWM, preserving its configuration
}
//***** in main, after init IO etc..  *******************
   initPWM(); // set up PWM values so that startPWM and stopPWM can control it.
        //Leave PWM off

while(1){
    while (PORTB.F0 == 0);  // wait for control to ON
  
    startPWM();    // turn PWM on
  
    while (PORTB.F0 == 1);  wait for control to OFF

    stopPWM(); // turn PWM off

    }// while
This example doesn't do things like debounce the input etc. but is a starting point.

A few other points:
Don't set up your TRIS registers as individual bits - do it as a byte-write i.e. TRISB = 0bxxxxxxxx; Individual bit flipping on TRIS can be problematic.
You don't have to do the wait for TMR2 after configuring it. Do all the config with the timer OFF then clear TMR2IF, enable the interrupts then turn the timer ON.

Good Luck.
 

spinnaker

Joined Oct 29, 2009
7,830
hi all i have the following code and i am trying to make the pwm run only when a specified pin is high example PORTB.F0 = 1; so far i have been unsuccessful. can anyone help please?
I don't see where you are detecting tha value of the pin.

And the whole purpose of the code tag is to preserve the tabs. No tabs makes your code very hard to read.
 

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
i have tried everything i could think of and please excuse my limited coding knowledge. The code is not mine i have adjusted it to serve my purpose. if you could please provide more help it would be appreciated.
 

spinnaker

Joined Oct 29, 2009
7,830
You first need to understand what you have and what you want to do. For example I see a timer interrupt code in there. What is the purpose of that code?
 

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
it is to call each value of the table (sin_table) four times. this is done so because i wanted a sinewave of 60Hz generated with pwm frequency of 16KHz.
 

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
i have done that i also tried the method suggested by johninTx. i have exhausted all my resources which brings me to the reason why i posted my problem in this forum.
 

JohnInTX

Joined Jun 26, 2012
4,787
i have done that i also tried the method suggested by johninTx. i have exhausted all my resources which brings me to the reason why i posted my problem in this forum.
Since you copied code from the internet, have you taken the time to understand how it works?

Post your revised code that shows the new PCM control functions and how you call them in response to the input. We can go from there.
 

spinnaker

Joined Oct 29, 2009
7,830
i have done that i also tried the method suggested by johninTx. i have exhausted all my resources which brings me to the reason why i posted my problem in this forum.

So you just want an answer but aren't willing to learn? Sorry but I do not think we can help you.

Break your project into small pieces. Write a program that does nothing but read input from a pin. The link I provided will help you to understand how to do that.
 

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
Code:
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};   //100% duty cycle

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 Run_PWM(){
SET_FREQ = 1024;
TBL_POINTER_SHIFT = 0;
TBL_POINTER_NEW = 0;
TBL_POINTER_OLD = 0;
DUTY_CYCLE = 0;
PR2 = 0b01111100; //124
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;
}


void main() {
ANSEL = 0; //Disable ADC
CM1CON0 = 0; //Disable Comparator
CM2CON0 = 0;
TRISC.F2 = 0;
TRISD.F5 = 0;
TRISD.F6 = 0;
TRISD.F7 = 0;
TRISB.F0 = 1;
PORTC.F2 = 0;
PORTD.F5 = 0;
PORTD.F6 = 0;
PORTD.F7 = 0;
PORTB.F0 = 1;

while (PORTB.F0 = 1) {
  Run_PWM();
}

while(1);
}
I have tried running it using if and do while functions but as soon as i put the run_pwm() inside a loop in the main my output is 0 from all four pwm pins no matter the state of the PORTB.F0.
 

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
Note that i have been able to run and stop the pwm by using the input PORTB.Fo as analog pin but in that case the ADC conversion interferes with the timing of the pwm and results in bad pwm signal.
 

JohnInTX

Joined Jun 26, 2012
4,787
OK, a few problems here.
In the 887A, you have to clear bit(s) in ANSELH for RB0, bit 4 (ANS12) must be 0 to allow reading from RB0 as a digital input. In chapter 3 of the databook, all the registers associated with all the ports are described. That's one reason your port input does not work.

You can't put startPWM in a while loop, it just keeps restarting the PWM. If you look at the code snippet I posted above you'll see
Code:
while(1)  //run the next 4 statements forever..
{
while(PORTB.F0==1);  //the colon at the end of the line says to loop right there as long as the input is 1.  When it is, the next statement will be executed:
startPWM(); // executed ONCE then wait on the port in the next line..
while(PORTB.F0==0); // wait for the port to be 1 this time..
stopPWM(); // then turn off the PWM - again ONCE
} // while
See the difference? For each IO change, startPWM/stopPWM only gets called once.

Finally, you missed a double == in your port test. Your code assigns 1 to the still-analog input (!) then proceeds.

Make sense?
 
Last edited:

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
Thanks a lot JohnInTx. i followed you suggestion and wrote the following code.
Code:
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};   //100% duty cycle

unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
unsigned int TBL_temp;
unsigned char DUTY_CYCLE;
unsigned int temp;

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 Run_PWM(){
PORTC.F2 = 0;
PORTD.F5 = 0;
PORTD.F6 = 0;
PORTD.F7 = 0;
SET_FREQ = 1024;
TBL_POINTER_SHIFT = 0;
TBL_POINTER_NEW = 0;
TBL_POINTER_OLD = 0;
DUTY_CYCLE = 0;
PR2 = 0b01111100; //124
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;
}
void Stop_PWM(){
PORTC = 0;
PORTD = 0;
}


void main() {
ANSEL = 0; //Disable ADC
ANSELH = 0x00;
CM1CON0 = 0; //Disable Comparator
CM2CON0 = 0;
TRISD.F0 = 1;
TRISC.F2 = 0;
TRISD.F5 = 0;
TRISD.F6 = 0;
TRISD.F7 = 0;
TRISB.F0 = 0;
PORTC.F2 = 0;
PORTD.F5 = 0;
PORTD.F6 = 0;
PORTD.F7 = 0;
PORTB.F0 = 0;
PORTD.F0 = 0;
SET_FREQ = 1024;
TBL_POINTER_SHIFT = 0;
TBL_POINTER_NEW = 0;
TBL_POINTER_OLD = 0;
DUTY_CYCLE = 0;
PR2 = 0b01111100; //124
CCP1CON = 0b01001100; // duty lowest bits + PWM mode

do{
if (PORTD.F0==0){
   PORTB.F0 = 0;
   Stop_PWM();
        }
else {
   Run_PWM();
   PORTB.F0 = 1;
  }

}while(1);
}
now i have a different issue as described with simulation in the attached pdf file i am getting the wrong signals and at the wrong times.
I want it to start with all 4 port as low then when PORTB.F0 is set high it should have wave form of 3 And when I set PORTB.F0 back to low all 4 pins should go low. I want the pwm to start from scratch whenever I set PORTB.F0 high.
 

Attachments

JohnInTX

Joined Jun 26, 2012
4,787
To stop the PWM, you need to disable it in CCP1CON then set the output ports to the desired OFF values. A running PWM will override the port values. Also, turn off the interrupts, stop the timer etc. A good way to know what to do to turn it off is to undo what you did to turn it on. Go through startPWM step by step and make sure you catch everything.

Your do-while loop still calls startPWM or stopPWM each time through. You can't do that. Review what I wrote and what you have, go through the code line by line until you see how they are different.
 

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
i changed the code
Code:
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};   //100% duty cycle

unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
unsigned int TBL_temp;
unsigned char DUTY_CYCLE;
unsigned int temp;
char i;

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 Run_PWM(){
PORTC.F2 = 0;
PORTD.F5 = 0;
PORTD.F6 = 0;
PORTD.F7 = 0;
SET_FREQ = 1024;
TBL_POINTER_SHIFT = 0;
TBL_POINTER_NEW = 0;
TBL_POINTER_OLD = 0;
DUTY_CYCLE = 0;
//ECCPAS = 0b00000101; //autoshutodwm disabled
PR2 = 0b01111100; //124
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;     // Timer2 interrupt flag
TRISC = 0;
TMR2IE_bit = 1;      //interrupt when TMR2 matches PR2
GIE_bit = 1;      // Global interrupt enabled
PEIE_bit = 1;     // peripheral interrupt enabled
ECCPAS =0b11000000;       //autoshutodwm on INT pin high
PWM1CON    = PWM1CON +   0b10000000  ;
}

void main() {
ANSEL = 0; //Disable ADC
ANSELH = 0x00;
CM1CON0 = 0; //Disable Comparator
CM2CON0 = 0;
TRISC.F2 = 0;
TRISD.F5 = 0;
TRISD.F6 = 0;
TRISD.F7 = 0;
TRISB.F0 = 1;
PORTC.F2 = 0;
PORTD.F5 = 0;
PORTD.F6 = 0;
PORTD.F7 = 0;
  PORTB.F7 = 0;
  Run_PWM();

while(1);
}
the pwm is interrupted with the enhanced pwm auto-shutdown ECCPAS. My new problem is that the code runs pwm at the beginning and stops after the second time i set the porb.f0 /INT pin low then runs again when i set it high. i want it to not run at the beginning when portb.f0 is initially low and start running after i set it high. your suggestions are more than welcome regarding this issue. thank you
 

JohnInTX

Joined Jun 26, 2012
4,787
Yeah.. the auto shutdown is not designed to be an on/off switch.

If you want to try using the shutdown feature as a control, do the power up function manually then configure the PWM for auto shutdown. So..
1)on power up with the PWM off, poll RB0 until its high.
2)when RB0==1, go ahead and configure the PWM with auto shutdown on INT/ (RB0)
3)the PWM should now follow RB0

You should still have a stopPWM function in case you want to turn it off under software control. I confess I didn't know the 887 had an auto-shutdown feature. I probably would not use it as a control for the reason you experienced plus I'd probably use it as a safety device... but give it a try.

Let us know how that works.
 

Thread Starter

Tamim Neak

Joined Feb 23, 2015
13
Yeah.. the auto shutdown is not designed to be an on/off switch.

If you want to try using the shutdown feature as a control, do the power up function manually then configure the PWM for auto shutdown. So..
1)on power up with the PWM off, poll RB0 until its high.
2)when RB0==1, go ahead and configure the PWM with auto shutdown on INT/ (RB0)
3)the PWM should now follow RB0

You should still have a stopPWM function in case you want to turn it off under software control. I confess I didn't know the 887 had an auto-shutdown feature. I probably would not use it as a control for the reason you experienced plus I'd probably use it as a safety device... but give it a try.

Let us know how that works.
I have the following code now. i know it is not great but it does the job.
Code:
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};  //100% duty cycle

unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ;
unsigned int TBL_temp;
unsigned char DUTY_CYCLE;
unsigned int temp;
char i;

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 Run_PWM(){
PORTC.F2 = 0;
 PORTD.F5 = 0;
 PORTD.F6 = 0;
 PORTD.F7 = 0;
 SET_FREQ = 1024;
TBL_POINTER_SHIFT = 0;
TBL_POINTER_NEW = 0;
TBL_POINTER_OLD = 0;
DUTY_CYCLE = 0;
ECCPAS =0b11000000;  //autoshutodwm on INT pin high
//ECCPAS = 0b00000101; //autoshutodwm disabled
PR2 = 0b01111100; //124
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;  // Timer2 interrupt flag
TRISC = 0;
TMR2IE_bit = 1;  //interrupt when TMR2 matches PR2
GIE_bit = 1;  // Global interrupt enabled
PEIE_bit = 1;  // peripheral interrupt enabled
PWM1CON  = PWM1CON +  0b10000000  ;
//ECCPAS &= 0x7f;
}
void Stop_PWM(){
while( PORTB.F0 == 0);
}

void main() {
ANSEL = 0; //Disable ADC
ANSELH = 0x00;
CM1CON0 = 0; //Disable Comparator
CM2CON0 = 0;
TRISC.F2 = 0;
TRISD.F5 = 0;
TRISD.F6 = 0;
TRISD.F7 = 0;
TRISB.F0 = 1;
 PORTC.F2 = 0;
 PORTD.F5 = 0;
 PORTD.F6 = 0;
 PORTD.F7 = 0;
  PORTB.F0 = 0;
  Stop_PWM();
  Run_PWM();

while(1);
 }
Thanks a lot for your help. let me know if you have any suggestions to improve the code
 
Top