如何用 c 语言来实现面向对象?这个问题在面试中被问得稀烂稀烂,今天我们就参考 linux 内核,用猫狗案例来实现一把, 代码尽量简单,尽量少写,有些地方可能会考虑不周,尽量还原。抽象、分装、继承、多态几个特征。第一步,抽象猫和狗都属于动物,但是 c 语言里面又没有 class, 只能用 struct。 不 管是猫还是狗,都应该用名字先来个成员 name, 然后它们还有各种行为,行为可以用函数来表示。上上上节课讲过, c 元的 struct 里面不能放函数,只能用函数指征, 所以就用一些函数指征来表示动物的各种行为,可以是说话、吃饭、睡觉等等。于是弊端出现了,这样写下去,结构体里面要放很多函数指征, 不仅会导致后面派生的猫和狗结构体都变大,还会导致牵引棒而动全身。某天再加个行为函数,整个结构体都要修改。这里模仿 linux 内核的 fire operation 结构体,专门用一个结构体来存放行为函数, 而在原结构体里面直接放个指征就行。这里的 struct animal operations 就 相当于虚函数表,为了简单一些,只保留一个 speak 函数指征,这些行为也需要一个统一的函数来调用,就叫 animal speak, 不 管是猫叫还是狗叫都用这个函数, 所以这里肯定需要一个参数来区分到底是哪个动物。通过 this 调用 o p s 成员,再调用 speak 函数指征,下面开始派生 c 元,并没有 c 家的继承语法,只能把安利帽结构体作为第一个成员来模拟继承的规则, 下面可以再加一些跟狗相关的成员,简单起见,我就不写了。狗的具体行为这里也要实现一遍。参数不能省略,因为同样是狗,也区分旺财和小强 函数。题打印出名字就行,区分一下哪条狗在叫。接下来就要完成狗的抽象工作,没有构造函数,只能自己写函数实现,所以抽象无非就是抽象结构体。结构体只有一个继承来的 base 成员, base 里面有两个成员名字 name 和指向 o p s。 所以初识化的时候至少也得提供三个参数才行。初识化哪个结构体用什么名字,初识化用哪个结构体指征初识化。而这里的结构体指征,我更愿意用全局变量来代替,因为不管是哪只狗,都要调用 dog speak 来实现叫的功能。 定义结构体变量。把函数指征 speak 抽象成 dog speak, 下面狗的抽象直接赋值拷贝就完了。如果你不嫌麻烦,相同代码再来一遍,还能派生出猫 主函数。定义三个结构体变量,在 c 加中叫做创建对象。两条狗,一只猫,分别对猫和狗进行抽象化, 只要调用 animal speak, 就 能听见猫叫和狗叫。如果想方便管理,可以来一个结构体指征数组,把三个动物放在数组里面,效果也一样。这里的 animal speak 就是 所谓的多肽,具体行为取决于参数指向哪个对象,就会调用哪个对象的函数。
粉丝6.2万获赞34.6万

c 语言和 c 加加只能写控制台程序,他们是辣鸡语言吗?是的,他们太简单了,简单到只用几行代码就能让这三个硬件工作起来了。一起来看看辣鸡语言是怎么控制这几个硬件的。 nice! 首先编程软件用的是 arduino, 想要安装的可以先看这个视频哦,安装完成,我们打开软件,它就长这个样子。然后呢,我们需要把这块 esp 八二六六开发板接到电脑上通电 啊,感觉全身充满了力量,哈哈哈哈!接着我们给三色 led 灯模块接线, 大家记住这几根线颜色和针角的对应位置,然后我们可以发现开发板上面有两排字母,每个字母背后都有一根针角,它的背后长的是这样子的。接着我们用刚刚的三色 led 灯模块,黄线接到符号为 g 的 针角上, 你不要过来,好恶心,快走开哦,疼!接下来呢,绿线接到第一针脚上,红线接到第二针脚上,蓝线接到第三针脚上,不要啊,不要啊 不要啊!哈哈哈哈!然后我们就可以写代码了,先定义针脚的值,然后第一、第二第三设置为输出模式,然后我们在主循环中对第一针脚输出高电瓶 d 二 d 三输出 d 电瓶暂停一秒后,所有引脚输出 d 电瓶再暂停一秒。接着我们就可以点这个按钮把程序翻译上传到开发板上了,经过几秒钟就可以看到红灯一闪一闪的亮起来了。 nice! 接着蜂鸣器像我这样子接线,记住线的颜色,子线接到开发板的 d 四蒸脚上, 灰线接到开发版的三 v 针角上,然后我们就可以定义第一次针角来控制蜂鸣器了,在灯亮起来的时候响起来,灯灭的时候停止 第一针角,目前控制的是红灯,现在我们改为控制第二针角,然后我们就可以点击编辑上传了,等待几秒有惊喜哦! 喂,你能解释我头顶上这个颜色是怎么回事吗?哈哈哈哈,所以 c 元和 c 加加太辣鸡了,太简单了。

今天我来讲一下谦阳市有哪些基本功,不管以后你们做什么细分领域,比如说车规啊,芯片啊,还是仪器啊,互联网啊这些,但他们基本功都是一样的。第一块就是不漏的, 我们需要知道从上电到系统启动,其中历经的每一步。 比如说简单一点就是 stm 三二的 bring up 里边会涉及启动文件时钟数,这些都是他们会编辑,然后需要彻底弄明白。 m 系列如果还不懂的话, a 系列那就更不可能懂。 第二块就是 cpu 原理或者架构,至少深入理解一个模块,比如说中断系统,需要熟悉一个中断,从电瓶触发到我们熟知的 c 语言,中断服务函数历经的每一行代码的含义,有一个地方不清楚就不合格, 这部分如果不能掰开了揉碎来分析,可能面试时候就会有问题。 第三就是指操作系统原理一样的,也需要至少深入理解一个模块,比如说就是调度部分,简单一点就是实时系统,实时系统 milos, milos 的 调度源码,我们当时上课的时候是老师带着我们一行一行读的。 嗯,一个任务它分三个部分,第一块是 t、 c、 b, 第二个就是它的任务堆栈,第三块就是它的任务代码, 这三个部分如何在内存里面组织在一起,然后在调度的时候又是如何相互切换相互的,这块如果能说清楚,在面试时候也是非常加分的。 第四块呢,就是行业经验,比如说做蓝牙的可能会有协议战,然后 gpu 呢?你有 gpu 的 渲染这些知识,医疗啊、通信啊都有自己的行业知识,这些行业知识可以在工作中接触,如果前面三个基本功都打牢的话,那上手是很快的, 这一块真正接触到了工资都不会太低。第五块是编辑链接的知识,我们现在已经习惯了用 ide 编程,但是脱离的 ide 就 不能说不会编了 啊。首先呢,就是要了解这个 makefile 大 致的运作原理,还要了解那个链接文件,链接文件会真正帮你划分内存, 这些是交叉编印的基础,所以说还有,嗯,写代码时候会有一些编印报错,都需要用相关知识来解决,这一部分也是程序员的一个基本功。

c 元的结构体可以放各种各样的数据,但就是不能放函数。为什么不能放函数?因为标准里面没有定义相关的语法, b g 不 支持。这到底是技术上实现不了,还是有其他的原因? 比如有这么一段代码,结构体 test, 简简单单放两个成员,一个 a, 一个 b, 然后我们强行往里面塞入一个函数,输出 a 和 b 的 值, 定义两个结构体变量,一个 t 一, 一个 t 二。按照 c 语言讲的,结构体的内存布局, t 一 和 t 二应该是这样的,分别占了八个字节。 这个函数放在哪?咱先别管它,通过结构体变量调用结构体里面的函数应该写成 t 一 点数,语法就模仿调用普通成员变量就行。那么问题来了,调用函数的时候输入的 a 和 b 到底是 t 一 里面的 a 和 b, 这个确实有点说不清。 为了解决这个问题,我们不妨在调用函数的时候把结构体的地址传进去。行参,同样加上一个指征,函数题里面也写清楚,通过指征去访问 a 和 b, 这样就不会存在歧义。即使通过 t 二去调用函数,也知道这里的 a 和 b 是 t 二的。 a 和 b 完美解决了 c 元结构体里面不能访问函数的问题。恭喜你发明了 c 加,如果早出生五十年,那么你就是 c 加之父。 c 加里面的结构体确实可以放函数,并且在编辑器端就进行了改造。通过对象调用函数,默认会把地址传进去,并且不需要人为的写行参,默认就有一个指征叫 this, 指征通过 this 来使用成员变量, this 也可以省略。 所以 c 元的结构体不能写函数,并不是技术上实现不了,而是没必要。在 c 元诞生和发展的底层视角下,函数就是一段独立的代码,不是数据,结构体,是数据的集合,没必要把两者强行混合,毕竟 c 元的特色就是简单透明高效。 那 c 元中一般怎么解决结构体放函数的问题太常见了,函数指数结构体里面可以放函数,指数指向需要的函数也能实现同样的效果。


前段时间整理电脑啊,翻到了一个二零一二年写的一个项目,然后这个项目呢,是太阳能热水器的这种控制器,用 s t s r 写的,然后业务代码大概有一千 内函数。呃,内内点 c 文件有一千三百多行,然后加上其他的什么 r t c, 估计有个一千七百行吧。然后所有的业务功能逻辑都是在这个内点 c 这个文件里面, 当然写的时候就已经发现了这个项目改功能很难,改一个地方呢,就是各种 bug 都冒出来了,不过那时候刚入行,没什么经验,就没有这种加过设计能力。这种代码发到现在我基本上都看不懂。 我估计不少人有这种经历,就是自己写代码前几年隔几年再回去看,和我一样,对吧?懵逼, 更何况是接受别人的这种老项目呢,原作者可能都不在那里了,然后有问题呢,也没有人问,然后我就想了一下,要我把这个代码丢给 ai 试一下, 然后我是怎么做呢?呃,我没有把代码直接丢给 ai 聊天窗口这种去问的,因为我之前试过这种代码,一大可能就会有那种上下文限制的问题,还有不同大模型的能力不一样,所以问出来结果它可能也会有差异的,只是去理解你这个代码。 所以我想法是,呃,尽可能提供更多关于项目的信息,去稀释对大模型能力的这种要求。是,我之前呢就用 cloud code 做了一两个 skill, 一个是这种这个去整理这个项目之库的一个 skill。 好,这个 skill 已经加载完成了。呃,下一步呢,我会把那个这个代码的原功能这个路径, 我把这个路径给它啊,这里选择完整,然后直接把这个路径给它, 那他就会去跑去整理这个支付, 那他整理出来之后是个什么样子呢?大概就是这样的一个结构的一个一系列的文档,那比如说这个,呃项目的阅读指南,对吧? 型号指标、文档、导航这些,就是后期的一个给问答呀的一个锁影。然后项目介绍这个项目实现什么功能这些呢?它是纯纯从那个原码里面去提取的,我没有提供任何的这种产品功能,说明给他硬件的配置,通过程序来提取硬件的接口, 还有系统的架构,还有这个它都实现了什么功能? 通信协议这些都是从源码里面去提取出来的。整理完支付之后就进入下一步 对这个智酷呢进行把它再投喂给 ai, 然后我又做了另外一个 skill, 这个是问答 skill 啊,就是根据智酷去做这个项目的精准问答了, 现在这个 skill 呢已经加载完成了,那我们要把这个刚刚整理 ai 帮我们整理的这个智酷的路径要告诉它, ok, 现在支付包它也已经加载完成了。那下面呢?我们就可以针对这个项目的问题去提问了,比如说我们的我问他一个这个项目的一些细节问题啊, 其实大家可以看到,对吧?它会根据我们支付的所有去找那个答案,如果说支付里面没有,那它就会去定位到那个关键代码那里, 因为我们支付呢,它已经是把那个呃 这个项目一些关键信息啊,就比如说它什么功能,该去找哪个程序文件,它是有记录这个信息的。然后一些比较细节的,比如说开启温差,还有关闭温差,这个设置范围是多少?出厂默认设置是多少?这个支付它不一定有记录, 因为项目设计的东西太多了,这种细节的话还是要通过代码去看的。将来植入部呢,就给他了一个,给了这个 ai 一个地图,让他从地图里面去找这答案,不要靠他的想象,现在他已经得出答案了, 开启出厂默认值是八度,然后关闭温差是四度, 那这里呢?还有它的时间 r, 呃,这里呢是实时范围对吧?是哪一个 同性字段,指哪个数组代表这个值的,对,它就会通过原码去推导,然后这里呢它也会告诉你,呃, 它的数据验证是来自于源码的哪一个文件,然后代码的哪一行,这个就是代表代码的行数。

今天想和刚开始学习潜入式开发的朋友分享一个我亲自走过的弯路和心得。最开始我和很多人一样,拿到一个项目就从没喊出,开始一行一行往下应读,试图理解每一句代码。 但很快我就发现这个方法效率太低了,尤其是面对企业里那些大型项目, 代码量巨大,架构复杂,想靠这种方法完全吃透,非常耗时,而且容易迷失在细节里,半天抓不住重点。后来经过不断学习和不断 c v 别人代码的过程中,发现复刻或学习别人项目时, 不要一开始就陷入代码细节。我建议先花时间去了解整个项目的整体流程和核心框架,他先做什么后做什么,就像看地图,先看竹竿道,而不是盯着每一条小巷。先把这条竹竿道跑通,在心里建立起一个清晰的脉络和骨架。 之后你再带着这个框架去查看具体的模块和代码,实现就会事半功倍,知道这段代码在全剧中扮演什么角色,这样也可以帮你建立起一种框架的思想。希望这个方法能帮你更高效的学习,把精力用在真正关键的地方。

c 加加中 const 能不能构成重组?什么是二异性?学完这节课写代码更专业。首先我们来聊聊函数重载的核心定义,就是到底什么情况下,我们可以说这几个函数是重载关系, 就是当在同一个作用域里面有一组函数,它们的名字是一样的,但是它们的参数列表是不同的,这个不同可以是参数的个数不同,类型不同,或者是参数的顺序不同。 这样的一组函数,我们就称之为函数重载。那能不能给我们举个实际的例子,就是这个函数重载到底长什么样子?比如说我们有一组函数,它的名字都叫 look up, 但是它的参数类型可以是 account, 也可以是 phone, 还可以是 name, 就 像这样,那这就是一个典型的函数重载,就是它名字虽然都叫 look up, 但是它其实根据你传入的参数类型的不同,会去调用不同的具体的实现。明白了,那下面我们需要函数重载。 函数重载到底解决了什么痛点?其实函数重载最核心的价值就是它可以减轻我们的命名负担,就是如果你的一组函数,它们的功能是非常相似的,只是说具体操作的数据类型或者说细节不一样, 那你其实没有必要给它们起一堆不同的名字。这么说的话,如果没有函数重载的话,我们要实现类似的功能,是不是代码会变得很难读?没错没错,就比如说你要查找一个东西,你可能要起名为 lookpuff account, lookpuff by phone, lookpuff by my, 就是你要记很多不同的函数名,但是有了函数重载之后,你就只需要记住一个 look up, 然后你就把你要查的东西传进去,它就会自动帮你找到对应的函数,这样的话你的代码也会更一致,然后也更好理解和使用。 就是当我们的函数参数带有 const 的 时候,到底是怎么影响函数重载的?呃,其实最核心的规则就是,当你的函数参数是一个引用或者是一个指真的时候, 这个对象它本身是不是一个 const, 会导致你的行餐类型是不一样的,那这样的话,它就可以构成有效的函数重载。比如我们这里提供的代码示意你看这两组函数,其实它们都是靠 const 来进行区分的。对,就是编辑器会根据你传入的时餐是不是 const 来判断你要调用哪一个函数。那如果说我现在有一个函数,它的参数是一个 constant string, 那 我如果想要在它的重载版本里面去调用这个 constant 版本的,要怎么实现呢?你可以这样做,比如说我们有一个 shorter string 函数,它是返回两个 string 里面较短的那一个, 那我们可能会写两个版本,首先长亮版的 shorter string 接收两个 const string 引用,比较它们的长度,直接返回较短的那个。然后是非常亮版本,它的参数是普通的自复串引用。为了调用长亮版本,我们必须先用 const 把 s 一 和 s 二转换成 const 自复串引用,否则编辑器会直接匹配当前的非常亮版本,造成无限低规。 调用完常量版本后,它返回的是一个 const 的 字串引用,而我们的非常量版本需要返回一个普通的字串引用,所以再用一次 const, 把返回值的 const 的 属性去掉,就能安全地返回了。这个技巧的核心就是利用 const 来实现两个重载版本的复用,避免代码拥余,同时保证了 const 的 安全性。 接下来我们来看看调用重载函数的三种可能结果。好啊,这个其实就三种情况,嗯,第一种情况就是 编辑器通过重载解析,他找到了一个唯一的参数列表,跟你调用最匹配的函数版本,那这个时候就会正常调用,没有任何问题。那如果说没有找到任何一个函数能匹配上呢?那就是第二种情况就是没有匹配, 没有匹配的话,编辑器就会直接报错,告诉你说没有找到匹配的函数,然后就编辑失败了,听起来就感觉还挺严格的。那第三种情况是什么呢?第三种情况是最容易被忽略的,就是二异性调用,就是有多个函数都可以匹配上,但是又没有哪一个是明显最好的, 那这个时候编辑器也会报错,告诉你说这个调用有歧义。好的,最后我们要聊的是这个重载和作用域,就是这个名字查找和屏蔽效应在 c 加里面是怎么影响我们的函数重载的?其实这里有一个特别重要的原则,就是名字查找是发生在类型检查之前的, 也就是说当编辑器在当前的座右铭里面找到了一个名字,它就会直接用这个名字,然后它就不会去管外面是不是还有其他的同名函数,就外层的这些抽象函数都会被隐藏掉,能不能用代码来给我们直观的解释一下这个屏蔽效应具体是怎么发生的?可以啊。 比如说我们在一个函数复半里面定义了一个 book 类型的 read, 那 这个时候如果你在函数里面再去使用 read, 它永远都只会是这个 book 类型的 read, 外面的那个 read 函数就被屏蔽掉了。对,然后包括你在这个函数里面又定义了一个新的 print 函数,那你在函数里面调用 print 的 时候,它永远都只会调用你这个作用域里面的 print, 而不会去管外面是不是还有其他的 print。 重载。好啦,这节课把函数重载的核心知识点和易错点都讲清楚了。理解原理,远离报错,我们下期再见。拜拜。

今天一期视频帮你解决学完单面机还是敲不出代码的问题,我之前也踩过这个坑,看视频根面全会,但自己上手就闷,直到我发现了一个核心,学单面机根本不用自己背代码,要培养的是思维,就比如我们要配置 g、 p i o, 像我按照我之前的想法的话,就是找到将写科技的配套历程, 然后就修改一下引脚,最后发现跑不通。现在我发现原来配置 g、 p、 i o 都是有流程的,就是首先我们要有个思路,第一步,用 r、 c、 c 开启 g p i o 的 时钟。第二步,用引念的函数初识化。第三步,调用这个函数控制这个引脚。 举个例子,关于第一步的函数,我们到哪里找呢?我们可以直接打开 r、 c c 点 h 的 文件,拉到最后,我们看到这三个常有的函数,但是不知道选哪一个。第一个方法,打 开 s t m 三二参考手册, ctrl f 这个 a p b 二可以找到 g p i o 就 挂在这条总线上,那对应的函数我们就直接选。 关于这个数据手册我会直接分享在群公告里面,或者大家可以直接到江写科技提供的资料那里获取。第二步, q 里面选中函数名,然后直接右键跳转定义注示说明,里面写的明明白白。 因此我们在学习的时候要非常明确,把每一个细节都想清楚,为什么要选择这个去配置,如何选择适配的函数?怎么去选? 带着问题去学习,我们会更有收获。你们在学习单片机还遇到过什么问题呢?欢迎在评论区讨论,如果这期视频对你有帮助的话,记得给我点赞收藏,也不要忘了给我点个关注哦,我们下期见,拜拜。

大家好,我们开始上课讲一下本节课的时讯部分。按键二的驱动。 第一步,在定时器中实现十毫秒的定时。首先定义两个全集变量和一个函数。在 global variable c 中建立两个全集变量和一个函数, 其中 flag 杠十毫秒用于控制按键扫描的周期,也就是说多久去扫描一次按键。为什么是十毫秒?我们在后边的按键去抖操作中会有详细讲解。 flag 杠 k 二为按键二的标志位,为一则表示该按键被按下,零表示未被按下。如果你想同时扫描四个按键,你需要建立四个按键的标志位, 定义一个按键扫描函数,这里面我们先空着,那么这两个全区变量和函数,那么你一定要声明 在定时器中实现十毫秒定时。这里面我们说一下这些代码呢,是本节课需要新增的代码部分,那么之前我们写过的关于 流水灯定时以及流水灯控制代码等等这些代码呢?大家不要删除,要保留下来。这些只是本节课新增的, 需要定义一个静态无符号自负型变量 i i 用于计时定时函数,因为已经被设计成每一毫秒进入一次,那么 i i 自加到十之后回零并置起十毫秒标志位。 flag 杠十毫秒 表示 flag 杠十毫秒,这个变量每十毫秒会被写一一次。 那么在主函数中,和我们之前的这个流水灯操作一样,判断十毫秒标志位,如果唯一,则进入按键扫描函数, 然后清空标志位,以便下一次进入,然后进入按键扫描函数,那么整个这些操作的目的就是为了实现函数 case 按键扫描函数会每十毫秒被执行一次。 按键扫描函数,我们先实现一个按键 k 二的扫描,先复习两个语句,第一个端口写低电平函数,第二个某引角至高电平函数。 那么本节课需要新学习一个新语句,读取 g p l 电平值,也是读取端口或引角电平值, 读取电瓶状态函数 h l g p l red pin。 然后你需要知道你所读的那个引脚在哪一个组上,然后是读哪一个。那么我们既然是读引脚电瓶函数,那么一定要有个读的结果,对吧? 读取的结果有两种,一种是 g p l 拼 recite 表示读到的结果为高电瓶。 我们来举个例子,假设现在我想读 testk r e c 零一杠 pc 这个端口的电瓶,那么我们说一下,如果说你想去读取这个端口电瓶,那么这个端口首先要一定被设置为输入才可以读取,如果它是输出,你是读取不到的, 那么第一个如果读取结果为低电瓶,则执行操作。 这是课程平台按键部分的原理图。我们来设计一下按键 k 二的驱动。 首先 testk t r n 零一和零二这两个引脚已经被设计为输出,那么 testk i e c 零一和零二这两个被设计为输入。 通过读取 testk i e c 零一这个引脚来确定按键 k 二是否被按下。 那么你想读取按键 k 二是否被按下,就需要保证 k 二在按下和未按下时,这个 a 点是两种不同的电瓶。那我们看一下,如果当 k 二没有被按下时,整个这条线被 电阻 r 九上拉为高电频,那么如果 k 二被按下了这条路导通之后,这个时候你会读到什么呢?你会读到当前这个引脚,它输出什么,你就会读到什么。 那么为了让这两种情况下的输出电频不一致,我们要求这个引脚输出为低电频。 所以说第一步写 testk t r n 零一端口为低电瓶,写 testk t r n 零二端口为高电瓶。 为什么要写它为高电瓶呢?就是说我们要区别 k 二和 k 四,因为如果你把这个也写为低电瓶的话,你按下 k 四,这同时也会读到低电瓶,对吧?所以说要把 t r n 零一写低,零二写高, 如果 k 二没有被按下,则 a 点为高电瓶,我们读这个引脚也会读到高电瓶。如果 k 二被按下,因为它已经被写低电瓶了,这个时候呢,我们就会读到低电瓶啊,这也是保证了按下和未按下,我们读取时是两种不同的电瓶。 那么根据这个图,我们讲解完了 k 二之后,大家可以思考一下按键 k 三怎么去做代码设计。 第一步,在主循环之前,先把 testk r e c 零 t r n 零一 pc 写低。 testk t r n 零二 pc 写高。 这段代码呢,放在主循环之前,因为在我们整个工程中,这两个电瓶并不需要修改,这样的电瓶代码可以写在主循环之前。第二步,按键扫描函数, 我们只需要读 test k i c 零一杠 pc 这个引脚电瓶值,就可以确定是否按键二被按下。 读引脚电瓶函数,引脚阻别引脚名为低电瓶,则证明按键 k 二被按下。置起按键 k 二标志为 flag 杠, k 二为高电瓶,则表示按键二没有被按下。 第三步,按键事件的处理,读取到按键后,在主函数中对事件进行处理。 在 y l e 中,首先需要定时扫描,读取定时扫描标志,清标志,进入按键扫描函数,然后主循环中还需要去判断按键标志为一,则证明按键被按下,然后清掉标志, 点一盏灯。如果有键,按下轻按键标志,等待按键下一次被按下。按键被按下后的处理可以根据需求,比如说点亮一盏灯等等。这个时候呢,主要为了证明你的按键已经控制成功了。 本节课的视频内容,熟悉按键的控制流程,实现按键的响应,比如说按下按键二,点亮灯一, 拓展任务一,尝试根据原理图以及我们刚才的分析和按键二的控制方法,实现按键三的控制,比如说按下按键三关灯二开灯三关灯。 拓展任务二,通过按键改变流水灯的流水效果,比如说流水灯的游泳方向。这个时候呢,就需要按键控制和我们之前的流水灯控制结合起来,这个任务呢,有一点点难度。 复习定时器,下节课讲解矩阵键盘的驱动控制方法。 我们看一下拓展任务一的效果, 通过按键二来开灯,按键三来关灯。 拓展任务二,按键改变流水状态,开始时流水灯从上往下流动, 按下按键三之后,流水灯改为由下向上流动,这里面涉及到一个按键和流水灯的联动控制问题。 我们讲一下程序的备份方法,从使用按键开始,代码量开始增加,代码需要在每节课后做好备份,方便以后增加功能和考试。首先有哪些文件需要备份,我们看一下 全部的全局变量和全部的函数肯定需要备份, 中断肯定需要备份。还有一个就是 main 点 h 头文件需要备份,那么有四个文件需要备份,我们讲一个相对比较简单的程序备份方法, 第一步,建立一个你的文件夹。第二步呢,在文件夹下建立四个 t、 s、 c 文件,这个文件的名字呢,就遵循我们刚才讲的需要备份的那四个文件名。 第三步,将工程中对应的文件打开, ctrl a 全选, ctrl c 复制,比如说我们的 main 点 c 需要备份,你就打开之后全选复制。 第四步呢,将复制好的内容按照对应的名字 ctrl v 粘贴到你刚才刚建的 t、 s、 c 文件中。那么把四个文件全部这样做之后,程序备份完成。 那么新课程开始之后呢,将 q 工程中对应的那个四个文件里面的东西全部删除,然后把你之前备份的代码全选复制之后再粘贴到工程中,这样就可以完成工程的备份以及使用的使用的过程。 肯定还有其他的方法,比如说一些整个工程的备份等。好,谢谢大家。

同学们,大家平常用 q 五写代码是不是会经常遇到以下这些问题?写代码全程没有代码提示,代码只能靠手敲,导致代码越写越乱,排版格式乱七八糟,找到个排错更是难上加难。除此 q 五的大家其实都知道,它的原声编辑功能老旧简陋,代码编辑体验感极差,很容易拖慢我们的开发进度。 那 vs code 作为一款轻量化开源,免费拓展性极强的代码编辑器,凭借着界面清爽简洁、兼容性强、插件生态丰富的优势,现在已经成为了我们铁路式开发工程师的首选代码编写平台。 那今天这期视频我们就来好好聊一聊, vs code 到底能为我们汽车单片机开发带来哪些实实在在的提升。其中最大一个好处就是你不用抛弃原来的 q 工程,也不用新建项目,不用改配置,可以直接在你的 vs code 的 里面进行 stm 三二的单片机代码开发,其中自带的代码自动不全、 函数一键跳转以及我们的同文件自动定位, 我们就可以不用像在 q 五里面那样纯靠手敲代码,到处翻文件找函数,写代码的效率直接拉满。 我们之前在用 q 五进行单面机兼容式开发的过程当中,很容易出现我们的代码缩进不统一、排版风格混乱以及我们的空格符号等各种各样的问题。那在我们的 vs code 的 过程当中,我们可以直接保存代码, 就能够一键的整理我们的代码格式,或者说我们在打分号的过程当中,也可以帮我们整理我们的代码格式,就能让我们的整个工程代码风格统一、结构清晰,可读性大幅提升。

你,你如果做这个就是初学者,你不知道要告诉 ai 什么,所以你必须要能够让这个,你得让 ai 来问,你,知道吧?就要来问你,你问你,等会我给大家演示一下吧,因为我正在背这个课, 正好跟大家一起体验一下这东西很有意思。我们其实从 ai 出来以后,一直都在探索 怎么把 ai 和架构相互结合,现在已经有非常明确的一条路。很多这个同学他一开始上来问 ai, 比如说帮我写一个 free r two s 的 控制 led 的 功能,但是 ai 其实是缺很多信息的, 那比如 led 是 哪个引角控制,有哪些文件可以修改,软件的分层是什么?这些都没有。所以正确做法其实应该还是让 ai 来问问这个学员, 问我们的初学者,否则 ai 就是 自己瞎补,知道吧?自己瞎补。所以我们要讲明白一句话,需求这种对 ai 来说没有意义,你 ai 提示词肯定要 ai 提示词的三要素,角色、任务、上下文 来给大家演示一下,在这个里面,好吧?演示一下,这是我们之前的一个项目工程,我们来演示一下, 我是一个初学者,我现在陷入是初学者,我们来看这个 ai, 他 会怎么样?我现在其实你去公司以后, 你拿到别人的就是你们公司的这个代码,他其实有,一般公司代码都有分层,你看不懂,但是领导不是让你去写一个项目,在这个里面,对于你来说,你肯定要知道 这个系统的分层是怎么样的,有经验的人他都能想到这一步。软件分层是怎么样的,那你肯定在对应的分层里面,但是小白用 ai, 他 肯定不懂这些的,所以你这个时候你要让 ai 来问你怎么问,你看我这个建筑从业者, 现在的工程是我刚开始接手,我想要在应用层,一般基本会应用层实现一个按键 控制 led 的 功能,你作为一个侵入式自身,你看我在这里,你看 这个东西我带了背景,我交代了背景,就我这里说的,你作为小白,你上来就先交代背景,但是接下来写提示词,怎么写?三要素,提示词,三要素这个东西大家一定要记住, 你写任何提示,你用任何 ai, 这三要素必须要有角色任务上下文,你是什么角色?你作为一个介入式资深工程师,我帮我在现有工程 中实现应用层按键控制 led 闪烁的功能。 上下文是什么?上下文就是我们目前这个工程,其实我在这已经说了,目前这个工程是公司现有的工程,好,现在就是比较关键了。你作为小白要这样问,作为一个初学者,你还需要知道哪些信息, 请找我确认,你看你像这样问就不一样,你用 ai, 其实无论你有没有经验还是怎么的,你都要让 ai 首先来问你,你看你把这个角色带入进去,你就不用去想你怎么驾驭 ai 了。 看到没,这这个其实我我我去仔细构思过我们这个课程,我是去构思过我们的这个课程究竟该怎么去设计, 因为我一上来,如果我教你怎么去写提示词,我一上来我教你怎么去写提示词,我觉得太难教了,因为你写架构的有一套提示词,你写驱动还有一套提示词,那你这辈子就离不开我们吗?天天都找我们要各种各样的提示词吗?不可能的, 这个直接没有任何意义。那我就想了一个什么样的方式,我来教你,我让你去让 ai 反问这个思路。太好了,这个思路我那天想,想了以后,我立马连夜做起来,把这个思路给写到我的笔记本上。我我我不教你写提示词,但我要告诉你提示词的三要素, 角色、任务、上下文。剩下让 ai 去问你。好,我们看下这个 ai 现在表现怎么样,你看你看没 看到没 ai, 这个时候他就被你驾驭了,你看没,他开始在思考了对不对?这个才是你用 ai, 不要老想着去靠你自己个人能力去驾驭, ai 的 能力是比你强的,包括甚至比我都强,只不过他沉睡的记忆太多了, 你要怎么样去把别人这个记忆唤醒?是不是你定义完你的这个情况以后, 你让 ai, 你 先给他定义角色,他是个潜入式资深工程师,要实现这个功能。上下文,是这个让 ai 来问你,这个绝对是正确的办法, 没有人可以比这个 ai 的 学习方法更正确,因为我仔细思考以后, ai 它是其实比任何人都强大, 他震慑了世界上所有的信息。而且我用的什么 ai? 我 用的是 mini max, 最垃圾的 ai。 不 不不,就是没有那么强的国产 ai, 我 这下面用的是 mini max 后面的大模型,你看他也开始问我一些关键信息啊,看到没?好,那你就顺着他思路来提示词,你不要想着去写提示词, ai 永远是比任何人都强大的, 你要做的只是让 ai 明白你的处境,给他定一个角色,你是个资深工程师,帮我完成一个什么任务,上下文是什么?你说你自己是个初学者,你有什么信息找我确认?因为他比你强大,你应该让他来问你,而不是你去引导他,包括 ai, 甚至比我都强大, 知道吧?比我强大,所以我当时想到这个办法,因为我那天一直在背这个课,教你怎么去写提示词, 但是我想来想去,其实最妙的招就是说让 ai 反问你妙不可言。我来看看 go 语言,他现在做一些应用开发完全是可以的,他以前是后端开发,切入式开发,在 linux 上有些也是用 go 语言的, 发色也有用的,也有用。哎,我今天就看下,他问我能怎么样,因为这个东西我本来要录个课,必须给兄弟们安排。明白,你看他帮我分析出来,他已经调研了你的工程,对,让 ai 来问你, 是不是这个分层都非常的细节,他分析我架构分析错了,看到没?这个地方也不算错啊,这个中间间对于芯片厂商来说确实是,你看他已经帮你分析出来,有道理,好,他给你列出出来,有这些文件在控制。现在我需要确认硬件问题,需要用万用表来,你看到没? 所以这个这个 ai 时代,你要驾驭 ai。 我 现在总算想明白了,你不要自己变得多强,你要给 ai 定一个角色,你老想着天天驾驭 ai, 驾驭 ai 是 不是? 包括我?贝克,我也天天想着我怎么驾驭 ai, 后来我想明白了, ai 肯定比你强的,但但你必须要给 ai 要讲明白这三个关键点, 然后让 ai 来问你,这才是正确的思路。原先你用 ai 说上来帮我写个什么功能,那未来你老想着给 ai 去补充这些边界对不对?补充这些信息你补不全呢? 就像我们天天告诉同学,我说你不要天天想着准备好再去面试,这个没有意义。什么叫准备好?包括杰克老师也不可能准备好的,因为切入式就不可能赋予你这样的能力,因为切入式实在太大了,没有所谓真正的准备好。 那么正确的做法是什么呢?就是说你无论再怎么补,你总有漏的,而且也很累。正确的做法就是你让 ai 问自己有什么事情还需要确认,像我这样, 你看 ai 不 就用的很好吗?我一个初学者,我照样把 ai, 你 看他能帮我想到的,他想的肯定比我全面,因为这是 ai 的 优势,他能把一件事情想的很全面。你看他又又来找我确认,确认完以后他就可以干活了。 我随便写一个高电频点亮。我我我看一下我的硬件资源手册,好吧,我我确认一下他分析的对不对。我的 led pc 十三,你看他分析对不对? pc 十三是对的,物理按键有两个可用, pb 一 和 pb 零。 我来看一看 pb 一, 他也分析对了,看没,他根据代码直接分析了 k 三 pa 零也对的,对不对?我希望用哪个看见,哎,我看一下这两个都是 u 三 k 对 不对? 然后我肯定想用这个 p b 一 控制,我就用 u 三 k 一。 所以你要用 ai 完全不需要,你不需要去,你很厉害,不要想着去驾驭 ai, ai 肯定比人强大的, 我都驾驭不了 ai, 我 也想着天天驾驭 ai 驾驭,后来还是你要去问 ai, 你 也不要想着能写好一个优秀的提示词,让 ai 来问你,这个路你就走对了。 好吧,千漩室里面上下文是最难的,上下文是最难的,所以你要看你的上下文够不够完整,让 ai 来问你。而且现在可 out, 它会自动压缩上下文,把关键信息给压缩住。我用轮询方式, 你看,你把他定义为高级工程师,别人都会来一步一步引导你,这样他又在教你啊。所以要善用 ai, 但不要想跟一个别人花几百个亿训练出来的大模型去对抗是吧?不要这样对抗,看到没? 非常优秀。你看他添加任务了,看到没?我们这个架构写的是非常优秀的,你要创建哪些任务,就在这面加就可以啊,他会自动循环去加载,你看他都直接自动识别了,看到没?自动识别了。我们的这个表驱动, 你看我们这地方,我们的架构用了一个表驱动来加载所有任务,我们的架构本身也很清晰明了,对于 ai, 它识别起来也很方便,对不对? 我们这个表驱动的一个架构就非常规范,非常的优秀。好,他已经修改完了在这个地方添加的任务,所以没取,添加了这样一个任务, 在这个里面包含了 g p i o part, 你 看他直接调用了这个抽象层, g p i o part, 是 我们这个 platform 抽象层 m c u platform g p i o part 点 h。 在 这个里面我们美取了各种各样的 按键,我们这个平台化做的非常好,我们这个平台化做的非常好,这个代码,所以它用起来也非常的流畅,你看改,都改到对应的层级里面了, 没有去乱改,看到没?它的所有的调用,你看所有的调用都是调用的抽象接口,没有去调用那个 hell gpo 什么,没有圈, 调用的都是抽象接口,看到没?这都是抽象接口,看没 top gpl 抽象接口没,没有写什么 hell gpl 什么的,没有对不对? 没有,他非常规范,他没有,没有跨越层级,这个里面有具体的 g p l 的 实现在这里,所以他成功的调用了抽象接口,也符合架构原则,非常的优秀。按键扫描任务特性,他要创建一个按键扫描任务 kiss 看,让我看一下他还有个 kiss 看,是吧?这一个就是他创建,是他, 那这个任务他应该就是在这里创建,他的任务函数在这里。那按键你看怎么写的? 是不是写的非常优秀?调用的也都是,我们叫什么调用的都是也是抽象接口,看到没?都是抽象接口,这是我们平台化的东西。在平台层你看 m c mcu platform 平台层,就这一层对 g p i o 进行了抽象平台层 g p i o 平台层 g p i o 的 适配层 port 点 c 抽象接口,这个抽象接口里面最终调用了 hell s t m 三二的 g p i o pin, 非常优秀。再再看一下他做的怎么样,我检查他作业任务名占大小,扫描周期十毫秒,依然掉了 osl 平台化的抽奖接口非常优秀, 下降眼检测定义了局部变量 u i n t 八,这个也很优秀。为什么我说这里很优秀?因为对于跨平台来说,类型一定要是能够跨平台的,他在这里你看 做了一个 s t d i n 的, 这里也没问题,可以这样,但是对于优秀的跨平台架构,不应该去包含 s t d i n d h, 不 应该包含它的,应该要包含一个自己的平台的一个 类型,他比不应该包含他,因为你包含他就代表着没有真正做到夸平台,我们一般会定一个这样的数据类型,这我后面再讲。反而可能你这个平台假如说 u n t 八,你会对他进行一个比如,比如指不是这样的前缀,反正你肯定要在这里,他的前面暂时想要这里 include, 就你这个跨平台的一个 t types, 比如我们这个手表的就是这个平台的叫 a plant form, 就 这样的一个头文件,这个 u i n t 八应该取这个里面定义的, 对,他就有这些定义去实现,就是 u i n t 八。具体的定义这里他可能没有考虑到这个细节,不过这个应该是平台的层考虑的,所以他也没有问题。我只提一下, 因为在我们这种编程非常严谨的一个视角来看,所有的地方都值得怀疑,没有付出值其实也是有问题的,这个东西必须要付出值的,所有的局部变量一定要付出值。好,这里也差不多,那剩下的还是比较优秀的,所有的基本上依赖的抽象接口。 这个地方我得看下他的返回值,为什么要做一个强转?他的意义是什么? ok, 那 这个地方他没有做一个说明, 因为我们这个地方用了我们平台化的返回值,对于平台化的返回值,你下载应用的时候应该对他的对他的返回值,如果你就算要强转,你应该要有一个注视的说,你对这个平台的接口的返回值进行了强转。但总体来说 这里还是理解有问题,因为因为你对阅读代码的人,这里他就不好理解了,对吧?因为我不认为这个东西为零的时候就代表按下来,对不对?这个地方我觉得还是需要一点点考虑,其他都还好, 但是大部分人写代码不会像我这么严谨,我这个人非常严谨的,总的来说还是不错的。大家感觉怎么样?感觉还不行,未来变成测试女娃。不会的,你看我刚才分析的这些细节,看到没,他有些东西还是没有做好。看来这个还不够资深。 看来这个 ai 还不够资深。看我的题,我的定义应该要写你是一个啊欠用,是世界套步一百强的 资深工程师。包括这个时候,其实你应该用我们的 skills, 我 们有一个按照立新的代码规范来检查代码的 skills, 把 skills 加上我觉得就没有问题。我们立新有一个非常强大的 skill 库, 就我们写的代码,我们会用这个 skills 去扫一遍,你把我的这个 skills, 我 到时候给它加进去, 你看它里面有没有说我刚才说的问题,已经很多人很多人已经下载了命名归一,你看这肯定没有,我肯定是不可能有 skills 比较全的外部输入民族教育,对你像我刚才那样的审查技能,我觉得要用 skills 其实就很容易达到我的那样的 检查水平,所以你作为一个初学者,你想把 ai 用好,不要强求自己写一个非常完美的提示词,不可能的,你要做的是让 ai 来问你, 然后 ai 问完你以后写出来的东西,用 skills 去补全,去检测,那就能用得很好。 skills 是 提示词,当然说 skills 它的它是附用别人的经验。我们也写了这么多 skills, 那 它的这个本身就是说附用别人的经验。你不能说我今天写个 led, 我 为了检查代码规范,自己再去凑一个代码检查规范的 skills 出来,很费时间。软件工程就是附用。 好,兄弟们,今天我们直播,先到这,好,谢谢大家。不用学 e m c, 先把软件干好就不错了。好,今天先到这,好,谢谢大家。

大家好,今天聊嵌入市 linux 上怎样让 ai 只搭脚手架?先看分工边界,模型只做模板、人手需求资源和并发关键底线。整体流程不是一件生成,而是约束环境分轮生成、持续人工把关。先写板卡系统 c 加加版本库和现成上线,再要目录空壳框架。 这张图说明脚手架范围核心业务先留白,别急着一次性生成首审分层依赖和 cmake, 再补日制热加载串口超时和协议,随后交叉编一跑版冒烟过了再裁切封存为团队模板。新项目附用 审核清单,要抓架构护采资源拍脑袋 x 八六甲兼容和 cmake。 这张图串起,提示生成审核闭环,避免模型越权。写关键业务维护期,让 ai 读日制找重复、识别盲等,并提醒 api 变更影响 性能要看 cpu 核端到端延时升级库先 diff, 再交叉翻译。压测定位时把日制时间线和假说并排,再用 stress、 puff 等政委 agent 先定机长权限五件套提示词,写清环境边界和禁区。 这张图总结该用不该用核心安全敏感密钥别放手,发虚就提审,落地前再自检现成 i o 设硬顶生成目标出处涉密双人走读。

面向对象最重要的能力之一就是管好访问权限。为什么数据要藏起来,接口要放出去?如何用有缘?答案全在这一节。 首先就是这个访问控制服,他到底有什么作用?其实啊,访问控制服就是用来严格的限制类的成员的访问权限。嗯, 那它其实是实现数据封装和信息隐藏的一个关键的手段。对,然后它可以让我们的代码更加的安全,也更加容易维护。那能不能具体说说都有哪几种访问控制符?然后它们分别都是什么权限?当然可以。呃,在 c 加加里面呢,有三种访问控制符。首先第一个是 public, 它修饰的成员呢,是完全开放的,就是谁都可以访问。嗯,然后一般我们会把对外的接口定义成 public 的。 第二个是 private, 被它修饰的成员呢,就是只有这个类自己的成员函数和它的有缘函数可以访问,其他的任何外部的代码都是没有办法访问的。嗯,还有一种呢,是 protective, 它和 private 类似,但是它多了一个就是可以被派生类访问。 哦,那如果我们在内的定义里面没有写任何的访问控制符的话,那默认就是 private, 这其实也是私家家的一个安全设计。了解了,那这个 public 成员它的核心作用到底是什么?其实 public 成员就是这个类的对外的接口, 就像一个大门一样,外部的代码想要和这个类打交道,想要使用这个类的功能,只能通过这些 public 的 成员来进行。 嗯,其他的部分对于外部来说都是隐藏起来的。所以一般会把哪些东西定义成 public 呢?呃,一般我们会把成员函数声明成 public, 让它们来提供对外的功能的接口,但是数据成员的话一般不会设成 public。 对, 这样可以防止外部的代码随意地去修改我们的数据。 嗯,这样就可以保证我们的数据的安全性。比如说我们可以看一下这个例子,在这个 class 关键字,而不是 struct, 它们在 c 加加里的唯一区别就是 struct 默认是 private 的 访问权限,使用上一般只用于简单的结构封装。 class 默认情况下的访问权限是 private, 这更符合面向对象的思想,所以在 c 加加中也更常用。我们在 public 下声明的成员就是对外的接口,像这里的 isbn 和 combine 就是 这样, 那外部的代码就是可以通过这些函数来和这个类进行交互。明白了,那 private 成员它的核心作用和设计原则是什么? private 成员其实就是这个类的实现细节, 它是完全藏在类的内部的,外部的代码是没有办法直接去访问它们的。哦,那这样的话就可以保证我们数据的安全,然后也实现了信息的封装。 所以说数据成员一般都会设成 private。 对, 那除了数据成员,还有什么样的东西会设成 private? 其实除了数据成员之外呢?那些只在类的内部使用的辅助函数,我们也一般会把它设成 private。 嗯,这样的话就不会让外界知道我们这个类里面到底是怎么实现的。对,这其实也符合最小权限原则,就是只给外部他需要知道的东西,其他的东西全部都藏起来, 就像一个遥控器,上面的按钮就是对外的接口是 public 部分,而内部需要调用的功能外壳里面的电路板上的各个模块就是 private 部分。错,没错, 那接下来我们就来聊一聊这个有缘。嗯,那有缘它的核心的概念和作用是什么?这个嘛,有缘其实就是 c 加加里面提供的一种打破封装的一个特例,就是它允许 一个外部的函数或者是另外一个类去直接访问这个类的私有成员和保护成员, 对它其实是一种非常亲密的协助关系。这个特性还挺有意思的。那它一般用在什么场景下呢?比如说我们会在这个类的内部去用 friend 关键字来声明这个有源函数或者有源类。 然后它通常用在比如说我们要重载一些运换符,但是这个运算符又没有办法作为成员函数的时候,比如说输入输出流的左右移运算符。 还有就是当我们有一些大局的函数,它需要高效地去访问多个类的私有数据的时候,我们也会用到有缘。 ok, 这一小节就讲完了,记住遥控器原则按钮公开电路隐藏 public, 给用户用 private 藏细节。这就是面向对象最核心的安全与优雅。我们下节课再见。拜拜。