Dzz
stmia r0!, {r2-r9}
In another thread I was whining about being unable to read the GP2X serial number, which is something I want to do as the first step in identifying Mk1 gp2x units so I can treat their graphics differently in my little games. Anyway, I finally figured it out. Code attached.
The "pszSerial" parameter should be a buffer of 25 or more characters. The result will be a null-terminated character string at most 24 characters long. The "pusRegs" parameter is the standard result from calling mmap() to get an address for the mmsp2 registers at 0xC0000000 (see many other code examples for doing that). The function returns a failure code if the I2C stuff fails, but makes no effort to validate the contents of the returned data, which looks like this:
20070607GP2XV00400000056
CODE
#define I2CCON (000>>1)
#define I2CSTAT (002>>1)
#define I2CADDR (004>>1)
#define I2CDATA (006>>1)
#define DEVICE 0xA0
int WaitOnI2CInterrupt(volatile unsigned short *pusRegs)
{
int n = 10000000;
while(n > 0)
{
if(pusRegs[I2CCON] & 0x10) // wait for interrupt pending flag
return 1;
n--;
}
return 0;
}
// returns 1 on success, 0 on failure
int FetchSerial(volatile unsigned short *pusRegs, char *pszSerial)
{
unsigned short usCount = 0;
// initialize registers
pusRegs[I2CCON] = 0xEF; // set clocks, enable interrupt, clear pending flag
pusRegs[I2CADDR] = DEVICE; // set address
pusRegs[I2CSTAT] = 0x10; // enable I2C
*pszSerial = 0;
// tell the eeprom what address we're interested in
pusRegs[I2CDATA] = DEVICE;
pusRegs[I2CSTAT] = 0xF0; // Master mode, data write
if(WaitOnI2CInterrupt(pusRegs))
{
pusRegs[I2CDATA] = 0x00; // high byte of address
pusRegs[I2CCON] = 0xEF;
if(WaitOnI2CInterrupt(pusRegs))
{
pusRegs[I2CDATA] = 0x20; // address of serial number
pusRegs[I2CCON] = 0xEF;
if(WaitOnI2CInterrupt(pusRegs))
{
// issue the read command
pusRegs[I2CDATA] = DEVICE;
pusRegs[I2CSTAT] = 0xB0; // Master mode, data read
pusRegs[I2CCON] = 0xEF;
if(WaitOnI2CInterrupt(pusRegs))
{
// read the data bytes
for(usCount = 0; usCount < 24; usCount++)
{
pusRegs[I2CCON] = 0xEF;
if(!WaitOnI2CInterrupt(pusRegs))
break;
*pszSerial++ = (char) pusRegs[I2CDATA];
*pszSerial = 0;
}
}
}
}
}
// shut down
pusRegs[I2CSTAT] = 0x90;
pusRegs[I2CCON] = 0xEF;
usleep(10000);
// leave the I2C in a quiet state
pusRegs[I2CCON] = 0x40; // disable interrupt
pusRegs[I2CSTAT] = 0x00; // disable I2C
if(usCount == 24)
return 1;
return 0;
}
The "pszSerial" parameter should be a buffer of 25 or more characters. The result will be a null-terminated character string at most 24 characters long. The "pusRegs" parameter is the standard result from calling mmap() to get an address for the mmsp2 registers at 0xC0000000 (see many other code examples for doing that). The function returns a failure code if the I2C stuff fails, but makes no effort to validate the contents of the returned data, which looks like this:
20070607GP2XV00400000056
CODE
#define I2CCON (000>>1)
#define I2CSTAT (002>>1)
#define I2CADDR (004>>1)
#define I2CDATA (006>>1)
#define DEVICE 0xA0
int WaitOnI2CInterrupt(volatile unsigned short *pusRegs)
{
int n = 10000000;
while(n > 0)
{
if(pusRegs[I2CCON] & 0x10) // wait for interrupt pending flag
return 1;
n--;
}
return 0;
}
// returns 1 on success, 0 on failure
int FetchSerial(volatile unsigned short *pusRegs, char *pszSerial)
{
unsigned short usCount = 0;
// initialize registers
pusRegs[I2CCON] = 0xEF; // set clocks, enable interrupt, clear pending flag
pusRegs[I2CADDR] = DEVICE; // set address
pusRegs[I2CSTAT] = 0x10; // enable I2C
*pszSerial = 0;
// tell the eeprom what address we're interested in
pusRegs[I2CDATA] = DEVICE;
pusRegs[I2CSTAT] = 0xF0; // Master mode, data write
if(WaitOnI2CInterrupt(pusRegs))
{
pusRegs[I2CDATA] = 0x00; // high byte of address
pusRegs[I2CCON] = 0xEF;
if(WaitOnI2CInterrupt(pusRegs))
{
pusRegs[I2CDATA] = 0x20; // address of serial number
pusRegs[I2CCON] = 0xEF;
if(WaitOnI2CInterrupt(pusRegs))
{
// issue the read command
pusRegs[I2CDATA] = DEVICE;
pusRegs[I2CSTAT] = 0xB0; // Master mode, data read
pusRegs[I2CCON] = 0xEF;
if(WaitOnI2CInterrupt(pusRegs))
{
// read the data bytes
for(usCount = 0; usCount < 24; usCount++)
{
pusRegs[I2CCON] = 0xEF;
if(!WaitOnI2CInterrupt(pusRegs))
break;
*pszSerial++ = (char) pusRegs[I2CDATA];
*pszSerial = 0;
}
}
}
}
}
// shut down
pusRegs[I2CSTAT] = 0x90;
pusRegs[I2CCON] = 0xEF;
usleep(10000);
// leave the I2C in a quiet state
pusRegs[I2CCON] = 0x40; // disable interrupt
pusRegs[I2CSTAT] = 0x00; // disable I2C
if(usCount == 24)
return 1;
return 0;
}