swzswz

极海 APM32 + RTC示例并写日历

0
阅读(1798)

APM32内置一个RTC,通过内部或者外部的时钟源实现精确走时,相比于定时器实现的RTC要准确得多。


  • 定义一个日期、时间结构体及结构体全局变量,定义一个基础月份对应的天数数组:

typedef struct
{
uint16_t year;
uint8_t  month;
uint8_t  day;
uint8_t  week;
uint8_t  hour;
uint8_t  minute;
uint8_t  second;
} CALENDAR_TypeDef;

const uint8_t    RTC_DayOfMonth[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
CALENDAR_TypeDef RTC_Calendar;
  • 闰年判断函数:

uint8_t RTC_LeapYear(uint16_t Year)
{
if(
(((Year % 400) == 0)                     ) ||   /* Century Leap Year */
(((Year % 100) != 0) && ((Year % 4) == 0))      /* Normal  Leay Year */
)
{
return 1;
}
else
{
return 0;
}
}
  • 通过蔡勒公式获取当前日期对应的是星期几:

uint8_t RTC_GetWeek(uint16_t Year, uint8_t Month, uint8_t Day)
{
int w, c, y;

/* Month 1 Or 2 of This Year Must Be As Last Month 13 Or 14 */
if((Month == 1) || (Month == 2))
{
Month += 12;
Year  -= 1;
}

w = 0;          /* Weekday */
c = Year / 100; /* Century */
y = Year % 100; /* Year    */

w = y + (y / 4) + (c / 4) - (2 * c) + (26 * (Month + 1) / 10) + Day - 1;

while(w < 0) w += 7;

w %= 7;

return w;
}
  • 通过读取当前RTC计数器的累加值,将其转换为对应的日期和时间信息

void RTC_UpdateCalendar(void)
{
static uint32_t PreTotalDay = 0;

uint32_t TotalSecond = 0;
uint32_t TotalDay    = 0;

uint16_t Year  = 1970;
uint8_t  Month = 0;

/* Get The RTC Counter Value */
TotalSecond = RTC_ReadCounter();
TotalDay    = TotalSecond / 86400;

if(PreTotalDay != TotalDay)
{
PreTotalDay = TotalDay;

while(TotalDay >= 365)
{
if(RTC_LeapYear(Year) == 1)
{
if(TotalDay >= 366)
{
TotalDay -= 366;
}
else
{
break;
}
}
else
{
TotalDay -= 365;
}

Year++;
}

RTC_Calendar.year = Year;

while(TotalDay >= 28)
{
if((Month == 1) && (RTC_LeapYear(RTC_Calendar.year) == 1))
{
if(TotalDay >= 29)
{
TotalDay -= 29;
}
else
{
break;
}
}
else
{
if(TotalDay >= RTC_DayOfMonth[Month])
{
TotalDay -= RTC_DayOfMonth[Month];
}
else
{
break;
}
}

Month++;
}

RTC_Calendar.month = Month    + 1;
RTC_Calendar.day   = TotalDay + 1;

RTC_Calendar.week  = RTC_GetWeek(RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day);
}

RTC_Calendar.hour   =  (TotalSecond % 86400) / 3600;
RTC_Calendar.minute = ((TotalSecond % 86400) % 3600) / 60;
RTC_Calendar.second = ((TotalSecond % 86400) % 3600) % 60;
}
  • 通过日期和时间信息将其转换为RTC的计数值,并将计数值设置到RTC中:

void RTC_SetDateTime(uint16_t Year, uint8_t Month, uint8_t Day,
uint8_t  Hour, uint8_t Min,   uint8_t Sec)
{
uint32_t TotalSecond = 0;

uint16_t y = 0;
uint8_t  m = 0;

if((Year >= 1970) && (Year <= 2099))
{
for(y = 1970;  y < Year; y++)
{
if(RTC_LeapYear(y) == 1)
{
TotalSecond += 31622400;    /* Total Seconds Of Leap   Year */
}
else
{
TotalSecond += 31536000;    /* Total Seconds Of Normal Year */
}
}

for(m = 0; m < (Month - 1); m++)
{
TotalSecond += RTC_DayOfMonth[m] * 86400;   /* Total Seconds Of Month */

if((RTC_LeapYear(Year) == 1) && (m == 1))
{
TotalSecond += 86400;
}
}

TotalSecond += (uint32_t)(Day - 1) * 86400; /* Total Seconds Of Day    */
TotalSecond += (uint32_t)Hour      * 3600;  /* Total Seconds Of Hour   */
TotalSecond += (uint32_t)Min       * 60;    /* Total Seconds Of Minute */
TotalSecond += Sec;

RTC_ConfigCounter(TotalSecond);
RTC_WaitForLastTask();
}
else
{
printf("\r\nError Date & Time!!!\r\n");
}
}
  • 打印日期和时间信息:

void RTC_PrintDateTime(void)
{
printf("\r\n%04d-%02d-%02d", RTC_Calendar.year, RTC_Calendar.month,  RTC_Calendar.day);

switch(RTC_Calendar.week)
{
case 0 : printf(" SUN "); break;
case 1 : printf(" MON "); break;
case 2 : printf(" TUE "); break;
case 3 : printf(" WED "); break;
case 4 : printf(" THU "); break;
case 5 : printf(" FRI "); break;
case 6 : printf(" SAT "); break;
default: break;
}

printf("%02d:%02d:%02d\r\n", RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
}
  • 自动加载并提取当前工程编译生成时的日期和时间信息,用于后面初始化日期和时间用:

void RTC_LoadDefault(void)
{
char    date[20], time[20];
char    text[6][5];
uint8_t index = 0, month = 0;

memset(date, 0, sizeof(date));
memset(time, 0, sizeof(time));
memset(text, 0, sizeof(text));

memcpy(date, __DATE__, sizeof(__DATE__));
memcpy(time, __TIME__, sizeof(__TIME__));

char *str;

str = strtok(date, " ");

while(str != NULL)
{
memcpy(text[index], str, strlen(str));
index++;

str = strtok(NULL, " ");
}

str = strtok(time, ":");

while(str != NULL)
{
memcpy(text[index], str, strlen(str));
index++;

str = strtok(NULL, ":");
}

#if 0
for(uint8_t i = 0; i < index; i++)
{
printf("\r\n->%s", text[i]);
}
#endif

char *strMonth[12] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};

for(uint8_t i = 0; i < 12; i++)
{
if(strcmp(text[0], strMonth[i]) == 0)
{
month = i + 1;
}
}

RTC_Calendar.day    = atoi(text[1]);
RTC_Calendar.month  = month;
RTC_Calendar.year   = atoi(text[2]);
RTC_Calendar.week   = RTC_GetWeek(RTC_Calendar.year,
RTC_Calendar.month,
RTC_Calendar.day);

RTC_Calendar.hour   = atoi(text[3]);
RTC_Calendar.minute = atoi(text[4]);
RTC_Calendar.second = atoi(text[5]);
}
  • RTC初始化及中断处理实现:

void RTC_Init(void)
{
RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)RCM_APB1_PERIPH_PMU);
PMU_EnableBackupAccess();

RCM_EnableLSI();
while(RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);

RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
RCM_EnableRTCCLK();

RTC_WaitForSynchro();
RTC_WaitForLastTask();

RTC_EnableInterrupt(RTC_INT_SEC);
RTC_WaitForLastTask();

RTC_ConfigPrescaler(32767);
RTC_WaitForLastTask();

RTC_LoadDefault();
RTC_SetDateTime(RTC_Calendar.year, RTC_Calendar.month,  RTC_Calendar.day,
RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
RTC_WaitForLastTask();

NVIC_EnableIRQRequest(RTC_IRQn, 0, 0);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention  
*******************************************************************************/
void RTC_IRQHandler(void)
{
if(RTC_ReadIntFlag(RTC_INT_SEC) != RESET)
{
RTC_UpdateCalendar();

RTC_PrintDateTime();
}

RTC_ClearIntFlag(RTC_INT_SEC | RTC_INT_OVR);
RTC_WaitForLastTask();
}