Redesigned timer module: got a possibly infinite amount of virtual timers.

This commit is contained in:
lephe 2017-03-01 11:07:28 +01:00
parent 7ab6170ca3
commit 31e2b453dd
35 changed files with 950 additions and 454 deletions

View file

@ -99,7 +99,7 @@ obj-std = $(foreach mod,$(modules-libc),$(mod-$(mod)-obj)) $(obj-std-spec)
obj-lib = $(foreach mod,$(modules-gint),$(mod-$(mod)-obj)) $(obj-lib-spec)
# Dependencies.
hdr-dep = $(wildcard include/*.h include/internals/*.h)
hdr-dep = $(wildcard include/*.h include/*/*.h)
@ -249,9 +249,9 @@ ifdef config_ext
endif
@ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n'
install-demo:
install-demo: all
p7 send -f $(target-g1a)
install-debug:
install-debug: all
p7 send -f $(target-dbg)
.PHONY: all-lib all help

4
TODO
View file

@ -12,7 +12,8 @@ Simple improvements:
- string: Use cmp/str to implement memchr() (assembler examples)
- string: Do some tests for memcmp()
- core: Register more interrupts (and understand their parameters)
- rtc: Take care of carry when reading time
- project: Clean headers that have some internal definitions
- project: Check size of all library structures
Larger improvements:
- errno: Introduce errno and use it more or less everywhere
- bopti: Monochrome bitmaps blending modes
@ -24,6 +25,7 @@ Larger improvements:
- usb: Implement a driver
- esper: Cleaner playback, synthetizing
- clock: Handle overclocking (relaunch clocks when overclocking)
- project: Unify this hellish mess of register access!
Things to investigate:
- Packed bit fields alignment

16
configure vendored
View file

@ -13,7 +13,7 @@ conf[GINT_EXTENDED_LIBC]=
# Size limits
conf[ATEXIT_MAX]=16
conf[RTC_CB_ARRAY_SIZE]=5
conf[TIMER_SLOTS]=16
conf[EVENTS_QUEUE_SIZE]=64
# Output files
@ -50,8 +50,8 @@ Options that affect the behavior of the library:
Options that customize size limits:
$Cr--atexit-max$C0=$Cy<integer>$Cg [default:$Cp 16$Cg]$C0
Number of exit handlers that can be registered by atexit().
$Cr--rtc-callbacks$C0=$Cy<integer>$Cg [default:$Cp 5$Cg]$C0
Number of RTC callbacks that can be registered.
$Cr--timer-slots$C0=$Cy<integer>$Cg [default:$Cp 16$Cg]$C0
Number of virtual timers that may be registered at the same time.
$Cr--events-queue-size$C0=$Cy<integer>$Cg [default:$Cp 64$Cg]$C0
Number of events simultaneously stored in the event queue.
EOF
@ -76,11 +76,11 @@ for arg; do case "$arg" in
conf[ATEXIT_MAX]=$size
else echo -e "$error --atexit-max expects an integer value"
fail=true; fi;;
--rtc-callbacks=*)
--timer-slots=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[RTC_CB_ARRAY_SIZE]=$size
else echo -e "$error --rtc-callbacks expects an integer value"
conf[TIMER_SLOTS]=$size
else echo -e "$error --timer-slots expects an integer value"
fail=true; fi;;
--events-queue-size=*)
size=${arg#*=}
@ -90,7 +90,7 @@ for arg; do case "$arg" in
"value"
fail=true; fi;;
--atexit-max | --rtc-callbacks | --events-queue-size)
--atexit-max | --timer-slots | --events-queue-size)
echo -e "$error syntax for $arg is $arg=<integer-value>";;
*)
@ -108,7 +108,7 @@ output_config_gcc()
[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "-D GINT_EXTENDED_LIBC"
echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}"
echo "-D RTC_CB_ARRAY_SIZE=${conf[RTC_CB_ARRAY_SIZE]}"
echo "-D TIMER_SLOTS=${conf[TIMER_SLOTS]}"
echo "-D EVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}"
}

View file

@ -521,7 +521,6 @@ int main(void)
return 0;
}
/*
void crash(void)
{
__asm__(
@ -530,4 +529,3 @@ void crash(void)
"trapa #1 "
);
}
*/

View file

@ -102,7 +102,7 @@ static void draw_events(enhanced_event_t *history, int size)
"0", ".", "\x08", "(-)", "EXE", NULL
};
const char *event_names[] = {
"None ", "User ", "Press", "Rept.", "Rel. "
"None ", "User ", "Press", "Rept.", "Rel. ", "Timer"
};
for(int i = 0; i < size && history[i].type != ET_None; i++)

View file

@ -16,7 +16,7 @@
#include <internals/rtc.h>
#include <mpu.h>
static void draw(struct RTCTime time)
static void draw(rtc_time_t time)
{
extern Image res_rtc_segments;
@ -53,8 +53,7 @@ static void draw(struct RTCTime time)
static void callback(void)
{
extern Image res_opt_rtc;
struct RTCTime time = rtc_getTime();
time.year += 1900;
rtc_time_t time = rtc_getTime();
dclear();
draw(time);
@ -62,7 +61,7 @@ static void callback(void)
dupdate();
}
static void set_region(struct RTCTime *time, int region, int value)
static void set_region(rtc_time_t *time, int region, int value)
{
switch(region)
{
@ -129,12 +128,10 @@ static void set(void)
{ 72, 39, 7, 9 }, { 84, 39, 7, 9 }, { 90, 39, 7, 9 },
{ 96, 39, 7, 9 }, { 102, 39, 7, 9 },
};
struct RTCTime time = rtc_getTime();
rtc_time_t time = rtc_getTime();
int region_count = 14;
int n = 0, slide = 0, key, leave;
time.year += 1900;
while(1)
{
dclear();
@ -157,11 +154,9 @@ static void set(void)
else if(key == KEY_F1 || key == KEY_EXE)
{
n++;
slide = 0;
if(n == region_count)
if(++n == region_count)
{
time.year -= 1900;
rtc_setTime(time);
return;
}
@ -187,7 +182,7 @@ static void set(void)
else if(n == 6 && (slide != 1 || k != 5))
{
int day = k + 4 * slide - 1;
int day = (k + 4 * slide - 1) % 7;
set_region(&time, n, day);
n++;
slide = 0;
@ -221,7 +216,6 @@ static void set(void)
n++;
if(n == region_count)
{
time.year -= 1900;
rtc_setTime(time);
return;
}

View file

@ -22,6 +22,7 @@ static clock_config_t conf;
static volatile int elapsed_timer = -1;
static volatile int elapsed_rtc = -1;
static int cb_id = -1;
static timer_t *htimer = NULL;
static void timing_rtc(void)
{
@ -35,7 +36,11 @@ static void timing_timer(void)
static void timing_start(void)
{
timer_start(TIMER_USER, 64, Clock_Hz, timing_timer, NULL, 0);
uint32_t delay = clock_setting(64, Clock_Hz);
htimer = htimer_setup(timer_user, delay, timer_Po_4, 0);
timer_attach(htimer, timing_timer, NULL);
timer_start(htimer);
rtc_cb_edit(cb_id, RTCFreq_64Hz, timing_rtc);
elapsed_timer = 0;
@ -253,8 +258,8 @@ void test_timer(void)
switch(getkey_opt(Getkey_NoOption, 1))
{
case KEY_EXIT:
timer_stop(TIMER_USER);
rtc_cb_end(cb_id);
timer_stop(htimer);
return;
case KEY_F1:

View file

@ -10,6 +10,8 @@
#ifndef _CLOCK_H
#define _CLOCK_H
#include <stdint.h>
//---
// Sleep functions.
//---
@ -21,12 +23,16 @@
void sleep(void);
/*
sleep_ms(), sleep_us()
Sleeps for the given number of milliseconds / microseconds using the
TIMER_USER timer. The actual sleep time will always be slightly less
than requested.
sleep_ms()
Sleeps for the given number of milliseconds using a virtual timer.
*/
void sleep_ms(int ms_delay);
/*
sleep_us()
Sleeps for the given number of microseconds using the hardware timer
timer_user.
*/
void sleep_us(int us_delay);
@ -83,7 +89,7 @@ typedef struct
Normally you need not use this function when setting up timers because
timer_start() handles this conversion for you.
*/
int clock_setting(int duration, enum ClockUnit unit);
uint32_t clock_setting(int duration, enum ClockUnit unit);
/*
clock_config()

View file

@ -9,26 +9,31 @@
#ifndef _EVENTS_H
#define _EVENTS_H
#include <timer.h>
/*
event_type_t
Something user programs will surely use most often.
*/
typedef enum
{
EventType_None = 0,
ET_None = EventType_None,
EventType_None = 0,
ET_None = EventType_None,
EventType_User = 1,
ET_User = EventType_User,
EventType_User = 1,
ET_User = EventType_User,
EventType_KeyPressed = 2,
ET_KeyPress = EventType_KeyPressed,
EventType_KeyPressed = 2,
ET_KeyPress = EventType_KeyPressed,
EventType_KeyRepeated = 3,
ET_KeyRepeat = EventType_KeyRepeated,
EventType_KeyRepeated = 3,
ET_KeyRepeat = EventType_KeyRepeated,
EventType_KeyReleased = 4,
ET_KeyRel = EventType_KeyReleased,
EventType_KeyReleased = 4,
ET_KeyRel = EventType_KeyReleased,
EventType_TimerUnderflow = 5,
ET_Timer = EventType_TimerUnderflow,
} event_type_t;
@ -47,6 +52,8 @@ typedef struct
void *data;
// For ET_KeyPress, ET_KeyRepeat and ET_KeyRel.
int key;
// For ET_Timer.
timer_t *timer;
};
} event_t;

View file

@ -0,0 +1,12 @@
#ifndef _INTERNALS_MODULES_H
#define _INTERNALS_MODULES_H
/*
mod_init()
Initializes the module data to make register access cross-platform. The
MPU needs to have been detected or this function will yield wrong
results.
*/
void mod_init(void);
#endif // _INTERNALS_MODULES_H

View file

@ -14,7 +14,7 @@
*/
struct rtc_cb
{
enum RTCFrequency freq;
rtc_frequency_t freq;
int id;
void (*callback)(void);
@ -24,6 +24,19 @@ struct rtc_cb
// The callback array.
struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE];
/*
rtc_perodic_interrupt()
Handles periodic interrupts and calls the callbacks.
*/
void rtc_periodic_interrupt(void);
/*
rtc_cb_interrupt()
Calls the RTC callbacks if necessary, and updates the repeat counts.
Should only be called when RTC periodic interrupts occur.
*/
void rtc_cb_interrupt(void);
/*
struct mod_rtc
This structure describes the arrangement of RTC register in the memory.

View file

@ -1,49 +1,76 @@
#ifndef _INTERNALS_TIMER_H
#define _INTERNALS_TIMER_H 1
#define _INTERNALS_TIMER_H
#include <timer.h>
#include <stdint.h>
/*
struct Timer
This structure holds information for a running timer.
timer_t
A virtual or hardware timer. We need to declare the struct timer_t name
so that we can forward-reference it.
*/
struct Timer
typedef struct timer_t
{
// Current delay, how much time elapsed since last interrupt occurred,
// and how many repeats are left.
int ms_delay;
int ms_elapsed;
int repeats_left;
// Is the virtual slot free? Is the virtual timer active?
uint8_t used :1;
uint8_t active :1;
// Is this a virtual timer? Is this the virtual timer support?
uint8_t virtual :1;
uint8_t vsupport :1;
// How many events do I have received but not executed?
uint8_t events :4;
// Callback function (NULL for event-firing timers) and its argument.
void *callback;
void *data;
int repeats;
};
void *argument;
extern struct Timer timers[3];
} timer_t;
// Hardware timers.
extern timer_t htimers[3];
// Virtual timers.
extern timer_t vtimers[TIMER_SLOTS];
/*
struct mod_tmu
This structure holds information about the timer unit (peripheral
module) registers.
timer_interrupt()
Handles the interrupt for the given timer channel.
*/
struct mod_tmu
{
unsigned int TCOR; // Timer constant register.
unsigned int TCNT; // Timer counter.
union
{
unsigned short WORD;
struct
{
unsigned :7;
unsigned UNF :1; // Underflow flag.
unsigned :2;
unsigned UNIE :1; // Underflow interrupt enable.
unsigned CKEG :2; // Clock edge (SH7705 only).
unsigned TPSC :3; // Timer prescaler.
};
} TCR; // Timer control register.
};
void timer_interrupt(int channel);
/*
timer_get()
Returns the timer and TSTR register addresses.
timer_callback_event()
Executes the callback of a timer, or pushes a new timer event depending
on the timer configuration. Also reduces the amount of repeats left and
clears the active flag (or stops the hardware timer) if this number
falls from one to zero.
*/
void timer_get(int timer, volatile struct mod_tmu **tmu,
volatile unsigned char **tstr);
void timer_callback_event(timer_t *timer);
/*
vtimer_interrupt()
Interrupt handling subsystem for the virtual timers.
*/
void vtimer_interrupt(void);
/*
vtimer_updateOne()
Update the virtual timer hardware support timer, knowing that a virtual
timer with the given delay has been started.
*/
void vtimer_updateOne(int additional_delay_ms);
/*
vtimer_updateAll()
Updates the virtual timer hardware support after computing the GCD of
all virtual timers delays. This is rather long so avoid calling this
when possible.
*/
void vtimer_updateAll(void);
#endif // _INTERNALS_TIMER_H

48
include/modules/macros.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef _MODULES_MACROS_H
#define _MODULES_MACROS_H
#include <stdint.h>
// Fixed-width types for bit field are totally meaningless.
typedef unsigned uint;
/*
byte_union()
Union between an uint8_t 'byte' attribute and a provided bit-field
structure that describe the contents of the byte.
*/
#define byte_union(name, fields) \
union \
{ \
uint8_t byte; \
struct { fields } \
__attribute__((packed)); \
} __attribute__((packed)) name
/*
word_union()
Union between an uint16_t 'word' attribute and a provided bit-field
structure that describe the contents of the word.
*/
#define word_union(name, fields) \
union \
{ \
uint16_t word; \
struct { fields } \
__attribute__((packed)); \
} __attribute__((packed)) name
/*
lword_union()
Union between an uint32_t 'lword' attribute and a provided bit-field
structure that describe the contents of the longword.
*/
#define lword_union(name, fields) \
union \
{ \
uint32_t lword; \
struct { fields } \
__attribute__((packed)); \
} __attribute__((packed)) name
#endif // _MODULES_MACROS_H

64
include/modules/timer.h Normal file
View file

@ -0,0 +1,64 @@
#ifndef _MODULES_TIMER_H
#define _MODULES_TIMER_H
#include <modules/macros.h>
#include <stdint.h>
/*
mod_tmu_timer_t
Registers of a single timer.
*/
typedef struct
{
uint32_t TCOR; /* Timer constant register */
uint32_t TCNT; /* Timer counter */
word_union(TCR,
uint :7;
uint UNF :1; /* Underflow flag */
uint :2;
uint UNIE :1; /* Underflow interrupt enable */
uint CKEG :2; /* Clock edge (SH7705) */
uint TPSC :3; /* Timer prescaler */
);
uint16_t :16;
} __attribute__((packed, aligned(4))) mod_tmu_timer_t;
/*
mod_tmu_tstr_t
The timer start register.
*/
typedef struct
{
byte_union(,
uint :5;
uint STR2 :1; /* Counter start 2 */
uint STR1 :1; /* Counter start 1 */
uint STR0 :1; /* Counter start 0 */
);
} __attribute__((packed)) mod_tmu_tstr_t;
/*
mod_tmu_t (resides into gint memory)
Basically three timer units and an additional register to control who's
running.
*/
typedef struct
{
/* Timer configurations */
volatile mod_tmu_timer_t *timers[3];
/* Timer start register */
volatile mod_tmu_tstr_t *TSTR;
/* Timer input capture 2 (SH7705) */
volatile const uint32_t *TCPR2;
} mod_tmu_t;
// The actual thing is there. It's initialized by gint at startup and contains
// addresses and information suitable for the current platform.
extern volatile mod_tmu_t TMU;
#endif // _MODULES_TIMER

View file

@ -9,38 +9,41 @@
#ifndef _RTC_H
#define _RTC_H 1
#include <stdint.h>
//---
// Time access.
//---
/*
struct RTCTime
rtc_time_t
Defines a point in time.
*/
struct RTCTime
typedef struct
{
int seconds; // Seconds in range 0-59
int minutes; // Minutes in range 0-59
int hours; // Hours in range 0-23
int month_day; // Day of month in range 1-31
int month; // Month in range 0-11
int year; // Years (full value)
int week_day; // Day of week in range 0(Sunday)-6(Saturday).
};
uint8_t seconds; // Seconds in range 0-59
uint8_t minutes; // Minutes in range 0-59
uint8_t hours; // Hours in range 0-23
uint8_t month_day; // Day of month in range 1-31
uint8_t month; // Month in range 0-11
uint8_t week_day; // Day of week in range 0(Sunday)-6(Saturday).
uint16_t year; // Years (around 2000)
} rtc_time_t;
/*
rtc_getTime()
Reads the current time from the RTC. There is no guarantee that the
week day is correct (use the time API for that).
*/
struct RTCTime rtc_getTime(void);
rtc_time_t rtc_getTime(void);
/*
rtc_setTime()
Sets the time in the RTC registers. The week day is set to 0 if greater
than 6. Other fields are not checked.
*/
void rtc_setTime(struct RTCTime time);
void rtc_setTime(rtc_time_t time);
@ -49,11 +52,11 @@ void rtc_setTime(struct RTCTime time);
//---
/*
enum RTCFrequency
rtc_frequency_t
Describes the possible frequencies available for the real-time clock
interrupt.
*/
enum RTCFrequency
typedef enum
{
RTCFreq_500mHz = 7,
RTCFreq_1Hz = 6,
@ -63,7 +66,8 @@ enum RTCFrequency
RTCFreq_64Hz = 2,
RTCFreq_256Hz = 1,
RTCFreq_None = 0,
};
} rtc_frequency_t;
/*
rtc_cb_add()
@ -74,7 +78,7 @@ enum RTCFrequency
The number of repeats may be set to 0, in which case the callback is
called indefinitely unless the user calls rtc_cb_end().
*/
int rtc_cb_add(enum RTCFrequency freq, void (*function)(void), int repeats);
int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats);
/*
rtc_cb_end()
@ -93,29 +97,6 @@ void rtc_cb_end(int id);
can set the function to NULL or the frequency to RTCFreq_None to
temporarily disable the callback.
*/
int rtc_cb_edit(int id, enum RTCFrequency new_freq,
void (*new_function)(void));
//---
// Internal API.
// Referenced here for documentation purposes only. Do not call.
//---
/*
rtc_interrupt()
Handles an RTC interrupt by calling the callback.
*/
void rtc_interrupt(void);
void rtc_interrupt_7705(void);
void rtc_interrupt_7305(void);
/*
rtc_cb_interrupt()
Handles an RTC interrupt. Calls the RTC callbacks if necessary, and
updates the repeat counts.
*/
void rtc_cb_interrupt(void);
int rtc_cb_edit(int id, rtc_frequency_t new_freq, void (*new_function)(void));
#endif // _RTC_H

View file

@ -11,106 +11,157 @@
#define _TIMER_H 1
#include <clock.h>
#include <stdint.h>
#include <stddef.h>
// The timer object is manipulated by the module; the user needs not access it
// directly. Its contents are defined in <internals/timer.h>.
struct timer_t;
typedef struct timer_t timer_t;
//---
// Constants.
// Virtual timer API
// Gint allows users to create virtual timers with 1-ms precision to
// handle an important amount of timed events. The amount of virtual
// timers available is gapped by the TIMER_SLOTS parameter, but this
// parameter can be customized when building the library; thus, the
// amount of usable virtual timers is not limited.
//---
// Timer identifiers.
#define TIMER_0 0
#define TIMER_TMU0 TIMER_0
#define TIMER_1 1
#define TIMER_TMU1 TIMER_1
#define TIMER_2 2
#define TIMER_TMU2 TIMER_2
// Timer function identifiers.
#define TIMER_KEYBOARD TIMER_TMU0
#define TIMER_GRAY TIMER_TMU1
#define TIMER_USER TIMER_TMU2
#ifndef TIMER_SLOTS
#define TIMER_SLOTS 16
#endif
// Timer prescalers.
#define TIMER_Po_4 0
#define TIMER_Po_16 1
#define TIMER_Po_64 2
#define TIMER_Po_256 3
#define TIMER_TCLK 5
/*
timer_create()
Basic timer configuration. This function creates a virtual timer and
configures its delay and repetition count. It returns a virtual timer
object that will be used by other functions. At most TIMER_SLOTS
virtual timers can be used at the same time: this function returns NULL
if there is no new slot available.
By default a virtual timer configured by timer_configure() will fire
ET_Timer events, which the user will need to catch. The user should
then execute some action.
There is another option: the user may call timer_attach() to attach a
callback to a virtual timer. A virtual timer which has a callback
attached will not fire any ET_Timer event and will call the callback
automatically instead.
*/
timer_t *timer_create(int delay_ms, int repeats);
/*
timer_destroy()
Destroys a virtual timer. This virtual timer pointer becomes invalid
and should not be used anymore.
*/
void timer_destroy(timer_t *virtual_timer);
//---
// Public API.
// Hardware timer API
// Gint provides access to hardware timer. These timer offer more or less
// microsecond-level control. However, the user should ensure, when using
// hardware timers, that they are not overriding the configuration of
// timers that are already running -- gint won't check.
//---
/*
timer_hard_t
Available hardware timers. The user can use timer_user freely, but
timer_gray and timer_virtual should not be used as long as the gray
engine or virtual timers are running (respectively).
*/
typedef enum
{
timer_tmu0 = 0,
timer_virtual = timer_tmu0,
timer_tmu1 = 1,
timer_gray = timer_tmu1,
timer_tmu2 = 2,
timer_user = timer_tmu2,
} timer_hard_t;
/*
timer_input_t
Available input clocks for the hardware timer:
- Po/4 Peripheral clock (frequency divided by 4)
- Po/16 Peripheral clock (frequency divided by 16)
- Po/64 Peripheral clock (frequency divided by 64)
- Po/256 Peripheral clock (frequency divided by 256)
- TCLK External clock
I'm not totally sure there is any signal on the external clock, so
don't use it unless you know what you are doing.
*/
typedef enum
{
timer_Po_4 = 0,
timer_Po_16 = 1,
timer_Po_64 = 2,
timer_Po_256 = 3,
timer_tclk = 5,
} timer_input_t;
/*
htimer_setup()
Configures a hardware timer. By default hardware timers generates
ET_Timer events but catching them may take some time, especially if
there are other events waiting in the queue. For increased timing, and
for fast timers, the user should consider using callbacks instead.
Returns a hardware timer object.
Returns the correct timer structure if the requested timer is free and
NULL otherwise.
*/
timer_t *htimer_setup(timer_hard_t id, uint32_t constant, timer_input_t input,
int repeats);
/*
htimer_reload()
Reloads a hardware timer without starting or stopping it.
*/
void htimer_reload(timer_hard_t id, uint32_t new_constant);
//---
// Common API
// The following functions work with both virtual and hardware timers.
//---
/*
timer_attach()
This function attaches a callback to a virtual or hardware timer. A
timer with a callback attached will stop firing ET_Timer events and
will call the callback function directly instead.
The type signature of the function should be as follows:
- void callback(void) if argument == NULL
- void callback(void *argument) if argument is non-NULL
In the latter case, the argument pointer will be passed to the callback
function.
*/
void timer_attach(timer_t *timer, void *callback, void *argument);
/*
timer_start()
Configures and starts a timer. The timer argument expects a timer name.
You can use TIMER_USER anytime. You may also use TIMER_GRAY if you're
not running the gray engine.
Unit names are defined in the clock.h header and must be one of the
following:
- Clock_us (microseconds)
- Clock_ms (milliseconds)
- Clock_s (seconds)
- Clock_Hz (hertz)
- Clock_kHz (kilohertz)
- Clock_MHz (megahertz)
The number of repeats may to set to 0. In this case, the timer will not
stop until timer_stop() is explicitly called.
The callback is expected to be a function of the following type:
- void callback(void) if data == NULL
- void callback(void *data) if data is non-NULL
In the latter case, the data pointer will be passed as argument to the
callback function.
Starts a virtual or hardware timer. If the timer has a callback
attached, then the callback function will start being called regularly;
otherwise, the timer will start pushing ET_Timer events to the event
queue. It is advised, not to change a timer's configuration while it's
running.
*/
void timer_start(int timer, int delay_or_frequency, enum ClockUnit unit,
void *callback, void *data, int repeats);
/*
timer_start2()
Basically the same as timer_start(), but uses a clock-count delay and a
prescaler. The possible values for the prescaler are dividers of the
peripheral clock frequency Po:
- TIMER_Po_4
- TIMER_Po_16
- TIMER_Po_64
- TIMER_Po_256
- TIMER_TCLK
*/
void timer_start2(int timer, int delay, int prescaler, void *callback,
void *data, int repeats);
void timer_start(timer_t *timer);
/*
timer_stop()
Stops the given timer. This function may be called even if the timer is
not running.
Stops a timer. If the argument is virtual timer, it is paused and can
be started again later.
If it's a hardware timer, it is freed and made accessible to other
uses. It should be set up again before being started.
*/
void timer_stop(int timer);
/*
timer_reload()
Reloads the given timer with the supplied constant. Starts the timer if
it was stopped.
*/
void timer_reload(int timer, int new_delay_or_frequency, enum ClockUnit unit);
/*
timer_reload2()
Same as timer_reload(), but again uses the native clock count. The
prescaler may not be changed.
*/
void timer_reload2(int timer, int new_delay);
//---
// Internal API.
// Referenced for documentation purposes only. Do not call.
//---
/*
timer_interrupt()
Handles the interrupt for the given timer.
*/
void timer_interrupt(int timer);
void timer_stop(timer_t *timer);
#endif // _TIMER_H

View file

@ -1,6 +1,6 @@
#include <clock.h>
#include <timer.h>
#include <internals/timer.h>
#include <modules/timer.h>
#include <rtc.h>
#include <stddef.h>
#include <mpu.h>
@ -19,9 +19,9 @@ static clock_config_t conf = {
Several units can be used. Be aware that the result is approximate, and
very high frequencies or very short delays will yield important errors.
*/
int clock_setting(int duration, enum ClockUnit unit)
uint32_t clock_setting(int duration, enum ClockUnit unit)
{
if(conf.Pphi_f <= 0) return -1;
if(conf.Pphi_f <= 0) return 0xffffffff;
uint64_t f = conf.Pphi_f >> 2;
uint64_t result;
@ -74,30 +74,42 @@ void sleep(void)
);
}
/*
sleep_ms(), sleep_us()
Sleeps for the given number of milliseconds / microseconds using the
TIMER_USER timer. The actual sleep time will always be slightly less
than requested.
*/
static void sleep_callback(void *flag)
static void sleep_callback(void *arg)
{
*((int *)flag) = 1;
int *flag = arg;
*flag = 1;
}
/*
sleep_ms()
Sleeps for the given number of milliseconds using a virtual timer.
*/
void sleep_ms(int ms_delay)
{
sleep_us(1000 * ms_delay);
volatile int sleep_done = 0;
timer_t *timer = timer_create(ms_delay, 1);
timer_attach(timer, sleep_callback, (void *)&sleep_done);
timer_start(timer);
while(!sleep_done) sleep();
}
/*
sleep_us()
Sleeps for the given number of microseconds using the hardware timer
timer_user.
*/
void sleep_us(int us_delay)
{
volatile int sleep_done = 0;
timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback,
(void *)&sleep_done, 1);
const uint32_t constant = clock_setting(us_delay, Clock_us);
do sleep();
while(!sleep_done);
timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1);
timer_attach(timer, sleep_callback, (void *)&sleep_done);
timer_start(timer);
while(!sleep_done) sleep();
}
@ -109,6 +121,7 @@ void sleep_us(int us_delay)
// Indicates whether the measurements are finished.
static volatile int clock_measure_done = 0;
// Once again SH7705 and SH7305 need different methods...
static timer_t *htimer_7705 = NULL;
static int cb_id_7705 = -1;
static void clock_measure_7705(void);
static void clock_compute_7305(void);
@ -128,25 +141,8 @@ void clock_measure(void)
// P_phi using a timer/RTC combination, and we deduce CKIO.
if(isSH3())
{
// We prepare the timer manually, without starting it, so that
// we only have to push the running bit to start it when the
// measurements begin. This might look of little effect but it
// makes the precision jump from ~97% to more than 99%.
volatile struct mod_tmu *tmu;
timer_get(TIMER_USER, &tmu, NULL);
tmu->TCOR = 0xffffffff;
tmu->TCNT = tmu->TCOR;
tmu->TCR.TPSC = TIMER_Po_4;
tmu->TCR.UNF = 0;
tmu->TCR.UNIE = 1;
tmu->TCR.CKEG = 0;
timers[TIMER_USER].callback = NULL;
timers[TIMER_USER].data = NULL;
timers[TIMER_USER].repeats = 0;
htimer_7705 = htimer_setup(timer_user, 0xffffffff, timer_Po_4,
1);
cb_id_7705 = rtc_cb_add(RTCFreq_256Hz, clock_measure_7705, 0);
}
@ -226,7 +222,7 @@ static void clock_compute_7305(void)
Given the number of P_phi / 4 timer ticks elapsed between two RTC
256 Hz interrupts, determines the clock configuration.
*/
static void clock_measure_7705_finalize(int elapsed)
static void clock_measure_7705_finalize(uint32_t elapsed)
{
volatile unsigned int *FRQCR = (void *)0xffffff80;
@ -254,13 +250,10 @@ static void clock_measure_7705_finalize(int elapsed)
*/
static void clock_measure_7705_callback(void)
{
timer_stop(TIMER_USER);
timer_stop(htimer_7705);
rtc_cb_end(cb_id_7705);
volatile struct mod_tmu *tmu;
timer_get(TIMER_USER, &tmu, NULL);
int elapsed = 0xffffffff - tmu->TCNT;
uint32_t elapsed = 0xffffffff - TMU.timers[timer_user]->TCNT;
clock_measure_7705_finalize(elapsed);
clock_measure_done = 1;
}
@ -274,9 +267,6 @@ static void clock_measure_7705_callback(void)
*/
static void clock_measure_7705(void)
{
volatile unsigned char *tstr;
timer_get(TIMER_USER, NULL, &tstr);
*tstr |= (1 << TIMER_USER);
timer_start(htimer_7705);
rtc_cb_edit(cb_id_7705, RTCFreq_256Hz, clock_measure_7705_callback);
}

View file

@ -8,6 +8,7 @@
#include <internals/clock.h>
#include <clock.h>
#include <gint.h>
#include <internals/modules.h>
int main(void);
@ -58,7 +59,7 @@ __attribute__((section(".pretext.entry"))) int start(void)
dg->stage = stage_startup;
// Exception records: what kind of exceptions / TLB faults / interrupts
// occured last to get some trace in case of crash.
// occurred last to get some trace in case of crash.
dg->excepts = 0;
for(size_t i = 0; i < sizeof dg->except_vect; i++)
dg->except_vect[i] = 0;
@ -67,7 +68,7 @@ __attribute__((section(".pretext.entry"))) int start(void)
dg->expevt = 0x00000000;
dg->tea = 0x00000000;
// Memory map: provides information about alignement and the relative
// Memory map: provides information about alignment and the relative
// size and position of each section, as well as read-only / read-write
// types of addresses.
dg->section_text.address = (uint32_t)&btext;
@ -107,7 +108,10 @@ __attribute__((section(".pretext.entry"))) int start(void)
dg->stage = stage_mmu;
#endif
// Initializing gint.
// Detecting the MPU type, initializing the register module addresses,
// and initializing gint.
mpu_init();
mod_init();
gint_init();
#ifdef GINT_DIAGNOSTICS

View file

@ -29,11 +29,6 @@ void gint_init(void)
uint32_t *ptr = &bgint;
uint32_t *src = &gint_data;
// This initialization routine is usually called before any
// constructor. We want to ensure that the MPU type is detected, but
// mpu_init() hasn't been called yet.
mpu_init();
// Loading the interrupt handler into the memory.
while(ptr < &egint) *ptr++ = *src++;

View file

@ -1,6 +1,6 @@
#include <internals/interrupts.h>
#include <timer.h>
#include <rtc.h>
#include <internals/timer.h>
#include <internals/rtc.h>
/*
inth_timer_underflow()
@ -17,5 +17,5 @@ void inth_timer_underflow(uint32_t channel)
*/
void inth_rtc_periodic(void)
{
rtc_interrupt();
rtc_periodic_interrupt();
}

49
src/core/modules.c Normal file
View file

@ -0,0 +1,49 @@
#include <modules/timer.h>
#include <stddef.h>
#include <mpu.h>
//---
// Structure information
// Here resides most of the platform-dependent register configuration.
// Module structures are arranged to mask as much as possible hardware
// differences to the user. When it becomes impossible to do so at
// compile-time, gint provides functions to ensure that the user does not
// confront to the hardware directly.
//---
volatile mod_tmu_t TMU;
//---
// Initializer
//---
static void mod_init_7705(void)
{
TMU.timers[0] = (void *)0xfffffe94;
TMU.timers[1] = (void *)0xfffffea0;
TMU.timers[2] = (void *)0xfffffeac;
TMU.TSTR = (void *)0xfffffe92;
TMU.TCPR2 = (void *)0xfffffeb8;
}
static void mod_init_7305(void)
{
TMU.timers[0] = (void *)0xa4490008;
TMU.timers[1] = (void *)0xa4490014;
TMU.timers[2] = (void *)0xa4490020;
TMU.TSTR = (void *)0xa4490004;
TMU.TCPR2 = NULL;
}
/*
mod_init()
Initializes the module data to make register access cross-platform. The
MPU needs to have been detected or this function will yield wrong
results.
*/
void mod_init(void)
{
isSH3() ? mod_init_7705() : mod_init_7305();
}

View file

@ -19,7 +19,7 @@ static int delays[2];
static int runs = 0;
#define GRAY_PRESCALER TIMER_Po_64
static timer_t *gray_timer = NULL;
@ -33,7 +33,8 @@ static int runs = 0;
*/
void gray_interrupt(void)
{
timer_reload2(TIMER_GRAY, delays[(~current) & 1]);
htimer_reload(timer_gray, delays[(~current) & 1]);
screen_display(vrams[current]);
current ^= 1;
}
@ -68,8 +69,10 @@ void gray_start(void)
{
if(runs) return;
timer_start2(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt,
NULL, 0);
gray_timer = htimer_setup(timer_gray, delays[0], timer_Po_64, 0);
timer_attach(gray_timer, gray_interrupt, NULL);
timer_start(gray_timer);
current &= 1;
runs = 1;
}
@ -81,9 +84,7 @@ void gray_start(void)
*/
void gray_stop(void)
{
if(!runs) return;
timer_stop(TIMER_GRAY);
timer_stop(gray_timer);
runs = 0;
display_useVRAM(display_getLocalVRAM());

View file

@ -7,7 +7,7 @@ struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE] = { 0 };
// Callback identifier (unique).
static int unique_id = 1;
// Current RTC interrupt frequency.
static enum RTCFrequency rtc_freq = RTCFreq_None;
static rtc_frequency_t rtc_freq = RTCFreq_None;
// 256-Hz tick count. This counter is stopped when no callback is registered.
static unsigned elapsed256 = 0;
@ -21,7 +21,7 @@ static unsigned elapsed256 = 0;
*/
static void rtc_cb_update(void)
{
enum RTCFrequency max = RTCFreq_None;
rtc_frequency_t max = RTCFreq_None;
int n;
for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) if(cb_array[n].id)
@ -46,7 +46,7 @@ static void rtc_cb_update(void)
The number of repeats may be set to 0, in which case the callback is
called indefinitely unless the user calls rtc_cb_end().
*/
int rtc_cb_add(enum RTCFrequency freq, void (*function)(void), int repeats)
int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats)
{
int n = 0;
if(freq == RTCFreq_None || !function || repeats < 0) return -2;
@ -92,7 +92,7 @@ void rtc_cb_end(int id)
-2 Invalid parameters
This function never removes a callback. Call rtc_cb_end() for this.
*/
int rtc_cb_edit(int id, enum RTCFrequency new_freq,
int rtc_cb_edit(int id, rtc_frequency_t new_freq,
void (*new_function)(void))
{
if(new_freq < 0 || new_freq > 7) return -2;

View file

@ -21,18 +21,24 @@ static int integer16(int bcd)
Reads the current time from the RTC. There is no guarantee that the
week day is correct (use the time API for that).
*/
struct RTCTime rtc_getTime(void)
rtc_time_t rtc_getTime(void)
{
volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305;
struct RTCTime time;
rtc_time_t time;
time.seconds = integer8(rtc->RSECCNT.BYTE);
time.minutes = integer8(rtc->RMINCNT.BYTE);
time.hours = integer8(rtc->RHRCNT.BYTE);
time.month_day = integer8(rtc->RDAYCNT.BYTE);
time.month = integer8(rtc->RMONCNT.BYTE);
time.year = integer16(rtc->RYRCNT.WORD);
time.week_day = rtc->RWKCNT;
do
{
rtc->RCR1.CF = 0;
time.seconds = integer8(rtc->RSECCNT.BYTE);
time.minutes = integer8(rtc->RMINCNT.BYTE);
time.hours = integer8(rtc->RHRCNT.BYTE);
time.month_day = integer8(rtc->RDAYCNT.BYTE);
time.month = integer8(rtc->RMONCNT.BYTE);
time.year = integer16(rtc->RYRCNT.WORD);
time.week_day = rtc->RWKCNT;
}
while(rtc->RCR1.CF != 0);
return time;
}

View file

@ -2,11 +2,13 @@
#include <rtc.h>
#include <mpu.h>
int rtc_carry_flag = 0;
/*
rtc_interrupt()
rtc_periodic_interrupt()
Handles an RTC interrupt by calling the callback.
*/
void rtc_interrupt(void)
void rtc_periodic_interrupt(void)
{
rtc_cb_interrupt();

View file

@ -22,15 +22,22 @@ static int bcd16(int integer)
Sets the time in the RTC registers. The week day is set to 0 if greater
than 6. Other fields are not checked.
*/
void rtc_setTime(struct RTCTime time)
void rtc_setTime(rtc_time_t time)
{
volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305;
int wday = (time.week_day < 7) ? (time.week_day) : (0);
rtc->RSECCNT.BYTE = bcd8(time.seconds);
rtc->RMINCNT.BYTE = bcd8(time.minutes);
rtc->RHRCNT.BYTE = bcd8(time.hours);
rtc->RDAYCNT.BYTE = bcd8(time.month_day);
rtc->RMONCNT.BYTE = bcd8(time.month);
rtc->RYRCNT.WORD = bcd16(time.year);
rtc->RWKCNT = time.week_day < 7 ? time.week_day : 0;
do
{
rtc->RCR1.CF = 0;
rtc->RSECCNT.BYTE = bcd8(time.seconds);
rtc->RMINCNT.BYTE = bcd8(time.minutes);
rtc->RHRCNT.BYTE = bcd8(time.hours);
rtc->RDAYCNT.BYTE = bcd8(time.month_day);
rtc->RMONCNT.BYTE = bcd8(time.month);
rtc->RYRCNT.WORD = bcd16(time.year);
rtc->RWKCNT = wday;
}
while(rtc->RCR1.CF != 0);
}

View file

@ -12,7 +12,7 @@
*/
time_t time(time_t *timeptr)
{
struct RTCTime rtc = rtc_getTime();
rtc_time_t rtc = rtc_getTime();
struct tm tm;
time_t calendar;

112
src/timer/common_api.c Normal file
View file

@ -0,0 +1,112 @@
#include <timer.h>
#include <internals/timer.h>
#include <modules/timer.h>
#include <events.h>
/*
timer_attach()
Attaches a callback to a virtual or hardware timer.
*/
void timer_attach(timer_t *timer, void *callback, void *argument)
{
if(!timer) return;
timer->callback = callback;
timer->argument = argument;
}
/*
timer_start()
Starts a virtual or hardware timer. If the timer has a callback
attached, then the callback function will start being called regularly;
otherwise, the timer will start pushing ET_Timer events to the event
queue. It is advised, not to change a timer's configuration while it's
running.
*/
void timer_start(timer_t *timer)
{
if(!timer) return;
timer->active = 1;
if(timer->virtual) vtimer_updateOne(timer->ms_delay);
else TMU.TSTR->byte |= (1 << (timer - htimers));
}
/*
timer_stop()
Pauses a virtual or hardware timer. The timer stops counting and can be
started again later.
*/
void timer_stop(timer_t *timer)
{
if(!timer) return;
timer->active = 0;
if(timer->virtual) vtimer_updateAll();
else
{
TMU.TSTR->byte &= ~(1 << (timer - htimers));
timer->used = 0;
}
}
/*
timer_interrupt()
Handles the interrupt for the given timer channel.
*/
void timer_interrupt(int channel)
{
// Is this the virtual timer support?
if(htimers[channel].vsupport) vtimer_interrupt();
else timer_callback_event(&htimers[channel]);
// Clearing the interrupt flag.
TMU.timers[channel]->TCR.UNF = 0;
}
/*
timer_callback_event()
Executes the callback of a timer, or pushes a new timer event depending
on the timer configuration. Also reduces the amount of repeats left and
clears the active flag (or stops the hardware timer) if this number
falls from one to zero.
*/
void timer_callback_event(timer_t *timer)
{
if(!timer) return;
// Callback-type timers.
if(timer->callback)
{
if(!timer->argument)
{
void (*fun)(void) = timer->callback;
fun();
}
else
{
void (*fun)(void *arg) = timer->callback;
fun(timer->argument);
}
}
// Event-type timers.
else
{
event_t event = {
.type = ET_Timer,
.timer = timer
};
event_push(event);
}
// Reducing the number of repeats and stopping the timer if required.
if(timer->repeats_left > 0)
{
if(!--timer->repeats_left)
{
if(timer->virtual) timer->active = 0;
else timer_stop(timer);
}
}
}

View file

@ -0,0 +1,63 @@
#include <timer.h>
#include <internals/timer.h>
#include <modules/timer.h>
//---
// Public API
//---
timer_t htimers[3] = { 0 };
/*
htimer_setup()
Configures a hardware timer.
*/
timer_t *htimer_setup(timer_hard_t id, uint32_t constant, timer_input_t input,
int repeats)
{
if(id < 0 || id >= 3 || htimers[id].used) return NULL;
timer_t *timer = &htimers[id];
// We don't care about this.
timer->ms_delay = 0;
timer->ms_elapsed = 0;
timer->repeats_left = repeats;
timer->used = 1;
timer->active = 0;
timer->virtual = 0;
timer->vsupport = 0;
timer->events = 0;
timer->callback = NULL;
timer->argument = NULL;
// Time to set up the real thing.
volatile mod_tmu_timer_t *tmu = TMU.timers[id];
tmu->TCOR = constant;
tmu->TCNT = constant;
tmu->TCR.TPSC = input;
tmu->TCR.UNF = 0; // Clear the interrupt flag.
tmu->TCR.UNIE = 1; // Enable underflow interrupt.
tmu->TCR.CKEG = 0; // Count on rising edge (SH7705).
return timer;
}
/*
htimer_reload()
Reloads a hardware timer without starting or stopping it.
*/
void htimer_reload(timer_hard_t id, uint32_t new_constant)
{
volatile mod_tmu_timer_t *tmu = TMU.timers[id];
// This is not much but we want to make the transition as swift as
// possible and I can prove that at least one cycle will be lost in
// processor operation. Thus...
tmu->TCNT = new_constant - 1;
tmu->TCOR = new_constant;
}

View file

@ -1,25 +0,0 @@
#include <internals/timer.h>
#include <mpu.h>
/*
timer_get()
Returns the timer and TSTR register addresses.
*/
void timer_get(int timer, volatile struct mod_tmu **tmu,
volatile unsigned char **tstr)
{
// Using SH7705 information for SH-3-based MPUs.
if(isSH3())
{
if(tstr) *tstr = (volatile unsigned char *)0xfffffe92;
if(tmu) *tmu = (volatile struct mod_tmu *)
(0xfffffe94 + 12 * timer);
}
// Assuming SH7305 by default.
else
{
if(tstr) *tstr = (volatile unsigned char *)0xa4490004;
if(tmu) *tmu = (volatile struct mod_tmu *)
(0xa4490008 + 12 * timer);
}
}

View file

@ -1,41 +0,0 @@
#include <internals/timer.h>
#include <timer.h>
#include <stddef.h>
struct Timer timers[3] = {
{ .callback = NULL, .data = NULL, .repeats = 0 },
{ .callback = NULL, .data = NULL, .repeats = 0 },
{ .callback = NULL, .data = NULL, .repeats = 0 },
};
/*
timer_interrupt()
Handles the interrupt for the given timer.
*/
void timer_interrupt(int timer)
{
volatile struct mod_tmu *tmu;
timer_get(timer, &tmu, NULL);
tmu->TCR.UNF = 0;
if(timers[timer].callback)
{
if(timers[timer].data)
{
void (*fun)(void *data) = timers[timer].callback;
fun(timers[timer].data);
}
else
{
void (*fun)(void) = timers[timer].callback;
fun();
}
}
// Reducing the number of repetitions left, if not infinite.
if(!timers[timer].repeats) return;
if(timers[timer].repeats == 1) timer_stop(timer);
else timers[timer].repeats--;
}

View file

@ -1,32 +0,0 @@
#include <internals/timer.h>
#include <timer.h>
/*
timer_reload()
Reloads the given timer with the supplied constant. Starts the timer if
it was stopped.
*/
void timer_reload(int timer, int new_delay_or_frequency, enum ClockUnit unit)
{
timer_reload2(timer, clock_setting(new_delay_or_frequency, unit));
}
/*
timer_reload2()
Same as timer_reload(), but again uses the native clock count. The
prescaler may not be changed.
*/
void timer_reload2(int timer, int new_delay)
{
volatile struct mod_tmu *tmu;
volatile unsigned char *tstr;
int byte = (1 << timer);
timer_get(timer, &tmu, &tstr);
// Setting the constant and the delay.
tmu->TCOR = new_delay;
tmu->TCNT = new_delay;
// Starting the timer.
*tstr |= byte;
}

View file

@ -1,48 +0,0 @@
#include <internals/timer.h>
#include <timer.h>
/*
timer_start2()
Configures and starts a time using a clock count and a prescaler.
*/
void timer_start2(int timer, int delay, int prescaler, void *callback,
void *data, int repeats)
{
volatile struct mod_tmu *tmu;
volatile unsigned char *tstr;
int byte = (1 << timer);
timer_get(timer, &tmu, &tstr);
// Loading the counter, the constant and the prescaler/
tmu->TCOR = delay;
tmu->TCNT = delay;
tmu->TCR.TPSC = prescaler;
// Resetting underflow flag and enabling interruptions.
tmu->TCR.UNF = 0;
tmu->TCR.UNIE = 1;
// Counting on rising edge (ignored on SH7305).
tmu->TCR.CKEG = 0;
// Loading the structure information.
timers[timer].callback = callback;
timers[timer].data = data;
timers[timer].repeats = repeats;
// Starting the timer.
*tstr |= byte;
}
/*
timer_start()
Configures and starts a timer using a delay, or a frequency, and the
associated unit.
*/
void timer_start(int timer, int delay, enum ClockUnit unit, void *callback,
void *data, int repeats)
{
timer_start2(timer, clock_setting(delay, unit), TIMER_Po_4, callback,
data, repeats);
}

View file

@ -1,18 +0,0 @@
#include <internals/timer.h>
#include <timer.h>
#include <stddef.h>
/*
timer_stop()
Stops the given timer. This function may be called even if the timer is
not running.
*/
void timer_stop(int timer)
{
volatile unsigned char *tstr;
int byte = (1 << timer);
timer_get(timer, NULL, &tstr);
*tstr &= ~byte;
}

223
src/timer/virtual_timers.c Normal file
View file

@ -0,0 +1,223 @@
#include <timer.h>
#include <clock.h>
#include <internals/timer.h>
#include <modules/timer.h>
//---
// Public API
//---
timer_t vtimers[TIMER_SLOTS] = { 0 };
/*
timer_create()
Creates a virtual timer and configures its delay and repetition count.
*/
timer_t *timer_create(int ms_delay, int repeats)
{
if(ms_delay <= 0) return NULL;
timer_t *timer = vtimers;
// Finding an available virtual slot.
while(timer < vtimers + TIMER_SLOTS && timer->used) timer++;
if(timer >= vtimers + TIMER_SLOTS) return NULL;
timer->ms_delay = ms_delay;
timer->ms_elapsed = 0;
timer->repeats_left = repeats;
timer->used = 1;
timer->active = 0;
timer->virtual = 1;
timer->vsupport = 0;
timer->events = 0;
timer->callback = NULL;
timer->argument = NULL;
return timer;
}
/*
timer_destroy()
Destroys a virtual timer. This virtual timer pointer becomes invalid
and should not be used anymore.
*/
void timer_destroy(timer_t *timer)
{
timer->events = 0;
timer->active = 0;
timer->used = 0;
vtimer_updateAll();
}
//---
// Virtual timers management
//---
static int current_delay = 0;
static uint32_t current_constant = 0;
/*
vtimer_interrupt()
Interrupt handling subsystem for the virtual timers.
*/
void vtimer_interrupt(void)
{
// Do we need to recompute the hardware support frequency?
int recalc = 0;
// No timer is running.
if(!current_delay) return;
// Update them all and call the required callbacks. Stop the ones that
// have been running for long enough.
for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++)
if(timer->used)
{
timer->ms_elapsed += current_delay;
// This should happen only once but in case there is a problem,
// this loop will ensure that at least the correct amount of
// callbacks is executed.
// We could divide but it would be slower.
while(timer->ms_elapsed >= timer->ms_delay)
{
// We don't call the callbacks now because we may need
// to update the timer frequency later and we want to
// get there asap.
timer->events++;
timer->ms_elapsed -= timer->ms_delay;
}
// We would need to stop one virtual timer.
if(timer->repeats_left > 0 && timer->repeats_left <=
timer->events) recalc = 1;
}
if(recalc) vtimer_updateAll();
for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++)
if(timer->used)
{
while(timer->active && timer->events)
{
timer_callback_event(timer);
timer->events--;
}
timer->used = timer->active;
}
}
/*
vtimer_update()
Swiftly updates the hardware support counter and constant to keep up
with new kinds of timers being added while disturbing the counting flow
as little as possible.
*/
static void vtimer_update(int new_delay)
{
volatile mod_tmu_timer_t *tmu = TMU.timers[timer_virtual];
timer_t *timer = &htimers[timer_virtual];
if(new_delay == current_delay) return;
if(!new_delay)
{
current_delay = 0;
current_constant = 0;
// At this point the virtual support timer was running and has
// been stopped, thus the hardware timer structure is filled
// properly so we can use this function.
timer_stop(timer);
return;
}
uint32_t new_constant = clock_setting(new_delay, Clock_ms);
// The transition needs to be as smooth as possible. We have probably
// spent a lot of time calculating this new GCD delay so we want to
// take into consideration the current value of the timer counter.
// The -1 is here to take into account the time between reading and
// writing the new constant (~10 I_phi cycles thus usually 5 P_phi
// cycles; given this timer runs on P_phi / 4 this makes 1 unit).
// Here we suppose that the new TCNT is positive, i.e that the update
// happened in less than 1 ms. This is reasonable as long as this
// happens *before* calling callbacks.
tmu->TCNT = new_constant - (current_constant - tmu->TCNT - 1);
tmu->TCOR = new_constant;
if(!current_delay)
{
// Ups, we've been using a random counter; the timer's not
// running.
tmu->TCNT = tmu->TCOR;
// Set it up then.
tmu->TCR.TPSC = timer_Po_4;
tmu->TCR.UNF = 0;
tmu->TCR.UNIE = 1;
tmu->TCR.CKEG = 0;
// Tell them that the virtual timer support is running there.
timer->used = 1;
timer->active = 0;
timer->virtual = 0;
timer->vsupport = 1;
timer->events = 0;
// And let's roll!
timer_start(timer);
}
current_delay = new_delay;
current_constant = new_constant;
}
/*
gcd()
Well, the Euclidean algorithm. That's a O(ln(a)) & O(ln(b)) FWIW.
*/
static int gcd(int a, int b)
{
while(b)
{
int r = a % b;
a = b;
b = r;
}
return a;
}
/*
vtimer_updateOne()
Update the virtual timer hardware support timer, knowing that a virtual
timer with the given delay has been started.
*/
void vtimer_updateOne(int additional_delay)
{
vtimer_update(gcd(current_delay, additional_delay));
}
/*
vtimer_updateAll()
Updates the virtual timer hardware support after computing the GCD of
all virtual timers delays. This computation is rather long (especially
this modulo can afaik only be achieved using division on SuperH and
it's a 70-cycle operation) so it should be avoided when possible.
*/
void vtimer_updateAll(void)
{
int gcd_delay = 0;
for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++)
if(timer->used && timer->active && (timer->repeats_left <= 0 ||
timer->repeats_left > timer->events))
{
gcd_delay = gcd(gcd_delay, timer->ms_delay);
}
vtimer_update(gcd_delay);
}