Is this a correct way to have a 1 second delay?
Repeat loops are only really any good for "rough" timing and for short periods. There are many factors that can effect just how long your loop takes:
- Clock frequency accuracy. 4 million clock cycles is only exactly 1 second if you have precisely a 4,000,000.00000000000000000000000000 Hz clock.
- Interrupts. If an interrupt occurs during your loop the loop will take longer.
- Unaccounted for extras. If you're working in C and your delay is in a function you have to take into account such things as the function preamble / postamble, time taken for the call instruction, etc.
Basically instruction cycle / clock cycle counting is a right pain in the rectum and not something you want to do very often.
The PIC, and most other microcontrollers, have better mechanisms.
- Internal timers. These are good for short periods and are far simpler than counting cycles. Set the timer up with an interrupt, sit back, and relax. For longer periods though they do tend to involve triggering the interrupt multiple times and counting up your own variable. One common way is to have a constantly running 1ms timer that is always counting up a millisecond counter. That is then used to time your delays. Not always 100% accurate, but very simple to manage. Timers of course are still dependant on the accuracy of your clock source.
- Real-time clocks. For longer periods of a second or longer, where you don't need fine grained accuracy but do you need coarse grained accuracy (i.e., accuracy to the second, not the µS, but that accuracy stable over long periods), you can't beat a real time clock module - either internal to the chip or external in a separate chip. The lower frequency clock sources (32768Hz) used in RTC modules tends to make them more accurate (50ppm in 4MHz is 200Hz; 50ppm in 32768Hz is 1.6384Hz).
Usually delay loops are used in unsophisticated programs where a short or rough delay is required. Of course you can make them cycle-perfect by adding a few NOPs or whatever. If the micro will do nothing else other than count a clock and output a square wave, you can count exactly 500,000 cycles with a 4MHz clock (inclusive of the looping and toggling instructions), toggle the output pin and repeat, and the output will be a 1Hz square wave as accurate as the clock (give or take nanosecond jitter) and with 50% duty cycle.
If you want accurate 1-second times, you can also use a timer/counter peripheral, clocked by the system clock, which allows the micro to do something else other than counting. Many PICs also have a secondary oscillator input which can work with (say) a 32,768Hz watch crystal, allowing an inaccurate clock such as the internal RC to be used for the system clock. If the counter/timer is programmed to toggle the output pin directly, the micro can be used for other purposes than just counting, and need only return to set up the next compare value before the next comparison would come due. Again the output will be about as accurate as the input clock frequency to the counter. Some have a real time counter peripheral that does the counting for you.
If you don't care too much about a bit of jitter (but frequency can still be bang-on) you can trigger periodic interrupts and count those. For example, if you had interrupts every 2ms you could simply count (in the interrupt service routine) to 250, then toggle the output, and reset the counter. Since the ISR can take varying amounts of time on some micros to begin (depending on what it was doing when it was interrupted) and for other reasons there can be a bit of jitter in this approach (some instruction cycles).
I lied a bit when I implied that the micro could do nothing else if it was counting cycles- it's possible (but rather tedious) to craft assembler code that is isochronous - it takes exactly the same number of cycles to execute no matter which branch is taken- so that useful things can be accomplished within a delay. It's not really a nice thing to have to do.
Any of these methods may be "correct" for a given purpose.