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/\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