Мое решение выглядит следующим образом:
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();
}
}
}