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 > touchReadTemperature() makes touchReadXY() hang [fixt]

#132980 - HyperHacker - Mon Jul 02, 2007 10:49 am

I'm noticing this code that used to work with older versions of DKP/libNDS no longer works anymore. If touchReadTemperature() has been called then touchReadXY() will never return and will sometimes turn the sound off. (IPC2->TEST stays at 7.) I tried disabling RTC stuff but the only way I found to make it not hang was to not call touchReadTemperature(). ARM9 is still running.

Here's the ARM7 code, yeah it kinda sucks, it's fairly old and needs cleaning/updating. The relevant parts are in Interrupt(). (Feel free to point out any other bugs too. :-p)
Code:
#include "main.h"

/*
Entry Point
CPU: ARM7
Inputs:
   -argc: Number of arguments
   -argv: Pointer to arguments
Returns: Program return code
*/
int main(int argc, char** argv)
{
   s32 i;

   //Message handling
   u32 RawData;
   A7_MESSAGE Message;
   u8 NumParams;
   u32 Param[4];

   REG_IME = 0;

   //Blank out IPC
   IPC2->Time_Month = 12;
   MEMZERO((u8*)IPC, sizeof(TransferRegion));
   MEMZERO((u8*)IPC2, sizeof(CustomIPCData));


   //Init interrupts
   IRQ_HANDLER = Interrupt; //Set handler callback
   REG_IE = IRQ_VBLANK | IRQ_TIMER3 | IRQ_WIFI;
   REG_IF = ~0;
   REG_DISPSTAT = DISP_VBLANK_IRQ;
   REG_IME = 1; //Enable interrupts


   //Init IPC
   REG_IPC_SYNC = IPC_SYNC_IRQ_ENABLE; //Enable IRQs from ARM9
   REG_IPC_FIFO_CR = IPC_FIFO_SEND_IRQ | IPC_FIFO_ENABLE; //Enable FIFO and interrupt on send FIFO empty/receive FIFO not empty

   //Init clock
   //Copied from a GBADev post. Todo: clean this up.
   REG_RCNT = 0x8100;

   //irqSet(IRQ_NETWORK, testSync);

   // Reset the clock if needed
   rtcReset();

   rtcGetTimeAndDate((uint8 *)&(IPC2->Time_Year));

   uint8 command[4];

   command[0] = READ_STATUS_REG2;
   rtcTransaction(command, 1, &command[1], 1);

   command[0] = WRITE_STATUS_REG2;
   command[1] = 0x41;

   rtcTransaction(command, 2, 0, 0);

   command[0] = WRITE_INT_REG1;
   command[1] = 0x01;
   rtcTransaction(command, 2, 0, 0);

   command[0] = WRITE_INT_REG2;
   command[1] = 0x00;
   command[2] = 0x21;
   command[3] = 0x35;

   rtcTransaction(command, 4, 0, 0);

   SOUND_CR = SOUND_VOL(127) | SOUND_ENABLE;
   swiWaitForVBlank();
   swiWaitForVBlank();
   swiWaitForVBlank();

   if(((*(uint8*)0x027FFCE4) & 8) != 0)
      IPC2->SysInfo |= SI_GBAONBOTTOM;



   //See if this is a DS Lite
   if(readPowerManagement(4) & PM_NDSLITE_ISLITE) IPC2->SysInfo |= SI_DSLITE;


   //Turn off wifi in case a loader left it on
   Wifi_Deinit();


   //Init timers
   TIMER3_DATA = 65535 - 34318; //should be one interrupt every ~1ms
   TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ;

   SendA9Message(A9_INIT, 0, 0, 0, 0, 0);

   REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz | SPI_CONTINUOUS;
   REG_SPIDATA = 0; //Offset: 0=power management, 1=battery status, 2=amplifier, 3=microphone
   SerialWaitBusy();
   REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz;
   REG_SPIDATA = PM_BACKLIGHT_TOP | PM_BACKLIGHT_BOTTOM | PM_SOUND_PWR;

   //SCHANNEL_TIMER(8) = SOUND_FREQ(2600*8);
   //SCHANNEL_CR(8) = SOUND_VOL(32) | SOUND_PAN(64) | SCHANNEL_WAVEDUTY(3) | SOUND_FORMAT_PSG | SCHANNEL_ENABLE;

   //u16 n = 0;
   while(true)
   {
      //Check for messages
      while(!(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY))
      {
         RawData = REG_IPC_FIFO_RX;
         Message = (A7_MESSAGE)(RawData >> 24);
         NumParams = (RawData >> 16) & 0xFF;
         if(NumParams > 4) NumParams = 4;

         for(i=0; i<NumParams; i++)
         {
            if(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY) break;
            Param[i] = REG_IPC_FIFO_RX;
         }

         DoA7Message(Message, i, Param[0], Param[1], Param[2], Param[3], true);
         IPC2->A7MessageCount++;
      }
      swiWaitForVBlank();
   }
   return 0;
}


/*
Processes messages from ARM9. This is a separate function so that ARM7 routines
can also perform these tasks without being signalled.
CPU: ARM7
Inputs:
   -Message: Message ID.
   -NumParams: Number of valid parameters.
   -Param1..Param4: Message parameters, if any.
   -FromA9: True if message sent from ARM9, false if called from an ARM7
    routine. Used to determine whether to send messages in response.
TODO: Disable interrupts while using the SPI bus.
*/
void DoA7Message(A7_MESSAGE Message, u8 NumParams, u32 Param1, u32 Param2, u32 Param3, u32 Param4, bool FromA9)
{
   u32 PowerStatus = 0;

   switch(Message)
   {
      case A7_NULL: //Dummy message, no parameters.
      break;


      case A7_INIT: //Used to pass some data from ARM9 at startup. Param1=Pointer to ARM7StackDump.
      ARM7StackDump = (u32*)Param1;
      break;


      case A7_RETURN: //Return value from a message; param1=message being responded to, others=return value.
      break;


      case A7_CHECK: //Used to check that ARM7 is still responding. Return value=anything; just returns a value to let ARM9 know it's still going.
      if(FromA9) SendA9Message(A9_RETURN, 2, A7_CHECK, 0xD06FECE5, 0, 0);
      break;


      case A7_GBAMODE: //Reboot into GBA mode, no parameters.
      /*
      //TEST OF ERROR HANDLER
      __asm (
         ".arm\n"
         "ldr r0, =ASMHackery\n"
         "str r13, [r0]\n"
         :
         :
         : "r0"
      );
      if(ARM7StackDump)
      {
         memcpy(ARM7StackDump, (u32*)(ASMHackery - A7_STACK_DUMP_NUMWORDS), A7_STACK_DUMP_NUMWORDS * 4);
         SendA9Message(A9_FATALERROR, 2, 0xD06FECE5, ASMHackery, 0, 0);
      }
      else SendA9Message(A9_FATALERROR, 2, 0xAAAAAAAA, ASMHackery, 0, 0);
      REG_IME = 0;
      while(true) swiWaitForVBlank();
      */

      __asm (".arm\n"
            "mov   r2, #0x40\n"
            "swi   0x1F0000\n"
      );
      break;


      case A7_SETPOWER: //Change power settings; param1=0 to write, 1 to AND, 2 to OR, 3 to XOR; param2=bitflags. Note that System Power bit is reversed; 1=turn off.
      SerialWaitBusy(); //Get current status
      REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz | SPI_CONTINUOUS;
      REG_SPIDATA = 0x80; //Set high bit for reading
      SerialWaitBusy();
      REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz;
      REG_SPIDATA = 0;
      SerialWaitBusy();
      PowerStatus = REG_SPIDATA & 0xFF;

      REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz | SPI_CONTINUOUS;
      REG_SPIDATA = 0; //Offset: 0=power management, 1=battery status, 2=amplifier, 3=microphone
      SerialWaitBusy();
      REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz;

      if(Param1 == 0)
         REG_SPIDATA = Param2;
      else if(Param1 == 1)
         REG_SPIDATA = PowerStatus & Param2;
      else if(Param1 == 2)
         REG_SPIDATA = PowerStatus | Param2;
      else if(Param1 == 3)
         REG_SPIDATA = PowerStatus ^ Param2;
      break;


      case A7_GETPOWER: //Retrieve power settings; no parameters, returns power bitflags.
      SerialWaitBusy();
      REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz | SPI_CONTINUOUS;
      REG_SPIDATA = 0x80;
      SerialWaitBusy();
      REG_SPICNT = SPI_ENABLE | SPI_DEVICE_POWER | SPI_BAUD_1MHz;
      REG_SPIDATA = 0;
      SerialWaitBusy();
      if(FromA9) SendA9Message(A9_RETURN, 2, A7_GETPOWER, REG_SPIDATA & 0xFF, 0, 0);
      break;


      case A7_SETVOLUME: //Set sound volume; param1=bits 0-6 set master sound volume, 7=1 to enable sound, 0 to disable.
      SOUND_CR |= SOUND_VOL(Param1 & 0x7F) | ((Param1 & 0x80) ? SOUND_ENABLE : 0);
      break;


      case A7_GETVOLUME: //Retrieve sound volume; return bits 0-6=master volume, 7=1 if enabled, 0 if disabled.
      if(FromA9) SendA9Message(A9_RETURN, 2, A7_GETVOLUME, (SOUND_CR & SOUND_VOL(127)) | ((SOUND_CR & SOUND_ENABLE) ? 0x80 : 0), 0, 0);
      break;


      case A7_SETSOUND: //Set flags for a sound channel; param1=channel (low 4 bits), param2=flags, param3=source, param4=length.
      if(NumParams >= 3)
         SCHANNEL_SOURCE(Param1 & 0xF) = Param3;
      if(NumParams >= 4)
         SCHANNEL_LENGTH(Param1 & 0xF) = Param4;

      //Flag format: //evvvvvvv pppppppw wwffffff ffffffft
      SCHANNEL_TIMER(Param1 & 0xF) = ((Param2 >> 1) & 0x1FFF) << 3;
      SCHANNEL_CR(Param1 & 0xF) = ((Param2 & 0x80000000) ? SCHANNEL_ENABLE : 0)
      | SOUND_VOL((Param2 >> 24) & 0x7F)
      | SOUND_PAN((Param2 >> 17) & 0x7F)
      | SCHANNEL_WAVEDUTY((Param2 >> 14) & 7)
      | ((Param2 & 1) ? SOUND_FORMAT_PSG : 0);
      break;


      case A7_GETSOUNDFLAGS: //Get flags for a sound channel; param1=channel (low 4 bits), return=flags.
      if(FromA9) SendA9Message(A9_RETURN, 2, A7_GETSOUNDFLAGS,
         ((SCHANNEL_CR(Param1 & 0xF) & SOUND_FORMAT_PSG) ? 1 : 0)
         | (SCHANNEL_TIMER(Param1 & 0xF) >> 2)
         | ((SCHANNEL_CR(Param1 & 0xF) & SCHANNEL_WAVEDUTY(7)) << 14)
         | ((SCHANNEL_CR(Param1 & 0xF) & SOUND_PAN(0x7F)) << 17)
         | ((SCHANNEL_CR(Param1 & 0xF) & SOUND_VOL(0x7F)) << 24)
         | ((SCHANNEL_CR(Param1 & 0xF) & SCHANNEL_ENABLE) ? 0x80000000 : 0),
         SCHANNEL_SOURCE(Param1 & 0xF),
         SCHANNEL_LENGTH(Param1 & 0xF));
      break;


      case A7_BOOTNDS: //Enter wait loop to boot .nds file on GBAMP.
      REG_IME = IME_DISABLE;   // Disable interrupts
      *((vu32*)0x027FFE34) = (u32)0x08000000;   // Bootloader start address
      swiSoftReset();   // Jump to boot loader
      break;


      case A7_WIFICTRL: //Wifi control messages
      if(Param1 == WIFIPARAM_SYNC) Wifi_Sync();
      else if(Param1 == WIFIPARAM_INIT) A7InitWifi(Param2);
      else if(Param1 == WIFIPARAM_STOP)
      {
         //todo: stop the LED blinking; this function doesn't seem to do it.
         Wifi_Deinit();
         IPC2->WifiEnabled = false;
      }
      break;


      case A7_BACKLIGHT: //Param1=Top light, Param2=Bottom light (-1=no change, 0=off, 1=on), Param3=Brightness (0-3 or -1 for no change, ignored if not DSLite)
      if(Param1 == 0xFFFFFFFF); //do nothing
      else if(Param1 == 0) //turn off
         DoA7Message(A7_SETPOWER, 2, 1, ~PM_BACKLIGHT_TOP, 0, 0, false);
      else if(Param1 == 1) //turn on
         DoA7Message(A7_SETPOWER, 2, 2, PM_BACKLIGHT_TOP, 0, 0, false);
      else if(FromA9) //invalid parameter
         SendA9Message(A9_ERROR, 1, 1, 0, 0, 0);

      if(Param2 == 0xFFFFFFFF); //do nothing
      else if(Param2 == 0) //turn off
         DoA7Message(A7_SETPOWER, 2, 1, ~PM_BACKLIGHT_BOTTOM, 0, 0, false);
      else if(Param2 == 1) //turn on
         DoA7Message(A7_SETPOWER, 2, 2, PM_BACKLIGHT_BOTTOM, 0, 0, false);
      else if(FromA9) //invalid parameter
         SendA9Message(A9_ERROR, 1, 2, 0, 0, 0);

      if(Param3 == 0xFFFFFFFF); //do nothing
      else if(Param3 < 4) //set brightness
      {
         if(IPC2->SysInfo & SI_DSLITE) writePowerManagement(4, (readPowerManagement(4) & ~3) | Param3); //Set brightness - bits 0-1 of reg 4
      }
      else if(FromA9) //invalid parameter
         SendA9Message(A9_ERROR, 1, 3, 0, 0, 0);

      break;


      default: //Invalid message
      if(FromA9) SendA9Message(A9_ERROR, 0, 0, 0, 0, 0);
      break;
   }
}


/*
Sends a message to ARM9.
CPU: ARM7
Inputs:
   -Message: Message to send.
   -NumParams: Number of valid parameters.
   -Param1..Param4: Message parameters.
Notes:
   -If send FIFO is full, it will wait until space is available.
    Todo: Implement some sort of check that the ARM9 is still responding,
    so ARM7 doesn't get caught in an infinite loop as well and can at least
    report the problem.
*/
void SendA9Message(A9_MESSAGE Message, u8 NumParams, u32 Param1, u32 Param2, u32 Param3, u32 Param4)
{
   while(REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL) swiDelay(10);
   REG_IPC_FIFO_TX = (Message << 24) | (NumParams << 16);

   if(NumParams < 1) return;
   while(REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL) swiDelay(10);
   REG_IPC_FIFO_TX = Param1;

   if(NumParams < 2) return;
   while(REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL) swiDelay(10);
   REG_IPC_FIFO_TX = Param2;

   if(NumParams < 3) return;
   while(REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL) swiDelay(10);
   REG_IPC_FIFO_TX = Param3;

   if(NumParams < 4) return;
   while(REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL) swiDelay(10);
   REG_IPC_FIFO_TX = Param4;
}


/*
Interrupt handler
CPU: ARM7
*/
void Interrupt()
{
   CustomIPCData Data;
   s32 t1=0, t2=0;
   static s16 SecCountdown = 1000; //Counts down to zero every second to signal re-read of RTC
   touchPosition tempPos;


   if(REG_IF & IRQ_VBLANK)
   {
      if(need_reboot()) reboot();
      if(IPC2->WifiEnabled) Wifi_Update();
      memcpy((u8*)&Data, (u8*)IPC2, sizeof(Data));

      //Read the buttons and touch screen
      uint32 B = ((~REG_KEYXY) << 10) & (KEY_X | KEY_Y);
      B |= (~REG_KEYINPUT) & 0x3FF;
      if(REG_KEYXY & 0x80) B |= KEY_LID;
      if(!(REG_KEYXY & 0x40)) //touched
      {
         B |= KEY_TOUCH;
         IPC2->TEST = 7;
         tempPos = touchReadXY();
         IPC2->TEST = 8;
         Data.TouchX = tempPos.px;
         Data.TouchY = tempPos.py;
      }

      Data.ButtonsHeld = (Data.ButtonsHeld | Data.ButtonsPressed) & B;
      Data.ButtonsPressed = B & ~Data.ButtonsHeld;

      Data.TouchZ1 = touchRead(TSC_MEASURE_Z1);
      Data.TouchZ2 = touchRead(TSC_MEASURE_Z2);


      //Update IPC
      IPC2->TouchX = Data.TouchX;
      IPC2->TouchY = Data.TouchY;
      IPC2->ButtonsHeld = Data.ButtonsHeld;
      IPC2->ButtonsPressed = Data.ButtonsPressed;
      IPC2->TouchZ1 = Data.TouchZ1;
      IPC2->TouchZ2 = Data.TouchZ2;
      memcpy((u8*)IPC->time.curtime, (u8*)Data.TimeData, sizeof(Data.TimeData)); //libFAT needs this
      memcpy((u8*)IPC2->TimeData, (u8*)Data.TimeData, sizeof(Data.TimeData));
      IPC2->Temperature = Data.Temperature;
      IPC2->A7FrameCount++;


      VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK; //Signal that vblank interrupt has been processed
      REG_IF |= IRQ_VBLANK;
   }
   else if(REG_IF & IRQ_TIMER3)
   {
      IPC2->TEST = 1;
      IPC2->TickCount++;
      VBLANK_INTR_WAIT_FLAGS |= IRQ_TIMER3;
      REG_IF |= IRQ_TIMER3;
      IPC2->TEST = 2;

      //Read the time
      SecCountdown--;
      if(SecCountdown < 1)
      {
         SecCountdown = 1000;
         IPC2->TEST = 3;
         syncRTC();
         IPC2->TEST = 4;

         //Read the temperature

         IPC2->Temperature = touchReadTemperature(&t1, &t2);
         IPC2->TEST = 5;
      }
   }
   else if(REG_IF & IRQ_WIFI)
   {
      IPC2->TEST = 6;
      if(IPC2->WifiEnabled) Wifi_Interrupt();
      VBLANK_INTR_WAIT_FLAGS |= IRQ_WIFI;
      REG_IF |= IRQ_WIFI;
   }
}


/*
Reads the clock.
Copied from a GBADev post. Todo: clean this up.
*/
void syncRTC()
{
   int oldhours = IPC->time.rtc.hours;

   uint8 command[2];

   command[0] = READ_STATUS_REG1;
   rtcTransaction(command, 1, &command[1], 1);

   IPC->mailSize = REG_RCNT;

   if ( command[1] & 0x30 ) {
      IPC->mailRead = command[1];
      REG_IF = IRQ_NETWORK;
   } else {
      IPC->mailAddr = command[1];
   }

   rtcGetTime((uint8 *)&(IPC->time.rtc.hours));

   if(oldhours>IPC->time.rtc.hours) // going from 23 to 0 hours, update whole time struct
      rtcGetTimeAndDate((uint8 *)&(IPC->time.rtc.year));

   memcpy((void*)IPC2->TimeData, (void*)IPC->time.curtime, sizeof(IPC2->TimeData));
}


/*
Initializes the wifi hardware.
Inputs:
   -A9Param: Parameter passed from ARM9; must be passed to Wifi_Init().
Returns: True on success, false otherwise.
*/
bool A7InitWifi(u32 A9Param)
{
   Wifi_SetSyncHandler(_A7WifiSync);
   Wifi_Init(A9Param);
   IPC2->WifiEnabled = true;

   return true;
}


/*
Wifi sync handler; called by wifi lib as necessary.
*/
void _A7WifiSync()
{
   SendA9Message(A9_WIFISYNC, 0, 0, 0, 0, 0);
}


/*
Fatal error handler.
Inputs:
   -Code: Error code to display.
Notes:
   -Does not return. The error code is output in 3 ways:
    1) The speakers will emit a tone whose frequency is Code * 100hz.
    2) The screen should display an error message if ARM9 is still running.
    3) The power LED will blink a number of times equal to the code. There is
       a small delay afterward, then it begins again.
*/
void FatalError(u32 Code)
{
   u32 i;

   //Play sound
   SCHANNEL_TIMER(8) = SOUND_FREQ(Code * 800);
   SCHANNEL_CR(8) = SOUND_VOL(32) | SOUND_PAN(64) | SCHANNEL_WAVEDUTY(3) | SOUND_FORMAT_PSG | SCHANNEL_ENABLE;
   SendA9Message(A9_ERROR, 2, 5, Code, 0, 0); //Send ARM9 a fatal error message

   //Blink power LED
   while(true)
   {
      for(i=0; i<Code; i++)
       {
         writePowerManagement(0, PM_SOUND_PWR | PM_BACKLIGHT_BOTTOM | BIT(4)); //Power LED off (slow blink)
         swiDelay(3000000);
         writePowerManagement(0, PM_SOUND_PWR | PM_BACKLIGHT_BOTTOM); //Power LED on
         swiDelay(3000000);
      }
      swiDelay(10000000);
   }
}


I suspect touchReadTemperature() is leaving the hardware or some internal variable in a state that touchReadXY() doesn't account for.
_________________
I'm a PSP hacker now, but I still <3 DS.


Last edited by HyperHacker on Mon Jul 02, 2007 11:06 pm; edited 1 time in total

#133013 - StoneCypher - Mon Jul 02, 2007 5:39 pm

You should track down some best practices or company standards documents. You'll notice a pattern - they almost all say that functions should have a cap length in the neighborhood of 15-20 lines.

There's a reason for that. I started trying to read your code. I had a cthulhu vertigo moment and gave up.
_________________
Quidquid Latine dictum sit, altum Sonatur
[Images not permitted - Click here to view it]

#133029 - simonjhall - Mon Jul 02, 2007 8:01 pm

I was originally gonna say that that's not a constructive comment (regarding the actual bug) but SC may have a point. Could you try trimming down the amount of code you've got to the bare minimum in order to see what isn't working right? I read about half of it and started to lose track!
I'm sure if you cut out all of the non-essential stuff (eg wifi, clocks etc) and also had a minimal ARM9 (just enough to crash it!), then I'm sure you'll see whatever's the problem in a flash!
_________________
Big thanks to everyone who donated for Quake2

#133035 - HyperHacker - Mon Jul 02, 2007 9:19 pm

That's probably true, but the only really big function here is the message handler, which isn't really related. I'll see though if I can hack up a smaller program.
_________________
I'm a PSP hacker now, but I still <3 DS.

#133037 - wintermute - Mon Jul 02, 2007 9:26 pm

You might also want to make use of the libnds interrupt support code instead of that nonsense you're currently using.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#133055 - HyperHacker - Mon Jul 02, 2007 11:05 pm

I guess Wintermute got it. When I switched to the libNDS interrupt functions, it stopped hanging (and looks cleaner too!). Thanks for pointing those out. :-)
_________________
I'm a PSP hacker now, but I still <3 DS.