blob: d9e5d79ca25991e4bd7c1bd667b1ef177a8a9ca6 [file] [log] [blame]
//*****************************************************************************
//
//! @file am_util_stopwatch.c
//!
//! @brief Provides functionality to measure elapsed time.
//!
//! Functions for measuring elapsed time. These can be useful for providing
//! 'ticks' where needed.
//!
//! @note These functions require a RTC to function properly. Therefore, if any
//! RTC configuring takes place after calling am_util_stopwatch_start() the
//! resulting elapsed time will be incorrect unless you first call
//! am_util_stopwatch_restart()
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2017, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision v1.2.10-2-gea660ad-hotfix2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "hal/am_hal_rtc.h"
#include "am_util_stopwatch.h"
//*****************************************************************************
//
// Format time based on resolution.
//
//*****************************************************************************
static uint32_t
time_format(uint64_t ui64TimeMS, uint32_t ui32Resolution)
{
switch (ui32Resolution)
{
case AM_UTIL_STOPWATCH_MS:
return ui64TimeMS;
case AM_UTIL_STOPWATCH_SECOND:
return ui64TimeMS / 1000;
case AM_UTIL_STOPWATCH_MINUTE:
return ui64TimeMS / 60000;
case AM_UTIL_STOPWATCH_HOUR:
return ui64TimeMS / 3600000;
case AM_UTIL_STOPWATCH_DAY:
return ui64TimeMS / 86400000;
case AM_UTIL_STOPWATCH_MONTH:
return ui64TimeMS / (uint64_t) 2592000000;
case AM_UTIL_STOPWATCH_YEAR:
return ui64TimeMS / (uint64_t) 31536000000;
default:
return ui64TimeMS;
}
}
//*****************************************************************************
//
// Return the absolute time in milliseconds from a RTC structure.
//
//*****************************************************************************
static uint64_t
elapsed_time_ms(am_hal_rtc_time_t *psStartTime, am_hal_rtc_time_t *psStopTime)
{
int64_t i64DeltaYear = 0;
int64_t i64DelataMonth = 0;
int64_t i64DeltaDay = 0;
int64_t i64DelatHour = 0;
int64_t i64DeltaMinute = 0;
int64_t i64DeltaSecond = 0;
int64_t i64DeltaHundredths = 0;
uint64_t ui64DeltaTotal = 0;
i64DeltaYear = (psStopTime->ui32Year - psStartTime->ui32Year) * (uint64_t) 31536000000;
i64DelataMonth = (psStopTime->ui32Month - psStartTime->ui32Month) * (uint64_t) 2592000000;
i64DeltaDay = (psStopTime->ui32DayOfMonth - psStartTime->ui32DayOfMonth) * (uint64_t) 86400000;
i64DelatHour = (psStopTime->ui32Hour - psStartTime->ui32Hour) * (uint64_t) 3600000;
i64DeltaMinute = (psStopTime->ui32Minute - psStartTime->ui32Minute) * (uint64_t) 60000;
i64DeltaSecond = (psStopTime->ui32Second - psStartTime->ui32Second) * (uint64_t) 1000;
i64DeltaHundredths = (psStopTime->ui32Hundredths - psStartTime->ui32Hundredths) * (uint64_t) 10;
ui64DeltaTotal = (i64DeltaYear + i64DelataMonth + i64DeltaDay + i64DelatHour +
i64DeltaMinute + i64DeltaSecond + i64DeltaHundredths);
return ui64DeltaTotal;
}
//*****************************************************************************
//
//! @brief Start the stopwatch.
//!
//! @param *pStopwatch - the pointer to the am_util_stopwatch_t structure.
//!
//! This function records the current time from the RTC and sets the start time.
//!
//! @return None.
//
//*****************************************************************************
void
am_util_stopwatch_init(am_util_stopwatch_t *pStopwatch)
{
//
// Initialize everything.
//
pStopwatch->ui64ElapsedTime = 0;
pStopwatch->ui64PausedTime = 0;
pStopwatch->bStarted = false;
pStopwatch->bPaused = false;
}
//*****************************************************************************
//
//! @brief Start the stopwatch.
//!
//! @param *pStopwatch - the pointer to the am_util_stopwatch_t structure.
//!
//! This function records the current time from the RTC and sets the start time.
//!
//! @return None.
//
//*****************************************************************************
void
am_util_stopwatch_start(am_util_stopwatch_t *pStopwatch)
{
am_hal_rtc_time_t rtc_time;
//
// If the start time is clear, read the RTC time to get a reference starting
// time.
if ( pStopwatch->bPaused == false && pStopwatch->bStarted == false )
{
//
// Clear the timer which gets the current time as well.
//
am_util_stopwatch_clear(pStopwatch);
}
//
// We were paused.
// Now we need to figure out how long we were paused for.
//
else if ( pStopwatch->bPaused == true && pStopwatch->bStarted == true )
{
//
// Get the RTC time.
//
while ( am_hal_rtc_time_get(&rtc_time) );
//
// Add the time we spent paused to the time we already spent paused.
//
pStopwatch->ui64PausedTime += elapsed_time_ms(&pStopwatch->sPauseTime, &rtc_time);
}
//
// Set started to true.
//
pStopwatch->bStarted = true;
//
// Set paused to false.
//
pStopwatch->bPaused = false;
}
//*****************************************************************************
//
//! @brief Stop the stopwatch.
//!
//! @param *pStopwatch - the pointer to the am_util_stopwatch_t structure.
//!
//! This function stops the stop watch and anytime am_util_stopwatch_elapsed_get()
//! is called it will return the same elapsed time until am_util_stopwatch_start()
//! is called again.
//!
//! @return None.
//
//*****************************************************************************
void
am_util_stopwatch_stop(am_util_stopwatch_t *pStopwatch)
{
//
// Save the current time so we know how long we've been paused for.
//
while ( am_hal_rtc_time_get(&pStopwatch->sPauseTime) );
//
// Set the state to paused.
//
pStopwatch->bPaused = true;
}
//*****************************************************************************
//
//! @brief Clears the stopwatch.
//!
//! @param *pStopwatch - the pointer to the am_util_stopwatch_t structure.
//!
//! This function clears the start time on the stop watch. If the stop watch is
//! running, it will continue to count the elapsed time from the new start time.
//!
//! @return None.
//
//*****************************************************************************
void
am_util_stopwatch_clear(am_util_stopwatch_t *pStopwatch)
{
//
// Read the RTC and save in pStopwatch->sStartTime.
//
while ( am_hal_rtc_time_get(&pStopwatch->sStartTime) );
//
// Reset the paused time.
//
pStopwatch->ui64PausedTime = 0;
//
// Reset the elapsed time.
//
pStopwatch->ui64ElapsedTime = 0;
}
//*****************************************************************************
//
//! @brief Restart the stopwatch.
//!
//! This function restarts the stopwatch.
//!
//! @param *pStopwatch - the pointer to the am_util_stopwatch_t structure.
//!
//! If the stopwatch was previously stopped this is functionally equivalent
//! calling am_util_stopwatch_clear() followed by am_util_stopwatch_start().
//!
//! If the stopwatch was previously started this is functionally equivalent to
//! am_util_stopwatch_clear().
//!
//! @return None.
//
//*****************************************************************************
void
am_util_stopwatch_restart(am_util_stopwatch_t *pStopwatch)
{
//
// Clear the stopwatch.
//
am_util_stopwatch_clear(pStopwatch);
//
// Start the stopwatch.
//
am_util_stopwatch_start(pStopwatch);
}
//*****************************************************************************
//
//! @brief Get the elapsed time from the stopwatch.
//!
//! @param *pStopwatch - the pointer to the am_util_stopwatch_t structure.
//! @param ui32Resolution - the desired resolution to return the elapsed time in.
//!
//! This function returns the elapsed time in the desired resolution as requested
//! from ui32Resolution.
//!
//! Valid values for ui32Resolution:
//! AM_UTIL_STOPWATCH_MS
//! AM_UTIL_STOPWATCH_SEC
//! AM_UTIL_STOPWATCH_MIN
//! AM_UTIL_STOPWATCH_HOUR
//! AM_UTIL_STOPWATCH_DAY
//! AM_UTIL_STOPWATCH_MONTH
//! AM_UTIL_STOPWATCH_YEAR
//!
//! @return Elapsed Time in ui32Resolution.
//
//*****************************************************************************
uint64_t
am_util_stopwatch_elapsed_get(am_util_stopwatch_t *pStopwatch, uint32_t ui32Resolution)
{
am_hal_rtc_time_t rtc_time;
//
// Stop watch is not paused and is running.
// Figure out elapsed time.
//
if ( pStopwatch->bPaused == false && pStopwatch->bStarted == true )
{
//
// Get the RTC time.
//
while ( am_hal_rtc_time_get(&rtc_time) );
pStopwatch->ui64ElapsedTime = elapsed_time_ms(&pStopwatch->sStartTime, &rtc_time) -
pStopwatch->ui64PausedTime;
}
//
// Return the elapsed time.
//
return time_format(pStopwatch->ui64ElapsedTime, ui32Resolution);
}
//*****************************************************************************
//
//! @brief Get and format the elapsed time from the stopwatch.
//!
//! @param *pStopwatch - the pointer to the am_util_stopwatch_t structure.
//! @param *pTime - the pointer to the am_util_stopwatch_elapsed_t structure.
//!
//! This function returns the fills in the am_util_stopwatch_elapsed_t structure
//! with "human readable" elapsed time.
//!
//! @return None.
//
//*****************************************************************************
void
am_util_stopwatch_elapsed_get_formatted(am_util_stopwatch_t *pStopwatch,
am_util_stopwatch_elapsed_t *psTime)
{
uint64_t ui64MS;
//
// Get the elapsed time in MS.
//
ui64MS = am_util_stopwatch_elapsed_get(pStopwatch, AM_UTIL_STOPWATCH_MS);
//
// Zero out the structure.
//
psTime->ui32Year = 0;
psTime->ui32Month = 0;
psTime->ui32Day = 0;
psTime->ui32Hour = 0;
psTime->ui32Minute = 0;
psTime->ui32Second = 0;
psTime->ui32MS = 0;
//
// Years.
//
if ( ui64MS >= 31536000000 )
{
//
// Fill in the structure.
//
psTime->ui32Year = (ui64MS / 31536000000);
//
// Subtract from ui64MS.
//
ui64MS -= (psTime->ui32Year * 31536000000);
}
//
// Months.
//
if ( ui64MS >= 2592000000 )
{
//
// Fill in the structure.
//
psTime->ui32Month = (ui64MS / 2592000000);
//
// Subtract from ui64MS.
//
ui64MS -= (psTime->ui32Month * 2592000000);
}
//
// Days.
//
if ( ui64MS >= 86400000 )
{
//
// Fill in the structure.
//
psTime->ui32Day = (ui64MS / 86400000);
//
// Subtract from ui64MS.
//
ui64MS -= (psTime->ui32Day * 86400000);
}
//
// Hours.
//
if ( ui64MS >= 3600000 )
{
//
// Fill in the structure.
//
psTime->ui32Hour = (ui64MS / 3600000);
//
// Subtract from ui64MS.
//
ui64MS -= (psTime->ui32Hour * 3600000);
}
//
// Minutes.
//
if ( ui64MS >= 60000 )
{
//
// Fill in the structure.
//
psTime->ui32Minute = (ui64MS / 60000);
//
// Subtract from ui64MS.
//
ui64MS -= (psTime->ui32Minute * 60000);
}
//
// Seconds.
//
if ( ui64MS >= 1000 )
{
//
// Fill in the structure.
//
psTime->ui32Second = (ui64MS / 1000);
//
// Subtract from ui64MS.
//
ui64MS -= (psTime->ui32Second * 1000);
}
//
// Milliseconds.
//
//
// Fill in the structure.
//
psTime->ui32MS = ui64MS;
}