gint/include/gint/timer.h

161 lines
6.2 KiB
C
Raw Normal View History

//---
// gint:timer - Timer operation
//---
#ifndef GINT_TIMER
#define GINT_TIMER
#include <gint/defs/types.h>
#include <gint/mpu/tmu.h>
#include <gint/hardware.h>
/* Timer identifiers
Hardware timers are numbered with integers starting from 0. You can freely
access all the available timers by using their number once you have
configured them with timer_setup(). The number of timers depends on the MPU:
SH3-based: 4 timers, ids 0..3 [SH7355, SH7337]
SH4-based: 9 timers, ids 0..8 [SH7305]
You should be aware that some of these timers are used by default by gint:
core, tmu: add gint_switch(), return to menu, and improve timer code * Add the gint_switch() function which executes user-provided code from the system (CASIOWIN) context. * Added interrupt masks to the core context (should have been there long ago). * Added the gint_osmenu() function that switches out of gint to invoke GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This uses many CASIOWIN syscalls, but we don't care because gint is unloaded. Trickery is used to catch the key following the return in the add-in and/or display a new application frame before GetKeyWait() even finishes after coming back. This is only available on fx9860g for now. * Removed any public syscall definition to clear up interfaces. * Patched the DMA interruption problem in a weird way on fxcg50, a driver function will be used to do that properly eventually. * Changed the driver model to save driver contexts in preallocated spaces instead of on the stack for overall less risk. * Enabled return-to-menu with the MENU key on fx9860g in getkey(). * Changed the keyboard driver to emit releases before presses, as a return-to-menu acts as a press+release of different keys in a single driver frame, which confuses getkey(). * Fixed a really stupid bug in memcpy() that made the function really not work. Improvements in the timer driver: * Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>. * Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead. Only interrupt gate entries are left hardcoded. * Discovered that not only every write to the TCNT or TCR of an ETMU takes about 1/32k of a second (hinting at registers being powered by the same clock as the timer), but every write occuring while a previous write is pending is *lost*. This led to terrible bugs when switching ETMU contexts too fast in gint_switch(). * Removed an internal timer_address() function. * Overall simplified the handling of timers and the initialization step.
2020-05-10 14:03:41 +02:00
- Timer 0 is used by the gray engine on fx9860g.
- Timer 3/8 is used by the keyboard on SH3/SH4.
timer_setup() will fail if you try to use a timer that's already running.
Always check the return value of timer_setup()! Using a timer id that has
not been validated by timer_setup() will work, but do *something else* than
what you intended. */
/* timer_count() - tells how many timers are available on the platform */
#define timer_count() (isSH3() ? 4 : 9)
/* Clock input
Timers count down when their input clock ticks, and fire when their counter
reach 0. The choice of the input clock influences the resolution of the
timer, but if the clock is too fast, the 32-bit counter might not be able to
represent long delays.
Several input clocks are available. The peripheral clock (Po) can be divided
by 4, 16, 64 or 256; as an alternative the external clock TCLK can be used
for counting. I suspect TCLK runs at a fixed frequency of 32768 Hz, but this
has yet to be verified.
You don't really need to choose an input clock unless you are doing
something very specific. In most practical cases you can use timer_default
which is 0. See the timer_delay() function for more information. */
typedef enum
{
timer_Po_4 = 0,
timer_Po_16 = 1,
timer_Po_64 = 2,
timer_Po_256 = 3,
timer_TCLK = 5,
timer_default = timer_Po_4,
} timer_input_t;
//---
// Timer functions
//---
/* timer_setup() - set up a timer
This function configures the requested timer without starting it. On
success, it returns the first argument "timer", which is used as a timer
identifier in all other timer functions. If the requested timer is already
in use, this function fails and returns a negative number.
This function sets the timer delay, the clock source, and registers a
callback function to be called when the timer fires. An argument can be
supplied to the callback function in the form of a pointer.
When the timer fires, the callback function is called with the provided
argument pointer. The callback decides whether the timer should continue
running (by returning 0) or stop (by returning nonzero). In the latter case,
events accumulated while the callback was running are dropped.
It is sometimes difficult to choose a timer constant and a clock source
given a wished delay in seconds, especially when overclock is used. The
timer_delay() function is provided for this purpose.
@timer Requested timer id
@delay Delay between each event (the unit depends on the clock source)
@clock Clock source used by the timer for counting down
@callback Callback function (called when the timer fires)
@arg Passed as argument to the callback function */
int timer_setup(int timer, uint32_t delay, timer_input_t clock,
int (*callback)(volatile void *arg), volatile void *arg);
/* timer_delay() - compute a delay constant from a duration in seconds
This function can used as a facility to calculate the [delay] argument to
the timer_setup() function. It takes a microsecond delay as an argument and
returns the corresponding timer constant. A typical use to start a timer
with a 25 ms interval would be:
timer_setup(0, timer_delay(0, 25 * 1000), 0, callback, arg);
WARNING: Only timers 0 to 2 can count microseconds! Other timers have a
resolution of around 30 us. Counting in ms is safe for all timers, though.
For standard timers (0 to 2) it uses Po / 4 as clock input, which is very
precise and can represent up to 3 minutes' time; for extra timers (3 and
above) the clock is fixed to 32768 Hz.
@timer The timer you are planning to use
@delay_us Requested delay in microseconds */
uint32_t timer_delay(int timer, uint64_t delay_us);
/* timer_start() - start a configured timer
The specified timer will start counting down and fire callbacks at regular
intervals.
@timer Timer id, as returned by timer_setup() */
void timer_start(int timer);
/* timer_reload() - change a timer's delay constant for next interrupts
Changes the delay constant of the given timer. Nothing will happen until the
next callback; then the timer will update its delay to reflect the new
constant. The new delay can be calculated by the timer_delay() function.
@timer Timer id, as returned by timer_setup()
@delay New delay (unit depends on the clock source) */
void timer_reload(int timer, uint32_t delay);
/* timer_pause() - stop a running timer
The specified timer will be paused; its counter will not be reset. A stopped
timer can be resumed anytime by calling timer_start(). If you want to also
reset the counter, use timer_reload().
@timer Timer id, as returned by timer_setup() */
void timer_pause(int timer);
/* timer_stop() - stop and free a timer
Stops and destroys a timer, making its id free for re-use. The id must not
be used anymore until it is returned by a further call to timer_setup().
@timer Timer id, as returned by timer_setup() */
void timer_stop(int timer);
/* timer_wait() - wait for a timer to stop
Waits until the specified timer stops running. If the timer is not running,
returns immediately. The timer might not be free if it has just been paused
instead of stopped entirely. */
void timer_wait(int timer);
//---
// Predefined timer callbacks
//---
/* timer_timeout() - callback that sets a flag and halts the timer
This predefined callback may be used when a timeout is required. It sets its
argument pointer to 1 and halts the timer. The pointer must be of type
int * and you must declare the variable as volatile int. */
int timer_timeout(volatile void *arg);
#endif /* GINT_TIMER */