diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day2/main.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day2/main.c" new file mode 100644 index 0000000000000000000000000000000000000000..2ae76570c1c1efc301fc8fbf31428820b57bf1dc --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day2/main.c" @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + +/* 为了体现优先级抢占与轮询,设置优先级 线程1>main>线程2=线程3 + * 如此,在main中执行startup时,当线程1被startup后会立即运行其内容,打断main + * 线程1没有while循环,执行完内容后会自动exit, + * 线程2线程3无delay,不会让渡出cpu,二者按照时间片轮询 + */ +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_uint32_t count = 0; + while (count < 10000) + { + rt_kprintf("thread1 count: %d\n", count ++); +// rt_thread_mdelay(1); + } +} +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_uint32_t count = 0; + while (1) + { + rt_kprintf("thread2 count: %d\n", count ++); +// rt_thread_mdelay(500); + } +} +static rt_thread_t tid3 = RT_NULL; +static void thread3_entry(void *parameter) +{ + rt_uint32_t count = 0; + while (1) + { + rt_kprintf("thread3 count: %d\n", count ++); +// rt_thread_mdelay(500); + } +} + +int main(void) +{ + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY - 1, 5); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + tid3 = rt_thread_create("thread3", thread3_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid3); + + + rt_uint32_t count = 0; + + while (1) + { + rt_kprintf("main count: %d\n", count ++); + + rt_thread_mdelay(5); + + } + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/event.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/event.c" new file mode 100644 index 0000000000000000000000000000000000000000..c8e09ca5cfa0e962822d8b9180cc0edf146fe4f8 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/event.c" @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 接受事件1&2 成功后发送事件3|4给thread2 + */ + +#define EVENT1 (1 << 1) +#define EVENT2 (1 << 2) +#define EVENT3 (1 << 3) +#define EVENT4 (1 << 4) + +static rt_event_t event = RT_NULL; + +//rt_uint32_t num1 = 0, num2 = 0; + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_err_t status = RT_ERROR; + rt_uint32_t recved = 0; + + status = rt_event_recv(event, EVENT1|EVENT2, RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, &recved); + + if(status == RT_EOK){ + rt_kprintf("th1 receive event1&2 , recved = %d\n",recved); + rt_kprintf("th1 send event 3 \n"); + rt_event_send(event, EVENT3); + rt_thread_mdelay(1000); + rt_kprintf("th1 send event 4 \n"); + rt_event_send(event, EVENT4); + + } + +// while (1) +// { +// +// +// } +} +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ +// rt_err_t status; + rt_kprintf("th2 send event1\n"); + rt_event_send(event, EVENT1); + rt_thread_mdelay(500); + + rt_kprintf("th2 send event2\n"); + rt_event_send(event, EVENT2); + rt_thread_mdelay(500); + + rt_err_t status = RT_ERROR; + rt_uint32_t recved = 0; + + + while(1){ + status = rt_event_recv(event, EVENT3|EVENT4, RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &recved); + if(status == RT_EOK){ + rt_kprintf("th2 receive event3|4, recevd = %d\n",recved); + + } + } + rt_kprintf("break\n"); + + + +// while (1) +// { +// +// } +} + + +int main(void) +{ + event = rt_event_create("event",RT_IPC_FLAG_PRIO); + + + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/mailbox.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/mailbox.c" new file mode 100644 index 0000000000000000000000000000000000000000..c7f8afaf53d7e1906aafee7233d6342c2f1ff79c --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/mailbox.c" @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread2 向 thread1 发送mb,其中str3是urgent发送 + * thread1 会先读取到str3 + */ + +static rt_mailbox_t mb; +static char str1[] = "first mail"; +static char str2[] = "second mail"; +static char str3[] = "third mail"; + + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_uint8_t cnt = 0; + char* rec = NULL; + while(cnt < 3){ + if(RT_EOK == rt_mb_recv(mb, (rt_ubase_t *)&rec, RT_WAITING_FOREVER)){ + rt_kprintf("rec: %s\n",rec); + cnt++; + } + } + + + + +} + +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_mb_send(mb,(rt_uint32_t)&str1); + rt_mb_send(mb,(rt_uint32_t)&str2); + rt_mb_urgent(mb,(rt_uint32_t)&str3); + + + + rt_thread_mdelay(500); + +} + + +int main(void) +{ + mb= rt_mb_create("mailbox", 4*3, RT_IPC_FLAG_PRIO); + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/msgqueue.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/msgqueue.c" new file mode 100644 index 0000000000000000000000000000000000000000..247b08df714876f91b5c2ab4a5373a43174d3f52 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/msgqueue.c" @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread2 发送 A~Z, thread1接收 + */ + +static rt_mq_t msq; + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_uint8_t num = 0; + char rec = '\0'; + while(rec < 'Z'){ + if (rt_mq_recv(msq, &rec, sizeof(rec), RT_WAITING_FOREVER) == RT_EOK){ + num++; + rt_kprintf("receive %c\n",rec); + } + rt_thread_mdelay(10); + } + +} + +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + static char buf = 'A'; + while(buf <= 'Z'){ + rt_mq_send(msq, &buf, 1); + rt_kprintf("sent %c\n",buf); + buf++; + + rt_thread_mdelay(10); + } +} + + +int main(void) +{ + msq = rt_mq_create("msq",sizeof(char),10,RT_IPC_FLAG_PRIO); + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/mutex.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/mutex.c" new file mode 100644 index 0000000000000000000000000000000000000000..b0c846a54f64d6631dd742f555dd3b4889f50659 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/mutex.c" @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 与 thread2 竞争同一资源。 + * 当thread1 持有资源时thread2 无法访问 + * num1 num2 使用互斥量加锁, num3未加锁 + * 通过输出可以看到,num1与num2一直相等,而num3存在与前两者不一致的情况 + * + */ +static rt_mutex_t mutex = RT_NULL; +rt_uint32_t num1 = 0, num2 = 0, num3 = 0; + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + + while (num3 < 1000) + { + rt_mutex_take(mutex, 0); + rt_kprintf("thread 1 num1 = %d, num2 = %d, num3 = %d\n",num1,num2,num3); + + num1++; + num2++; + rt_kprintf("release mutex\n"); + rt_mutex_release(mutex); + num3++; + + } +} +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_err_t status; + + while (num3 < 1000) + { + rt_mutex_take(mutex, 0); + rt_kprintf("thread 2 num1 = %d, num2 = %d, num3 = %d\n",num1,num2,num3); + num1++; + num2++; + rt_kprintf("release mutex\n"); + rt_mutex_release(mutex); + num3++; + } +} + + +int main(void) +{ + mutex = rt_mutex_create("mutex1",RT_IPC_FLAG_PRIO); + + + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/semaphore.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/semaphore.c" new file mode 100644 index 0000000000000000000000000000000000000000..4bf0d5e5e1e64ed5ad2bdb03f74a64b0830409a0 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/semaphore.c" @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 定时释放sem + * thread2 take sem 如果take到就计数++ + * thread1优先级 == thread2,二者轮询调度,thread2可能会获取信号量失败。 + * + */ +static rt_sem_t dynamic_sem = RT_NULL; + + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_uint32_t count = 0; + while (1) + { + count++; + count %= 100; + if(count ==0){ + rt_kprintf("release sem, count = %d\n",count); + rt_sem_release(dynamic_sem); +// rt_thread_mdelay(5); + } + + } +} +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_uint32_t take_num = 0; + rt_err_t status; + + while (1) + { + rt_kprintf("taking sem..."); + + status = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER); + if(status == RT_EOK){ + take_num++; + rt_kprintf("succsessful, take_num = %d\n",take_num); + } + } +} + + +int main(void) +{ + dynamic_sem = rt_sem_create("sema", 0, RT_IPC_FLAG_PRIO); + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/signal.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/signal.c" new file mode 100644 index 0000000000000000000000000000000000000000..07cff1d12ff14447333103bbf505fed973835bfc --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\344\275\234\344\270\232/Day3/signal.c" @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 和 thread2 分别安装信号,main进行发送,当num>2000后 + * thread2将mask信号,只有thread1还能接收到信号 + */ +rt_uint32_t num = 1000; +void signal_handler1(int sig) +{ + + rt_kprintf("thread1 received signal %d\n", sig); + num += 2; + + rt_kprintf("num = %d\n",num); +} +void signal_handler2(int sig) +{ + rt_kprintf("thread2 received signal %d\n", sig); + num += 2; + + rt_kprintf("num = %d\n",num); +} + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_signal_install(SIGUSR1, signal_handler1); + rt_signal_unmask(SIGUSR1); + + rt_uint8_t local = 0; + while(1){ + local++; + rt_thread_mdelay(100); + + } + + +} + +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_signal_install(SIGUSR2, signal_handler2); + rt_signal_unmask(SIGUSR2); + + + + rt_uint8_t local = 0; + while(1){ + if(num > 2000)rt_signal_mask(SIGUSR2); + local++; + rt_thread_mdelay(100); + + } + +} + + +int main(void) +{ + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + while(1){ + rt_thread_kill(tid1, SIGUSR1); + rt_thread_kill(tid2, SIGUSR2); + rt_thread_mdelay(10); + } + + + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2541\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2541\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..0bdb77d96fff0ba3985cb583b41c4eb61c9628ce --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2541\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,57 @@ +# 第一天笔记 +由于提交过bsp(被merge),这些基础知识只大概记录了要点 +## env的使用 +`pkgs --upgrade` 升级软件包 + +`pkgs --update` 更新软件包 + +使用 `scons -j4` 对文件进行编译(如果报错是没config文件导致的,先打开menuconfig再save退出即可) + +使用qemu时,输入`qemu-nograhic.bat`可以运行无UI的模拟器,使用`ctrl+a x`回到命令行;直接输入`qemu`进入带UI的模拟器时,使用`ctrl+c`退出 + +在qemu中,使用 `list` 查看相关命令 + +## 关于scons工具 +### sconscript语法 +创建新文件夹需要添加sconscript文件,scons构建是基于sconstruct和sconscript的,后者每个文件夹都有一个,python通过该文件递归遍历每一个文件夹以控制编译内容。没有sconscript的话编译器是找不到文件的。 + + + #导入库 + import os + from building import * + + #获取当前路径 + cwd = GetCurrentDir() + objs = [] + #列举当前路径下的内容 + list = os.listdir(cwd) + #遍历寻找子文件夹的文件,如果没有子文件夹可以去掉这个for循环 + for d in list: + path = os.path.join(cwd, d) + #如果子文件夹有sconscript就把对应文件添加到obj + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + #把obj返回给上一级 + Return('objs') + +## git +git是一个版本管理工具,组成为 工作区--暂存区--本地仓库--远端仓库 +### git重要命令 +`git push/pull` 推送/拉取 + +`git add .` 添加所有修改的文件到暂存区 + +`git commit -m "log"` commit,log是commit的标题 + +`git log` 查看修改日志 + +`git status` 查看文件状态 + +`git checkout -b first_branch `创建一个分支名为first_branch,可以通过`git switch`切换分支,`git branch` 查看分支 + +`git reset --hard HEAD~` 硬重置,强制删除上一个commit + +`git reset --soft HEAD~` 软重置,把上一个commit退回暂存区里,后面还可以重新commit + + +命令后缀添加`--force`可以进行强制推送和拉取 \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2542\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2542\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..de30fa714609338e9913eaf812e0009336384a19 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2542\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,159 @@ +# 裸机 vs RTOS +开发上主要是单一系统和复杂系统的区别,使用RTOS的工程功能丰富、逻辑复杂、代码耦合度高 + +**裸机优点:** + +运行效率高,占用存储小 + +**裸机缺点:** + +代码耦合度高,复用性差; + +代码不合理时可能造成系统堵塞 + +**RTOS优点:** + +代码耦合度低、复用性好; + +可以实现复杂代码逻辑; + +延时是阻塞延时,挂起当前线程,期间可以运行其他线程,不浪费CPU + +**RTOS缺点:** + +本身占用存储;不适合单一嵌入式系统,系统调度有额外开销 + +裸机主要以前后台方式运行,后台是任务,前台是中断服务程序;RTOS模拟多任务同时运行 +# 内核入门 +## 一些概念 +临界区:临界区的资源只能同时被一个线程使用,其他线程想要获取该资源必须等待。 + +多线程使用同一个串口的情况下,如果不保护串口可能出现打印乱码的情况 + +## 系统启动 +### rt studio +下载--选择模拟器 +ps 查看线程状态 +tab 查看shell命令 +### 启动文件 +32的启动文件一般是.S汇编写的,开头的`reset handler`是程序一开始运行的内容,之后进行相关数据段的搬运,然后进行时钟配置: `bl SystemInit` + +对于裸机来说,接下来就直接进入`main`函数了: +``` + bl main + bx lr +``` +对于RTT来说,接下来进入`entry`函数,然后再进行软件上的启动。对于不同的IDE,有不同的代码以启动 +``` + bl entry + bx lr +``` +### 软件启动 +初始化的内容主要有:板级外设、时间相关、调度器相关(线程相关) + +freertos是任务准备好再启动,rtt是一个线程先启动再进行后面的工作 + +**流程(从上到下执行):** + +`rt_hw_interrput_disable()`:关闭中断,不让初始化过程被外部中断影响 + +`rt_hw_board_init()`:板级外设初始化 + +`rt_show_version()`:串口被上一个函数初始化了,在此打印logo以及版本号 + +`rt_system_timer_init()`:初始化定时器(基于中断,在硬件跑的) + +`rt_system_scheduler_init()`:初始化调度器 + +`rt_system_signal_init()`: + +`rt_application_init()`:创建main线程 + +`rt_system_timer_thread_init()`:初始化定时器(在软件中跑的,精度不如硬件定时器) + +`rt_thread_idle_init()`:创建空闲线程(优先级最低且永远不会挂起),可以执行cpu使用率统计、低功耗使能、回收僵尸队列的线程等操作。如果启用了shell线程那么启动后至少有三个线程在运行 + +`rt_system_scheduler_start()`:调度,如果运行`main_thread_entry()`,其`rt_component_init`函数,将进行一些自动初始化的操作(本质是遍历函数,使用ifdef进行裁剪) + +main函数也是一个线程,不能在main中定义太大的内容,线程并没有太多资源。rtconfig.h中 `RT_MAIN_THREAD_SIZE`是main的大小 + +## 内核入门 +在:软件包-杂项软件包--sample 中可以查看对应示例。 + +`name`线程的名称,最大长度由 rtconfig.h中定义的RT NAME_MAX宏指定,多余部分会被自动截掉,通过线程名字可以直接查找到对应控制块 + +`entry`线程入口函数 + +`parameter`线程入口函数参数 + +`stack_size`线程栈大小,单位是字节。在大多数系统中需要做栈空间地址对齐(例如 ARM 体系结构中需要向4字节地址对齐) + +`priority`线程的优先级。优先级范围根据系统配置情况(rtconfig.h中的RT THREAD_PRIORITY_MAX宏定义),如果支持的是 256 级优先级,那么范围是从0~255,数值越小优先级越高,0代表最高优先级 + +`tick`线程的时间片大小。时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行。tick在rtconfig里定义。 `RT_TICK_PER_SECOND 1000` 默认1ms。tick越大越快,系统开销也会越高 + +优先级数字越小优先级越大,main的默认是10 +`RT_MAIN_THREAD_PRIORITY 10` +`FINSH_THREAD_PRIORITY 20` + +栈有两个相关结构体 +``` +struct stack_frame +{ + rt_uint32_t r4~r11; + struct exception_stack_frame exception_stack_frame; +} +struct exception_stack_frame +{ + rt_uint32_t r0~r3,r12,lr,pc,pse; + +} +``` +struct先从r4开始写是因我cm3、4内核支持硬件压栈,struct extension_stack_frame是硬件自动压栈的部分 + +线程初始化流程:分配一块空间,初始化栈结构体,初始化线程其他内容 + +当要运行这个线程的时候,会把对应寄存器的值从struct里面搬运过去 +### 线程API + +`init`静态创建,编译阶段就分配了,空间是定死的,安全类产品不在乎内存开销,一般用init + +init对应`detach`,仅从就绪列表移除,但原来的内存没有释放。 + +`creat`动态创建,适合内存有限但是功能很多的情况(消费类电子) + +creat对应`delete`,该线程对应的内存会被释放。 + +`starup`挂载线程到就绪列表(转化为就绪态),如果新启动的线程优先级比当前线程高,直接切换到新启动的线程。 + +`yield`立即放弃当前线程,进行调度。 + +`delay`启动定时器,把当前线程挂起 + +`control`改变线程优先级 + +`suspend`挂起线程 + + +### 线程调度 +挂起状态通过`delete()`和`detach()`可以进入关闭状态。对于不是死循环的线程,在运行状态结束后会通过`exit()`回收该线程进入关闭状态。 + +调度器的任务:决定任务运行顺序,执行任务切换。 + +优先级抢占:最高优先级的任务立刻运行 + +时间片轮转:相同优先级根据时间片大小轮转 +,时间片只在优先级最高的情况下存在(否则不存在线程之间的竞争) + +任何系统的优先级都是低于中断的。线程优先级是软件概念,中断优先级是硬件的概念。调度依赖的是最低优先级的中断 + +小技巧:一开始创建线程的时候stack的值尽量大一些,可以1~2k;防止栈溢出导致系统崩溃 + +# 作业 +为了体现优先级抢占与轮询,设置优先级 线程1>main>线程2=线程3 + +如此,在main中执行startup时,当线程1被startup后会立即运行其内容,打断main + +线程1没有while循环,执行完内容后会自动exit + +线程2线程3无delay,不会让渡出cpu,二者按照时间片轮询 \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2543\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2543\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..7e135e612359b19bc7381075c0922021c1bb2435 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\257\233\345\245\225\347\277\224/\347\254\224\350\256\260/\347\254\2543\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,175 @@ +# 线程间同步 +## 信号量 +信号量没有所有权概念 + +一般分为三种: + +* 互斥信号量: 可能会引起优先级反转问题 +* 二值信号量: 主要用于线程与线程之间、线程与中断服务程序(ISR)之间的同步。初始值为0,表示同步事件尚未产生;线程获取信号量以等待该同步事件的发生; +另一个任务或 ISR到达同步点时,释放信号量表示同步事件已发生唤醒等待的任务。 +* 计数信号量: 用于解决资源计数问题 + +### 信号量API +`rt_sem_create`: 动态创建信号量,当调用这个函数时,系统将先从对象管理器中分配一个semaphore 对象,并初始化这个对象,然后初始化父类IPC对象以及与 semaphore相关的部分。在创建信号量指定的参数中,信号量标志参数决定了当信号量不可用时,多个线程等待的排队方式。当选择`RT_IPC_FLAG_FIFO`(先进先出)方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择 `RT_IPC_FLAG_PRI0`(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。 + +`rt_sem_delete`: 对应`rt_sem_create`。 +当调用这个函数时,系统将删除这个信号量。如果删除该信号量时,有线程正在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程(等待线程的返回值是-RTERROR),然后再释放信号量的内存资源。 + +`rt_sem_init`: 静态创建信号量 + +`rt_sem_detach`: 对应`rt_sem_init`。脱离信号量就是让信号量对象从内核对象管理器中脱离,适用于静态初始化的信号量。 +使用该函数后,内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离。原来挂起在信号量上的等待线程将获得-RTERROR 的返回值。 + +`rt_sem_take`: 线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减1。 +在调用这个函数时,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数 time 指定的时间内依然得不到信号量,线程将超时返回,返回值是`- RT_ETIMEOUT` + +`rt_sem_trytake`: 等效rt_sem_take(sem,0);不等待资源,如果获取不到直接返回`-RT_ETIMEOUT` + +`rt_sem_release`: 释放信号量,唤醒等待的线程。 + +## 互斥量 +互斥量具有所有权的概念 + +互斥量是一种特殊的二值信号量。它和信号量不同的是,它支持: + +* 互斥量所有权:互斥量具有线程所有权,只有加锁的线程才能解锁,否则可能导致未定义行为(如死锁) +* 递归访问 +* 优先级继承:防止优先级反转 + +优先级反转:当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证 + +优先级继承:提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。 +### 互斥量API +`rt_mutex_creat`:动态创建互斥量,参数flag已经作废,无论用户选择 `RT_IPC_FLAG_PRIO` 还是 `RT_IPC_FLAG_FIFO`,内核均按照 `RT_IPC_FLAG_PRIO` 处理 + +`rt_mutex_delete`: 删除互斥量以释放系统资源,适用于动态创建的互斥量 + +`rt_mutex_init`: 静态创建互斥量 + +`rt_mutex_detach`: 把互斥量对象从内核对象管理器中脱离,适用于静态初始化的互斥量 + +`rt_mutex_take`:线程获取了互斥量,那么线程就有了对该互斥量的所有权,某一个时刻一个互斥量只能被一个线程持有 + +`rt_mutex_trytake`: 等效`rt_mutex_take(mutex, 0);`不等待,如果获取不到直接返回。 + +`rt_mutex_release`: 释放互斥量 + +使用完共享资源的时候,必须尽快释放互斥量,以保证系统的实时性。 + +## 事件集 + +事件集是一个无符号32bit的数,每个事件用一个bit位代表 + +发送:可以从中断或者线程中进行发送 + +接收:线程接收,条件检查(逻辑与方式、逻辑或方式) + +### 事件集API +`rt_event_creat`: 动态创建事件集 + +`rt_event_delete`: 对应`rt_event_creat`。删除事件集对象控制块来释放系统资源,在删除前会唤醒所有挂起在该事件集上的线程(线程的返回值是 -RT_ERROR),然后释放事件集对象占用的内存块 + +`rt_event_init`: 静态创建事件集 + +`rt_event_detach`: 对应`rt_event_init`。将事件集对象从内核对象管理器中脱离 + +`rt_event_send`: 通过参数 set 指定的事件标志来设定 event 事件集对象的事件标志值,然后遍历等待在 event 事件集对象上的等待线程链表,判断是否有线程的事件激活要求与当前 event 对象事件标志值匹配,如果有,则唤醒该线程。 + +`rt_event_recv`: 内核使用 32 位的无符号整数来标识事件集,它的每一位代表一个事件,因此一个事件集对象可同时等待接收 32 个事件,内核可以通过指定选择参数 “逻辑与” 或“逻辑或”来选择如何激活线程,使用 “逻辑与” 参数表示只有当所有等待的事件都发生时才激活线程,而使用 “逻辑或” 参数则表示只要有一个等待的事件发生就激活线程。 + +# 线程间通信 +## 消息邮箱 +特点:是开销比较低,效率较高。 + +邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针的大小即为 4 个字节,所以一封邮件恰好能够容纳一个指针)。典型的邮箱也称作交换消息,线程或中断服务例程把一封4字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。 + +非阻塞方式的邮件发送过程能够安全的应用于中断服务中,是线程、中断服务、定时器向线程发送消息的有效手段。 + +通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为0时,邮件收取过程将变成阻塞方式。在这类情况下,只能由线程进行邮件的收取。 + +当一个线程向邮箱发送邮件时,如果邮箱没满,将把邮件复制到邮箱中。如果邮箱已经满了,发送线程可以设置超时时间,选择等待挂起或直接返回`-RT_EFULL`。如果发送线程选择挂起等待,那么当邮箱中的邮件被收取而空出空间来时,等待挂起的发送线程将被唤醒继续发送。 + +当一个线程从邮箱中接收邮件时,如果邮箱是空的,接收线程可以选择是否等待挂起直到收到新的邮件而唤醒,或可以设置超时时间。当达到设置的超时时间,邮箱依然未收到邮件时,这个选择超时等待的线程将被唤醒并返回`-RT_ETIMEOUT`。如果邮箱中存在邮件,那么接收线程将复制邮箱中的4个字节邮件到接收缓存中。 + +### mailbox API +`rt_mb_create`: 动态创建邮箱对象 + +`rt_mb_delete`: 对应`rt_mb_create`。如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是 -RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象 + +`rt_mb_init`: 静态创建邮箱对象 + +`rt_mb_detach`: 对应`rt_mb_init`。将该邮箱对象从内核对象管理器中脱离 + +`rt_mb_send`: 发送的邮件可以是 32 位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 -RT_EFULL 的返回值。 + +`rt_mb_send_wait`: 如果邮箱已经满了,那么发送线程将根据设定的 timeout 参数等待邮箱中因为收取邮件而空出空间。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。 + +`rt_mb_urgent`: 当发送紧急邮件时,邮件被直接插队放入了邮件队首 + +`rt_mb_recv`: 当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回 RT_EOK 的返回值,否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回 + +## 消息队列 +将多条消息排成的队列形式,是一种常用的线程间通信方式,可以应用在多种场合,线程间的消息交换,使用串口接收不定长数据等。线程可以将一条或多条消息放到消息队列中,同样一个或多个线程可以从消息队列中获得消息同时消息队列提供异步处理机制可以起到缓冲消息的作用。 + +使用消息队列实现线程间的异步通信工作,具有以下特性: +* 支持读消息超时机制 +* 支持等待方式发送消息 +* 允许不同长度(不超过队列节点最大值)任意类型消息 +* 支持发送紧急消息 + +线程或中断服务例程可以将一条或多条消息放入消息队列中。同样,一个或多个线程也可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常将先进入消息队列的消息先传给线程,也就是说,线程先得到的是最先进入消息队列的消息,即先进先出原则(FIFO) + +### 消息队列API +`rt_mq_create`: 动态创建队列,给消息队列对象分配一块内存空间,组织成空闲消息链表,这块内存的大小 =[消息大小 + 消息头(用于链表连接)的大小]X 消息队列最大个数,接着再初始化消息队列,此时消息队列为空 + +`rt_mq_delete`: 对应`rt_mq_create`,如果有线程被挂起在该消息队列等待队列上,则内核先唤醒挂起在该消息等待队列上的所有线程(线程返回值是 - RT_ERROR),然后再释放消息队列使用的内存,最后删除消息队列对象 + +`rt_mq_init`: 静态创建队列 + +`rt_mq_detach`: 对应`rt_mq_init`,将使消息队列对象被从内核对象管理器中脱离 + +`rt_mq_send`: 发送者需指定发送的消息队列的对象句柄(即指向消息队列控制块的指针),并且指定发送的消息内容以及消息大小。在发送一个普通消息之后,空闲消息链表上的队首消息被转移到了消息队列尾 + +`rt_mq_send_wait`: 如果消息队列已经满了,那么发送线程将根据设定的 timeout 参数进行等待。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码 + +`rt_mq_urgent`: 当发送紧急消息时,从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂到队首 + +`rt_mq_recv`: 当消息队列中有消息时,接收者才能接收消息,否则接收者会根据超时时间设置,或挂起在消息队列的等待线程队列上,或直接返回 + +## 信号 +信号(又称为软中断信号),在软件层次上是对中断机制的一种模拟,在原理上,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。 + +信号在 RT-Thread 中用作异步通信,POSIX 标准定义了 sigset_t 类型来定义一个信号集,然而 sigset_t 类型在不同的系统可能有不同的定义方式,在 RT-Thread 中,将 sigset_t 定义成了 unsigned long 型,并命名为 rt_sigset_t,应用程序能够使用的信号为 SIGUSR1(10)和 SIGUSR2(12)。 + +信号本质是软中断,用来通知线程发生了异步事件,用做线程之间的异常通知、应急处理。一个线程不必通过任何操作来等待信号的到达,事实上,线程也不知道信号到底什么时候到达,线程之间可以互相通过调用 `rt_thread_kill()` 发送软中断信号。 + +收到信号的线程对各种信号有不同的处理方法,处理方法可以分为三类: + +第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。 + +第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。 + +第三种方法是,对该信号的处理保留系统的默认值。 + +当信号被传递给线程时,如果它正处于挂起状态,那会把状态改为就绪状态去处理对应的信号。如果它正处于运行状态,那么会在它当前的线程栈基础上建立新栈帧空间去处理对应的信号,需要注意的是使用的线程栈大小也会相应增加。 + + +信号的使用一般比较少。它跟中断是差不多的。它主要可以用来事件通知。比如通知线程某个事件已经发生、数据就绪、任务完成之类的。 +也可以用来进行错误的通知。可以实现错误的处理。信号就是软中断,跟中断其实是差不多的。 +### 信号API +`rt_signal_install`:如果线程要处理某一信号,那么就要在线程中安装该信号。安装信号主要用来确定信号值及线程针对该信号值的动作之间的映射关系,即线程将要处理哪个信号,该信号被传递给线程时,将执行何种操作 +在信号安装时设定 handler 参数,决定了该信号的不同的处理方法。处理方法可以分为三种: + +1)类似中断的处理方式,参数指向当信号发生时用户自定义的处理函数,由该函数来处理。 + +2)参数设为 SIG_IGN,忽略某个信号,对该信号不做任何处理,就像未发生过一样。 + +3)参数设为 SIG_DFL,系统会调用默认的处理函数_signal_default_handler()。 + +`rt_signal_mask`: 如果该信号被阻塞,则该信号将不会递达给安装此信号的线程,也不会引发软中断处理 + +`rt_signal_unmask`: 线程中可以安装好几个信号,使用此函数可以对其中一些信号给予 “关注”,那么发送这些信号都会引发该线程的软中断 + +`rt_signal_kill`: 给设定了处理异常的线程发送信号 + +`rt_signal_wait`: 等待 set 信号的到来,如果没有等到这个信号,则将线程挂起,直到等到这个信号或者等待时间超过指定的超时时间 timeout。如果等到了该信号,则将指向该信号体的指针存入 si \ No newline at end of file