завел такое вот интересное - С++20 Coroutine на К1948ВК018,
прикольно. оно работает. можно делать несложне шедуллеры не выбегая
за средства языка. миленько компактненько простенько. Для
контроллеров с озу 2к флеша 8к просто огонь! https://downloads.ctfassets.net/oxjq45e8ilak/4kVoTkxYBiVM9lPBZiG2HO/a5e36dc80fd898885269bd6320c96196/Pavel_Novikov_Uchimsya_gotovit_C_korutiny_na_praktike_2020_06_28_18_49_49.pdf
https://en.cppreference.com/w/cpp/language/coroutines
https://lewissbaker.github.io/2022/08/27/understanding-the-compiler-transform
https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html
https://habr.com/ru/articles/519464/
https://habr.com/ru/articles/798935/
https://habr.com/ru/articles/687266/
класс hrono - взводить асинхронные события для корутин по таймауту системного таймера в прерывании
/*
 * hrono.h
 *
 *  Created on: 01 jrn. 2024 г.
 *      Author: klen
 */
#pragma once
template < typename handlers_t, uint32_t timeout = 1000 >
struct hrono_tt
{
  __ai__ hrono_tt()
    {
	  disable();
	  scr1_tim.time = 0 ;
	  scr1_tim.compare = timeout ;  //  1 КГц
	  scr1_tim.prescaler = 32-1 ;
	  mchine_timer_interrupt_enable();
      enable();
    }
  __ai__ auto mtime() noexcept { return scr1_tim.time ; }
  __ai__ void generate_event_if()
    {
	  if ( clock_ms%1000 == 0 ) handlers_t::event0_set(); // срабатывание события task0 с периодом 1 сек
	  if ( clock_ms%2700 == 0 ) handlers_t::event1_set(); // срабатывание события task1 с периодом 2,7 сек
	  if ( clock_ms%3300 == 0 ) handlers_t::event2_set(); // срабатывание события task2 с периодом 3,3 сек
	  if ( clock_ms%4000 == 0 ) handlers_t::event3_set(); // срабатывание события task3 с периодом 4 сек
    }
  __ai__ void enable()  { scr1_tim.enable(); }
  __ai__ void disable() { scr1_tim.disable(); }
  __ai__ void timeout_handler()
     {
	   scr1_tim.compare += timeout ;
	   clock_ms++ ;
	   generate_event_if();
     }
  volatile size_t clock_ms ;
} ;
 
класс "задач",  реализованных по средством корутин и шедуллер который передает им управлние по карусели.  каждая задача имеет объект синхронизации - асинхронное событие, по взводу его задачка выполняет одн цикл работы
/*
 * co_task.h
 *
 *  Created on: 17 сен. 2024 г.
 *      Author: klen
 */
#pragma once
#include <coroutine>
class co_event_t
{
     using co_handle_t = std::coroutine_handle<>;
     struct awaiter
       {
	     co_event_t& event;
         co_handle_t handle = nullptr;
         __ai__ awaiter(co_event_t& val) noexcept : event(val) {}
         __ai__ bool await_ready() const noexcept { return event.state(); }
         __ai__ bool await_resume() noexcept { return event.reset(); }
         // регистрация потребителя события
         __ai__ void await_suspend(co_handle_t val) noexcept
           {
             handle = val;
             event.set_awaiter(this);
           }
       };
  public:
    // yстановка (активации) события
    __ai__ void set() noexcept { s = true; }
    // сброс события, возврат значения перед сбросом
    __ai__ bool reset () noexcept { auto const rv = s ;  s = false; return rv;}
    // проверка состояния события
    __ai__ bool state() const noexcept { return s; }
    // Метод регистрации потребителя
    // Можно реализовать целый список потребителей
    __ai__ void set_awaiter(awaiter* val) noexcept { consumer = val; }
    // Перегруженный оператор co_await, так как тип event не является awaitable
    // Подробное разъяснение необходимости этой перегрузки можно найти в лекции Владимирова
    __ai__ awaiter operator co_await() noexcept { return awaiter(*this); }
  private:
    awaiter* consumer = nullptr;
    bool s = false;
};
struct co_task_t
{
    struct promise_type
       {
          using co_handle_t = std::coroutine_handle<promise_type>;
          __ai__ auto get_return_object() noexcept { return co_handle_t::from_promise(*this); }
          // Изначально задача остановлена
          // Хотя можно вернуть suspend_never, чтобы дать возможность проинициализировать всё необходимое
          __ais__ auto initial_suspend() noexcept { return std::suspend_always(); }
          // Задача будет содержать бесконечный цикл, поэтому в этот метод попадать не должны
          __ais__ auto final_suspend() noexcept { return std::suspend_never(); }
          // ensure the use of non-throwing operator-new
          __ais__ co_task_t get_return_object_on_allocation_failure()
          {
             __throw_memmgr_error("отказ выделения памятя для объекта co_task_t");
          }
          __ais__ void return_void()  {}
          __ais__ void unhandled_exception() {}
          // custom non-throwing overload of new
          void* operator new(std::size_t n) noexcept
            {
                 if (void* mem = malloc(n)) return mem;
                 return nullptr; // allocation failure
            }
       };
    using co_handle_t = promise_type::co_handle_t;
    __ai__ co_task_t(co_handle_t handle) noexcept : handle(handle) {}
    // Деструктор можно не определять, так как он ни разу не будет вызван
    __ai__ void resume()  const noexcept { handle.resume(); }
    __ai__ void destroy() const noexcept { handle.destroy(); }
    private:
       co_handle_t handle;
};
struct co_scheduller_t
{
  template <typename T>
  __ains__ void start(const T& tasks) noexcept
    {
       for(;;) [[likely]] { for (auto& task : tasks) [[likely]] task.resume(); }
    }
};
 заголовок в котором  определены события
/*
 * apptypes.h
 *
 *  Created on: 17.09.2024
 *      Author: klen
 */
#pragma once
#include "platform_config.h"
#include "co_task.h"
#ifdef __CRT_IMPL__
  co_event_t event0 ;
  co_event_t event1 ;
  co_event_t event2 ;
  co_event_t event3 ;
#else
  extern co_event_t event0 ;
  extern co_event_t event1 ;
  extern co_event_t event2 ;
  extern co_event_t event3 ;
#endif
struct event_handlers_t
{
  static void event0_set() noexcept { event0.set(); }
  static void event1_set() noexcept { event1.set(); }
  static void event2_set() noexcept { event2.set(); }
  static void event3_set() noexcept { event3.set(); }
};
#include "hrono.h"
using hrono_t = hrono_tt<event_handlers_t> ;
#ifdef __CRT_IMPL__
   hrono_t hrono;
#else
   extern hrono_t hrono;
#endif
 
код приложения
#include "appdefs.h"
co_task_t task0()
{
  cout < cout_t::vt100_t::yellow <= " task0->begin\n" ;
  size_t ev_counter=0 ;
  mem_info();
  for(;;)
      {
  	     if ( !co_await event0)  continue ;
  	     cout < cout_t::vt100_t::white < hrono.clock_ms < cout_t::vt100_t::yellow <= " task0->ev #" <  ev_counter++ <= "\n" ;
      }
}
co_task_t task1()
{
  cout < cout_t::vt100_t::uv <= " task1->begin\n" ;
  size_t ev_counter=0 ;
  for(;;)
      {
  	     if ( !co_await event1)  continue ;
  	     cout < cout_t::vt100_t::white < hrono.clock_ms < cout_t::vt100_t::uv <= "\t task1->ev #" <  ev_counter++ <= "\n" ;
  	     //if (ev_counter==2)  mem_info();
      }
}
co_task_t task2()
{
  cout < cout_t::vt100_t::ocean <= " task2->begin\n" ;
  size_t ev_counter=0 ;
  for(;;)
      {
  	     if ( !co_await event2)  continue ;
  	     {
  	       cout < cout_t::vt100_t::white < hrono.clock_ms < cout_t::vt100_t::ocean <= "\t\t task2->ev #" <  ev_counter++ <= "\n" ;
  	       //mem_info();
  	     }
      }
}
co_task_t task3()
{
  cout < cout_t::vt100_t::ocean <= " task3->begin\n" ;
  size_t ev_counter=0 ;
  for(;;)
      {
  	     if ( !co_await event3)  continue ;
  	     cout < cout_t::vt100_t::white < hrono.clock_ms < cout_t::vt100_t::green <= "\t\t\t task3->ev #" <  ev_counter++ <= "\n" ;
      }
}
int main()
{
   const auto tasks = {task0() , task1(), task2(),task3()};
   co_scheduller_t::start(tasks);
}
namespace mik32v2
{
  __attribute__((aligned(4),interrupt,used,section(".trap"))) void trap_handler() noexcept
    {
       push_regs();
       if (epic.dma_status())
    	   cout.async_write_event_handler();
       else
           hrono.timeout_handler();
       pop_regs();
    }
  void __init(){}
  void __premain()
    {
	  cout.reserve(1024-sizeof(o1heap::fragment_t)) < cout_t::vt100_t::clear <= cout_t::vt100_t::cursor_left_up ;
	  print_startup_hellow();
    }
  pm_t::sys_init_status_t sys_init_status = { status_t::undefined, status_t::undefined, status_t::undefined, status_t::undefined };
  constexpr pm_t::system_init_profile_t system_init_profile =
  	{
  	  .oscs= pm_t::system_init_profile_t::oscs_t::all ,
      .system_clock_source=    pm_t::system_init_profile_t::oscs_t::hse,
  	  .system_clock_source_force=  pm_t::ahb_clock_mux_t::force_t::unfixed,
  	  .freq_monitor_clock_source= pm_t::system_init_profile_t::freq_monitor_clock_source_t::auto_select,
  	  .ahb_div=  1,
      .apb_m_div=1,
  	  .apb_p_div=1,
  	  .hsi_cal=128,
  	  .lsi_cal=128,
  	  .rtc_clock_source=      pm_t::system_init_profile_t::rtc_clock_source_t::auto_select,
  	  .cpu_rtc_clock_source = pm_t::system_init_profile_t::cpu_rtc_clock_source_t::lse
  	};
}
 
результат работы - имеем 4 задачи,  объекты синхронизации которых взводятся с периодом 1, 2,7  3,3  и 4 секунды соответсвенно, по событию задачки выводят в консоль время в миллисикундах  и количество сработок.

зашло в моск не спервого раза, тяжело .. но потом начинаешь ощущать глубину мысли кратеров стандарта C++
мне кажется это завбавным...