什么?你还不知道?回答函数?今天我不讲那些高大上的概念,直接从生活场景切入,然后用最简单的代码把这个场景实现出来,保证小白都能听懂。 先说一个生活当中的例子,你去商店买最新款的 iphone 九十九,结果店员说没货了,你着急啊,问什么时候有货?店员说,不确定,可能明天,也可能下一周,你总不能天天跑过来问吧?于是店员说,要不你留个电话号码,有货了我打给你,你说,好啊,留下电话号码就走了,该干嘛干嘛去了。 过了几天,店里进货了,店员翻出你电话号码,给你打电话,你好,你要的 iphone 九十九到货了,起来取货,你接到电话,立马跑到店里把手机拿走了。这整个过程就是回调函数的完美体现。在这个例子里,你的电话号码就是回调函数, 你把电话号码留给店员,就是注册回调函数,店里进货了就是触发事件。店员给你打电话就是调用回调函数,你去取货就是执行回调函数的内容。关键点在哪?关键在于你不需要一直等着, 你留下电话号码就可以走了,该上班上班,该玩游戏玩游戏电影,也不需要知道你在干嘛,他只需要知道你的电话号码有货了,打给你就行了。这就是回到函数的核心思想, 结偶和异步。好,现在我们就用最简单代码把这个场景实现出来。首先定一个函数,指征类型,表示电话号码的格式。这就是说,电话号码指向一个没有参数,没有返回值的函数。 然后写一个店员打电话的函数,他接收一个电话号码作为参数,接下来定义你接到电话后的反应,最后在主函数里把整个流程串起来,就这么简单运行。结果是这样的,看到了吗? 整个代码不到二十行,但完美的演示了回到函数的工作原理。 socket 函数就是店员,他不知道客户是谁,但他知道客户的电话号码,当货到了,他就拨打这个电话号码,也就是调用 customer respond 函数。这就是回到函数的本质, 你把一个函数当做参数,传给另一个函数,这个函数在合适的时候调用你传进去的函数,就像你把电话号码留给电源,电源在合适的时候打给你一样。我们再看一个更直观的对比,如果不用回掉函数,你得这么写,这样写有什么问题呢?电源函数和客户的反应藕合在一起了, 如果换个客户反应不一样,你就得修改 stop notify 函数。当有回调函数,你只需要传不同的函数进去就行了。同一个 stop notify 函数传不同的回调函数,但是实现不同的行为,这就是回调函数的威力,灵活节,藕可复用。在实际编程中,回调函数无处不 大。笨鸡的按键中段,你写一个函数,注册到中断限量表,按键按价时,硬件自动调你的函数。网页的按钮点击,你写一个函数,绑定到按钮上,用户点击时,浏览器自动调你的函数。定时器到期,网络请求完成,文件传输完成,都是通过回调函数来通知你的。 读掉函数是生物中的留电话号码,你不需要一直等着,写在联系方式就可以走了,事情办好了,对方会主动履行。代码实现就是定义函数指向类型,把函数名为参数传递,在合适的时候调用这个函数。 总结一下,回调函数三步走,第一步,对应函数指征类型。第二步,写一个函数,接收这个函数指征作为参数。第三步,在需要的时候通过函数指征调用传进来的函数,就这么简单,掌握回调函数,你就能够理解异步编程事件驱动的本质。 要看到函数参数里有个函数指征,你就知道这是要你提供一个回调函数,告诉他事情办好了该怎么通知我。好了,今天就到这,如果觉得有用,记得给梁旭一键三连,我们下期见。
粉丝6.9万获赞20.8万

哈喽,小伙伴们大家好,这里是左左右。这期视频我们会通过一个非常经典的按键控制 led 小 灯的例子,彻底搞懂终端的工作流程,以及在实际开发中的正确知识。在之前的视频中,我们写过一个按键控制 led 亮灭的代码,当时 我们把按键检测的逻辑直接放在了内函数的外循环里面,大概是这样的,循环里不断读取按键 i o 口的电瓶,如果判断按键被按下, 就翻转 led 的 状态。这种方式虽然能工作,但存在一个很明显的缺陷,主程序被主设了。大家可以想一下,按下按键这个动作是随机的,而且不是持续发生的,但是我们的程序却要在外循环里一刻不停的去读按键,那他还怎么去干别的活呢?这就是所谓的轮询方式, 他会占用 cpu 的 全部时间,导致其他任务无法及时执行。说通数点,主程序什么都干不了了,只能守在那里等待按键,那有没有更好的办法呢?当然有,既然按键动作是随机发生的,我们就应该让他主动通知 cpu, 而不是让 cpu 去主动问,这就是中断的典型应用场景。把按键检测放到中断里面, cpu 平时可以安心处理其他事情, 一旦按键被按下,硬件就会自动触发中断,暂时打断当前任务去执行我们预先写好的中断处理程序,执行完再回来继续原来的工作,这样一来主程序就不会被堵塞了。好, 理论说完了,我们打开 stm 三二 cube ide, 实际看一下中断在底层到底是怎么被调用的。 首先将引脚设置为外部中断触发, 由于我们的按钮是与 d 相连的,所以我们将 pc 十三引脚设置为下降延触发,输入模式为上拉输入,同时在 n v i c 中将中断,使能点击保存代码就自动生成了。 我们都知道中断是由硬件自动调用的,那它到底调用了哪个函数呢?我们可以打开启动文件 stm 三二 f 幺零三 c 八 tx 点 s。 这个文件是用会编辑的, 里面定义了各种中断向量。仔细看你会看到一大堆以 i r q hunderer 结尾的函数名,这些就是硬件发生中断时自动调用的入口函数。 比如我们这次要用到的按键,它接在哪个 io 口呢?我们这里用的是 pc 四三,那对应的外部中断通道就是 e x t s 三, 它属于 e x t i 十五十这一组,所以我们就能看到 e x t i 十五十 i r q hounder 这个函数。当按键按下触发中断后,硬件就会自动跳转到这个函数, 我们点进去看它的实现过程,发现这个函数里调用了 hell g p i o e x t i i r q hounder, 并且传入了对应的 g p i o 引角。再继续往下追, 在 hellg p i o e x d i i r q hander 这个函数内部,它又会调用一个叫做 hellg p i o e x d i call back 的 函数。注意看,这个回调函数前面有一个 weik 关键字, 在 c 语言中表示弱定义什么意思呢?就是说如果你自己没有写这个函数,变异器就会用这个默认的什么都没做的弱函数。 如果你自己写了一个同名的函数,那编辑器就会用你的版本覆盖掉弱定义 hell 库,把中断的处理流程固定好了,但真正要做的事情留给我们去实现。所以结论很简单,我们想要在中断发生时执行的自定义代码都应该写在 hell g p i o e x t i call back 这个函数里面。 我们将之前写好的代码复制到这里就可以了。 点击运行, 这时我们发现按下按钮并不能实现小灯的亮灭功能,这是为什么呢? 这时我们将 hell delete 这个延时函数更换为我们之前自己写的 my delete 延时函数, 点击运行 程序正常执行了。我想此时大家心中应该已经有答案了。 hello delay 和麦 delay 这两个函数的区别就是, hello delay 使用了 d 大 定时器,这个中断 e x t i 本身就是中断,它打断了主程序,而 hello delay 中也包含中断, 也就是要实现中断的嵌套。这就要求 d 大 定时器的抢占优先机要高于 e 叉 ti。 打开 n v c 来看一下这里,我们只要保证 d 大 定时器抢占优先级确实高于 e x t i 就 可以了。 点击运行 程序是不是也能正常执行了?由于这里是 ex ti 十五减十的中断,这里也可能是除 pc 十三外的其他引脚触发的中断。 谨慎起见,我们在这里加个 if 判断,确保是 pc 十三触发的中断来去执行我们这段代码。其实这段代码还是存在一定问题的, 这里加入黑曜类的岩石其实并不合适。虽然四毫秒对于我们来说是一瞬间的事,但是对于单片机来说是很漫长的。中断讲究短且快, 所以在中断里使用延时是并不合适的,但是在这个案例中使用是没有问题的。随着视频内容的推进,我们会讲更合适的方法来处理这个问题的。我只是想通过这个案例让大家熟悉中断和中断嵌套的相关知识点。好的,今天的视频就到这里, 下期视频我们将使用 client 使用寄存器的方式完成相同的案例。一定要关注、点赞、收藏、评论,您的支持是我这样的小博主持续更新下去的最大动力,期待下期视频再见!

函数就是 call back 直译过来的意思,那么从它本身来看呢,它就是一个普通的函数,之后将它整个放在一个特定的环境下,然后把它称作为回调函数,那么 顾名思义,回调函数的意就是说回过头来再调用,也就是说等特定环境的特定事件发生之后,我们再去回过头来调用这个函数。那么在嵌入式当中呢, 回调函数作为中断的基石,也就是说我整个中断程序的执行是离不开回调函数的,比如说我们在中断的过程中,需要编写的是这个回调函数,之后呢, 由 cpu 去进行中断触发,也就是决定了这个回调函数是何时调用,就是我定义函数, cpu 决定何时调用整个的流程。 那么回调函数的基础本质呢,就是一个函数指征,我们通过将回调函数的入口地址作为中断触发函数的参数 来进行回调函数的触发,那么在标准库当中呢,我们对应的是 i s 二、中断服务函数,它本身就是一个硬件级的回调函数,所以我们在编辑程序的时候,直接在中断服务程序中去编辑我们需要的功能。在中断出发之后,那么在 hello 库当中呢, 我们就有专门的对应的 callback 函数,比如说我们的 gpio 中断, gpio 触发中断的回调函数, 我们需要把我们想要在中断出发之后实现的功能写入到 callback 函数里面,通过调用 lq handy 这个函数来等待中断出发之后呢, 回过头来调用 callback 函数实现硬件中断中呢,最常见的就是虚焊,那我们可以使用加立创的 smt 贴片工艺防止被虚焊。回调函数呢?不光是在 c 元中有 常见的流行语言 java 和 c 加加以及 python 呢都有所应用。

大家好,欢迎参加本次关于 stm 三二标准库回调函数的解析分享,本次分享将以精简的形式带大家快速掌握回调函数在 stm 三二标准库中的实现原理和应用方法。 本次分享将分为八个部分,从基础概念讲起,逐步深入到标准库中的具体实现代码视例、应用场景,并与 h i l 库进行对比,最后给出开发建议和总结,希望能帮助大家系统地理解这一知识点。 首先我们来明确一下回调函数的基本概念,简单来说,回调函数就是一个通过函数指数被间接调用的函数,它的核心思想是事件驱动,也就是说, 我们的程序不需要一直去检查某个事件是否发生,而是当事件真的发生时,系统会自动通知我们, 并调用我们预先设定好的处理函数。这种机制最大的好处就是实现了代码的解偶,让底层驱动和上层应用可以独立开发和维护。那么在 stm 三二标准库中,回调函数是如何具体工作的呢? 它主要分为四个步骤,首先硬件事件发生,比如定时器到时间了,然后 c、 p、 u 会自动跳转到对应的中断服务函数。在这个函数里,我们会先处理一些底层的事情, 比如清除中断标志。然后关键的一步来了,我们会在这里调用我们之前注册好的回调函数, 最后程序就会执行到我们写的回调函数里去,处理我们真正关心的业务逻辑。了解了工作流程后,我们来看具体的代码实现步骤。 这里以定时器为例,主要分为四步,第一步,我们需要用 tifat 定义一个函数指征类型,它规定了我们的回调函数应该是什么样子的。 第二步,声明一个局的函数指数变量,它就像一个容器,用来存放用户写的回调函数的地址。 第三步,我们写一个注册函数,方便用户把他们的回调函数地址放到这个容器里。最后一步也是最关键的,在定时器的中断服务函数里,当检测到中断发生后,我们就通过这个函数指数变量 去调用用户的回调函数。现在我们来看一个完整的使用。例,在主函数中,我们首先定义了自己的回调函数 on timer time, 里面实现了翻转 led 的 逻辑。然后在初步化完成后,我们调用 timer register callback 函数, 把我们的 on tommy time 二函数注册进去,这样一来,主循环就可以空出来做其他事情了。当电磁气中断发生时,系统会自动去调用我们的回调函数来处理 led 的 翻转,整个过程非常清晰和高效。 回调函数的应用场景非常多,几乎所有需要处理异步事件的地方都可以用到它。 比如,当我们需要检测按键是否按下时,可以用 gpio 外部中断的回调。当我们需要每隔一段时间执行一个任务,比如采集传感器数据,可以用定时器中断的回调。 此外,在串口、 s, p, i, a, to c 等通信过程中,数据传输完成的通知也通常通过回调函数来实现。可以说,回调函数是实现事件驱动编程的核心。 最后,在实际开发中,我们还需要注意几个关键点,第一,回调函数是在中断中执行的,所以它的代码必须非常简短,绝对不能有延时等阻滞操作。 第二,在调用回调函数之前,一定要检查函数时针是否为空,否则程序会崩溃。 第三,如果涉及到共享资源的访问,要注意处于好临界区,避免出现数据不一致的问题。第四,良好的命名和注视习惯能让我们的代码更容易被理解和维护。 总结一下本次分享的内容,回调函数是实现事件驱动编程的关键,它能很好地解偶我们的代码。在 s t m 三二标准库中,我们需要手动利用函数指真来搭建这个机制, 其核心步骤就是定义、声明、注册和调用。它适用于各种异步事件的处理。虽然相比 h a l 库,标准库的方式更复杂一些,但它也带来了更高的灵活性和更清亮的代码。 希望通过这次分享,大家能对标准库中的回调函数有一个清晰的认识,谢谢大家!

跟着练,你的单片机会很牛!二零二六年单片机从零到一的完整学习路线。第一步,编程基础,因为我们想让单片机工作,你要写代码的,用什么语言呀? c 语言, 在这个阶段,咱们一定要把 c 语言和数据结构搞定,零基础。小白,按我这个来就行,我给你划重点,任何一门语言啊,数据类型,常量、变量、运算符,接下来你是不是要写循环呀,什么数族,四幅串,整数等等吧。 啊。还有一个结构体,在单反节开发当中,结构体用的非常的多,还有结构体之争,一定要注意,到此结束,暂时先不用去看什么 mylock free 这些动态内存相关的内容,单反节开发当中用的比较少了。 另外一个,写代码的时候,我们要会写 make file, c 语言有了之后,哎,我们要用 c 语言去实现各种各样的数据结构,还是划重点, 那我们重点学的是限行表。限行表有两种实现方式,一个是顺序表,一个是链表。链表重点看单链表。接下来站和对链作为重点一样的,也是分顺序方式的实现。链式方式的实现我建议到此为止。至于后续的什么数啊,图啊,你暂时也不用看, 用我这个方法纳学,一周期三十天听清楚了吗?一定要在一个月之内搞定数学题,三十天,听清楚了。这里边有一个特别大的坑, 编程课你不能光看不练啊,所以一定一定要动手敲代码。有太多的小伙伴一看视频什么都会,把视频一关, 脑子一片空白,代码一点都写不出来。那我们定一个小目标,你每天要求自己大代码小代码全都要敲,我觉得你至少要保证两百行吧,可以吗?睡觉之前想一想,敲够两百行了没有?在这个阶段,顺道的时候把面试题刷了,反正也要写代码。写什么?不是写,把面试题里的代码刷一遍,这样到最后找工作的时候, 第一关笔试这一关肯定轻松过。如果觉得 c n 税结构比较枯燥,那你就搞点小项目,比如说做一些小游戏,俄罗斯方块啊,贪吃蛇呀,都是很好的练手的一些方法。我自己录制了一套 c n, 对 小白非常的友好,而且重点强化了代码。教你怎么写程序的同时呢,把一些 经典的编程题目我都给你梳理出来了,六十道,你要把它吃透,反复的去敲一敲编程搞定了第二步干什么?该硬件基础了,你想嘛?你做单片机开发的时候,是不是要看电路原理图?如果你电路原理图都看不懂, 那你传参数的时候传个零,传个一,哪个灯泡能亮啊,你就傻眼了。所以我们这个阶段要补一下电路设计,这边电路设计你可以用 a 创 e d a, 就 很多小伙伴肯定就会觉得我这也太全推了, 他会死磕这个魔术店?不能啊,你要死磕魔术店,那书本那么厚,很容易入门到放弃的。其实我们在补这个硬件基础的时候,我不是让你把大学的魔术店那本书从第一页看到最后一页,你要学会划重点,够单面机开发用,能看懂图就行了。那常见的原件有什么?定做 电容、电感二极管、三极管、魔术管,说实话,运放用的都相对比较少了,然后我们可以自己买一些原件,然后自己搭搭电路,有点感觉,或者说呢,你结合一些仿真工具啊,有在线的呀,或者说什么 u g c m 呀,都可以,这样提升你的学习兴趣,学习效率也更高,给大家提供一套免费的教程,一周搞定魔术店 注意,我的目标不是让你精通,是让你能够看懂单片机的原理图,魔术店一周简单的画个板,比如说最小系统。 那十天够了,终于到 itm 三二了,软件基础面基础都有了,学习基础,外设和高级接口,如果说你的学习时间比较充足,比如说你大一大二,那大家有机会的话玩一下五幺也行,毕竟对于五幺来说,它是用计算器的方式直接控制硬件的, 你能够知道它的本质,知道单刷机底层的工作原理,然后你再进阶到 itm 三二,那你说老师我时间比较紧张,我也着急找工作,着急实习,那我要这个地方可以直接跳过,没关系的,什么样的外设是重点呢啊?第一个肯定要点灯,点灯用的什么 gpl, 你 要调试程序,那用的是什么串口, 你要控制按键,用到了什么中断,然后最基础的什么系统时钟啊,定时器啊,那你想到定时器,是不是后续就想到了 pwm, 想到了看门狗?还有咱们的一些 操作模拟传感器的时候,是不要用到了 adc, dac, 还有各种经典的总线协议, iphonec 啊, spi 啊,还有单总线协议, 那么间接一点的接口,像一些 touch 接口,电源的管理, st 卡, usb。 这个阶段学生最容易踩的坑是什么呢?就是学东西太死板,然后只知道照着教程去复刻一些现象。他不做项目, 那你不做项目一定会学后头往前头,关键是等你做项目的时候,或者是换个开发版,让你去做项目的时候,没有什么思路的,做不出来呀。那比较好的方法 就是一边学接口,一边想着在这个项目场景当中,我们回来用这个接口,比如说你要启动一个屏幕,那在这个时候你是不是就用到了 f c d 屏这样的一个外设?你会用到了 f c d 和 sp i 这样的走线协议,那你还会把一些传感器的数据读出来,就连显示到屏幕上。所以你看 只要有这么一个项目场景,其实能够把前面重要的一些基础的外设知识,协议的知识都串起来的。定个目标不容易放弃,这个阶段一定要想办法提炼至少两个项目, 将来我要写到简历上打呃,注意休息周期,一个月足够了,给大家提供一些资源,教程大家都好找是吧?那我给你提供不好找的项目。智能运动手表用到了 lv g l r t s 无线通信,还有智能语音小助手,从电路设计到单面机开发全流程的,还有蓝牙音响,也是 软硬结合的项目,而且这里面有蓝牙通信,智能桌面小狗,这也是单面机里边非常综合的一个项目。第四步,该上操作系统了,我们学 i two s 加 l w i p, 只学裸机的话,新的商圈是可见的, 在北京这个城市最高也就八 k, 其实有没有 r to s 这几乎是一个大数据工程师薪资的分水岭,想过万必须要搞 r to s, 划重点啊,怎么创建一个任务,做任务怎么调度?任务之间怎么样通信 消耗量,定时器内存的管理,还有 socket。 这个阶段它的逻辑有点复杂了,所以很多同学在学习的时候,你如果光看不练,你一定会觉得它非常的抽象,因此在这个阶段一定要一托于代码和项目来去做,这样你才能够理解。哎,用逻辑的时候遇到过什么问题? 那上了实时操作系统之后,我这个项目它是怎么加工更合理的?代码是怎么变得好维护的啊?这个用户体验是怎么变丝滑的?有了前面版本机的技术,再学 r t s, 加上一些无线通信啊,包括 l w i p 啊,留一个月的时间就行。推荐一下韦东山老师的福尔达 s, 从入门到精通。

大家好,本期视频讲解跑马灯与延迟函数,先来看一下完整的效果, 再给大家看一下连接图,我这边使用的 a 八 a 九 a 十眼角,下面我们开始制作这个程序,打开之前创建的目录,把工程模板复制一份, 重新命名一杠二,跑马灯, 点击 project 进入程序, 现在看到的代码就是上次写的,全部删除掉。首先还是导入头文件, 是能 p b 二总线的时钟, 创建一个用来存放 g p l 配置参数的结构体, 复制这个结构体放到下面演出后面需要的参数, 下面开始配置模式,还是使用推挽输出 设置我们要用到的引脚,首先就是八号引脚,不能把这个代码复制下来,再设置九和十把,这样太麻烦了, 我们可以在后面加上或九和或十, 设置速率还是五十, 这样我们前面的配置就完成了,我们用循环的方式持续点灯, 现在给这三个引脚高电瓶, 按照正常来说,是不是在给这三个引角低电瓶就能实现好马当了呢?现在来试试直接把代码复制改成低电瓶, 稍露一下看看 一直亮,并没有实现视频开头的效果,这是为啥呢?因为设备的运行速度很快,肉眼根本看不到, 我们可以使用延迟函数,让程序稍微的暂停一下,来实现跑马灯的效果。我这边用到的是江邪科技的延迟,我们把延迟函数的文件复制下来, 创建一个 system 文件夹,在 system 里面放入延迟函数文件, 再去 q 中把文件添加进来, 这样就好了。然后导入延迟函数的头文件, 给每一个地方都加入延迟一百毫秒 编辑,运行一下看看, 就会看到和开头效果一样了,眼角还有另一种写法就是十六禁制,跳过去看看, 后面就是对应的十六禁制,把它拿到程序里试试, 分别是八九十。 再来运行一下看看,可以看到效果都是一样的。视频到这就结束了,下期驱动蜂鸣器,拜拜。

如果你玩过 stm 四二,也写过一些代码的话,那么你应该会发现很多函数的前面会加张 weak, 并且被 weak 修饰过的函数可以再去定义一份,就是相同名字的。 这个如果从 c 语言的角度去看的话,肯定是行不通的,因为 c 语言里面不允许出现两个同名的函数,翻译的时候就会提示重复定义 weak, 在 stm 三二里面,它其实是一个红定义,如果你跳过去的话,应该会发现就是这么一个东西,叫做 abbeute, 然后呢,是两个括号里面写了一个 weak aspect, 它不是 c 语言里面的关键字,也不是 c 标准规定的什么特性,而是属于某些编辑器的扩展属性,比如说我们常见的 g c c 编辑器就支持这种用法。 用 aspect 加上 weak 定义的这个符号呢,叫做弱符号,是什么意思呢?举个例子啊,比如说现在写一个一点 c 里面定一个函数也叫 test, 如果让一点 c 和二点 c 放在一起去翻译的话,那肯定会提示重复定义,这个时候呢,我把一点 c 里面的 test 的 函数前面写上 attribute weak, 然后再去翻译的话,链接器在链接的时候就会链接二点 c 里面的 test, 因为这个函数呢,属于叫强符号, 编码呢没有问题,运行呢,输出的就是二点 c 里面 printf 打印的内容,那这个时候如果我再来一个三点 c 的 话,里面还是写一个 test 的 函数,编码又会出错了,因为二点 c 和三点 c 里面的 test, 它都属于叫强符号, 是不能同时出现的。又是一个重复定义,那这个时候呢,只要你把二点 c 或者三点 c 里面的这个 test 函数前面也加上 attribute weak 就 能够解决问题,然后运行变异都是没有问题的。 weak 呢?在 stm 三二里面,经常会出现在一些中断函数的前面,满足某个条件,触发某个中断,那这个中断函数呢?一般就是留给用户自己来写的,那这样的话,即使说工程里面出现两个同名的函数并也不会报错。

这节课我们来学习 stm 三二单面机的中断系统啊,如果大家学过五一单面机的话,对这个中断呢,应该不是特别陌生。首先我们需要给大家介绍一下中断的基本概念, 在日常生活里什么叫中断啊?就是打断你正在进行的事物,然后你暂停当前的动作,转而处理其他的事项,这就是我们在日常生活里接触到的中断。实际上单面机这个内部的这个中断系统,它的原理呢,大致也是这样子啊,我们首先来看下单面机它常规的一个运行逻辑,我们要写代码啊, 像 c 语言写的高级代码,我们翻译之后呢,其实会得到翻译指令,那最终呢,会转化为啊,转化为这个二进制机器码, 那随后呢?哎,我们这个代码呢,依照主函数啊,主函数,然后单片机内核呢去,哎,按照顺序逐条执行,是不是?那工程里面我们通常呢会放一个,哎, well, 死循环啊,无限循环,让我们这个程序持续的循环运行,那比如说你现在这个程序在执行的过程中,有一些突发事件啊,需要你立马暂停去处理,可行吗?如果说没有中断机制的话,可行吗?不可行啊,不可行, 因为当紧急事件到来的时候,我们说了,你当前这个程序呢,是要按照我这个指令的顺序,从上往下顺序执行,不能出错。如果没有这个中断系统的话,或者说没有这种打断机制的话,那紧急事件到来的时候,他就到来了啊, 你不能立刻处理,那有些紧急事件需要你去及时的响应的,如果你不及时响应的话,会造成一些恶劣的后果,所以说,哎,如果说我们没有打断机制,紧急事件到来不能立刻处理,那么 就可能会造成严重的恶果,对吧?本质上就是因为我们必须要完成当前全部的代码才能响应新事件,如果要有这个 while 一 这个死循环存在的话,我们当前这个代码就不可能执行完啊,那么这些紧急事件到来就到来了,我们也不能去处理它们。 好,下面我们来看一下具体的定义啊。具体的定义,当我们这个内核或者 cpu 呢,正常执行主程序时啊,我们说了这个主程序呢,往往呢是这个提前规划好的,按顺序推进的常规事务,对吧?你看我们写那个主函数的时候,先干什么?后干什么, 他的这个流程是固定的,步骤是可控的,从上面开始执行,哎,从上往下是不是按照顺序执行?记住代码,这就是我们这个主程序。好,那有的时候呢?有些突发事件啊,突发事件,或者说这个中断事件他什么时候来啊?我们不知道,我们无法提前预判这个,呃,这个具体的突发事务, 那么这个时候呢,就需要有中断机制,一旦检测到,哎,预设的这个突发事件来了,我们应该立刻暂停当前的运行流程。你看我这个主程序啊,其实里面是有很多 呃,很多指令的,那么比如说到这行指令结束的时候,哎,有一个中断,那么我就跳转到这个中断去执行这个中断啊?在什么地方处理这个中断?其实有个有个概念叫中断服务程序,说白了就是写个函数啊,写个函数来专门处理这个紧急事件,那么也对应了一些指令,对吧? 我们写的代码其实就是一些指令,那么当我这个紧急事件处理结束之后呢?最后一条指令指令结束,那么再返回到主程序,相当于是在之前这个地方打了个断点啊,我记忆这个上下文是在这个地方跳转到我这个,呃,紧急处理紧急事件的中断服务程序的,那我这个中断服务程序 处理结束之后呢,我再回到我之前这个断点,哎,然后呢接着往下执行后面的指令啊,也就说事件处理结束后,返回中断这个中断的这个断点处啊,断点处,然后继续执行原有的主程序,该运行机制呢,称为中断, 就是能够打断我这个主程序的他的执行,然后转而呢去处理紧急事件。那么对于这个主程序来说,谁在执行 cpu, 或者说我们这个内核在做计算啊,判断这些工作,那这个处理紧急事件是谁做?肯定还是一样的 cpu 去做呗, 是吧?所以说我 cpu 在 做这个东西的时候,前面这个地方你就只能卡在这等着,等我这个紧急事件处理结束之后,哎,我再回到主程主程序往下执行 啊。因此呢,我们这个中断它的运行流程呢,可以分为这个几个状态。首先是常态啊,我来写一下什么叫常态呢?哎,就是我正常开展自身的常规事务处理这个主程序啊,这就是常态。那么在这个地方,哎,有一个触发,什么叫触发呢?就是 突发事件到来了,我们需要暂停我当前这个手头工作,哎,去处理这个紧急事件啊,所以说这个地方有个触发,那触发完了之后就处理呗。啊?你,你就开始处理我们这个紧急事件,哎,你要做什么事?第一件事做什么?第二件事做什么?第三件,第四件啊,围绕我这个紧急事件, 哎,做一系列的事啊,做干一系列的事,干完之后啊,当我这个事件处理完毕之后呢?回到,回到这个原有事物继续推进,那么这个过程呢?称为恢复。所以说四个,四个过程啊,常态,然后这又触发,然后这是处理,那最终呢?这其实呢?哎,又回来,对吧?就是恢复。 其实呢有一个与之相对应的方法啊,你不就是为了检测有没有这些突发事件到来吗?啊?有一种方式呢,叫轮询,或者说查询。轮询 什么意思?我们给大家举个例子啊,比如说你当前的主任务呢?是学习啊,学学单面机啊,学 stm 三二单面机,这是你的主任务。哎,你从第一节开始看,然后第二节、第三节、第四节到某一时刻可能会有中断事件啊,比如说你妈给你打电话 哎,你爸给你打电话,你女朋友给你打电话,你朋友给你打电话,这些呢?其实呢都是这个中断事件, 那么这个轮询它是怎么做的?就是说你脑子里哎,有一块呢,你女朋友给你打电话,你朋友给你打电话,这些呢?还要留出一块内容, 哎,要留出一块内容干什么?始终的问自己,哎,你妈给你来电话了没?你爸给你来电话了没?你女朋友给你来电话了没啊?如果说没有,然后再回过头来接着问,你妈给你来电话了没?你爸给你来电话了没?你女朋友给你来电话了没?直到某一个时刻,哎,你问,你妈给你来电话了没?哎?你妈给你来电话了,然后呢?你去处理对应的这个事件 啊,你看这个方式呢?轮询,这个方式,其实我们要对键盘的事件进行轮询,你哪一个按键按下来?没有 啊?其实就轮询一遍,就这个概念,但这样的一个问题,他是效率很低,对吧?你看还要占用你一块这个大脑,还占用一块你的这个,这个思维的这种这种资源, 那对于我们这个终端来说呢?哎,我,你就是全心全意的去去去学习就行了啊,你的大脑全新全新,全身心的放在学习上,然后呢?有人给你打电话,通过什么方式来提醒你?通过你的手机铃铃声啊?手机铃声有了 啊,来了你就知道有人给你打电话了,然后呢?你在,你在这个中断你,你,然后呢?你再暂停你当前的这个事物去处理紧急事件,完了之后,回来之后你接着学习全身心的一个投入啊,这就是我们这个轮询和中断,那这个 轮询他其实是靠循环来的,就是你,你得反复的去问啊,反复的去问,谁给我来电话了没?谁给我?谁给我打电话了没?各种各样的这个, 这个,这个中断事件啊,你,你得循环去查询,这个过程呢,是非常低效的啊,非常低效的,相对于我这个相比较之下呢,我这个中断系统,它的效率是更高的啊。 好,随后我们来看下一个概念叫中断源啊,能够发出中断请求,引发 cpu 中断的来源就是中断源啊。你比如说你在学习,在学习单面机,对吧?你妈可能找你,你爸可能找你,你女朋友可能找你, 或者其他什么样的人可能会找你,导师可能会找你,对吧?你研究生你,你在外放假,然后你导师可能会找你,那这些这些人呢?其实就是你的终端员,比如说你当前正在学习,或者你当前当前正在打游戏, 他们就是中断员,谁能够发起打断请求,谁就是中断员。那什么是中断请求呢?啊?什么叫中断请求呢?就是中断员向 cpu 发出的我需要处理的信号,就是中断请求,比如说你在打游戏,然后呢,你妈给你打个电话,你的手机上就会有有铃声,对吧?你打开手机一看这个铃声,这个铃声上面写的是,哎,妈妈来电,你就知道, 哎,这个时候呢,就是中断员向你发出了这个我需要处理的这个信号啊,这其实就是中断请求。 那什么叫中断服务程序呢?当 c p u 收到这个中断请求后啊,专门用来处理该中断事件的一段代码,就叫中断服务服务函数。比如说你还是一样的在打游戏,然后呢,你妈给你打电话,你妈就是中断员,欸, 并且呢,你收到了他给你的来电中断请求,你接收到了,然后呢,你妈可能会叫你去买菜,对吧?可能叫你去这个呃干一些其他的事,那么这个呢过程呢?就叫中断处理程序。所以说对于一个中断员来说,往往他是固定的,比如说就是,哎,有哪些人会给你打电话 啊?你在在一个简单的情况下,你正常是知道的啊,哪些人可能会给你打电话?比如说你爸你妈,对吧?你女朋友, 那中断处理程序呢?往往是不确定的,对一个中断员来说,他可能对应了不同的这个中断处理程序啊,你比如说你妈今天给你打电话是让你去买菜,明天叫你呢,哎,可能是干别的事,后天叫你呢,比如说让你去取快递, 对吧?大后天叫你呢,打电话叫你呢,是让你搬东西,都有可能啊,所以说这个中断处理程序呢,是需要我们主动去定义的,而这个中断员呢,往往呢是确定的,有哪些中断员是确定的? 好,那么下面的概念呢?是上下文保护啊,我们也称为这个保护现场,现场保护。那么在想用中断前,你比如说我在执行主程序,对吧?这个主程序其实就是一条一条的命令,到某一行命令执行结束的时候,哎,我这个中断来了,那么我就要从这开始, 哎,把之前的这些所有的状态都给保留下来,以及当前这个点啊,是哪一个点?我是从哪一条指令结束之后去处理这个紧急事件的? 把这之前所有的状态给保留下来,你比如说我这个所有的变量啊,啊,这个运行状态啊,变量啊,这些数据啊,全应该保存下来啊, 这样子的话,当我处理完紧急事件之后,我才能回到一个正确的位置,开始执行下面的命令啊,并且呢,哎,把之前的这些数据给恢复,叫恢复现场,对吧?中断处理完成之后,把之前保存的上下文数据还原,回到断点, 继续执行主程序啊。如果这些工作没有做好的话,你看你,你出去之后再回来,回来之后,你首先你可能不知道从什么地方开始啊,不知道从什么地方开始,以及如果说前面这些数据都丢失了, 或者说你没处理好,没有保存好,那么即使你知道从什么地方开始,那么下面这些指令的执行也是没有意义的,因为下面这些指令指令指令很有可能是需要之前的状态作为铺垫的啊,你比如之前某一个变量啊,某一个变量,但是你现在对这个关于这个变量的这个内容给丢失了,那么下面的这些指令呢?也就很难再执行了 啊。所以说关于这个中段里面有这样两个概念,叫保护现场以及恢复现场。好,下面呢,我列出了中段的完整执行流程,大家可以自己看一下啊,这个内容我们刚才都给大家讲过了, 随后我们来看下一个概念叫呃,中段宪套啊。当正在执行一个中段服务程序时,又被新的更高优先级的中段 打断,那么这种中断套中断的机制,我们称为中断签套啊。举个例子,比如说你当前的研究生,是吧?研究生你有自己的课题组,有自己的办公室,你在你的办公室里,比如说在正在写论文,或者说初一老师给你交代一些事,比如说写基金呀, 啊,比如说写写论文啊,做项目啊等等,对吧?那在某一时刻啊,你也想摸鱼了,你累了,正好你女朋友给你打电话来了,然后呢?哎,你就开始和你这个女朋友不停的去交流,对吧?哎,交流各种各样的事情,讨论各种各样的事情,聊的不亦乐乎。那么在某一个时刻,你聊的正起劲的时候,然后呢,你导师给你来电话了, 那这个时候呢,你导师的优先级更高,因为今天可能本来就是一个正常的工作日,于是你就得跟女朋友说好,到目前为止,我们之前的谈话我都知道了。然后呢,我导师给我来电话了,他逼事真多,我得先处理他的事去,一会我处理他的事。之后呢, 处理完之后,我再来给你来回电话,于是呢,你就屁颠屁颠去处理这个更紧急的事件,就是你导师这个更紧急的事件,哎,你到你导师这,你导师告诉你,你去给我把什么什么材料交一下 啊,你去给我填一下什么什么系统,总而言之,让你干一些杂事啊,干完一些杂事之后呢,哎,做完了回来,回来之后呢,你又接着跟你女朋友聊天说,哎,好,之前的这个话题呢,我们接着接着聊啊,你之前这个内容呢?我,我恢复了记,记着呢,我们接着往下聊,你往下聊,那聊到某一时刻,比如说,哎,到点了啊,到点了, 然后你再回到你这个主程序啊,接着去接,接着去干你下面这些这些工作,继续执行啊,这就是中段签套。那么中段签套有一个核心条件是,这个新的这个中段的优先级一定要大于你当前正在执行的中段优先级。那你别再,那你再,比如说, 还是一样的,在这个地方,你和你女朋友打电话,然后呢?这突然来一个推销,推销这个电话啊,推销电话,那往往你是直接忽略了 啊,你个推销电话,你怎么能给我这个女朋友打电话?这种相,这个重要重要程度相提并论呢是不是?那我就,我就直接忽略你啊,我直接忽略你不存在后面这个地方,哎,然后我接着这个,接着接,接着和女朋友打电话啊,也就说低优先级中断,不能打断,高优先级中断。 下一个问题,我们来看 stm 三二单片机,它的中断源,根据根据这个中断源的来源呢,可以分为两大类,一个呢是外部中断源哎,比比如说我们这个单片机,这个片上外设 gpl、 定时器、串口、 lnc, 它们呢?能够发起中断请求啊, 那对应的中断我们称为外中断。那还有一类啊,还有一类这个中断中断源是内部中断源,指的是这个内核自身产生的啊, 那它对应的概念叫内中断。内中断也称为异常。你比如说芯片内核出现一些问题,断电啊,硬件故障等情况,那这个时候你肯定也要终止当前的这个任务了吧?就好比说你正在学习啊,正在学习,然后你这个大脑呢,出现了一些问题哎,比如说缺氧啊,或者说其他什么,哎,一些疾病, 一些故障,那这个时候你肯定要中断当前的这个学习,然后去处理一下你这个这个问题,是吧? 好,大家注意一下啊,一般提到异常,在中断这个概念里面一般提到异常,我们指的是内中断,我们这个课程里主要主要讲的是单面积的各种这个片状外设啊,所以说重点呢,在上面这个地方,下面这个什么内部中断啊,大家了解一下就行了,主要呢是在上面。 好,接下来呢,我们来思考一下,为什么我们这么需要中断啊?其实是对前面的课程进行一个总结, 通过前面的一些概念,大家可以看到,在很多场景下啊,一些事件是突发的,我们根本无法预先安安排好这个每一步要发生的事 啊,那么程序只能是被动的等待这个突发事件到来,然后呢再去处理,无法预设这个执行的这个时间,是不是要等待, 那如果说一直等待,那我们就需要持续的查询状态,这个效率非常低啊,就说你要一直问,你妈给你来电话了没?你爸给你来电话了没?哎,你女朋友给你来电话了没?然后呢?哎,再你问完一遍,然后再回过头来接着问啊,这其实就是轮询, 那它其实呢会占用你大量的这个系统资源啊,就是周期性的一轮一轮的询问,这种方式简单直接。其实在我们这个游戏里面啊,这个写游戏的时候经常会遇到这样的情况,游戏里它有有自己的循环哎, well, 哎,比如说来个主循环, 然后进来第一件事,先轮询一下我这个键盘,我这个键盘有没有什么按键按下,比如说 w、 a、 s、 d 检测一下,如果说,哎, 这个玩家呢,按下了 w, 那 么在这一轮轮循里面就能检测到 w 啊,比如说往上移,那么这个人物本来是在这个地方,然后然后检测到这个 w, 我 让我这个人物人物的位移往上走一点,随后把这个人画在上面这个位置, 哎,然后把下面这个原来这个位置擦着,就是把原来的擦着在新的位置画出来,这就是游戏的本质啊,本质就是一秒只要这个速度画绘质,这个速度超过多少帧?你比如说三十帧,那么他就是一个连续的过程。 本他那个原理很简单,把之前的内容擦掉,然后呢在新的位置上换上我这个人物,或者说你把原来的动作擦掉,然后换上新,换上新的动作,只要这个过程够够快,他就是一个流畅的画面。那你比如说第一次是轮巡,对吧?这个游戏的这个键盘轮巡,然后我第二第二步呢?我要再轮巡下我这个鼠标,比如说左键 按下没有啊?滚轮滚动没有,或者说右键按下没有啊,以此类推,这其实就是轮巡。轮巡在游戏里是经常使用的,但是呢,在我们这个单片机里, 我们这个芯片他本身的性能就不是特别好啊,他会这种方式呢?会这个大量消耗我们这个,呃,系统资源,因为我们知道这个中断员其实特别多啊,那你都问一遍, 其实挺挺值资源的啊,挺值资源的。所以说,呃,关于这个轮询方式他是不太适合的,那与之对应的这个中断呢? 他非常适合。有什么样的这个中段员?我不管你啊,什么样的中段员,你什么时候来都行,只要你给我发出这个请求,然后我,我去处理你的这个对应的这个任务就行了。我不关注你什么时候来啊,以及你来不来。 所以说中段最核心的价值在什么地方?在解放 cpu 啊,你这个 cpu, 你 去完成你正常的这个任务,哎,我们这个主卡中,你去完成吧,然后随后呢有中段来了,我提醒你,然后你去处理对应的这个中段服务程序,然后你再回到你的 注函数里,保证你在做每一个工作的时候都是在全心全意的工作。在了解了终端的基本概念、原理和重要性之后呢?啊,我们再来看看 stm 三二这款芯片,它的终端配置,还有还有这个终端系统的工作方式。 首先大家要明白我们这个 stm 三二它的内核啊,当前我们这个型号它内核呢是 context m 三,那我们就要了解 context m 三内核对中断的支持,大家要注意一下,我这句话我写出来吧,我们需要了解 context m 三这样一个内核对中断的支持,为什么有这样一句话啊? 因为中断的核心是什么?是外部或内部的一些突发的事件,意外的事件,打断 cpu, 或者说内核的处理流程,对吧?所以说中断支持能力和内核的架构是直接相关的。所以说我们首先要了解 callix m 三内核对中断的支持啊, corex m 三内核呢,它支持的这个中中段数量特别多啊,原生支持就是这个 corex m 三啊,它原生支持二百五十六路中段,也就意味着如果你一个芯片使用我这个内核,那么最多 啊,是有二百五十六路中段的,那能比它高吗?不能比它高,那可以比它低吗?可以啊,十一的芯片我们是要精简的,你比如说我们当前这个 stm 四二 f 幺零三系列,它合计呢就 七十个中断啊,虽然我最多支持二百五十六路,但是我只用了七十个。那么在这个 context m 三这个原声的二百五十六路中,有十六路呢是内部中断啊,就是异常嘛,剩余的呢是剩余二百四十路呢是外部中断。 可以看出来啊,这个内部中断它是 cpu 内部产生的呃,异,比如说是 cpu 内部产生的异常,这种中断呢,它的数量比较少啊,十六个,比如说掉电啊、硬件故障啊等一些技术类型啊,一些这个技术类型,再比如说一些不可屏蔽的中断。一会我们在呃一个表里啊给大家看, 总而言之呢是十六个,那还有二百四十个呢是外部中断啊,可根据这个外设来进行,哎,进行这个选择最多呢是二百四十个啊,你比如说我们这个 stm 三二 f 幺零三系列,它其实呢只支持六十个,但是它是基于这个内核的,这个内核,哎,它能够提供二百四十路, 只是说,呃,只是说我们不全用啊,不全用。另外对于这个二百五十六路中断,理论上来说存在二百五十六路,这个二百五十六级优先级啊,这个优先级我们说了,优先级高的, 他可以打断 u 线级低的,那我现在一共有二百五十六路中断,对吧?最多有二百五十六路中断,那我给每一个中断一个 u 线级,相当于是有二百五十六个人,然后我给他编号,这个编号恰好就是从一一人一个编号,不同的编号一到二百五十六,一共二百五十六级,那比如说这个数值越小 u 线越高啊,此时呢,就是每一个中断都有自己一个 优先级啊,并且自己的优先级一定和别人的不一样,因为现在是二百五十六个中段,然后还给他配了二百五十六个优先级啊,并且呢支持中段监考,也就是高优先级的能够打打断这个低优先级的这个中段。 但是在这个实际芯片中啊,我们不需要如此多的这个中段啊,如此多的中段以及这个优先级。 所以说,呃,一般呢,不同芯片的厂商会对 callix m 三的这个中段数量和优先级进行裁剪 啊,这个 stm 四二最大支持八十四个中中段,十六个是内核中段,六十八个是这个外部中段啊,以及它不再支持二百五十六级优先级了,太多了,一般用不了这么多,给它裁剪成了十六级可编程的优先级。你比如说啊,我现在有八十四个中段,哎, 八十四个中断,每个中断呢,我肯定可以给他划分一些优先级,对吧?哎,你比如这这里面,这样一个组里面啊,这样一个组里面有一些中断,第二个组里面有一些中断,哎,第三个组里面有一些中断员,那一共呢?有多少个?一共有十六级, 对吧?这是第一集里面包含了一些,第二集里面包含了一些中断啊,第三集里面包含了中断,然后第十六集,哎,里面的也包含了一些中断,然后把它们合起来放在一起啊,合起来放在一起,所有的这个中断拿出来,其实正好就是这八四个中断, 他是这样一个概念。而对于我们当前这个,呃 stm 三二 f 幺零三来说,他又进一步进行了这个裁剪啊,因为我们这个芯片,他的,他的这个配置不是特别高, 总共支持七十路中断,其中十个呢是内核中断。你看我这个 cortex m 三最多呢是有十六个,但是我在现在呢只只使用了十个,并且呢最多支持六十个片上外设中断。

同学们好,我是超子,那么今天呢,我们是第六小节啊,我们穿插一个知识点,对 help 啊,它的中断处理函数,以及 week 啊,若声明的中断回调函数,那我们呃先着重的介绍一下 啊,因为呢,他不光光是针对咱们外部 l 口的外部中断啊,那各个呃外设都是一样的。 好,我们先来看啊,就是 help 和标准库的有一个很大的区别点啊,就是 help 啊,它相当于是一个 呃保姆级别的,对吧?他将各个外设的中断处理函数啊,都帮我们做好了啊,不光光是 l 口的外部中断啊,定时器,串口, adc, spi, f, oc 啊,各个外设, 那么然后呢,他会通过一个回调函数啊,就相当于是个子函数,你在这个子函数当中啊,去添加你想要处理的工作,那比如 说我们下一个章节啊,我们去做外部中断检测按键好,按键处罚了产生的中断。那么在这个外部中断的回调函数当中,那我们去添加什么程序呢?去添加控制 pb 零 l 口高低变电瓶的程序,那么进而呢,去控制 led 四的亮面。 哎,所以说各个外设的中断处理以及这个回调函数,他都是这个思路。那么所以说呢,这种情况啊,对我们新同学和老同学可能会有不同的感受。那么老同学,那我们用标准库 啊,那么标准库的时候呢,我们都是自己去处理中断处理函,中断这个函数啊,想要干什么,判断什么标志位,那我们自己处理。所以这个时候呢,可能会觉得黑小库啊,将这个条条框框卡的太紧啊,那么这个时候黑小库也并不是强制要求我们必须用他的中断处理函数,我, 那也可以像标准库一样啊,我们自己来做这个中断这个部分,那如果说老同学啊,也想省点事,是吧?啊,那咱们用他这个回调函数,那也是可以的,那如果说对新同学而言,对吧,那么这个还有库呢就,呃,应该是比较这个贴心了 啊,因为新同学啊,可能会对这个底层硬件啊,这个标志位啊啊,或者各个外设的了解呢,可能不是那么深入啊, l 口这个外部中断还比较简单, 对吧?你到了窗口 adc, 定时器, spi, 尤其是 iphonec 这个东西啊啊,各个中段啊,非常多啊,各个标志位也非常多啊,还有逻辑处理,谁在前谁在后啊。所以这个时候啊,我们用配角库啊,他做好的这个中段处理函数啊,那就我们就比较省心了 啊,就防止我们新同学啊,可能是对这个外设了解的还不够深入啊,对各标志位了解不够深入,对吧, 如果贸然自己去写的话,可能会埋下一些相关的一些隐患,那你就用它现成的来去回调函数中,你去处理你的应用程序的逻辑任务就可以了啊。所以有这么一个特点和区别点啊,大家先做一个了解 好,然后呢,我们去看黑奥库啊,嗯,各个中段的回调函数啊,它都是加了一个 weak 的一个弱声名啊,相当于替身演员。好,我们就以 l 口的外部中断啊,我们来举个例子。好,我们先打开 头文件吧,哎,从头文件看,这个函数声明列表啊,就比较清晰啊,好,我们找 g p l 口的。 好,我们看到最后啊,那么一般啊,对,各个外设的这个头文件啊,最后的这些函数,那就是我们的中段啊,还有我们的 core back 加 core back 呢,就是这种回调函数啊,好,我们可以来跳转一下, 你看他做好的这个 l 口的外部中断在干啥?首先呢是判断标志位,然后呢去清楚标志位, 那如果说啊,我们自己来写的话,那也也是这个思路,对吧?哎,所以说 hell 库呢,并不是说他有什么特殊的操作啊,只是说他帮我们去完成了这些, 呃,工作,让我们省点心。好,然后你看调用了什么呢?调用了 crow back, 呃,加 crow back 就是回调好,我们在这里面啊,去做相关的一些,我们先编一下啊,好,我们跳转一下啊,直接跳到下面了,对吧?哎,就是这样,你看他加了一个胃口 啊,那么胃口啊,我们怎么说呢?打比方就相当于是替身演员。好,那么这个时候呢,我们还没有去调用啊,就是我们自己去做这个扣外科,所以这个时候就相当于演员本人他没来,那么这个时候在 编译程序的时候啊,如果说你没有这个替身,你缺少这个函数,你说你这肯定编译不过去,对吧?所以他加了一个胃口,有个弱声名。好,那么这个时候啊,就如果说我们现在用它了, 那你说那我可不可以在这里面直接添加我们想要处理的程序,没有问题,我是可以的,对吧?因为他一调用,哎,你在里面添加了,比如说控制 pp 零, ok 啊, 但是呢,这个黑药库啊,他加这个胃口的最终目的啊,就是希望呢,我们如果说用到他的时候啊,不去破坏或者是更改黑药库的原码, 对吧?这个框架咱们不要改好,那么这个时候你看我们怎么办?我们去做一个类似于这种呃呃,引号的啊,强声名。好,什么是强声名啊?你就把这个位可去了,参数不变,函数名称不变。好,那么这会我们再来演示一下,你看我 现在跳转是跳到替身这了。好,你看啊,我演员本人来了,函数名称参数一模一样。好,这个时候我再来编译一下,嗯,点错了啊,好,编译。 好,那么这个时候我们再观察啊,我再去跳转,哎,就跳到了我们自己啊,做的这个强声明的一个函数,就相当于此时我要用了, 哎,我把演员本人请来了,那么这时候替身呢,你就不用上台了。好,我们就在我们自己做的强声明,也就是演员本人的这个 这个回调函数当中,比如我们去控制 p b 零就 ok 了啊,所以说各个外设啊,不光是 l 口,他都是这样的啊,你用哪个扣外壳,你就去做这么一个强声明,把演员本 人请来,你在里面添加你想要处理的任务啊,就可以了。好,那么今天这一节呢,我们是做一个支点的补充啊,我们下一节就进入到外部中断啊,按键检测。

疫情那年, stm 三二芯片暴涨几十倍,很多公司要紧急换芯片,结果发现换不了代码和芯片绑死了,改个底层,上面全崩。 我在霍尼韦尔的时候也遇到过芯片停产,但产品还要继续卖,这就是为什么 c 语言需要面向对象。 大家好,我是赵明,三家世界五百强,十一年一线潜入式,今天讲一个百分之九十九的潜入式教程,不会教你的东西。 c 语言面向对象和工程项目里的分层架构。大部分人写 c 代码是这样的, 应用逻辑里直接调号库,函数名里写死了 stm 三二。 jpi 用的是 stm 三二的 api, 串口用的也是 stm 三二的 api, 整个代码从上到下全部和一颗芯片绑在一起,能跑吗?能跑, 但是负责人说,换个芯片,你看看你得改多少地方,从底层改到上层,从驱动改到业务逻辑。 这不叫软件开发,这叫重写。怎么解决?加一层。一九七二年,计算机科学家帕纳斯发表一篇经典研究文章,模块的划分 应该按什么最可能变化来决定,芯片会停产、会涨价,这就是最可能变化的东西。所以我们加一层 plant form 层,把它隔离起来,它长这样。 左边是接口,一个结构体,里面是函数指数, open, read, write, close, ctrl, 右边是具体实现 stm 三二的 u、 r 的 驱动,填充这些函数指数。 你的应用代码只掉左边的接口底下是 stm 三二还是 d? d 三二还是别的芯片,他不关心换芯片,只写一套新的实现,上面所有的代码一行不动。 这个结构体里的函数指数就是 c 元的虚函数表,这就是面向对象,不需要 cr 加纯 c 就 能做到, 但光隔离芯片还不够。你想电机有补紧电机、直流电机、无刷电机,显示屏有 lcd oled 墨水屏,图像采集头有 ov 系列 mic 接口, 存储器有 flash 一 方 prm、 sd 卡。每一种都有不同的型号,不同的厂家。所以在 plantform 层之上还有一个 device 层、驱动层。 每一种硬件设备都是一个单独驱动模块,用面向对象来设计、积累、定义、统一接口,不同型号的设备各自实现。换一个电机,换一个屏幕,只需要写一个新的派生类上层应用,一行代码不动。 而且这些驱动全部是非阻测的,中断 dma 回调, cpu 从不空转。这套东西你听着熟不熟悉? iphonec 总线、 spi 总线、 u r 的 串口,我的单片机驱动和 linux 内核里的驱动函数名都能对得上, 不只是一种总线,每一种总线,每一个子系统全是这个模式。我的单片机代码和 linux 内核用的是同一套设计思想。 很多人觉得 linux 内核很复杂,驱动开发很难,其实不是。当你理解了这套面向对象的设计思想,封装、继承、多态在 c 语言里怎么落地,你再去看 linux 内核驱动的代码,跟看小说一样, 这套东西不是理论,是我六套系统每天在跑的架构。这套分层和面向对象的思想学的不是某一个芯片的用法,是设计方法。 方法学对了,比多写一万行代码都有用,它是可以迁移的,你换芯片能带走,换平台能带走,从单片机走到 linux 也能带走。后面我会教大家如何设计,如何分析?如何一步步落地到自己的项目里? 这些内容市面上几乎没人讲。如果你不想只停留在开发版 demo 阶段,关注嵌入式照明,点个赞、收个藏,别刷走了,后面找不着评论区,告诉我你的项目代码有分层设计吗?