ping-pong-await.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // SPDX-License-Identifier: MIT
  2. // SPDX-FileCopyrightText: 2022 Ivan Baidakou
  3. #include "common.h"
  4. #include <avr/interrupt.h>
  5. #include <avr/io.h>
  6. #include <avr/sleep.h>
  7. #include <avr/wdt.h>
  8. #include <rotor-light.hpp>
  9. #include <util/delay.h>
  10. #define LED PB5
  11. namespace rl = rotor_light;
  12. namespace message {
  13. struct Ping : rl::Message {
  14. using Message::Message;
  15. static constexpr auto type_id = __LINE__;
  16. rl::MessageTypeId get_type_id() const override { return type_id; }
  17. };
  18. struct Pong : rl::Message {
  19. using Message::Message;
  20. static constexpr auto type_id = __LINE__;
  21. rl::MessageTypeId get_type_id() const override { return type_id; }
  22. };
  23. } // namespace message
  24. struct Pinger : rl::Actor<2> {
  25. using Parent = Actor<2>;
  26. void initialize() override {
  27. subscribe(&Pinger::on_pong);
  28. Parent::initialize();
  29. }
  30. void advance_start() override {
  31. Parent::advance_start();
  32. ping();
  33. }
  34. void ping() {
  35. /* toggle led */
  36. PORTB ^= (1 << LED);
  37. send<message::Ping>(0, ponger_id);
  38. }
  39. void on_pong(message::Pong &) {
  40. add_event(
  41. 500, [](void *data) { static_cast<Pinger *>(data)->ping(); }, this);
  42. }
  43. rl::ActorId ponger_id;
  44. };
  45. struct Ponger : rl::Actor<2> {
  46. using Parent = Actor<2>;
  47. void initialize() override {
  48. subscribe(&Ponger::on_ping);
  49. Parent::initialize();
  50. }
  51. void on_ping(message::Ping &) {
  52. //
  53. send<message::Pong>(0, pinger_id);
  54. }
  55. rl::ActorId pinger_id;
  56. };
  57. using Supervisor =
  58. rl::Supervisor<rl::SupervisorBase::min_handlers_amount, Pinger, Ponger>;
  59. using Storage = rl::traits::MessageStorage<rl::message::ChangeState,
  60. rl::message::ChangeStateAck,
  61. message::Ping, message::Pong>;
  62. using Queue = rl::Queue<Storage, 5>; /* upto 5 messages in 1 queue */
  63. using Planner = rl::Planner<1>; /* upto 1 time event */
  64. static void app_hw_init();
  65. static void perform_sleep(const rl::TimePoint &until);
  66. int main(int, char **) {
  67. app_hw_init();
  68. /* allocate */
  69. Queue queue;
  70. Planner planner;
  71. rl::Context context{&queue, &planner, &get_now};
  72. Supervisor sup;
  73. /* setup */
  74. sup.bind(context);
  75. auto pinger = sup.get_child<0>();
  76. auto ponger = sup.get_child<1>();
  77. pinger->ponger_id = ponger->get_id();
  78. ponger->pinger_id = pinger->get_id();
  79. /* disable poll timer */
  80. sup.start(false);
  81. /* main cycle */
  82. while (true) {
  83. sup.process();
  84. auto next_event_time = planner.next_event();
  85. if (next_event_time) {
  86. perform_sleep(next_event_time);
  87. }
  88. }
  89. return 0;
  90. }
  91. uint8_t mcusr_mirror __attribute__((section(".noinit")));
  92. void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3")));
  93. void get_mcusr(void) {
  94. mcusr_mirror = MCUSR;
  95. MCUSR = 0;
  96. wdt_disable();
  97. }
  98. void perform_sleep(const rl::TimePoint &until) {
  99. auto now = get_now();
  100. auto left = until - now;
  101. auto amount = rl::Duration{};
  102. auto sleep_constant = uint8_t{};
  103. cli();
  104. disable_timer();
  105. while (left > 0) {
  106. cli();
  107. if (left < 15) {
  108. break;
  109. } else if (left < 30) {
  110. amount = 15;
  111. sleep_constant = WDTO_15MS;
  112. } else if (left < 60) {
  113. amount = 30;
  114. sleep_constant = WDTO_30MS;
  115. } else if (left < 120) {
  116. amount = 60;
  117. sleep_constant = WDTO_60MS;
  118. } else if (left < 250) {
  119. amount = 120;
  120. sleep_constant = WDTO_120MS;
  121. } else if (left < 500) {
  122. amount = 250;
  123. sleep_constant = WDTO_250MS;
  124. } else if (left < 1000) {
  125. amount = 500;
  126. sleep_constant = WDTO_500MS;
  127. } else if (left < 2000) {
  128. amount = 1000;
  129. sleep_constant = WDTO_1S;
  130. } else if (left < 4000) {
  131. amount = 2000;
  132. sleep_constant = WDTO_2S;
  133. } else if (left < 8000) {
  134. amount = 4000;
  135. sleep_constant = WDTO_4S;
  136. } else {
  137. amount = 8000;
  138. sleep_constant = WDTO_8S;
  139. }
  140. TCNT0 = 0;
  141. adjust_timer(amount * 1000);
  142. wdt_reset();
  143. wdt_enable(sleep_constant);
  144. // enable watchdog interrupt
  145. WDTCSR |= (1 << WDIE);
  146. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  147. sleep_enable();
  148. sei();
  149. sleep_cpu();
  150. sleep_disable();
  151. sei();
  152. left -= amount;
  153. }
  154. enable_timer();
  155. sei();
  156. }
  157. static void app_hw_init() {
  158. // We will now disable the watchdog.
  159. // Service the watchdog just to be sure to avoid pending timeout.
  160. wdt_reset();
  161. // Clear WDRF in MCUSR.
  162. MCUSR &= ~(1U << WDRF);
  163. // Write logical one to WDCE and WDE.
  164. // Keep the old prescaler setting to prevent unintentional time-out.
  165. WDTCSR |= (1U << WDCE) | (1U << WDE);
  166. // Turn off the WDT.
  167. WDTCSR = 0x00;
  168. // We will now initialize PORTB.5 to be used as an LED driver port.
  169. // Set PORTB.5 value to low.
  170. PORTB &= ~(1U << PORTB5);
  171. // Set PORTB.5 direction to output.
  172. DDRB |= (1U << DDB5);
  173. enable_timer();
  174. // Enable all interrupts.
  175. sei();
  176. }
  177. ISR(WDT_vect) {
  178. // WDIE & WDIF is cleared in hardware upon entering this ISR
  179. wdt_disable();
  180. }