From f7ae8d226b77190fd29cdc3b81ded615cdb80b5e Mon Sep 17 00:00:00 2001 From: hangpersonal Date: Mon, 12 Oct 2026 22:24:42 -0700 Subject: [PATCH] Add 07_multithread_rr and 08_thread_control_apis --- apps/07_multithread_rr/CMakeLists.txt | 7 + apps/07_multithread_rr/prj.conf | 12 ++ apps/07_multithread_rr/src/main.c | 66 +++++++ apps/08_thread_control_apis/CMakeLists.txt | 7 + apps/08_thread_control_apis/prj.conf | 12 ++ apps/08_thread_control_apis/src/main.c | 193 +++++++++++++++++++++ 6 files changed, 297 insertions(+) create mode 100644 apps/07_multithread_rr/CMakeLists.txt create mode 100644 apps/07_multithread_rr/prj.conf create mode 100644 apps/07_multithread_rr/src/main.c create mode 100644 apps/08_thread_control_apis/CMakeLists.txt create mode 100644 apps/08_thread_control_apis/prj.conf create mode 100644 apps/08_thread_control_apis/src/main.c diff --git a/apps/07_multithread_rr/CMakeLists.txt b/apps/07_multithread_rr/CMakeLists.txt new file mode 100644 index 0000000..26e0072 --- /dev/null +++ b/apps/07_multithread_rr/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(07_multithread_rr) + +target_sources(app PRIVATE src/main.c) \ No newline at end of file diff --git a/apps/07_multithread_rr/prj.conf b/apps/07_multithread_rr/prj.conf new file mode 100644 index 0000000..daa60c6 --- /dev/null +++ b/apps/07_multithread_rr/prj.conf @@ -0,0 +1,12 @@ +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_PRINTK=y + +# Enable zephyr timeslicing +CONFIG_TIMESLICING=y + +# Set the timeslice lenght in milliseconds +CONFIG_TIMESLICE_SIZE=10 + +# Timeslicing only applies to threads whose priority is numerically >= this value(e.g. 0) +CONFIG_TIMESLICE_PRIORITY=0 diff --git a/apps/07_multithread_rr/src/main.c b/apps/07_multithread_rr/src/main.c new file mode 100644 index 0000000..bc3913d --- /dev/null +++ b/apps/07_multithread_rr/src/main.c @@ -0,0 +1,66 @@ +#include +#include + +// Define stack size in bytes +#define THREAD_1_STACK_SZ 1024 +#define THREAD_2_STACK_SZ 1024 + +// Define priority, lower number = higher prio +#define THREAD_1_PRIORITY 5 +#define THREAD_2_PRIORITY 5 + +static void thread_1_function(void *p1, void *p2, void *p3) { + ARG_UNUSED(p1); + ARG_UNUSED(p1); + ARG_UNUSED(p1); + printk("\r\n---------------[RTOS THREAD] Project----------------\r\n"); + while(1) { + printk("Hello from Thread 1 \r\n"); + k_msleep(50); + } +} + +static void thread_2_function(void *p1, void *p2, void *p3) { + ARG_UNUSED(p1); + ARG_UNUSED(p1); + ARG_UNUSED(p1); + printk("\r\n---------------[RTOS THREAD] Project----------------\r\n"); + while(1) { + printk("Hello from Thread 2 \r\n"); + k_msleep(50); + } +} + +// Statically create the thread and auto-start +K_THREAD_DEFINE( + thread_1_tid, // Symbolic thread identifier + THREAD_1_STACK_SZ, + thread_1_function, + NULL, + NULL, + NULL, + THREAD_1_PRIORITY, + 0, + 0 // Start immediately at boot +); + +// Statically create the thread and auto-start +K_THREAD_DEFINE( + thread_2_tid, // Symbolic thread identifier + THREAD_2_STACK_SZ, + thread_2_function, + NULL, + NULL, + NULL, + THREAD_2_PRIORITY, + 0, + 0 // Start immediately at boot +); + +int main(void) { + printk("\r\n----- Main Function -----\r\n"); + while(1){ + k_sleep(K_FOREVER); + } + return 0; +} diff --git a/apps/08_thread_control_apis/CMakeLists.txt b/apps/08_thread_control_apis/CMakeLists.txt new file mode 100644 index 0000000..6e6b739 --- /dev/null +++ b/apps/08_thread_control_apis/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(08_thread_control_apis) + +target_sources(app PRIVATE src/main.c) \ No newline at end of file diff --git a/apps/08_thread_control_apis/prj.conf b/apps/08_thread_control_apis/prj.conf new file mode 100644 index 0000000..b6e6e6d --- /dev/null +++ b/apps/08_thread_control_apis/prj.conf @@ -0,0 +1,12 @@ +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_PRINTK=y + +# Enable zephyr timeslicing +CONFIG_TIMESLICING=y + +# Set the timeslice lenght in milliseconds +CONFIG_TIMESLICE_SIZE=10 + +# Timeslicing only applies to threads whose priority is numerically >= this value(e.g.0) +CONFIG_TIMESLICE_PRIORITY=0 diff --git a/apps/08_thread_control_apis/src/main.c b/apps/08_thread_control_apis/src/main.c new file mode 100644 index 0000000..ad588d8 --- /dev/null +++ b/apps/08_thread_control_apis/src/main.c @@ -0,0 +1,193 @@ +#include +#include + +/* Demonstrated APIs + * - k_thread_create() + K_THREAD_STACK_DEFINE() : create threads at runtime + * - k_thread_name_set() : name threads for clarity + * - k_yield() : voluntarily yield the CPU + * - k_msleep() / k_sleep() / k_wakeup() : sleep and wake threads + * - k_thread_suspend() / k_thread_resume() : stop and restart a thread + * - k_thread_priority_get() / k_thread_priority_set() : inspect/change priority + * - k_thread_abort() : permanently terminate a thread + * + * + * What happens at runtime + * - Creates two worker threads (A and B) at the *same priority* (to show round-robin). + * - A controller thread (slightly higher priority) performs timed actions: + * 1) yields (so you see initial fair sharing), + * 2) suspends B (A runs alone), + * 3) resumes B (sharing resumes), + * 4) boosts A's priority (A dominates; B runs only when A sleeps), + * 5) lowers A's priority (sharing resumes), + * 6) puts B to sleep forever then wakes it with k_wakeup(), + * 7) aborts B (permanently ends B). + * - Each action prints to UART so you can see the scheduler effects. + */ + +#define STACK_SZ 1024 +#define PRIO_BASE 5 + +// -------------------------- Thread A -------------------------- + +K_THREAD_STACK_DEFINE(stack_a, STACK_SZ); + +static struct k_thread thread_a; + +static void thread_a_entry(void *p1, void *p2, void *p3) { + + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + k_thread_name_set(k_current_get(), "A"); + + printk("\r\n[START] A: prio=%d (time-sliced peer)\r\n", + k_thread_priority_get(k_current_get())); + + while(1) { + printk("A: running...\r\n"); + k_msleep(20); + } +} + +// -------------------------- Thread B -------------------------- + +K_THREAD_STACK_DEFINE(stack_b, STACK_SZ); + +static struct k_thread thread_b; + +static void thread_b_entry(void *p1, void *p2, void *p3) { + + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + k_thread_name_set(k_current_get(), "B"); + + printk("\r\n[START] B: prio=%d (time-sliced peer)\r\n", + k_thread_priority_get(k_current_get())); + + while(1) { + printk("B: running...\r\n"); + k_msleep(20); + } +} + +// ---------------------- Controller Thread --------------------- + +K_THREAD_STACK_DEFINE(stack_ctl, STACK_SZ); + +static struct k_thread thread_ctl; + +static void print_prios(void) { + printk("[INFO] prio(A)=%d prio(B)=%d\r\n", + k_thread_priority_get(&thread_a), + k_thread_priority_get(&thread_b)); +} + +static void controller_entry(void *p1, void *p2, void *p3) { + + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + k_thread_name_set(k_current_get(), "CTL"); + + printk("[CTL] controller started (prio=%d)\r\n", + k_thread_priority_get(k_current_get())); + + // Initial yield so A & B get a turn immediately. + printk("[CTL] yield once to let A & B run\r\n"); + k_yield(); // voluntary yield + + // After 1s, suspend B + k_msleep(1000); + printk("[CTL] suspend B\r\n"); + k_thread_suspend(&thread_b); + k_msleep(100); + print_prios(); + + // Resume B + printk("[CTL] resume B\r\n"); + k_thread_resume(&thread_b); + k_msleep(100); + + // Boost A priority (A dominates, B only runs when A sleeps) + printk("[CTL] raise A priority (from %d to %d)\r\n", + k_thread_priority_get(&thread_a), 3); + k_thread_priority_set(&thread_a, 3); + print_prios(); + k_msleep(100); + + // Lower A priority back to baseline (sharing resumes) + printk("[CTL] lower A priority (to %d)\r\n", PRIO_BASE); + k_thread_priority_set(&thread_a, PRIO_BASE); + print_prios(); + k_msleep(100); + + // Put B to sleep "forever", then wake it with k_wakeup() + printk("[CTL] ask B to sleep forever; will wake it in 500 ms\r\n"); + k_thread_suspend(&thread_b); + k_msleep(500); + printk("[CTL] wake B (resume)\r\n"); + k_thread_resume(&thread_b); + k_msleep(100); + + // Abort B (permanent termination) + printk("[CTL] abort B\r\n"); + k_thread_abort(&thread_b); + k_msleep(100); + + // Sleep controller + printk("[CTL] A still running; B aborted.\r\n"); + + while(1) { + k_sleep(K_FOREVER); + } + +} + +static void start_thread(struct k_thread *tcb, + k_thread_stack_t *stack, + size_t stack_sz, + k_thread_entry_t entry, + int prio, + const char *name) { + + k_tid_t tid = k_thread_create( // create a new thread + tcb, // control block address + stack, // stack memory + stack_sz, // stack size + entry, // entry function + NULL, NULL, NULL, // parameters + prio, // thread priority + 0, // special options + K_NO_WAIT); // start immediately + + k_thread_name_set(tid, name); // assign a name +} + + +int main(void) { + + printk("\r\n[MAIN] Thread control API project starting...\r\n"); + + // Create worker A at base priority (time-sliced peer) + start_thread(&thread_a, stack_a, STACK_SZ, + thread_a_entry, PRIO_BASE, "A"); + + // Create worker B at the same priority (time-sliced peer) + start_thread(&thread_b, stack_b, STACK_SZ, + thread_b_entry, PRIO_BASE, "B"); + + // Create controller at a higher priority + start_thread(&thread_ctl, stack_ctl, STACK_SZ, + controller_entry, 4, "CTL"); + + while(1){ + k_sleep(K_FOREVER); + } + + return 0; + +}