粉丝3042获赞9033

好,我们一起来看一下。 codex 在 最近的一次更新当中,更新了两个特别有用的指令,分别是 go, per side。 那 什么是 go 呢?我们这里呢和普通的模式进行了一次对比,那在正常的任务下面呢,我们知道我们向 ai 描述一个任务,然后 ai 给我们响应,就是单回合的 prompt, 那 这个 go 命令就相当于是长跨度的 prompt。 什么意思?比如说我给他一个任务,我希望他能够进行四十八小时,甚至三天六天持续去进行,持续去烧这个 token, 不 断去烧是烧 token, 注意这个词哦, 就是我们把这头啃放到火里面烧一样,很耗头啃。就是比如说我前两天安排了一个项目给大家开发,他连续跑了大概四点五天才跑完这个任务,那我可想而知就是这个模式是非常耗头啃的,所以称之为 girl, 那 我可以看到,对吧?就是这个命名的,很多人说,嗯,在扣袋子当中,这是我见过发布出来的最重要的东西, 很多人都这样描述,真的太惊艳了。那我也强烈推荐大家看我每期视频去尝试一下。那首先,嗯,你需要把你的这个项目呢更新成这个,嗯,这个最新的这个版本,对吧?这个地方有一个嗯,自然语言描述,比如说如果人家开启了,就直接把这段话复制给这个啊,这个 啊,扣代斯点克林,你就复制它,因为它这个指定是在这个文件里面去开启它开启完成之后呢,啊,就会自动的去就可以存在这一个命令,比如说你现在输入 go, 它就有这个 mini, 哎,通过这个 mini 后面就可以输入我们的提炼词了,这里的提炼词你不能输的很笼统,因为这个任务是长时间执行的,所以你要输入清楚你的交付,最终的交付物什么样才能够算是完成 啊?因为它会持续运行,所以你可以相当于理解你布置的所有任务呢,都要能够去长时间支撑你这个任务,而不是说一个笼统提炼词。帮我开发一个叉叉,对吧?你肯定是不行的,你要给他具体的验收目标,哎,验收到一个什么地步才算完成,对不对?比如说这里有个例子,比如说我希望你帮我开发一个技术项目,那呢?你这个项目就直接在这个边去进行开发, 那你什么情况下算是呃完成呢?你能够可以启动,你能够实现这些东西,并且能够生效,这时候才算完成,对吧?那这是他的一个啊,具体的这个条件相当于你要完成一个这样的具体的结构,再给他去执行,这样他会不断去执行,直到你的 token 耗尽为止。大家举例子,比如说我现在帮我开发一个圆形图,那这个圆形图已经开发完了,对吧?我们就把它打开, 打开,打开,然后再打开啊,这时候还是会去打开我们刚刚这个圆形图,当然我们也可以直接在这里去打开我们这个图图,圆形图,对不对?就是通过我们这种方式,我直接直接打开吧, 打开,你看他这个圆形图就开发完了,这是我的验收目标。很简单,就是他能够把这个圆形图打开,这是我的验收目标啊,能够去加这个图图,你看是不是?哎,比较简单啊,就是这么一个任务,那这个 go 呢?哎,我们讲完了, 我们后面可以大家自己去尝试一下,其实和普通最大不同就是你能够自动去规划,自动去执行,自动去运筹,然后呢能够进行持久化的把这个任务给它,哎,完成就这么简单。好吧,其他的我就不多说了啊,大家自行花费。因为这题的词呢,我们 并不是人去写的,而是由 ai 去写的,就包括我刚刚给大家讲的这个 go 的 题,比如说对你的条件是什么,以及你的这个范围,还有你的这些啊,什么验收标准这些东西呢?我不建议大家一个一个去写,我建议大家只用 ai 生成,比如说你可以帮我生成一个 go 指令相关的这种啊,任务题词就说你再交给这个 go, 那 就跑一天,跑一天之后,如果你觉得这个东西那一套方案行得通,你再把这一套方案附用到其他项目上,比如说我能够用它开发借势本,那我也可以用它开发这个旅游管理系统,我也可以让它开发商务事务管理系统,或者是物流管理系统。一套方案跑通之后,我们可以长期的去附用,对吧?这是我们所做的,对不对?就是 复利,是不是类似复利一样,对吧?就这个是相信自己的财富啊,对吧?不断的去增长,通过这种方式,哎,跑动这个模式。第二个是这个 side, 这 side 就是 说我们有时候在对话的时候,我们在 callix 里面可能更多的是想要临时的开一个分支,对吧? 我们又不想去开一个工作树,所以呢,我们在不影响主任务的情况下,临时开一个啊绘画上下文的窗口啊,不进行污染原来的上下文,然后进行最小的这种改动,然后进行总结,因此啊,就称之为这两个指令啊。最后我们再总结一下 一个 go 呢,它可以帮助我们长时间去啊,根据我们输入目标去执行,那直到我们通过耗尽为止。第二个呢是 side, 这个 side 呢是临时开启一个绘画,并且呢不影响当前的主绘画进程,然后去帮助我们解答一些问题,并且它是有原来上下文的。那这是这么一个点,比如说我现在呢,哎,我开发完了,对不对?那我这边就直接开启一个 side, 我 先 重新看一下吧, codex, 大家如果注意版本啊,这个版本一定要是对的,比如说我现在这里呢,这个 codex 版本一定是幺四幺二八以上,好吧,幺二八以上,好吧,好,那本期视频全部都用了,我是小刘,我们下期再见。

哎呀,编程有很多坑啊,我分享一下我踩过的坑。第一个踩过坑什么呢?就是我以前开发社会,一个个去问就遇到什么问题,哎,这里有 bug 给我修复一下,遇到什么问题提一下,就想到哪里就跟大家做,等他做完再提, 这样效率很慢,然后其实效率不是很快,然后呢?他的号拖克号非常高,这样效率非常,这是一个坑。那怎么解决呢?就这个西红柿,他是可爱,这个是我改过一个版本的他,你看他的我改是怎么改的? 呃,他是首先先要他要归档这 task, 就 每次重新开始之前他就会帮我把这个 task 的 任务归档一下,就把做完的就算做完的把我清理掉,放到放历史里面去, 这样的话我后面可以去锁眼,去干嘛去,去追踪都可以。好,清空之后第二步是干嘛?第二步是找我这个开头有没有任务了,没任务我就跳到步骤十, 持续优化模式,就是他会自己过去优化改十个优化建议自己去优化好,如果有任务那接着做,如果有任务展示信息,展示任务信息开始执行,按执行完成,立即边写怎么做的,他会告诉怎么做边说明写入 task。 这个我踏实任务踏实个里面去,不要等后面统一写他的前,前面就开始写一条,就去做一条让他写,做完之后再去执行验证流程,他就做完之后他会验证一下 好,然后这个在这一个并标记成,如果是做完了,标记个叉并添加完成时间,并并实时说明验证结果,对吧?把这块都都写清楚,并每一条完成之后立即去写禁止 统一下,这里是重复的形式。好,如果当前任务因为什么依赖什么阻设的话,那么直接就依赖原因跳到第八步,继续下个任务哈。嗯, 就这样,它会一直循环,就哪怕它做完了之后,它还会去写,给我写十个任务,再接着做,直到等我这把。因为 vsco 的 吗?它是以按绘画来计算的,所以说我一次绘画可以跑二三十个任务,然后呢? 直到它上下文不够,它自己给我断掉,然后再接着跑坑,这一天跑两条,跑两次绘画,那两次绘画嘛,就消耗百分之一二个点了。 好,这是第一个坑,第二个坑。那么测试时候就比如说发现了个 bug, 对 吧?发现 bug, 好, 我要去修复它,修复它时候不要去,不要去说,哎,修复这 bug 不要这样去做,因为你会你会测出很多 bug, 所以 你再去修复的话,你会一直在测测测时间很长时间,你可以找一个 skill 测试,给它全自动化的模拟人模拟界面去测试,你看我的一些 m c p 里面有应该有很多测试的 skill, 我现在一下不好找,就找一个测试 skill, 让他用 ai 去找,就说帮我找个测试 skill, 测试前端优化,前端测试后端测试接口验证数据的,怎么去验证,对吧? 好,找了之后如果你发现一个问题,你就跟他说帮我排除类似的问题,并排除一下,这样的话它就会全部排除下,就没什么问题了。所以说测试啊,测试这样提高性能的好。第三步, 当我们其实去交互的时候,要写很多文档,其实也可以用 ai 去写文档,像什么有一个 ppt 的 一个 skill 啊,专门写文档的,它可以做成 ppt, 还可以做成视频,有些需要视频介绍的话可以做成视频去介绍, 嗯,这也是个 ppt 的 skill, 对 吧?就我这里面会有很多很多 skill, 嗯,然后会把还有还有就是二卡时候他老是会用到一些新技术, 因为新技术的话,嗯,你不要呃,每次都要去说,哎呀,不要用新技术或者是什么会把你档案搞乱,其实这个也是可以可以去避免的,就是把我们的规则啊,把我们这个项目,比如说我们这项目里面规范啊,然后一些环境规则呀,一些 一些已知的一些需求啊,勾勾个 ai, 让它吃透,然后代码,对吧?就第一句话就说,你先深度理理解我这个项目,然后分析我的需求,然后理解我的需求和并且 你讲的秘密规则,然后并且把它们做成 skill, 把把秘密规则做成 skill, 把环境做成 skill。 然后你再加一句,不,如果现有技术已实现,就不要去用新技术,这样的话他就不会去把你的代码搞乱,而且然后再加一句,他会去,因为我这个任务他会去归短的话, 所以说他每次做的东西他会都会把你的需求归短,好,下次他也不会做乱。嗯, 这个时候踩过的三个坑都可以往上解决了,如果在开会中还遇到什么坑,可以在评打到评论区,说不定我已经踩过,或者说我评论区,我评论区里面很多高手,有同行的技术人员,他会给你答案。关注我,每天分享一点 ai 编程技术。

玩游戏时,你是否也被重复的日常任务消耗耐心?比如每天要按固定顺序点击 n、 p、 c 提交物品, 等待倒计时。手动操作不仅枯燥,还容易出错。这里分享一个我亲测的派展技术。优化思路,全程只模拟人工操作逻辑,不修改任何游戏数据。 一、步骤拆解,把任务拆成,点击固定坐标,等待固定时长,输入固定内容的可附用流程。二、工具选型,用 python 的 拼图图实现鼠标 键盘的模拟操作,轻量易上手。三、逻辑实现,先手动记录每个操作的坐标和时间键,用循环封装成函数,让程序按顺序执行,加入随机延迟,让操作节奏更贴近人工。四、安全边界, 只做重复劳动的自动化,不读取游戏内存,不篡改数据,完全合规。这个思路不仅能优化游戏体验,还能帮你掌握自动化编程的核心逻辑,适合想提升效率同时学习拍段的朋友。

同学们好,这节课的内容是基于 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 配置好了 任务的话,也可以正常的去执行对应的那个代码。然后呢,那这节课的内容就到这里了。

大家好,欢迎来到 esp 三二入门系列教程。今天我们将深入探索 esp 三二开发的核心 free r t o s。 实时操作系统。无论你是刚接触 esp 三二的新手,还是希望提升系统开发能力的工程师,掌握 free r t o s。 都是必经之路。 本次教程将带你从零开始,一步步揭开 r t o s。 的 神秘面纱。本次教程将分为六个部分,首先,我们会介绍学习 free r t o s。 的 背景和重要性,接着探讨为什么在复杂项目中需要 r t o s。 然后,我们将深入学习 free r t o s。 的 核心概念任务,之后会详细讲解队列、信号量、互斥所、事件组和任务通知等关键的通信与同步机制。让我们开始吧! 首先,让我们进入第一章节前沿。在这一部分,我们将了解为什么 free r t o s。 对 于 e s p。 三二开发直观、重要,以及它为我们的项目带来了哪些核心价值。 e s p i d f。 框架与 free r t o s。 是 密不可分的,我们开发的每一个应用本质上都是在 free r t o s。 的 环境中运行。 学习 free r t o s 不 仅能让我们更好地利用 esp 三二的硬件资源,更重要的是它是一项可迁移的技能。由于其开源免费的特性, free r t o s。 已成为嵌入式领域的事实标准,掌握它将极大地拓展你的职业道路。 接下来,我们探讨一个根本性问题,为什么我们需要 r t o s? 在 资源有限的低端设备上,裸机程序似乎也能工作,那么引入 r t o s 能带来什么好处呢? 这张图直观地展示了两者的区别。左边是裸机程序的大循环模型,一个环节卡住,整个系统就停摆。右边是 r t o s。 的 多任务调度模型,高优先级任务可以随时抢占 cpu, 低优先级任务在等待时会主动释放 cpu, 这种机制使得系统能够高效有序地处理多个任务,这正是 r t o s。 的 核心魅力所在。了解了为什么要用 r t o s 之后,我们来深入学习 free r t o s。 的 核心概念。任务任务是 free r t o s。 应用程序的基本执行单元。 这张状态转换图清晰地展示了任务在四种状态之间如何转换。例如,一个运行中的任务调用 v task delay 后会进入阻设态,当延时结束,它会回到就绪态,等待调度器再次选中它。理解这些状态转换是掌握任务调度的关键。 优先级是任务调度的核心依据,我们可以被不同重要性的任务设置不同的优先级,确保关键任务能够得到及时响应。例如,处理案件中断的任务优先级应该高于一个周期性打印日制的任务。合理的设置优先级是保证系统实施性的关键。 创建任务是使用 free r t o s。 的 第一步。 x task create pinned to core 是 esp 三二特有的函数,它允许我们将任务绑定到特定的核心,这对于双核处理器的性能优化非常重要。 而 vsk delay 是 最常用的让任务主动让出 cpu 的 方法,它让任务进入堵塞状态,等待指定时间后自动回到就绪态。有了任务,我们还需要让任务之间能够相互通信和同步。 接下来,我们将学习 free r t u s 中用于任务间通信和同步的几种核心机制,队列信号量和互斥锁。 这些机制是构建复杂嵌入式系统的基石,能够确保系统运行的正确性与高效性。这张图展示了队列的工作原理。任务 a 和中断服务函数都可以通过 xq send 向队列发送数据,而任务 b 则通过 xq receive 从队列中取走数据。 队列就像一个安全的中转站,确保了数据在不同执行单元之间可靠的传递。通过队列,我们不仅实现了任务间的数据交换,还利用其阻塞特性让任务能以更高效有序的方式协同工作。这些是操作队列最核心的 api。 xq create 用于出使化队列。 xq send to back 和 xq receive 是 最常用的发送和接收函数。特别需要注意的是,在中断中必须使用 from e s r 版本的函数,因为中断上下文与任务上下文不同,需要特殊处理以保证安全。 这张图展示了二进置信号量如何用于任务同步。任务 a 通过 x and f for take 获取信号量,如果获取不到就进入阻涩。中断服务函数在某个事件发生后,通过 x and f for from s i r 释放信号量唤醒等待的任务 a。 这是实现中断服务函数与任务同步的经典模式。 护齿锁是信号量的一种特殊形式,它的关键特性是优先级继承。如右图所示,当低优先级任务 l 持有护齿锁时,高优先级任务 h 请求该锁会被堵塞,此时任务 l 的 优先级会被提升到与 h 相同,确保它能尽快完成工作并释放锁, 从而避免了中等优先级任务 m 的 干扰,这保证了系统的实时性。接下来我们学习事件组,当一个任务需要等待多个事件中的任意一个或全部发生时,使用事件组会比使用多个信号量更加高效和方便。 这张图展示了事件组的应用场景,任务一可以等待事件位零和事件位一都被置位,任务二和任务三可以分别设置这两个事件位。当两个事件都发生后,任务一被唤醒,继续执行。这种等待多个条件的能力是事件组独有的优势。 最后,我们来介绍一种更轻量级、更高效的通线方式,直达任务通知,它可以替代二,禁止信号量技术信号量甚至简单的队列,并且开销更小。 任务通知是 free r t o s。 提供的一种非常高效的通信机制,因为它不需要额外的内核对象,所以速度更快,内存开销更小。对于许多简单的同步场景,比如任务间的简单通知或模拟信号量,使用任务通知是优于传统队列和信号量的选择。 到这里,我们关于 free r t o s。 核心概念的介绍就全部结束了,从任务管理到各种通信同步机制。希望这次教程能帮助大家顺利开启 esp 三二的 r t o s。 开发之路。 技术之路永无止境,希望大家能在实践中不断探索和进步。谢谢大家!现在是问答环节,我是致歉, ai 工作室余生关注我,一起用 ai 玩转介入式。

不管你是老米车主还是小米车主啊,如果你跟我一样不希望在车里面看到任何多角的生物,那一定要把这个超级任务给设置起来,尤其是我们女车主哈,因为 像我昨天开车的时候,我刚一在车库上车,我就看见我的前挡玻璃有一只这么大的黑色蚂蚁尾巴,还带着丝,还好是在车库哈低速行驶的状态,我连忙踩了个急刹,用纸巾把它捏着包好扔掉了, 哎,给你看把,这个镜头应该能看见吧,这里在浮动的那里,就是我确定这个就是我那天在车里捏死的那只蚂蚁尾部牵扯出来的那个网,那个丝。 所以吧,这个设置真的很重要呀,朋友们,为了避免以后再出现这样的情况呢,所以我就设置了一个超级任务哈,你们也一定要用起来。这超级任务就是当在汽车挂 p 档的时候呢,然后就自动把这个空调调为内循环。 好,那调为内循环过后,如果我们不设置他的话,那他你在开车的时候,他还是会一直保持内循环,这样不利于一个空气的流通,所以同时要设置一个在 d 档的时候,让汽车自动开启那个空调的一个循环哦,这个分享码哈,我就直接来,这个就是 p 档内循环的, 你们可以直接照抄哈,可以截个图保存一下,然后这个是 d 档自动开为那个循环的。好的,然后这个可以照抄一下啊,然后为了我的安全,也为了大家的安全,尤其是我们比较怕这些多脚生物的女车主们哈,我就赶紧把这个分享出来了,希望你们都赶紧的用起来啊。

polos 实时操作系统让单片机从单县城变成多县城的神器。它有三个核心概念,任务就是一个个独立的小程序,每个任务有自己的外音,各跑各的 调度器是操作系统的心脏,决定哪个任务此时此刻占用 cpu, 你 跑一会我跑一会,快到人眼以为是独食 站是每个任务的私人笔记本,任务切换时, cpu 会把当前任务的现场保存到它的站里,下次回来时恢复好像什么都没发生过。 fritz 为什么那么小?因为他只做任务调度和通信,没有 linux 那 些文件系统网络协议站 多任务的好处是按键任务显示任务通信任务各写各的,互相不打架,程序结构清晰十倍。

队列就是一条数据运输的管道,有的任务在那头向队列里塞数据,有的任务在这头从队列里取数据。 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 推荐给朋友、同学甚至你的老师,你的鼓励是我制作更多优质内容的动力。让我们下期视频见,拜拜!

好的,各位同学,欢迎回来,这里是 free rts 实操系统训练营,我是你们小赵同学,本节是我们这套课程的第六节课,本期课程呢,我们来讲一下这个 free rts 的 一个事件组的一个通知。 好吧,那么下面进入我们的课程吧,那么在前面的课程当中呢,我们学习了如何使用这个信号量来进行同步,但是使用信号量来同步的话,那么任务啊,只能和单个任务之间进行同步,有时候呢,我们 啊可能某个任务啊,要跟就是多个任务进行同步,这个时候啊,这个信号量就没有办法去做这个事情了,那么 f r t s 呢,就提供了一个比较好的解决方法啊,那就是我们的这个事件标志组,那么今天呢,我们来了解一下这个事件标志组,那么我们这个事件组呢 啊,事件是一种实现任务之间通信的机制,主要用于实现多任务之间的一个同步。像我们之前学过的这个信号量啊,他呢就是我们这个一对一啊,就是单个任务之间他们进行一个同步,他不可以实现就是一对多的那种 啊,或者是多对多的一些同步操作,他是做不了的。但我们这个事件组呢,还是可以实现这个多对多的一个通信,当然也可以实现一对一 啊,然后他呢就是占用的这个内存呢,是比较少的哈,占用内存是比较少的,是这样非常少内存啊,你看,但是这个事件组啊,只能是事件类型 的一个通信啊,他不可以传输这个数据,就是什么什么意思呢?他就是不可以跟我们之前学过的那个消息队列一样啊,去传输里面的数据,他只能传输,有没有啊?那就是我们的一个二值信号量啊,他就是一个信号量,说白了与信号量之间 不同的呢?是什么呢?它可以实现一对多啊和多对多的一个同步啊,刚才我们已经讲过了,对不对?那我们先画一个草图来给大家做一个理解。我们之前的一个信号量呢,只能是你看这里有两个任务,对不对? 这个是我们的一个任务一和任务二, 然后任务二呢一直去检测我们的这个状态,状态啊,就是这个信号量啊,一直检测信号量,然后处于挂起态,直到我们的 这个任务一呢,把这个信号量至为一啊,至为一以后他读取回去,读取回去之后呢,才可以去进行一个这个程序运转,对不对?那我们这个世界呢?他可以干什么?他可以去干什么? 比如说我们有一个任务啊,它里面有两个按键,有两个按键啊,然后呢?我们这边有一个任务, 或这么说,或这么说。嗯,稍等啊,稍等,我把这个擦掉,全部擦掉。 这么说,比如说我们现在有一个任务,然后两个任务,三个任务,我们现在有三个任务,三个任务之间呢,我们需要去一对多的一个通信,然后这个呢?它比如说它是一个停车场吧 啊?比如说还是我们那个停车场,举例子,就是按按一释放,按二申请啊,然后这是两个单独运行那个任务哈?两个单独运行的任务,然后这个呢?任务三他呢需要去做一个一对多的通信, 那这个应该怎么做呢?比如说我们这边有个呃 a 和 b, 当我们 a 按下以后呢,它可以选择啊,就是我们直接摁这个 a 按下以后,我们直接去运行啊,那这就是一对一对不对,那或者呢我们可以等这个 按键 b 也按下的时候呢啊,去进行一个执行,就是按键 a 呢和按键 b 同时按下啊,就是就全部触发,全部触发以后呢,你才可以去执行这个操作啊,才可以去执行这个操作。 如果有一方没有去触发的话,那就一直在等待,一直在等待啊,这个呢就是我们的一个一对多的一个通信,一对多的通信就是什么呢?我们这个只有任务一和任务二同时被这个响应了啊,同时响应, 那才可以去执行这个任务三的一个任务完任务三才可以去执行他的任务,否则就是不可以用啊,否则就不可以用,那这个呢就是我们一个呃 事件哈事件组和这个信号量的一个区别啊,一个区别。那么我们这个事件组呢,它只需要占用很少的一个 run 空间来保存我们这个事件组的一个状态,那我们这个事件组呢,它本身就存储在一个这个是 e v e n t b i t s 的 一个这个变量里面哈,它的本质呢就是一个 uint 三二杠 t 的 一个类型啊,然后 这个变量啊,在我们这个结构体当中呢,是有定义的,如果我们这个红定义啊为零的时候,那么这个红啊,在我们这个 r to s config 点 h 这个同文件里面呢,是有定义的,如果它定义为零的时候, 那么我们这个变量啊,它是一个三十二位的一个一个变量啊,那么其中呢,这三十二位中有二十四位呢,是我们实际用来 做我们这个标志组的。如果说这个红啊定义为一的话,那么它这个变量类型啊,是由一十六类型的啊, 也只有一十六位,那么其中呢,有八位是用于我们这个呃事件标志组的哈,就用于我们这个事件标志组,而我们 atm 三二呢,一般是将这个红啊设置为零, 也就说我们这个变量是三十二位的,其中有二十四位呢,用来实现我们这个事件组的一个标志位,那存储我们事件组标志位的。 在我们多任务的时候啊,这个任务和中断之间往往是需要同步操作的,那么一个事件发生会告知我们这个等待中的任务,那么就形成了一个任务与任务中断与任务之间的一个同步, 那么事件呢,可以提供一个一对多,多对多的一个同步操作,那么一对多呢,他的这个就是一个任务去等待多个事件的一个触发, 这种情况是比较常见的。多对多啊,他这个模型呢,就是多个任务去等待多个任务的一个触发。嗯,当然这个我们用的不是很多哈,只是暂时用的不是很多。 那我们这个 fris 的 事件呢,它仅仅用于同步,就是任务之间的同步,它不用于这个传输这个信息的功能哈,它不具备传输信息的功能,那么我们传输信息的话,还是要用我们的消息队列。 天呢,就是 fris 的 一个事件组简单介绍,那接下来呢,我们来看一下 fris 给我们提供的事件组,它的一个特点啊,它的一个特点 啊,那首先呢,我们这个事件啊,只与任务相关联,事件呢,它是相互独立的,一个三十二位的事件集合就是这个变量, 它是三十二位的,但它只有二十四位是可以用于事件,用于标识该任务的事件类型。其中每一位呢,它表示一个事件类型,所以呢可以有支持二十四,二十四种啊, 那么零呢,表示这个事件类型没有发生,那么一呢,就是表示这个事件已经发生了啊,一共是二十四种事件类型。 然后呢,事件类型哈,事件仅用于同步,不提供数据传输功能,刚才我们也已经讲过了,而且我们的这个事件他无排队性, 就是说多次向任务设置同一事件啊,如果这个事件他没有被这,这个任务他没有被读走,那么就等同于你设置了一次啊,就等同于设置了一次,这个是没有计数累加的哈, 就是你没有读走,然后他又设置了这个事件,那仅等同于设置了一次,好吧,允许多个任务对同一个事件进行读写,这是一个多任务对同一事件的一个访问, 还支持事件超时等待机制。就说我们这个任务啊,它在等待时候,我们可以设置一个主设时间,看这个时间内有没有等到,或者说我们只有等待这个, 等到这个事件的时候啊,我们才进行一个触发。在 f r t s 当中呢,每 每个事件获取的时候,用户呢都可以选择感兴趣的事件,并且呢选择获取获取信息。事件标记它有三个属性,分别是逻辑语,逻辑或 以及是否清除标记。当我们的任务在等待同步的时候啊,在等待这个事件同步的时候, 可以通过任务感兴趣的事件位和标志组的一个信息,就是我们这个事件信息标志来判断我们当前接受的事件是否满足要求, 如果满足,则说明任务等待相应事件的系统呢,将唤醒等待的任务,否则我们的任务呢,会根据用户指定的主色时间继续等待下去啊。 那 free r t s 事件呢?常用于事件类型的通信,没有数据传输功能,也就是说我们可以用事件来标来作为我们这个标志位,判断某些事件是否发生了,然后根据结果呢来进行处理。那么很多人就会问, 为什么我们不直接用变量来做标志位呢?我定一个全局变量不是更好啊?那不是这样的,如果,如果是在我们这个裸机编程当中啊,我们完全可以用这个全局变量来做这个 标志位,这是非常有效的一种方法。但是在操作系统当中呢,使用大局变量就需要考虑这几个问题。首先第一个,如何对大局变量进行保护,如何处理多任务之间同时对他的一个访问,这是一个问题。 第二个,如何让这个内核对这个事件进行有效的管理。使用全局变量的话,只呃就需要在任务之间轮询的这种方式啊,就要在任务之间一个轮询的方式 去检查这个事件是不是发生了啊,是否有发生,这是非常浪费 cpu 资源的一个操作方式,还有就是等待超时机制,使用全局变量的话,那么需要用户去实现 这个东西哈,显然使用全局变量这种方式啊,是非常麻烦的,在我们操作系统当中啊, 事件的这种通信机制,为什么我们不去运用它呢?对不对?人家已经给我们做好了,所以我们只需要去使用事件来代替我们这个全局变量就可以了啊,这也是一个非常高效的一个行为。那不要一味的去认为这个全局变量啊,是可以在操作系统当中一样去进行应用的啊, 有的时候你可能用这种全局编啊,他是可以跑起来的,但是这个不稳啊,不稳定,我们用 r、 p、 s 不 就是求稳定吗?是不是?不就是求一个同步 性,一个实时性对不对?那么在某些场合中啊,可能需要等待很多个时间,呃,多个事件发生了,才能进行下一步 这么一个操作。比如我们有一些危险的机器啊,啊,我们这个机器他启动的话,首先预先要检查很多的一个指标,当这些指标都合格的话 才能去启动。如果说有某个不合格啊,有一项他不合格,他都无法去启动,那么在检测这个指标的时候,不能一下子就检查完毕啊,需要事件组 来统一的进行一个等待。当我们所有的事件都完成以后啊,他才能去进行一个启动, 比如说我们这个 a、 b、 c、 d 事件啊,全部检查完成了,那就可以去进行一个启动了,否则只要有一个不合格,他就不能启动,这时候呢,我们使用一个事件组啊,来进行一个标记是非常方便的啊, 非常方便的啊。事件呢,在多种场合下的一个使用,它能够在一定的程度上替代这个信号量,用于任务之间,呃,就是任务与任务之间中断与任务之间的一个同步。 那么一个任务啊,或者是这个中断服务历程呢,发送一个事件给这个事件对象,然后呢等待任务会被唤醒 啊,我们这个等待的任务呢,还会被唤醒,并对这个相应的事件呢进行一个处理,但它和信号量呢又不同, 事件发送的操作呢,是不可以累加的,而是呃,而我们这个信号量的一个释放动作是可以进行累加的, 因为信号量呢有呃,有一定的一个技术,技术型信号量事件组另外一个特性呢,是在接收任务的时候可以等待多种事件啊,可以等待多种事件, 也就是说多个事件对应一个任务或者是多个任务,还有就是他可以同时按照任务等待的参数可以选择逻辑与逻辑或来进行触发的一个特性。 他这个特性呢在限号量呢,在限号量里边是不具备的哈,不具备的限号量呢,他只能识别单一的这个同 同步动作,而不能同时等待多个事件的一个同步,这是事件和信号量的一个区别之处啊。下面呢我们再来看一下这个事件的运转机制。在接收事件的时候啊,我们可以 对感兴趣的这个事件进行来接收啊,单个多个事件类型都可以的。那么事件接收成功以后啊,必须使用一个子函数来进行清除接收事件的一个类型,否则呢就不会清除已接收的事件,这样就需要我们用户 来手动的去清除一下这个事件位,用户呢可以自己定义,通过传入的这个参数来选择读取模式。 是等待所有感兴趣的事件还是等待一个任意的事件?那么在设置事件的时候呢,是对指定的事件写入指定的事件类型,可以对这个事件位啊字为一就可以对它进行写入,也就相当于标记了, 可以一次或多次同时写入多个事件标志类型啊,事件类型,那么在清除事件的时候呢,也就是说将对应的事件置为零啊,他就可以清除了,那么事件啊,他跟任务是不相关的啊,不关联的 事件呢,是相互独立的,那么一个三十二位的变量,他只有二十四位有事件啊,那么用于标识任务的发生和 事件的类型啊,每一位啊,它里面的每一位表示一种事件类型啊,从这张图呢,我们可以看到总共二十四位,如果说二十四位为零的话啊,事件位为零的话,表示这个事件类型呢是没有发生,如果为一的话, 发呃,就是说明我们这个事件类型呢已经发生了,对不对? 那我们可以从这一个事件里面读取啊,读取出来看他这个位啊,看他这个位是否为一,为一的话,表示这个事件呢已经发生了啊,这个事件类型已经发生了,如果为零,则没有发生。我们再来看一下右边这张图, 这是我们事件唤醒机制,当我们这个任务呢,应等待,或者是等待某个啊,或者是多个 事件发生时,他会进入一个主色的状态啊,那当事件发生时呢,会被唤醒。我们从这张图可以清晰的看到我们的任务一,他是对事件三 和事件五感兴趣,也就是说这里使用的是货啊,使用的是货,也就是说当我们的事件三或事件五 发生了啊,发生的时候,我们的任务一就可以被唤醒,就往下去执行操作。那从这个流程可以看啊,可以看到啊,显示任务一在等待三和三和五的话啊,三和五 啊,等待它的这个发生,那这里呢,这个事件三发生了啊,标记了以后呢,那我们这个任务一啊,就会被直接 啊这个唤醒啊,任务一会唤醒执行,那我们这个任务二呢,就是继续等待这个事件五的发生,那么事件五发生以后呢?事件五发生以后,我们的任务一呢,已经在这个,呃事件三 发生的时候呢,已经去唤醒执行了。那么我们这个任务五啊,他在这个发生以后呢,我们的这个任务一还是正常运行的啊,正常一个运行的状态,那么我们的任务二就等到了我们这个事件五,对不对? 因为他是雨,对不对?他是对任务啊,他是对这个事件三啊,与这个事件五感兴趣,他是雨,那雨的话是什么呢?一一得一,是不是一一得一,那这个呢?或呢是一零得一,零一得一,对不对?零零才得零,对不对?哎, 那这个时候呢,他的三和五条件同时达成了,同时达成以后呢,他就会把我们的任务二唤醒 啊,那这个呢,就是他的一个运行机制啊,这个呢,就是我们的这个呃事件组的一个运行机制。那么接下来呢,我们来看一下本节课要实现的一个功能啊。本节课要实现的功能, 本章主要是要实现的功能呢?是啊,创建两个任务。创建两个任务,一个任务啊,是设置事件任务的啊, 一个是设置事件任务,一个是等待事件任务。两个任务独立运行啊,独立运行,设置事件任务,通过检测按键按下的这个状况,设置不同的事件标志。 等待事件任务呢,则获取两个标志位啊,并进行判断两个事件是否都发生。如果是啊,则输出相应的信息 led 进行翻转。 等待事件啊,就是等待事件的任务。等待的时间呢?是这个啊,等待时间是这个,那就是一直等待啊,一直等待, 一直等到这个事件发生呢啊,等到事件发生之后,他这个对应的哈,对应的这个事件组呢?会被清零啊,会被清零。好吧,那我们现在来看一下我们这节课的一个程序代码啊,程序设例。 好,那么接下来看一下我们本节课要用到的一个程序代码啊。程序代码,那依旧是一百七十行啊,这个程序不算多,一百七十行, 我们来看一下。首先呢,上面这些都不用再过多赘述了啊,首先是我们的头文件,头文件,然后还有一些这个 任务啊,任务优先级啦,还有对战大小任务巨饼的一些初步化,还有一些红定义啊,我们这节课呢,用到了,一共是几个啊?一共是三个任务, 然后还有一个启动任务啊,创建任务用的任务啊,跟之前是一样的, 首先在这里呢,我们需要先将我们的这个事件啊事件的一个他的这个锯饼啊,先粗化一下啊,先定义一下,定义完之后呢,我们需要在这里设置一下严码,设置一下严码啊,同学们。 嗯,其实你设置不设置这这边啊,你一味不一味的, 呃,你也可以这么写吗?你也可以这么写,就是这边你可以直接写成这个呃,零 x 零一,然后下面呢直接写一零,这样也是可以的,或者说什么呢?你如果不想来做这个定义的话,那你直接在下面直接写零一或者一零就可以了, 我们这个是在给后期啊,给后期是留有一些这个余量的哈,去做了一个用于的一个程序编辑的方式,当我们去修改这里的数值就可以了啊,只需要修改这里的这些数值就可以去实现一个这个啊多事件啊,多事件的一个触发。好吧, 方便我们去观察这个状态,方方面我们去获取他的一个这个事件,是吧? 然后我们再往下看呢,这里呢才是一些粗俗化啊,还是一些粗俗化代码,从这里呢?我们通过 print f 呢,通过 print f, 然后发送一个这个 串口,串口数据啊,发送一个数据,我们呢 free r t s 事件标志组的实验。 好,那我们继续往下,首先要创建一个任务,创建任务以后呢跟我们之前那个一样,然后开启调度,开启调度以后呢进入这个临界区,进入临界区之后我们需要在这里创建一个我们的时间组, 创建时间组以后呢,他的返回值呢,正好是这个锯饼,然后我们需要用我们之前这个定义的这个锯饼呢,来去接受一下,接受到这个锯饼,对不对? 你看他的返回值呢,是返回这个类型的一个锯饼,然后如果失败的话,还是返回空啊,还是返回空?那我们在这里呢,可以加一个判断,判断他是不是成成功了,判断是不是成功了啊,同学们后期可以自行加一下啊。 然后在这里呢,我们还是沿用了之前那几节课的那个名字,我们也没有改,但大体意思都差不多,是不是 led, 然后 led? 二、然后这个 k y, 这个呢就是我们用来接收接收任务的啊,这个是用来出发出发任务的,就是触发事件的,这是接收事件, 这个呢啊,还是控制我们的这个 led 一 进行一个闪烁啊?进行闪烁它是跟剩下这两个任务是没有关联的哈,没有任何关联的。然后上面这个呢,主要是显示一下我们那个调度状态啊,看一下这个 f r t s 是 不是正常调度 啊,然后在这里呢退出并删除哈,退出连接区,然后删除一下我们这个,呃,其中键 上传完之后呢,我们先从最高优先级开始看,这个呢是我们的最高优先级,最高优先级进来以后呢,首先定一个用那个用它八杠 t 的 一个 key number 啊,可以 key number, 然后接受一下我们的这个返回啊返回的键码,然后去判断这个键码 啊,如果说第第一个按下以后呢,第一个按下,首先通过 friend f 打印出来说这个 k y e 已被按下,然后呢?我们先触发事件一,我们触发事件一,那它里面传这个行餐哈,传这个行餐什么呢?我们来跳转一下看看啊, 传到这个行餐呢,就是我们一开始创建那个事件组创建的事件组,我们之前那节课,呃,我们之前的课程已经有讲过,有讲过,就是说我们这个,呃,如果说它的这个 啊, f r t s config 点一次文件里面的这个红绿叶呢?至为一的话,至为一的话呢,它就是三十二位,三十二位实际可用的这个位数呢,一共是二十四个啊,所以说,那我我们这个事件组啊,一定不要超过二十四个啊,最高也就二十四个。事件啊,事件组啊,事件组。 然后我们把这个事件组的这个矩阵传入,传入以后呢,我们选择通知,通知这个对象啊,通知的是谁啊? 从这个 k y 一 对不对? k y 一, 然后我们在上面已经通过严码的方式啊进行写了,那它本质呢?就是零 x 零幺,对不对?它本质就是发送一个零 x 零幺啊, 那第二个同理对不对?这是发送零 x 零二,是不是?然后进入我们的这个前置挂起啊?前置挂起,然后让最高优先级的这个任务释放 cpu 资源,释放 cpu 资源,让那个 r t s 进行一个正常调度。 接下来我们来看一下接收接收的这个任务啊。接收任务,首先接下来以后呢,我们先定义一个这个接收事件的一个变量啊,接收完,呃,定义完以后呢,我们放到这里,准备接收他的返回值,准备接收他的返回值, 首先他传入的这个,呃,成员都有谁哈?我们来看一下,首先呢我们要先传入这个事件的具这个对象啊,我们先把对象具柄传入进去啊,他的返回值呢?就是出发了哪一位,对不对? 然后呢传入这个你这个事件啊,事件具柄以后呢,我们再往下看, 在这里,那我们获上这两个先获啊,就是传入一下我们感兴趣的事件啊,我们感兴趣的事件,我们只对这两个事件感兴趣,然后我们只读这两个事件,进行个获啊,进行个获运算,对, 然后我们再来看退出标志,还有这些东西,都填成这个样子就可以了啊,都填成这个样子就可以了,这个没有过多的要求,后面都给同学们备注好了啊,都给备注好了,然后呢我们来看一下,在这里 只有这两个按键,全部按下啊,在这里你看这里有判断,对不对?只有两个按键啊,全部按下以后呢,他才会去打印啊,打印我们的普通 f, 通过普通 f 打印,我们的这个一和二都被按下了,对不对? 你看但他这个哈,他这个是接收完以后啊,就是两个接收完以后他自动清零的哈,自动清零的哈,接收完以后他就自动清除,对不对?你看这里退出时自动清除,对不对? 然后呢我们在这里看一下他这个判断逻辑哈,判断逻辑,首先呢接收,接收完以后呢,我们放这里去进行一个语啊,进行一个语算,判断我们的反义词是不是他们两个啊?他们两个任意一个啊,任意一个, 然后再判断他他们两个是不是一模一样的,对不对? 这个同学们应该都能看懂哈,这个应该都可以看懂,就是这一个很简单哈,很简单就判断这两个按键是不是同时按下了啊? 然后否则呢,他又说事件报错啊,但这个事件报错呢,是触发不了了啊,绝对触发不了了,在我们这个程序里是触发不了了,因为他没有其他事件,对不对?他没有其他事件,只有这两个事件,然后在这边只判断这两个事件了,对不对? 然后我们再来看一下这个任务哈,这个任务任务呢,就是你看啊,他就是每三百毫秒进行一个翻转哈, led 的 翻转,然后在这里呢,我们没有写那个 led 翻转,我们只需要打印打印数据就可以了,我们只需要看数据,好吧, 然后这个程序呢,上面都已经备注好了,同学们,到时候哪里不懂的话你们再去,呃,好好翻一下,好好翻一下,好吧,好好翻一下,看一下。那么我们先来看一下这个 程序的一个最终效果,好吧,我们给它下到内存机里,好,我们进行一个程序的下载,首先一下好零错警告,然后我们点击下载,稍等,稍等,我们先把它连上, 连接上以后呢,我们先点击编,编完之后点下载好,下载成功了以后呢,打开我们的创创口助手,首先把我们的这个创口工具呢插上, 设置一下 u t s 八,点击打开,我们下载完以后呢,需要点一下这个复位啊,好在这里呢,现在已经显示了,对不对?然后我们的这个 r t 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。 当黑盒子用了,花半天时间搞懂它,你会发现这东西简单到不可思议。