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.

DS development > Timers, keysDownRepeat and keysDown questions [solved]

#163293 - dsbeginner - Sun Sep 28, 2008 10:54 pm

Hi everyone,

I have my DSL since a while and only recently found out about homebrew. I was intrigued right away and finally got around to fiddle with it myself.

Now that I've played with some code and tested it in iDeaS, there are a few things that aren't quite working as I expected them to. Ok, so my C++ skills are rather limited and rusty at best, and I obviously have to get my head around libnds first. I searched the forum, finding a lot of cool and smart stuff, but none that helped me with my problems. Maybe someone could point out to me what I'm doing wrong?

  1. How do you set up a timer, using just libnds functions? I thought it should work with
Code:
irqSet(IRQ_TIMER0, on_irq);
but that doesn't seem to do anything for me. However
Code:
IRQ_HANDLER = on_irq;
does the job perfectly. So while my timer does work as expected, writing to the memory directly feels odd.

  • Code:
    keysDown() & KEY_L
    doesn't trigger in my code. Ever. No matter how I press the L button. It only seems to work with keysHeld().
    Code:
    keysDown() & KEY_A
    in the same place works as expected. So it's not about the code around it, just the key, it seems.

  • Code:
    keysDownRepeat() & KEY_UP
    works fine. But I can't get any of the other D-pad diretions to work with keysDownRepeat(), only with keysDown().

    Thanks for reading, I hope someone can help me out.



    Last edited by dsbeginner on Mon Sep 29, 2008 6:22 pm; edited 1 time in total

    #163294 - mml - Sun Sep 28, 2008 11:13 pm

    Assuming you're doing all your input processing once per frame, then you need to also call scanKeys() exactly once per frame in order to use keysDown()/keysUp()/keysHeld().

    keysDown, keysUp and keysHeld will reset next time scanKeys is called, so you should not call scanKeys again until you have processed all the input you cared about, because otherwise you will miss keysDown and keysUp events. keysHeld feels more persistant since it will continue returning the same data over and over again as long as the same keys are held.

    I haven't touched timers at all, but the usual way of setting up an interrupt handler requires irqInit(), irqSet() and irqEnable(). Have a look at those functions and it should get you going.

    #163295 - dsbeginner - Sun Sep 28, 2008 11:46 pm

    Hey, thanks for your response (and so quick).

    mml wrote:
    Assuming you're doing all your input processing once per frame, then you need to also call scanKeys() exactly once per frame in order to use keysDown()/keysUp()/keysHeld().

    keysDown, keysUp and keysHeld will reset next time scanKeys is called, so you should not call scanKeys again until you have processed all the input you cared about, because otherwise you will miss keysDown and keysUp events. keysHeld feels more persistant since it will continue returning the same data over and over again as long as the same keys are held.


    That's what I'm doing... I hope. Simplified:
    Code:

    keysSetRepeat(60, 60);
    while(1) {
       scanKeys();
       if (keysDownRepeat() & KEY_UP) { ... }
       if (keysDownRepeat() & KEY_DOWN) { ... }
       if (keysDown() & KEY_A) { ... }
       if (keysDown() & KEY_L){ ... }
       swiWaitForVBlank();
    }

    KEY_UP works fine, KEY_DOWN not. KEY_A works, KEY_L not. Beats me :(

    mml wrote:
    I haven't touched timers at all, but the usual way of setting up an interrupt handler requires irqInit(), irqSet() and irqEnable(). Have a look at those functions and it should get you going.

    I thought it should work something like this:
    Code:

       irqInit();
       TIMER0_DATA = TIMER_FREQ_1024(1);
       TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1024 | TIMER_IRQ_REQ;
       irqSet(IRQ_TIMER0, on_irq);
       irqEnable(IRQ_TIMER0);

    But as said above, irqSet doesn't seem to do the right thing. If instead I use
    Code:
    IRQ_HANDLER = on_irq;
    then it works and the function on_irq is triggered with every timer tick. Not sure what I'm missing.

    #163296 - elhobbs - Mon Sep 29, 2008 12:37 am

    keysDown() returns a bitmask for all of the keys that have just been pressed down - kind of like a keydown event it does not indicate when a button is held - that is what keysHeld() is for. so, save the result of keyDown() value to a variable and test that for each key.

    #163297 - dsbeginner - Mon Sep 29, 2008 1:08 am

    elhobbs wrote:
    so, save the result of keyDown() value to a variable and test that for each key.


    If I understood it correctly, then keyDown() returns the same result until scanKeys() is called again. So besides maybe a slight performance increase, I don't see how storing the result to a local variable before making the bitmask checks could affect my problem (and from what I tried, it doesn't).

    But thanks for trying to help. Any other ideas?

    #163299 - gauauu - Mon Sep 29, 2008 3:07 am

    What are you doing with the value you get from this?

    Code:

    keysDown() & KEY_L


    I ask because I had the same problem. My problem was based on using a bool type. Libnds defines a bool as an enum with two values, false and true. Which means if you set a bool value to 8, which normally in C evaluates to a true value, it can evaluate as false. (see this thread).

    Which means that if you do:

    Code:

    bool isLHeld()
    {
       return keysDown() & KEY_L;
    }


    It won't be correct. You'd have to do something like:
    Code:

    bool isLHeld()
    {
       return keysDown() & KEY_L ? true : false;
    }


    In my case, up, down, left, right, A and B worked. L, R, Y, and B failed. Which sounds a lot like your issue....

    #163304 - mml - Mon Sep 29, 2008 10:08 am

    Does something like this make any difference?

    Code:
       if (keysDownRepeat() & KEY_UP == KEY_UP) { ... }
       if (keysDownRepeat() & KEY_DOWN == KEY_DOWN) { ... }
       if (keysDown() & KEY_A == KEY_A) { ... }
       if (keysDown() & KEY_L == KEY_L){ ... }


    Also, are you in C or C++?

    #163310 - dsbeginner - Mon Sep 29, 2008 1:19 pm

    gauauu wrote:
    You'd have to do something like:
    Code:

    bool isLHeld()
    {
       return keysDown() & KEY_L ? true : false;
    }


    In my case, up, down, left, right, A and B worked. L, R, Y, and B failed. Which sounds a lot like your issue....


    That did it. In fact I was storing the result of the check to a 'bool' variable first. Thanks a lot. But does the solution feel cumbersome only to me?

    mml wrote:
    Does something like this make any difference?

    Code:
       if (keysDownRepeat() & KEY_UP == KEY_UP) { ... }
       if (keysDownRepeat() & KEY_DOWN == KEY_DOWN) { ... }
       if (keysDown() & KEY_A == KEY_A) { ... }
       if (keysDown() & KEY_L == KEY_L){ ... }

    After changing it to this, it stopped working altogether. That puzzled me until I realized that == is stronger than & so I changed it to
    Code:
       if ((keysDownRepeat() & KEY_DOWN) == KEY_DOWN) { ... }
    but that didn't make a difference to the original version. So no improvement here it seems. keysDownRepeat still only works with KEY_UP.

    mml wrote:
    Also, are you in C or C++?


    I thought I'm in C++, but after playing with some #ifdef's, it turns out I'm in C (and have no idea how to change that, or what the implications are).

    #163317 - gauauu - Mon Sep 29, 2008 3:08 pm

    dsbeginner wrote:

    That did it. In fact I was storing the result of the check to a 'bool' variable first. Thanks a lot. But does the solution feel cumbersome only to me?


    Well, you should also be able to do:
    Code:

    bool isLHeld()
    {
       return ((keysDown() & KEY_L) > 0);
    }

    Which is slightly less cumbersome.

    But yes, I agree. I've found the enum'd bool to be rather a pain (as opposed to just typedef'ing bool as an int, and #define true and false). It seems to me that the disadvantages of it tend to outweigh the advantages. But the libnds authors are better programmers than I, so I'm guessing there's some good reason I'm not aware of as to why it is preferable to do it as an enum.

    #163320 - elhobbs - Mon Sep 29, 2008 3:49 pm

    gauauu wrote:
    But yes, I agree. I've found the enum'd bool to be rather a pain (as opposed to just typedef'ing bool as an int, and #define true and false). It seems to me that the disadvantages of it tend to outweigh the advantages. But the libnds authors are better programmers than I, so I'm guessing there's some good reason I'm not aware of as to why it is preferable to do it as an enum.
    a couple of reasons would be: it allows the compiler to typecheck better and you can use autocomplete in some IDEs on enums. also, #defines can be hard to debug so it may be more of a habit to use enums - just conjecture on my part on the last bit though.

    #163323 - dsbeginner - Mon Sep 29, 2008 4:50 pm

    I just found a solution to my keysDownRepeat problem:

    First I had this code, which didn't work:
    Code:

       if (keysDownRepeat() & KEY_RIGHT) { ... }
       if (keysDownRepeat() & KEY_LEFT)  { ... }
       if (keysDownRepeat() & KEY_UP)    { ... }
       if (keysDownRepeat() & KEY_DOWN)  { ... }

    It works now like this:
    Code:

       uint32 repeated_keys = keysDownRepeat();
       if (repeated_keys & KEY_RIGHT) { ... }
       if (repeated_keys & KEY_LEFT)  { ... }
       if (repeated_keys & KEY_UP)    { ... }
       if (repeated_keys & KEY_DOWN)  { ... }


    So apparently, different from the other keys-functions, keysDownRepeat returns different results when called more than once between two scanKeys. I wonder if that's intended.

    Now my last issue (for now, heh) is that with the timer.

    #163327 - elhobbs - Mon Sep 29, 2008 5:40 pm

    dsbeginner wrote:
    Now my last issue (for now, heh) is that with the timer.
    here is the code I use to setup the timer for dswifi - it causes the Wifi_timer_50ms function to be called at approximately 50ms intervals
    Code:
    TIMER3_CR = 0;//disable timer 3
    irqSet(IRQ_TIMER3, Wifi_timer_50ms); // setup timer IRQ
    irqEnable(IRQ_TIMER3);
    TIMER3_CR=TIMER_ENABLE|TIMER_DIV_256|TIMER_IRQ_REQ;
    TIMER3_DATA=TIMER_FREQ_256(20);

    #163328 - Lazy1 - Mon Sep 29, 2008 5:53 pm

    My code for a timer that counts milliseconds, actually it uses 2 timers but I'm sure someone has a more efficient one.

    Code:

    static volatile u32 TimerBaseMS = 0;

    static void IRQTimer1( void ) {
       TimerBaseMS+= 65536;
    }

    // get time in milliseconds
    int Timer_GetMS( void ) {
       return TimerBaseMS + TIMER1_DATA;
    }

    void Timer_Init( void ) {
       irqSet( IRQ_TIMER1, IRQTimer1 );
       irqEnable( IRQ_TIMER1 );
       
       TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1;
       TIMER0_DATA = 32768;
       
       TIMER1_CR = TIMER_ENABLE | TIMER_CASCADE | TIMER_IRQ_REQ;
       TIMER1_DATA = 0;
    }

    #163329 - dsbeginner - Mon Sep 29, 2008 6:22 pm

    Okay, apparently my problem wasn't the call of irqSet(), but the irq handling function itself. I took it from http://www.double.co.nz/nintendo_ds/nds_develop8.html without really understanding what e.g. VBLANK_INTR_WAIT_FLAGS is for.

    Removing the if's and register assignments did the trick.

    Thanks all!

    #163333 - Cydrak - Mon Sep 29, 2008 7:23 pm

    dsbeginner wrote:
    Okay, apparently my problem wasn't the call of irqSet(), but the irq handling function itself. I took it from http://www.double.co.nz/nintendo_ds/nds_develop8.html without really understanding what e.g. VBLANK_INTR_WAIT_FLAGS is for.

    Erk! Well, yes, that is a problem... it looks to me like some of that page is outright wrong--most notably the explanation/code for REG_IF and VBLANK_INTR_WAIT_FLAGS. :|

    Anyhow, if you use irqInit()/irqSet()/etc, libnds sets its own IRQ_HANDLER to call your functions. It also does a fair amount of stuff behind the scenes so you shouldn't have to touch anything IRQ-related (including the flags) unless you know what you're doing.

    #163540 - a128 - Sat Oct 04, 2008 4:35 pm

    elhobbs wrote:
    here is the code I use to setup the timer for dswifi - it causes the Wifi_timer_50ms function to be called at approximately 50ms intervals
    Code:
    TIMER3_CR = 0;//disable timer 3
    irqSet(IRQ_TIMER3, Wifi_timer_50ms); // setup timer IRQ
    irqEnable(IRQ_TIMER3);
    TIMER3_CR=TIMER_ENABLE|TIMER_DIV_256|TIMER_IRQ_REQ;
    TIMER3_DATA=TIMER_FREQ_256(20);


    I guess you are right (as I tested your code) but I do not get it why it's every 50ms

    OK.

    1000ms / 50 ms = 20

    but why you do we have to use the prescaler
    Code:
    TIMER_DIV_256?!


    I thought 1000ms/50ms = 20 has to be with
    Code:
    TIMER_DIV_1?!


    i.e. a 1ms timer is setup with
    Code:
    TIMER_FREQ(1000)


    so a 50ms timer has to be
    Code:
    TIMER_FREQ(1000/50)  ?!

    #163542 - gauauu - Sat Oct 04, 2008 6:01 pm

    elhobbs wrote:
    it allows the compiler to typecheck better


    Except unfortunately, in the issue that me and dsbeginner and I were having, GCC doesn't complain, which makes the issue harder to debug.

    #163548 - eKid - Sat Oct 04, 2008 7:09 pm

    a128 wrote:


    but why you do we have to use the prescaler

    The macro does (2^25) / x, (2^25)/20 is bigger than 16 bits (1677721), so you need a /256 prescaler to divide the counter by 256 so it fits within 16 bits (6553)

    Quote:

    i.e. a 1ms timer is setup with
    Code:
    TIMER_FREQ(1000)



    This fits in 16 bits so no prescaler needed ((2^25)/1000 = ~33554)

    #163563 - elhobbs - Sun Oct 05, 2008 1:49 am

    gauauu wrote:
    elhobbs wrote:
    it allows the compiler to typecheck better


    Except unfortunately, in the issue that me and dsbeginner and I were having, GCC doesn't complain, which makes the issue harder to debug.
    in C it doesn't help. In C++ it does.

    #163571 - a128 - Sun Oct 05, 2008 8:18 am

    eKid wrote:

    This fits in 16 bits so no prescaler needed ((2^25)/1000 = ~33554)


    Great Thanks.