ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Воскресенье
24 ноября
1046514 Топик полностью
evgeniy1294 (23.10.2020 15:57, просмотров: 698) ответил Peter_M на Подскажите как сейчас правильно организовать в микроконтроллере программные таймеры?
Мое решение выглядит следующим образом: 

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();
}
}
}