Skip to content

Instantly share code, notes, and snippets.

@OrsoEric
Created August 2, 2020 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OrsoEric/b5dd79decad2e1cf883e47edc64d118a to your computer and use it in GitHub Desktop.
Save OrsoEric/b5dd79decad2e1cf883e47edc64d118a to your computer and use it in GitHub Desktop.
Longan Nano GD32VF103 Chrono Class
/**********************************************************************************
BSD 3-Clause License
Copyright (c) 2020, Orso Eric
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.
**********************************************************************************/
/**********************************************************************************
** ENVIROMENT VARIABILE
**********************************************************************************/
#ifndef LONGAN_NANO_CHRONO_H_
#define LONGAN_NANO_CHRONO_H_
/**********************************************************************************
** GLOBAL INCLUDES
**********************************************************************************/
#include <gd32vf103.h>
/**********************************************************************************
** DEFINES
**********************************************************************************/
/**********************************************************************************
** MACROS
**********************************************************************************/
/**********************************************************************************
** NAMESPACE
**********************************************************************************/
//! @namespace Longan_nano namespace encapsulating all related drivers and HAL
namespace Longan_nano
{
/**********************************************************************************
** TYPEDEFS
**********************************************************************************/
/**********************************************************************************
** PROTOTYPE: STRUCTURES
**********************************************************************************/
/**********************************************************************************
** PROTOTYPE: GLOBAL VARIABILES
**********************************************************************************/
/**********************************************************************************
** PROTOTYPE: CLASS
**********************************************************************************/
/************************************************************************************/
//! @class Chrono
/************************************************************************************/
//! @author Orso Eric
//! @version 2020-07-28
//! @brief Deals with busy delays, elapsed time and accumulated time
//! @bug None
//! @copyright BSD 3-Clause License Copyright (c) 2020, Orso Eric
//! @details
//! SysTick
//! Use 64b 27MHz SysTick timer.
//! History Version
//! 2020-07-20
//! Rework Utils library into Chrono library to add start/stop timer
//! "start" snaps the start timestamp at full resolution
//! "stop" snaps the stop timestamp at full resolution
//! "elapsed" returns the elapsed time between stop and start if valid. zero otherwise
//! 2020-07-28
//! I need a method to accumulate execution time and profile how long an activity has taken
//! "accumulate" add stop-start to an internal accumulator at full resolution
//! Add combined "stop" "elapsed" implementation with better performance and fewer calls needed
//! I can combine the stop and accumulator counters since i use one or the other
//! I use a flag to handle initialization and invalid when switching between modes and automatically reset the accumulator
/************************************************************************************/
class Chrono
{
//Visible to all
public:
//--------------------------------------------------------------------------
// ENUM
//--------------------------------------------------------------------------
//Configurations of the SysTick
typedef enum _Config
{
PEDANTIC_CHECKS = false, //Pedantic checks inside the functions
SYSTICK_INVALID = 0xFFFFFFFF, //Invalid timer value
SYSTICK_PRE = 4, //Prescaler for the systick timer
TIME_INVALID = -1, //Return time in case time is invalid
} Config;
//What time unit to use
typedef enum _Unit
{
milliseconds,
microseconds,
} Unit;
//--------------------------------------------------------------------------
// CONSTRUCTORS
//--------------------------------------------------------------------------
/***************************************************************************/
//! @brief Empty Constructor
//! Chrono | void
/***************************************************************************/
//! @return void
//! @details \n
//! Initialize timestamps to invalid
/***************************************************************************/
Chrono( void )
{
//Initialize timestamps to invalid
this -> g_systick_start = Config::SYSTICK_INVALID;
this -> g_systick_stop = Config::SYSTICK_INVALID;
//Regular timer mode
this -> g_f_accumulator_mode = false;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return;
} //End Constructor: Chrono | void
//--------------------------------------------------------------------------
// DESTRUCTORS
//--------------------------------------------------------------------------
/***************************************************************************/
//! @brief Empty Destructor
/***************************************************************************/
//! ~Chrono | void
//! @return void
/***************************************************************************/
~Chrono( void )
{
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return;
}
//--------------------------------------------------------------------------
// OPERATORS
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// SETTERS
//--------------------------------------------------------------------------
/***************************************************************************/
//! @brief public setter
//! start | void
/***************************************************************************/
//! @return void
//! @details \n
//! Start the timer
/***************************************************************************/
void start( void )
{
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//Snap timer start
this -> g_systick_start = get_timer_value();
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return;
} //End setter: start | void
/***************************************************************************/
//! @brief public setter
//! stop | void
/***************************************************************************/
//! @return void
//! @details \n
//! Stop the timer. Snap the stop time
/***************************************************************************/
void stop( void )
{
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//Snap timer start
this -> g_systick_stop = get_timer_value();
//Regular timer mode
this -> g_f_accumulator_mode = false;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return;
} //End setter: stop | void
/***************************************************************************/
//! @brief public setter
//! stop | Unit |
/***************************************************************************/
//! @param unit
//! @return void
//! @details \n
//! Stop the timer. Snap the stop time
//! Return the elapsed time between stop and start in the given unit
/***************************************************************************/
int32_t stop( Unit unit )
{
//----------------------------------------------------------------
// CHECK
//----------------------------------------------------------------
//Get start time
uint64_t start_tmp = this -> g_systick_start;
//If: bad timestamp
if ((Config::PEDANTIC_CHECKS == true) && (start_tmp == Config::SYSTICK_INVALID))
{
return Config::TIME_INVALID; //FAIL
}
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//Snap the timestamp
uint64_t stop_tmp = get_timer_value();
//Record the stop timestamp inside the timer
this -> g_systick_stop = stop_tmp;
//Regular timer mode
this -> g_f_accumulator_mode = false;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
//return elapsed time
return this -> compute_elapsed( start_tmp, stop_tmp, unit );
} //End setter: stop | Unit |
/***************************************************************************/
//! @brief public method
//! accumulate | void |
/***************************************************************************/
//! @return unsigned int | frequency of the SysTick timer
//! @details \n
//! Snap the stop time
//! Accumulate the difference between stop and start inside the accumulator
//! Swap the stop and start, invalidate the stop. Prepare for next cycle
//! Use stop counter as accumulator
//! If timer was in timer mode, reset the accumulator
/***************************************************************************/
bool accumulate( void )
{
//----------------------------------------------------------------
// CHECK
//----------------------------------------------------------------
//Get start time
uint64_t start_tmp = this -> g_systick_start;
//If: bad timestamp
if ((Config::PEDANTIC_CHECKS == true) && (start_tmp == Config::SYSTICK_INVALID))
{
return true; //FAIL
}
//Temp accumulator
uint64_t accumulator_tmp;
//if: Regular timer mode
if (this -> g_f_accumulator_mode == false)
{
//reset the accumulator
accumulator_tmp = 0;
//go into accumulator mode
this -> g_f_accumulator_mode = true;
}
//If: accumulator mode
else
{
//Fetch the current accumulator count
accumulator_tmp = this -> g_systick_stop;
}
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//Snap the timestamp
uint64_t stop_tmp = get_timer_value();
//Record the stop timestamp inside the start timestamp and invalidate the stop timestamp
this -> g_systick_start = stop_tmp;
//Accumulate the DeltaT inside the accumulator at full resolution
accumulator_tmp += stop_tmp -start_tmp;
//Store the accumulator value
this -> g_systick_stop = accumulator_tmp;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return false; //OK
} //End public method: accumulate | void |
/***************************************************************************/
//! @brief public method
//! accumulate | Unit unit |
/***************************************************************************/
//! @return unsigned int | frequency of the SysTick timer
//! @details \n
//! Snap the stop time
//! Accumulate the difference between stop and start inside the accumulator
//! Swap the stop and start, invalidate the stop. Prepare for next cycle
/***************************************************************************/
int32_t accumulate( Unit unit )
{
//----------------------------------------------------------------
// CHECK
//----------------------------------------------------------------
//Get start time
uint64_t start_tmp = this -> g_systick_start;
//If: bad timestamp
if ((Config::PEDANTIC_CHECKS == true) && (start_tmp == Config::SYSTICK_INVALID))
{
return true; //FAIL
}
//Temp accumulator
uint64_t accumulator_tmp;
//if: Regular timer mode
if (this -> g_f_accumulator_mode == false)
{
//reset the accumulator
accumulator_tmp = 0;
//go into accumulator mode
this -> g_f_accumulator_mode = true;
}
//If: accumulator mode
else
{
//Fetch the current accumulator count
accumulator_tmp = this -> g_systick_stop;
}
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//Snap the timestamp
uint64_t stop_tmp = get_timer_value();
//Record the stop timestamp inside the start timestamp and invalidate the stop timestamp
this -> g_systick_start = stop_tmp;
//Accumulate the DeltaT inside the accumulator at full resolution
accumulator_tmp += stop_tmp -start_tmp;
//Store the accumulator value
this -> g_systick_stop = accumulator_tmp;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return this -> compute_accumulator( accumulator_tmp, unit );
} //End public method: accumulate | void |
/***************************************************************************/
//! @brief public getter
//! get_elapsed | Unit |
/***************************************************************************/
//! @return int | elapsed milliseconds. negative mean the timer was uninitialized
//! @details \n
//! Time elapsed between start and stop
/***************************************************************************/
int32_t get_elapsed( Unit unit )
{
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//Fetch timestamps
uint64_t start_tmp = this -> g_systick_start;
uint64_t stop_tmp = this -> g_systick_stop;
//----------------------------------------------------------------
// CHECKS
//----------------------------------------------------------------
//If: a timetamp is invalid
if ((start_tmp == Config::SYSTICK_INVALID) || (stop_tmp == Config::SYSTICK_INVALID))
{
return Config::TIME_INVALID; //Invalid
}
//If: accumulator mode
if (this -> g_f_accumulator_mode == true)
{
return Config::TIME_INVALID; //Invalid
}
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
//return elapsed time
return compute_elapsed( start_tmp, start_tmp, unit );
} //End public getter: get_elapsed | Unit |
/***************************************************************************/
//! @brief public getter
//! get_accumulator | Unit |
/***************************************************************************/
//! @return int | accumulators. negative mean the timer was uninitialized
//! @details \n
//! return the DeltaT accumulated by the accumulate function in the given time unit
/***************************************************************************/
inline int32_t get_accumulator( Unit unit )
{
//----------------------------------------------------------------
// CHECK
//----------------------------------------------------------------
//If: accumulator mode
if (this -> g_f_accumulator_mode == false)
{
return Config::TIME_INVALID; //Invalid
}
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
//return accumulated time
return this -> compute_accumulator( this -> g_systick_stop, unit );
} //End public getter: get_elapsed | Unit |
/***************************************************************************/
//! @brief public static method
//! get_systick_freq | void
/***************************************************************************/
//! @return unsigned int | frequency of the SysTick timer
//! @details \n
//! The SysTick timer is tied to the CPU clock prescaled by four
/***************************************************************************/
static unsigned int get_systick_freq( void )
{
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return SystemCoreClock /Config::SYSTICK_PRE;
} //End static method: get_systick_freq | void
//--------------------------------------------------------------------------
// TESTERS
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// PUBLIC METHODS
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// PUBLIC STATIC METHODS
//--------------------------------------------------------------------------
/***************************************************************************/
//! @brief public static method
//! delay | Unit | unsigned int |
/***************************************************************************/
//! @param delay | unsigned int | how long to wait for in milliseconds
//! @return void |
//! @details \n
//! Use the SysTick timer counter to busy wait for the correct number of microseconds
//! The CPU SysTick timer is clocked by the ABH clock/4 = 27MHz
//! SystemCoreClock defines the frequency of the CPU in Hz
/***************************************************************************/
static bool delay( Unit unit, unsigned int delay_tmp )
{
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//Temp timestamp
uint64_t systick_tmp;
//Compute final timestamp
uint64_t systick_stop;
//Ticks required to count 1mS
uint32_t numticks = compute_tick_per_time_unit( unit );
//If: bad unit
if (numticks == 0)
{
return true; //fail
}
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
// Wait for the correct number of ticks
//Snap start
systick_stop = get_timer_value();
//Compute stop time.
systick_stop += numticks *delay_tmp;
//Wait an additional tick for current tick
systick_stop++;
//Busy wait for time to pass
do
{
//Snap timestamp
systick_tmp = get_timer_value();
}
while( systick_tmp < systick_stop );
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return false; //OK
} //End public static method: delay | Unit | unsigned int |
/***************************************************************************/
//! @brief public static method
//! delay | unsigned int |
/***************************************************************************/
//! @param delay | unsigned int | how long to wait for in milliseconds
//! @return void |
//! @details \n
//! wrapper of delay
//! its assumed that without arguments the user want a millisecond busy delay function
/***************************************************************************/
static inline bool delay( unsigned int delay_tmp )
{
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return delay( Longan_nano::Chrono::Unit::milliseconds, (unsigned int)delay_tmp );
} //End public static method: delay | unsigned int |
//--------------------------------------------------------------------------
// PUBLIC VARS
//--------------------------------------------------------------------------
//Visible only inside the class
private:
//--------------------------------------------------------------------------
// PRIVATE METHODS
//--------------------------------------------------------------------------
/***************************************************************************/
//! @brief private method
//! compute_tick_per_time_unit | Unit |
/***************************************************************************/
//! @return uint32_t | number of systick counts needed to count one time unit
//! @details
//! Compute the number of systick counts needed to count one time unit
/***************************************************************************/
static inline uint32_t compute_tick_per_time_unit( Unit unit )
{
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
// Compute del
//Switch: Time unit
switch( unit )
{
case Unit::milliseconds:
{
return SystemCoreClock /1000 /Config::SYSTICK_PRE;
break;
}
case Unit::microseconds:
{
return SystemCoreClock /1000000 /Config::SYSTICK_PRE;
break;
}
//Unhandled time unit
default:
{
return 0; //Invalid number of systick counts. Using it will yield infinite time
}
}; //End switch: Time unit
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return 0; //Invalid number of systick counts. Using it will yield infinite time
} //End private method: compute_tick_per_time_unit | Unit |
/***************************************************************************/
//! @brief private method
//! compute_elapsed | uint64_t | uint64_t | Unit |
/***************************************************************************/
//! @return int32_t | negative = invalid | zero or positive = elapsed time in the given time unit
//! @details \n
//! use start and stop timestamp to compute the elapsed time in a given time unit
/***************************************************************************/
inline int32_t compute_elapsed( uint64_t start, uint64_t stop, Unit unit )
{
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//If: causality violation
if ((Config::PEDANTIC_CHECKS == true) && (start > stop))
{
//Hopefully the timestamps are wrong and the universe still works as intended
return Config::TIME_INVALID; //FAIL
}
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//SysTick counts in one time unit
uint32_t numticks_time_unit = this -> compute_tick_per_time_unit( unit );
//If: bad unit was provided
if ((Config::PEDANTIC_CHECKS == true) && (numticks_time_unit == 0))
{
return TIME_INVALID;
}
//Compute DeltaT in system ticks as stop-start
uint64_t deltat = stop -start;
//Compute DeltaT in time units
deltat /= numticks_time_unit;
//Demote
int32_t ret = deltat;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return ret;
} //End private method: compute_elapsed | uint64_t | uint64_t | Unit |
/***************************************************************************/
//! @brief private method
//! compute_accumulator | uint64_t | Unit |
/***************************************************************************/
//! @return int32_t | negative = invalid | zero or positive = elapsed time in the given time unit
//! @details \n
//! use start and stop timestamp to compute the elapsed time in a given time unit
/***************************************************************************/
int32_t compute_accumulator( uint64_t accumulator, Unit unit )
{
//----------------------------------------------------------------
// VARS
//----------------------------------------------------------------
//If: accumulator
if ((Config::PEDANTIC_CHECKS == true) && (accumulator == Config::SYSTICK_INVALID))
{
return TIME_INVALID;
}
//----------------------------------------------------------------
// BODY
//----------------------------------------------------------------
//SysTick counts in one time unit
uint32_t numticks_time_unit = this -> compute_tick_per_time_unit( unit );
//If: bad unit was provided
if ((Config::PEDANTIC_CHECKS == true) && (numticks_time_unit == 0))
{
return TIME_INVALID;
}
//Compute DeltaT in time units
accumulator /= numticks_time_unit;
//Demote
int32_t ret = accumulator;
//----------------------------------------------------------------
// RETURN
//----------------------------------------------------------------
return ret;
} //End private method: compute_accumulator | uint64_t | Unit |
//--------------------------------------------------------------------------
// PRIVATE VARS
//--------------------------------------------------------------------------
//true = accumulator mode | false = regular start stop mode
bool g_f_accumulator_mode;
//Systick Timestamps
uint64_t g_systick_start;
//Combined stop and accumulator counter
uint64_t g_systick_stop;
}; //End Class: Chrono
/**********************************************************************************
** NAMESPACE
**********************************************************************************/
} //End Namespace: Longan_nano
#else
#warning "Multiple inclusion of hader file LONGAN_NANO_CHRONO_H_"
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment