diff --git a/include/gint/clock.h b/include/gint/clock.h index c35c0ba..fa75c9b 100644 --- a/include/gint/clock.h +++ b/include/gint/clock.h @@ -5,6 +5,8 @@ #ifndef GINT_CLOCK #define GINT_CLOCK +#include + //--- // Clock signals //--- @@ -59,22 +61,13 @@ const clock_frequency_t *clock_freq(void); add-in is idle, for instance while waiting for keyboard input. */ #define sleep() __asm__("sleep") -/* sleep_us() - sleep for a definite duration in microseconds +/* sleep_us(): Sleep for a fixed duration in microseconds + Stops the processor until the specified delay in microseconds has elapsed. + (The processor will still wake up occasionally to handle interrupts.) This + function selects a timer with timer_setup() called with TIMER_ANY. */ +void sleep_us(uint64_t delay_us); - Stops the processor until [delay_us] microseconds have elapsed. Interrupts - may occur during that time (especially timers firing), in which case the - events will be treated as usual. The processor will resume sleeping after - handling them. - - The user may choose the timer used to time out the sleep. Remember that only - timers 0 to 2 have microsecond-level resolution; other timers count in units - of about 30 us. - - @timer Which timer to use to time out the sleep - @us_delay How long to sleep (in microseconds) */ -void sleep_us(int timer, int us_delay); - -/* sleep_ms() - sleep for a definite duration in milliseconds */ -#define sleep_ms(timer, ms_delay) sleep_us(timer, (ms_delay) * 1000) +/* sleep_ms(): Sleep for a fixed duration in milliseconds */ +#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull) #endif /* GINT_CLOCK */ diff --git a/include/gint/defs/attributes.h b/include/gint/defs/attributes.h index 1ca4ca7..3c75322 100644 --- a/include/gint/defs/attributes.h +++ b/include/gint/defs/attributes.h @@ -28,11 +28,12 @@ /* Aligned variables */ #define GALIGNED(x) __attribute__((aligned(x))) - /* Packed structures. I require explicit alignment because if it's unspecified, GCC cannot optimize access size, and reads to memory-mapped I/O with invalid access sizes silently fail - honestly you don't want this to happen */ #define GPACKED(x) __attribute__((packed, aligned(x))) +/* Transparent unions */ +#define GTRANSPARENT __attribute__((transparent_union)) /* Weak symbols */ #define GWEAK __attribute__((weak)) diff --git a/include/gint/timer.h b/include/gint/timer.h index da0e986..4c4da72 100644 --- a/include/gint/timer.h +++ b/include/gint/timer.h @@ -9,114 +9,203 @@ #include #include -/* Timer identifiers +/* Timer types and numbers - Hardware timers are numbered with integers starting from 0. You can freely - access all the available timers by using their number once you have - configured them with timer_setup(). The number of timers depends on the MPU: + If you're new to timers, read this comment and then check timer_setup() and + timer_start(): most of the time you only need these. - SH3-based: 4 timers, ids 0..3 [SH7355, SH7337] - SH4-based: 9 timers, ids 0..8 [SH7305] + There are two types of timers on the calculator: normal timers called TMU, + and extra timers added by Casio called ETMU. The main difference is that TMU + are very precise (about 4 MHz; the resolution is about 250 ns) while ETMU + are much less precise (32 kHz; the resolution is about 30 µs). - You should be aware that some of these timers are used by default by gint: - - Timer 0 is used by the gray engine on fx9860g. - - Timer 3/8 is used by the keyboard on SH3/SH4. + The number of available timers also depends on the platform: + * SH3-based fx9860g have 3 TMU (ID 0,1,2) and 1 ETMU (ID 3) + * SH4-based fx9860g and fxcg50 have 3 TMU (ID 0,1,2) and 6 ETMU (ID 3..8) - timer_setup() will fail if you try to use a timer that's already running. - Always check the return value of timer_setup()! Using a timer id that has - not been validated by timer_setup() will work, but do *something else* than - what you intended. */ + You can request "a" timer with timer_setup(), and gint will choose an + available timer depending on the precision you requested. Or, if you want + one specifically, you ask for an ID of your choice. -/* timer_count() - tells how many timers are available on the platform */ + gint uses 1 to 2 timers by default: + * One for the keyboard on all calculators, always an ETMU + * The gray engine on fx9860g uses TMU0 for interrupt priority reasons; TMU0 + is normally available for it unless you've used all TMUs at the same time + libprof also uses a TMU. + + Most of the time you can just use timer_setup() and specify TIMER_ANY. If + you want to be sure that you have a TMU or an ETMU, you can do so by + specifying TIMER_TMU or TIMER_ETMU. If you further want to have a specific + timer with specific settings, then you can: + + * Set a specific ID in timer_setup(), in which case the delay is no longer + interpreter as count of µs, but as a TCOR value. + * If this ID is a TMU, you can further add (with + or |) a prescaler + specification, one of TIMER_Po_{4,16,64,256}. + * Regardless of how the timer was obtained, you can use timer_reload() to + replace the value of TCOR. + * Also note that TMU0, TMU1, TMU2 and the ETMU have respective interrupt + priority levels of 13, 11, 9, and 7. The gray engine uses TMU0 to + guarantee maximum visual stability in the presence of interrupts. + + In this module, timers are manipulated through their ID. timer_setup() + returns the ID of a timer which was allocated to you. You can check it to + determine whether your timer is a TMU (0,1,2) or an ETMU (3 or more). */ + +/* timer_count(): Number of timers available on the platform */ #define timer_count() (isSH3() ? 4 : 9) /* Clock input - Timers count down when their input clock ticks, and fire when their counter - reach 0. The choice of the input clock influences the resolution of the - timer, but if the clock is too fast, the 32-bit counter might not be able to - represent long delays. + Standard TMU can count at different speeds. A fast speed offers more + precision but a slower speed offers longer delays. gint automatically + selects suitable speed by default. - Several input clocks are available. The peripheral clock (Po) can be divided - by 4, 16, 64 or 256; as an alternative the external clock TCLK can be used - for counting. I suspect TCLK runs at a fixed frequency of 32768 Hz, but this - has yet to be verified. + If you want something very particular, you can add (with + or |) a prescaler + value to a chosen ID in timer_setup() to request that specific value. The + default prescaler if the ID is fixed is TIMER_Pphi_4. */ +enum { + TIMER_Pphi_4 = 0x00, + TIMER_Pphi_16 = 0x10, + TIMER_Pphi_64 = 0x20, + TIMER_Pphi_256 = 0x30, +}; - You don't really need to choose an input clock unless you are doing - something very specific. In most practical cases you can use timer_default - which is 0. See the timer_delay() function for more information. */ -typedef enum +/* Timer selection; see timer_setup() */ +enum { + TIMER_ANY = -1, + TIMER_TMU = -2, + TIMER_ETMU = -3, +}; + +/* Type of callback functions + + This module used to require callbacks of type int (*)(volatile void *), but + callbacks are rarely this generic. Now timer_setup() accepts functions of + any of the types below. */ +typedef union { - timer_Po_4 = 0, - timer_Po_16 = 1, - timer_Po_64 = 2, - timer_Po_256 = 3, - timer_TCLK = 5, + /* No argument, returns either TIMER_CONTINUE or TIMER_STOP */ + int (*v)(void); + /* Single integer argument */ + int (*i)(int); + /* Single pointer argument, cv-qualified as needed */ + int (*pv) (void *); + int (*pVv) (volatile void *); + int (*pCv) (const void *); + int (*pCVv)(volatile const void *); - timer_default = timer_Po_4, +} GTRANSPARENT timer_callback_t; -} timer_input_t; +/* Return value for timer callbacks, indicating whether the timer should + continue running and fire again, or stop now */ +enum { + TIMER_CONTINUE = 0, + TIMER_STOP = 1, +}; //--- // Timer functions //--- -/* timer_setup() - set up a timer +/* timer_setup(): Reserve and configure a timer - This function configures the requested timer without starting it. On - success, it returns the first argument "timer", which is used as a timer - identifier in all other timer functions. If the requested timer is already - in use, this function fails and returns a negative number. + This function finds and configures a timer (without starting it). On + success, it returns the ID of the configured timer, which is used in all + other timer functions. If no timer matching the requested settings is + available, this function returns -1. - This function sets the timer delay, the clock source, and registers a - callback function to be called when the timer fires. An argument can be - supplied to the callback function in the form of a pointer. + When started, the configured timer will run for the requested delay and call + the supplied callback function at the end of this delay. The callback + function can then decide whether to leave the timer running (and be called + again after the same delay) or stop the timer. - When the timer fires, the callback function is called with the provided - argument pointer. The callback decides whether the timer should continue - running (by returning 0) or stop (by returning nonzero). In the latter case, - events accumulated while the callback was running are dropped. + The first argument specifies what kind of timer you want. + * TIMER_ANY will let timer_setup() choose any available timer. timer_setup() + will only use an ETMU if the delay is more than 0.1 ms to avoid resolution + issues. Most of the time this is what you need. + * TIMER_TMU or TIMER_ETMU will let timer_setup() choose an available TMU or + ETMU, respectively. + * Specifying an ID (0..8) will request exactly that timer. In this case, and + if the ID is a TMU (0,1,2), you may add (with + or |) a prescaler value to + specify the clock input. Otherwise the clock is set to Pphi/4. + If no timer matching the supplied settings is available, timer_setup() + returns -1. - It is sometimes difficult to choose a timer constant and a clock source - given a wished delay in seconds, especially when overclock is used. The - timer_delay() function is provided for this purpose. + The second argument is the delay. With TIMER_ANY, TIMER_TMU and TIMER_ETMU, + the delay is interpreted as a number of µs. With an explicit ID, the delay + is interpreted as a value of TCOR; see timer_delay() in this case. Note that + TCOR values are sensitive to the overclock level! - @timer Requested timer id - @delay Delay between each event (the unit depends on the clock source) - @clock Clock source used by the timer for counting down - @callback Callback function (called when the timer fires) - @arg Passed as argument to the callback function */ -int timer_setup(int timer, uint32_t delay, timer_input_t clock, - int (*callback)(volatile void *arg), volatile void *arg); + The third argument is a function to be called when the timer expires. It may + have a single 4-byte argument (int or pointer), in which case you should + provide the value to call it with as an optional argument (you can only use + a single argument). It must return an int equal to either TIMER_CONTINUE or + TIMER_STOP to control the subsequent operation of the timer. See the + definition of timer_callback_t above for the possible function types. If + this argument is NULL, a default function that stops the timer will be used. -/* timer_delay() - compute a delay constant from a duration in seconds + On success, the configured timer becomes reserved; it can no longer be + returned by timer_setup() until: + * Either timer_stop() is called, + * Or the callback returns TIMER_STOP (which also stops the timer). + Remember than the returned timer is not started yet; see timer_start(). - This function can used as a facility to calculate the [delay] argument to - the timer_setup() function. It takes a microsecond delay as an argument and - returns the corresponding timer constant. A typical use to start a timer - with a 25 ms interval would be: + @timer Requested timer; TIMER_{ANY,TMU,ETMU} or an ID with prescaler + @delay Delay between each event, in µs unless first argument is an ID + @callback Function to be called when the timer fires + @... If the callback takes an argument, specify that value here */ +int timer_setup(int timer, uint64_t delay_us, timer_callback_t callback, ...); - timer_setup(0, timer_delay(0, 25 * 1000), 0, callback, arg); +/* Makes sure an argument is always provided, for va_arg() */ +#define timer_setup(...) timer_setup(__VA_ARGS__, 0) - WARNING: Only timers 0 to 2 can count microseconds! Other timers have a - resolution of around 30 us. Counting in ms is safe for all timers, though. - - For standard timers (0 to 2) it uses Po / 4 as clock input, which is very - precise and can represent up to 3 minutes' time; for extra timers (3 and - above) the clock is fixed to 32768 Hz. - - @timer The timer you are planning to use - @delay_us Requested delay in microseconds */ -uint32_t timer_delay(int timer, uint64_t delay_us); - -/* timer_start() - start a configured timer - The specified timer will start counting down and fire callbacks at regular - intervals. - - @timer Timer id, as returned by timer_setup() */ +/* timer_start(): Start a configured timer + The specified timer will start counting down and call its callback function + at regular intervals until it is paused or stopped. */ void timer_start(int timer); -/* timer_reload() - change a timer's delay constant for next interrupts +/* timer_pause(): Pause a timer without freeing it + Pauses the specified timer will be paused. The timer stops counting but is + not freed and can be resumed by calling timer_start(). */ +void timer_pause(int timer); + +/* timer_stop(): Stop and free a timer + Stops and frees a timer, making it available to timer_setup(). This timer + should no longer be used unless it is returned again by timer_setup(). */ +void timer_stop(int timer); + +/* timer_wait(): Wait for a timer to stop + Waits until the timer pauses or stops. If the timer is not running, returns + immediately. Even after timer_wait(), the timer may not be available since + it may have only paused. If the timer never stops, you're in trouble. */ +void timer_wait(int timer); + +//--- +// Low-level functions +//--- + +/* timer_delay(): Compute a timer constant from a duration in seconds + + This function calculates the timer constant for a given delay. timer_setup() + does this computation when TIMER_ANY, TIMER_TMU or TIMER_ETMU is provided, + but expects you to provide the exact constant when an explicit timer ID is + requested. timer_reload() also expects the constant. + + For TMU the clock can be Pphi/4, Pphi/16, Pphi/64 and Pphi/256, which can + respectively count up to about 350 seconds, 23 minutes, 95 minutes and 381 + minutes. + + For ETMU the clock is TCLK at 32768 Hz, which can count up to 36 hours. Any + longer delay should probably be managed by the RTC (which counts even when + the calculator is off). + + @timer The timer you are planning to use + @delay_us Requested delay in microseconds + @clock The clock value, irrelevant if timer >= 3 */ +uint32_t timer_delay(int timer, uint64_t delay_us, int clock); + +/* timer_reload(): Change a timer's delay constant for next interrupts Changes the delay constant of the given timer. Nothing will happen until the next callback; then the timer will update its delay to reflect the new @@ -126,35 +215,14 @@ void timer_start(int timer); @delay New delay (unit depends on the clock source) */ void timer_reload(int timer, uint32_t delay); -/* timer_pause() - stop a running timer - The specified timer will be paused; its counter will not be reset. A stopped - timer can be resumed anytime by calling timer_start(). If you want to also - reset the counter, use timer_reload(). - - @timer Timer id, as returned by timer_setup() */ -void timer_pause(int timer); - -/* timer_stop() - stop and free a timer - Stops and destroys a timer, making its id free for re-use. The id must not - be used anymore until it is returned by a further call to timer_setup(). - - @timer Timer id, as returned by timer_setup() */ -void timer_stop(int timer); - -/* timer_wait() - wait for a timer to stop - Waits until the specified timer stops running. If the timer is not running, - returns immediately. The timer might not be free if it has just been paused - instead of stopped entirely. */ -void timer_wait(int timer); - //--- // Predefined timer callbacks //--- -/* timer_timeout() - callback that sets a flag and halts the timer - This predefined callback may be used when a timeout is required. It sets its - argument pointer to 1 and halts the timer. The pointer must be of type - int * and you must declare the variable as volatile int. */ +/* timer_timeout(): Callback that sets a flag and halts the timer + This predefined callback may be used when a timeout is required. It takes a + single argument which must be a pointer to int (or other integer type of 4 + bytes) and increments it. */ int timer_timeout(volatile void *arg); #endif /* GINT_TIMER */ diff --git a/src/gray/engine.c b/src/gray/engine.c index 09f24fe..d0d1d4a 100644 --- a/src/gray/engine.c +++ b/src/gray/engine.c @@ -74,13 +74,13 @@ GDESTRUCTOR static void gray_quit(void) } /* gray_int(): Interrupt handler */ -int gray_int(GUNUSED volatile void *arg) +int gray_int(void) { t6k11_display(vrams[st ^ 2], 0, 64, 16); timer_reload(GRAY_TIMER, delays[(st ^ 3) & 1]); st ^= 1; - return 0; + return TIMER_CONTINUE; } /* gray_start(): Start the gray engine */ @@ -92,9 +92,8 @@ void gray_start(void) if(runs) return; - int free = timer_setup(GRAY_TIMER, delays[0], timer_Po_64, gray_int, - NULL); - if(free != GRAY_TIMER) return; + int timer = timer_setup(GRAY_TIMER|TIMER_Pphi_64, delays[0], gray_int); + if(timer != GRAY_TIMER) return; timer_start(GRAY_TIMER); st = 0; diff --git a/src/keysc/keysc.c b/src/keysc/keysc.c index b95515d..8e8a678 100644 --- a/src/keysc/keysc.c +++ b/src/keysc/keysc.c @@ -248,7 +248,7 @@ int keydown_any(int key, ...) // Driver initialization //--- -static int callback(GUNUSED volatile void *arg) +static int callback(void) { keysc_frame(); time++; @@ -258,20 +258,18 @@ static int callback(GUNUSED volatile void *arg) /* init() - setup the support timer */ static void init(void) { - int tid = isSH3() ? 3 : 8; - /* Configure the timer to do 128 keyboard scans per second. This frequency *must* be high for the KEYSC interface to work! */ /* Note: the supporting timer always runs at 32768 Hz. */ - int delay = 32768 / KEYBOARD_SCAN_FREQUENCY; + int delay = 1000000 / KEYBOARD_SCAN_FREQUENCY; if(!delay) delay = 1; /* Set the default repeat times (milliseconds) */ getkey_repeat(400, 40); /* The timer will be stopped when the timer driver is unloaded */ - timer_setup(tid, delay, 0, callback, NULL); - timer_start(tid); + int tid = timer_setup(TIMER_ANY, delay, callback); + if(tid >= 0) timer_start(tid); gint[HWKBD] = HW_LOADED | (isSH3() ? HWKBD_IO : HWKBD_KSI); gint[HWKBDSF] = KEYBOARD_SCAN_FREQUENCY; diff --git a/src/tmu/sleep.c b/src/tmu/sleep.c index fb30f19..6b8effc 100644 --- a/src/tmu/sleep.c +++ b/src/tmu/sleep.c @@ -5,15 +5,14 @@ #include #include -/* sleep_us() - sleep for a definite duration in microseconds */ -void sleep_us(int tid, int us_delay) +/* sleep_us(): Sleep for a fixed duration in microseconds */ +void sleep_us(uint64_t delay_us) { volatile int flag = 0; - uint32_t delay = timer_delay(tid, us_delay); - int free = timer_setup(tid, delay, 0, timer_timeout, &flag); - if(free < 0) return; + int timer = timer_setup(TIMER_ANY, delay_us, timer_timeout, &flag); + if(timer < 0) return; - timer_start(tid); - while(!flag) sleep(); + timer_start(timer); + timer_wait(timer); } diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index 33c0376..e690fad 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -7,12 +7,11 @@ #include #include #include - -#include #include -#include -#include +#include + +#undef timer_setup //--- // Driver storage @@ -21,9 +20,9 @@ /* inth_data_t - data storage inside interrupt handlers */ typedef struct { - int (*cb)(volatile void *arg); /* User-provided callback */ - volatile void *arg; /* Argument for [callback] */ - volatile void *TCR; /* TCR address for TMU */ + void *function; /* User-provided callback */ + uint32_t arg; /* Argument for function */ + volatile void *TCR; /* TCR address for TMU */ } GPACKED(4) inth_data_t; @@ -37,21 +36,17 @@ GDATA static etmu_t *ETMU = SH7305_ETMU; GDATA static volatile uint8_t *TSTR = &SH7305_TMU.TSTR; //--- -// Timer API +// Local functions //--- -/* timer_setup() - set up a timer */ -int timer_setup(int id, uint32_t delay, timer_input_t clock, - int (*callback)(volatile void *arg), volatile void *arg) +/* configure(): Configure a fixed timer */ +static void configure(int id, uint32_t delay, int clock, void *f, uint32_t arg) { - /* Timer is not installed (TCR address is really required) */ - if(!timers[id]) return -1; - if(id < 3) { /* Refuse to setup timers that are already in use */ tmu_t *T = &TMU[id]; - if(T->TCR.UNIE || *TSTR & (1 << id)) return -1; + if(T->TCR.UNIE || *TSTR & (1 << id)) return; /* Configure the counter, clear interrupt flag*/ T->TCOR = delay; @@ -67,10 +62,10 @@ int timer_setup(int id, uint32_t delay, timer_input_t clock, else { etmu_t *T = &ETMU[id-3]; - if(T->TCR.UNIE) return -1; + if(T->TCR.UNIE) return; - /* No clock input and clock edge here. But TCR and TCNT need a - delay to retain the value */ + /* No clock input and clock edge here. But TCR and TCNT need + some time to execute the write */ do T->TCR.UNF = 0; while(T->TCR.UNF); @@ -83,25 +78,128 @@ int timer_setup(int id, uint32_t delay, timer_input_t clock, T->TCR.UNIE = 1; } - timers[id]->cb = callback; + timers[id]->function = f; timers[id]->arg = arg; - return id; +} + +/* matches(): Check if a timer matches the provided specification and delay */ +static int matches(int id, int spec, uint32_t delay) +{ + /* A specific idea only matches the exact timer */ + if(spec >= 0) return id == (spec & 0xf); + /* TIMER_ANY always matches ETMU only for delays at least 100 µs */ + if(spec == TIMER_ANY) return (id < 3 || delay >= 100); + /* TIMER_TMU and TIMER_ETMU match as you'd expect */ + if(spec == TIMER_TMU) return (id < 3); + if(spec == TIMER_ETMU) return (id >= 3); + /* Default is not matching */ + return 0; +} + +/* available(): Check if a timer is available (UNIE cleared, not running) */ +static int available(int id) +{ + /* The timer should also be installed... */ + if(!timers[id]) return 0; + + if(id < 3) + { + tmu_t *T = &TMU[id]; + return !T->TCR.UNIE && !(*TSTR & (1 << id)); + } + else + { + etmu_t *T = &ETMU[id-3]; + return !T->TCR.UNIE && !T->TSTR; + } +} + +/* stop_callback(): Empty callback that stops the timer */ +static int stop_callback(void) +{ + return TIMER_STOP; +} + +//--- +// Timer API +//--- + +/* timer_setup(): Reserve and configure a timer */ +int timer_setup(int spec, uint64_t delay, timer_callback_t function, ...) +{ + int clock = 0; + + /* Get the optional argument */ + va_list va; + va_start(va, function); + uint32_t arg = va_arg(va, uint32_t); + va_end(va); + + /* Default value for the callback */ + if(!function.v) function.v = stop_callback; + + /* Find a matching timer, starting from the slowest timers with the + smallest interrupt priorities all the way up to TMU0 */ + for(int id = timer_count() - 1; id >= 0; id--) + { + if(!matches(id, spec, delay) || !available(id)) continue; + + /* If ID is a TMU, choose a timer prescaler. Assuming the worst + running Pphi of ~48 MHz, select the finest resolution that + allows the requested delay to be represented. */ + if(id < 3 && spec >= 0) + { + /* Explicit timers with clock in the specification */ + clock = (spec >> 4) & 0xf; + } + else if(id < 3) + { + uint64_t sec = 1000000; + + /* Pphi/4 until 350 seconds */ + if(delay <= 350 * sec) clock = TIMER_Pphi_4; + /* Pphi/16 until 1430 seconds */ + else if(delay <= 1430 * sec) clock = TIMER_Pphi_16; + /* Pphi/64 until 5720 seconds */ + else if(delay <= 5720 * sec) clock = TIMER_Pphi_64; + /* Pphi/256 otherwise */ + else clock = TIMER_Pphi_256; + } + + /* Find the delay constant for that timer and clock */ + if(spec < 0) delay = timer_delay(id, delay, clock); + + configure(id, delay, clock, function.v, arg); + return id; + } + + return -1; } /* timer_delay() - compute a delay constant from a duration in seconds */ -uint32_t timer_delay(int id, uint64_t delay_us) +uint32_t timer_delay(int id, uint64_t delay_us, int clock) { - /* TODO: Proper timer_delay() */ - const clock_frequency_t *clock = clock_freq(); - uint64_t freq = clock->Pphi_f >> 2; + uint64_t freq; + + if(id < 3) + { + const clock_frequency_t *cpg = clock_freq(); + freq = cpg->Pphi_f; + if(clock == TIMER_Pphi_4) freq >>= 2; + if(clock == TIMER_Pphi_16) freq >>= 4; + if(clock == TIMER_Pphi_64) freq >>= 6; + if(clock == TIMER_Pphi_256) freq >>= 8; + } + else + { + /* ETMU all run on TCLK at 32768 Hz */ + freq = 32768; + } /* fxcg50: Calculated = 29491200 but it's too low */ /* TODO: Account for down spread spectrum in the CPG */ // uint64_t freq = 29020000 >> 2; - /* Extra timers all run at 32768 Hz */ - if(id >= 3) freq = 32768; - uint64_t product = freq * delay_us; return product / 1000000; } @@ -184,11 +282,11 @@ void timer_wait(int id) //--- /* timer_timeout() - callback that sets a flag and halts the timer */ -int timer_timeout(volatile void *arg) +int timer_timeout(void volatile *arg) { - volatile int *x = arg; - (*x)++; - return 1; + int volatile *x = arg; + if(x) (*x)++; + return TIMER_STOP; } //---