分类目录:摩登3官网注册

摩登3注册平台官网_FreeRTOS的入门材料

嵌入式系统不只是ARM+Linux,不是只有安卓,凡是电子产品都可称为嵌入式系统。物联网行业的兴起,也提升了FreeRTOS市场占有率。本文就是介绍FreeRTOS基础及其应用,只是个人整理,可能存在问题,其目的只是简要介绍系统的基础,只能作为入门资料。 目录 一、 为什么要学习RTOS  二、 操作系统基础 三、 初识 FreeRTOS 四、 任务 五、 队列  六、 软件定时器 七、 信号量 八、 事件  九、 任务通知 十、 内存管理 十一、 通用接口 一、 为什么要学习 RTOS 进入嵌入式这个领域,入门首先接触的是单片机编程,尤其是C51 单片机来,基础的单片机编程通常都是指裸机编程,即不加入任何 RTOS(Real Time Operating System 实时操作系统)。常用的有国外的FreeRTOS、μC/OS、RTX 和国内的 RT-thread、Huawei LiteOS 和 AliOS-Things 等,其中开源且免费的 FreeRTOS 的市场占有率较高。 1.1 前后台系统 在裸机系统中,所有的操作都是在一个无限的大循环里面实现,支持中断检测。外部中断紧急事件在中断里面标记或者响应,中断服务称为前台,main 函数里面的while(1)无限循环称为后台,按顺序处理业务功能,以及中断标记的可执行的事件。小型的电子产品用的都是裸机系统,而且也能够满足需求。 1.2 多任务系统 多任务系统的事件响应也是在中断中完成的,但是事件的处理是在任务中完成的。如果事件对应的任务的优先级足够高,中断对应的事件会立刻执行。相比前后台系统,多任务系统的实时性又被提高了。 在多任务系统中,根据程序的功能,把这个程序主体分割成一个个独立的,无限循环且不能返回的子程序,称之为任务。每个任务都是独立的,互不干扰的,且具备自身的优先级,它由操作系统调度管理。加入操作系统后,开发人员不需要关注每个功能模块之间的冲突,重心放在子程序的实现。缺点是整个系统随之带来的额外RAM开销,但对目前的单片机的来影响不大。 1.3 学习RTOS的意义 学习 RTOS,一是项目需要,随着产品要实现的功能越来越多,单纯的裸机系统已经不能完美地解决问题,反而会使编程变得更加复杂,如果想降低编程的难度,就必须引入 RTOS实现多任务管理。二是技能需要,掌握操作系统,和基于RTOS的编程,实现更好的职业规划,对个人发展尤其是钱途是必不可少的。 以前一直觉得学操作系统就必须是linux,实际每个系统都有其应用场景,对于物联网行业,杀鸡焉用牛刀,小而美,且应用广泛的FreeRTOS 是首选。有一个操作系统的基础,即使后续基于其他系统开发软件,也可触类旁通,对新技术快速入门。目前接触的几款芯片都是基于FreeRTOS。 如何学习RTOS?最简单的就是在别人移植好的系统之上,看看 RTOS 里面的 API 使用说明,然后调用这些 API 实现自己想要的功能即可。完全不用关心底层的移植,这是最简单快速的入门方法。这种学习方式,如果是做产品,可以快速的实现功能,弊端是当程序出现问题的时候,如果对RTOS不够了解,会导致调试困难,无从下手。 各种RTOS内核实现方式都差不多,我们只需要深入学习其中一款就行。万变不离其宗,正如掌握了C51基础,后续换其他型号或者更高级的ARM单片机,在原理和方法上,都是有借鉴意义,可以比较快的熟悉并掌握新单片机的使用。 二、 操作系统基础 2.1 链表 链表作为 C 语言中一种基础的数据结构,在平时写程序的时候用的并不多,但在操作系统里面使用的非常多。FreeRTOS 中存在着大量的基础数据结构链表和链表项的操作(list 和 list item)。FreeRTOS 中与链表相关的操作均在 list.h 和 list.c 这两个文件中实现。 链表比数组,最大优势是占用的内存空间可以随着需求扩大或缩小,动态调整。实际FreeRTOS中各种任务的记录都是依靠链表动态管理,具体的可以参考源码的任务控制块tskTCB。任务切换状态,就是将对应的链表进行操作,链表操作涉及创建和插入、删除和查找。 2.2 队列 队列是一种只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。队尾放入数据,对头挤出。先进先出,称为FIFO。 2.3 任务 在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。在多任务系统中,根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。系统中的每一任务都有多种运行状态。系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。  就绪(Ready):该任务在就绪列表中,就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。   运行(Running):该状态表明任务正在执行,此时它占用处理器,调度器选择运行的永远是处于最高优先级的就绪态任务。   阻塞(Blocked):任务当前正在等待某个事件,比如信号量或外部中断。   挂起态(Suspended):处于挂起态的任务对调度器而言是不可见的。 挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到调用恢复任务的 接口;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。 各任务运行时使用消息、信号量等方式进行通信,不能是全局变量。任务通常会运行在一个死循环中,不会退出,如果不再需要,可以调用删除任务。 2.4 临界区 临界区就是一段在执行的时候不能被中断的代码段。在多任务操作系统里面,对全局变量的操作不能被打断,不能执行到一半就被其他任务再次操作。一般被打断,原因就是系统调度或外部中断。对临界区的保护控制,归根到底就是对系统中断的使能控制。在使用临界区时,关闭中断响应,对部分优先级的中断进行屏蔽,因此临界区不允许运行时间过长。为了对临界区进行控制,就需要使用信号量通信,实现同步或互斥操作。 三、 初识 FreeRTOS 3.1 FreeRTOS源码 FreeRTOS 由美国的 Richard Barry 于 2003 年发布, 2018…

摩登3平台首页_5G网络下,打电话VoNR和VoLTE有什么区别?

打电话,是每个人最原始的需求,也是移动通信最初的目标。 目前,以微信为代表的各种OTT(Over The Top)语音非常流行。但是,仍然无法取代传统语音电话业务。 传统语音电话业务,作为最基础的通信服务,拥有最高的优先级。在关键时刻,它是我们的救命稻草。 在网络信号不好的时候,上网龟速,微信语音卡成狗,视频根本无法接通。但是,电话肯定是可以打通的,虽然音质可能不好,但可以满足基本需求。这就是基础服务保障的承诺。 当遇到紧急情况时,不管你的手机有没有信号,甚至连SIM卡都没插,照样能打通紧急呼叫电话。这就是传统语音电话业务的优势。 5G,作为最先进的移动通信网络,是如何实现语音业务的呢? 最根本的方式是:自己动手,丰衣足食。也就是说,5G直接支持VoNR(Voice over New Radio),不看4G甚至3G和2G的脸色。 5G的网络架构其实承袭自4G,只支持分组交换,不支持电路交换,也就是说自身的5GC核心网是没法支撑语音业务的,必须依赖于一个叫做IMS的系统。 IMS又叫IP多媒体子系统,可以在分组交换网络下实现语音业务。5G的无线接入部分叫做NR(New Radio),跟IMS结合之后,独立打电话的问题完美解决。因此基于5G的语音业务就叫做VoNR (Voice over NR)。 这一点跟4G如出一辙,4G在IMS支持下的语音业务就叫VoLTE(Voice over LTE)。VoLTE目前已经在国内广泛支持。 如果5G不支持VoNR,那就只能靠4G的VoLTE,甚至3G和2G支持的电路交换域语音业务,进行兜底。 根据网络部署模式,5G可分为NSA(非独立组网)和SA(独立组网)两类。再根据5G是否支持VoNR,以及4G是否支持VoLTE,分为以下多种方案。 NSA下的语音业务: 在NSA下,5G网络被称作辅节点,作为4G的流量补充,并不直接参与语音业务,所有语音功能完全由4G完成,因此5G就都不支持VoNR。 如果4G支持VoLTE功能,则直接进行语音,覆盖不好的时候通过SRVCC(Single Radio Voice Call Continuity,单无线语音呼叫连续性)切换到3G或者2G。 如果4G不支持VoLTE,在拨打电话的时候就会直接回落到3G或者2G(这个功能称作CS Fallback,电路交换回落)。 SA下的语音业务: 在SA模式下,5G语音方案比较复杂,有四种场景。总体思路是,5G网络优先使用VoNR,如不支持,则回落到到4G的VoLTE,最后由3G或者2G进行兜底。 场景1:5G网络支持语音功能(VoNR),则可直接在5G上接通电话,然后在5G信号不好的时候切换到4G的VoLTE。如果用户跑到了4G覆盖不好的地方,还可以通过SRVCC切换到3G或者2G。 场景2:5G网络支持VoNR,则可直接在5G上接通电话,在5G信号不好的时候发现4G信号也不好,直接由5G通过SRVCC把电话切换到3G。 5G到3G的SRVCC刚刚在3GPP R16版本中标准化,目前还没有手机支持。 既然从5G能切换到3G,未来也会支持切到2G吧?实际上没有那个必要,因为一般情况下3G已经覆盖够好,足够用来兜底了,再说2G也没几年就要退网了,不值得再花钱投资。 场景3:5G网络不支持VoNR,则在打电话的时候先通过EPSFB(EPS Fallback)来回落到4G的VoLTE,在4G覆盖不好的时候再通过SRVCC切换到3G或者2G。 场景4:5G网络不支持VoNR,则在打电话的时候先通过EPSFB来回落到4G,结果很不幸,4G也不支持VoLTE,只能再次通过CSFB回落到3G或者2G来打电话了。 可以看出,在这几个场景中,手机打着打着电话,很可能从5G跑到了4G,甚至还很可能从4G再跑到3G或者2G。就打完电话之后,还要继续留在4G,甚至3G或者2G吗? 由俭入奢易,由奢入俭难。习惯了5G/4G的高速率,对于3G和2G的龟速是不可接受的,因此需要尽快让手机返回能力最强的网络,这个过程就叫做快速返回。 同样是基于IMS的语音业务,VoNR和VoLTE相比到底有什么优势呢? 首先,当手机驻扎在5G小区时,使用VoNR简单直接,否则还要经过EPS Fallback回落到4G,信令流程增加了,时延也必然增加,影响用户体验。 然后,VoNR下强制支持一种新的语音编解码方案,可以有效提升语音通话的音质到HiFi的级别,这就是EVS(Enhanced Voice Services),也叫超高分辨率语音(Super HD Voice)。 其实EVS早在3GPP R12版本就已经定义了,彼时还是LTE的发展正如日中天,但由于大家对语音质量都不够重视,一直少有手机支持。这一拖,就到了5G时代。 EVS是怎么提升音质的呢? 声音是由振动产生的,在空气中传播就形成了声波。但人的耳朵只能听到有限一段频率内的声波,范围是20Hz到20000Hz。 人的声带能发出的频率范围要更窄一些,为85Hz到1100Hz。 在以前的语音编解码方案中,只包含了人的听觉频率范围中的一小段,有些甚至连人的发声带宽都没有完全编码。 比如最早的标准语音编码的频率范围是300Hz到3400Hz,而人的发声频率范围是85Hz到1100Hz,也就是说,从85Hz到300Hz这一段的声音根本就没有被传输。 这种窄带编码导致了音色的损失。最直观的感受是,在打电话时,虽然对方说的语句是能辨认的,含义也能听明白,但却经常分辨不出谁在说话,像被变声了一样。 EVS直接实现了人的听觉范围全带宽的编码,除了人的声音之外,连背景里汪星人和喵星人的叫声也真真切切,可媲美CD的音质。 总而言之,我们的5G语音,已经默默做到了最好。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3登录网站_访谈:同一个技术点,还有再写的必要么?

前段时间,一位朋友问了小林一些问题,类似访谈式的提问,主要是关于写文章之类的事情,我觉得对一些读者会有帮助,所以重新整理了下。 1、为什么你们这么肝,你们是怎么坚持下来的,并且越做越好。 我感觉我还好,只是把以前周末看电影或者无所事事的时间拿来写文了,公司基本双休,时间还算比较充裕,但是相比 996 还能保持高频率更新的其他号主,还差的远了,惭愧惭愧。 对于万字长文 + 多图的文章,我一般花的时间比较长,可能得 1~2 星期,几千字的文章可能也需要一个周末的时间。 学习本身就是个反人性的事情,再加上写文章,那更是 N 倍痛苦。上班都在搞技术,下班了还要来搞技术文章,简直是自己折磨自己,所以如果自己花了那么多心思写的文章,没有得到有效的正反馈,能坚持下来才怪,即使真有人坚持下来了,那他一个是个恨人。 我之所以能坚持到现在,很大一部分原因是关注的读者越来越多, 从而形成了更大的动力,就会感觉每一位读者都在 push 我前进。 2、为什么我总是写了一段时间就写不下去了,文章没什么人看,也不会宣传,怎么克服这种心态? 你这心态太正常了,写文章肯定需要正反馈的,写文章本身就是个费时费力的事情,辛辛苦苦写好的文章,却得来几个阅读,换做谁都很难坚持。 在工作我们可能只需要把眼前的代码写好,其余业务上的发展与宣传都不需要我们操心,毕竟公司有其他团队负责这一块的事情,但是对于我们自己写公众号的,那就不一样了,我们不仅要学会如何产出好的内容,还要学会如何宣传自己的内容,做事情的方向不再是单一的,而是需要有多元化的技能。 其实,我觉得技术文涨粉是不难的事情,因为有太多号主有转载文章的需求了,只要把文章写好,然后去找他们投稿转载就可以了。 像我刚开始写 C++,也是没什么人看,反思一番后,同时观察哪些文章比较容易受到转载,于是就开始转到写图解计算机基础,然后非常不要脸的疯狂投稿,当然并不是每次都顺利,但至少有一个号主转载我就赚了,被转载多了,读者就慢慢起来了,不过说实话,我每一篇文章都很用心,真的用图和大白话的方式做到通熟易懂,所以我文章被转载的机会也多,读者关注的转化率也很不错。 多去反思,多去学习别人,多去尝试,用行动敲碎自己的痛点。 3、同样的一个技术点,别人已经写的烂大街了,还有再写的必要么? 你看我写的文章,是已经基础不能在基础的文章,网上随便一搜都大把多相关的文章,但是能把烂大街的东西写好并不是简单事情,比如我是以图解的方式,把那些晦涩难懂的基础知识图解明白了,文章用心了,读者是能看出来的,自然他们也会因你的认真而关注过来。 不怕大家笑,小林我也是非科班的,很多知识都没有系统看过书,于是我就通过边输入边输出,所以我写的文章都是小白的视角,因为我在重新系统学习的时候,会碰到让人疑惑的地方,我都会搜索很多资料把这个疑惑弄清楚来,因为我知道我把自己的遗憾解决了,自然就等于把很多人的疑惑也解决了,因为我既是读者,也是一个写者。 4、每天,每周花在写作上的时间是多少,平时是如何平衡工作和写作的? 我工作效率还算比较高,周末很少会加班,本身也比较宅,所以就把周末的时间都会拿来写文章了。 不过,除了周末写文章的时间之外,工作下班的空余时间,我都会用来看资料了,因为如果不保证知识的输入,肚子里的墨水再多,也会有被掏空的一天。 因此可以说,除了上班的时间,我不是在写文章,就是看书的路上,就是这么折腾。 5、平时是如何做选题的?大量输入必然有大量输出,平时主要会看哪些信息源,写一篇文章大概需要花多少时间? 我的写作方向也比较明确,就是写计算机基础类的文章,比如计算机网络、操作系统、计算机组成、数据库、算法等等,而且还会结合大厂爱问的知识点以及工作中的经验。 我的学习渠道主要是书籍 + 专栏 + B站视频 + 博客,多图长文一般会 1~2 星期,几千字的文也需要一个周末,码字不易啊,画图更不易啊。 6、写完一篇文章自己能记得住多少?会定时看以前写的文章么? 说实话,全记住是不可能的,但是至少你有个很深刻的一个影响,如果工作遇到类似的问题,我会翻一番以前的文章。 写过的文章就像在你脑海里建立过了一个索引,遇到问题了,就可以快速去回顾旧文,回顾之后,知识点回想起来会很快的,而不至于一脸茫然。 其实我觉得文章就是一个保存自己当时所知道的知识点的一种形式,就像照片保存了某一瞬间的画面,回顾的时候,会特别有感觉的,但是照片不能再次修改,而文章可以继续修改的,如果某天你对这个知识点有新的见解和认识,可以「升级」以前的文章,这个过程还是蛮有趣的,自己迭代自己的知识。 7、坚持写作给你带来最大的收获是什么? 收获真的蛮多的,不仅是对技术有了更深刻的认识,还有一个重要的点,通过写文的方式认识了很多优秀的前辈和后浪,现在有一种「四海皆朋友」的感觉,这在以前是不可能的,毕竟工作的圈子就这么丁点儿大,今年 8 月份,也有幸参加了大佬们的线下交流会,我旧文也提及过「小林才写作半年,竟混进年入百万千万的圈子?」。 另外,由于一些博客平台时常会有一些活动,所以通过写文章也获得不少实质性的奖品,比如水杯、靠枕、雨伞、书包、书等等,特别是对于书,很少要自己掏钱包了,时常会有出版社免费给小林送书,现在家里的书都堆积如山了,日后考虑把这些书送给读者们。 当然,最让我意想不到的是,写文章这件事儿,还给我带来了财富收益,这是件很酷的事情,给自己生活带来了除工作之外的收入,俗称副业,虽然这点小钱谈不上大富大贵,但是已经能抵掉了生活上的日常开销了。 而且,就在最近小林也靠这笔钱,买了梦寐以求的苹果电脑(虽然是二手的),还重新翻新了自己的「生产力」环境,把桌子、键盘、鼠标等都换了,相比以前看上去舒服多了,也更有干劲了。 说些喜事,秋招也差不多告一段落了,最近不少斩获大厂 offer 的读者纷纷私聊感谢我,说我的文章对他们秋招起到了很大的帮助,小林为此也很高兴,也为他们感到开心,没想到写文章成就了自己也成就了别人。当然,我的文章给予他们的帮助可以说是微乎其微的,更关键的是他们自身的努力。 – – 快年底了,工作和生活上的事情越来越多了,忙里忙外,这周的技术文很抱歉拖更啦,不过没关系,小林已经写了七七八八了,为了证明小林没有偷懒,先给大家放个图。 也打个小预告,下篇文章相当硬核,通过 Linux 内核的角度来再次理解进程和线程,待小林周末再完善和补一些图,下周就发出来,我们下次见! 读者问:小林你能分享做公众号的经验吗? 读者问:小林你的 500 张图是怎么画的? 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3新闻554258:_深度解析焦耳小偷电路,如何榨干每一节废旧干电池

常见一些电子类的发烧友DIY的焦耳小偷电路。 这样的 这样的 还有这样的 焦耳小偷电路是一个简约的自激振荡升压电路,只需三个元件:三极管、电阻、电感即可实现升压,成本低、易制作。 它可以榨干一节废旧干电池上的所有能量,即使是那些在其它电路中已经被认为没电的电池。在制作焦耳小偷电路时,一定要注意两个电感的方向相反。通常1.5V的干电池用完之后还会有1.1V左右的电压,说明此时电池内还有能量,只不过内阻变的很大,输出电流很微弱,已经无法驱动一般的电路,更无法点亮LED。而焦耳小偷电路可以通过磁感线圈产生高频脉冲电压,使LED导通,通过调整合适的参数,可以将电池电压升高10-100倍以上。 下面这个对焦耳小偷电路的解析通俗易懂。 焦耳小偷全解释 点亮一个LED: 我们知道通常LED工作电压在1.7~3V,也就是说,要点亮LED我们需要一个高于1.7V的电压。 那么最简单点亮LED的办法就是--如图: 二个电池叠加电压高于1.7V就能点亮LED。这是一个极简单的工作。 现在我们来看下图: 在这里,我们将一个电感替代了一个电池,加了一个开关。 这时LED是无法点亮的,因为其电压只有一个电池供电为1.5V。 当我们按下开关时,电池仅向电感供电,电流在电感上形成磁场。 这一过程我们且称之为电池对电感冲能。 放开开关时,由电池叠加电感上的电压对LED放电,这是电压就高于1.7V,因而点亮LED。 在这里电感充当了一个电池的作用,和普通电池不同的是,电感的能量是依赖电池。 需要电池不断给电感充电,然后再对外释放。 我们不可能一直不断的按动那个开关,另外让依赖我们手动,其工作频率也很低。 那么LED一闪就灭,甚至很难被我们观察到LED在闪亮。 这时我们就采取了一个三极管作为自动开关,来替代我们手动的开关。 电路边演化为: 现在我们只要给三极管基极一个信号,就能控制三极管导通还是截至。 只需周期性的给基极信号,那么三极管就充任了自动开关的角色。 能完成将电池负载不断的从电感和LED之间转换。 当电感成为负载时,电池对电感冲能,(三极管导通状态),当LED成为负载时,(三极管截至)电感释放能量。 再看下图: 这里我们再加上一组反馈线圈,以便向三极管提供触发信号。 当电感冲能时电感上存在电流,那么感应线圈就能为三极管提供触发信号,使得三极管导通。 当电感冲能完毕,在电感上形成磁场,同时也产生一个感应电动势。该电动势会阻止电流在电感上流过。 这是感应线圈上缺乏足够感应电流,无法维持三极管导通,此时三极管截至。 就着样,三极管配合电感形成导通-截至-导通-截至不断循环。 就相当于以上说明中那个开关,不断通断。 那么最后,我们还得为三极管加上保护,以避免三极管基极被击穿。这样就形成了焦耳小偷的电路: 现在我们应该明白焦耳小偷的一般性常识了,由此也知道在制作焦耳小偷时各个元件都担任什么作用。 那么也明白只要是三极管,都能用于制作焦耳小偷,只要这个三极管还存在截至能导通的能力。 放大倍数,工作频率这些都能忽略。 只要能提供信号能维持三极管进行导通和截至的工作,即便是可控硅,达林顿复合管之类也能胜任。 这里需要注意的是: 1、电感需要高的磁导率,因为电感对外提供能量,完全依赖它存储的磁能转化为电能。由此知道,该电感在通电时所能存储磁能越大,那么提供的能量也越高。 2、焦耳小偷对外提供的是脉冲直电流,并非交变电流。 3、任何电子电路都要消耗电能,而焦耳小偷消耗的仅仅是在电感上的略微损失和开启三极管导通的些许能量。 这也是焦耳小偷的神奇之处,如果我们制作一个单管自激振荡,形成交变电流,再由变压器升压。 同样能提升电压,但是这个过程中负担电子电路所消耗的能量要比焦耳小偷大的多。 那么到此焦耳小偷的概念应该都说明了,剩下的是题外话。 正如我们看到的第一张图,如果我们有足够的电池,那么就不需要什么焦耳小偷了。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3注册网址_台积电美国建厂获批,美亚利桑那州将提供2.05亿美元资金支持

据路透社报道,亚利桑那州凤凰城的城市官员当地时间周三一致投票批准与台积电的发展协议,该协议将提供2.05亿美元的城市资金用于基础设施建设。 台积电在五月份透露了其在亚利桑那州建立5纳米芯片工厂的计划,这将是其在美国的第一家先进制造工厂。本月初,台积电批准了在美国设立全资子公司。 据了解,凤凰城议会以9票对0票的投票结果允许该市加入该协议。在投票前,凤凰城市长Kate Gallego称该协议“帮助亚利桑那州成为了先进制造业的领导者。” 根据该协议,台积电将建立一个新工厂,创造1900个新的全职工作,并将在五年内分阶段进行。建设将于2021年初开始,预计于2024年开始生产。 作为回报,凤凰城将以6100万美元的价格建造三英里的街道,花费3700万美元用于改善水利基础设施,花费1.07亿美元用于废水改善。该公司将在选择地点之后与该市达成正式协议,该地点有望在今年年底之前决定。 21ic家注意到,目前,台积电拥有全球最先进的5nm生产能力,同时在积极推进下一代工艺进程,计划2022年下半年量产3nm工艺,2023年下半年试产2nm工艺。 -END- | 整理文章为传播相关技术,版权归原作者所有 |  | 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3平台注册登录_千亿项目武汉弘芯烂尾后续:CEO蒋尚义已辞职,地方政府接管

近日,总投资额高达1280亿元的武汉弘芯半导体制造项目,已被当地政府接管。11月17日,蒋尚义通过律师发表声明,宣称已于今年6月因个人原因辞去武汉弘芯董事长、总经理等一切职务,且武汉弘芯已接受了蒋尚义递降的辞呈。 不过值得注意的是,7月8日在武汉弘芯官网发布的新闻稿中,当天下午,武汉弘芯举办了一场员工表彰大会,文中还称“公司李雪艳董事长、蒋尚义总经理兼首席执行官出席会议并为坚守岗位的11名员工现场颁奖。” 据资料显示,11月10日,原本北京光量蓝图科技有限公司持有武汉弘芯90%的股权,现如今已被武汉光量蓝图科技有限公司全资收购,且武汉光量蓝图科技有限公司为武汉市东西湖区国有资产监管局100%持股;剩余10%股权仍在武汉临空港经济技术开发区工业发展投资集团有限公司,其系武汉市东西湖区国有资产监管局旗下单位。 至此,武汉弘芯已被武汉市东西湖区政府全资接管。 此前由于资金链断裂,武汉弘芯目前已经卷入了3起官司,其中和上海英格索压缩机有限公司的官司已经通过调解方式结案,目前仍有两起诉讼尚未结案。 发改委曾回应个别芯片项目烂尾:造成重大损失将问责。 武汉弘芯的烂尾事件给国芯产业泼了一盆冷水,如今国家大力发展半导体芯片产业,急功近利是不行的,科学合理的发展才是长远之路! -END- 来源 | 镁客网 | 整理文章为传播相关技术,版权归原作者所有 |  | 【1】华为晒凤凰引擎:或为自研GPU做准备! 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3新闻554258:_PCB线路设计制作百句术语大全!

作为一个电子工程师设计电路是一项必备的硬功夫,但是原理设计再完美,如果电路板设计不合理,性能将大打折扣,严重时甚至不能正常工作。下面小编为大家整理了104条PCB线路设计制作术语合集,希望能提升你的工作效率! 1、Annular Ring 孔环 指绕接通孔壁外平贴在板面上的铜环而言。在内层板上此孔环常以十字桥与外面大地相连,且更常当成线路的端点或过站。在外层板上除了当成线路的过站之外,也可当成零件脚插焊用的焊垫。与此字同义的尚有 Pad(配圈)、 Land (独立点)等。 2、Artwork 底片在电路板工业中,此字常指的是黑白底片而言。至于棕色的“偶氮片”(Diazo Film)则另用 Phototool 以名之。PCB 所用的底片可分为“原始底片”Master Artwork 以及翻照后的“工作底片”Working Artwork 等。 3、Basic Grid 基本方格指电路板在设计时,其导体布局定位所着落的纵横格子。早期的格距为 100 mil,目前由于细线密线的盛行,基本格距已再缩小到 50 mil。 4、Blind Via Hole 盲导孔指复杂的多层板中,部份导通孔因只需某几层之互连,故刻意不完全钻透,若其中有一孔口是连接在外层板的孔环上,这种如杯状死胡同的特殊孔,称之为“盲孔”(Blind Hole)。 5、Block Diagram 电路系统块图将组装板及所需的各种零组件,在设计图上以正方形或长方形的空框加以框出, 且用各种电性符号,对其各框的关系逐一联络,使组成有系统的架构图。 6、Bomb Sight 弹标原指轰炸机投弹的瞄准幕。PCB 在底片制作时,为对准起见也在各角落设置这种上下两层对准用的靶标,其更精确之正式名称应叫做Photographers’ Target。 7、Break-away panel 可断开板指许多面积较小的电路板,为了在下游装配线上的插件、放件、焊接等作业的方便起见,在 PCB 制程中,特将之并合在一个大板上,以进行各种加工。完工时再以跳刀方式,在各独立小板之间进行局部切外形(Routing)断开,但却保留足够强度的数枚“连片”(Tie Bar 或Break-away Tab),且在连片与板边间再连钻几个小孔;或上下各切 V 形槽口,以利组装制程完毕后,还能将各板折断分开。这种小板子联合组装方式,将来会愈来愈多,IC卡即是一例。 8、Buried Via Hole 埋导孔指多层板之局部导通孔,当其埋在多层板内部层间成为“内通孔”,且未与外层板“连通”者,称为埋导孔或简称埋孔。 9、Bus Bar 汇电杆多指电镀槽上的阴极或阳极杆本身,或其连接之电缆而言。另在“制程中”的电路板,其金手指外缘接近板边处,原有一条连通用的导线(镀金操作时须被遮盖),再另以一小窄片(皆为节省金量故需尽量减小其面积)与各手指相连,此种导电用的连线亦称 Bus Bar。而在各单独手指与 Bus Bar 相连之小片则称Shooting Bar。在板子完成切外形时,二者都会一并切掉。 10、CAD电脑辅助设计Computer Aided Design,是利用特殊软体及硬体,对电路板以数位化进行布局(Layout),并以光学绘图机将数位资料转制成原始底片。此种 CAD对电路板的制前工程,远比人工方式更为精确及方便。 11、Center-to-Center Spacing 中心间距指板面上任何两导体其中心到中心的标示距离(Nominal Distance)而言。若连续排列的各导体,而各自宽度及间距又都相同时(如金手指的排列),则此“中心到中心的间距”又称为节距(Pitch)。 12、Clearance 余地、余隙、空环指多层板之各内层上,若不欲其导体面与通孔之孔壁连通时,则可将通孔周围的铜箔蚀掉而形成空环,特称为“空环”。又外层板面上所印的绿漆与各孔环之间的距离也称为 Clearance 。不过由于目前板面线路密度愈渐提高,使得这种绿漆原有的余地也被紧逼到几近于无了。 13、Component Hole 零件孔指板子上零件脚插装的通孔,这种脚孔的孔径平均在 40 mil 左右。现在SMT盛行之后,大孔径的插孔已逐渐减少,只剩下少数连接器的金针孔还需要插焊,其余多数 SMD 零件都已改采表面粘装了。 14、Component Side 组件面早期在电路板全采通孔插装的时代,零件一定是要装在板子的正面,故又称其正面为“组件面”。板子的反面因只供波焊的锡波通过,故又称为“焊锡面”(Soldering Side) 。目前 SMT 的板类两面都要粘装零件,故已无所谓“组件面“或“焊锡面”了,只能称为正面或反面。通常正面会印有该电子机器的制造厂商名称,而电路板制造厂的 UL 代字与生产日期,则可加在板子的反面。 15、Conductor Spacing 导体间距指电路板面的某一导体,自其边缘到另一最近导体的边缘,其间所涵盖绝缘底材面的跨距,即谓之导体间距,或俗称为间距。又,Conductor 是电路板上各种形式金属导体的泛称。 16、Contact Area 接触电阻在电路板上是专指金手指与连接器之接触点,当电流通过时所呈现的电阻之谓。为了减少金属表面氧化物的生成,通常阳性的金手指部份,及连接器的阴性卡夹子皆需镀以金属,以抑抵其“接载电阻”的发生。其他电器品的插头挤入插座中,或导针与其接座间也都有接触电阻存在。 17、Corner Mark 板角标记电路板底片上,常在四个角落处留下特殊的标记做为板子的实际边界。若将此等标记的内缘连线,即为完工板轮廓外围(Contour)的界线。 18、Counterboring 定深扩孔,埋头孔电路板可用螺丝锁紧固定在机器中,这种匹配的非导通孔(NPTH),其孔口须做可容纳螺帽的“扩孔”,使整个螺丝能沉入埋入板面内,以减少在外表所造成的妨碍。 19、Crosshatching 十字交叉区电路板面上某些大面积导体区,为了与板面及绿漆之间都得到更好的附着力起见,常将感部份铜面转掉,而留下多条纵横交叉的十字线,如网球拍的结构一样,如此将可化解掉大面积铜箔,因热膨胀而存在的浮离危机。其蚀刻所得十字图形称为 Crosshatch,而这种改善的做法则称为 Crosshatching。 20、Countersinking 锥型扩孔,喇叭孔是另一种锁紧用的螺丝孔,多用在木工家俱上,较少出现精密电子工业中。 21、Crossection Area 截面积电路板上线路截面积的大小,会直接影响其载流能力,故设计时即应首先列入见,常将感部份铜面转掉,而留下多条纵横交叉的十字线,如网球拍的结构一样,如此将可化解掉大面积铜箔,因热膨胀而存在的浮离危机。其蚀刻所得十字图形称为 Crosshatch,而这种改善的做法则称为 Crosshatching。 22、Current-Carrying Capability 载流能力指板子上的导线,在指定的情况下能够连续通过最大的电流强度(安培),而尚不致引起电路板在电性及机械性质上的劣化 (Degradation),此最大电流的安培数,即为该线路的“载流能力”。 23、Datum Reference 基准参考在…

摩登3平台登录_规则设置如何应用于我的PCB设计?

       在PCB设计中,Design Rule设计规则是关系到一个PCB设计成败的关键。所有设计师的意图,对于设计的功能体现都通过设计规则这个灵魂来驱动和实现。精巧细致的规则定义可以帮助设计师在PCB布局布线的工作中得心应手,节省工程师的大量精力和时间,帮助设计师实现优秀的设计意图,大大方便设计工作的进行。        在设计数据从原理图阶段转移到PCB设计阶段之后,进行PCB设计布局布线时,就需要提前定义好设计规则Design Rule。后续的整个PCB设计都需要遵守规则定义。包括最基本的电气规则(间距,短路断路),布线规则(线宽,走线风格,过孔样式,扇出等),平面规则(电源地平面层连接方式,铺铜连接方式);以及其他常用的辅助规则如布局规则,制造规则,高速设计规则,信号完整性规则等等。在规则驱动的设计完成之后,还可以进行规则检查Design Rule Check来重新审视您的设计,看看有无违反规则的情况发生并加以改进和完善。最终设计出完全符合规则定义并满足设计意图的优秀作品。        相对于PCB制造相关的设计规则尤其具有现实意义。倘若设计规则设置的不符合PCB工艺制造的要求,将不仅仅是影响产品功能那么简单,甚至会无法加工无法实现工程师的设计意图。因此,在定义设计规则的时候,了解下下游制造方对设计的工艺制造要求是至关重要的。 PCB加工的制造工艺有哪些精度方面的要求?        如下图所示为某家PCB制版生产厂家的工艺要求。包括电路板层数,厚度,孔径,最小线宽线距,铜厚等基本参数要求;也包括板材类型,表面处理,特殊加工等特别要求。一般在PCB加工的时候,分测试用的打样加工,以及最终成型的批量产品加工。对于设计师来说,有实际意义并需要严格遵守的是批量产品加工的工艺要求。        而对于制造精度相关的工艺要求来说,最基本最重要的是线宽线距和最小孔径。也即加工厂能处理最小多细的线宽以及最小多大的孔。如果线宽在设计中没有达到要求,太细的话是无法正确加工出来的。线宽线距精度同样影响到丝印层上的文字图案是否清晰。而孔径太小的话也是没有相应的钻头支持的。最小孔径所对应的钻头尺寸同样影响到机械孔,安装孔等各种类型板形剪切的公差精度。 线宽线距与孔径规则设置注意事项        本文带您了解如何根据PCB生产制造工艺要求,在PCB制造精度方面,设置合乎要求并且满足设计意图的线宽线距与孔径规则. 最小线宽/间距4mil       在PCB设计中,批量加工所能支持的最高精度为线宽线距4mil。即布线宽度必须大于4mil,两条线之间的间距也需要大于4mil。当然只是线宽线距的最低极限值。在实际的工作中线宽需要按照设计需要定义为不同的值。比如电源网络定义宽一些,信号线定义细些。这些不同的需求都可以在规则Design – Rules – Routing – Width 里定义不同网络不同的线宽值,然后根据重要程度设置规则应用优先级。同样,对于线距来说,在规则页面Design – Rules – Electrical – Clearance 里定义不同网络之间的电气安全间距,当然也包括线距。       另外有一种特殊情况。对于高密度管脚的元器件来说,器件内焊盘之间的间距一般很小,比如6mil,虽然满足最小线宽或间距大于4mil的制造方面的要求,但作为设计PCB来说可能不符合规则设计要求。如果整个PCB的最小安全间距设置是8mil,那幺元器件焊盘的间距明显违反了规则设置。在规则检查时或在线编辑时会一直绿色高亮来显示违规。这种违规显然是不需要处理的,我们应该修正规则设置来消除绿色高亮显示。在原来的处理办法中,是用query语言单独为这个器件定义不同的安全间距规则,并设置为高优先级。在新的版本中,只需要简单的勾选选项即可解决这个问题,即忽略封装内的焊盘间距Ignore Pad to Pad clearance within a footprint。如下图所示。        用此选项勾选非常简便。不需要原来那样用Query语句InComponent(‘U1’) ,然后设置其最小安全间距为6mil,并设为最高间距优先级.       2.   最小机械孔径0.2mm(8mil)最小镭射孔径4mil         PCB设计中不可避免要用到钻孔。而在设计规则的设置方面,甚至具体的钻孔操作方面,具体要钻怎样的孔(通孔,盲孔,埋孔,还是背钻孔?)以及钻多大尺寸的孔,您做到心中有数了吗?您会不会看别人钻多大孔自己也钻多大孔,或者随便填写个尺寸以满足板面布局和走线的方便程度?       孔的类型如下图所示。一般不太复杂的设计,板层叠层不多的设计中通常用到通孔。在复杂的设计中,特别是多层板,高速高密设计,PCB布线空间要求很高的情况下,可根据实际需要设置盲孔或埋孔。当然盲埋孔因为制造工艺上比通孔复杂,制造成本会相应增高。 机械钻孔与镭射钻孔的区别       首先了解下钻孔的过程。如下图所示,钻孔是用不同规格的钻头尺寸来进行的。如果您的设计中过孔孔径的尺寸与加工厂现有的钻头尺寸不相同,那么会选择离您的设计值最近的钻头规格来钻孔。而Entry面板是用来防护钻头及台面,减少毛刺并降低钻头温度的作用。Backup底板是用来保护板面防止压痕,防止打滑导向并减少毛刺的作用。 机械钻孔的钻头通常有ST型和UC型。一般来说UC型比ST型钻孔的精度更高。        镭射钻孔一般用于微通孔。随着PCB想微型和高密度互联的方向发展,越来越多制板加工采用导孔的连接方式实现高密度互连。而传统机械钻孔的小孔能力,几乎到了极限。随着盲孔设计的发展,高密度的需求其可靠性也要新的工艺来改善,镭射钻孔应运而生。如下图所示为镭射钻孔的方法。        所以,机械钻孔与镭射钻孔的区别如下:        镭射钻孔的精度会比机械钻孔高出许多。因此最小机械钻孔的孔径在规则设置里不得小于0.2mm(8mil)。最小镭射孔径在规则里设置不能小于4mil。 3.   孔径设置与板层厚度,层数等关系         孔径的设置大小首先在满足最小工艺要求的情况下,根据板子要求的精度,板层厚度,叠层数等共同决定。它们之间的关系如下:       因此,在设置孔径尺寸的时候可以参考上面的表格来根据板厚,叠层数等来设置相应的符合要求的尺寸。也可通过如下简易的板厚/孔径比来大概根据整个板厚尺寸来定义合适的钻孔孔径的尺寸。 -END- 来源 | Altium | 整理文章为传播相关技术,版权归原作者所有 |  | 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登三1960_图解!打工人看腾讯这道多线程面试题

1. 骚动的周五 小黑是大白前同事,现在俩人在不同的公司,但是都做后端开发工作。 虽然两个人都在北京,但是距离不算近,一个在望京,一个在中关村,算是北京几大IT聚集圈之二了。 两个人日常除了工作,业余活动并不多,当然头发也不多,宇宙中心五道口成了二人的集结地。 眨了5次眼,又到周五了,仿佛空气都弥漫着明天放假的欢快气息,当然还有骚动的大白和小黑: 大白看着时间差不多了,检查完上线监控报警,没啥问题,背上电脑走出了写字楼。 中关村到五道口还是比较近的,扫上低碳环保的青桔单车,一路向北到北大东门转弯来到了五道口地区。 小黑也坐上13号线,人贴人差点挤成肉饼,美食召唤下他还是在8点准时到了老地方。 大白:黑哥,你啥时候面的腾讯?挂了?你咋不找我内推我们公司呀! 小黑:还没挂,等GM面呢,你们公司手撕红黑树,整不了啊。 大白:就你这样,这么喜欢穿红卫衣和黑裤子,不问你红黑树才怪。话说腾讯都问啥了? 小黑:腾讯的面试整体感觉还是不错的,面试很宽泛,从操作系统、网络到系统设计、常用组件都会问,并且不偏不怪。 大白:那确实不错,是本着去挖掘和探测候选人技术边界,有啥奈斯的问题吗?讲讲啊 小黑:有个问题算是我的盲区了,给了几个Linux系统函数,让我看哪些是线程安全的,哪些是可重入的,并解释下为啥。 大白:哦哦,这是考察对线程安全函数和可重入函数的理解。那你咋回答的? 小黑:卧槽,我说我不太会呀,然后就jump下一题了。要不你给我讲讲?我先干一个! 小黑说完,吨吨吨,一大杯啤酒下肚了,大白见状扶了扶好几年没换的眼镜,开始和小黑讨论什么是线程安全和可重入。 2. 多线程和并发 在使用C++开发的服务端程序中多线程还是主流,一般来说会有个线程池来处理接收的请求,这样可以有效提供服务器的并发能力和CPU的利用率。 但是,多线程也是一把双刃剑。 单线程模式下,一切都是那么单调而稳定,所有的资源都是自己的,我的资源我做主。 多线程模式下,一个进程下装载了多个线程,每个线程除了部分资源是独享外,多个线程对大部分系统资源是共享的。 多个线程共享的进程资源: 内存 文件描述符 地址空间 全局数据 … 每个线程独享的资源: 线程寄存器 线程栈 线程ID、错误返回码、信号屏蔽码 … 敲黑板划重点: 1.进程是系统进行资源分配和调度的基本单位,线程是CPU调度和分派的基本单位; 2.进程是线程的载体,进程有独立地址空间,所有线程共享所在进程的地址空间; 3.进程是系统资源的大股东,而线程基本上不拥有系统资源,只占用少量在运行中必不可少的资源,比如程序计数器、一组寄存器和调用栈; 同一个进程中的多个线程有点像合租,大家共用大部分资源,自己独占一小部分资源,相互影响,然而但单进程单线程就是整租,自己独占所有资源,谁也不影响。 掌握多线程中资源共享和相互影响的特点之后,再来看看线程安全和可重入就容易很多。 3. 什么是线程安全 计算机中所谓的安全大多是指结果的正确且可预测性。 前面我们知道,多线程运行起来虽然可以提高并发能力,但是多个线程会共享很多资源,比如写全局数据,这种情况下就需要额外干预,否则将引发错乱的结果。 线程安全是在拥有共享数据的多条线程并行执行的进程中,可以正常且正确的执行,不会出现数据污染等意外情况,反之则称为线程不安全。 通俗一点讲,线程安全就怎么跑都不乱,线程不安全就是一跑就可能五花八门。 所以可能产生线程不安全根本原因在于:共享数据且共享数据可变。 这些共享数据包括全局变量、局部静态变量等,每个线程都可能对这个数据进行操作,并且操作结果会影响其他线程。 我们还经常提到另外一个术语:线程安全函数/线程安全类。 线程安全函数的一些特征: 无任何共享的数据,都是局部数据; 存在写共享数据,但是进行了加锁处理,可以实现多线程的同步调用; 存在读但无写共享数据,无需加锁; 从图中可以看到: 同一进程内有四个工作线程; 公共函数A 只执行打印操作,无论何时何线程调用,结果都是确定且正确的,因此是线程安全函数; 公共函数B 使用了全局变量Count,并对其进行递增1操作,但是没有进行加锁同步处理,因此结果是不确定的,为线程不安全函数; 公共函数C 使用了全局变量Factor,并对其进行递增2操作,使用了互斥锁进行同步确保结果的正确,是线程安全函数; 在编写多线程程序时,如果涉及多个线程操作一个公共函数,如果该函数本身不是线程安全的。 例如当一个函数F是线程安全函数,但是F调用线程不安全函数G时,同样需要对G进行加锁处理,否则函数F也将不安全。 在《深入理解计算机系统》一书中深入指出了线程不安全函数的分类: 不保护共享产量的函数 保持跨越多个调用状态的函数 返回指向静态变量的指针的函数 调用线程不安全函数的函数 前面介绍的几个例子大部分都是全局变量的不加锁控制相关的,还有两种就是: 函数本次调用依赖于上次调用结果,也就是所谓的跨状态,典型的Linux中的rand()函数; 函数将结果放在一个全局的指针中,典型的gethostbyname、localtime、strtok等; // 函数原型struct tm * localtime(const time_t *clock);/* localtime example */#include    #include    int  (){  time_t rawtime;  struct tm * timeinfo;  time (&rawtime);  timeinfo = localtime (&rawtime);   0;} 在localtime中将结果存放在timeinfo中,这个全局变量可以被任意的线程操作,因此将引发线程不安全。 对于Linux中线程不安全的函数可以查阅: https://man7.org/linux/man-pages/man7/pthreads.7.html 在理解了线程安全的相关定义和形成原因之后,我们来看下什么是可重入。 先来看看可重入的相关定义: 一个程序可以在任意时刻被中断,然后系统去执行另外一段代码,结束后又调用继续原来的子程序不会出错,则称其为可重入(reentrant或re-entrant)。 从根本上来说: 可重入函数只使用自己栈上的变量,不依赖任何外部数据,可以允许有该函数的多个副本在运行,因为每个调用者产生的函数栈都是相互独立的; 不可重入函数使用了一些系统资源,如果被中断的话,可能会出现问题; 可重入函数又分为两大类: 显式可重入:所有函数的参数都是值传递,并且只使用本地栈变量,那么函数就是显示可重入的,无论如何调用,都是可重入的,是绝对无条件的。 隐式可重入:可重入函数中的一些参数是引用传递,只有在调用线程的时候传递指向非共享数据的指针时,它才是可重入的,是相对有条件的。 : 函数内部不使用静态或者全局数据 函数不返回静态或全局数据,数据的产生都由调用者提供 不调用不可重入函数 从本质上来说,可重入函数实现了算法和数据的分离,函数内部的计算不依赖于外部,不影响也不受外部影响,是一种高效且安全的函数。 可重入函数都是线程安全函数,线程安全不一定是可重入函数。 不可重入函数可以遵守可重入规则去改造,从而变为可重入函数。 本文从多线程并发编程的一些特征进行阐述,引出了多线程下资源的共享本质。 正因为临界资源和竞态条件的存在,就产生了线程安全问题,在编写多线程程序时一定要考虑线程不安全带来的问题。 在理解线程安全的概念之后进一步引出了可重入函数。 从本质上来说,都是并发环境下由于共享资源带来的问题。 就这样,小黑听完之后虽然一知半解,但也频频点头,一看表快10点了,两个打工人结完账,消失在了去13号线五道口站的夜色中。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3平台登录_STM32如何高效接收串口数据?

目录 USART3_DR的地址 DMA的通道 DMA的中断 USART接收回调函数 头文件源码 DMA的基本配置 环形队列接收数据 函数原型 参考用例 总结 硬件:stm32f103cbt6软件:STM32F10x_StdPeriph_Lib_V3.5.0 DMA,直接内存存取,可以用它的双手释放CPU的灵魂,所以,本文通过USART3进行串口收发,接受使用DMA的方式,无需CPU进行干预,当接受完成之后,数据可以直接从内存的缓冲区读取,从而减少了CPU的压力。 具体的代码实现如下: usart_driver.h  封装了接口,数据接收回调函数类型,基本数据结构等; usart_driver.c  函数原型实现,中断服务函数实现等; 拷贝这两个文件即可,可以根据目录下的参考用例,进行初始化。 头文件usart_driver.h已经声明了外部函数可能用到的接口; USART3_DR的地址 因为USART3接收到数据会存在DR寄存器中,而DMA控制器则负责将该寄存器中的内容一一搬运到内存的缓冲区中(比如你定义的某个数组中),所以这里需要告诉DMA控制去哪里搬运,因此需要设置USART3_DR的总线地址。 USART3的基址如下图所示; USART3的基址 DR寄存器的偏移地址如下图所示; DR偏移地址 所以最终地址为:0x40004800 + 0x004#define USART_DR_Base 0x40004804 DMA的通道 因为有很多外设都可以使用DMA,比如ADC,I2C,SPI等等,所以,不同的外设就要选择属于自己的DMA通道,查找参考手册; DMA通道 因此USART3_RX在这里会使用DMA1的通道3,这都是硬件上已经预先分配好的,我们需要遵循这个规则。所以在代码中我们做出相应的定义;如下所示; #define USART_Rx_DMA_Channel    DMA1_Channel3 DMA的中断 DMA支持三种中断:传输过半,传输完成,传输出错; DMA中断 因此在使用是相当安全也相当灵活,而本文只是用了传输完成中断;如下定义了,传输完成中断的标志位,DMA1_FLAG_TC3也就对应了图中的TCIF; #define USART_Rx_DMA_FLAG       DMA1_FLAG_TC3 USART接收回调函数 在STM32的HAL中封装了大量外设的回调函数,使用起来十分方便,但是标准库中则没有这样的做法,但是这里我们可以自己实现,rx_cbk就是回调,即串口数据接收完成就会执行已经注册的回调函数; typedef void (*rx_cbk)(void* args); 通过使用接口usart_set_rx_cbk进行回调函数的注册,pargs为将传递的参数指针; void usart_set_rx_cbk(uart_mod_t *pmod, rx_cbk pfunc,void *pargs); 头文件源码 #ifndef USART_DRIVER_H#define USART_DRIVER_H#include  #include  /* Private function prototypes -----------------------------------------------*/#define USE_MICROLIB_USART 1#if USE_MICROLIB_USART#ifdef __GNUC__/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf   set to 'Yes') calls __io_putchar() */#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)//#define GETCHAR_PROTOTYPE int fgetc(FILE *f)#endif /* __GNUC__ */extern PUTCHAR_PROTOTYPE;#else#endif //default 8N1#define COM_PORT USART3#define TX_PIN  GPIO_Pin_10#define RX_PIN  GPIO_Pin_11#define BAUDRATE 115200#define IRQ_UART_PRE 3#define IRQ_UART_SUB 3#define USART_Rx_DMA_Channel    DMA1_Channel3#define USART_Rx_DMA_FLAG       DMA1_FLAG_TC3#define USART_DR_Base           0x40004804#define USART_BUF_SIZE   ((uint16_t)16)typedef void (*rx_cbk)(void* args);struct uart_mod {  uint8_t rx_buf[USART_BUF_SIZE]; uint8_t rx_dat_len; uint8_t head; uint8_t tail;   void (*init)(void);  void *pargs; rx_cbk pfunc_rx_cbk;};typedef struct uart_mod uart_mod_t;extern  uart_mod_t user_uart_mod;void usart_init(void);void usart_set_rx_cbk(uart_mod_t *pmod, rx_cbk pfunc,void *pargs);void usart_send_char(char ch);void usart_test_echo(void);uint8_t usart_recv_char(void);int usart_printf(const char *fmt, ...);//extern GETCHAR_PROTOTYPE;#endif DMA的基本配置 串口接收DMA的配置在函数dma_init中; static void dma_init(void) 已经定义了数据缓冲区,如下: uint8_t RxBuffer[USART_BUF_SIZE] = { 0 }; 因此需要在DMA的配置中设置USART_DR的地址,和数据缓冲区的地址,以及两者的大小;还有就是数据流向; 寄存器流向内存; 内存流向寄存器;这个需要搞清楚;相关配置如下所示;  DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_Base; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer;   DMA_InitStructure.DMA_BufferSize = USART_BUF_SIZE; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 注意:DMA_DIR_PeripheralSRC表示,外设作为源地址,数据是从外设寄存器流向内存,即DMA会把数据从地址USART_DR_Base搬运到RxBuffer去。如果这个地方搞错,会导致RxBuffer始终没有你想要的数据。 环形队列接收数据 线性缓冲区会因为缓冲器接收数据已满导致无法继续接收的问题;而环形队列进行接收的话,会自动进行覆盖,这样一来,在读取数据的时候,也要配置一个环形队列进行数据处理,下面的配置是把DMA配置为循环模式; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 在结构体user_uart_mod中,则用两个变量分别指向队首head和队尾tail;具体数据的读取在函数USART3_IRQHandler中,会把数据从内存的RxBuffer读取到结构体user_uart_mod的成员变量rx_buf中;最终调用回调函数。 函数原型 usart_driver.c #include  #include  #include "stm32f10x_usart.h"#include "usart_driver.h"uint8_t RxBuffer[USART_BUF_SIZE] = { 0 };uart_mod_t user_uart_mod = { .rx_dat_len = 0, .head = 0, .tail = 0, .pfunc_rx_cbk = NULL, .pargs = NULL};static USART_InitTypeDef USART_InitStructure;static void rcc_init(void){ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* Enable GPIO clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB        | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART3, ENABLE);}static void gpio_init(void){  GPIO_InitTypeDef GPIO_InitStructure;  /* Configure USART Tx as alternate function push-pull */  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  GPIO_InitStructure.GPIO_Pin = TX_PIN;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  GPIO_Init(GPIOB, &GPIO_InitStructure);  /* Configure USART Rx as input floating */  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  GPIO_InitStructure.GPIO_Pin = RX_PIN;    GPIO_Init(GPIOB, &GPIO_InitStructure);}static void dma_init(void){  DMA_InitTypeDef DMA_InitStructure;  /* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */  DMA_DeInit(USART_Rx_DMA_Channel); DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_Base; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer; //DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = USART_BUF_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(USART_Rx_DMA_Channel, &DMA_InitStructure);}static void irq_init(void){ NVIC_InitTypeDef NVIC_InitStructure; /* Enable the USART3_IRQn Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = IRQ_UART_PRE; NVIC_InitStructure.NVIC_IRQChannelSubPriority = IRQ_UART_SUB; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}void usart_send_char(char ch){ /* Loop until the end of transmission */ //while (USART_GetFlagStatus(COM_PORT, USART_FLAG_TC) == RESET){} while((COM_PORT->SR & USART_FLAG_TC) != USART_FLAG_TC){  }  USART_SendData(COM_PORT, (uint8_t) ch);}uint8_t usart_recv_char(){ /* Wait the byte is entirely received by USARTy */    //while(USART_GetFlagStatus(COM_PORT, USART_FLAG_RXNE) == RESET){} while((COM_PORT->SR & USART_FLAG_RXNE) != USART_FLAG_RXNE){  }     /* Store the received byte in the RxBuffer1 */    return (uint8_t)USART_ReceiveData(COM_PORT);}int usart_printf(const char *fmt, ... ){    uint8_t i = 0;    uint8_t usart_tx_buf[128] = { 0 };    va_list ap;    va_start(ap, fmt );    vsprintf((char*)usart_tx_buf, fmt, ap);    va_end(ap);  while(usart_tx_buf[i] && i < 128){  usart_send_char(usart_tx_buf[i]);     i++; }     usart_send_char('\0'); return 0;}void usart_test_echo(){ uint8_t tmp_dat = 0xff; tmp_dat = usart_recv_char(); usart_send_char(tmp_dat);}void usart_init(void){ rcc_init (); gpio_init (); irq_init();  /* USARTx configured as follow:  - BaudRate = 115200 baud    - Word Length = 8 Bits  - One Stop Bit  - No parity  - Hardware flow control disabled (RTS and CTS signals)  - Receive and transmit enabled */ USART_InitStructure.USART_BaudRate = BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* USART configuration */ USART_Init(COM_PORT, &USART_InitStructure); USART_ITConfig(COM_PORT, USART_IT_IDLE, ENABLE); //USART_ITConfig(COM_PORT, USART_IT_RXNE, ENABLE); /* Enable USART */ USART_Cmd(COM_PORT, ENABLE);  USART_DMACmd(COM_PORT,USART_DMAReq_Rx, ENABLE); dma_init(); DMA_ITConfig(USART_Rx_DMA_Channel, DMA_IT_TC, ENABLE);  DMA_ITConfig(USART_Rx_DMA_Channel, DMA_IT_TE, ENABLE); DMA_Cmd(USART_Rx_DMA_Channel, ENABLE); }void usart_set_rx_cbk(uart_mod_t *pmod, rx_cbk pfunc,void *pargs){ pmod->pargs = pargs; pmod->pfunc_rx_cbk = pfunc;}void DMA1_Channel3_IRQHandler(void){     if(DMA_GetITStatus(USART_Rx_DMA_FLAG) == SET){                DMA_ClearITPendingBit(USART_Rx_DMA_FLAG);    }}/**  * @brief  This function handles USART3 global interrupt request.  * @param  None  * @retval None  */void USART3_IRQHandler(void){ uint8_t buf[USART_BUF_SIZE]; uint16_t rect_len = 0; if(USART_GetITStatus(COM_PORT, USART_IT_IDLE) != RESET)  {  uint8_t i = 0;  USART_ReceiveData(COM_PORT);  user_uart_mod.head = USART_BUF_SIZE - DMA_GetCurrDataCounter(USART_Rx_DMA_Channel);    //fifo is not full   while(user_uart_mod.head%USART_BUF_SIZE != user_uart_mod.tail%USART_BUF_SIZE){      user_uart_mod.rx_buf[i++] = RxBuffer[user_uart_mod.tail++%USART_BUF_SIZE];  }  user_uart_mod.rx_dat_len = i;  //DMA_Cmd(USART_Rx_DMA_Channel, ENABLE);  if(user_uart_mod.pfunc_rx_cbk != NULL){   user_uart_mod.pfunc_rx_cbk(user_uart_mod.pargs);  } } USART_ClearITPendingBit(COM_PORT, USART_IT_IDLE); //USART_ClearITPendingBit(COM_PORT, USART_IT_RXNE);}#if USE_MICROLIB_USART/**  * @brief  Retargets the C library printf function to the USART.  * @param  None  * @retval None  */PUTCHAR_PROTOTYPE{ /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ USART_SendData(COM_PORT, (uint8_t) ch); /* Loop until the end of transmission */ while (USART_GetFlagStatus(COM_PORT, USART_FLAG_TC) == RESET) {} return ch;}#else#pragma import(__use_no_semihosting)                          struct __FILE {  int handle; }; FILE __stdout;       int _sys_exit(int x){  x = x;  return 0;} int fputc(int ch, FILE *f){       /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ USART_SendData(COM_PORT, (uint8_t) ch); /* Loop until the end of transmission */ while (USART_GetFlagStatus(COM_PORT, USART_FLAG_TC) == RESET) {} return ch;}#endif 参考用例 这里需要调用usart_init,并设置回调函数,如果不设置,则不会执行回调。 void motor_get_cmd_from_uart(void *pargs){  if(pargs == NULL){  return; }  uart_mod_t *p = pargs; if(p->rx_dat_len > 0 && p->rx_dat_len == PACKAGE_SIZE){  if(p->rx_buf[0] == PACKAGE_HEAD   && p->rx_buf[PACKAGE_SIZE - 1] == PACKAGE_TAIL){   user_cmd_mod.head = p->rx_buf[0];   user_cmd_mod.cmd.value_n[0] = p->rx_buf[1];   user_cmd_mod.cmd.value_n[1] = p->rx_buf[2];      user_cmd_mod.option = p->rx_buf[3];      user_cmd_mod.data.value_n[0] = p->rx_buf[4];   user_cmd_mod.data.value_n[1] = p->rx_buf[5];   user_cmd_mod.data.value_n[2] = p->rx_buf[6];   user_cmd_mod.data.value_n[3] = p->rx_buf[7];      user_cmd_mod.tail = p->rx_buf[PACKAGE_SIZE - 1];   user_cmd_mod.process_flag = 1;  }   } p->rx_dat_len = 0; }int main(void){ usart_init(); usart_set_rx_cbk(&user_uart_mod, motor_get_cmd_from_uart,&user_uart_mod);} 总结 本文简单介绍了基于STM32基于DMA,利用串口空闲中断进行串口数据接收的具体配置和实现方法,代码基于标准库3.5版本;因为标准库ST目前已经不再更新,并且ST提供了cubemx工具可以进行基于HAL库和LL库的外设快速配置,从而简化大量工作;当然为了不掉头发感觉撸寄存器也不错,最终适合自己的才是最好的。 —— The End — — 推荐好文   点击蓝色字体即可跳转  感觉身体被掏空!只因为肝了这篇空间矢量控制算法  我打赌!你还不会UART  PID微分器与滤波器的爱恨情仇  简易PID算法的快速扫盲 增量式PID到底是什么? 三面大疆惨败,因为不懂PID的积分抗饱和 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!