main.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #include <stdio.h>
  2. #include "hardware/gpio.h"
  3. #include "pico/stdlib.h"
  4. #include "tinykernel/kernel.hpp"
  5. #include "tinykernel/actor.hpp"
  6. #include "pico/multicore.h"
  7. // Atomic printf helper: stdio is shared, so wrap each call with the kernel lock.
  8. #define P(...) \
  9. do { \
  10. bool _lk = tk::lock(); \
  11. printf("[C%u] ", (unsigned)get_core_num()); \
  12. printf(__VA_ARGS__); \
  13. tk::unlock(_lk); \
  14. } while (0)
  15. // ---------------------------------------------------------------------------
  16. // Actor demonstration: Receiver Actor waiting for messages.
  17. // It consumes 0 CPU cycles while waiting, and immediately wakes up on post().
  18. enum AppMsgId : uint32_t {
  19. MSG_LED_TOGGLE = 1,
  20. MSG_PING = 2,
  21. };
  22. class LedActor : public tk::Actor<8> {
  23. protected:
  24. void on_message(const tk::Message& msg) override {
  25. if (msg.id == MSG_LED_TOGGLE) {
  26. bool state = !gpio_get(25);
  27. gpio_put(25, state);
  28. P("LedActor: received MSG_LED_TOGGLE (seq=%lu) -> LED %s, now=%llu ms\n",
  29. (unsigned long)msg.arg, state ? "ON" : "OFF", (unsigned long long)tk::now_ms());
  30. } else {
  31. P("LedActor: received unknown msg id=%lu, arg=%lu\n",
  32. (unsigned long)msg.id, (unsigned long)msg.arg);
  33. }
  34. }
  35. };
  36. static LedActor g_led_actor;
  37. // Sender task: periodically posts messages to LedActor
  38. static void task_sender(void* arg) {
  39. (void)arg;
  40. uint32_t seq = 0;
  41. for (;;) {
  42. tk::sleep_ms(500);
  43. P("Sender: posting MSG_LED_TOGGLE seq=%lu...\n", (unsigned long)seq);
  44. g_led_actor.post(MSG_LED_TOGGLE, seq++);
  45. }
  46. }
  47. // ---------------------------------------------------------------------------
  48. // Static task: FPU self-check. Runs the same deterministic float/double sequence
  49. // twice; any corruption of FPU state across context switches shows up as a
  50. // mismatch or a drifting result.
  51. static float fpu_run_f(float v) {
  52. for (int i = 0; i < 10000; ++i) v = v * 1.000001f + 0.5f;
  53. return v;
  54. }
  55. static double fpu_run_d(double v) {
  56. for (long i = 0; i < 10000; ++i) v = v * 1.0000000002 + 0.5;
  57. return v;
  58. }
  59. void task_fpu(void* arg) {
  60. (void)arg;
  61. for (;;) {
  62. float fa = fpu_run_f(1.5f);
  63. float fb = fpu_run_f(1.5f);
  64. double da = fpu_run_d(2.5);
  65. double db = fpu_run_d(2.5);
  66. if (fa != fb || da != db) {
  67. P("FPU FAIL f: %8.6f vs %8.6f d: %.9f vs %.9f\n", (double)fa, (double)fb, da, db);
  68. } else {
  69. P("FPU ok f: %8.6f d: %.9f\n", (double)fa, da);
  70. }
  71. tk::sleep_ms(300);
  72. }
  73. }
  74. TK_TASK(task_fpu, tk::PRIO_NORMAL)
  75. // ---------------------------------------------------------------------------
  76. // Dynamic task (higher priority): busy-spins for a few ms to demonstrate that it
  77. // preempts the lower-priority tasks immediately.
  78. static void task_hi(void* arg) {
  79. (void)arg;
  80. for (;;) {
  81. volatile uint32_t n = 0;
  82. while (n < 500000u) ++n;
  83. P("HI prio=%u busy-spin done, now=%llu ms\n", (unsigned)tk::PRIO_HIGH, (unsigned long long)tk::now_ms());
  84. tk::sleep_ms(700);
  85. }
  86. }
  87. // ---------------------------------------------------------------------------
  88. // Dynamic task: prints a few times and then exits.
  89. static void task_dying(void* arg) {
  90. (void)arg;
  91. for (int i = 1; i <= 3; ++i) {
  92. P("DYING %d (now=%llu ms)\n", i, (unsigned long long)tk::now_ms());
  93. tk::sleep_ms(250);
  94. }
  95. P("DYING calling tk::exit()\n");
  96. tk::exit(0);
  97. }
  98. // ---------------------------------------------------------------------------
  99. // Dynamic task: plain periodic printer at normal priority.
  100. static void task_print(void* arg) {
  101. (void)arg;
  102. uint32_t count = 0;
  103. for (;;) {
  104. P("A #%lu now=%llu ms\n", (unsigned long)count++, (unsigned long long)tk::now_ms());
  105. tk::sleep_ms(150);
  106. }
  107. }
  108. int main() {
  109. stdio_init_all();
  110. gpio_init(25);
  111. gpio_set_dir(25, GPIO_OUT);
  112. printf("tinykernel: registering dynamic tasks...\n");
  113. g_led_actor.start(tk::PRIO_NORMAL);
  114. auto h_sender = tk::create(task_sender, nullptr, tk::PRIO_NORMAL);
  115. auto h_hi = tk::create(task_hi, nullptr, tk::PRIO_HIGH);
  116. auto h_dy = tk::create(task_dying, nullptr, tk::PRIO_LOW);
  117. auto h_pr = tk::create(task_print, nullptr, tk::PRIO_NORMAL);
  118. if (!g_led_actor.handle().is_valid() || !h_sender.is_valid() || !h_hi.is_valid() || !h_dy.is_valid() || !h_pr.is_valid()) {
  119. printf("tinykernel: task creation FAILED\n");
  120. for (;;) {
  121. }
  122. }
  123. printf("tinykernel: starting scheduler (preemptive, dual-core M33)\n");
  124. tk::start(); // never returns
  125. }