- Published: Tuesday, 16 November 2010
- Written by Graham Mitchell
- Hits: 11148
The flashing LED - an iconic starting point for many embedded hobbyists and professionals alike. There are several reasons that contribute to the popularity of the humble flashing LED program, simplicity is likely the greatest influence followed closely by its reliability as a test program.
I should clear up something now rather than later: pre-made delay libraries or other "simpler" means for creating delays could have been employed in this project. Timer1 has been used as I wanted to get familiar with one of the other onboard peripherals at the same time. The code is still very easy to follow, so don't be mislead by the perceived difficulty (if there is any?)
The following video demonstrates the completed project. The code is explained further on in the article.
Correction - I say "once every second" though the program is designed to toggle every 500mS.
You too can replicate this project by either building your own circuit or using the Explorer 16 Development board by Microchip, with the PIC24FJ128GA010 installed. Note - If you're unfamiliar with these devices, perhaps the following article would be worth a read: "Handy Resources for PIC24 + C30". And there's always the Digital DIY forum for guidance as well.
This article was not intended to teach someone C from the ground up - if you're not familiar with C then have a read of this article. The program is quite simple - toggle each nibble of PORTA. The result is a pattern of four alternating LEDs as shown in the above video..
Device + Config
C30 has no idea what device your going to use unless you declare it. The same goes for the device configuration settings - both are listed below:
The pre-compile definitions allow me to assign numbers and equations that will be calculated at compile. From there, the results can be accessed in code with minimal waste of precious CPU cycles. These definitions assist with calculating the correct settings for the Timer1 module which is used later for delays.
Now if the desired delay period, oscillator speed or Timer1 prescale changes, the end user only needs to update the above definitions. Everything else is calculated on compile.
Another interesting point here is C30 will treat pre-compile definitions as the widest object type found in its definition. Consider DELAY_CYCLES. If 1000.0 were changed to 1000 then DELAY_CYCLES would be treated as a long int. Encoding 1000 as 1000.0 ensures that C30 will treat DELAY_CYCLES as an object of type float (as the widest object type is now a float type).
Why go to all the hassle? If the widest object was of type long int (FOSC = 32000000 in the above program) then all decimal places would be truncated during the math operations, resulting in some rather large errors!
Using floats is incredibly inefficient and anyone who is familiar with the architecture of a small microcontroller would be familiar with the resources involved. I prefer not to work with them, if possible. With that said, the float is converted to a long type object with the following statement:
Here's a couple of activities for anyone that is overly interested with any of this:
- Why is 2097 the maximum DELAY_MILLI_SEC can be set too?
- What happens if DELAY_MILLI_SEC is set beyond 2097?
- Change the declaration for delay_cycles to 'float' and review the difference in resources.
Now that all of the hardware has been defined, and I've got some handy pre-compile definitions to use - its time to begin the main program. Before the main program loop can commence, all of the software initialisation must take place..
The above code snippet configures the direction of PORTA pins, and sets the outputs with a default state. Following that, Timer1 is configured.
The Main Loop
The above code is easy to follow. PORTA is inverted, and TMR1 increments until the desired number of cycles have elapsed. The result is as shown in the above video - an alternating LED pattern.