Timer driver/utility for all platforms

Post your example source code and application notes to share with others
User avatar
lgitlitz
Posts: 328
Joined: Wed Apr 23, 2008 11:43 am
Location: San Diego, CA
Contact:

Timer driver/utility for all platforms

Post by lgitlitz » Wed May 20, 2009 5:04 pm

I created a driver that makes using hardware timers pretty trivial to use for all the current NetBurner products. This driver will use the DMA timer peripheral for all platforms except the MCF5272 which uses the timer peripheral. There are 3 main types of functionality available in this utility. To use this in a application just include StopWatch.cpp and StopWatch.h in your application. I also included a main.cpp as an example to demonstrate the various functionality described below.

The first is a simple stopwatch for basic timing. I use this when I capture code execution time when benchmarking performance. An initialize function for the timer needs to be called once:
void StopWatchInit( int Timer = DEFAULT_TIMER, double InterruptTime = 0, WORD Prescaler = 0x0 );
For the highest precision on a single timer no parameters are needed, just call StopWatchInit();
To start the timer:
void StopWatchStart( int Timer = DEFAULT_TIMER );
To read the elapsed time in seconds since start call:
double StopWatchReadTime( int Timer = DEFAULT_TIMER );
To stop and clear the timer:
void StopWatchStopClear( int Timer = DEFAULT_TIMER );
If you wish to keep all the timing results as non-floating points use the following functions.
To read the actual count register from the timer:
DWORD StopWatchReadLow( int Timer = DEFAULT_TIMER );
To read the software overflow register, incremented in interrupt every time the hardware timer overflows:
DWORD StopWatchReadHigh( int Timer = DEFAULT_TIMER );
Once the timer is running it is interrupt driven with an extra 32-bit overflow register. This means that the timer running at the highest precision (75MHz) should run for 7800 years before overflowing on the 150MHz platforms. The MCF5272 timer will overflow after about 50 days since it only has a 16-bit timer, the prescaler parameter can be used to decrease the precision and overflow/interrupt frequency.

The second functionality provided by this driver is called StopWatchDelay. This will function very similarly to an OSTimeDly call but will provide much higher precision. The only function required for this is:
void StopWatchDelay( double DelayTime, int Timer = DEFAULT_TIMER );
The DelayTime parameter is the amount of time to delay in seconds. So say you want to do a 5ms delay and let other tasks run like an OSTimeDly, just call StopWatchDelay( 0.005 );. The standard OSTimeDly must be called in increments of 50ms and has an accuracy precision of 50ms, StopWatchDelay should have an accuracy of a few uS depending on the task switch time performance. The timer parameter is like the other stopwatch function, it tells which timer to use. If you are using a timer for the StopWatch functions and also want to use delays, you will need to use two separate timers. Also if you are using delays in more then one task you will also need to use a different timer for each task. Calling a delay in a task on the same timer that another task is using can cause a deadlock.

void StopWatchPollingDelay( double DelayTime, int Timer = DEFAULT_TIMER );
This delay function is a new addition to the driver. It will do a polling delay instead of the interrupt based delay. Since there is no task or interrupt switching, this will be a much more accurate timer. The downside compared to the normal StopWatchDelay function is that this delay will not allow lower priority tasks to run during the delay. I suggest using this version of the timer delay if you want sub uS accuracy or are calling delays that are less then 2 or 3 ms.

The third functionality is a clock generator for the timer output pin of a timer. This one is pretty simple, just call:
void StopWatchClockGenerator( double Frequency, int Timer = DEFAULT_TIMER );
The Frequency parameter is in Hz. You must also make sure to configure the pin to be multiplexed to the timer peripheral for this to work.

There is also one extra callback function that will work with both the StopWatch and Delay functions. This is a callback function linked to the interrupt routine that captures the overflow of the hardware timer:
extern void ( *StopWatch_Interrupt_Function )( int Timer );
This will allows a simple way to have high precision interrupt timing events. For example, say you want to increment LEDs with high precision timing.
First you would make a function to do this with the format of the callback:
void IncrementLEDs( int Timer )
{
// The Timer parameter is not used here since we do not need to determine which timer interrupted
LEDValue++;
putleds( LEDValue );
}
Then point the callback to your function:
StopWatch_Interrupt_Function = IncrementLEDs; // Point timer interrupt call-back to LED function
Then start the timer with your precision...
To have the timer interrupt every 10ms, this will also call the IncrementLEDs function every ms:
StopWatchInit( 1, 0.01 );
StopWatchStart();
If you just want to have the delay occur once and want to task to block, use the delay call instead:
StopWatchDelay( 0.01 );

All comments are welcome, good or bad.

-Larry
Attachments
StopWatch.zip
Hardware timer driver and example for all platforms
(5.05 KiB) Downloaded 1226 times
Last edited by lgitlitz on Fri Feb 11, 2011 11:34 am, edited 3 times in total.
Reason: Updated files to include StopWatchStop function and fixes for 72 processors

greengene
Posts: 164
Joined: Wed May 14, 2008 11:20 am
Location: Lakeside, CA

Re: Timer driver/utility for all platforms

Post by greengene » Fri Dec 04, 2009 8:49 am

this package is working very nicely for me, thanks.
just one nit - in the demo driver, main.cpp, a break at the end of the case '3' block
should be added.

swmoore
Posts: 5
Joined: Wed Nov 04, 2009 11:18 am

Re: Timer driver/utility for all platforms

Post by swmoore » Wed Dec 16, 2009 12:32 pm

StopWatch looks excellent. One of the biggest failings of the Netburner is the lack of timebase. The hardware timers are usable, but StopWatch looks great!

I am using the MOD5213. I dropped the StopWatch.h into the \nburn\include_nn\ directory and added the line:

Code: Select all

#include "StopWatch.h"
and further down (in UserMain):

Code: Select all

StopWatchInit();
However, the NBEclipse gives the following error:
c:\nburn\NBEclipse\workspace\TestProgram\Release/...\main.cpp:116: undefined reference to 'StopWatchInit(int, double, unsigned short)'
Obviously, the compiler is finding the StopWatch.h include, because it found the (int, double, unsigned short) fields.

Any suggestions?

greengene
Posts: 164
Joined: Wed May 14, 2008 11:20 am
Location: Lakeside, CA

Re: Timer driver/utility for all platforms

Post by greengene » Thu Dec 17, 2009 1:51 pm

did you add stopwatch.cpp to your project?

caburfoot
Posts: 2
Joined: Sun May 23, 2010 8:09 pm

Re: Timer driver/utility for all platforms

Post by caburfoot » Sun May 23, 2010 8:17 pm

I'm trying to learn more about timer interrupts. I downloaded and ran this example. However, from two different TTY utilities, I was unable to 'enter' the value to allow gets(buffer) to return for the program to continue.

Any ideas?

(Used MTTY from netburn and TeraTerm).

Crystiano
Posts: 1
Joined: Thu Nov 11, 2010 11:07 am

Re: Timer driver/utility for all platforms

Post by Crystiano » Thu Nov 11, 2010 11:19 am

Hello,

I have some problems with my MOD5282. I must control QADC with e.g 0.1 miliseconds and I want to involve interruptions to my device which will be "sampling" QADC. My idea is to call function "StopWatchDelay" from this topic. Unfortunately my device goes to infinite loop, when I call this function, so this utility isn't working properly. I attach screen from MTTY. Have you any ideas how to correct code?
Attachments
1.JPG
First and fourth options works properly as you can see on the screen.
1.JPG (87.52 KiB) Viewed 18563 times

Ridgeglider
Posts: 513
Joined: Sat Apr 26, 2008 7:14 am

Re: Timer driver/utility for all platforms

Post by Ridgeglider » Thu Nov 11, 2010 11:46 am

I'd recomend using a PIT interrupt and calling the ADC sample routine directly from within the PIT ISR. Note that this probably means setting up the AD to do one-time samples on the channel(s) of interest rather than using the NB default AD code which just sets the AD into continuously sampling all 8 channels. One of the issues will be where to store the AD results for each channel's conversion. Obviously, there will be a finite limit to the duration you'll be able to sample at the 0.1 msec rate. For the PIT IRQ code, see http://www.netburner.com/downloads/mod5 ... ppNote.pdf The AD can generate its own IRQ upon completion of the conversion.

I've done what you are doing on the 5213 and it is pretty straightforward. The most complicated part is determining exactly how to set up the 5282 AD. See the Freescale manual for that. The 5213 has lots of interesting features. One allows simultaneous conversion on two channels. Another option (if I remember correctly) is that you can set the AD up to convert 1 channel sequentially 8 times per trigger. These 8 values can then be averaged to filter a noisy input. Obviously this might not be useful if you're concerned with an exact sampling interval occuring at a very deterministic rate. Another option is the ability to generate IRQs on results over or under set limits, or upon completion of the conversion set. I think most of these options are also avalable on the 5282.

Ridgeglider
Posts: 513
Joined: Sat Apr 26, 2008 7:14 am

Re: Timer driver/utility for all platforms

Post by Ridgeglider » Thu Nov 11, 2010 12:04 pm

Slight correction to the previous post: Each time the PIT runs, it increments a volatile sample counter variable and compares it to the desired number of samples. If add'l samples are needed, call the StartAD_Conversion function. This function should just set a bit in one of the AD registers to begin conversion so it will be fast and allow the PIT_ISR to return quickly. Configure the AD to generate an IRQ upon sample (or sample set) completion. The AD_ISR increments a static volatile variable used to index the storage locations. When this index variable exceeds the desired number of samples, have the AD_ISR issue a semaphore or OS_FLAG indicating that the desired set of samples are complete.

oleg_osov
Posts: 20
Joined: Tue Jan 04, 2011 1:58 pm
Location: Montreal, Canada
Contact:

Re: Timer driver/utility for all platforms

Post by oleg_osov » Mon Feb 28, 2011 9:39 am

Hello!

lgitlitz, thank you a lot! This MUST be in examples which is shipped with NetBurner. I have to implement timers, similar to unix timers (one-shot timers and periodic timers) and this program gave me a clue how to do it. Thank you very much, again! A great work, the program works seamlessly!
Best regards,
Oleg Osovitskiy

oleg_osov
Posts: 20
Joined: Tue Jan 04, 2011 1:58 pm
Location: Montreal, Canada
Contact:

Re: Timer driver/utility for all platforms

Post by oleg_osov » Tue Mar 01, 2011 3:28 am

Is it possible to use POSIX timers on NetBurner? I use Mod5234, NBEclipse 2.4 RC2 + gcc 4.2.1. I found a file

\nburn\gcc-m68k\m68k-elf\include\time.h

There are declaration of such usefull functions which I use on a normal UNIX platforms like clock_gettime(), timer_create(), timer_gettime() and so on. So, my question - is it possible to use this functions? When I add define _POSIX_TIMERS to my project I get unresolved references while linking, if I remove this define I get undeclared function clock_gettime() (for example).

What have I do?

May be there is a way to get get a current time with a ms/us precision?
Best regards,
Oleg Osovitskiy

Post Reply