raphnet / gc_n64_usb-v3

Gamecube/N64 to USB adapter firmware (3rd generation)
http://www.raphnet.net/electronique/gcn64_usb_adapter_gen3/index_en.php
GNU General Public License v3.0
42 stars 10 forks source link

[CODE HELP] resetFirmware() question #13

Closed Dignity1337 closed 4 months ago

Dignity1337 commented 9 months ago

Hi. I'm currently playing around with the code (I'm using an ATmega32u4) and ran into an issue. Since there is no forum or other way I thought that I might get some help here. I should mention that I have no prior experience with AVR and only few experience with C. What I'm trying to do is to use PD4 as an input pin to switch between single mode and dual mode (high = dual, low = single).

So what I did first was to modify main.c line 533: DDRD = 0x60; Setting it to 0x60 instead of 0x70 just enables PD4 as an input while keeping the other pins the same they were before.

Then inside the int main function during the while(1) loop I inserted after line 747:

if(!(PIND & 0x10) && g_eeprom_data.cfg.mode == 16)      // if PD4 is low and we are in dual mode change to single mode
{
    g_eeprom_data.cfg.mode = 0;
} else if((PIND & 0x10) && g_eeprom_data.cfg.mode == 0))    // if PD4 is high and we are in single mode change to dual mode
{
    g_eeprom_data.cfg.mode = 16;
}

Ok that seems to work. If I unplug the usb cable and then replug it again it typically changes the mode if I pull PD4 low or high (sometimes two replugs are needed). So I just tried to reset the adapter from code like this:

if(!(PIND & 0x10) && g_eeprom_data.cfg.mode == 16)      // if PD4 is low and we are in dual mode change to single mode
{
    g_eeprom_data.cfg.mode = 0;
    resetFirmware();
} else if((PIND & 0x10) && g_eeprom_data.cfg.mode == 0) // if PD4 is high and we are in single mode change to dual mode
{
    g_eeprom_data.cfg.mode = 16;
    resetFirmware();
}

And now the adapter just doesn't get recognized if the code is called. I guess it just ends in an endless loop or something. So my next idea was to just give it some time before resetting by using a while loop that runs for a short time. So I defined an interger variable like this: uint32_t shouldReset = 0; And the code looks like this:

if(!(PIND & 0x10) && g_eeprom_data.cfg.mode == 16)      // if PD4 is low and we are in dual mode change to single mode
{
    g_eeprom_data.cfg.mode = 0;
    while(shouldReset < 20000)
    {
        shouldReset++;
    }
    shouldReset = 0;
    resetFirmware();
} else if((PIND & 0x10) && g_eeprom_data.cfg.mode == 0) // if PD4 is high and we are in single mode change to dual mode
{
    g_eeprom_data.cfg.mode = 16;
    while(shouldReset < 20000)
    {
        shouldReset++;
    }
    shouldReset = 0;
    resetFirmware();
}

Now everything gets out of control. Sometimes it works after some reset attemts, sometimes it just hangs up and sometimes nothing happens. I then tried to get it into the cmdbuf inside of hiddata.c. Sadly I was unable to get any success that way. However since it works really well with the command line (gcn64ctl -f --set_mode 16 --reset) I would assume that the problem I have is related to interrupts. I'm really new to AVR but I guess that these USB commands trigger some sort of interrupt. That's where I'm currently at. If anyone could tell me how to properly trigger the resetFirmware() function I would highly appreciate it.

Thanks and have a nice day

raphnet commented 4 months ago

Hello,

Calling resetFirmware() is correct. It will cause a re-enumeration as a side effect of resetting.

Based on what you describe, I think the adapter is simply resetting in a loop.

g_eeprom_data.cfg.mode is an ordinary global variable (See eeprom.c for more info) so unless you are calling eeprom_commit() somewhere, just doing g_eeprom_data.cfg.mode = 0; is not permanent and will not survive a reset. So the firmware probably starts, detects that it's not in the correct mode, then resets, ... and this repeats endlessly.

If you knew this already, and simply did not include the eeprom_commit() call in the code excerpt above for brevity, then I'm sorry but I have no idea.

One thing that comes to mind however, since you mention that sometimes it works (i.e. unpredictable behavior), is how your switch is wired to the input pin. Is it a SPST switch to ground? In that case, did you enable the internal pull-up? If the pin is sometimes floating (not connected), the result of PIND & 0x10 will be unpredictable. (Sorry if this is obvious - I don't know how much electronics experience you have...)

Dignity1337 commented 4 months ago

Thank you so much. Indeed I forgot to call the eeprom_commit() function. It now works flawlessly. Thanks for your help.