mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 04:23:36 +01:00
Redesigned timer module: got a possibly infinite amount of virtual timers.
This commit is contained in:
parent
7ab6170ca3
commit
31e2b453dd
35 changed files with 950 additions and 454 deletions
6
Makefile
6
Makefile
|
@ -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
4
TODO
|
@ -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
16
configure
vendored
|
@ -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]}"
|
||||
}
|
||||
|
||||
|
|
|
@ -521,7 +521,6 @@ int main(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void crash(void)
|
||||
{
|
||||
__asm__(
|
||||
|
@ -530,4 +529,3 @@ void crash(void)
|
|||
"trapa #1 "
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
12
include/internals/modules.h
Normal file
12
include/internals/modules.h
Normal 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
|
|
@ -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.
|
||||
|
|
|
@ -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
48
include/modules/macros.h
Normal 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
64
include/modules/timer.h
Normal 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
|
|
@ -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
|
||||
|
|
217
include/timer.h
217
include/timer.h
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++;
|
||||
|
||||
|
|
|
@ -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
49
src/core/modules.c
Normal 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();
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
112
src/timer/common_api.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
63
src/timer/hardware_timers.c
Normal file
63
src/timer/hardware_timers.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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--;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
223
src/timer/virtual_timers.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue