gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > GBA Timers - Tutorial

#114732 - Minco - Wed Jan 10, 2007 2:58 pm

I tried using the code from KrakKeN's GBA Timers tutorial, which can be found here: http://www.gbadev.org/docs.php?showinfo=43

When I try to compile a simple program using this delay function, I immediately get the following errors:

compiler_test.c: In function `DelayExecution':
compiler_test.c:29: warning: assignment makes pointer from integer without a cast
compiler_test.c:30: warning: assignment makes pointer from integer without a cast
compiler_test.c:32: warning: comparison between pointer and integer
compiler_test.c:33: warning: comparison between pointer and integer


In the code this will be the following lines:


line 29: REG_TMCNT[2] = (TIMEENABLE | TIME256);
line 30: REG_TMCNT[3] = (TIMEOVERFLOW | TIMEENABLE);
line 31: REG_TMD[2] = REG_TMD[3] = 0;
line 32: while (REG_TMD[3] < iSecs) { /*/ Do Nothing /*/ }
line 33: while (REG_TMD[2] < (65535 / 1000) * iMilliSecs) { /*/ Do Nothing /*/ }


I've just copy pasted the code from KrakKeN's GBA Timers Tutorial, but I immediately get this errors. When I try to set a delay, let's say 1000 milliseconds, like DelayExecution(1000);, the program immediately 'crashes' at that point.

How can I fix this errors and fix this timer problem? (By the way, does anyone know a good other timing tutorial?)

Thanks!

(p.s. if someone else also got some working functions for setting delays or setting a counter which will return a value after a given time interval or something like that, please let me know --> arjan.van.bremen@gmail.com)


Last edited by Minco on Wed Jan 10, 2007 7:21 pm; edited 1 time in total

#114736 - Ant6n - Wed Jan 10, 2007 3:27 pm

the 'errors' are actually 'warnings'. what do you mean the program 'crashes'?

#114740 - Minco - Wed Jan 10, 2007 4:05 pm

I mean that the program 'stalls', it doesn't go any further. Here you can find the sourcecode, I used a simple example which first sets the background colour to blue, then it should wait 1000ms, and change the background colour to white/yellow, the sourcecode is pretty straightforward (I also included a make batch file, just change the path to your devkitadvance bin).

SOURCECODE:
http://www.webjuice.eu/source.zip

#114765 - Miked0801 - Wed Jan 10, 2007 6:37 pm

REG_TMCNT[] appears to be mis-cast somehow if you are getting that warning. It means that it thinks the register is an array pointer and that you are feeding it integers which is bad.

Can you post how REG_TMCMT is declared in the headers?

#114768 - Minco - Wed Jan 10, 2007 7:19 pm

I'll just simply post the sourcecode which is given in the tutorial:

SOURCECODE timer.c

Code:
#include "timer.h"

void DelayExecution(int iTime) {
     // Initialise Two Integers To Hold Seconds And Milliseconds //
     int iMilliSecs, iSecs; 
 
    // Get Millisecond Delay //
    // "%" Gets The Remainder Of The Answer After Division //
    iMilliSecs = iTime % 1000; 

    // Get Second Delay //
    // Remove Any Remainder From "iTime" And Divide By "1000" To Get Value In Seconds //
    iSecs = (iTime - iMilliSecs) / 1000;
   
    // Enable Timer #2 And Set The Increment Frequency To 256 Cycles //
    REG_TMCNT[2] = (TIMEENABLE | TIME256);
   
    // Enable Timer #3 And Set It To Increment When Timer #2 Overflows //
    REG_TMCNT[3] = (TIMEOVERFLOW | TIMEENABLE);
   
    // Reset The Timers To 0 So Any Previous Use Of The Timers Are Cleared //
    REG_TMD[2] = REG_TMD[3] = 0;
   
    // Loop While Timer #3 Is Under "iSecs" //
    while (REG_TMD[3] < iSecs) { /*/ Do Nothing /*/ }
   
    // Divide 2^16 By 1000 Then Multiply By "iMilliSecs" //
    // To Get The Equivalent Amount Of Cycles In Milliseconds //
    // Do Nothing While Counter Is Under This Value //
    while (REG_TMD[2] < (65535 / 1000) * iMilliSecs) { /*/ Do Nothing /*/ }
   
    // Clear The Control Register For Timer #2 And Timer #3 - Disable It //
    REG_TMCNT[2] = REG_TMCNT[3] = 0;
}




SOURCECODE HEADER timer.h

Code:
// DEFINE "uShort" AS AN "unsigned short int" //
typedef unsigned short int uShort;

// DECLARE AN ARRAY POINTER TO THE FOUR TIMER CONTROL REGISTERS //
volatile uShort* REG_TMCNT[4] = {
  (uShort*)0x4000102,
  (uShort*)0x4000106,
  (uShort*)0x400010A,
  (uShort*)0x400010E
};

// DECLARE AN ARRAY POINTER TO THE FOUR TIMER COUNTER REGISTERS //
volatile uShort* REG_TMD[4] = {
  (uShort*)0x4000100,
  (uShort*)0x4000104,
  (uShort*)0x4000108,
  (uShort*)0x400010C
};

#define TIMESYSTEM      0x0
#define TIME64          0x1
#define TIME256         0x2
#define TIME1024        0x3

#define TIMEOVERFLOW    0x4
#define TIMEIRQ         0x40
#define TIMEENABLE      0x80

#114773 - Ant6n - Wed Jan 10, 2007 7:55 pm

you have a volatile array of pointers, when you write to the array, you change these pointers. when you write to them you have to dereference them, also, you have to declare the pointers themselves volatile.

uShort* REG_TMD[4] = {
(volatile uShort*)0x4000100,
(volatile uShort*)0x4000104,
(volatile uShort*)0x4000108,
(volatile uShort*)0x400010C
};

access with
REG_TMD[n]*

#114778 - tepples - Wed Jan 10, 2007 8:12 pm

Those definitions for the timer look confusing. Here are the ones from my own header:
Code:
typedef struct TIMER_REC
{
  unsigned short count;
  unsigned short control;
} TIMER_REC;

#define TIMER ((volatile TIMER_REC *)0x04000100)

Then you do TIMER[0].count, TIMER[3].control, etc. But if you're dealing with intervals longer than 50 milliseconds, it might be better to count vblanks.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#114785 - Minco - Wed Jan 10, 2007 8:28 pm

Ant6n wrote:
you have a volatile array of pointers, when you write to the array, you change these pointers. when you write to them you have to dereference them, also, you have to declare the pointers themselves volatile.

uShort* REG_TMD[4] = {
(volatile uShort*)0x4000100,
(volatile uShort*)0x4000104,
(volatile uShort*)0x4000108,
(volatile uShort*)0x400010C
};

access with
REG_TMD[n]*


Thanks, that does the trick! Only need to access them with *REG_TMD[n] ofcourse ;-). I'll use this code for some short delays. Someone got a tutorial or tips for using vblanks, frame rate checkings etc.? Thanks again!

#114790 - Cearn - Wed Jan 10, 2007 8:37 pm

Ant6n wrote:
REG_TMD[n]*

That would be *REG_TMD[n] :P. Tepples' way is nicer though.

For more on timers, tonc: timers. But as tepples said, counting vblanks is the usual way for delays. Note that this
Code:
while(ScanlineCounter<160) {}

won't work as a frame-counter because in the next iteration of the main loop you'll probably still be in the VBlank period. For more, see here.