PIC16F88 PWM Help

Thread Starter

rkrutz

Joined Dec 2, 2009
29
Hello,

I am trying to get the hardware pwm on the 16F88 to work. I currently have it hooked up to an LED, but I am having no luck with the voltage changing. I am basing my code of this tutorial here and have modified it to work with the 16F88.

From looking at the code it should have the LED be at two different brightnesses then shut off and then repeat. However right now all it does is turn on and stay on. Could someone please show me where I am going wrong?

I am using MPLABX and XC8.
Rich (BB code):
#include <stdio.h>
#include <stdlib.h>

#define _XTAL_FREQ 100000//Declare internal OSC Freq as 1MHz
#include <xc.h>
#include<pic.h>

__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_OFF & MCLRE_ON & BOREN_OFF & LVP_OFF & CPD_OFF & WRT_OFF & CCPMX_RB0 & CP_OFF);
__CONFIG(FCMEN_ON & IESO_ON);

main()
{

    TRISA=0;
    OSCCON=01111110;
     CCP1CON = 0b00001100;   // Enable PWM on CCP1
    TRISB = 0;     //
    T2CON = 0b00000100;     // Enable TMR2 with prescaler = 1
    PR2 = 249;   // PWM period = (PR2+1) * prescaler * Tcy = 1ms
    CCPR1L = 25; // pulse width = CCPR1L * prescaler * Tcy = 100us
     while(1)
    {
         //Shows Chip has been programed
         PORTAbits.RA0=1;
         __delay_ms(1000);
         PORTAbits.RA0=0;
        // 50% duty cycle for 500ms
        CCPR1L =125 ;
        __delay_ms(1000);

       // 10% duty cycle for 500ms
        CCPR1L = 25;
        __delay_ms(1000);

        // 0% duty cycle for 500ms
        CCPR1L = 0;
        __delay_ms(1000);
    }
}
 

tshuck

Joined Oct 18, 2012
3,534
CCP1 outputs on RB0, yet it looks like you have attached the LED to RA0. RA0 will be on from the beginning, RB0 will change with the different PWM values.
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
Thanks for the help. I have 1 LED on RA0 that I am using to make sure the code has been uploaded to the PIC and another on the CCP1 output. My mistake was I read the data sheet as RB3 was the only CCP1 pin and had my led hooked up there once I moved it to RB0 it works. However with that said how would I configure the CCP1 on RB3? Looking at the CCP1CON has me a little bit confused. I understand that the CCP1X and CCP1Y are used for the capture and compare modes, but the other four bits are not exactly clear to me. To get RB3 as a pwm pin would I need to set CCP1M1 and CCP1M2 to 1 and then configure T2CON differently?
 

tshuck

Joined Oct 18, 2012
3,534
Its due to the configuration word you have
Rich (BB code):
CCPMX_RB0
. Alternatively, some PICs have a special alternative pin function register (APFCON, if memory serves), so it isn't a configuration setting...

If you look at the footnote on the pin diagrams page, it tells you what selects the CCP1 pin.
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
So then due to it getting configured in the configuration the PIC16F88 can only have 1 pin be a pwm output at a time? So then if I wanted to use two pwm pins on different settings I would need to look for a chip that has the special function register? Also one last question to set the frequency to 1MHz all I need to do is this
Rich (BB code):
#define _XTAL_FREQ 100000//Declare internal OSC Freq as 1MHz
and
Rich (BB code):
OSCCON=01001110;
? I did this but the pin does not seem to be using a 1sec delay it is something faster, so I assume there is a step I am missing.
 

tshuck

Joined Oct 18, 2012
3,534
All the #define does is tell the delay routines what the oscillator is running at, you can set it to 1 if you want and not affect the function of the controller, beyond the delay routines.

There is only one output per CCP module in your microcontroller. You cannot output from the CCP1 to multiple pins in this case.

The PWM mouse if the CCP module runs off of timer 2, do you need to configure the oscillator (OSCCON) and timer 2 to do that. Read the datasheet to figure out what the requirements are to do that. It will include formulas you will need.

Perhaps when I get to my computer tonight, I will dive a little deeper...
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
Maybe I am not understanding this correctly. I have used the equations to set the values of CCPR1L and those seem to be working fine, since the higher duty cycle the brighter the LED.

Where my code is seeing the issue is in the __delay_ms(), which I thought was only effected by the OSSCON. When I configure OSCCON to 01111110, which according to the data sheet is what it should be for 8MHz the delay between the different duty cycles are 1 second like it should be. However when I configure OSCCON to 01001110, which should set the internal oscillator to 1MHz the delay becomes faster. By using the __delay_ms(1000) the delay should be the same at 1 second since the function takes care of the number of clock cycles required to reach that delay.

So my question is besides changing bits 4-6 in OSCCON to select the frequency is there something else I need to change to get the internal oscillator working properly at 1MHz from 8MHz? If not then the __delay_ms() command should still be producing the correct delay, or am I missing something with the command?
 

tshuck

Joined Oct 18, 2012
3,534
The delay_Ms function doesn't use what the oscillator is running at to determine the delay . What happens is the preprocessor goes through and replaces any _XTAL_FREQUENCY with the value and the delay function is calculated from that. Your #defined frequency should be equal to your actual frequency.


OSCCON sets the frequency at which the processor operates at.
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
Thank you for the explanation now that makes a lot more sense. So then what ever OSCCON is set to will be the frequency that my calculations should use for pwm? And if I wanted to use a different frequency for _XTAL_FREQUENCY that would only effect my delay? The reason I ask is because I like using the 1MHz as the frequency for the math because it is easier to use, but in order to get my __delay_ms() to work properly my _XTAL_FREQUENCY needs to be set at 8MHz.
 

tshuck

Joined Oct 18, 2012
3,534
Yes, anytime you see Fosc, that is the speed determined by the OSCCON register, be it an internal RC or external crystal, use this for your calculation.

Using a different XTAL_FREQUENCY will cause whatever uses this number to calculate whatever it needs to, assuming this is Fosc, regardless of whether or not it is.
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
Thank you for all your help, it is helping me grasp the functions of the PIC a lot quicker then just reading. It sounds like it will become a very powerful and useful tool in my electronics toolbox once I master it.

Thanks again.
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
I have another question on this topic. I have moved along to using PWM on a servo and I have it working pretty well. The one thing I have noticed is if I briefly tap the signal wire to the pin it provides the smoothest motion. If I want to automate this same response would using Timer0 be the best option? I am looking for a way to just send
Rich (BB code):
 CCPR1L = 62500 ;//Move to 180
        __delay_ms(10);
this once then shut off. I basically want to make a one shot system and instead of using a push button have it all software controlled, which is why I figured a timer is the next best option. Is this true or is there a easier/more effective way?
 

tshuck

Joined Oct 18, 2012
3,534
To be clear, you know that the servo will not hold it's position if you don't send it another position every 20ms, right?

What you could do is use a pwm with period of 1ms, and get 8-bits of resolution(with the way you are using it), and enable it every 20ms, and shut off 1ms after starting.


Or, you could set the pwm period to 20ms and inky use the values that will give you a pulse width between 1 and 2ms, sacrificing resolution for simplicity...

Not sure that answers your question, but it helps us be on the same page...
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
I did not know the servo won't hold its position because mine was looking like it did, but that does explain some of the issues. So basically what you are saying is if I want the servo to stay at the 180 deg position for a second then move to the 90 deg position for a second then the 0 deg position for a second I would need to do something like the following:
Rich (BB code):
 CCPR1L = 62500 ;//Move to 180 2.5ms
        __delay_ms(20);
      CCPR1L = 62500 ;
        __delay_ms(20);
      CCPR1L = 62500 ;
        __delay_ms(20); 
      CCPR1L = 62500 ;
        __delay_ms(20);
      CCPR1L = 37500 ;//move to 90 1.5ms
        __delay_ms(20);
      CCPR1L = 37500 ;
        __delay_ms(20); 
      CCPR1L = 37500 ;
        __delay_ms(20);
      CCPR1L = 37500 ;
        __delay_ms(20);
      CCPR1L = 12500 ;//move to 0 0.5ms
        __delay_ms(20);
      CCPR1L = 12500 ;
        __delay_ms(20);
      CCPR1L = 12500 ;
        __delay_ms(20);
      CCPR1L = 12500 ;
        __delay_ms(20);
What is a good rule of thumb if there is any for how long I should keep a servo in 1 position before I have to worry about burning out the motor?
 

tshuck

Joined Oct 18, 2012
3,534
The problem with your code is that the pwm is generated during your 20ms delay. Essentially, you will generate a pwm signal for 20ms, then do it again.

What you are trying to do, provided you want the 8-bit resolution, begs that you use interrupts. You could do something like:
-set pwm with pulse width, while having a 1ms period.
-wait for 1ms
-turn off the pwm module (or, at least its output)
-wait 19ms
-repeat

The servo is a motor with a control system built in, you needn't worry about burning out the motor from holding a position, unless doing so puts the motor out of operating parameters.
 

Thread Starter

rkrutz

Joined Dec 2, 2009
29
Now I understand I was thinking that the PWM would send a pulse of the duration I declared and keep repeating, which is why I put the delay in there to fit the requirements of the servo. However I should do something like you suggested by:
-sending the required pulse
-use a small delay to make sure that command has been executed
-then turn off my pwm by either using something like PORTBbits.RB3==0, which would require me to turn it back on before it could work again or use interrupts, which from looking at the Gooligum tutorial on them briefly requires using Timer0 and looks to be the more efficient way of solving this problem
-finish the delay period that equals 20ms
-repeat
 
Top