Files
zTC1/mico-os/platform/MCU/STM32F4xx/peripherals/platform_rtc.c

730 lines
23 KiB
C

/**
******************************************************************************
* @file platform_rtc.c
* @author William Xu
* @version V1.0.0
* @date 05-May-2014
* @brief This file provide RTC driver functions.
******************************************************************************
* UNPUBLISHED PROPRIETARY SOURCE CODE
* Copyright (c) 2016 MXCHIP Inc.
*
* The contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of MXCHIP Corporation.
******************************************************************************
*/
#include "platform.h"
#include "platform_peripheral.h"
#include "debug.h"
/******************************************************
* Macros
******************************************************/
#define MICO_VERIFY_TIME(time, valid) \
if( (time->sec > 60) || ( time->min > 60 ) || (time->hr > 24) || ( time->date > 31 ) || ( time->month > 12 )) \
{ \
valid= false; \
} \
else \
{ \
valid= true; \
}
#define LEAP_YEAR_OR_NOT(year)( ( year % 4 ) ? ( 0 ) : ( 1 ) )
/******************************************************
* Constants
******************************************************/
#define LEAP_YEAR_DAY_COUNT ( 366 )
#define NOT_LEAP_YEAR_DAY_COUNT ( 365 )
#define NUM_SECONDS_IN_MINUTE ( 60 )
#define NUM_SECONDS_IN_HOUR ( 3600 )
#define NUM_1P25MS_IN_SEC ( 800 )
/******************************************************
* Enumerations
******************************************************/
typedef enum
{
CLOCKING_EVERY_SEC,
CLOCKING_EVERY_1p25MSEC
}rtc_clock_state_t;
/******************************************************
* Type Definitions
******************************************************/
/******************************************************
* Structures
******************************************************/
/******************************************************
* Static Function Declarations
******************************************************/
static OSStatus stm32f2_rtc_change_clock ( rtc_clock_state_t* current, rtc_clock_state_t target );
static uint32_t convert_rtc_calendar_values_to_units_passed( void );
static void reset_rtc_values ( void );
#if ( defined( MICO_ENABLE_MCU_RTC ) && !defined( MICO_DISABLE_MCU_POWERSAVE ) )
static OSStatus compensate_time_error ( uint32_t sec, bool subtract );
static int add_1p25ms_contribution ( uint32_t ms, uint32_t* seconds_contribution );
static void add_second_to_time ( platform_rtc_time_t* time );
static void subtract_second_from_time ( platform_rtc_time_t* time );
#endif /* #ifndef MICO_DISABLE_MCU_POWERSAVE */
/******************************************************
* Variables Definitions
******************************************************/
platform_rtc_time_t default_rtc_time =
{
/* set it to 12:20:30 08/04/2013 monday */
.sec = 30,
.min = 20,
.hr = 12,
.weekday = 1,
.date = 8,
.month = 4,
.year = 13
};
static const char not_leap_days[] =
{
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const char leap_days[] =
{
0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static platform_rtc_time_t saved_rtc_time;
static rtc_clock_state_t current_clock_state = CLOCKING_EVERY_SEC;
/******************************************************
* Function Declarations
******************************************************/
/******************************************************
* Function Definitions
******************************************************/
OSStatus platform_rtc_init(void)
{
#ifdef MICO_ENABLE_MCU_RTC
RTC_InitTypeDef RTC_InitStruct;
RTC_DeInit( );
RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24;
/* RTC ticks every second */
RTC_InitStruct.RTC_AsynchPrediv = 0x7F;
RTC_InitStruct.RTC_SynchPrediv = 0xFF;
RTC_Init( &RTC_InitStruct );
#ifdef USE_RTC_BKP
/* Enable the PWR clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
/* Allow access to BKP Domain */
PWR_BackupAccessCmd(ENABLE);
PWR_BackupRegulatorCmd(ENABLE);
#endif
/* Enable the LSE OSC */
RCC_LSEConfig(RCC_LSE_ON);
/* Wait till LSE is ready */
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{
}
/* Select the RTC Clock Source */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
/* Enable the RTC Clock */
RCC_RTCCLKCmd(ENABLE);
/* RTC configuration -------------------------------------------------------*/
/* Wait for RTC APB registers synchronisation */
RTC_WaitForSynchro();
#ifdef USE_RTC_BKP
if (RTC_ReadBackupRegister(RTC_BKP_DR0) != USE_RTC_BKP) {
/* set it to 12:20:30 08/04/2013 monday */
platform_rtc_set_time(&default_rtc_time);
RTC_WriteBackupRegister(RTC_BKP_DR0, USE_RTC_BKP);
}
#else
//#ifdef RTC_ENABLED
/* application must have mico_application_default_time structure declared somewhere, otherwise it wont compile */
/* write default application time inside rtc */
platform_rtc_set_time(&mico_default_time);
//#endif /* RTC_ENABLED */
#endif
return kNoErr;
#else
return kUnsupportedErr;
#endif
}
/**
* This function will return the value of time read from the on board CPU real time clock. Time value must be given in the format of
* the structure mico_rtc_time_t
*
* @return kNoErr : on success.
*/
OSStatus platform_rtc_get_time( platform_rtc_time_t* time)
{
#ifdef MICO_ENABLE_MCU_RTC
RTC_TimeTypeDef rtc_read_time;
RTC_DateTypeDef rtc_read_date;
if( time == 0 )
{
return kParamErr;
}
/* save current rtc time */
RTC_GetTime( RTC_Format_BIN, &rtc_read_time );
RTC_GetDate( RTC_Format_BIN, &rtc_read_date );
/* fill structure */
time->sec = rtc_read_time.RTC_Seconds;
time->min = rtc_read_time.RTC_Minutes;
time->hr = rtc_read_time.RTC_Hours;
time->weekday = rtc_read_date.RTC_WeekDay;
time->date = rtc_read_date.RTC_Date;
time->month = rtc_read_date.RTC_Month;
time->year = rtc_read_date.RTC_Year;
return kNoErr;
#else /* #ifdef MICO_ENABLE_MCU_RTC */
UNUSED_PARAMETER(time);
return kUnsupportedErr;
#endif /* #ifdef MICO_ENABLE_MCU_RTC */
}
/**
* This function will set MCU RTC time to a new value. Time value must be given in the format of
* the structure mico_rtc_time_t
*
* @return kNoErr : on success.
*/
OSStatus platform_rtc_set_time( const platform_rtc_time_t* time )
{
#ifdef MICO_ENABLE_MCU_RTC
RTC_TimeTypeDef rtc_write_time;
RTC_DateTypeDef rtc_write_date;
bool valid = false;
MICO_VERIFY_TIME(time, valid);
if( valid == false )
{
return kParamErr;
}
rtc_write_time.RTC_H12 = 0;
rtc_write_time.RTC_Seconds = time->sec;
rtc_write_time.RTC_Minutes = time->min;
rtc_write_time.RTC_Hours = time->hr;
rtc_write_date.RTC_WeekDay = time->weekday;
rtc_write_date.RTC_Date = time->date;
rtc_write_date.RTC_Month = time->month;
rtc_write_date.RTC_Year = time->year;
RTC_SetTime( RTC_Format_BIN, &rtc_write_time );
RTC_SetDate( RTC_Format_BIN, &rtc_write_date );
return kNoErr;
#else /* #ifdef MICO_ENABLE_MCU_RTC */
UNUSED_PARAMETER(time);
return kUnsupportedErr;
#endif /* #ifdef MICO_ENABLE_MCU_RTC */
}
OSStatus platform_rtc_enter_powersave ( void )
{
//#ifdef RTC_ENABLED /* !!If we dont read the time and store it. get an error while trying to enter STM RTC initialisation mode */
/* save current rtc time locally */
platform_rtc_get_time( &saved_rtc_time );
//#endif /* #ifdef RTC_ENABLED */
/* Reset RTC values */
reset_rtc_values();
/* Change the clocking state of the RTC, so it ticks every 1.25ms while cpu is sleeping - 800Hz clock */
stm32f2_rtc_change_clock( &current_clock_state, CLOCKING_EVERY_1p25MSEC );
return kNoErr;
}
OSStatus platform_rtc_abort_powersave( void )
{
/* Change the clocking state of the RTC, so its tick is back to normal */
stm32f2_rtc_change_clock( &current_clock_state, CLOCKING_EVERY_SEC );
//#ifdef RTC_ENABLED /* !!If we dont set the time after the clocks have been changed. */
/*get an error while trying to enter STM RTC initialisation mode */
/* restore time saved before */
platform_rtc_set_time( &saved_rtc_time );
//#endif /* #ifdef RTC_ENABLED */
return kNoErr;
}
OSStatus platform_rtc_exit_powersave( uint32_t requested_sleep_time, uint32_t *cpu_sleep_time )
{
uint32_t time_units_passed_since_powersave_enter; /* time unit is 1.25ms when we are sleeping */
#ifdef MICO_ENABLE_MCU_RTC
uint32_t seconds_contribution;
static int leftover=0;
#endif /* #ifdef MICO_ENABLE_MCU_RTC */
float temp;
/* Get current calendar register values and convert them to a number of 1.25ms that passed since power-save entry */
time_units_passed_since_powersave_enter = convert_rtc_calendar_values_to_units_passed();
#ifdef MICO_ENABLE_MCU_RTC
/* Make contribution of time-units to the current time, get the leftover that is less then a second */
leftover+= add_1p25ms_contribution( time_units_passed_since_powersave_enter, &seconds_contribution );
/* Subtract 1 second for every 1.25 * 800, because in reality we are running 799,2195Hz which wil give the tick value = 1.25122 */
if( leftover > NUM_1P25MS_IN_SEC )
{
compensate_time_error(1, false);
leftover -= NUM_1P25MS_IN_SEC;
}
#endif /* #ifdef MICO_ENABLE_MCU_RTC */
//#ifdef RTC_ENABLED /* !!If we dont set the time after the clocks have been changed. */
/*get an error while trying to enter STM RTC initialisation mode */
/* update RTC time */
platform_rtc_set_time(&saved_rtc_time);
//#endif /* #ifdef RTC_ENABLED */
/* Change, rtc clock state and update rtc peripheral, even when RTC is not enabled */
/* reducing clock frequency is better for power consumption */
stm32f2_rtc_change_clock( &current_clock_state, CLOCKING_EVERY_SEC );
temp = (float)time_units_passed_since_powersave_enter * (float)1.25;
/* Round up to milliseconds, not a problem if the system will get less ticks as expected, it is not a time which is populated to a user */
if( requested_sleep_time > 1 )
{
*cpu_sleep_time = (uint32_t)temp;
}
else
{
/* When a delay was set to 1, we will get a wake up interrupt before RTC tick */
/* we will think that there were no rtc tick, and the cpu_sleep_time will be set to 0 */
/* operating system will miss a tick which in reality did happen */
*cpu_sleep_time = 1;
}
return kNoErr;
}
static OSStatus stm32f2_rtc_change_clock( rtc_clock_state_t* current, rtc_clock_state_t target )
{
uint8_t sync_div;
uint8_t async_div;
ErrorStatus status;
/* Changing the synchronous and asynchronous prescalers according to the current clocking state and target_clock_state */
/* of the RTC */
if( *current == CLOCKING_EVERY_SEC )
{
if( target == CLOCKING_EVERY_1p25MSEC )
{
sync_div = 0;
async_div= 40;
/* Disable write protection of rtc registers, now we will be able to update RTC register values */
RTC_WriteProtectionCmd(DISABLE);
/* Enable initialisation mode */
status = RTC_EnterInitMode();
REFERENCE_DEBUG_ONLY_VARIABLE(status);
check_string( status==SUCCESS, "Rtc can not enter intialisation mode");
/* Update RTC prescaler */
RTC->PRER = (uint32_t)( sync_div );
RTC->PRER |= (uint32_t)(async_div << 16);
RTC_ExitInitMode();
/* Enable write proteciton of rtc registers back */
RTC_WriteProtectionCmd(ENABLE);
*current = CLOCKING_EVERY_1p25MSEC;
return kNoErr;
}
else
{
return kNoErr;
}
}
else if( *current == CLOCKING_EVERY_1p25MSEC )
{
if( target == CLOCKING_EVERY_SEC )
{
/* We will get approximately 800Hz clock, 1 tick will correspond to 1.25ms(0,00125s), by addind 8 together we will get 1ms */
/* the closest division factor which is 40.96( 32768/800 ), we will take 41, the error will be very very tiny */
async_div = 127;
sync_div = 255;
/* Disable write protection of rtc registers */
RTC_WriteProtectionCmd(DISABLE);
/* Enable initialisation mode */
status = RTC_EnterInitMode();
check_string( status==SUCCESS, "Rtc can not enter intialisation mode");
/* Update RTC prescaler */
RTC->PRER = (uint32_t)( sync_div );
RTC->PRER |= (uint32_t)(async_div << 16);
RTC_ExitInitMode();
/* Enable write proteciton of rtc registers back */
RTC_WriteProtectionCmd(ENABLE);
*current = CLOCKING_EVERY_SEC;
return kNoErr;
}
else
{
return kNoErr;
}
}
return kNoErr;
}
static void reset_rtc_values( void )
{
ErrorStatus status;
int retry = 3;
/* Disable write protection of rtc registers */
do{
RTC_WriteProtectionCmd(DISABLE);
status = RTC_EnterInitMode();
REFERENCE_DEBUG_ONLY_VARIABLE(status);
retry--;
}while( retry && (status!=SUCCESS) );
check_string( status==SUCCESS, "Rtc can not enter intialisation mode");
/* Reset calendar date registers */
RTC->TR = 0;
RTC_ExitInitMode();
status = RTC_WaitForSynchro();
check_string( status==SUCCESS, "Rtc can not synchronize" );
/* Enable write protection of rtc registers */
RTC_WriteProtectionCmd(ENABLE);
/* Disable write protection of the rtc registers */
RTC_WriteProtectionCmd(DISABLE);
status = RTC_EnterInitMode();
check_string( status==SUCCESS, "Rtc can not enter intialisation mode" );
/* 2000 year 01/01 */
RTC->DR= 0;
RTC->DR= ( 1<<13 ) | ( 1<<8 ) | ( 1<<0 );
RTC_ExitInitMode();
status = RTC_WaitForSynchro();
check_string(status==SUCCESS, "Rtc can not synchronize");
/* Enable write protection of rtc registers */
RTC_WriteProtectionCmd(ENABLE);
}
static uint32_t convert_rtc_calendar_values_to_units_passed( void )
{
long int temp1=0;
long int temp2=0;
int temp=0;
long int temp_days=0;
uint8_t current_year;
RTC_TimeTypeDef rtc_read_time;
RTC_DateTypeDef rtc_read_date;
/* Read current rtc time */
RTC_GetTime( RTC_Format_BIN, &rtc_read_time );
RTC_GetDate( RTC_Format_BIN, &rtc_read_date );
/* Calculate number of days in the previous years */
if( rtc_read_date.RTC_Year != 0 )
{
for( temp = (int)( rtc_read_date.RTC_Year - 1 ); temp >= 0; temp-- )
{
temp_days += (LEAP_YEAR_OR_NOT(temp)) ? (LEAP_YEAR_DAY_COUNT): (NOT_LEAP_YEAR_DAY_COUNT);
}
}
current_year = rtc_read_date.RTC_Year;
check_string( (rtc_read_date.RTC_Month != 0), "Inappropriate month value in RTC");
if( rtc_read_date.RTC_Month != 0 )
{
/* Calculate number of days passed in the current year and add them to previous days value */
for( temp = (int)( rtc_read_date.RTC_Month - 1 ); temp > 0; temp-- )
{
temp_days += LEAP_YEAR_OR_NOT(current_year)?(leap_days[temp]):(not_leap_days[temp]);
}
}
/* Convert passed hours, seconds and minutes to seconds */
temp1 = rtc_read_time.RTC_Seconds + rtc_read_time.RTC_Minutes*NUM_SECONDS_IN_MINUTE + rtc_read_time.RTC_Hours*NUM_SECONDS_IN_HOUR;
check_string(( rtc_read_date.RTC_Date != 0 ), "Inappropriate date value in RTC");
/* Convert passed days to seconds */
if( rtc_read_date.RTC_Date != 0 )
{
temp2 = ( ( rtc_read_date.RTC_Date - 1 ) + temp_days ) * NUM_SECONDS_IN_HOUR * 24;
}
/* Return total number of seconds passed */
return (uint32_t)( temp1 + temp2 );
}
#if ( defined( MICO_ENABLE_MCU_RTC ) && !defined( MICO_DISABLE_MCU_POWERSAVE ) )
static void add_second_to_time( platform_rtc_time_t* time )
{
if ( time->sec == 59 )
{
if ( time->min == 59 )
{
if ( time->hr == 23 )
{
if( time->date == ( LEAP_YEAR_OR_NOT(time->year) ? leap_days[time->month] :not_leap_days[time->month] ) )
{
if( time->month == 12 )
{
/* Adding one second leads to year increment */
time->year++;
time->month=1;
time->date=1;
time->hr=0;
time->min=0;
time->sec=0;
if( time->weekday == 7 )
{
time->weekday=1;
}
else
{
time->weekday++;
}
}
else
{
/* Adding one seconds leads to month increment */
time->month++;
time->date=1;
time->hr=0;
time->min=0;
time->sec=0;
if( time->weekday == 7 )
{
time->weekday=1;
}
else
{
time->weekday++;
}
}
}
else
{
/* Adding one seconds leads to data increment */
if ( time->weekday == 7 )
{
time->weekday=1;
}
else
{
time->weekday++;
}
time->date++;
time->hr=0;
time->min=0;
time->sec=0;
}
}
else
{
/* Adding one seconds leads to hour increment */
time->hr++;
time->min=0;
time->sec=0;
}
}
else
{
/* Adding one seconds leads to minute increment */
time->min++;
time->sec=0;
}
}
else
{
time->sec++;
}
}
static void subtract_second_from_time( platform_rtc_time_t* time )
{
if ( time->sec == 0 )
{
if ( time->min == 0 )
{
if ( time->hr == 0 )
{
if( time->date == 1 )
{
if( time->month == 1 )
{
/* Subtracting one second leads to year decrement */
time->year--;
time->month=12;
time->date=(uint8_t)( LEAP_YEAR_OR_NOT(time->year) ? leap_days[time->month] :not_leap_days[time->month] );
time->hr=23;
time->min=59;
time->sec=59;
if(time->weekday == 1)
{
time->weekday=7;
}
else
{
time->weekday--;
}
}
else
{
/* Subtracting one second leads to month decrement */
time->month--;
time->date=(uint8_t)( LEAP_YEAR_OR_NOT(time->year) ? leap_days[time->month] :not_leap_days[time->month] );
time->hr=23;
time->min=59;
time->sec=59;
if(time->weekday == 1)
{
time->weekday=7;
}
else
{
time->weekday--;
}
}
}
else
{
/* Subtracting one second leads to date decrement */
if ( time->weekday == 1 )
{
time->weekday=7;
}
else
{
time->weekday--;
}
time->date--;
time->hr=23;
time->min=59;
time->sec=59;
}
}
else
{
/* Subtracting one second leads to hour decrement */
time->hr--; /* */
time->min=59;
time->sec=59;
}
}
else
{
/* Subtracting one second leads to minute decrement */
time->min--;
time->sec=59;
}
}
else
{
time->sec--;
}
}
static OSStatus compensate_time_error( uint32_t sec, bool subtract )
{
if( subtract == false )
{
/* Adding seconds to time */
for( sec=sec ; sec > 0; sec--)
{
add_second_to_time(&saved_rtc_time);
}
}
else
{
/* Subtracting seconds from time */
for( sec=sec ; sec > 0; sec-- )
{
subtract_second_from_time(&saved_rtc_time);
}
}
return kNoErr;
}
static int add_1p25ms_contribution( uint32_t units_1p25ms, uint32_t* seconds_contribution )
{
float temp;
temp = (float)units_1p25ms*(float)1.25;
/* Get number of ticks converts them to seconds and returns, number of seconds that were contributed to the */
/* current time value. This value will be required by compensation algorithms */
*seconds_contribution = (uint32_t)temp / 1000;
if(*seconds_contribution)
{
compensate_time_error(*seconds_contribution, false);
}
/* Returns leftover of 1.25 ms units, that are still remaining to contribute to a second */
return (int)(units_1p25ms % NUM_1P25MS_IN_SEC);
}
#endif /* #ifdef MICO_ENABLE_MCU_RTC */