These interfaces contain definitions related to clock and timer handling. They include interfaces to initialize and read a clock for generating regular interrupts, definitions for setting the frequency of the clock, and support for short timed delays.
HAL_CLOCK_INITIALIZE( period ) HAL_CLOCK_RESET( vector, period ) HAL_CLOCK_READ( pvalue )
These macros provide control over a clock or timer device that may be used by the kernel to provide time-out, delay and scheduling services. The clock is assumed to be implemented by some form of counter that is incremented or decremented by some external source and which raises an interrupt when it reaches a predetermined value.
HAL_CLOCK_INITIALIZE() initializes the timer
device to interrupt at the given period. The period is essentially the
value used to initialize the timer counter and must be calculated from
the timer frequency and the desired interrupt rate. The timer device
should generate an interrupt every
HAL_CLOCK_RESET() re-initializes the timer to
provoke the next interrupt. This macro is only really necessary when
the timer device needs to be reset in some way after each interrupt.
HAL_CLOCK_READ() reads the current value of the
timer counter and puts the value in the location pointed to by
pvalue. The value stored will always be the
number of timer cycles since the last interrupt, and hence ranges
between zero and the initial period value. If this is a count-down
cyclic timer, some arithmetic may be necessary to generate this value.
This macro provides a busy loop delay for the given number of
microseconds. It is intended mainly for controlling hardware that
needs short delays between operations. Code which needs longer delays,
of the order of milliseconds, should instead use higher-level
functions such as
cyg_thread_delay. The macro
implementation should be thread-safe. It can also be used in ISRs or
DSRs, although such usage is undesirable because of the impact on
interrupt and dispatch latency.
The macro should never delay for less than the specified amount of
time. It may delay for somewhat longer, although since the macro uses
a busy loop this is a waste of CPU cycles. Of course the code invoking
HAL_DELAY_US may get interrupted or timesliced,
in which case the delay may be much longer than intended. If this is
unacceptable then the calling code must take preventative action
such as disabling interrupts or locking the scheduler.
There are three main ways of implementing the macro:
a counting loop, typically written in inline assembler, using an outer loop for the microseconds and an inner loop that consumes approximately 1us. This implementation is automatically thread-safe and does not impose any dependencies on the rest of the system, for example it does not depend on the system clock having been started. However it assumes that the CPU clock speed is known at compile-time or can be easily determined at run-time.
monitor one of the hardware clocks, usually the system clock. Usually this clock ticks at a rate independent of the CPU so calibration is easier. However the implementation relies on the system clock having been started, and assumes that no other code is manipulating the clock hardware. There can also be complications when the system clock wraps around.
a combination of the previous two. The system clock is used during system initialization to determine the CPU clock speed, and the result is then used to calibrate a counting loop. This has the disadvantage of significantly increasing the system startup time, which may be unacceptable to some applications. There are also complications if the system startup code normally runs with the cache disabled because the instruction cache will greatly affect any calibration loop.
CYGNUM_HAL_RTC_NUMERATOR CYGNUM_HAL_RTC_DENOMINATOR CYGNUM_HAL_RTC_PERIOD
These macros are defined in the CDL for each platform and supply the necessary parameters to specify the frequency at which the clock interrupts. These parameters are usually found in the CDL definitions for the target platform, or in some cases the CPU variant.
CYGNUM_HAL_RTC_NUMERATOR and CYGNUM_HAL_RTC_DENOMINATOR specify the resolution of the clock interrupt. This resolution involves two separate values, the numerator and the denominator. The result of dividing the numerator by the denominator should correspond to the number of nanoseconds between clock interrupts. For example a numerator of 1000000000 and a denominator of 100 means that there are 10000000 nanoseconds (or 10 milliseconds) between clock interrupts. Expressing the resolution as a fraction minimizes clock drift even for frequencies that cannot be expressed as a simple integer. For example a frequency of 60Hz corresponds to a clock resolution of 16666666.66... nanoseconds. This can be expressed accurately as 1000000000 over 60.
CYGNUM_HAL_RTC_PERIOD specifies the exact value used to initialize the clock hardware, it is the value passed as a parameter to HAL_CLOCK_INITIALIZE() and HAL_CLOCK_RESET(). The exact meaning of the value and the range of legal values therefore depends on the target hardware, and the hardware documentation should be consulted for further details.
The default values for these macros in all HALs are calculated to give a clock interrupt frequency of 100Hz, or 10ms between interrupts. To change the clock frequency, the period needs to be changed, and the resolution needs to be adjusted accordingly. As an example consider the i386 PC target. The default values for these macros are:
CYGNUM_HAL_RTC_NUMERATOR 1000000000 CYGNUM_HAL_RTC_DENOMINATOR 100 CYGNUM_HAL_RTC_PERIOD 11932
To change to, say, a 200Hz clock the period needs to be halved to 5966, and to compensate the denominator needs to be doubled to 200. To change to a 1KHz interrupt rate change the period to 1193 and the denominator to 1000.
Some HALs make this process a little easier by deriving the period arithmetically from the denominator. This calculation may also involve the CPU clock frequency and possibly other factors. For example in the ARM AT91 variant HAL the period is defined by the following expression:
((CYGNUM_HAL_ARM_AT91_CLOCK_SPEED/32) / CYGNUM_HAL_RTC_DENOMINATOR)
In this case it is not necessary to change the period at all, just change the denominator to select the desired clock frequency. However, note that for certain choices of frequency, rounding errors in this calculation may result in a small clock drift over time. This is usually negligible, but if perfect accuracy is required, it may be necessary to adjust the frequency or period by hand.