free rtos 队列常用操作介绍上期视频我们简单介绍了一下队列的创建和读写操作。创建队列我们使用了 xq create 函数,这种方式是动态创建队列,队列的内存由 free rtos 动态分配 队列还有静态创建方式,这种方式队列的内存由我们自己分配,静态创建队列我们了解一下即可。常用的还是动态创建队列,读写队列我们使用了这两个函数。 excuse and 函数是将数据写入队列的尾部, 它等同于 xq center back 函数,由尾部写入,也有头部写入函数。它们的使用方式是一样的,区别就是插入数据的位置不同,可以根据需要自行选择。此外还有一种对列写入 数据的方式就是覆盖写入,使用 xq over right 函数,顾名思义,对列满的时候,他可以用新数据覆盖之前的数据,注意,这个函数只能用于长度唯一的对列。 以上这几种就是写对列的常用基本操作了,他们还有中断中使用的版本 数据,带有 from isr 的函数都是在中断中使用的。接下来我们再来看一下读对列操作。 xq receive, 读取数据后就会删除对列中的数据,如果不想删除数据,我们就可以使用 xqp 函数, 他读取数据后不会删除数据,数据还可以供其他任务使用。以上这两个就是读对列的常用基本操作了,他们同样有中断中使用的版本。除了创建读协对联, 还有这些常用操作函数。删除队列,注意,只能删除动态创建的队列,因为它会释放内存,复位队列可以恢复队列的初始状态,下面这两个一个是查询队列可用数据个数,另一个是查询可用空间个数。 看完这些常用队列操作,我们再来讲一下队列的传书。负一 rtos 的队列是使用拷贝传书,也就是发送时要将数据复制到队列,读取时再将队列数据复制出来,这期间数据被复制了两次, 空间占用,也多出了两份数据的空间大小。如果要传输的数据非常大,那么空间也会造成很大的浪费,这时候该怎么传输数据呢?我们可以使用指针来传输数据,现在我们来操作一下,打开上一期的视频,比如 现在我们要传递一个一千字节的字符数据,我们就需要先定义一下数据的全局变量,在上方这里定义一个一千字节的叉二型数组。 我们要使用指针传递数据,所以创建对列,这里的数据大小也要修改一下,要传递的数据类型是叉二型指针,用 size of 计算一下大小,这样写就可以了。回到上面我们开始修改写对列的任务, 这个结构题就不用了,我们删掉多余代码。在上面我们定义一个叉二星指针,用来指向要传递数据的数组。定义好后,使用 s printf 函数初始化一下数据, 后面这里加一个静态变量,做一下数据的区分, c, n, t 做一下累加。下面这里第一个参数不用修改,最主要的是第二个参数,大家可以暂停思考下第二个参数怎么填写,因为我们传输的数据是一个地址,这里第二个参数是需要传进去数据的地址, 所以我们需要传进去 p data 这个指针的地址前面加一个取地址符号,这样写就可以了。下面任务二按照同样的方法修改, 修改好之后,我们再来修改下任务三的读取数据,我们需要在任务三这里定义一个叉儿型指针,用来接收读取到的地址,将这个指针传进读取函数中,然后修改一下下面的打印函数,打印一下接收到的 地址里面的数据,这样修改之后,编译执行会有一个问题,我们先来编译执行,看下现象, 进入 debug, 点击全速运行,可以看到这里只打印了任务二的数据,没有任务一的数据,这是什么原因呢?其实这里就是我们采用指针传递数据需要必须注意的地方, 这里任务一把自己的数据填入这个全局数组中后,倒读到任务二后,任务二覆盖了任务一的数据这里这三个任务都是相同优先级,所以他们的执行顺序是一二三, 这样任务一的数据还没有被任务三接收显示,就被任务二给覆盖了,最后能接收的就只有任务二的数据了。所以我们在使用 地址传递数据时,多个地方修改地址里面的数据,要小心谨慎。现在这个实验的问题也很好解决,只需要把接收数据的任务,也就是任务三优先级调高一点就可以了。 任务三的优先级调高,当队列中写入数据后,任务三就会马上抢先执行,数据也就不会被覆盖了。现在变异运行,看下现象 可以看到任务一发送的数据也会被打印了, 现在总结一下传输大数据快时需要注意的地方,由于采用指针方式传输,所以存储数据的数组要定义成全局变量, 在多个地方修改数据时要注意数据覆盖问题,谨慎修改。今天的视频就到这里了,欢迎留言评论,我们下期见!
粉丝7259获赞3.1万

简单使用下 freertos 队列,队列是 freertos 数据传输方式的一种,可以用于任务间数据传输,也可用于任务与中断间数据传输。在之前的视频中,我们使用串口打印信息,在每个任务中都有使用,也就是这种结构。 这种结构中每个任务既需要产生数据,比如处理传感器数据等,又需要关心数据的显示,导致任务逻辑层次不清晰。每个任务都有显示程序,我们就可以使用对列,将程序分层一下,把显示相关程序单独拿出来,组成一个显示任务, 其他几个任务只需要将各自要显示的数据都写入队列。显示任务只需要从队列中读取数据进行显示,这样整体结构就变得很清晰。 任务一到三就可以专注自己的数据产生,而不需要关心怎么显示数据。显示任务就只关心怎么显示数据,而不需要关心数据怎么产生的。下面我们就使用对列简单实现一下这种结构。这是之前多任务视频的代码,我们先删除多余的程序, 每个任务只保留打印程序,然后保存编译一下没有错误,就可以开始本期实验了。 我们需要使用队列,所以要先包含一下队列的头文件。本次实验实现一下任务一任务二写队列,任务三读数据并打一显示。 首先我们需要创建一个队列,使用 x q create 可以动态创建一个队列。我们右键转到定义,看 下他的参数,这里他是使用了一个红定义,定义了一下这个函数,方便了我们的使用。第一个参数就是对列的长度,也就是对列可以存放多少个数据。第二个参数是每个数据的大小,单位是字节, 所以这里的参数就可以这样写。上面有两个任务需要写数据,对列长度我们就设置为二,而每个任务要写的数据是一个字符串,我们就设置二十个字节的数据大小吧。 现在创建队列参数已经写好了,可是创建的队列我们去哪里找呢?其实这个函数的返回值就是我们创建的队列,我们可以看一下函数原形,右键跳转过去,这里返回值是这种类型,我们也要定义这样一个变量,用来接收创建好的队列句柄, 复制一下回到我们的代码,在上方定义一个变量,随便起一个对列名字, 用这个变量接收一下返回值,这样一个队列就创建好了。更严谨的写法,还需要在下方判断一下返回值是否为空为空,队列创建失败,我这里就不写了。队列创建好后就可以写队列了, 现在开始修改任务函数,将打印程序注释掉,写对列。我们使用 xq 三的函数,可以右键转到定义,看下参数, 也是一个红定义函数。第一个参数是堆列句柄,第二个参数是数据的地址,这个数据的值会被复制进堆列。第三个参数是阻塞时间, 对列满无法写入新数据时,会阻塞我们设定的时间,回到我们的代码。第一个参数我们填入对列句柄, 第二个参数是数据地址,我们要先定义一下数据,在上方定义一个字符数组,数组大小就是创建对列时指定的数据大小,二十字节,不能随便定义大小。 我们初始化为这个字符串,将数组名填入第二个参数,第三个参数是组色时间,我这里写零,也就是无法写入数据时,函数会立刻返回,这样写入对列就完成了。我们把任务二同样的操作修改一下, 这里字符串改为任务二,写入对列完成后,就开始读对列并显示数 据了。我们修改一下任务三,读对列,使用 xq receive 函数,同样转到定义看下参数,这里和写对列参数基本一致,就不过多介绍了。回到我们的代码,直接写参数,第一个是对列句柄, 第二个是读取的数据存放地址。我们也定义一个二十字节字符数组,用来接收读取的数据。将数组名填入参数, 第三个阻塞,我们填入一个 port max delay, 也就是读不到数据就一直阻塞在这里。这样做的目的是没有数据时,就等在这里有数据才会继续执行下方的显示程序,这样就不执行无用代码,提高效率。 现在读对列已经完成了,我们开始写显示程序。显示之前要先判断一下是否读取成功。 右键转到定义,看下返回之类型。我们定义一个这种类型的变量,用来接收返回的状态。回到代码,在这里定义一下, 用这个变量接收一下返回值,读取成功会返回 p d two。 在下方判断一下返回值, 这里应该是 p d 处,写 p d pass 也没问题。然后把下方的显示代码放到 f 判断里面,修改一下,打印函数,打印接收数据 data 数组中的字符串, 这样就可以了,由于独对列没有数据是一直阻塞的,下方的 delay 也可以不需要了,现在我们编译一下,没有错误,我们点击进入 debug 看下现象,点击全速运行,可以看到串口是正常打 音数据的,这样就实现了任务键传输字符串。那要是需要传输很多不同类型的数据该怎么办呢? 大家可以暂停思考下,我这里告诉大家答案,可以使用结构体,具体怎么做,我们现在来写一下代码。首先我们要定义一下要传输数据的结构体类型,在上方定义一下结构体, 结构体中定义要传输的数据,我这里传输一下这个字符串和一个 int 变量。定义好结构体后,要修改一下创建对列,这里的数据大小,数据大小我们使用 size of 来计算一下结构体大小, 这样修改后再修改一下任务函数,先定义一个结构体变量,再 初始画一下结构题的字符串参数,下方这里数据要取地址这里我们再让变量艾字加下方的任务二,同样的方法修改一下, 然后修改一下显示任务,这里也要定义一个结构体变量,用来接收读对列数据,这里别忘了要取地址, 下方这里我们就打印一下读取的结构题数据,这样修改就可以使用对列传递结构题数据了。我们编译一下,进入 debug 看下现象,点击全速运行, 可以看到现象是正确的,这样就实现了任务间依次传递多种阐述了。本期的视频就到这里了,欢迎留言评论,我们下期见。

队列就是一条数据运输的管道,有的任务在那头向队列里塞数据,有的任务在这头从队列里取数据。 hello, 欢迎小伙伴们回来,这里是波特律动、 kiss king、 励志做作业等教程。前面的几期视频中,我们使用原理动画的形式学习了 fraortos 有 关任务的各种概念,并且简单实现的两个任务并发执行。 本期视频我们不妨再来实现这样一个需求。一个任务是按键检测任务,在检测按键按下的同时,还要记录下按键按下的次数。另一个任务则是数据处理任务, 我们每一次案件都要进行一次数据处理,为了便于观察,我们假定数据处理很繁琐,需要耗时一秒钟。数据处理完成后,我们通过串口发送出一些信息,信息包括当前案件次数以及这是第几次数据处理。 聪明的小伙伴们应该已经发现,与之前我们实现的两个任务相互独立运行不同,这次的两个任务需要进行一定的数据交流。按键任务不仅需要告知数据处理任务,按键被按下,还要传递按键按下的次数。 这要如何实现呢?可能小伙伴们很容易便想到了全局变量方案,把按键是否按下以及按键次数的变量提取为全局变量。数据处理任务不就可以轻松访问它们? 我们不妨来看一下实际效果。我已经写好了全局变量方案的工程,首先是 q m x 里创建的工程 pr q, 并且勾选了很多小伙伴。在实际工程中,需要勾选的为每个外设生成单独的点 c 点 h 文件。第八个模式设为了 siriware, 因为 faratos 要占用 cistik, 进而 hellokus 时间机准设为了 team 四, 设置了按键的 g、 p、 l 口为输出,并设置了用户标签,开启了串口用于输出信息。然后 frontos 里设置了按键与数据处理两个任务 代码中,由于我们勾选了为每个外设生成单独的点 c 点 h 文件,问函数中之前创建任务部分的代码不见了,取而代之的是 mx frontos 以内的函数,我们点进其定义, 会发现原来创建任务的函数在此函数内部,并且我们上下滚动可以发现任务属性结构体 任务函数都移到了这 fraltos 点 c 文件中。然后我定义了两个全局变量, button press 代表按键是否被按下。 button counter 用于记录按键次数。 在按键任务函数中,我实现了简单的带消抖的按键检测逻辑,检测到按键后设置 button pressed 为一,并且 button counter 自增一,以记录按键次数,按键松开后设置 button pressed 为零。 随后在数据处理任务中,需要每隔一小会儿就检测一下 button press 的 状态,若此时是按下状态,自增数据处理次数,并且模拟一秒钟的数据处理时长。随后将按键次数与数据处理次数用串口输出出来。 ok, 让玩便易下载,一气呵成,看一下效果。按一下按键,一秒钟后输出按键一次,数据处理一次, 再按一次。一秒钟后按键两次,数据处理两次。看起来没什么问题,但如果我们一秒钟内快速按几次按键呢?三四五, 一秒钟后只有一次串口输出,按键次数增加了很多,但数据处理次数却只增加了一,我们想要的是按键多少次就要处理多少次数据,但现在看来好像有 bug 发生了,很严重的事件丢失,然后我们再延长按钮的按下时间试试。 哦吼!现在按键次数不增长,但却一秒钟增加一次数据处理次数是非常严重的。事件重复、事件丢失与事件重复这两个 bug 是 如何出现的呢?小伙伴们不妨自己思考一下。发送到弹幕中, 看来全聚变量方案没能完美地实现我们的需求。或许有些伙伴说,我们可以再增加几个变量,再增加几次 f 判断就可以解决这些问题了。 是的,但其实 frattos 早就为我们准备了更优雅的解决方案。队列 q 队列,顾名思义就是排好队,一个一个来,他就像是一根用来传递数据的管道。产生数据的任务,我们称为生产者,他可以将数据塞入到管道中排队,而需要这个数据的任务,我们称为消费者,他可以从队列中取出数据使用。 由于生产者塞入到队列中多少条数据,进而也就不会出现事件重复的问题。 而且由于队列有一定的长度,可以排队缓存数据,因此便不会出现因消费者在处理数据时无暇顾及而导致生产者产生的数据被忽略的这种事件丢失问题。 而是生产者产生的数据塞入到队列中排队,消费者依次取来使用即可,有条不稳。 当然了,队列的长度是有限的,还是可能发生溢出的现象,使用时也需要我们多加注意进行取舍。 除了能解决我们遇到的这两个问题,队列更优雅的地方还在于,当队列中没有数据需要处理时,消费者无需像全聚变量方案一样,一遍又一遍地检查是否有新数据需要处理,而是将自己直接置于阻设态,不再占用 cpu 运行资源。 而当生产者向对列中塞入新数据时, frattos 便会将消费者唤醒到就绪态,等获得时间片后取出新数据进行处理。既然对列这么不错,那小伙伴们想一下如何用对列来实现我们的需求呢? 产生数据的按键任务当然是作为生产者,而数据处理任务则是要作为消费者。传递的数据可以是按键的次数, 每当我们按下一次按键时,按键任务就将按键次数塞入到队列中。数据处理任务依次取出进行处理,最后将取到的按键次数输出出来就好了。看起来逻辑非常清晰完美, 那让我们赶紧到工程中。 forast task and qs 标签页下面的区域便是我们管理队列的地方,点击 id 进行添加 队列名称,我们写个 button q 队列长度,也就是队列中最多支持多少个数据排队。我们先保持默认的十六 item size 元素大小。这里我们真正要设置的是队列所传递的数据类型。 uint 十六杠 t, 当然便是两个字节, uint 三十二杠 t 是 四个字节,我们刚刚计数用的是 uint 三十二杠 t, 这样队列就会占用十六乘四等于六十四字节的内存用于数据排队,剩下的是关于内存分配方式的设置,等我们学到内存管理时再来理解。点击 ok, 这样我们的 button q 队列就添加好了,让我们点击生成代码。 小伙伴们可以看到,在创建任务的代码前,现在又多了一条创建队列的代码,调用的是 os maxq new 函数,队列长度是十六,每项信息的长度是 uni 的 三十二杠 t 的 长度。 队列创建函数的返回值保存在了 button q 函数变量中,这是队列的操作矩阵,我们在使用此队列时都要通过它来操作。那现在让我们使用队列改造一下当前工程吧。首先我们不妨先把这两个全句变量删掉, 然后来到按键任务中,按键次数还是要统计的,所以先定义一个任务函数内的 button counter 变量。 按下按键后,不需要标记按键按下了,而是要向队列中塞一条信息。 boost message queue put 向队列放信息的函数。第一个参数是队列的操作矩阵,也就是我们刚刚说的 button queue handle。 第二个参数是要放入队列的数据的指针,我们这里便是 button counter 的 指针。 第三个参数对 fourier 来说没有意义,填个零即可。第四个参数为当队列满时的阻滞等待时间,如果我们填写零,那么当队列已满时,就会直接丢弃掉此数据,任务继续执行。 另外我们还可以填写 o sweet forever 永久等待,那么数据就会在队列已满时进入阻设态,不再继续执行,直到队列中出现新的空位,任务再回来将当前数据塞入继续执行。 虽然这样能保证当前数据被放入队列,但也会导致任务阻设暂停。另外,我们也可以在此处填写一个超时时间,那么任务便也会在队列已满时阻设等待。 如果超时时间内队列出现空位,那便将数据塞入。如果超时时间内未出现空位,便将数据丢弃,继续执行任务,那在实际工程中,到底是填龄还是等待一会甚至永久等待, 需要根据我们实际的项目情况去取舍平衡。比如我们当前任务,我觉得如果本次按键的数据无法塞入队列的话,再检测到更多的按键也没什么用,进而不如直接写个 oh sweet forever, 永久等待 好的。随后我们再将松开按键后的按键标记也删除掉,这样我们便完成了按键任务的改造,在检测到按键时,向队列里发送按键计数, 再来到数据处理任务。首先用了队列,我们就不需要每隔一段时间对按键状态进行判断了,而是在处理数据前先从队列中接收按键技术。 先是要创建一个变量接收按键次数,然后我们调用 os message q get 函数从队列中获取数据。第一个参数当然还是队列的操作矩阵,第二个参数是用于获取数据的变量的指向,第三个参数还是没用,填零或者 non 也可以。 第四个参数依旧是等待超时时间,不过对于消费者任务而言,就是当队列为空时的阻滞。超时时间我们还是可以填写零,代表队列里是空的话就不取数据了,继续向下运行。 也可以填写 os wait forever, 代表没有数据就堵塞,必须要等到数据才会继续向下执行。另外也还是可以直接填写一个超时时间,代表会在此时间内堵塞等待,等到了就取走继续执行, 等不到便不等了,也接着继续执行。那对于我们当前的任务,取不到数据继续运行没有任何意义。然而我们需要填写 os wait forever。 ok, 这样我们也完成了对数据处理任务的改造。任务在循环一开始便等待数据,等到数据后,数据会被写入 button counter 变量,然后在一秒钟的模拟数据处理后将技术信息发送出来。 而且此 button counter 变量是数据处理任务函数自己的局部变量,与按键任务的 button counter 只是同名,却无任何关系,不会受其改变的影响。那么具体效果如何呢?让我们喊出我们的口号,编辑下载,一气呵成。 按一下,一秒钟后输出按键一次,数据处理一次,再按一次,一秒钟后按键两次,数据处理两次。基础功能看起来没什么问题,那让我们多按几次,三四五,不错不错,每个按键都没有丢失,那让我们长按试试。 只在一秒钟后输出了一次,并没有发生事件重复,请把完美打到弹幕中。 ok, 现在我们就成功实现了通过队列进行两个任务的数据通信,让我们稍作总结。 队列使用其实很简单,在 q m x 中添加一个队列, q m x 就 会自动帮我们在代码中创建此队列。在生产者任务中产生要传递的数据后,调用 o s message q po 的 函数将数据传入队列。 注意,第二个参数是要传递的数据的指向,在消费者任务中,我们使用 os message q get 函数获取队列中的数据。注意,第二个参数是要用来接收数据的变量的指向 怎么样,各位小伙伴们清晰明朗了吗?别忘了一键三连交个学费哦!后面的视频中我们还会讲解队列的更多细节,例如在中段中发送数据到队列,队列如何传递复杂数据,队列的多对一通讯等等, 可别忘了点个关注不迷路。喜欢 k k 教程的小伙伴,别忘了把 k k 推荐给朋友、同学甚至你的老师,你的鼓励是我制作更多优质内容的动力。让我们下期视频见,拜拜!

同学们好,这节课的内容是基于 vr tos 实现简易手表系统,本节课将分为以下三部分进行展开,第一部分是原理概述,第二部分是代码实现,第三部分就是时间操作。 首先我们先来看一下本节课的实验现象,本节课我们是基于 vr t r s 操作式系统实现简易的手表系统, 那么它的现象就是右上角的那个电量表示秒数,中间的部分表示月日时分以及星期值。 同时它可以按下屏幕右边的按键实现页面的一个切换,我们可以看一下视频, 右上角就表示描述,然后当它变为零的时候就嗯,这些日期啊,时间都会对应的变化,然后按下右边按键就可以实现页面的切换。 那么接着来到原理概述,首先我们先来大概了解一下裸机,裸机是没有任务调度的,核心就是无限循环加中断, 那么下面这个图形就是大概讲解了裸机的一个呃执行过程。首先第一部分就是它需要一个抽象 出抽象,这部分就是抽象一些硬件的代码,比如说嗯, g, g p i o 或者是定时器一些 硬件,说实话,说实话完成之后就来到一个 y 循环里面,在 y 的 循环里面可以执行呃,比如说读取 rtc 时间,更新 lvgr 界面,扫描按键,切换界面等,但是这些是按照顺序一个一个执行的, 也是说同一时刻呢,它是不能够。嗯,读取 rtc 时间同时更新 lvgr 界面的, 如果他要呃,有外部事件呢,他是可以触发中断的,触发中断之后呢,就可以来到这个中断服务函数里面去处理这个中断事件,处理完之后呢,再次回到这个循环里面去继续轮回,那这个就是大概一个逻辑的一个过程。 接着我们来到这个 free r t s。 的 一个开发,这个过程就是嗯,就通过多任务调度,然后多人分工干活, 就说我们的裸机的这个这些事件都是放在一个外循环里面的,但是 free r t s 就 可以将这些不同的事件呃独立成一个单独的一个任务, 比如说硬件部署化任务、时间更新任务,呃按键读取任务以及 lgr 处理任务等。同时每一个任务里面都有一个外循环,在外循环里面也可以循环执行这个呃对应的一个事件, 由于那个芯片是单核系统,所以说他同一时刻是不能够嗯执行多个任务的,所以说他通过那个优先级来呃决定高优先级的先执行, 有有中低优先级的任务就是后续执行,当那个高优先级主摄的时候,就轮到中低优先级那些任务去执行。如果是同等优先级的,就是,嗯,通过时间片调度,然后使那个 嗯 cpu 在 两个任务之间嘛,然后快速切换,就可以实现看起来同同步同步执行的一个效果 啊。如果你那个要想在任务之间传输数据,就可以通过下棋对列,然后就实现两个不同任务的那个数据传输了, 那么这个就是大概的一个 vr t s。 的 一个过程。那么在手表场景下, vr t s 相比于裸相比于裸机的一个优点,就大概大概有以下几个部分, 就是功能拆分成独立任务之后呢,它代码是模块化了,如你后期想要增加新功能,就比如说你要添添加蓝牙,那么只需要添加新任务就可以了,所以说它的维护性是比较强的, 同时它不影响原有的一个任务,所以说扩展性也是比较强。 而且,嗯,在手表场景下呢, lvgr 可以 单独作为一个高优先级任务,这样定时器调用这个 lv task handler 这个函数的时候,可以在其他任务运行的时候, lvgr 也能够流畅刷新, 所以说 vr tls 是 对那个 rgr 刷新是有很大帮助的。同时它的任务是独立的,所以说它可以精确的定位 bug 啊,可以精确定定位 bug, 比如说按键没响应的时候呢,它是只查按键任务和队列就可以,可以只查这两个就可以呃,定位那个 bug 的 的位置,这样就不需要去其他任务啊,一个依次查找, 这样相对来说就比较方便一点。接着我们本节课的任务呢,就是有四个。首先第一个是 lv handle task 任务,这个任务是通过调用 lv 七幺的核心处理函数, 也就是 lv task handle 这个函数,然后驱动 lv 七幺,呃,完成后台的一个工作,保证界面刷新、动画正常执行、按键操作及时响应等,它的优先级是比较低的。 接着来到这个按键读取任务,在这个任务里面呢,我们可以循环持续的检测手表了,按按键的按下状态,检测到之后呢,将按键事件通过消息队列传递给其他任务, 那么这是这个任务的主要的一个功能,它按键也是优先级也是比较低的。 接着我们来到这个硬件初步化任务,这任务是把手表所有的硬件初步化集中到这个独立任务里面,包括那个按键呢?触摸以及 lcd 屏屏幕和 lgl 的 一个初步化都集中到这个任务里面。 由于我们只需要出使化一次,所以这个任务,呃执行完这些出使化代码之后呢,就会调用这个 v t s delay 这个函数,呃,然后去下回这个任务,这样就可以实现呃出使化,只出化只出使化一次的一个效果。 由于初步化是在前面的,所以说这个优任务的优先级是最高的,需要先优先呃初步化这些代码之后呢,才能够去执行其他任务的一些操作。 接着是界面刷新的一个任务,在这个任务里面呢是周期性的读取 rtc 时间,并且更新手表主页的一个时间日期电量显示的 同时监听按键队列消息,实现按键界面的一个切换。也就是说在这个任务里面,首先就是先获取到实时时间,然后再将这些实时时间显示在屏幕上, 但此时只显示一个界面,如果要显示切换界面的话,就需要获取到按键的一个队列消息,如果检测到按键按下,那么然后再去进行页面的一个切换操作。 接着来到代码实现。首先我们先来了解一下 rtc, rtc 是 stm 三二芯片内部的一个独立时时时钟模块,核心就是脱离主 cpu 和主时钟, 它能够独立运行,并且持续记录年月日时间呃等一些时间信息, 即使 cpu 休眠,呃主时钟关闭它,呃如果有备用电源的话,它还是可以继续计时的,它是手表项目中显示实时时间的一个核心依赖, 它也就相当于芯片内置了一个电子手表,它是专门负责计时间的, 它是不依赖主系统,即使主系统断电了,它也可以通过呃备用电源继继续去计时,那么呃它就是通过以下这两个函数来获取时间以及获取日期的。 接着我们来到呃 qms 的 一个配置,首先我们需要配置 vr tos 的 一个实际源, 那么时机源的定义就是 vr 需要一个周期性的定时器来驱动任务调度、延时、时间片轮转等核心功能, 那么这个时间呃定时器就就叫做一个时机源,那么我们就按照这个以下这个步骤来配置我们的时机源。首先第一步,我们先来到这个 system 库, 然后找到 sms 这里,然后找到这个实际源头,我们可以选择定时器三或者是定时器二都可以,那么这里就是选择定时器三。 接着我们来配置 v r t l s 的 一个接口版本,我们也也是按照这这个步骤来配置这个接口版本,那我们就选择第二个第二个版本。接着我们来添加新任务, 我们来到这个 test and qs 这里,而我们要呃点击右下角这个 app 就可以添加新任务。然后啊就来到这个页面,在这个页面里面我们可以啊配置这个任务名称,任务优先级及占大小任务函数的一个名称。 那么配置完之后呢,我们就可以点击 ok 就 可以啊配啊添加一个新的任务了,然后这个左下角左下角就是一些听啊,已经添加了一些任务, 那么这个赞大小呢?就是这个赞呢,就是任务专属了一二一段连续运营空间,它是用来存放任务运行时的一个临时数据的, 所以说我们在配置的时候,这个幺二八一般是不够的,我们需要配置大一点,比如说五幺二的, 就是说如果任务函数里面局部变量太大太多,或者是函数调用层次太深,中断任务切换开销等这些因素都会导致栈溢出, 就容易导致程序崩溃。所以说我们需要配置,配置的时候需要配置这个栈大小要配置大一点。 接着我们来添加新队列,我们也是来到 test and q 这里,然后来到这个队列,然后点击右下角这个 app, 就 可以打开这个新队列配置选项, 我们配置这个队列名称,队列长度以及单个的消息的大小,队列长度就表示呃你这个队列最多能够存出多少个消息, 然后单个消息的大小就可以是一个字节或是两个字节,那么这里的 uint 十六杠 t 就 表示两个字节。 配置完之后呢,我们就点击 ok 就 可以。接着是使能呃低代码值函数, 那么这个是我们来到这个配置参数这里,然后我们按照这个步骤来,然后找到这个 use tick and hook 这个,然后右边原本是 disable, 我 们需要把它勾选成 enable, 也就是使能低它勾值函数, 那么我们勾选这使能之后呢,我们就开启了这个 d 大 构成函数,开启之后呢,这个飞 r t o s 的 一个系统 d 大 定时器就会每每隔一毫秒就触发一次中断,同时呃自动调用了这个函数 这个回调函数,所以说这个选项的话,如果不开机的话,它会导致它会 gl 能失去那个时间概念, 就会导致那个动画卡死,定时器不触发或是界面不响应等。所以说我们需要把这个给开启,才能够让我们的那个呃屏幕的那个界面能够动起来, 否则它就是一个静态的一个界面,那么对应的代码就是如图所示。 首先我们需要定义一个函数,也就是这个 tusk hook 这个函数,然后我们这个定义这个函数里面呢,就要在呃这个 可以在函数里面啊去引用,去调用这个函数,而这个这函数里面还有一个时间递增函数,它的作用就是告诉 l v g l 图形库已经过去一毫秒,为 l v g l 的 一个相关功能, 比如说动画界面刷新等提供一个时间精准, 所以说它整个流程就是啊,如图左边这些所示啊,如果你勾选了那个,也就是使能了滴答勾子函数之后呢, c r t o s 的 一个系统滴答定时器就会每一毫秒触发一次中断,触发中断之后呢,就会调用这个函数, 调用这个回调函数,我这个在这个回调函数里面再去调用这个函数,然后这个函数里面就有一个时间递增函数,这个函数就使 lv g l 的 一个时间计算器就加一毫秒, 就让那个 m g l 的 一个时间观念了,这样的话就可以使 l v g l 呃时的时间相关功能,比如说界面刷新等,就有一个统一的一个时间精准了,这样 l v g l 呃,它那个界面才会动起来。 接着是配置 rtc 的 一个实时时钟啊,我们来到这个 thomas 这里,然后可以找到这个 rtc, 然后我们首先需要勾选这个 rtc 的 一个时钟源, 它的一个作用就是使其具备计数功能,有了时钟源才能够计数嘛。接着是激活 rtc 的 一个日历,激活之后呢,它能够读日历,然后获取到我们的那个年月日的一些数据 啊。接着是那个下面参数的一个设置,我们可以在这里去设置,嗯 啊,商店之后呢, rtc 的 一个初始时间是多少?比如说这里是设置的二十三分钟,设置为五十九秒,就设置为五十五,就表示呃,初始,初始的时候 rtc 那 个时间就是二十三小时五十九分五十五秒, 然后它的日呃日期就是这里也是设置成二月二十七日星期五嘛,然后对应的年就是二零二六年。 接着是我们来到那个代码讲解,我们配置完那个 qq max 之后呢,就点击上传工程就可以啊,来到这个 k 五工程里面, 首先我们啊点击这个,找到这个 free r t s, 点击这个文件里面,我们需要在这个 begin 和 end 之间啊去添加一些头文件, 因为我们后续需要呃使用一些抽象代码,代码函数,那么要使用这些函数的话,我们就需要呃去引用这些头文件, 否则就会就会报错了。嗯,接着还是在这个 vr t o s 点点 c 文件里面去定义一些核心变量,比如说存储时间的日期的一些数据的一变量 啊,比如说这个第一行就是这里呃,星期制服串数组,我们就把这个周一到周日这些,嗯, 这数据嘛,然后映射成那个对应的一到七,然后我们如果想想要这些周一周二显示在屏幕上的时候,我们就可以直接啊引用这个数组 的一个元素就可以,然后引用这个数组的一个元素之后呢,再将它写入到对应的那个星期的一个标签,就可以让它显示在那个屏幕上了。 然后接着呢我们还需要定一些传输变量,这些传输变量呢就是存储要显示的时间日期数值,也就是月份日期、星期啊,小时分钟描述等。 嗯,接着我们还要定义这个跳库的结构体变量,它就是承接 rtc 提取的一个原始时间日期数据。 我们定义了这个结构体面料之后呢,我们后续就可以直接呃引用这个结构体的成员 就可以获取到 rtc 的 一个日期以及时间。接着我们,嗯,需要来到这个任务的一个代码,首先我们来到这个 lv h 的 test 这个任务,在这个任务里面呢,我们就添加 lv g l 的 一个核心任务, 也是 lv task handle 的, 在这个任务里面的一个循环的一个逻辑,就是五毫秒为周期循环,调用 lvgr 的 一个核心处理函数, 那么这个函数呢,就是驱动 i v g l 完成后台一个工作的,比如说界面刷新了,如果没有调用这个函数的话,我们的界面是不可以刷新的,这是我们来到这个硬件出使出使化的一个函数, 在这个函数里面呢就是添加一些初识化代码,由于我们这个初识化代码只初识化一次,所以我们在呃初识化前面那些代码之后呢,我们会在最后一行去呃引用这个 v、 t、 s、 d 类这个函数, 然后将整个任务来消除,然后完成呃只出只出此话一次的一个效果。那首先我们先来看一下第一行, 第一行就是暂停所有 vrts 任务调度,为什么要暂停?因为呃它是这个任务是优先级最高的。我们我们希望出此话,在所有任务 呃那个操作之前嘛,你就是先浊化,然后再去再去执行其他任务。所以说我们需要可以先暂停其他那个任务调度,先执行完以下这个代码,然后再调用这个恢复函数,然后恢复那个任务调度, 然后接着是一些出使画嘛,那么出使画就分为两个大部分嘛,一个是底层硬件出使画,一个是 g u i 出使画,底层硬件出使画就是包括一些嗯,那个按键啊,触摸芯片 以及那个屏幕的一些抽象,同时还一些开机动画,比如说这个 welcome 以及那个啊那个手表那个版本。然后这个部分大概是这样子,然后接着是 g u i 的 一个抽象, 那首先就是初识化 lvgl 内核了,这个呢就是先初识化内核才能够去使用 lvgl 的 一些 api 函数, 嗯,然后就是接着再初识化这个 lvgl 的 一个显示接口是配的一个函数, 它的一个就是关联 lcd 的, 也就是说让那个 lv 啊 lvgl 知道呃在哪个设备上去显示, 也就是说那,那这个这个这个功能就是让它 lvgl 知道,呃,要将我们的那个 ui 界面显示在我们的那个屏幕上。 我接着是触发这个 lvgr 输入接口适配的一个函数, 它的功能就是连接按键触摸的,也就是说,嗯,我们的那个屏幕按键,呃是可以触摸的嘛?触摸之后呢就会就会有一个反馈,那反馈呢?就是它可以通过这个函数然后传递给 lvgr, lvgr 它就可以接收这些嗯触摸的一些信息,然后去执行对应的一些操作,也就是说让 lvgr 能够接收用户,用户的一些操作。 然后接着是出场那个手表自定义 ui, 也就是我们设置的 ui 界面,我们需要先出场, 接着话,呃,接着我们这些出场就完成了,完成之后呢我们就恢复这个任务调度, 接着我们再销毁置身任务,也就是说,嗯,后续的任务调度就不会再去执行这个任务了,也就不会再去执行, 也不会再去呃,出场这些函数了,那么也就实现了只出场一次的一个效果。然后接着我们来到这个,嗯,也是 vr r t o s 点系的这个文件里面也是这个任务 啊,这是这个就是主页面的一个刷新任务,它就是添加那个主页界面的一些交互的一个任务, 然后它的一个功能就是周期性的读取 rtc 的 一个是时间,同时更新手表主页的一个日期啊,星期时间、模拟电量等一些显示内容。 这一直说啊,更新了这些时时间之后呢就将它显示在屏幕上, 有,此时只是显示一个主页面,如果你想要嗯,切换那个页面,就需要去监听那个队列的消息, 然后判断这个按键是否按下,然后再去执行那个主页面和测试页面的一个界面切换。那首先我们先看一下第一行,就是需要先定一些呃制服缓冲区,存储按键状态变量,呃,切屏标记变量等, 接着我们来到这个 for 循环里面, for 循环里面呢?首先第一部分就是通过调用 hellrtc gettime 和 hellrtc getday 这两个函数都来获取那个时间嘛, 那获取时间呢?就是那时间就存入到我们刚才定义的那个两个结构体之间之中,由我们后续再将 rtc 的 一个结构体数据, 你就是调用它的一个成员数据以后再将它复制给 ui 全职 ui 变量,那就是我们刚才定义的变量 要复制给这个变量之后呢,再将它,呃,通过调用这个 spring f 这个函数, 通过格式化,然后将这些变量,然后的值给格式化,格式化到这个 制服的一个缓冲区,然后再将这些制服串写入到日期的那些标签控控件里面,这样就可以让我们的那个页面上的那些控件能够动态的变化, 同理也,呃,这些星期标签呢?还有那个,嗯,时间标签,电量标签,都是一样的道理,都是 将那个变啊全局变量,然后格式化成字母串,再将字母串写入到那些标签就可以,嗯,实现那个页面上那些控件动态变化。 接着是非主摄读区按键队列,也就是通过这个队列获取这个函数,然后获取到我们这个队列消息,然后再将它复制给这个变量, this key, 这个按这个变量以后,我们接着再对这个变量进行判断,如果等于一就表示我们的按键按下,因为我们前面的那个任务,呃,因为我们后面的那个任务呢?这个 key 与 task 跟这个任务 就是,嗯,就检测那个扫描按键状态了嘛,按下的话这个就等于一,比如说我们判断这个等于一的话,就表示按键按下,按键按下之后呢,我们就将这个变量,嗯,这个 就将这个变量以后由一变为零,或者由零变为一嘛,也是取反嘛。 如果我们再这样对再对这个变量 进行判断,如果等于一的话就执行那个测试界面,如果等于零就测试那个就加载这个主页面, 那么这样就可以实现那个,嗯,按键按下,然后去切换那个页面, 然后接着就是一个任务,休眠一百毫秒的。就是说啊,这个任务呢执执行完之后呢,你不可能一直循环在这里面,你需要主设一会,然后让其他任务给继续执行一会, 其他任务执行一会,然后又主设,然后再再到这个任务去执行一会,这就是跟那个 free rts 的 一个大概一个过程, 然后接着是那个按键提取的一个任务,他这个是主要是负责按键扫描以及通过消息队列将那个按键的一个信息给发送出去的,发送给其他任务。 那么首先,首先第一就是通过那个按键扫描结果啊,首先是定义一个变量,然后存储按键扫描结果, 然后呢再将这个变量,嗯,就是调用这个按键扫描那个函数, 然后将它的那个结果给复制给这个变量,然后再对这个变量进行判断,如果等于一,就表示你那个扫描到了按键的那个值,就是给一嘛,就表示按下嘛。如果我们再通过这个队列发送函数,然后将那个队列那个消息发送出去, 然后同时嗯延任务休眠一百毫秒, 你就说他,你扫描一会之后呢,然后也休眠一会,然后再让其他任务去执行一会,这样可以降低扫描频率,也可以节省那个 cpu。 然后接着就是那个效果嘛, 我们添加代码之后呢,就可以下载了,效果就是这样子, 那同时我们本节课呢?呃,我们是不从零开始去配,呃,新建一个工程啊,我们是已经在一个原有的一个工程基础上,然后去添加我们的这个 vr 贴 os, 那 个操作系统, 有有那个 u i 界面,这些配就是之前就已经设置好了,然后我们就直接引用之前的那个 u i 界面就可以了, 然后这阵就是一个实践操作了,接下来我们就开始实践操作, 首先我们先来到呃这个工程文件,我们打开这个 vr tos watch 这个工程文件,我们要在这个工程文件的基础上去添加那个呃 vr tos 操作系统, 然后然后这个文件到时候也会发给你们,你们到时候也在这个工程文件的基础上去配置那个 qms, 以及添加那个对应的代码就可以了。我们点击这个工程文件,然后打开这个 qms, 然后我们按照那个 ppt 的 那个步骤来, 首先第一步就是那个配置时机源,我们来到这个机子灯库, 然后我们看到这里已经配置好了,我们就选择三或者是二都可以了。接着我们嗯配置那个 vr t 二那个接口版本, 我们点击这个,然后在这里选择这个版本就可以了。接着我们添加新任务, 我们找到那个 task n queens 这个选项,然后我们点击这个艾特就可以添加新任务了。那为了方便快一点,我们就直接呃 直接复制过来就可以了。 然后我们这个啊, lv handle 这个文件啊,这个任务的那个右键就选择 low 就 可以, 然后占大小就选择五幺二, 然后就防止他那个占不足,然后导致那那个程序死机啊,所以说我们要这个占大小,要设置大一点, 嗯,然后这个是设置这个任务函数,然后我们只需要嗯配置前几前四个就可以了,我们点击 ok, 然后我们再打开这个,我们这里就已经配置好了,我们再添加后续的任务, 这个是硬件出实化这个任务, 然后那个任那个任务的名称,他这里的长度是有限制的,你说这里多出的那个长度,那个字母他就不会显示, 然后他的任务呢,我们就设置高一点,那那优先级我们就设置高一点,然后占大小,你就设置五幺二。名称的话, 我们多添加一个 k 啊,然后前面也是添加那个麦, 然后这样就设置好了。接着是配置下一个任务 homepage task, 然后点击添加, 然后这个任务的话就选择露一,然后右下角就选择露一,然后占大小就选择五幺二,然后这个任务函数也是一样, 呃,在前面添加那个麦, 嗯,接着添加下一个, 这是按键捕取的一个任务, 它的任务优先级也是选择 low, 然后啊占大小就选择五幺二, 嗯,也是点击 ok 就 可以了。那么这四个任务就配置好了。 然后接着我们配置那个队列,队列也是我们把这个名称给复制过来, 我们就来到这个,嗯,你是点击这个 app, 然后把这个名称给复制过来, 然后那个对应的长度我们选择它四就可以了,因为我们只需要传传一个按键值, 只需要传一个就可以了。然后那个每一个消息的大小我们就选择呃 u u in 的 八,也就是一个字节,一个消息的大小就是一个字节,那么点击 ok, 那 就可以了。接着我们在,嗯,还需要配置那个 看一下 ppt。 配置好任务之后,我们就要使用这个滴答勾子函数, 我们找到这个,嗯,就参数这里,然后找找到这个 us t 户这里,把这个使能, 使能之后呢就打开了这个滴答够参数了,这时候我们配置 r t c 时钟, 嗯,呃,来到这个 thomas 这里, 我们勾选这个时钟源以及日历。勾选之后呢,我们就来到这个参数设置,我们把这个设置为二十三小时,作为他一个初始,初始的一个时间有五十九分五十五秒, 然后日历的话,这个就选择二十七号吧,也就是星期五,嗯,二月 二月二二十七号,二零二六年,那这样就配置好了, 看一下,我们就直接点击上传工程就可以了。然后呢之前那些,呃,这些配置的话,我们也可以看一下,这些已经配置好了,所以说就不用管。 嗯嗯,然后有还有一个注意点,就是如果你们那个版本跟这里不是最新的版本,你们可以把这个去掉,然后选择对应的版本,那我这里是最新的,所以说就选择把它勾上就可以了。 然后配置好之后呢,我们直接点击生成过程, 嗯,首先我们编辑一下, 没有问题啊,那么首先我们还需要,嗯,先改一些设置,就是首先我们需要把这个 k 点 c 给打开,然后我们啊把这个给注置给去掉, 没有,呃,去掉之后,因为我们后面下面要使用这个啊, free r t o s 自带的这个演示函数啊,要把那个 delete 好, 秒这个给注射掉,下面这个也是这个也注射掉, 然后把这个给把注射给去掉,使用那个 free r t o s 自带那个演示函数啊。嗯,接着我们再编辑一下, 有没有问题啊?嗯,接着我们再啊简单介绍一下这个啊,这个工程文件里面到底有什么?这个主要是啊,已经配置好那些,嗯,已经添加了 那个 g u i 以及屏幕的一些代码, 就比如说这些,还有那个 u r 代码,这些都已经配置好了,所以说我们只需要在那个 呃找到这个 free r t o s 点 c 这个文件,嗯,然后我们只需要在这里面添加代码就可以了。接着我们再看一下那个 ppt 啊,首先是引用了头文件了, 用同文件我就来到妹点 c 以后,把这些呃给复制过来,然后复制到这里,然后接下来还有一个 time 点 c 和,嗯, 这个 rtc 点 h, 这个 time 点 h 和 rtc 点 h 这两个文件,然后添加添加到这里,让我们编辑一下, 那没有错误,有错误我们就接着看这个 ppt, 要接着是添加一些核心的那个变量, 嗯,然后我们这里这个实验操作主要是演示一下这个是怎么添加那个工程的,然后至至于里面 一些函数的一些细节啊,参数细节是怎么配置的,我们我这里就不过多的讲解了,那我这里直接复制复制的话,我就来到这个,呃,已经配置好了一个工程, 我们找到这些,嗯,参数我们直接复制过来, 嗯,然后我们复制到这个 begin 啊和 end 之间。这个我们看到这里复制过来之后,呃,乱码,因为可能是那个字体的问题, 然后我们就不管这些注字那个乱码了,我们直接只需要把这个改一下,周一、周二、周三这些改一下, 而这些,呃,就不过多解释。为什么定义这些呢?因为我刚才 bt 已经讲过了,大概讲过了,因为你们只需要了解一个,呃,大概一个过程就可以了, 我们编一下 啊,编辑没有问题啊,我们接着看这 ppt 啊,接着是在那个 lv handle 了 task 这个任务里面去添加这个 lv task 了,也就是实现那个调用这个函数之后呢,就可以驱动 lv g l 完成所有的后台工作, 那么直接也是把这个,呃,这已经配置好了这个给复制过来, 我们找到这个 lv and 的 这个 task 这个文件,这个任务,然后复制过来, 呃,然后还有还有一个,嗯,就是这个 lvgr 提供了一个时间递增函数,这里我们要定义一下,所以说我们这里也是直接复制过来,把这个任务给复制,把这个函数给复制过来, 嗯,然后复制到。呃, 复制到这里, 然后呢这个定义之后呢?这函数定义之后呢?我们就在这个这函数里面去调用这函数, 然后我们编辑一下 啊,没有问题。 接着我们再看一下啊,接着我们在这个硬件抽象这里,这还这个任务里面去添加一些抽象代码,然后也是直接复制过来, 那这里也是不过多解释了 硬件,然后这里也是注字也是乱码,我们就先不用管了。 嗯,编辑没有错,我们再来看一下这个主页面呢,主页面这个刷新也是复制, 把这个给复制过来, 嗯,主页面这个任务里面把这个给替换掉,然后再编辑一下, 然后接着是那个按键的读取任务, 我们也是直接复制过来,把这给复制过来, 找到这个任务,然后把这个给替换掉,再编辑一下, 并没问题了,我们再看一下,后面就已经没有了,然后我们的代码就是这些了,就是我们要,嗯, 就是有四个任务嘛,然后每个任务里面有对应了一个功能,这个这个任务的就是去调用这个函数,然后使啊 l、 g、 l 那 个完成后台的工作嘛, 然后这个是硬件触使画,就是触使画一些代码嘛,啊,包括那个屏幕啊、按键啊啊,那个引脚啊啊, 以及那个触摸芯片的一些硬件触使画,还有那个啊,一些那个开机动画,还有这个 l e、 g l 的 那个触使画, 还有那个我们的 ui 界面初十化,然后这个任务的话是优先级最高了以后,嗯,也是初十化一次,所以说最后就调用这个函数去删除这个任务。嗯,然后接着是这个主页面,就是 这里是获取时间嘛?获取时间,然后将那个结构体里面这些成员的数值复制给这个传据变量,这传据变量在,嗯,这个格式化成字符串,然后再写入到这些标签, 这样就可以显示在屏幕那个那个页面上,也就是显示到屏幕上就可以使使其动态变化嘛。然后接着就是那个队列,这里是获取队列,获取那个按键值,然后再判断, 如果是判断,然后就判断这个值是否等于一,然后等于一就加载那个测试界面,不等于一就加载主界面嘛,然后实现那个页面切换的一个功能。接着是这个, 嗯,按键获取的那个任务,这任务就是首先是扫描嘛,扫描获取那个按键值,按键状态值,然后再通过那个消息队列发送出去,也就是发送给这个函,呃,发送出去之后呢,在这在这个函数里面去接收, 嗯,然后就就这样子,然后呢这个实验的话,实验的话主要是教了 嗯,怎么了解这个过程啊?然后具体里面的一些实践操作啊,那些函数的,嗯,实践过程 就怎么怎么怎么去配置这些参数啊,嗯,怎么,为什么要调用这些这些函数就不过多让他解释了, 因为如果一个解释的话,那个内容还是挺多的,这里主要是教大家怎么去,嗯,了解这个怎么使用 vr tos 去。嗯,完成一个手表的一个系统, 然后这些代码的话,你们,呃到时候也会把这个工程文件,也就是已经配置好,这个工程文件也会发给你们的。然后你们到时候也可以直接把那代码给复制过来就可以了。 然后我在这里的话,我们就下载试一下, 然后下载成功之后呢,我这边呢是已经可以看到那个时间在跳动的, 然后按下那个按键之后,页面是可以切换的,那说明我们的代码已经 v r t s 配置好了 任务的话,也可以正常的去执行对应的那个代码。然后呢,那这节课的内容就到这里了。

逼自己一个月学完,你的 free r t o s。 水平就很牛了,存下吧,真的很难找全的!本系列课程耗时三个月,制作共计四百三十分钟,全场七小时, 带你一口气从零基础入门进阶,掌握 free r t o s。 操作系统所有重难点知识。课程内容全是行业刚需,包括任务创建与调度、信号量与呼气所消息队列、通信、实时中断处理等高频实用知识点,不讲废话,只讲重点。 由于视频较长,分为六期发布,为了让零基础的小伙伴学习起来没有负担,教程中配套资料以及我整理的全套学习资料, 对零基础超有帮助的学习路径以官家评论,我需要即可全部带走。如果你看到了这,还请支持一下主播,让更多想要学习 free r t o s。 的 小伙伴看到这个视频。 接下来我们开始这套系统课的学习吧!进入式中的 r t o s。 为何如此重要呢?作为一种轻量级操作系统, r t o s。 专注于实时任务处理, 广泛应用于工业控制、智能家居和自动驾驶等领域。与普通系统不同, r t o s。 以任务优先级为核心,确保关键任务优先执行,响应速度可达微秒级。 其具有多个核心技术特性,例如任务调度、中断处理、内存管理以及多任务并发任务调度。而 t o s。 根据优先级动态分配资源,确保高优先级任务优先执行,中断处理。而 t o s。 能快速响应中断,确保系统实时性,例如工业设备的紧急制动。 内存管理。 r t o s。 在 资源受限的环境中高效管理内存,确保任务隔离和资源利用率。多任务并发, r t o s。 在 同一时间异形多个任务,例如自动驾驶系统中同时处理传感器数据和路径规划。 r t o s。 如何开发使用呢? 开发工具,使用 k o c i a r embedded workbench 等工具轻松进行 r t o s。 应用程序开发。开发流程,从硬件设计到软件编程、项目驱动教学,让你快速掌握 r t o s。 开发流程优化技巧,通过任务优先级调度和减少任务切换开销, 提升系统性能。随着智能化产业的涌现与升级, r t o s。 在 未来的发展中前景非常可观。在互联网、人工智能和自动驾驶中将更加智能化和轻量化,在智能家居、智能穿戴等领域将为其提供强大的实时支持。在医疗设备等高安全性领域, r t o s。 将确保系统的可信和安全性。 r t o s。 还将与人工智能深度融合,支持边缘计算,让设备更智能、更高效。在最后,小编为您准备了渐入式学习资料包,欢迎滴滴小编哦,今天就到这里了,我们下期见, 大家好呃,欢迎回来呃,我讲解的 rto 系他的课程呃,今天我开始讲第一讲,了解单片机运行的底层逻辑 啊,单片机的话相信大家读过哎,关于关于我们的电子技术啊,这些课程的话都会有所了解,但单片机它究竟是怎样的运行的?它的底层逻辑是怎样的 啊?今天我来讲解一下。假如我这里,我这个右右边这个图,左边这个框框这里, 它是一个我们采用的一个呃断片机,它是一个 c 八 t 六 st 公司的三十二位 g f 幺零三 c 八 t 六这个型号的断片机,它的内核是 cotas n 三内核, 这个图是一个比较简单的粗略的一个图, 比如我们这个单片机它会包含什么东西呢?包含一个 cpu 是 吧?一个 cpu cpu 是 什么?它是一个呃处理指令的一个地方, 就是处理指令的一个地方,是吧?这个一条指令过来,是吧?它就处理这个指令, 它有什么?它里面就有这个东西,这个东西它是一些 通用计算器跟一些特殊功能,计算器里面就是有一些存放数据的东西, 你可以把它理解成用来存放数据临时处理的一些地方,或者是描描写我们程程序或者硬件它状态的一些东西就好了。 第一点就是 cpu 是 要有的,第二点就是 flash, flash 是 什么?它是一个储存单元,这个储存单元里面存储的是什么呢?可以是我们的程序代码, 我们程序把,呃,就是我们用软件编辑好程序之后,把程序稍写到我们的单片机里面,而这个稍写到单片机里面,那它放在就是 fashion 里面, 它是相当于电脑里边的一个硬盘,里面存放的就是我们的代码, 就是存放我们代码,是吧?加上那我能程序运行的话,就把这些代码一条一条的放过来我们的 cpu 里面,是吧? cpu 执行这个代码,执行我们代码之后 怎么了?他会通常会有一些读写的一些东西,他的一些数据他会放到我们的内存里面,这个是内存 ram 内存, 这个就是我们的呃读写数据的一个内存,这个内存是比较重要的,我们玩单片机就是玩的就是这个数据链,这个数据块里面的东西, 你要计算机吗?单面机也叫计算机吗?计算机就是把东西计算得出一些结果,那结果就是放在我们这个内存里面,明白没用。 而这个 m v i c 是 什么呢? m v i c 是 我们的一个中断控制器,中断嵌套控制器,它是一个呃中,可以把它理解成一个中断控制器, 因为我们的单片机有一个机制叫做中断机制,这个中断就是呃,我们俗称的前后台系统,就是一个中断限量表,这个限量表里面呢?对应有呃每一个中断符函数的地址啊, 我一产生一个中断,我就会跳到这个服务历程里面去执行这个相对应的服务,呃就是中断服务函数处理一些内容,它相当于一个控制器,可以这样说吧, 这个控制器里面有什么呢?有很多的一些中断。呃,这个中断包括一些系统的, 呃,系统的中断跟一些外部的一些中断,那例如是系统的大底寄出去,这些系统的拼打, sv 中断,这些都系统而一些外部中断。 所以我们的单片机它的核核心就是这样一个框架, 根据这个核心我们看这里在这两段是一个代码,是我们的呃返回边代码,看一下我们程序是如何运行的这面函数的返回边,你看这 这些地方,它就是一个指令啊,不是说错了,不好意思,这个是我们的 fetch 里面的指令,就是这个指令。哎,我刚才都说了指令是放在 fetch 里面的,这个就是 fetch 的 地址, 明白没有?这个是 fast 的 地址,而这个才是指令,这个上去知道这个这个东西才是指令,我们的指令, 我们,呃,关于指令的话我下一节才讲,我现在说这这部分就是我们的指令,这个指令会我们放到我们的 cpu 里面,比如这个 是吧?第一条指令 f 七 f 一、 f d 八一是吧? 它会放到 cpu 里面, cpu 会根据这个指令指令码会执行相应的动作,明白没有?而相应的动作其实就是我们后面这里 这个就是跳转,就是进行跳转指令的意思。 还有所以我们整个它面积,它运行内核运行的话,其实是不是很复杂的?简单的来说就是 cpu 曲子曲, 指令就是曲子接着运行指令,一个曲子一个运行, 就是这么简单,曲子运行,曲子运行,它的内核就是这样一个循环。 好,现在看一下下面,这里,它就是我们这里就是我们的 cpu 里面的一些寄存器, 刚才说了一包括一些通风积存器,还有一些程序状态的积存器,特殊功能积存器这些组成的。 这个右边这个表就是我们的中断相关的东西。呃,异常清单嘛?就是异常中断这些 它的一些表,一些清单,刚才有我说的一些这个 kind of s v c s t 系统的集成性, 还有一些外部中断清单。所以,呃,整个运行的话其实不难,单片机整个运行的话它是不难的, 这个曲子运行,但运行的话,你,你想运行他是在哪里啊?一个外循环是吧? 里面运行是吧?当我外循环的话,他会,会什么时候产生中断?是不知道。他一产生中断,一产生这里,一产生中断,他就会跳转到我们的对应的服务函数里面, 会有一个我们说的一个中断限量表里面就会有相对应的中断服务函数的地址,他一产生中断,哎,是哪?这个 mac 判断是哪个中断了,他就跳转到哪个中断服务书里面。 呃,这个地址里面,接着取这个地址运行,跳转到对应的服务函数里面 去运行,运行完之后又跳回来外循环,是吧?又运行完又跳回来外循环去进行外循环的这些东西。 所以单面机它的整体的运行的逻辑就是这样的,其实不复杂,是吧? 好,今天的课程到此结束。哎,下一节我接着讲关于,呃,是,下一节讲的是我们的双对战机制。好,今天的课程到此结束,谢谢大家。 大家好,呃,欢迎回来我的课程,今天我要打算讲一下第二课, coach n 三,它的双对战机制。这个课程是非常重要的,对于学习 rto s 的 话, 对于学习 rto s 来说是非常重要的。好,现在开讲它有四点,我们的 rto, 呃,我们的继承器组还有归战。 第二点是我们的 push 入账,还有呃 put 出账,还有第三点,我的 psp 积分,还有 msp 积分。第四点,模式选择好,现在开始。呃, 大家先这个关于我们的入站,出站这两个呃哈指令的话,先大概了解一下就行,因为下一节我会继续。呃,比较详细的讲一下会编指令,这两个就是会编指令啊,现在可以讲 啊,左边这里,左边这里,这里就是我们的 cpu, 它的一些继电器,刚才上一节已经说了, cpu 有 通用继电器,还有特殊功能继电器 啊,也不多,就十,十,十七个,十七个,对,十七个。好,这里就是。呃,中间这里,中间这里 它是一个,你可以把它了解成一个内存这里中间这里 你把它了解成一个内存,看吧,看不看到这里内存地址,这里是内存地址。好,右边这里,右边这里。它是一个图啊,等一下,会减的。 这位右下角,他这里是一个。呃, 成,呃,我们的看一下啊,他是一个符。嗯,看一下,等一下,这里,哎,怎么有个东西, 哦,对,中断,呃,它的一个中断限量表,对,它是一个中断限量表。哦,哦,不是,它不是中断限量表,它是一个。你看这里 零八零零,它是一个 flash 地址,这也是一个 flash 地址,就是我们复位之后 我们程序跳转到的。呃,第一个位置就是我们 cpu 从这个位置取第一条指令,就是这条指令,这是我们 cpu 取的第一条指令,从这里开始的。零八零零零零零。 这个是是一个 face 地址。好,现在开讲。嗯,出站,入站, 我们怎样出站?怎样入站?我们先讲入站,我们的。呃,这里是 cpu, 是 不是?那 cpu 什么时候发生出站入站呢? 呃,大家想一下什么时候会出现出站入站 是不是要跳转到另外一个函数啊?它就会处在一张,比如呢?我们现在我们程序码是不是一个外循环,是吧? 那它会有很多个语句啊,某一条语句它就是一个函数,比如这里,呃, f, y c, 是 吧?呃,它这个函数是吧? 啊?他执行到这里来了,他就会跳转到这个函数里面去执行,是吧?那他跳转的过程他其实会出征于战,这是第一种情况。第二种情况是什么呢? u y 循环也是 y 循环,是吧?它程序执行,执行执行,是吧?突然外部来了一个中断, 是吧?那中断是什么?中断它其实也是一个函数, 它是一个函数,那会它跳,它也会跳转到函数里面啊?中断函数咯,是不是 i r q i r q 啊?是吧?这个函数里面去执行一些东西,是吧?它会跳转到这里来,它也会出账、入账,是吧?这种两种情况,那我们 r t o s, 它是哪种情况? 我们是不是希望他,他学习性很强?是不是?我们希望 r t o g, 他的学习性是很强的?所以是不是新冠他是中断引起的一个切换了,是不是?这里的中断? 我们一般来说是拼打 sv 中断,拼打 sv p d s v 引起了一个中断,它是一个远中断,它,呃,触发了某个位,它就可以引起一个中断,在中断里面进行一个切换 啊?中断服务函数里面进行切换。所以大家首先要明白什么时候会进行出站入站,这是第一点。那出站入站基金是怎样呃运行的呢?你看,首先 这个,是啊,我们 cpu 的 一些计算器,是吧?首先它会最从最底端这里,这个是程序状态计算器 pr, 它会先把这个东西放到我们的内存里面,这个是你内存地址 放到内存里面,是吧?你看,我都已经写好了 p s r 这接着这个是 pc 寄存器,八十五是 pc 程序寄出器, 它会把它放到这里来,是吧?第二个这里 l r 是 吧?它是,呃保存我们的返回地址的 l r 寄存器,它放在里面来,是不是 接着 r 十三?不是 r 十三,它只是一个指征, 它是,呃,我们当前指向这个指征指向我们内存的某些地方,它的地址,明白没有?这个我们不是出站入站的,我们 r 十二 来到了二十二是吧?现在我们出站,入站多少个了啊?入站多少个了?一二三四,入站了四个是吧? 那第五个呢?从哪里开始?阿三,从这里开始,这里开始。阿三,阿二 二一二零。好了,八个, 八个。这八个是我们一开始把 rto 写 rto s 产生中断,拼打 s b, 中断它进进行入账是吧?拼打 s b 这个函数进入函数之前 就是它之前,进入函数之前它是硬件自动入账的,明白没有?那硬件自动入账是吧?那现在我们的呃 msp 之争 啊?不是。呃,我们的 p s p, 不是 l s p。 说错了,不好意思啊,因为,呃,刚才我说纸张,我还没说,所以我先剪剪个答案给你, 我们现在我们的 p s p 指直线这个位置, 接着我们要做什么?下一步要做什么?我们才入了八个啊,总共是多少个?总共是。呃,总共十七个,减去这个二十三,是不是十六个?是不是总共十六个? 现在我们还有八个还没入账的,那这八个要不要入账?肯定要的嘛,是不是这个八个?这八个的话是我们保存的一些临时,这个继承期里面保存一些临时数据,如果你不让不入账是吧?你不保存它 下次程序运行产生的一些中间的一些变量,他是不是会把它覆盖?是不是?所以我们一定要把它入账,入账。怎么入账?你看 我们进入了我们的服务函数,我们需要做的是什么呢?我们在服务函数这里, 我们首先进入好之后,我们首先要在这一开始这一段要手动的,手动啊, 手动的去保存,我们剩下这些计算器哪些?这这几个是不是 r 四到 r 十一这几个, 这八个手动的保存到我们的内存里面。保存,手动保存。大家记住没有? 如何手动保存的话,到时候我在程序里面。呃,会详细的来说,因为它设置到会编的指令。好吧,它设置到会编的指令,到时候我程序里面会说的, 你看,接着看一下右边这个图,右边图它是一个堆赞,这个一个堆赞图, 你看对战图第一个位置,这个位置就是我们的。 呃,我们的内存第一个内存地址是吧?这内存内存地址放的是什么?嗯, 哦,不好意思,选一下。对,这个是我们的 flash 的, 刚才说了吗? 我闪存的地址,闪存地址是存放这个,存放这内存它的地址,这个内存地址存放在闪存里面, 明白没有?哎?这地偏移的四位,四位之后存放的是这里, 这个就是一个 v c 函数,它的一个地址。明白没有? v c 函数,它的一个地址,先不管这个。呃, v c 函数之后,这里会跳转到这里来,是吧?会进行复位,接着启动引导程序,引导代码, 我们主要关心的是这个位置,不好意思,暂停一下。 嗯,不管了,先不管,先装完它吧。呃,你看我们这个位置 是我们的内存地址,是吧?接着我们从内存地址这个位置会写入我们的 psr, 是 吧?接着第二个入站,这个 pc 是 吧?第三个入站是 lr 这样,所以堆站它是往下生长的,就是 他这个就是站顶吗?是不是我们的站顶?接着我每写一个他就往下去扩展我们的内容, 明白没有?而堆他是不一样的,堆他是往上的,但我们现在不说堆,我们说的是站,所以我们了解站就行了,我们每写一个就是每入站一个,每入站一个就往下生长,所以到了这里是吧? 所以最后 psp 就 指向了这里。大家我先问一下,问一个问题给你们,到时候再陈述你们再你们再得出一个答案,我们 psp 我 这个值,我需不需要去保存它 啊?我需不需要去保存它保存到哪里?大家想一下,因为我们呃保存的这个占内存嘛,是不是我们下次 需要读的嘛?这个是入站,我们出站怎么办?我出站是下次啊,从这个位置开始读是吧?从这个位位置开始读,一个一个一个一个的读出去是吧? 读完之后,呃,接着这个就是自动出站了,上面上面这八个就是自动出站了, 这就是一个呃任务切换它的一个流程。下面也其实也不用讲了,刚才已经讲的很清楚了,这是出站入站啊,讲一下这里,这里也很重要, 这个是一个控制计算器,这个控制计算器是 quarters m 三,它的比较重要的一个计算器, 它只要两个位,一个位,这个它是一个选择我们的呃模式,它是呃现成的用户级模式还是呃现成的特呃特权级模式, 用户级跟特权级啊。首先说现成模式是什么? 大家想一下现成模式是什么?除了现成模式还是什么?什么模式?从这个图可以看得出 hunter 模式, 所以我们的单片机程序里面有两种模式,一个是现成模式,又是 hunter 模式。 hunter 模式简单来说就是我们的中断, 我们进入中断之后,他就自动进入进入了 hold 模式,而中断以外的都是我们的现成模式, 大家明白没有?所以就两种很简单嘛,一个是中断,你把它理解成进入中断就行了嘛。一个是没有进入中断,就这么简单。而特权级跟用户级又是什么? 特权级,他的意思是我可以访问一些高级的寄存器,他叫特权级,如果级就是我不能访问一些高级的寄存器里面高级的寄存器就是指特特殊功能寄存器。 呃,例如是中断相关的啊,中断相关的继承器啊,或者是程序状态继承器,这些就是特殊功能的嘛, 我们就不能反问,如果你是用户机,特权机它才能访问,明白吧?那大家想啊,他我为会您怎么只都是只能设置 现成现成模式不能设置。呃,嗨的模式肯定是不能的吗?中断他是自动进入的吗?是吧?自动进入嗨的模式的吗? 我们只能选择什么呢?选择现成模式,它现成模式它只能选择特权级和用户值,而 hand 模式它是肯定是一个特权级的,它肯定是可以访问一些特殊功能计算器, 只有现成模式才有的选择,我们一般的话也会把它选择选择成特权级, 系统默认也是可以选择,呃,也是选择特权机,就是你可以去访问他,访问一些特殊功能计算器。好,呃,接着卫 卫仪,卫仪是选择我们的,呃, m s b 指征就是主堆站指征,呃,县城堆,县城堆站指征。这两个堆站指征其实就是 r 十三,刚才说的 r 十三, 呃,我们 cotz n 三,它是双对称直角的,呃,你,你选择零的话, 那它就是选择了主单向直角,呃,直角,而选择一的话就选择了,呃,近程对称直角。 那我问大家,我,我进入了中断之后,那我这个 r 十三代表的是 r 十三代表的是什么?代表的是 msp 主堆栈子帧,就不用问我,它程序就是这样设置的, 听到没有?但我,我又要问你,我进入了中断之后,那你刚才说嘛?我拼到 sv, 它是进行用户切换的,是不是?我拼到 sv 进行用户切换,那用户切换?假如我, 呃,我的任务是用 psp 指征的,那你说我进入任务,呃,呃,终端之后,我怎么去用 psp 呢?你不是说用 nsp 吗?其实,呃,我们可以 选择用 psp 的, 进入终端之后,我们也可以对 psp 进行读写, 明白没有?是可以进行读写的,但你,你想啊,它,呃它的入站出站,它入站出站它是默认使用 nsp 的, 这个它会前置变为 nsp, 但你可以设置设置为 psp, 但不知道大家明不明白这个道理,嗯,到时候在程序里面我会呃详细的讲一下。好,这里是一个呃 呃现模式的一些转换模式转换图其实也刚才也说了, 我们,呃,看这里就知道了他的模式,他这是一个中断,他有异常的时候就会进入他,就这么简单。 好,呃,今天的课程到此结束。呃,下一节讲一下我们的会变语言。好,谢谢大家。 大家好,欢迎回来我的 ios 课程,今天我要讲解一下第三课,掌握基本的会编指令。 呃,我想先问大家一个问题,会编指令它究竟分呃,哪种?哪几种?好了,回,回到上上一节课看看, 看看这个图,你看我之前说过了吗?这里,呃, 这个位置就是我们的指令,是吧?这个位置就是我们的指令。那指令你看有些他是多少个位?呃, 八八乘四,八乘四,三十二位,是不是有一些他是三十二位的?你看有一些四乘四十六位。哎,为什么有一些指令他是三十位,有些指令他是十六位的。 大家有没有想过这个问题?其实我们的会变指令其实是好多种的, 这只是我们单片机他的一些指令机, 还有其他的指令呢?你看 x 八六,我们经常用的计算机,他也有会编指令的,他也有一个指令机啊,所以会编指令他的范畴是很大。那如果你 说你会不会编指令,那觉得你反而人家觉得你读他会。会编指令,应该说你会会编指令。哪一个指令集就是会编指令,是吧?嗯,指令集, 你要说明你会的是哪些指令集,明白没有?因为会编指令范围是很广的。 好,现在讲一下我们简单的一些基础的一些会变指令。 好,会变指令总量,它有很多种的,很多种的。现在我讲的刚才说的。哎,有一些三十二位的,是吧?有一些十六位的, 而十六位的我们通常接触的单边只有二指力,它是十六位的,也有三百指力,它也是十六位的,也有三百二指力,它是三十二位的。 呃他是。哎,错了吧?说错了,不好意思啊。哎,这里怎么这么搞?说错了,调过来 调过来。对, 三十二位也是三十二位,这才是十六位, 我们的三百二才是十六位的。大家清楚三百二才是十六位,它是比较新的一个指令级十六位。十六位跟三十二位它的区别就是 十六位比三十二位。他的指令他的呃指令密度啊,就比较紧凑。明白,没有他指令他的密度就比较紧凑一点。 好,呃点一下我们指令它的类别有哪些?主要有四大类,就是一个数据处理,就是数据操作的。数据操作类一个是内存读写的,一个是跳转的,还有一些其他的 啊数据操作主要是什么?加法啊,减法啊,乘法啊,一起加载啊,逻辑运算啊, 你看逻辑运算还有什么?呃左移啊,右移啊呃这些左移右移啊那些 呃还有一些暗微操作啊暗微啊,暗微操作啊暗微语啊那些啊这些暗微。暗微语也叫逻辑运算嘛,它也是暗微操作 加载就是把一个数加载到另外一个继承器里面啊。乘法不用说了,加减乘嘛就没有除的,反正就不见有除的操作。 你别说我们的,我们都会编指定有个除法的指令,但是没有的只有加减乘啊。呃除了我们的数据操作还有一个内存读写,内存读写就更简单,也很少, 主要就是读内存写内存。内存嘛。内存不是寄存器哇,大家要清楚啊,我们寄存器通常说的就是我们的 cpu 啊,就那么几个,刚才说了啊,十十七个减去一个 s b 的, 就这 r 十三的, r 十三是一个直角嘛,其他的就是一个 r 十六个除张除张, 所以我们通常说这个数据操作就是应用在这里,应用在我们的计算机,而我们数据内存读写主要的就是对账, 这里就是对账。什么读内存啊?读对账嘛就是读内存就是读对账嘛。啊就写啊啊读啊, 读啊,读到哪里啊?读到姬存希里,读到姬存希里面嘛,是不是写啊?把姬存希写进来嘛, 是不是?所以这个数据操作主要是操作的就是我们的同城集选区啊,内存操作,内存读写槽主要是我们的对账啊,出账入账啊,出账入账。 没?不用说了,就跳转就更简单了,就一个无条件跳转一个一个,还有一个有条件的,条件的条件的。有个有条件的。这里没有说我 啊。什么有一些 eq 啊,是吧? eq 啊,还有什么? 哎,忘记了,反正就它根据有。有四个状态位嘛有四个状态位 有四个状态位,呃标志也不算状态位。四个标志哪四个?一个是复数标志, 一个是除了复数还有一个。呃呃进位好像是进位进位,呃。一个溢出 还有一个,嗯忘记了,反正就四个,哎。这个这里啊,好多种的,十五种, 这个就标。呃。这个是叫什么?嗯条件嘛?对的十五个条件嘛, 它有十五个条件嘛?这个是相等, 呃对,呃。还有一些是否负数啊什么的,是否进位啊?呃是否溢出啊这些所以它有十十十五种条件嘛, 它会把这些条件码放到我们的 b 后面,这里后面会有 e q 是 吧? e q 是 不是相等是吧?它会根据这个条件码来跳转,如果它先等了,它就会跳转到 呃,相应的一个位置里面,明白没有啊?这个位置是哪里啊?就是我们的 l r 吗? 跳转到我们的 l r 里面嘛。而这个 b l r 是 什么? b l r 是 它跳转了 l r, 经常更新的 l r 就是 这个意思。而 it, it 什么?类似于 if 是 吧? f 如果是条件福利了,成立了就执行这个,如果不成立了, 他就执行这个是吧?那么他还有还可以这里又加一点 i n f 是 吧?这又加一个条件,又跳, 它有好几个的,这个 i t 还有 t 啊,你加加几个也可以,它就这样,它是一个语句块 啊,与巨款大家不不清楚的话可以去酷特斯 n 三塔神的一个呃指令机里面去详细的去了解,它里都有很详细的说明的,或者去去百度搜索一下也行 啊。其他指令就不多说了,就一个开中断和关中断用的比较多的, 开中断,半中断这两个 c p s i i e c p s i d 啊, i d i e 嘛,这中断 enable 嘛,中断 disable 其实就会变指令也不多,主要是,呃,你们多可以把一些 你们写的一些习语言程序经过反馈编之后,他会生成一些会编码,你可以多去把那些会编码去呃,学一下,看一下,熟练一下, 就基本上可以掌握了。能写能看就行,写的话其实不不太需要自己去写会编的 啊,大家明白,其实不太需要进行写会编,哪些需要呢?就是哪些需要去写会编呢?就是出站入站。刚才说了这些要访问一些特殊功能计算器啊,这些 特福特访问特殊功能计算器这里好像没说哎。还有一个,这个 m s r m r s 这两个指令,这个就是写的 写特殊的功能的计算器的,这个就是读的读特殊功能计算器的, 明白没有?刚才上一节我讲出站入站怎么说的,你进入了憨憨的模式, 那它指 r 十三变成了 nsp 是 不是?那我 r 十变成 nsp, 我 怎么去处理 psp 的 数据啊? 怎么办?我读嘛,我可以通过这个汇编指令,是不是 r m r s 读我们的 p s p 指征是吧?读之后那我们是不是可以保存呢?是不是保存 或者呃,读取嘛?接着我不要,我需要保存了,我去保存它啊,我去写它。那我写呃 psr 嘛,是不是? 是不是我保存完了我写一下 psr 现在的词是不是?所以就是,所以说有一些地方它还是要优惠,变成 有些呃,大部分的不需要,有一些关键的还是要。嗯,也不多,大家自己去消化一下。今天的课程到此结束,谢谢大家。 大家好。呃,欢迎回到我的课程,今天我继续来讲最后一节课第四课,手把手教你如何编成一个 r t o x。 呃,上几点已经讲解了好呃,比较多的一些内容,关于算对账啊,会编啊呃之类的,今天的内容也是非常硬核的 呃,这七个步骤都是比较重要的。接着最后还有一个呃,我字的编辑,好的,一个 小型的 r t o s 我 会展示给大家看看之前要先了解这些内容。好,先看点。第一,原子操作。什么是原子操作?原子操作就是 让我们的读且改是一个完整的过程。读且改啊,不好意思,读改写, 读改写,它是一个完整的过程,不可以打断。明白没有?这一点, 就是他这三步骤是同时进行啊,不是同时进行,他是连续进行,不可以被打断,他叫软式操作, 这是对于 r t o s 来说是非常重要的,因为 r t o s 它是一个怎么一个情况?它是不断的去切换任务,切换任务就是出站入站,出站入站是吧?那出站入站的话,假如我在任务 这里有一个任务一,这有一个任务二,是吧?任务一跟任务二前面有一个, 呃变量,这是全变量,我对全变量进行写一操作,但,呃呃,或者这样加加吧, 我对他操作,我也对他操作,那操作这个的时候他是一个读改写,操作这个的时候也是一个读改写, 他在读改写,比如我任务一,他在读改写的时候被打断了,读改写,是吧? 行,这也是读改写, 对吧?我读 读改写,改了之后我没有写进去,我又读,我读的是什么?是他写之后的诗吗?不是,是他原本的值,对吧?原本值,他又改又写 又写回去了,是吧?那我现在改完之后,我写的值是哪个? 我问大家写的是哪个?是他当前堆在里面继承器的值,明白没有?他,所以他只写了一次, 明白,没有。这个我这个写了没用了,我这个写没用了。其实写的就是我堆在里面这个值,这个阿里他这个值我写的没用了,我把它覆盖了, 它就不是一个原子操作了,明白没有?它一定要是连续的做完了这三个步骤,你才能做其他连续的操作,这叫原子操作,这个对于 rtv 来说非常重要。 好,废话不多说。下一个 用户状态,用户状态有哪些?我们 rtv 用户状态有哪些? 一个是就绪。 哎,就 就字怎么写?打出来了忘记 就绪。一个是就绪态,第二个呢? 延时,延时差,呃。不是,就是一个,是我们的运行 三,还有我们的挂机 四,我们的通道五,我们的删除, 我们的任务状态只有这五种,明白?没有?我们任务一个任务他只有这五种状态, 大家机器人没有这五种状态,它是可以,有一些是可以相互转换的,有一些只能单向,也有有些是双向。 比如我就绪是吧?就绪之后它到到达了最高优先级,它就变成了转运行了,是吧?当我发生了挂运行之后,它发生挂起了,它也会被挂起, 对吧?如我中弹来了,他会打断这个运行, 他会打断这个运行,但我删除也会把就是他里面东西删除,他就不能运行了。所以他是互相可以会转换的,也不是说互相,他有一些地方是可可以转换的, 大家了解一下就行。呃,这个任务状态好。呃。下一点, 下一个我们的优先级,这个是非常重要,其实这两个都非常重要,我们的优先级。任务优先级跟终端不一样,任务优先级他是可以无限多的,大家可以 啊,可以无限去设置我们的用户优先级。呃。假如我设置了三十二个用户优先级,我可以用一个变量来表示我们的优先级,什么意思?只要我这个变量它是三十二位的, 这里是位三十一是吧?这,呃,下面这个位三十是吧?最后位一位一是吧? 那假如我用一个变量去表示,表示优先级是怎样表示?嗯, 我这个优先。呃,我这个任务,这个任务,假如这个任务 一,这个任务一他优先只是五,那五十在哪里?数一下这个二、三、四是吧?五,五在这里,五, 我这个任务就会放到五的就绪列表里面,他一创建他就是一个就绪状态,明白没?我,我今我会任务会插入到我们的就绪列表里面,那我就绪列表 会让这个变念致意,致意的意思代表我就绪列表里面有任务就绪了的意思。那我假如我优先寄回你,我对应的也有一个就绪列表, 这个就绪列表假如它是有就绪任务的,那它它也致意, 因为我这个三十二位的变量,他就变成有三十二位,他的对应的位,他表示当前这个位里面任务的就绪列表有没有就绪任务, 那我现在为的,比如我为零有就绪任务了, 我为您代表是就优先级为零的。呃,继续那个,那我,那我是不是也现在优先执行?他是不是?所以他就是一个寻找这个,呃,变念 变脸,哪个位有优先级就执行哪个就绪列表里面第一个任务它的意思,所以这里优先级说完之后我是不是延伸了一个任务列表, 是不是现在开始任务列表了?大家要注意一下。

哈喽,大家好,这期视频我们来分享一个单片机项目,个人感觉更偏好软件方向,也就是编程那些。硬件的话比较简单,首先说明该项目在立创开源平台上就能找到 效果如下,这些界面主要是通过 l v g l 去实现, 这里有其主要框架,其实最核心的就是 lv g l 和 freeitools 了,这两个需要你提前去学习,其他的就是平常的模块开发了。 当然你可能觉得这个手表要实现好多功能,其实你也不需要把这里展示的所有功能实现,因为越多功能代表越多模块,然后也越复杂,同时越多模块。为什么说越复杂呢? 因为数据你得考虑什么时候读取,什么时候使用,这都是难点,建议去体会一下。会遇到各种奇奇怪怪的逻辑问题。 这个项目主要是练习 freer tools 和 lvgl, 然后最重要一点是多个模块跑起来的兼容,思考如何解决逻辑 bug。 然后硬件上的话其实不是太难,你现学现卖就 ok 了。难点在软件上,单片机型号可以选择自己熟悉的 stm, 三二的话推荐选 f 四系列以上的主要是 f 一 那些可能内存相对较小。

are you with me? i with me? are you with me? are you with me?

前面的两期视频中,我们详细讲解了 fraortos 中最重要的任务间通信方式队列,它就像一个传送带,生产者任务负责往里塞数据,然后通知消费者任务,把数据取走。 无论是普通的数值还是复杂的结构体,队列都能帮我们安全有序的在任务之间传递。但在某些实际工程的场景下,小伙伴们会发现一个有意思的现象,有时候我们并不需要传递什么数据,只是想要通知某个任务,某件事发生了, 比如数据处理完成了,或者中断发生了,按键被按下了。虽然这种事情我们依旧可以使用队列进行通知,但好像有点杀鸡用牛刀了,所以 fritz 为我们准备了更合适的工具。二进至信号量 哈喽,欢迎小伙伴们回来,这里是立制作最好、最简洁、最等教程的 kiss king。 二进制信号量其实本质上就是一个被阉割到极致的队列, frattos 内部附用了队列的代码将其实现, 进而我们才说他是队列的小弟,因为他本质上就是一个长度只有一的队列,而且他这一个的位置大小也是零字节,存储不了任何数据,只能通过队列的相关属性表示有数据还是没数据,进而才叫做二进制信号量。 二进制信号量适合于某个任务需要等待某件事情的发生,比如任务 a 想要等待事件 s 发生后再执行接下来的代码,而任务 b 负责完成事件 s, 或者负责判断事件 s 是 否发生。 那么任务 a 就 可以通过 os symbol acquire 函数试图获取代表事件 s 发生的二进制信号量,如果没有获取到,那便进入阻蚀态等待不占用 cpu。 而当任务 b 完成了事件 s, 或者得知了事件 s 发生,则通过 os symbol release 函数释放事件 s, 这时 frattos 就 会唤醒任务 a, 任务 a 成功获取到此信号量,得以向下执行代码。 我们不妨来举个例子,在上期视频中,我们利用队列的多对一通信实现了使用串口与按键控制三色小灯,小伙伴们可以在文档网站找到上期视频的工程下载链接。 其中按键检测任务是通过轮询方式实现的,不管按键有没有被按下,都会每十毫秒检测一次 g p l 口电瓶状态多少有点不够优雅, 因为大多数时间里按键其实并未被按下,但任务却在反复醒来,为一件没有发生的事情进行判断。 如果我们能让任务在按键真正被按下时才会被唤醒就好了。这一需求对某些需要低功耗的产品尤为重要,而这就是二进至信号量大显身手的好时候了。我们可以在外部中断检测按键下降时对二进至信号量进行释放。 在按键任务中试图 aquire 获取二进置信号量,这样按键任务就只会在中断触发释放二进置信号量后才进行按键消抖与队列发送相关的工作。那让我们打开工程的 qmx 配置。 fratos 的 设置中 thermos and semiforce 选项卡,其中会有 binary semiforce 的 列表框, 我们点击爱的进行添加。首先要起一个合适的名字,比如根据我们的需求场景叫做 key simple, 然后是出场值,默认值是 available, 可用的意思是二进式信号量在程序启动后就可以被消费者 acquire 获取一次。另一个选项是 depleted 的, 耗尽的意思是二进制信号量在程序启动后不可以立刻被 acquire 获取,必须由生产者进行 release 释放,消费者才能 acquire 获取到。选择 available 还是 depleted 的 取决于我们的应用场景。 比如现在我们使用二进置信号量的目的是为了进行任务同步,需要按键按下后中断触发按键任务,才需要执行按键任务,在程序启动时就执行一次,毫无意义。因此我们选择 delete t 的 即可,然后点击 ok, 这样我们就创建好了一个新的二进置信号量,让我们保存并生成代码。随后在 frontos 点 c 中 我们就能看到 q 版 max, 帮我们用 os sum for new 创建好了我们的二进置信号量。第一个参数 max count 是 一, 代表这是一个二进置信号量。后面我们学习计数信号量时,这里就会填别的数字了。随后 initial count 被设为零,是因为我们出示值设为了 depleted, 如果我们设为 available 的 话,这里就会是一了。 最后这个属性参数里其实就写了个名字。然后为了能在其他文件中使用 key semple, 我 们将其定义复制一下,来到 men 点 h 进行 external 导出, 随后来到 k task 点 c。 之前我们已经引入过 man 点 h, 随后我们来到任务函数中,在循环的一开始,我们就调用 os symbol acquire 函数来试图获取信号量,要获取的信号量是我们的 k symbol 最后一个参数,跟我们之前学习的队列相关的函数一样,超时时间。因为没等到信号之前,按键任务完全无需进行任何操作,进而我们写 oh sweet forever 一 直等待, 这样按键任务就会在循环一开始便尝试获取 k sigma, 获取不到就会进入阻设态睡大觉, 直到有生产者释放 k c m 否才会醒来。完成按键的消抖判断等剩余任务,随后循环回来,试图重新获取 k c m 否,然后进入阻设态睡大觉。 当然这个判断按键按下的函数我们之前的写法比较依赖多次循环,但是现在因为信号量的阻设,每次任务运行它只会被调用一次了,进而我们将其改成一次性完成消抖的写法。 ok, 那 这样按键任务我们便改造完成了。随后我们就需要使用外部中断来充当生产者释放 ksim 4。 首先我们需要回到 qmx, 将按键的 gpl 口改为外部中断模式,名称记得改回去。在 gpl 设置里, 我们可以将触发模式改为下降延触发,然后因为学习版的 k 一 有外部上拉,进而我们保持不上拉不下拉。如果小伙伴们要用 k 二的话,记得设置内部上拉。 然后来到中断控制器 n v i c 的 设置,将相关的外部中断打开, ok, 保存变生成代码。 然后我们找个合适的地方,比如 gpl 点 c 的。 最后来定义外部中断的回调函数,可以用 ctrl 加 shift 加 f 调起全句搜索,找到回调函数的 wek 定义,然后复制函数定义过来。 函数内,我们首先判断一下触发中断的引脚号,要严谨的话还可以再用 hell gpl read 读一下当前 ke 的 电瓶, 随后使用 ossim for release 函数释放二进至信号量,只有一个参数,就是我们要释放的信号量,因为信号量没有像队列那样的阻滞的情况,进而释放信号量没有超时时间。 ok, 这样我们就完成了在中断中释放信号量,唤醒在阻设态沉睡的按键任务,完成按键的检测等相关逻辑。为了便于观察,我们还可以在按键任务的循环中加个串口输出。 ok, 弹幕喊出我们的口号,编辑下载一气呵成。 按下按键,按键还是能够正常控制,红色小灯开关没有问题。看一下串口输出,在我们没有按下按键时,没有任何串口输出,按一下按键就输出一次,获取到信号量, 说明按键任务一直是处于阻设态当中,没有运行,只有按键按下后中断释放了信号量,按键任务才获取到信号量,运行了一次,完美。 ok, 本期视频内容就是这些了。二阶质信号量非常简单,本质就是一个长度为一的队列, 不负责传递数据,只负责传递事件是否发生的信号需要等信号通知的任务。使用 os symbol acquire 进行信号获取能够释放信号的任务,或中断使用 os symbol release 进行信号释放。 不过二进制信号量的局限性是只能知道事件是否发生,却无法计数事件发生了几次。如果事件快速发生了多次,而处理任务又处理较慢的话,可能会只处理到一次。对于我们的按键任务,这样其实还不错, 但对于希望处理次数与事件发生次数一致的需求场景,我们就需要用到下期视频要学习的技术信号量了。 求点赞!求用币、求收藏、求转发!没点关注的小伙伴赶紧点击关注不迷路!你的鼓励是我持续更新更多内容的动力,让我们下期视频一会见,拜拜!

操作系统的这个消息队列,他在设计的时候实际上考虑了很多的异常,就比如说原子性啊、互斥性啊,好在设计这种消息队列的时候实际上已经做过一些处理了,那我们如果是自己去做这种信号量全局的话,你可能在这个终端里面可以去操作他,另外一个终端里面也可以去操作他。 然后这个中段的优先级如果你不太确定的话,那就可能会导致这个代码最终执行就跟你期待的不一样,你去上这个虎牙 q s 的 话,你其实任务之间的优先级都是可以去指定的,获取这个数据,你传递这个信号就可以更明确一些,它就可以避免一些诡异的 bug。

下面呢,我们来以 a few rts 为例,来给大家介绍这个 few rts 的一个操作系统。 那么我们首先来看一下 feel rts 这个名字,这个名字呢,可以分成两部分,第一部分是 f r e e feel 啊,第二部分呢是 rts, 那 feel 啊,就是免费的,自由的,不受约束的这个意思。那么阿 qs 呢,就我们前面说的一个实时操作系统啊,他的一个首字母的一个缩写, 那么可以看出啊,从名字就可以看出啊, feelrts 啊,他就是一个免费的 rts 的一个系统。那么这里要注意哈, itos, 他并不是指一个某一个确定的系统, 而是指一类系统实时操作系统,那比如 mucose 啊, mucose 啊,还有我们的现在学的 i feel itos, 或者是 it straight 等等啊,他是指一类十操作系统,那么操作系统啊,他是允许多个任务同时运行的, 那这个呢,叫做多任务,实际上一个处理器核心呢,在某一时刻只能运行一个任务, 这跟我们裸机执行是一个道理。我们的 cbu 在执行我们的工人的时候,我们一个时刻只能执行一件事啊,他只能干一件事。 那么操作系统中的任务调度器啊,他的责任呢,就是决定在某一时刻究竟运行哪一个任务,那么任务调度啊,在各个任务之间进行一个切换,这个速度呢,非常快, 这样就给我们造成同一时刻会有多个任务同时运行的一种错解啊,学过啊,动态数码馆的啊,朋友应该知道,利用我们人的一个余晖效应啊,来来, 让我们感知啊,他是同时显示啊,他是这个是一个道理啊,就是说他的一个切换任务切换的速度啊,非常快,让我们感觉到这个些东,这些任务啊,是同时进行运行的啊,同时运行的,那么操作系统的分类方式啊,可以由 任务调度器的这个工作方式来决定。比如有的操作系统啊,给每个任务分配同样的运行时间,那么时间到了就轮到下一个任务执行, 那么 unni 啊,这种操作系统啊,他就是这种啊。而 ts 的十任务调度器呢,被设计为可预测的, 而这正是嵌入是实时操作系统啊,所需要的实时环境中,要求我们的操作系统呢,必须对某一个事件做出实时的一种响应,因此啊,系统任务调入器的行为必须是可以预测的。 那么像 furts 这种传统的 rts 类操作系统呢,是给由用户来给每个任务分配一个任务优先级, 那么任务调度器呢,可以根据这个优先级来决定下一个啊,运行的是哪一个任务,根据你的优先级的高低啊, 那么 furts 啊,它是一个,呃,是 rts 的一种,那它的一个是一种十分小巧,可以在资源有限的微控制器当中进进行一个运行。当然 furts 不仅局限于微控制性当中使用, 那他在一些啊,其他的一些大型的这种啊, mcu 或者是一些啊 mpu, 他也可以运行 furts 啊。 那么如果你学习过,在学习服用 rts 之前,学习过没有 cos 这种操作系统的话,你会发现 furts 的它的文件数量啊,远低于没有 cos 二或者是三啊,没有 cos 啊,我们在我们的这个资料里面也有他的一些啊历程啊,你可以后续对比一下他的工程啊,他的一个 呃圆码的一个数量啊,相差是非常大的,那么 feel rts 的特点呢?我们来看一下 furts, 它是一个可裁剪的小型的 rqs 的一个系统,那么它的特点呢,包含这么多啊, furts 的内核支持抢占式 啊,还有合作室和时间片的一个调度,那么抢占时啊,是我们实时操作系统当中啊,必须要的啊,因为我们要让我们的一个啊,高优先级的任务啊,优先执行啊,抢占低优先级的啊,随便的 资源来执行。那么还有 sf rts 的衍生字,我们的这个 furts, 那 sf 这个 rts 在代码完整性上呢,要比 furts 的更胜一筹,但是这个系统呢是要收费的啊,所以呢啊,我们 在嵌入式当中啊,基本上用 furts 已经足够,还有我们的 furts 提供了一个用于低工号的一个 tylus 的这种模式 啊,这种模式呢是在啊,我们的低功耗需要我们的产品需要低功耗的时候,我们可以应用啊,他其实呢,就是对我们的一个 空闲任务的时候,把我们的这些外设时钟不需要使用的外设时钟啊给关闭啊,如果说退出的空闲模式的时候,那我们可以打开 对应的一些外设时钟,从而降低我们的工号啊。系统的组建呢,在创建的时候可以选择动态或者是静态的 rem 啊,这个呢,在我们后面 啊学习这个呃任务创建的时候我们就会接触到啊,可以通过我们动态的一个创动态内存或者是静态内存来创建我们的任务啊,比如我们的任务的创建消息对电,还有信号量软件定时器等等 啊,这都是我们的一些操作系统内部的一些啊,资源啊资源。那么还有已经在这个超过三十种架构的芯片上面呢,进行了移植 啊,在 furts 的官方啊,提供给了我们一些啊,一字号的一些工程啊,相对于我们的这个 stm 四二啊, 还有幺零三四零七四二九等等七六七啊,都给我们移植好了,所以呢,我们可以啊,把这些县城的一些工程啊应用到我们的工程哎,芯片当中来啊,非常简单,这官方已经给我们移植好了,我们直接使用就可以 啊, vrtsmpu 支持这个 criticsm 内核的一些 mpo 的一些单元啊,像我们有 mpo 的话是 f 四, f 四以上的一些系列芯片啊,他都可以拥有 mpmpo 啊,当然 mfurqs 他也对这个支持 furts 的系统呢,简单小巧易用啊,通常情况下呢,占内核内核占用呢四到九 k 的一个自洁空间啊,对于我们 stm 三来说啊,这个远远啊,远远已经足够了啊,我们的一个 芯片的内核啊文件,我们芯片的 ram 啊,已经足够啊,满足这个 furts 的一个运行。 然后呢,他有一个特点啊,非常高的可移植性啊,因为它采用的是 c 元,纯 c 元来编写的,所以呢啊,对于移植性来说啊,服用 rts 是非常方便移植的啊,移植到各种 平台啊,然后呢,还支持实时任务和携程啊,这里也叫合作式协同程序,那么我们这里呢,携程的话,我们这里 fmrts 现在已经不用他了啊,不用他了, 这是我们的 furts 的这些特点。还有就是在任务与任务,任务与中段之间可以 直使用任务通知消息队列二直信号量,数字型信号量,还有地规互次信号量以及互次信号量进行通信和同步啊,这些呢,我们后续都会学习到啊,都会学习到的 啊,创新的试验组啊,试验组我们也会学习到,以及我们优先级继承,这是在互资信号量当中会使用到,因为通过这种优先级继承的话,可以实现啊, 必尽量的避免他的一个啊,优先及翻转的这一种啊特性。 还有高效的软件定时器,这种呢是纯软件的一种定时器,和我们硬件定时器呢,功能是类似啊,但是我们 atm 上他有硬件啊,硬件的一个定时器啊,这里 furtis 呢,是提供我们的软件定时器,当我们硬件定时不够的时候,我们可以通过软件定时器来实现一些啊,时间上的一些定时的一些功能 啊,还有强大的跟踪执行功能啊,还有堆占溢出检测功能,通过他的一些红定义来设置啊,如果设置了一些这种检测功能的话,那么你需要编写一些啊啊,回调函数,也就是钩子函数之类的 啊,还有任务数量呢,它不限啊,理论上这些任务数量是可以不限制的,但实际上受限于我们的 ram 啊,我们的芯片 ram 不可能说无限大的啊,你的任务数量越多,你的 ram 呢?占用的也就越多啊,任务 优先级不限啊。同样同样的一个道理啊。这是我们的这个 furts 的一个。呃,特点的一个介绍啊?特点的一个介绍。

很多潜入式新手刚入门都会陷入一个误区,要么对着 c 语言死磕语法,盲目刷题,要么跳过基础直接上手 s t m 三二,最后越学越猛,甚至半途而废。 今天就给大家整理一份接地气不踩坑的单片机,加 free 二 t o s 学习路线,全程贴合新手节奏跟着走,从入门到能投简历不是难题。 第一步,康实 c 语言基础不死磕不盲目,实战中练熟。首先明确一点,单片机开发的核心是用 c 语言解决硬件问题,不是让你成为 c 语言语法专家。很多小伙伴纠结 c 语言要学到什么程度,要不要刷几百道题,其实完全没必要。 新手误区,死记硬背语法规则,盲目刷算法题,浪费时间还没效果。正确做法是不用死磕偏门语法,也不用盲目刷题。先掌握核心知识点,后续做项目时自然会熟练。 大部分人都是在学习 stm 三二外设的过程中慢慢把 c 语言练扎实的,但反过来,单片机学不会的根源,百分之九十都是 c 语言基本功不扎实。这一点一定要记住。针对单片机开发 c 语言,重点掌握这五个核心,数组指真、函数结构体、结构体指真。 至于动态内存 melok, 新手阶段完全不用重点学。实际单片机开发中用到的场景极少,没必要提前消耗精力学习技巧,疯狂敲代码, 不能光看视频不动手,每天至少保证二百行代码的练习量,哪怕是重复写简单的逻辑,也能培养手感和代码思维,手感练出来了,后续写外设驱动重构项目都会事半功倍。 第二步,上手 stm 三二,选对教程,抓准重点,边学边做项目 c 语言,掌握核心知识点后,直接上手 stm 三二,不用再额外花时间学其他基础。 这里给新手一个真诚建议,优先看铁头山羊的教程,不是江科大的不好,而是铁头山羊的教程更贴合小白,讲解更通俗,重点更突出,不会让你陷入繁杂的知识点里抓不住核心。江科大的课程内容很全面,但信息量太大,很多内容是新手阶段用不上的,容易学懵,半途而废。 stm 三二学习重点, gpl、 串口、中段、 iic、 spi、 定时器、 dma 这七大外设是重中之重,必须煮个吃透。学习方法很简单,一边听视频理解理论知识,一边亲手敲代码。注意 是一个字一个字敲,不是复制粘贴,跑一遍现成代码就完事。只有亲手敲,才能记住代码逻辑,理解外设配置的细节,才能真正学会关键避坑点。不要像背单词一样,一个外设接一个外设,死记硬背。 一定要结合传感器搭建简单的应用场景,比如用 g、 p l 控制 led, 用串口实现数据收发,用定时器做简单的延时,用 r、 s e 连接温湿度传感器读取数据。 这样一边学外设,一边做小项目,不仅能巩固知识点,还能积累项目经验,后续写简历也有东西可写,避免学完啥也拿不出手的尴尬。第三步,学习 free 二 tos, 先会用,再懂原理,项目驱动成长,基础打牢,能独立完成综合小项目后,再开始学习 free 二 tos。 这里要记住, free 二 tos 不是 看会的,是用会的,只看视频教程,你只会知道有这个东西,却不知道怎么用,用在什么场景,等于白学 free 二 tos 核心学习思路有两个,一、先会用,再看原理。新手初期的目标不是搞懂 free 二 tos 底层原码和调度机制,而是能用上它重构项目。 比如把之前做的温湿度采集项目用 free 二 tos 拆分任务,一个任务负责采集温湿度,一个任务负责串口显示,一个任务负责 led 报警。先实现项目效果,再慢慢深究底层逻辑,避免被复杂的原理困住,打击学习信心。 二、多做项目视频只是用来引导方向,解决难点的,不能作为主要学习方式。多把之前做过的项目用 free r t o s 重构,在实践中理解任务调度队列信号量的用法,搞清楚什么场景下需要用 r t o s 进阶步骤。当你能熟练用 free r t o s 完成项目后,再回头看底层源码,研究它的调度原理、任务创建机制,从现象到本质,此时你才能真正理解 free r t o s, 才算真正摸到介入式开发的门槛。

r t o s。 核心就两件事,同步和互斥。别再被元马吓懵了!很多人一提到 free r t o s 就 觉得头大,任务调度信号量、消息队列,学了一个月还是晕。 今天说句扎心的话, r t o s。 其实非常简单,半天就能搞明白,你学不会,不是智商问题,是教程太烂。上来就讲 api 贴源码,连为什么需要现成都不解释。一、 r t o s。 到底在解决什么问题? 裸机程序是一个大外循环,所有模块排队执行,但有些是很急,有些是不急。于是我们把程序拆成多个县城,给每个县城标一个优先级调度器,负责让 cpu 永远跑优先级最高的那个就绪县城, 这就是 r t o s。 的 全部核心。剩下的什么任务,站上下文切换,都是为这个目标服务的工程实现。二、县城带来的两个问题, 同步和互斥。把程序拆成现成后,麻烦来了,同步 a 县城,要等 b 县城干完某件事才能继续。怎么等?互斥? 两个县城同时访问同一块内存, a 写到一半被 b 打乱,数据全乱,怎么防?解决方案就一个字,信号量 p 操作,如果计数器大于零,就减一,然后继续,否则等待 v 操作, 计数器加一,然后叫醒正在等待的县城,把信号量出使值设为零, a 县城 p 一下,等 b 县城 v 一下再执行,这就是同步。把出使值设为一, a 县城先 p, b 县城再 p, 就 得等着, 这就是互斥消息队列,互斥所、事件所都是信号量的变体,解决的无非还是同步和互斥,你搞懂信号量,就搞懂了百分之八十的 ipc 机制。三、为什么你学起来这么痛苦? 因为大部分教程是反人类的。第一步,不告诉你为什么要用 r t o s, 而是直接画个任务创建函数的框图。第二步,不讲 p v 操作的物理意义,而是贴一堆 x m a for take 的 圆码,你还没会走,他就让你跑, 结果就是 api 被的滚瓜烂熟,换个场景照样抓瞎。正确的学习路径应该是理解县城和优先级的概念,搞懂同步和互斥需要什么,自己用几十行伪代码模拟一个信号量,再去翻 free r t o s。 的 手册,看 api 怎么用。 做完这四步,你甚至会觉得 free r t o s。 代码太啰嗦。事实上,一个最简 r t o s。 内核,只要四百行代码就能实现任务切换、信号量、消息队列,那些几千行的源码,多半是各种红和移植层。 四、行业现状,会调 api 的 人太多,懂原理的人太少。现在潜入式面试,十个人有八个说熟悉 free r t o s。 一 声问信号量实现原理就卡壳 企业不缺调用 api 的 人,缺的是遇到优先级反转死组战役初期能自己分析定位的人。而这些能力恰恰来自对 r t o s。 底层模型的理解,不是靠被 api 能解决的。 别再把 r t o s。 当黑盒子用了,花半天时间搞懂它,你会发现这东西简单到不可思议。

哈喽,欢迎小伙伴们回来,这里是立志做最好、最简洁、最等教程的 kiss king。 上期视频我们学习了二进制信号量,它的本质就是一个不存储任何数据的队列,使记录和通知是否有事件发生。 但有些场景下,我们不仅需要知道事件是否发生,还需要知道事件发生的次数,发生了多少次就要处理多少次。 那这种情况下,若使用二 g 值信号量,就会出现将多次事件合并为一次的问题。 然而,针对这类场景, fourier 为我们准备了能够计数的信号量。计数信号量我们说二 g 值信号量就是一个长度只有一位的队列,而且这一位的大小也是零字节,是虚假的,一位存储不了任何数据, 仅仅靠队列中一个记录当前有多少数据的属性来记录是否有数据。而且由于是二进制信号量,队列的长度就只有一进,而当前数据数量也只能是零和一。 而计数信号量其实就是在此基础上解开了队列长度的限制,可以设置为任意长度。比如设置为十,就是有十个位置的队列,当前数据数量便可以从零记录到十。 当然每个位置依旧是虚拟的,大小是零字节。每当生产者任务使用 ose 释放信号量时,数据数量就加一。 有消费者任务通过 osimf acquire 获取信号量时,只要有信号存在,数据数量就减一。 而与二进制信号量不同的是,如果消费者任务一次消费时间比较长,生产者任务可以多次释放信号计数信号量,并可以记录下来释放了多少次等消费者任务消费完成后,再继续获取信号量,消费相应的次数 不会像二进制信号量一样,当有信号等待获取时,就忽略掉后续的释放计数信号量。在我们实际的工程中,通常有两种工作场景, 盘点事件与资源管理。盘点事件其实就像我们想的那样,记录下事件发生的次数,进行相应次数的处理,比如按揭按下多少次就发送多少个事件,消费者任务就处理多少次。 而资源管理则有点像反过来使用技术信号量。使用技术信号量来表示系统中某种资源的可用数量, 在程序驶驶化时,就将技术信号量驶驶化为此资源的数量。那么当有一个任务需要使用此种资源时,便使用 oxsafe acquire 获取信号量,这时技术信号量的数量便减一, 然后此任务便可以对一个此资源进行使用。如果有多个任务使用此资源,那么每个任务都会对此信号量进行获取,减少信号量的计数值。当信号量减少为零,后面的任务便获取不到信号量,只好进入主色菜等待。 而当前面的某个任务使用完了此资源,便可以使用 oh signal release 释放信号量,那在主色态等待的任务就可以被唤醒,获取到资源进行使用。 如果小伙伴们还有点搞不懂的话,我们不妨举两个例子,小伙伴们便能够清晰明了了。让我们先来快速搭建一个工程 设置 delete 模式,时间精准改一下设置时钟, 再来配置一下工程名称和路径改为 cmake, 生成单独的点 c 点 h 文件。回到基本设置,设置一下按键一, 然后打开创口二, ok, 再来打开 fratos。 首先是盘点事件的简单例子,假如我们在做一个导弹发射系统,需要按几次按键就发射几枚导弹,而每次导弹发射都需要几秒的时间来完成,我们需要一个按键任务, 还有一个模拟的发射任务。 然后信号量这边因为要按几次就发射几枚,进而就需要创建具有计数功能的计数信号量了, 名字就叫做 launch simple。 最大数量就是指这个计数信号量最大能数到多少,比如可以写个十,然后促使值我们保持为零,毕竟不能刚开机就自动发射导弹出去。然后让我们保存并生成代码。 来到 c line, 我 们将刚刚生成的工程打开,注意一下配置文件。 来到 fratos 点 c 文件中 mx fratosinet 里对我们的信号量和任务进行了初步化。小伙伴们可以发现计数信号量的初步化与之前二阶式信号量的初步化都是 ose 函数, 只是第一个参数最大值从一变为了我们设置的十。当然了,这只是 simsons os 为我们提供的小便利,点进此函数的话还是能看到根据最大值是不是一分别调用的是 fourier 原声的创建二 g 值信号量和创建计数信号量的函数。 那返回来,我们来到下面的任务函数。首先在按键任务中实现一个简单的按键消抖逻辑, 检测到按键后,我们就使用 ossim for release 函数发布一下信号量,也就是给技术信号量加一,我们信号量的操作句柄是 launch sim handle。 此外,为了方便观察,我们再来搞个自增的串口信息输出, ok, 另外,当前按键任务的优先级比较高,别忘了加个 o s delay, 防止它一直占着 cpu。 那 来到导弹发射任务中,我们首先要通过 o s sim for acquire 获取信号量。信号量操作矩阵是 launch sim handle, 不等到指令可不能发射导弹,婴儿超时等待时间设置为 oh sweet forever。 然后我们可以写个 o s delay 来假装导弹的发射需要消耗的时间。为了观察,我们也搞一个自增的串口输出, ok, 那 让我们配置一下 st link, 然后编辑下载,一气呵成。那连接上串口助手,我们按一下按键发射一次导弹,那我一二三连按三次,三枚导弹依次发射, 再来一二三四四枚导弹依次发射。完美,我们成功实现了通过技术信号量记录下按键的次数,再依次进行相应次数的处理。 虽然说效果上来看,与我们刚开始学习队列时举的例子效果是差不多的,但队列更适合于需要在通知的同时传递一个数据参数的场景,而技术信号量则只要在不丢失次数的前提下通知到即可。它的实现基于队列, 但无需在内存中开辟一块区域用于存方,数据稍微的清亮一些。 那说完了技术信号量进行事件盘点的例子,我们再来举一个技术信号量进行资源管理的例子。还是以我们的导弹场景为例。 我们知道对于这类军事设备,数据加密一定是重中之重,那直接使用单片机 cpu 进行数据加解密计算,不仅效率低下,也占用其他任务的 cpu 资源。因此很多单片机芯片内置了专门的加密计算模块, 甚至很多产品上会为单片机外挂专用加密芯片。假设我们的整个导弹系统中外挂了三个相同的加密芯片,系统中可能有多个任务需要用到这些芯片提供的加密服务。为了避免冲突,我们就可以使用技术信号量来进行资源管理。 来到 qimax 的 信号量这里,我们再来添加一个技术信号量。加密芯片信号量最大值我们设为三,意思是有三个资源可用,初始值也要设为三,意思是刚开始三个资源都可以被使用的。 然后我们来到任务管理中,添加三个任务名字我们就不修改了,大概就是模拟一些通信等的需要加解密的任务。添加完任务,小伙伴们就会发现 fritz 堆使用情况选项卡出现了红色报错,那我们点进来会发现 是堆的总使用量爆红了。那关于内存相关的知识,我们后面的视频再来详细学习。 现在我们只要知道当前我们的堆占用到了三千三百三十六 bytes, 其中很大一个原因是我们刚刚添加的这些任务每个都要从堆里申请不止一百二十八 words 的 内存空间, 而我们当前的默认设置堆的大小是小于当前需要占用的,因此 q 版 max 也就报错提醒我们了。 我们可以来到参数设置这里,找到堆大小的设置,可以看到确实小于三千三百三十六 bytes, 那 我们可以将堆大小设置大一点,比如一万零二百四十 bytes, 也就是十 kb, 这个数值也不是任意多大都行,需要根据具体芯片和内存使用情况来定,我们后面的视频中再来具体学习。 ok, 那 在生成代码前,我们再来将红色小洞的 gpl 口设置为输出,并且设置好用户标签, 随后生成代码。那回到代码,小伙伴们可以看到,现在有了 crypto iccm 的 出式化,最大值是三,出式值也是三。 之前我们写的代码中出现了一些由于头文件缺失产生的报错,是因为我们写这些代码时, c 乱自动帮我们加了头文件,但不在比特恩的注视队中重新生成代码时被 q mx 删掉了。我们可以按 alt 加回车, 让 c 乱再帮我们自动加上这些头文件。不过为了下次生成不又被删掉,我们最好还是将它们放入到注视队中。 回到正题,我们来到任务三的任务函数,我们可以假设这个任务间隔一秒钟就需要获取一次加密芯片, 我们要用 old symbol aquire 来获取计数信号量,这样就能使计数信号量代表的资源数减一。然后我们假设任务三需要持有计数信号量十秒, 之所以要十秒这么长,是为了我们一会儿方便观察,然后用完了资源,一定要记得再使用 oscim for release 来释放技术信号量,使技术信号量代表的资源数加一,相当于返还资源, 不然只获取不返还,本来能够多次利用的资源就会被消耗光了。那为了观察现象,我们不妨也来加些串口输出。我们可以通过 ossim for get count 函数来获取技术信号量当前的数值 释放技术信号量后也来个串口输出, ok, 模拟使用加密芯片的任务三,我们搞完了,我们再来如法炮制写一下任务四, 可以让其间隔一千五百毫秒进行一次,然后再来实现一下任务五, 间隔两秒进行一次, ok, 那 我们的发射任务当然也要使用加密芯片,那我们也使用 osimf 快 尔尝试获取加密芯片的技术信号量,获取到后就也创口输出一下。 另外我们再来让红色小灯闪烁一下,代表导弹发射了。这样做的用意小伙伴们一会观察现象时就能知道了。使用完加密芯片后,记得要释放技术信号量,返还资源, ok, 那 么现在我们有三个任务,在每隔一会就使用一段时间的加密芯片。另外,导弹发射任务也需要使用加密芯片,但是只有三个加密芯片能被同时使用。 然而我们使用技术信号量来管理加密芯片资源。如果前三个任务获取了加密芯片进行使用, 那么技术信号量也就被减为零,那导弹发射任务输出开始。发射导弹后,试图使用加密芯片时,发现技术信号量当前为零,无法获取,进而进入阻蚀态等待。 直到某个任务使用完加密芯片释放技术信号量,祖赛泰的任务便被唤醒,获取到技术信号量,继续执行输出,获取到加密 ic, 并且小灯闪烁一次。那让我们来看看现象是否就是这样,便已下载一气呵成。 程序开始执行后,三个任务依次获取到了加密 ic, 那 这个时候技术信号量就减为了零。我们按下按键,导弹开始发射,可以发现确实没有输出发射任务获取到加密 ic, 说明它在等技术信号量, 直到任务三释放了技术信号量,按理说发射任务就会被唤醒获取到技术信号量继续执行,但我们依旧没有看到其输出获取到加密 ic。 不 过小伙伴们应该已经观察到了, 我们的红色小灯已经亮过了。并且任务四在释放加密 ic 时,技术信号量是从零到一的, 这说明发射任务确实继续向下执行了,而且他也成功获取到了技术信号量,使技术信号量从一减为了零。 不错,看来除了串口没有正常输出这个小瑕疵,我们还是成功实现了使用技术信号量进行资源的管理分配。 当资源都在被使用时,需要使用资源的任务便可以进入组态态等待。而当资源被释放后,任务又可以被唤醒,获取到资源继续执行。那为何串口没有被正常输出呢?我们不妨通过的 bug 调试查询一下原因。 在这句没有正常执行的串口发送函数前打个断点。为了方便操作,也在任务三执行前打个断点,然后我们点击小虫子启动调试。 ok, 在 任务三断点下来了,我们继续执行。 等三个任务都获取到信号量后,按下按键,一会儿后,程序成功在有问题的串口发送函数前断点了下来。我们点步入函数,首先步入的是 string length 函数,我们不出,回来后继续步入,来到串口发送函数内部, 向下执行是一个 if 判断,再向下执行,哦吼,直接返回了 base 状态,没有进行任何的串口操作, 说明上面的这个 f 判断完全没有通过,那这个 f 看起来是在判断当前串口的状态是不是在 ready 状态,那我们把鼠标挪上来,就可以发现这个代表串口状态的变量,当前处于 b z t x, 也就是忙于发送, 那他在忙着发什么呢?从戛然而止的串口输出我们也能猜出来了,原来是任务三在释放完技术信号量后,正在进行串口输出, 正好时间片耗尽,而刚因技术信号量被唤醒的发射任务也就获取到了时间片执行,却在进行串口输出时,发现串口此时正在被使用,无法进行串口输出。原来这也是个多任无下的资源管理问题, 那我们是不是也可以使用技术信号量进行解决呢?当然是可以的,而且像串口这样只有一个的资源,其实使用二 g 信号量就可以了, 小伙伴们不妨动手试一下,解决一下这个问题。那在文档网站中为小伙伴们提供的本期视频的程序中,我已经添加了使用二 g g 信号量解决串口冲突,小伙伴们可以下载参考。 不过其实福尔托斯还为我们准备了一个更适合完成此任务的工具,它就是我们下期视频将要学习的互斥锁,具有独特的优先级反转机制,小伙伴们不妨一键三连催催更, 没点关注的小伙伴也别忘了点击关注不迷路,那本期视频就是这些了,求点赞、求收藏、求两颗硬币,小伙伴们的鼓励是我加速更新的动力,那让我们下期视频一会见,拜拜。

freertos 队列互斥信号量 freertos 队列用户通常情况下会把灵活性与简单性相结合,但是这种类型通常是属于互斥的。消息是通过复制的方式经过队列来进行发送,这说明数据的本身被复制到队列中, 而不是队列对存储数据的一直引用。这种方式相对来说是较为友好的,主要原因以下几点如果已经包含在 c 变量之中,整形结构体短小的,是可以直接发送到队列中的,不需要为消息进行缓冲去, 也可以直接从队列中把消息读取到 c 变两中。除此之外,这种队列发送方式即使发送的消息仍保留在队列之中,也是允许发送任务立即覆盖发送到队列的重区或 电量。由于电量中包含的数据被复制到对列中,所以电量本身可以重用。无论任务是接收消息还是发送消息,不需要商定是哪个任务接收消息以及哪个任务负责发送消息。 使用通过复制传递数据的对列并不会阻止使用对列。通过引用传递数据,当消息的大小达到某个点时,将整个消息字节复制到对列中是不现实的。则定义对列夷保存指针,只将指向消息的指针复制到对列中。 内核完全负责分配,用作队列存储区域的内容。可以通过定义结构体成员来保存大小不一的信息,然后再通过队列进行消息的发送和接收。最近很多小伙伴找我说想要一些单片机学习资料,然后我根据自己从业十年经验,熬夜搞了几个通宵,精心整理了 一份单片机入门到高级教程加工具包,点个关注,全部无偿共享给大家。大家可以查看我的主页简介信息就可以拿走,你过来呀!

大家好,欢迎参加本次技术分享。今天我们将深入探讨嵌入式开发中两个非常重要的实时操作系统 u c o s b i fritz。 我 们将重点对比它们在任务同步与通信方面的核心机制,信号量和消息队列。 通过本次分享,希望能帮助大家更好地理解这两种 toros 的 设计思想,并为未来的项目选型提供有价值的参考。本次分享将分为四个部分,首先我们会简要介绍 toros 的 重要性以及 u c o s e i。 和 fritus 这两个主角。接着我们会分别深入剖析 u c o i, c i。 和 fritus 中的信号量与消息传递机制。最后也是最重要的部分,我们会对两者进行全面的对比分析,并给出总结和选型建议。 首先让我们明确一下背景,在嵌入式开发中,特别是在 s t m。 三二平台上,实时操作系统 turbo 是 实现复杂功能的基石, 它帮助我们管理多个任务,并确保它们能够有序高效地协调工作。其中,任务间的同步和通信是核心挑战,信号量和消息队列就是解决这些问题的关键工具。 今天我们就来对比两款业界主流的 toros, 商业化的 u c o i c i 和开源的 freutas, 看看它们是如何实现这些机制的。接下来我们进入第二部分,深入了解 u c o i c i。 的 核心机制。 u c o s e i。 以其优雅的设计和严谨的代码结构闻名,学习它的实现方式对于理解 toros 的 原理非常有帮助。我们将重点关注它的信号量和邮箱机制。在 u c o i c i。 中,信号量是一种非常基础的同步工具。 它的工作原理很简单,就是通过一个计数器来控制资源的访问。当任务需要访问共享资源时,它会调用 o s s m pad 来获取信号量。计数器减一, 如果计数器变为零,说明资源被占用,任务就会进入等待状态。当任务完成操作后,会调用 o s s m post 来释放信号量。计数器加一并唤醒等待的任务。 右侧的示意图直观地展示了多个任务如何通过信号量协调对共享资源的访问。这里是一个简单的代码示意。我们首先创建一个局的信号量指征 my sum of four。 在 任务一中,我们使用 o s s m create 零 创建一个初始值为零的二进制信号量。然后在一个循环中,他每隔一秒调用 o s s m post 来发送信号量。在任务二中,他不断地调用 o s s m 来等待信号量。当任务一发送信号量后,任务二被唤醒 并打印一条消息。这个例子清晰地展示了如何使用信号量实现两个任务之间的同步。接下来是 u c o i c i。 的 邮箱机制。邮箱的主要作用是在任务之间传递一个指征, 这个指征可以指向任何复杂的数据结构。它的工作方式与信号量类似,但传递的不是一个简单的计数值,而是一个地址。 发送方调用 o s m box post 将一个纸质放入邮箱,接收方调用 o s m box and 来获取这个纸质。 需要注意的是, u c o i c i。 的 邮箱本身只存储一个纸质,开发者需要自己管理纸质指向的内存,这既有灵活性,也带来了内存管理的责任。 这个代码示意展示了邮箱的用法。我们定义了一个局的消息缓冲区 message buffer 和一个邮箱指针。 my mailbox 发送任务 task sender 每隔一秒更新缓冲区的数据,并调用 o s m box post 将缓冲区的地址发送出去。接收任务 task receiver 则不断调用 o s m box bind 等待消息。 一旦收到纸真,他就通过这个纸真访问并打印出缓冲区中的数据。这个例子清晰地展示了如何通过邮箱传递纸真来共享数据。了解完 u c o i c i, 我们现在转向它的主要竞争对手 freutas。 freutas 以其开源、免费和高度可配置性赢得了大量开发者的青睐。接下来,我们将分析它的信号量和消息队列机制,看看它与 u c o i c i。 有 何不同。 freutas 的 信号量设计非常巧妙,它没有为信号量单独设计一套复杂的结构,而是附用了消息队列的代码。 二、禁制信号量可以看作是一个只能容纳一个空消息的队列,而技术型信号量则是一个可以容纳多个空消息的队列。这种设计减少了代码涌跃。 它的 api 也非常直观, excel for take 用于获取, excel for give 用于释放。特别值得一提的是, free to 中断服务程序 i s r 提供了专门的 from i s r 版本的 api, 这在嵌入式开发中事关重要。这是一个非常典型的 freetus 应用场景任务与中断服务程序 i s r。 的 同步 任务,在 x sema for take 处永久等待。当某个中断发生时, i s r 会调用 x sema forgive from i s r 来释放信号量。这个函数会唤醒等待的任务, 并通过 x higher priority task token 参数告诉内核是否需要在中断退出后立即进行任务切换,以确保高优先级的任务能够被立即执行。 这种机制保证了中断响应的实时性。 fritz 的 消息队列是其一大亮点。与 u c o s i。 的 邮箱不同, fritus 的 队列传递的是数据本身,而不是指真。当你调用 x q sound 时,数据会被拷贝到队列的缓冲区中。调用 x q receive 时, 数据又会被拷贝出来。这种数据拷贝的方式虽然有一定的性能开销,但它更安全,避免了指真、悬空等常见问题。 右侧的 five four 示意图展示了数据入队和出队的顺序。另外,大家在开发时要特别注意,在中断中千万不要调用普通的 x q 三和 x q r 滤镜,而要使用带有 from i s r 后缀的专用 a p i。 这个视力展示了如何使用 fritz 队列传递一个自定义的结构体。我们首先创建一个队列,指定其长度为五, 每个元素的大小为 data t 结构体的大小。发送任务为三的 task 不 断更新结构体数据, 并调用 x q 三的将整个结构体拷贝到队列中。接收任务为 receiver, task 则从队列中拷贝数据到自己的缓冲区 r x data 中,并进行处理。 这个过程完全由内核管理内存,开发者无需担心纸质的安全性,非常安全和方便。现在我们已经分别了解了 u c o s e i 和 freeitas 的 核心机制接下来,我们将进入最关键的部分,将它们放在一起进行全面的对比分析, 从设计、思想、 api、 功能等多个维度来评估它们的优劣。这张表格总结了 u c o s d i 和 freutas 在 各个维度的关键差异。 可以看到, u c o i, c i。 的 优势在于其统一、严谨的设计和商业级的支持,而 fritus 则在灵活性、资源占用、社区生态和许可证方面具有压倒性优势。特别是在消息传递方面, fritus 的 数据拷贝机制比 u c o i, c i。 的 指针传递更安全、更易用。最后,我们来总结一下, u c i s。 益安是经典的教科书式的 toros, 适合用于深入学习和对稳定性要求极高的商业项目。 而 freitas 则凭借其开源、免费、轻量级和强大的社区成为了当前嵌入式领域的主流选择。在选型时,如果你的项目预算有限、资源紧张或者需要强大的消息传递功能,那么 freitas 无疑是更好的选择。 反之,如果商业支持和代码的严谨性是你的首要考虑,那么 u c o i c i 依然是一个可靠的选项。