Мое решение выглядит следующим образом:
1) Есть универсальная обертка, реализующая функционал таймера
template < class Source >
class Timer
{
public:
void Start(std::uint32_t interval)
{
this->timestamp = Source::GetTick();
this->interval = interval;
this->active = true;
}
void Stop() { active = false; }
void Reload()
{
this->timestamp = Source::GetTick();
}
bool IsTimeOut()
{
return (((Source::GetTick() - this->timestamp) > this->interval) && this->active);
};
void Delay(std::uint32_t tick)
{
std::uint32_t start = Source::GetTick();
while((Source::GetTick() - start) < tick);
}
private:
bool active = false;
std::uint32_t interval = 0u;
std::uint32_t timestamp = 0u;
};
Шаблонный класс, использующий Source в качестве источника времени. От Source требуется только наличие функции GetTick(), пример реализации:
namespace mpp::core {
namespace __private { std::uint32_t GetTick(); void IncTick(); } // namespace __private
inline namespace cortex_m {
template< class ClockSystem >
class Systick {
public:
constexpr static std::uint32_t TickPerSec = 1000u;
inline static void Init() { SysTick_Config(ClockSystem::kSysTickClkHz / 1000u); }
inline static std::uint32_t GetTick() { return __private::GetTick(); }
static void Interrupt() { __private::IncTick(); }
};
template< class ClockSystem >
class ClockCounter {
public:
constexpr static std::uint32_t TickPerSec = ClockSystem::kSysClkHz;
inline static void Init() {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
inline static std::uint32_t GetTick() {
DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
std::uint32_t tmp = DWT->CYCCNT;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
return tmp;
}
};
}// inline namespace
}
Как видно из кода, это реализация работы с системным таймером и счетчиком тактов ядра Cortex-Mx, универсальная логика. Текущее системное время хранится в отдельном срр-файле:
namespace mpp::core::__private {
volatile std::uint32_t systime = 0u;
std::uint32_t GetTick() { return systime; }
void IncTick() { systime++; }
}
Это была библиотечная часть. Использовать её в своём коде просто:
// Где-нибудь в заголовочнике добавляем специализацию для системного таймера и dwt
using Systick = mpp::core::Systick < PllConfig >; using ClockCounter = mpp::core::ClockCounter < PllConfig >;
// Уже в срр-файле создаем экземпляры таймеров. Экземляр таймера нужно создавать для того, чтобы хранить уникальный контекст
// для каждого таймера (период срабатывания, запущен/остановлен, временная метка запуска mpp::utils::Timer< Systick > tim; mpp::utils::Timer< ClockCounter > dwt;
// Непосредственное использование, мигание светодиодом
int main() {
// Устанавливаем период в 100 тиков источника (1 тик = 1мс для системного таймера, есть constexpr-функции перевода мс/сек в тики)
tim.Start(100);
while(1){
if (tim.IsTimeOut())
{
tim.Reload();
Leds::Toggle();
}
}
}