50Hz square wave example for PIC16F1829

This post presents two example programs for the PIC16F1829 microcontroller that generate 50Hz square waves. The first example uses simple bit banging to generate the waveform. The second example uses the dedicated PWM hardware module.

Example 1: bit banging

In this example program, I set a single pin as a digital output (RC5), then generate a 50Hz square wave using straightforward bit banging.

The clock oscillator is left at its default frequency of Fosc = 500kHz. Therefore, the oscillator period Tosc = 2us. Since the PIC16F1829 performs one machine instruction every four oscillator cycles, the instruction cycle, Tcy = 8us.

//
// PIC16F1829 50Hz 50% duty cycle example program
// Written by Ted Burke (http://batchloaf.com)
// Last updated 5-6-2013
//
// To compile with XC8:
//     xc8 --chip=16F1829 main.c
//
  
#include <xc.h>

// Select the internal oscillator (default Fosc = 500kHz, which gives
// an instruction cycle Tcy = 8us), disable low voltage programming,
// disable reset pin, disable watchdog timer
#pragma config FOSC=INTOSC,LVP=OFF,MCLRE=OFF,WDTE=OFF

int main(void)
{
	// Make RC5 a digital output
	TRISCbits.TRISC5 = 0;

	// Now just switch RC5 high and low repeatedly
	while(1)
	{
		LATCbits.LATC5 = 1; // Set RC5 high
		_delay(1250); // 10ms delay @ Fosc = 500kHz, Tcy = 8us
		LATCbits.LATC5 = 0; // Set RC5 low
		_delay(1250); // 10ms delay @ Fosc = 500kHz, Tcy = 8us
	}

    return 0;
}

I don’t have a PIC16F1829 to try this out on, so the best I could do was test it using MPLAB SIM (the simulator in MPLAB v8.50). Here’s a screenshot I captured of the Simulator Logic Analyzer tool (under the View menu in MPLAB) displaying the generated waveform.

MPLAB Simulator Logic Analyzer screenshot showing generated square wave

The x-axis units in the figure above are instruction cycles, so the period of the generated signal is approximately 2500 instruction cycles.

\textrm{frequency} = \frac{1}{2500 \times 8 \times 10^{-6}} = 50 \textrm{Hz}

Careful examination of the waveform in the figure above reveals that the period is actually not precisely 2500 instruction cycles. This may be due to the overhead associated with the additional instructions caused by the while loop and bit-banging. If precision is required, the delay values could be carefully tweaked to compensate. However, it is worth bearing in mind that the frequency of the internal RC oscillator can vary, so if precision is required an external crystal oscillator might be a better choice.

Example 2: using the PWM module

Here’s another example of a 50Hz square wave. This time, the PWM module is used:

//
// PIC16F1829 50Hz 50% duty cycle example program
// Written by Ted Burke (http://batchloaf.com)
// Last updated 7-6-2013
//
// To compile with XC8:
//     xc8 --chip=16F1829 main.c
//
   
#include <xc.h>
 
// Select the internal oscillator (default Fosc = 500kHz, which gives
// an instruction cycle Tcy = 8us), disable low voltage programming,
// disable reset pin, disable watchdog timer
#pragma config FOSC=INTOSC,LVP=OFF,MCLRE=OFF,WDTE=OFF
 
int main(void)
{
    // Set up PWM
    T2CON = 0b00000110;   // Enable TMR2 with prescaler = 16
    PR2 = 155;            // PWM period = (PR2+1) * 16 * Tcy = 19.968ms
    CCPR1L = 78;          // Pulse width = CCPR1L * 16 * Tcy = 9.984ms
    CCP1CON = 0b00001100; // Enable PWM on CCP1
    TRISCbits.TRISC5 = 0; // Make CCP1 pin an output
     
    // Now just do nothing while PWM module does the work
    while(1);
     
    return 0;
}

Configuring the PWM module is a little more complicated than just bit banging to generate the signal. However, it has (at least) two very important benefits:

  1. The program can get on with other tasks while the square wave is generated continuously in the background by the dedicated PWM hardware module.
  2. Since there are no extra instructions associated with a while loop or anything else, it’s much easier to determine the exact length of the waveform period and pulse width (measured in instruction cycles).
This entry was posted in PIC and tagged , , , , , , , , , , , . Bookmark the permalink.

27 Responses to 50Hz square wave example for PIC16F1829

  1. help says:

    Haha… actually i already have did the “lazy way” as you do and i got the same result.
    But after i saw your post, what i willing to have, is learn a different method to get the 50Hz via your way. It look more “Advanced”.
    So, you have to be hardworking to teach me here. (^_^)

  2. help says:

    Hardware?
    not we could just use the Proteus for the simulation?

    • batchloaf says:

      Yes, you can probably simulate it in Proteus (although I haven’t used that software myself). The real PIC has dedicated hardware for generating PWM signals very precisely in the background while your C program is getting on with other tasks. Proteus can probably simulate that hardware functionality, but when you run it on the real system, the PWM will be generated by dedicated hardware on the chip.

      When I said about using the dedicated hardware to generate the squarewave, that was in contrast to the bit banging approach – writing a loop to set a digital output pin equal to 0, 1, 0, 1, 0, 1,… etc.

      Hopefully my example which I will post soon will clarify the difference.

    • batchloaf says:

      Ok, I added in another example. This time I used the PWM hardware module, which is probably a better idea in the long run. Let me know if you have any questions.

      Ted

  3. Help says:

    it take me few days to understand the code.
    it really advanced than the previous, as a newbie , I learned new thing : )
    thank you VERY much, if I have somemore question i’m will back here again.
    THANKS!!

  4. bala says:

    Can anybody give me circuit diagram for pwm square wave generator i want to control a bldc motor with this… Thanks in advance

  5. John says:

    Hey I am using a PIC 16F1829 and I want to determine the frequency of an incoming square wave how would I go about this. I am trying to use the ccp_capture_div_16 but the code it proving to be difficult can you help?
    Thanks,

  6. Charlie says:

    Thanks for the examples! They’re great, but as I try to soak this up and understand it, I have some questions about the hardware PWM example.

    Following your example, the PWM Period = (155 + 1) * 64 * .000008 which for me comes to 79.872ms (about 25 Hz). Also according to your example, the Pulse Width = 78 * 64 * .000008 which comes to 39.936ms which does appear to be 50% duty cycle of the 25Hz frequency. Can you clear up this misunderstanding?

    Also can you explain why there was no need to take the DC1B (2 LSB of duty cycle) into consideration in this example? I would think that minimally the “78” would need to be bit shifted into CCPR1L (8 MSB of duty cycle). I must be missing something though because I do appear to get a 50% duty cycle using your example code.

    Thanks!

    • Charlie says:

      Actually 79.872ms is closer to 12.5Hz I think…I must be overlooking something big while trying to learn this.

      • Charlie says:

        Perhaps you meant to use 16 as the TMR2 prescaler? That works out to 19.968ms as you have written in the comment of your code. Sorry for all of the messages; feel free to just delete them. I’m just trying to get a handle on this stuff!

      • batchloaf says:

        Hi Charlie,

        Thanks for your comments. Looking at it now, I think you’re dead right that something was out of place here. I wrote this example upon request from a reader and unfortunately I didn’t (and still don’t) have a chip to test it on, so I didn’t spot the problem. I’ve updated the code in the second example (hardware PWM module) to set the prescaler to 16:1. I think it should now be a 50Hz waveform with a 50% duty cycle!

        Just to explain about the last two bits of the duty cycle value:

        To give better PWM resolution, the 10-bit duty cycle value is actually a multiple of the oscillator period (Tosc), rather than the instruction cycle (Tcy) which is used for most other time values. Note that Tosc = Tcy / 4, so that results in 4 times higher resolution. Personally, I find this confusing! Even though I’ve been using PICs for years, I still find myself making silly mistakes because of the way the PWM registers are laid out. Anyway… bits 5 and 4 of CCP1CON (that’s DC1B1-2) are the two least significant bits of the 10-bit duty cycle value. The 8 most significant bits are in CCPR1L. Basically, in my example, I’m just leaving the two least significant bits set to zero (the default) and writing 8-bit duty cycle values into CCPR1L. Even though I’m leaving the two 0 bits untouched, they still get added onto the end my 8 bit CCPR1L value, with the effect of multiplying it by 4 (equivalent to left bit-shifting the CCPR1L value by 2).

        So basically, the data sheet (Section 24.3.5) states that the pulse width is

        pulse width = (CCPRxL:CCPxCON<5:4>) * Tosc * (TMRx Prescale Value)
        

        which is almost equivalent to how I’m calculating it:

        pulse width = CCPR1L * Tcy * (TMR2 Prescale Value)
        

        where

        Tcy = 4 * Tosc.
        

        Hopefully that helps to clarify it!

        If you can check the updated example on your hardware, I’d really like to know that it’s correct, so please let me know how you get on.

  7. Charlie says:

    Yep, that clarifies a bunch! Actually plugging in the numbers according to the datasheet’s formula for the pulse width, I get a value of 307.5 which is closer to 77 for CCPR1L when divided by 4, but that’s splitting hairs.

    I’m in the middle of another project at the moment (that’s why I was researching PWM to begin with) so it may be a few days, but yes, I’ll check the results of your hardware example on my chip & scope and let you know. I’m near 100% sure that it’s correct though.

    • batchloaf says:

      I actually only calculated the PR2 value directly to get as close to 50Hz as I could. Then I just calculated the CCPR1L value from the PR2 value to give a 50% duty cycle, i.e.

      CCPR1L = (PR2 + 1) / 2

      I haven’t really thought through the implications of the 2 least significant bits in the 10-bit duty cycle value. I was really just thinking of the duty cycle as an 8-bit value in units of Tcy, which is a little bit sloppy, but that’s how I was calculating it. Either way, it’s pretty close!

      Thanks again for picking this apart so carefully – it’s always good to iron out a mistake like this since it can cause so much confusion for others trying to make sense of example code.

      Ted

  8. Charlie says:

    There’s no need to post this to your blog because I want to verify the results on a real oscope, but my diy usb scope is showing the period to be about 40ms…still twice what we expect. I’ve checked & triple checked the calculations and the value of the TMR2CON register, and I come up with the same values that you have. If I change the Fosc to 1MHz, then I get a 20ms period.

    But my oscope was a project that I found on the net and built. It’s value for the period might not be correct. I’ll try to have someone verify those results for me on Monday on a real scope.

  9. batchloaf says:

    Based on section 5.2.2.5 of the datasheet, my understanding is that the default frequency of the internal oscillator is 500 kHz (it’s described as the “Default after Reset”).

    In most of the PIC microcontrollers I’ve used, Tcy = 4 * Tosc (i.e. one instruction is carried out every 4 oscillator cycles). Based on Figs 8.3 and 8.4, it looks like this is the case for the PIC16F1829.

    Therefore, I have been working on the assumption that, by default,

        Fosc = 500hKz            (oscillator frequency)
        Tosc = 2us               (oscillator period)
        Tcy = 8us                (instruction cycle)
    

    Since I don’t have an actual PIC16F1829 chip, it’s difficult for me to confirm my above assumptions. It took me a while fishing around the datasheet to work out what the default should be, so I could easily have missed something!

    As a matter of interest, have you tried running the first example from this post? It produces a 50Hz square wave (at least that’s what it’s supposed to produce) using a simple bit-banging approach. It delays 1250 instruction cycles for the negative half cycle and 1250 more for the positive half cycle, giving a total period…

        T = (1250 + 1250) * Tcy = 20ms
        f = 1 / T = 50Hz
    

    If the first example produces the right frequency but the second one doesn’t, that will help to pin down the problem because we’ll know that Tcy really is 8us. Could you check that please?

    Ted

    • Charlie says:

      I get the same 40ms period using the bit banging code. I don’t trust my scope though, so I’ll get a real electronics engineer to check it on a real scope in a few days.

      • batchloaf says:

        Ok good. It sounds like either:

        a.) The scope is sampling at a different rate than it’s supposed to be,
        b.) The PIC16F1829 is (for some reason) not really running at 500kHz,
        c.) Tcy is not equal to 4*Tosc on the PIC16F1829.

        I’ll be back in the office tomorrow. I’ll ask around to see if anyone has a PIC16F1829 I can borrow to try the code out myself.

        In the mean time, here’s a very simple experiment you can use to confirm whether Tcy is 8us or not, without relying on your scope:

        Modify the first example program so that the square wave period is MUCH slower and then connect an LED to the output pin so that you can measure the frequency yourself with a stopwatch. For example, you could change lines 26 and 28 to:

        _delay(125000);
        

        which should set the frequency to 0.5Hz. In other words, the LED should be off for one second, on for one second, and so on. It should flash 5 times in 10 seconds.

      • Charlie says:

        Yep, _delay(125000) blinks an led on 1 second & off 1 second. I’d say that it’s my scope. It was a USB project that I downloaded from the net with the software to run it…could be a bug with the software or maybe I messed up the circuit somewhere.

        I’ll still try to check it on a real scope tommorrow.

      • batchloaf says:

        Ok, that’s an interesting development! Sounds like it might be the scope in that case. The good news is that since it’s out by a factor of 2, hopefully the scope problem might be something that’s easy to correct. Anyway, thanks for the updated and please let me know what you see when you check it on another scope.

        Ted

      • Charlie says:

        OK, I just confirmed that the period is indeed 20ms, so your code for the hardware PWM is spot on. It’s my scope that seems to out by a factor of 2. Unfortunately I downloaded the hex & software, so I have no source code to work with.

        As long as it’s always a factor of 2, I can deal with it for now.

        Thanks for humoring me through all of this!

      • batchloaf says:

        Hi Charlie,

        Not at all! I’m happy to be of assistance. Also, don’t forget that there actually were problems with the original version of the code that I had in the post, so thanks again for helping to iron those out.

        I’m curious about the DIY oscilloscope you’re using by the way. Could you post a link to it please?

        Ted

      • Charlie says:

        I built this one:
        http://www.circuitvalley.com/2011/07/two-channel-pcbased-oscilloscope-usb.html

        I remember that some of the components on the PCB didn’t match with the schematic, so maybe I have some parts misplaced. At any rate, for my needs it’ll probably do for now.

      • batchloaf says:

        That’s an interesting design. Looks useful! Thanks for the link. I’ll have to keep it in mind for future reference.

        Ted

  10. Berr says:

    Hi,
    I want to create an automatic varying DC source, 90s for 0.25V and 60s for 0.8V.
    I am using pic16877a. Is it possible to use your codes under “Example 2: using the PWM module”? If so, what should be the changes? Thanks 🙂

  11. Pingback: Quora

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s