Again a tutorial to blink a led; but this I think is interesting from many points of view.
Whenever you look for a quick way to make a LED blinking, you fall into the classic example where it shows how to change the logic level of a port by timing the event through the delay() functions. It's ok if you want to check the operation of the micro. It is not good if you want to make the LED blinking meanwhile another program is running.
Then you go to the use of the timers that allows you to see the LED blinking even during the execution of another program (see the example LED blinking). It can be useful to make sure that the micro is working: ie debugging our program. It is however wasteful to use the timer exclusively to drive a LED (even if you have more than one timer).
In this example I want to have a timing system that allows me to synchronize the blinking of a LED and any other timing of my program. What I want to do is something similar to the millis() function in the Arduino library, which is a function that, when queried, returns the number of milliseconds that have elapsed since a fixed point.
This is the scheme of the test circuit used; I mounted 7 LEDs, plus one used as a monitor, just to highlight how easily it is possible to simultaneously and reliably timing many different functions with a single timer.
We launch MPLABX and create a new empty project, then we launch the MCC tool. It is necessary to set up:
- The source and the clock frequency.
- The pins used to drive LEDs.
- Timer0 and IRQ.
When starting MCC, the first mask that appears is the setting of the system clock:
Subsequently we set the digital outputs by clicking on the padlocks of the ports we want to use:
It is then necessary to give a useful name to the identification of the LEDs and, since the LEDs are connected so as to light up when the port goes low, select the checkbox "Start High". In the box on the top left "Project Resources" click on the item "Pin Module" and set the following mask as in the figure:
All that remains is to start the Timer0. Locate the window on the left called "Device Resources" and scroll down until you find the entry "Timer-> TMR0"; click on it twice and set the "Easy Setup" mask as shown:
We set Timer0 with a period of 1ms and we have enabled the interrupt. We press the "Generate" button and close MCC. The micro is ready to operate, we just have to tell him how to use the timer.
First we create a global variable that can contain the millisecond count. If we could have a 64 or 128-bit variable we would not have worried about what happens when the count exceeds the variable's capacity, but if we have at most a 32-bit variable we have to take into account that after 49 days the variable starts from zero. We could accept the thing (as Arduino does by warning that after "approximately" 50 days the function millis() will no longer return what it should),
or we can take note of this limitation and make sure that it does not damage the function that we will define shortly. Adopting this second mode of operation is not necessary to have a 32-bit variable, we can use a 16-bit one taking into account that it will reset every minute.
I wanted to put the variable in a special file but it is sufficient that it is global. The millisec value will be increased by one unit every time the millisecond expires, here's how:
The MCC tool has created for us all the procedures for managing the IRQ of the timer0, in particular in the file tmr0.c we find the function TMR0_DefaultInterruptHandler() which is called at the end of every millisecond. In the body of the function we write the increment of the variable defined above.
How can we use a variable that counts milliseconds and zeroes every minute? With two functions: start_msTimeout and checkTimeoutExpired; but first we need a structure that allows us to encapsulate the values of the counts of each calling function.
Let's think back to the goal we set ourselves: use a single timer to make different timings and, in our case, turn on LEDs at different frequencies. To do this we will use for all cases only two functions to which we will pass from time to time the data related to the specific LED count. This is the reason why the msecdata structure was created and for which its address is passed to the functions.
Let's see the details of the functions:
start_msTimeout is used to store the number of desired milliseconds of delay and the value of the millisec variable at the beginning of the specific count.
With the checkTimeoutExpired function we check if the set timeout has expired comparing the current value of milliseconds with the starting value, in the case we return true. It should be noted that first of all we verify that the current value of the variable millisec is lower than the starting value of the count, if this is the case the millisec variable has overflowed and has been reset; we must take this event into account by adding to the count value the milliseconds that had been counted before the overflow. This is the purpose of the gap variable.
Let's see how to use the two functions defined above.
First of all it is necessary to remember to enable the interrupts (MCC inserts them in the code but leaves them commented) then we create a msecdata type variable for each timeout we want to activate, then we pass the structure pointer created to the start_msTimeout function adding also the number of milliseconds after which the timeout expires.
Then in the main loop of the program we check with checkTimeoutExpired if the timer has expired in case we change status to the LED.
If you try the program it will be instructive to see how the timeouts work independently of each other while relying on a single timer.
As usual, the project can be downloaded from github. download project