main.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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 "tinykernel/sync.hpp"
  7. #include "pico/multicore.h"
  8. // Atomic printf helper: stdio is shared, so wrap each call with the kernel lock.
  9. #define P(...) \
  10. do { \
  11. bool _lk = tk::lock(); \
  12. printf("[C%u] ", (unsigned)get_core_num()); \
  13. printf(__VA_ARGS__); \
  14. tk::unlock(_lk); \
  15. } while (0)
  16. // ---------------------------------------------------------------------------
  17. // Actor demonstration: Receiver Actor waiting for messages.
  18. // It consumes 0 CPU cycles while waiting, and immediately wakes up on post().
  19. enum AppMsgId : uint32_t {
  20. MSG_LED_TOGGLE = 1,
  21. MSG_PING = 2,
  22. };
  23. class LedActor : public tk::Actor<8> {
  24. protected:
  25. void on_start() override {
  26. // Hardware initialization within task context
  27. gpio_init(25);
  28. gpio_set_dir(25, GPIO_OUT);
  29. gpio_put(25, 0);
  30. P("LedActor: initialized GPIO 25 in on_start()\n");
  31. }
  32. void on_message(const tk::Message& msg) override {
  33. if (msg.id == MSG_LED_TOGGLE) {
  34. bool state = !gpio_get(25);
  35. gpio_put(25, state);
  36. P("LedActor: received MSG_LED_TOGGLE (seq=%lu) -> LED %s, now=%llu ms\n",
  37. (unsigned long)msg.arg, state ? "ON" : "OFF", (unsigned long long)tk::now_ms());
  38. } else {
  39. P("LedActor: received unknown msg id=%lu, arg=%lu\n",
  40. (unsigned long)msg.id, (unsigned long)msg.arg);
  41. }
  42. }
  43. };
  44. static LedActor g_led_actor;
  45. // Sender task: periodically posts messages to LedActor
  46. static void task_sender(void* arg) {
  47. (void)arg;
  48. uint32_t seq = 0;
  49. for (;;) {
  50. tk::sleep_ms(600);
  51. P("Sender: posting MSG_LED_TOGGLE seq=%lu...\n", (unsigned long)seq);
  52. g_led_actor.post(MSG_LED_TOGGLE, seq++);
  53. }
  54. }
  55. // ---------------------------------------------------------------------------
  56. // Mutex demonstration: Two tasks contend for a shared counter protected by Mutex
  57. static tk::Mutex g_counter_mutex;
  58. static uint32_t g_shared_counter = 0;
  59. static void task_mutex_worker(void* arg) {
  60. const char* tag = static_cast<const char*>(arg);
  61. for (;;) {
  62. {
  63. tk::LockGuard<tk::Mutex> guard(g_counter_mutex);
  64. g_shared_counter++;
  65. P("[%s] holds mutex: counter=%lu\n", tag, (unsigned long)g_shared_counter);
  66. volatile uint32_t n = 0;
  67. while (n < 100000u) ++n;
  68. } // guard destructor automatically calls unlock()
  69. tk::sleep_ms(350);
  70. }
  71. }
  72. // ---------------------------------------------------------------------------
  73. // Semaphore demonstration: Signaler wakes up Waiter task
  74. static tk::BinarySemaphore g_demo_sem(0, 1); // Initially 0 (blocked)
  75. static void task_sem_waiter(void* arg) {
  76. (void)arg;
  77. for (;;) {
  78. g_demo_sem.acquire(); // Blocks until release() is called
  79. P("SemWaiter: >>> WOKEN UP by semaphore! now=%llu ms\n", (unsigned long long)tk::now_ms());
  80. }
  81. }
  82. static void task_sem_signaler(void* arg) {
  83. (void)arg;
  84. for (;;) {
  85. tk::sleep_ms(1000);
  86. P("SemSignaler: releasing semaphore...\n");
  87. g_demo_sem.release();
  88. }
  89. }
  90. // ---------------------------------------------------------------------------
  91. // Static task: FPU self-check. Runs the same deterministic float/double sequence
  92. // twice; any corruption of FPU state across context switches shows up as a
  93. // mismatch or a drifting result.
  94. static float fpu_run_f(float v) {
  95. for (int i = 0; i < 10000; ++i) v = v * 1.000001f + 0.5f;
  96. return v;
  97. }
  98. static double fpu_run_d(double v) {
  99. for (long i = 0; i < 10000; ++i) v = v * 1.0000000002 + 0.5;
  100. return v;
  101. }
  102. void task_fpu(void* arg) {
  103. (void)arg;
  104. for (;;) {
  105. float fa = fpu_run_f(1.5f);
  106. float fb = fpu_run_f(1.5f);
  107. double da = fpu_run_d(2.5);
  108. double db = fpu_run_d(2.5);
  109. if (fa != fb || da != db) {
  110. P("FPU FAIL f: %8.6f vs %8.6f d: %.9f vs %.9f\n", (double)fa, (double)fb, da, db);
  111. } else {
  112. P("FPU ok f: %8.6f d: %.9f\n", (double)fa, da);
  113. }
  114. tk::sleep_ms(500);
  115. }
  116. }
  117. TK_TASK(task_fpu, tk::PRIO_NORMAL)
  118. int main() {
  119. stdio_init_all();
  120. printf("tinykernel: registering dynamic tasks...\n");
  121. g_led_actor.start(tk::PRIO_NORMAL);
  122. auto h_sender = tk::create(task_sender, nullptr, tk::PRIO_NORMAL);
  123. auto h_mtx_a = tk::create(task_mutex_worker, const_cast<char*>("Mtx-A"), tk::PRIO_NORMAL);
  124. auto h_mtx_b = tk::create(task_mutex_worker, const_cast<char*>("Mtx-B"), tk::PRIO_NORMAL);
  125. auto h_waiter = tk::create(task_sem_waiter, nullptr, tk::PRIO_HIGH);
  126. auto h_sig = tk::create(task_sem_signaler, nullptr, tk::PRIO_NORMAL);
  127. if (!g_led_actor.handle().is_valid() || !h_sender.is_valid() ||
  128. !h_mtx_a.is_valid() || !h_mtx_b.is_valid() ||
  129. !h_waiter.is_valid() || !h_sig.is_valid()) {
  130. printf("tinykernel: task creation FAILED\n");
  131. for (;;) {
  132. }
  133. }
  134. printf("tinykernel: starting scheduler (preemptive, dual-core M33)\n");
  135. tk::start(); // never returns
  136. }