3DS实时时钟相关的问题

最近做的一个项目中正好需要获取时间,正好DS内部是有硬件时钟的,而且被整合到了标准库中的time相关函数中,devkitpro自带的example中就有这样的获取时间演示。其中有一个OpenGL的显示时钟演示,但是经过测试发现,这个钟并不会走,而是第一次调用time函数获取一次时间后,以后无论多少次调用都会获得同样的结果。
为了方便测试,我设置了一个硬件时钟用于定期调用获取时间并定期显示在屏幕上的程序,该程序根据example中的示例修改而来,程序如下:

#include 
#include 
#include 


const char* months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};


const char* weekDays[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};


const u16 daysAtStartOfMonthLUT[12] =
{
	0	%7, //januari		31
	31	%7, //februari		28+1(leap year)
	59	%7, //maart			31
	90	%7, //april			30
	120	%7, //mei			31
	151	%7, //juni			30
	181	%7, //juli			31
	212	%7, //augustus		31
	243	%7, //september		30
	273	%7, //oktober		31
	304	%7, //november		30
	334	%7  //december		31
};


#define isLeapYear(year) (((year)%4) == 0)


uint getDayOfWeek(uint day, uint month, uint year)
{
	//http://en.wikipedia.org/wiki/Calculating_the_day_of_the_week


	day += 2*(3-((year/100)%4));
	year %= 100;
	day += year + (year/4);
	day += daysAtStartOfMonthLUT[month] - (isLeapYear(year) && (month <= 1));
	return day % 7;
}


void update_clock()
{
	int hours, seconds, minutes, day, month, year;


	time_t unixTime = time(NULL);
	time(&unixTime);
	struct tm* timeStruct = gmtime((const time_t *)&unixTime);


	hours = timeStruct->tm_hour;
	minutes = timeStruct->tm_min;
	seconds = timeStruct->tm_sec;
	day = timeStruct->tm_mday;
	month = timeStruct->tm_mon;
	year = timeStruct->tm_year +1900;


	printf("\x1b[2J%02i:%02i:%02i", hours, minutes, seconds);
	printf("\n%s %s %i %i", weekDays[getDayOfWeek(day, month, year)], months[month], day, year);
}


int main(void) 
{
	timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(1), update_clock);
	consoleDemoInit();  //setup the sub screen for printing
	while(1) {
		swiWaitForVBlank();
	}
	return 0;
}


该程序每隔1秒更新显示时间,这个程序在我这里无法正常运行,它一直显示一个固定的时间,就是该程序载入时获取的时间。


为此我使用了另一个方法,就是自己维护时间的改变。载入程序时立即获取时间并且用硬件时钟按秒回调时间更新函数,手动更新时间,做法很丑陋但是还是实现了需要的功能。
之后我特地咨询了一个搞过这方面开发的朋友,他确定他用的也是标准库中普通的time函数,并且能在DSL中正常运行。这时我才想起来,也许是我们俩用的硬件不一样,说不定3DS在处理时间计数方面有不同的机制。于是我使用DSL运行该程序,程序正常运行,能正确显示时间的改变。


在libnds的arm9部分的头文件中,没找到获取自带的实时时钟相关的内容,相反在arm7目录中找到了clock.h,其中有这么个函数:

//---------------------------------------------------------------------------------
void rtcGetTimeAndDate(uint8 * time) {
//---------------------------------------------------------------------------------
	uint8 command, status;

	command = READ_TIME_AND_DATE;
	rtcTransaction(&command, 1, time, 7);

	command = READ_STATUS_REG1;
	rtcTransaction(&command, 1, &status, 1);

	if ( status & STATUS_24HRS ) {
		time[4] &= 0x3f;
	} else {

	}
	BCDToInteger(time,7);
}

可以说明该函数就是为了更新硬件时钟而准备的。他会在后台以某种机制更新时间以IPC共享该arm9端,使得arm9端需要时可以获取准确时间。
//---------------------------------------------------------------------------------
void resyncClock() {
//---------------------------------------------------------------------------------
	RTCtime dstime;
	rtcGetTimeAndDate((uint8 *)&dstime);
	
	__transferRegion()->unixTime = __mktime(&dstime);
}

clock.c中的该函数就实现了同步时钟的功能,而resync函数在初始化时钟中断的时候被调用了一次:

//---------------------------------------------------------------------------------
void initClockIRQ() {
//---------------------------------------------------------------------------------

	REG_RCNT = 0x8100;
	irqSet(IRQ_NETWORK, syncRTC);
	// Reset the clock if needed
	rtcReset();

	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);

	// Read all time settings on first start
	resyncClock();
}



也许这就是开机时第一次获取的时间是正确的原因。
system.c中,该函数也出现了一次,出人意料的是竟然出现在 powerValueHandler 函数原型如下:
void powerValueHandler(u32 value, void* user_data)

似乎就是合盖后,也就是系统Sleep后这个时钟才开始运作。可是我们需要的是开着盖的时候就保持时钟的运作啊,不过这依然无法解释为什么在DSL上能正常工作。


现在无法获取正确时间就是时间没有得到正确的更新,于是在arm7端手动调用该函数,将结果存放在0x02fff800位置,数据的格式为7个unsigned char, 内容分别为:年(后两位)、月、日、时、分、秒。

	// Keep the ARM7 mostly idle
	while (!exitflag) {
		if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) {
			exitflag = true;
		}
		rtcGetTimeAndDate((uint8 *)0x02fff800);
		swiWaitForVBlank();
	}


从arm9端读取数据并显示在屏幕上:
#include 
#include 


void update_clock()
{
	vu8* data = (vu8*)0x02fff800;
	iprintf("%d %d %d %d %d %d %d\n",data[0],data[1],data[2],data[3],data[4],data[5],data[6]);
}


int main(void) 
{
	timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(1), update_clock);


	consoleDemoInit();  //setup the sub screen for printing


	while(1) {


		swiWaitForVBlank();
	}


	return 0;
}


在DSL和3DS上测试都运行正确,可以获取实时时钟的时间。
事后我问那个朋友为什么要选择0x02fff800这个数字?有什么特别的地方?他说,只要两个CPU都能访问而且没有被cache就行了……虽然这么做是挺危险的,但是我还是从中得到了启发,嵌入式的程序只要在了解硬件的体系结构的情况下所有的资源都是可以随意使用的,就像内核开发一样,而不需要用在操作系统分保护下的惯性思维来限制自己。

你可能感兴趣的:(command,timer,测试,network,嵌入式,struct,DS,技术文档,3DS)