Duggle

Using a RTCC to set the SD card file system timestamp

16 posts in this topic

Hi All,

I've connected a RTCC (real time clock calendar) chip MCP79410 to IIC bus STM core32. I'm able to set it and read it o.k

It keeps time when the core is not powered (by way of supercap).

Question is: Is there a way to use this time with SDcard so that files created have the correct timestamp?

Ive been playing with the SD card Tool application to see how file accesses are done.

thanks

Share this post


Link to post
Share on other sites

Interesting! I don't have an immediate answer to this, but the underlying API is FatFs, and there is at least a command to change the date/time stamp, see the FatFs website.

Share this post


Link to post
Share on other sites

Interesting! I don't have an immediate answer to this, but the underlying API is FatFs, and there is at least a command to change the date/time stamp, see the FatFs website.

Yes, the set_timestamp(..) example works. Just have to make sure this gets called when data in the file is changed.

Thanks!

Share this post


Link to post
Share on other sites

Hey, good to hear! Could you tell us how exactly you connected the RTCC, and what you did to set it properly? Does it account for leap years and daylight saving time changes automatically? For how long does it keep the correct time / date if not powered up? Hehe, too many questions...

Edit: well, never mind, it's all in the device's documentation which can be found here.

Thanks, ilmenator

Edited by ilmenator

Share this post


Link to post
Share on other sites

Here's the interconnects I'm using:

gallery_3440_57_1007.png

Share this post


Link to post
Share on other sites

Cool, thanks! Have you got an estimation of how long the supercap will hold enough charge to keep the RTCC running? Which model are you using? The original design suggests the use of a battery.

Share this post


Link to post
Share on other sites

Cool, thanks! Have you got an estimation of how long the supercap will hold enough charge to keep the RTCC running? Which model are you using? The original design suggests the use of a battery.

I'm using the device without unique ID feature MPC79410.

The data sheet says 700nA to power in standby (Vbat=1.8V) so I think a Lithium coin cell is the best for most use cases.

I calculate the 0.9F supercap will keep the clock running for only 36days.

A coin cell may easily have 100mAhrs that's >100,000 hrs, well over 10 years.

Share this post


Link to post
Share on other sites

Here's some code to access this device: (at least the RTCC features)

MCP79410.h



#ifdef __cplusplus

extern "C" {

#endif


extern void RTC_init();

extern char* RTC_GetTimeString();  				//read time "hh:mm,dd-mm-yyyy"

extern char IsTimeStrFormat(char * ts); 		//validate TimeString format

extern void RTC_SetFromTimeString(char* ts);	//set time "hh:mm,dd-mm-yyyy"

extern void RTC_PutForward1Hour(); //put time forward an hour (i.e -> Daylight Savings)

extern void RTC_PutBack1Hour();    //put time back an hour (i.e-> NON Daylight Savings)

extern char RTC_IsSet();			//has the RTCC been set?


#ifdef __cplusplus

}

#endif


MCP79410.c



#include <mios32.h>

#include <string.h>

#include <time.h>

#include "MCP79410.h"

#define I2CADDR  0x6F

#define VbatEnableReg 3

#define VBATEN 3

#define ST  7

#define reg_seconds 0

#define msk_seconds 0x7F

#define reg_minutes 1

#define reg_hours 2

#define msk_hours 0x3F

#define reg_day 3

#define msk_day 0x07

#define reg_date 4

#define reg_month 5

#define msk_month 0x1F

#define reg_year 6


#define reg_control 0x07

#define SQWE  6

#define OUT 7


#define SRAM_START 0x20


void RTC_WrReg(u8 r,u8 d){

 u8 sub[2];

 sub[0]=r;

 sub[1]=d;

s32 x=MIOS32_IIC_Transfer(0,IIC_Write,I2CADDR<<1,sub,2);

 x=MIOS32_IIC_TransferWait(0);

}


u8 RTC_RdReg(u8 r){

 u8 d;

 d=r;

 s32 x=MIOS32_IIC_Transfer(0,IIC_Write,I2CADDR<<1,&d,1);

 x=MIOS32_IIC_TransferWait(0);

 x=MIOS32_IIC_Transfer(0,IIC_Read,I2CADDR<<1,&d,1);

 x= MIOS32_IIC_TransferWait(0);

 return d;

}


u8 Int2BCD(int x){   //return integer as two decimal digits

  char low=x%10;

  char hi=(x/10)<<4;

  return hi|low;

}

int BCD2Int(u8 bcd){  //Convert two BCD digits to Integer

  int h=10*(bcd>>4); 

  return h+(bcd&0x0f);

}


void RTC_init(){

  char regval;

  regval=RTC_RdReg(VbatEnableReg);  //read dont change other bitfields

  regval|=(1<<VBATEN);              //enable backup supply 

  RTC_WrReg(VbatEnableReg,regval);   

  regval=RTC_RdReg(reg_seconds);  //read dont change other bitfields

  regval|=(1<<ST);                //start the osc   

  RTC_WrReg(reg_seconds,regval);    

  RTC_WrReg(reg_control,((1<<SQWE)|(1<<OUT)));  //enable squarewave 1Hz on MF Pin. Requires pullup on MFP

}


char* RTC_GetTimeString(){  //return timestr

  static char s[24];

  char secs,mins,hrs,date,mth,yr;

  secs=RTC_RdReg(reg_seconds);

  secs&=msk_seconds;

  mins=RTC_RdReg(reg_minutes);

  hrs=RTC_RdReg(reg_hours);

  hrs&=msk_hours;

  date=RTC_RdReg(reg_date);

  mth=RTC_RdReg(reg_month);

  mth&=msk_month;

  yr=RTC_RdReg(reg_year);


  sprintf(s,"%02X:%02X,%02X-%02X-20%02X",hrs,mins,date,mth,yr); //TimeStr

  return s;

}


char IsTimeStrFormat(char * ts){   //"hh:mm,dd-mm-yyyy"

  char result=0;

  if (strlen(ts)==16) 

		result=1;

  else{

		MIOS32_MIDI_SendDebugMessage("TimeStr format:\"hh:mm,dd-mm-yyyy\" bad length-%d-",strlen(ts));

		return 0;

  }

  if ((ts[2]==':')&&(ts[5]==',')&&(ts[8]=='-')&&(ts[11]=='-'))

   	result=1; 

  else{

		MIOS32_MIDI_SendDebugMessage("TimeStr format:\"hh:mm,dd-mm-yyyy\" bad format");

		return 0;

  }  

  return result;

}


void TimeStr2tm(struct tm* T, char* t){      //fill in struct tm with an input timestr

    T->tm_hour=(t[0]-0x30)*10+(t[1]-0x30);  

    T->tm_min= (t[3]-0x30)*10+(t[4]-0x30);  

    T->tm_sec=0;  

    T->tm_mday= (t[6]-0x30)*10+(t[7]-0x30);  

    T->tm_mon= (t[9]-0x30)*10+(t[10]-0x30)-1; 	//from 0

    T->tm_year= 100+(t[14]-0x30)*10+(t[15]-0x30);  

    T->tm_wday=0;

    T->tm_isdst=0;

}



char RTC_IsSet(){

  char yr;

  yr=RTC_RdReg(reg_year);

  return (yr>0x10);        //unset RTC has year==01

}


void RTC_PutForward1Hour(){     	//put time forward an hour (i.e -> Daylight Savings)

char hrs=RTC_RdReg(reg_hours);

char x=hrs;

 	x&=~msk_hours;  //clear hours field

 	hrs&=msk_hours;  //clear non hours field(s)

     hrs=BCD2Int(hrs);

 	++hrs;       	//inc hours as integer 

     if (hrs==24) hrs=0;

     hrs=Int2BCD(hrs);//store as BCD 

 	hrs|=x;          //set control bits  

 	RTC_WrReg(reg_hours,hrs);   

};


void RTC_PutBack1Hour(){            //put time back an hour (i.e-> NON Daylight Savings)

u8   hrs=RTC_RdReg(reg_hours);

u8   x=hrs;

 	x&=~msk_hours;  //clear hours field

 	hrs&=msk_hours;  //clear non hours field(s)

     hrs=BCD2Int(hrs);

     if (hrs==0) 

		hrs=23;

     else	

        --hrs;       	//dec hours  

     hrs=Int2BCD(hrs);//store as BCD 	

 	hrs|=x;                 	//set control bits  

 	RTC_WrReg(reg_hours,hrs);   

};


void RTC_SetTime(struct tm* T){

  char x,mins,hrs,date,mth,yr;


  mins=Int2BCD(T->tm_min);           	//load and convert to BCD 

  hrs=Int2BCD(T->tm_hour);

  date=Int2BCD(T->tm_mday);

  mth=Int2BCD(T->tm_mon+1);         	//months start from 0

  yr=Int2BCD(T->tm_year-100);     	//year counted from 1900

                                    //dont worry about seconds

  RTC_WrReg(reg_minutes,mins); 


  x=RTC_RdReg(reg_hours); 	//read all

  x&=~msk_hours;              //clear data bitfields

  hrs|=x;                 	//set control bits  

  RTC_WrReg(reg_hours,hrs);   


  RTC_WrReg(reg_date,date); 


  x=RTC_RdReg(reg_month);

  x&=~msk_month;

  mth|=x;

  RTC_WrReg(reg_month,mth);


  RTC_WrReg(reg_year,yr); 

}


void RTC_SetFromTimeString(char* ts){ //"hh:mm,dd-mm-yyyy"

	struct tm T;

	TimeStr2tm(&T,ts);

	RTC_SetTime(&T);

}

Share this post


Link to post
Share on other sites

Nice! :)

Under modules/fatfs/src/diskio.c you will find the get_fattime() function which is currently not implemented:


DWORD get_fattime(void)
{
/* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
/* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
return 0;
}
[/code]

I could declare it as "weak", this would allow you to implement an alternative version in your driver (a weak function will be replaced by your implementation if it's linked into the project)

Best Regards, Thorsten.

Share this post


Link to post
Share on other sites

Hi TK,

I've experimented by modifying the header declaration to:

DWORD get_fattime (void) __attribute__((weak));

I'm confused however, as the declaration appears in ff.h (not diskio.h) and your implementation is defined in diskio.c

Anyhow, I'm finding that your implementation is getting executed, not mine, which is implemented in my app.cpp

I'm sure what I need to do here? Did I get the declaration syntax right?

thanks.

Share this post


Link to post
Share on other sites

I think this article demonstrates how the attribute works: Article

I'll have another try.

Share this post


Link to post
Share on other sites

The declarations and definitions in the repo need to be appropriately modified for this weak override business to work. The only way I can seem to get it to work is to comment out TK's implementation of get_fattime().

Anyhow it's working fine!

This is in my app.cpp



extern "C" DWORD get_fattime(void)

{

  return RTC_GetFatTime();

}

and this is added to the RTCC driver routines listed earlier:

 DWORD RTC_GetFatTime()

{

	DWORD FatTime=0;

	char secs,mins,hrs,date,mth,yr;

  	secs=RTC_RdReg(reg_seconds);

  	secs&=msk_seconds;

  	mins=RTC_RdReg(reg_minutes);

  	hrs=RTC_RdReg(reg_hours);

  	hrs&=msk_hours;

  	date=RTC_RdReg(reg_date);

  	mth=RTC_RdReg(reg_month);

  	mth&=msk_month;

  	yr=RTC_RdReg(reg_year);


  	FatTime|=BCD2Int(secs)>>1;

  	FatTime|=BCD2Int(mins)<<5;

  	FatTime|=BCD2Int(hrs)<<11;

  	FatTime|=BCD2Int(date)<<16;

  	FatTime|=BCD2Int(mth)<<21;

  	FatTime|=(BCD2Int(yr)+20)<<25;


  	return FatTime;

}

/* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */

 /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */


Share this post


Link to post
Share on other sites

I just have changed diskio.c, so that get_fattime is handled as a weak function now.

Could you please check, if your function is now able to overload the "weak" implementation without source code change?

Best Regards, Thorsten.

Share this post


Link to post
Share on other sites

Thanks Thorsten,

it works fine.

The only thing I had to do was put in

#ifdef __cplusplus

extern "C" {

#endif

..

#ifdef __cplusplus

}

#endif

around the function declarations in ff.h for c++ compilation.

It would be good (for c++ coders) if headers get committed this way.

Cheers

Share this post


Link to post
Share on other sites

I don't want to change the original header files for such a purpose, because the modification would get lost if I (or somebody else) would update to a newer version, and the next guy who would try to compile your application would get confusing compile errors.

You could just wrap the #include statements in your .cpp file instead:

extern "C" {
#include <ff.h>
#include <diskio.h>
}
[/code]

This has the same effect.

Best Regards, Thorsten.

Share this post


Link to post
Share on other sites

You could just wrap the #include statements in your .cpp file instead:

extern "C" {

#include <ff.h>

#include <diskio.h>

}

This has the same effect.

Great, I'll do this.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now