mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03: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)
|
obj-lib = $(foreach mod,$(modules-gint),$(mod-$(mod)-obj)) $(obj-lib-spec)
|
||||||
|
|
||||||
# Dependencies.
|
# Dependencies.
|
||||||
hdr-dep = $(wildcard include/*.h include/internals/*.h)
|
hdr-dep = $(wildcard include/*.h include/*/*.h)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -249,9 +249,9 @@ ifdef config_ext
|
||||||
endif
|
endif
|
||||||
@ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n'
|
@ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n'
|
||||||
|
|
||||||
install-demo:
|
install-demo: all
|
||||||
p7 send -f $(target-g1a)
|
p7 send -f $(target-g1a)
|
||||||
install-debug:
|
install-debug: all
|
||||||
p7 send -f $(target-dbg)
|
p7 send -f $(target-dbg)
|
||||||
|
|
||||||
.PHONY: all-lib all help
|
.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: Use cmp/str to implement memchr() (assembler examples)
|
||||||
- string: Do some tests for memcmp()
|
- string: Do some tests for memcmp()
|
||||||
- core: Register more interrupts (and understand their parameters)
|
- 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:
|
Larger improvements:
|
||||||
- errno: Introduce errno and use it more or less everywhere
|
- errno: Introduce errno and use it more or less everywhere
|
||||||
- bopti: Monochrome bitmaps blending modes
|
- bopti: Monochrome bitmaps blending modes
|
||||||
|
@ -24,6 +25,7 @@ Larger improvements:
|
||||||
- usb: Implement a driver
|
- usb: Implement a driver
|
||||||
- esper: Cleaner playback, synthetizing
|
- esper: Cleaner playback, synthetizing
|
||||||
- clock: Handle overclocking (relaunch clocks when overclocking)
|
- clock: Handle overclocking (relaunch clocks when overclocking)
|
||||||
|
- project: Unify this hellish mess of register access!
|
||||||
|
|
||||||
Things to investigate:
|
Things to investigate:
|
||||||
- Packed bit fields alignment
|
- Packed bit fields alignment
|
||||||
|
|
16
configure
vendored
16
configure
vendored
|
@ -13,7 +13,7 @@ conf[GINT_EXTENDED_LIBC]=
|
||||||
|
|
||||||
# Size limits
|
# Size limits
|
||||||
conf[ATEXIT_MAX]=16
|
conf[ATEXIT_MAX]=16
|
||||||
conf[RTC_CB_ARRAY_SIZE]=5
|
conf[TIMER_SLOTS]=16
|
||||||
conf[EVENTS_QUEUE_SIZE]=64
|
conf[EVENTS_QUEUE_SIZE]=64
|
||||||
|
|
||||||
# Output files
|
# Output files
|
||||||
|
@ -50,8 +50,8 @@ Options that affect the behavior of the library:
|
||||||
Options that customize size limits:
|
Options that customize size limits:
|
||||||
$Cr--atexit-max$C0=$Cy<integer>$Cg [default:$Cp 16$Cg]$C0
|
$Cr--atexit-max$C0=$Cy<integer>$Cg [default:$Cp 16$Cg]$C0
|
||||||
Number of exit handlers that can be registered by atexit().
|
Number of exit handlers that can be registered by atexit().
|
||||||
$Cr--rtc-callbacks$C0=$Cy<integer>$Cg [default:$Cp 5$Cg]$C0
|
$Cr--timer-slots$C0=$Cy<integer>$Cg [default:$Cp 16$Cg]$C0
|
||||||
Number of RTC callbacks that can be registered.
|
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
|
$Cr--events-queue-size$C0=$Cy<integer>$Cg [default:$Cp 64$Cg]$C0
|
||||||
Number of events simultaneously stored in the event queue.
|
Number of events simultaneously stored in the event queue.
|
||||||
EOF
|
EOF
|
||||||
|
@ -76,11 +76,11 @@ for arg; do case "$arg" in
|
||||||
conf[ATEXIT_MAX]=$size
|
conf[ATEXIT_MAX]=$size
|
||||||
else echo -e "$error --atexit-max expects an integer value"
|
else echo -e "$error --atexit-max expects an integer value"
|
||||||
fail=true; fi;;
|
fail=true; fi;;
|
||||||
--rtc-callbacks=*)
|
--timer-slots=*)
|
||||||
size=${arg#*=}
|
size=${arg#*=}
|
||||||
if [[ $size == +([0-9]) ]]; then
|
if [[ $size == +([0-9]) ]]; then
|
||||||
conf[RTC_CB_ARRAY_SIZE]=$size
|
conf[TIMER_SLOTS]=$size
|
||||||
else echo -e "$error --rtc-callbacks expects an integer value"
|
else echo -e "$error --timer-slots expects an integer value"
|
||||||
fail=true; fi;;
|
fail=true; fi;;
|
||||||
--events-queue-size=*)
|
--events-queue-size=*)
|
||||||
size=${arg#*=}
|
size=${arg#*=}
|
||||||
|
@ -90,7 +90,7 @@ for arg; do case "$arg" in
|
||||||
"value"
|
"value"
|
||||||
fail=true; fi;;
|
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>";;
|
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"
|
[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "-D GINT_EXTENDED_LIBC"
|
||||||
|
|
||||||
echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}"
|
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]}"
|
echo "-D EVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -521,7 +521,6 @@ int main(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void crash(void)
|
void crash(void)
|
||||||
{
|
{
|
||||||
__asm__(
|
__asm__(
|
||||||
|
@ -530,4 +529,3 @@ void crash(void)
|
||||||
"trapa #1 "
|
"trapa #1 "
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ static void draw_events(enhanced_event_t *history, int size)
|
||||||
"0", ".", "\x08", "(-)", "EXE", NULL
|
"0", ".", "\x08", "(-)", "EXE", NULL
|
||||||
};
|
};
|
||||||
const char *event_names[] = {
|
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++)
|
for(int i = 0; i < size && history[i].type != ET_None; i++)
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include <internals/rtc.h>
|
#include <internals/rtc.h>
|
||||||
#include <mpu.h>
|
#include <mpu.h>
|
||||||
|
|
||||||
static void draw(struct RTCTime time)
|
static void draw(rtc_time_t time)
|
||||||
{
|
{
|
||||||
extern Image res_rtc_segments;
|
extern Image res_rtc_segments;
|
||||||
|
|
||||||
|
@ -53,8 +53,7 @@ static void draw(struct RTCTime time)
|
||||||
static void callback(void)
|
static void callback(void)
|
||||||
{
|
{
|
||||||
extern Image res_opt_rtc;
|
extern Image res_opt_rtc;
|
||||||
struct RTCTime time = rtc_getTime();
|
rtc_time_t time = rtc_getTime();
|
||||||
time.year += 1900;
|
|
||||||
|
|
||||||
dclear();
|
dclear();
|
||||||
draw(time);
|
draw(time);
|
||||||
|
@ -62,7 +61,7 @@ static void callback(void)
|
||||||
dupdate();
|
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)
|
switch(region)
|
||||||
{
|
{
|
||||||
|
@ -129,12 +128,10 @@ static void set(void)
|
||||||
{ 72, 39, 7, 9 }, { 84, 39, 7, 9 }, { 90, 39, 7, 9 },
|
{ 72, 39, 7, 9 }, { 84, 39, 7, 9 }, { 90, 39, 7, 9 },
|
||||||
{ 96, 39, 7, 9 }, { 102, 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 region_count = 14;
|
||||||
int n = 0, slide = 0, key, leave;
|
int n = 0, slide = 0, key, leave;
|
||||||
|
|
||||||
time.year += 1900;
|
|
||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
dclear();
|
dclear();
|
||||||
|
@ -157,11 +154,9 @@ static void set(void)
|
||||||
|
|
||||||
else if(key == KEY_F1 || key == KEY_EXE)
|
else if(key == KEY_F1 || key == KEY_EXE)
|
||||||
{
|
{
|
||||||
n++;
|
|
||||||
slide = 0;
|
slide = 0;
|
||||||
if(n == region_count)
|
if(++n == region_count)
|
||||||
{
|
{
|
||||||
time.year -= 1900;
|
|
||||||
rtc_setTime(time);
|
rtc_setTime(time);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +182,7 @@ static void set(void)
|
||||||
|
|
||||||
else if(n == 6 && (slide != 1 || k != 5))
|
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);
|
set_region(&time, n, day);
|
||||||
n++;
|
n++;
|
||||||
slide = 0;
|
slide = 0;
|
||||||
|
@ -221,7 +216,6 @@ static void set(void)
|
||||||
n++;
|
n++;
|
||||||
if(n == region_count)
|
if(n == region_count)
|
||||||
{
|
{
|
||||||
time.year -= 1900;
|
|
||||||
rtc_setTime(time);
|
rtc_setTime(time);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ static clock_config_t conf;
|
||||||
static volatile int elapsed_timer = -1;
|
static volatile int elapsed_timer = -1;
|
||||||
static volatile int elapsed_rtc = -1;
|
static volatile int elapsed_rtc = -1;
|
||||||
static int cb_id = -1;
|
static int cb_id = -1;
|
||||||
|
static timer_t *htimer = NULL;
|
||||||
|
|
||||||
static void timing_rtc(void)
|
static void timing_rtc(void)
|
||||||
{
|
{
|
||||||
|
@ -35,7 +36,11 @@ static void timing_timer(void)
|
||||||
|
|
||||||
static void timing_start(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);
|
rtc_cb_edit(cb_id, RTCFreq_64Hz, timing_rtc);
|
||||||
|
|
||||||
elapsed_timer = 0;
|
elapsed_timer = 0;
|
||||||
|
@ -253,8 +258,8 @@ void test_timer(void)
|
||||||
switch(getkey_opt(Getkey_NoOption, 1))
|
switch(getkey_opt(Getkey_NoOption, 1))
|
||||||
{
|
{
|
||||||
case KEY_EXIT:
|
case KEY_EXIT:
|
||||||
timer_stop(TIMER_USER);
|
|
||||||
rtc_cb_end(cb_id);
|
rtc_cb_end(cb_id);
|
||||||
|
timer_stop(htimer);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case KEY_F1:
|
case KEY_F1:
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef _CLOCK_H
|
#ifndef _CLOCK_H
|
||||||
#define _CLOCK_H
|
#define _CLOCK_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Sleep functions.
|
// Sleep functions.
|
||||||
//---
|
//---
|
||||||
|
@ -21,12 +23,16 @@
|
||||||
void sleep(void);
|
void sleep(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
sleep_ms(), sleep_us()
|
sleep_ms()
|
||||||
Sleeps for the given number of milliseconds / microseconds using the
|
Sleeps for the given number of milliseconds using a virtual timer.
|
||||||
TIMER_USER timer. The actual sleep time will always be slightly less
|
|
||||||
than requested.
|
|
||||||
*/
|
*/
|
||||||
void sleep_ms(int ms_delay);
|
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);
|
void sleep_us(int us_delay);
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +89,7 @@ typedef struct
|
||||||
Normally you need not use this function when setting up timers because
|
Normally you need not use this function when setting up timers because
|
||||||
timer_start() handles this conversion for you.
|
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()
|
clock_config()
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#ifndef _EVENTS_H
|
#ifndef _EVENTS_H
|
||||||
#define _EVENTS_H
|
#define _EVENTS_H
|
||||||
|
|
||||||
|
#include <timer.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
event_type_t
|
event_type_t
|
||||||
Something user programs will surely use most often.
|
Something user programs will surely use most often.
|
||||||
|
@ -30,6 +32,9 @@ typedef enum
|
||||||
EventType_KeyReleased = 4,
|
EventType_KeyReleased = 4,
|
||||||
ET_KeyRel = EventType_KeyReleased,
|
ET_KeyRel = EventType_KeyReleased,
|
||||||
|
|
||||||
|
EventType_TimerUnderflow = 5,
|
||||||
|
ET_Timer = EventType_TimerUnderflow,
|
||||||
|
|
||||||
} event_type_t;
|
} event_type_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -47,6 +52,8 @@ typedef struct
|
||||||
void *data;
|
void *data;
|
||||||
// For ET_KeyPress, ET_KeyRepeat and ET_KeyRel.
|
// For ET_KeyPress, ET_KeyRepeat and ET_KeyRel.
|
||||||
int key;
|
int key;
|
||||||
|
// For ET_Timer.
|
||||||
|
timer_t *timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
} event_t;
|
} 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
|
struct rtc_cb
|
||||||
{
|
{
|
||||||
enum RTCFrequency freq;
|
rtc_frequency_t freq;
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
void (*callback)(void);
|
void (*callback)(void);
|
||||||
|
@ -24,6 +24,19 @@ struct rtc_cb
|
||||||
// The callback array.
|
// The callback array.
|
||||||
struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE];
|
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
|
struct mod_rtc
|
||||||
This structure describes the arrangement of RTC register in the memory.
|
This structure describes the arrangement of RTC register in the memory.
|
||||||
|
|
|
@ -1,49 +1,76 @@
|
||||||
#ifndef _INTERNALS_TIMER_H
|
#ifndef _INTERNALS_TIMER_H
|
||||||
#define _INTERNALS_TIMER_H 1
|
#define _INTERNALS_TIMER_H
|
||||||
|
|
||||||
|
#include <timer.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
struct Timer
|
timer_t
|
||||||
This structure holds information for a running timer.
|
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 *callback;
|
||||||
void *data;
|
void *argument;
|
||||||
int repeats;
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
timer_interrupt()
|
||||||
This structure holds information about the timer unit (peripheral
|
Handles the interrupt for the given timer channel.
|
||||||
module) registers.
|
|
||||||
*/
|
*/
|
||||||
struct mod_tmu
|
void timer_interrupt(int channel);
|
||||||
{
|
|
||||||
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.
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
timer_get()
|
timer_callback_event()
|
||||||
Returns the timer and TSTR register addresses.
|
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,
|
void timer_callback_event(timer_t *timer);
|
||||||
volatile unsigned char **tstr);
|
|
||||||
|
/*
|
||||||
|
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
|
#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
|
#ifndef _RTC_H
|
||||||
#define _RTC_H 1
|
#define _RTC_H 1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Time access.
|
// Time access.
|
||||||
//---
|
//---
|
||||||
|
|
||||||
/*
|
/*
|
||||||
struct RTCTime
|
rtc_time_t
|
||||||
Defines a point in time.
|
Defines a point in time.
|
||||||
*/
|
*/
|
||||||
struct RTCTime
|
typedef struct
|
||||||
{
|
{
|
||||||
int seconds; // Seconds in range 0-59
|
uint8_t seconds; // Seconds in range 0-59
|
||||||
int minutes; // Minutes in range 0-59
|
uint8_t minutes; // Minutes in range 0-59
|
||||||
int hours; // Hours in range 0-23
|
uint8_t hours; // Hours in range 0-23
|
||||||
int month_day; // Day of month in range 1-31
|
uint8_t month_day; // Day of month in range 1-31
|
||||||
int month; // Month in range 0-11
|
uint8_t month; // Month in range 0-11
|
||||||
int year; // Years (full value)
|
uint8_t week_day; // Day of week in range 0(Sunday)-6(Saturday).
|
||||||
int week_day; // Day of week in range 0(Sunday)-6(Saturday).
|
uint16_t year; // Years (around 2000)
|
||||||
};
|
|
||||||
|
} rtc_time_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
rtc_getTime()
|
rtc_getTime()
|
||||||
Reads the current time from the RTC. There is no guarantee that the
|
Reads the current time from the RTC. There is no guarantee that the
|
||||||
week day is correct (use the time API for that).
|
week day is correct (use the time API for that).
|
||||||
*/
|
*/
|
||||||
struct RTCTime rtc_getTime(void);
|
rtc_time_t rtc_getTime(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
rtc_setTime()
|
rtc_setTime()
|
||||||
Sets the time in the RTC registers. The week day is set to 0 if greater
|
Sets the time in the RTC registers. The week day is set to 0 if greater
|
||||||
than 6. Other fields are not checked.
|
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
|
Describes the possible frequencies available for the real-time clock
|
||||||
interrupt.
|
interrupt.
|
||||||
*/
|
*/
|
||||||
enum RTCFrequency
|
typedef enum
|
||||||
{
|
{
|
||||||
RTCFreq_500mHz = 7,
|
RTCFreq_500mHz = 7,
|
||||||
RTCFreq_1Hz = 6,
|
RTCFreq_1Hz = 6,
|
||||||
|
@ -63,7 +66,8 @@ enum RTCFrequency
|
||||||
RTCFreq_64Hz = 2,
|
RTCFreq_64Hz = 2,
|
||||||
RTCFreq_256Hz = 1,
|
RTCFreq_256Hz = 1,
|
||||||
RTCFreq_None = 0,
|
RTCFreq_None = 0,
|
||||||
};
|
|
||||||
|
} rtc_frequency_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
rtc_cb_add()
|
rtc_cb_add()
|
||||||
|
@ -74,7 +78,7 @@ enum RTCFrequency
|
||||||
The number of repeats may be set to 0, in which case the callback is
|
The number of repeats may be set to 0, in which case the callback is
|
||||||
called indefinitely unless the user calls rtc_cb_end().
|
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()
|
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
|
can set the function to NULL or the frequency to RTCFreq_None to
|
||||||
temporarily disable the callback.
|
temporarily disable the callback.
|
||||||
*/
|
*/
|
||||||
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));
|
||||||
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);
|
|
||||||
|
|
||||||
#endif // _RTC_H
|
#endif // _RTC_H
|
||||||
|
|
217
include/timer.h
217
include/timer.h
|
@ -11,106 +11,157 @@
|
||||||
#define _TIMER_H 1
|
#define _TIMER_H 1
|
||||||
|
|
||||||
#include <clock.h>
|
#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.
|
#ifndef TIMER_SLOTS
|
||||||
#define TIMER_0 0
|
#define TIMER_SLOTS 16
|
||||||
#define TIMER_TMU0 TIMER_0
|
#endif
|
||||||
#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
|
|
||||||
|
|
||||||
// Timer prescalers.
|
/*
|
||||||
#define TIMER_Po_4 0
|
timer_create()
|
||||||
#define TIMER_Po_16 1
|
Basic timer configuration. This function creates a virtual timer and
|
||||||
#define TIMER_Po_64 2
|
configures its delay and repetition count. It returns a virtual timer
|
||||||
#define TIMER_Po_256 3
|
object that will be used by other functions. At most TIMER_SLOTS
|
||||||
#define TIMER_TCLK 5
|
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()
|
timer_start()
|
||||||
Configures and starts a timer. The timer argument expects a timer name.
|
Starts a virtual or hardware timer. If the timer has a callback
|
||||||
You can use TIMER_USER anytime. You may also use TIMER_GRAY if you're
|
attached, then the callback function will start being called regularly;
|
||||||
not running the gray engine.
|
otherwise, the timer will start pushing ET_Timer events to the event
|
||||||
Unit names are defined in the clock.h header and must be one of the
|
queue. It is advised, not to change a timer's configuration while it's
|
||||||
following:
|
running.
|
||||||
- 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.
|
|
||||||
*/
|
*/
|
||||||
void timer_start(int timer, int delay_or_frequency, enum ClockUnit unit,
|
void timer_start(timer_t *timer);
|
||||||
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);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
timer_stop()
|
timer_stop()
|
||||||
Stops the given timer. This function may be called even if the timer is
|
Stops a timer. If the argument is virtual timer, it is paused and can
|
||||||
not running.
|
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);
|
void timer_stop(timer_t *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);
|
|
||||||
|
|
||||||
#endif // _TIMER_H
|
#endif // _TIMER_H
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <clock.h>
|
#include <clock.h>
|
||||||
#include <timer.h>
|
#include <timer.h>
|
||||||
#include <internals/timer.h>
|
#include <modules/timer.h>
|
||||||
#include <rtc.h>
|
#include <rtc.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <mpu.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
|
Several units can be used. Be aware that the result is approximate, and
|
||||||
very high frequencies or very short delays will yield important errors.
|
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 f = conf.Pphi_f >> 2;
|
||||||
uint64_t result;
|
uint64_t result;
|
||||||
|
|
||||||
|
@ -74,30 +74,42 @@ void sleep(void)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void sleep_callback(void *arg)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
*((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)
|
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)
|
void sleep_us(int us_delay)
|
||||||
{
|
{
|
||||||
volatile int sleep_done = 0;
|
volatile int sleep_done = 0;
|
||||||
timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback,
|
const uint32_t constant = clock_setting(us_delay, Clock_us);
|
||||||
(void *)&sleep_done, 1);
|
|
||||||
|
|
||||||
do sleep();
|
timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1);
|
||||||
while(!sleep_done);
|
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.
|
// Indicates whether the measurements are finished.
|
||||||
static volatile int clock_measure_done = 0;
|
static volatile int clock_measure_done = 0;
|
||||||
// Once again SH7705 and SH7305 need different methods...
|
// Once again SH7705 and SH7305 need different methods...
|
||||||
|
static timer_t *htimer_7705 = NULL;
|
||||||
static int cb_id_7705 = -1;
|
static int cb_id_7705 = -1;
|
||||||
static void clock_measure_7705(void);
|
static void clock_measure_7705(void);
|
||||||
static void clock_compute_7305(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.
|
// P_phi using a timer/RTC combination, and we deduce CKIO.
|
||||||
if(isSH3())
|
if(isSH3())
|
||||||
{
|
{
|
||||||
// We prepare the timer manually, without starting it, so that
|
htimer_7705 = htimer_setup(timer_user, 0xffffffff, timer_Po_4,
|
||||||
// we only have to push the running bit to start it when the
|
1);
|
||||||
// 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;
|
|
||||||
|
|
||||||
cb_id_7705 = rtc_cb_add(RTCFreq_256Hz, clock_measure_7705, 0);
|
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
|
Given the number of P_phi / 4 timer ticks elapsed between two RTC
|
||||||
256 Hz interrupts, determines the clock configuration.
|
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;
|
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)
|
static void clock_measure_7705_callback(void)
|
||||||
{
|
{
|
||||||
timer_stop(TIMER_USER);
|
timer_stop(htimer_7705);
|
||||||
rtc_cb_end(cb_id_7705);
|
rtc_cb_end(cb_id_7705);
|
||||||
|
|
||||||
volatile struct mod_tmu *tmu;
|
uint32_t elapsed = 0xffffffff - TMU.timers[timer_user]->TCNT;
|
||||||
timer_get(TIMER_USER, &tmu, NULL);
|
|
||||||
int elapsed = 0xffffffff - tmu->TCNT;
|
|
||||||
|
|
||||||
clock_measure_7705_finalize(elapsed);
|
clock_measure_7705_finalize(elapsed);
|
||||||
clock_measure_done = 1;
|
clock_measure_done = 1;
|
||||||
}
|
}
|
||||||
|
@ -274,9 +267,6 @@ static void clock_measure_7705_callback(void)
|
||||||
*/
|
*/
|
||||||
static void clock_measure_7705(void)
|
static void clock_measure_7705(void)
|
||||||
{
|
{
|
||||||
volatile unsigned char *tstr;
|
timer_start(htimer_7705);
|
||||||
timer_get(TIMER_USER, NULL, &tstr);
|
|
||||||
|
|
||||||
*tstr |= (1 << TIMER_USER);
|
|
||||||
rtc_cb_edit(cb_id_7705, RTCFreq_256Hz, clock_measure_7705_callback);
|
rtc_cb_edit(cb_id_7705, RTCFreq_256Hz, clock_measure_7705_callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <internals/clock.h>
|
#include <internals/clock.h>
|
||||||
#include <clock.h>
|
#include <clock.h>
|
||||||
#include <gint.h>
|
#include <gint.h>
|
||||||
|
#include <internals/modules.h>
|
||||||
|
|
||||||
int main(void);
|
int main(void);
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ __attribute__((section(".pretext.entry"))) int start(void)
|
||||||
dg->stage = stage_startup;
|
dg->stage = stage_startup;
|
||||||
|
|
||||||
// Exception records: what kind of exceptions / TLB faults / interrupts
|
// 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;
|
dg->excepts = 0;
|
||||||
for(size_t i = 0; i < sizeof dg->except_vect; i++)
|
for(size_t i = 0; i < sizeof dg->except_vect; i++)
|
||||||
dg->except_vect[i] = 0;
|
dg->except_vect[i] = 0;
|
||||||
|
@ -67,7 +68,7 @@ __attribute__((section(".pretext.entry"))) int start(void)
|
||||||
dg->expevt = 0x00000000;
|
dg->expevt = 0x00000000;
|
||||||
dg->tea = 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
|
// size and position of each section, as well as read-only / read-write
|
||||||
// types of addresses.
|
// types of addresses.
|
||||||
dg->section_text.address = (uint32_t)&btext;
|
dg->section_text.address = (uint32_t)&btext;
|
||||||
|
@ -107,7 +108,10 @@ __attribute__((section(".pretext.entry"))) int start(void)
|
||||||
dg->stage = stage_mmu;
|
dg->stage = stage_mmu;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Initializing gint.
|
// Detecting the MPU type, initializing the register module addresses,
|
||||||
|
// and initializing gint.
|
||||||
|
mpu_init();
|
||||||
|
mod_init();
|
||||||
gint_init();
|
gint_init();
|
||||||
|
|
||||||
#ifdef GINT_DIAGNOSTICS
|
#ifdef GINT_DIAGNOSTICS
|
||||||
|
|
|
@ -29,11 +29,6 @@ void gint_init(void)
|
||||||
uint32_t *ptr = &bgint;
|
uint32_t *ptr = &bgint;
|
||||||
uint32_t *src = &gint_data;
|
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.
|
// Loading the interrupt handler into the memory.
|
||||||
while(ptr < &egint) *ptr++ = *src++;
|
while(ptr < &egint) *ptr++ = *src++;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <internals/interrupts.h>
|
#include <internals/interrupts.h>
|
||||||
#include <timer.h>
|
#include <internals/timer.h>
|
||||||
#include <rtc.h>
|
#include <internals/rtc.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
inth_timer_underflow()
|
inth_timer_underflow()
|
||||||
|
@ -17,5 +17,5 @@ void inth_timer_underflow(uint32_t channel)
|
||||||
*/
|
*/
|
||||||
void inth_rtc_periodic(void)
|
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;
|
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)
|
void gray_interrupt(void)
|
||||||
{
|
{
|
||||||
timer_reload2(TIMER_GRAY, delays[(~current) & 1]);
|
htimer_reload(timer_gray, delays[(~current) & 1]);
|
||||||
|
|
||||||
screen_display(vrams[current]);
|
screen_display(vrams[current]);
|
||||||
current ^= 1;
|
current ^= 1;
|
||||||
}
|
}
|
||||||
|
@ -68,8 +69,10 @@ void gray_start(void)
|
||||||
{
|
{
|
||||||
if(runs) return;
|
if(runs) return;
|
||||||
|
|
||||||
timer_start2(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt,
|
gray_timer = htimer_setup(timer_gray, delays[0], timer_Po_64, 0);
|
||||||
NULL, 0);
|
timer_attach(gray_timer, gray_interrupt, NULL);
|
||||||
|
timer_start(gray_timer);
|
||||||
|
|
||||||
current &= 1;
|
current &= 1;
|
||||||
runs = 1;
|
runs = 1;
|
||||||
}
|
}
|
||||||
|
@ -81,9 +84,7 @@ void gray_start(void)
|
||||||
*/
|
*/
|
||||||
void gray_stop(void)
|
void gray_stop(void)
|
||||||
{
|
{
|
||||||
if(!runs) return;
|
timer_stop(gray_timer);
|
||||||
|
|
||||||
timer_stop(TIMER_GRAY);
|
|
||||||
runs = 0;
|
runs = 0;
|
||||||
|
|
||||||
display_useVRAM(display_getLocalVRAM());
|
display_useVRAM(display_getLocalVRAM());
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE] = { 0 };
|
||||||
// Callback identifier (unique).
|
// Callback identifier (unique).
|
||||||
static int unique_id = 1;
|
static int unique_id = 1;
|
||||||
// Current RTC interrupt frequency.
|
// 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.
|
// 256-Hz tick count. This counter is stopped when no callback is registered.
|
||||||
static unsigned elapsed256 = 0;
|
static unsigned elapsed256 = 0;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ static unsigned elapsed256 = 0;
|
||||||
*/
|
*/
|
||||||
static void rtc_cb_update(void)
|
static void rtc_cb_update(void)
|
||||||
{
|
{
|
||||||
enum RTCFrequency max = RTCFreq_None;
|
rtc_frequency_t max = RTCFreq_None;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) if(cb_array[n].id)
|
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
|
The number of repeats may be set to 0, in which case the callback is
|
||||||
called indefinitely unless the user calls rtc_cb_end().
|
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;
|
int n = 0;
|
||||||
if(freq == RTCFreq_None || !function || repeats < 0) return -2;
|
if(freq == RTCFreq_None || !function || repeats < 0) return -2;
|
||||||
|
@ -92,7 +92,7 @@ void rtc_cb_end(int id)
|
||||||
-2 Invalid parameters
|
-2 Invalid parameters
|
||||||
This function never removes a callback. Call rtc_cb_end() for this.
|
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))
|
void (*new_function)(void))
|
||||||
{
|
{
|
||||||
if(new_freq < 0 || new_freq > 7) return -2;
|
if(new_freq < 0 || new_freq > 7) return -2;
|
||||||
|
|
|
@ -21,10 +21,14 @@ static int integer16(int bcd)
|
||||||
Reads the current time from the RTC. There is no guarantee that the
|
Reads the current time from the RTC. There is no guarantee that the
|
||||||
week day is correct (use the time API for that).
|
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;
|
volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305;
|
||||||
struct RTCTime time;
|
rtc_time_t time;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
rtc->RCR1.CF = 0;
|
||||||
|
|
||||||
time.seconds = integer8(rtc->RSECCNT.BYTE);
|
time.seconds = integer8(rtc->RSECCNT.BYTE);
|
||||||
time.minutes = integer8(rtc->RMINCNT.BYTE);
|
time.minutes = integer8(rtc->RMINCNT.BYTE);
|
||||||
|
@ -33,6 +37,8 @@ struct RTCTime rtc_getTime(void)
|
||||||
time.month = integer8(rtc->RMONCNT.BYTE);
|
time.month = integer8(rtc->RMONCNT.BYTE);
|
||||||
time.year = integer16(rtc->RYRCNT.WORD);
|
time.year = integer16(rtc->RYRCNT.WORD);
|
||||||
time.week_day = rtc->RWKCNT;
|
time.week_day = rtc->RWKCNT;
|
||||||
|
}
|
||||||
|
while(rtc->RCR1.CF != 0);
|
||||||
|
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
#include <rtc.h>
|
#include <rtc.h>
|
||||||
#include <mpu.h>
|
#include <mpu.h>
|
||||||
|
|
||||||
|
int rtc_carry_flag = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
rtc_interrupt()
|
rtc_periodic_interrupt()
|
||||||
Handles an RTC interrupt by calling the callback.
|
Handles an RTC interrupt by calling the callback.
|
||||||
*/
|
*/
|
||||||
void rtc_interrupt(void)
|
void rtc_periodic_interrupt(void)
|
||||||
{
|
{
|
||||||
rtc_cb_interrupt();
|
rtc_cb_interrupt();
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,14 @@ static int bcd16(int integer)
|
||||||
Sets the time in the RTC registers. The week day is set to 0 if greater
|
Sets the time in the RTC registers. The week day is set to 0 if greater
|
||||||
than 6. Other fields are not checked.
|
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;
|
volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305;
|
||||||
|
int wday = (time.week_day < 7) ? (time.week_day) : (0);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
rtc->RCR1.CF = 0;
|
||||||
|
|
||||||
rtc->RSECCNT.BYTE = bcd8(time.seconds);
|
rtc->RSECCNT.BYTE = bcd8(time.seconds);
|
||||||
rtc->RMINCNT.BYTE = bcd8(time.minutes);
|
rtc->RMINCNT.BYTE = bcd8(time.minutes);
|
||||||
|
@ -32,5 +37,7 @@ void rtc_setTime(struct RTCTime time)
|
||||||
rtc->RDAYCNT.BYTE = bcd8(time.month_day);
|
rtc->RDAYCNT.BYTE = bcd8(time.month_day);
|
||||||
rtc->RMONCNT.BYTE = bcd8(time.month);
|
rtc->RMONCNT.BYTE = bcd8(time.month);
|
||||||
rtc->RYRCNT.WORD = bcd16(time.year);
|
rtc->RYRCNT.WORD = bcd16(time.year);
|
||||||
rtc->RWKCNT = time.week_day < 7 ? time.week_day : 0;
|
rtc->RWKCNT = wday;
|
||||||
|
}
|
||||||
|
while(rtc->RCR1.CF != 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
*/
|
*/
|
||||||
time_t time(time_t *timeptr)
|
time_t time(time_t *timeptr)
|
||||||
{
|
{
|
||||||
struct RTCTime rtc = rtc_getTime();
|
rtc_time_t rtc = rtc_getTime();
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
time_t calendar;
|
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