最近因为项目需要,去看了一下libsodium库的实现,发现里面各种宏定义骚操作真是肯尼迪坐敞篷车,脑洞大开啊。

其中负责加解密一块的逻辑使用了硬件指令集来加速,用到了AES专用指令集一类的。作为专用硬件指令集,调用方法之一自然是在代码里写__asm__了,然而这样会让代码显得很丑,逻辑也不够清晰。于是另一种方法就是封装,反正原理大家都懂,调用的时候做成黑盒就好看多了,于是就有了各种XXXintrin.h,封装了从MMX、SSE直到AVX等SIMD指令集的C语言接口函数。

然而大家似乎并不关心这些函数的命名规则,亦或者说是这个领域的工作者足够细心严谨,所有人都是先读完intel的手册再开始干活的,以至于当我习惯性地试图从函数名推断出这个函数的功能或者数据特征的时候,一脸茫然= =

根据 牙膏厂的官方文档 所说,这些过渡函数的命名是三段式的:

Most intrinsic names use the following notational convention: _mm_<intrin_op>_<suffix>

第一段是固定的_mm,然而intel并没有解释这个mm是什么含义,我个人猜测是同MMX中的Multi-Media,作为一种历史习惯遗留下来;第二段是函数对应汇编指令的功能;而第三段表示的是该汇编指令接收的数据格式与类型,进一步由两部分组成。

以文中提到的_mm_set_pd为例,该函数的作用是从内存中加载两个双精度浮点数据到128位的SIMD寄存器。同样地,以AES-NI指令集的核心指令之一AESENC来说,它的C形式是_mm_aesenc_si128,表示该函数对应的指令为AES一轮加密(_aesenc),传入的数据应该是一个完整(_s)的128位整型数据(i128)。

然而,AVX系列的函数可能并不完全遵循如上规则。比如说涉及数据类型转换的_mm256_castps256_ps128,功能是将一组256位单精度浮点转换为一组128位单精度浮点数(看描述,似乎就是舍弃了高128位?),该函数的第三段_ps128代表的不再是输入数据格式,而是输出数据格式了。

类似地,AVX512的函数前缀就变成了_mm512,应该就是和对应的数据宽度为512相关联吧。

前两周终于把嵌入式系统的期末作业给交了,老师大发慈悲给了个及格,被课程设计这把达摩克利斯之剑悬在头顶的难受日子终于过去了。秉持做事有始有终的原则,在此记录一下我在这门课程,或者说是在这个方向上总结的知识吧。

甲 嵌入式是什么?

首先需要一点想象力来描述嵌入式的使用场景:

提到“嵌入”一词,相信联想到戒指上 镶嵌 钻石这个场景不是什么难事,想必读者也能轻松接受钻石往往比指环本身小的事实。这也是我对嵌入式理解的一个切入点,即小而精的系统,在我们的语境下,这还是一个计算机系统。

再看看百度上的定义:

嵌入式系统是以应用为中心,以现代计算机技术为基础,能够根据用户需求(功能、可靠性、成本、体积、功耗、环境等)灵活裁剪软硬件模块的专用计算机系统。

这是某个权威组织给嵌入式系统下的定义,但我忘了是哪个了。显然,在这条标准定义中,也着重强调了系统的灵活性,除功能和可靠性可能与嵌入式系统的体积有正相关外,我认为,其余需求都可以总结为四个字:越小越好。甚至为了达成这个目的,嵌入式系统舍弃了x86生态引以为傲的模块化、标准化模式,每搭一个新系统就要重做一大堆 轮子 (意义不大但不可避免的工作,如硬件驱动等),导致如今作为主要应用场景之一的智能手机,无可避免地走上了“只换不修”、“不能升级,只能重买”的道路,严重怀疑手机厂商是故意的

因而,如何设计像麻雀一样小而五脏俱全的计算机系统,正是嵌入式系统所追求的。

乙 嵌入式要做什么?

嵌入式开发可以分为两个部分,硬件开发和软件开发。

硬件开发是嵌入式系统主要区别于一般计算机系统的特点,主要表现在为了裁剪系统规格,需要为每一个应用场景专门设计系统原理图,并设计对应的电路板。(P.S. 正是在这里,嵌入式系统引入了微电子学的要素,电源和晶振电路的参数要自己算,使嵌入式系统这门课成为了我本学年最折磨的一门课QwQ)

电路板设计的过程中,需要根据电子元件的物理和电气属性,结合应用场景,合理选择板型、制作并排布焊盘,为可能需要的外设引出合适的接口,然后完成走线工作。设计完成后的电路板可以导出工业标准的文件,交给PCB厂打样生产,做成可以焊接使用的电路板实物;还可以寻找SMT等贴片焊接厂家,将设计过程中使用的电子元器件导出成BOM(Bill Of Material,物料清单)与摆设信息(如pick place文件等),让厂家代为焊接,拿到手就是一片开箱即用的板子了。

软件开发与x86平台上的开发流程有着一定的区别。在编程上,绝大部分工作都使用C/C++完成,由相应的交叉编译工具链生成二进制代码(不是ELF也不是COFF),更不可能出现动态链接;在运行上,嵌入式程序直接以二进制的形式烧录到Flash芯片上,CPU可直接寻址执行,不存在像x86一样从磁盘读进内存,再读进CPU缓存后执行的漫长数据通路;在设计上,由于在嵌入式系统中,资源通常比较紧张,操作系统不是刚需(轻量的RTOS实时操作系统甚至可以被称为线程库),单线程的程序往往足以达到嵌入式系统的设计需求。

这里可以强调的一点是,嵌入式系统(主要考虑ARM)的内存地址空间是连续的,但介质可以是不连续的,即DRAM和NAND/NOR等通常被视为运存和外存的两种介质可以映射到同一地址空间的不同区域。(NOR Flash虽然是通常意义上的外存芯片,但由于其支持字节寻址,是支持XIP(eXecute-In-Place,片内执行,意味着介质可作内存使用)的,但性能较低、成本较高)

丙 硬件开发的具体步骤

硬件开发的主要任务是原理图和电路板的设计,在使用计算机辅助设计的情况下,绝大部分工作都在EDA(Electronic Design Automation,电子设计自动化)工具中完成——没错,就是漂亮国卡咱脖子的那个。除了能够替代人力完成绘图这种基本工作外,EDA软件还可以提供自动布线、(手动)布线检查、逻辑综合、仿真等多种人力难以快速办到的功能。当然,对于我们这种初级电路板设计来说,其中的大部分功能都不一定用得到就是了=w=

下面列出的硬件开发步骤中,二-六步都是在EDA软件中完成的,我使用的是Altium Designer 21。当然,工具不能成为限制开发的理由,真正要搞大新闻的时候,还是不要跟风,选择合适的工具才是上策。

还是码一下吧,我学Altium Designer主要看的是av62785020,虽然讲得有点啰嗦,但是还是很全面清晰了。

一 器件选型

这个部分和我们平常自己买电脑时,挑选配置的工作非常相似。稍微了解一点买电脑的话,买之前要做的第一件事情就是做规划:想要什么样的尺寸、需要什么样的功能、打算花多少钱,等等。

这里的器件选型工作也是一样的,先要完成一些预备工作,准备足够的信息指导自身完成选择。首先分析设计需求,抽象地提出组成目标系统所需的模块,比如供电所需的电源模块、通信所需的Wifi/蓝牙模块、信息输入所需的传感器模块、信息输出所需的音频/视频模块等等,并理清这些模块之间的连接关系。

其次要考虑的是应用场景,将其中最为重要的特征筛选出来,用以指导具体器件的选择:如果想做贴身设备,如电子手环,就必须在后续选型中将重量(轻)、体积(小)、电源(自带电池、低功耗)作为选型的首要依据,可适当选择性能偏低的芯片作为权衡;如果想做高性能设备,如 漏油器 路由器,就必须在后续选型中将性能(高)作为首要依据,而无需过多考虑重量、体积、电源等参数;如果想做的设备要外接大量模块的话,就必须选择引脚足够多的主控芯片(微控制器),以确保主控可以直接控制所有需要的外围设备。

接下来就是根据上述步骤收集的信息完成芯片的选择了。这个过程需要了解一下上面自己所提出的功能模块对应的具体模块的专业名称,比如:对于电源模块来说,完成常见的5V-3.3V电平转换的模块叫做 稳压器,诸如3.7-3.3V一类压差小于1.7V的电平转换模块叫 低压差稳压器(LDO,Low-DrOpout regulator);开发板上外接针脚用的排针,金属针凸出来的叫排针 废话,塑料凹进去的叫排母。其实遇到专业名词的比例并不大,但是一旦遇到了的话,往往会因为没有门路,老半天找不到自己想找的器件。一种曲线救国的方式是用自己的大白话在搜索引擎或者淘宝上搜,有概率能够通过一些推荐算法摸到这些专业名词。

当然,在芯片选型的过程中,还可以结合成本等非主要因素,适当地调整芯片的选型。需要注意的是,芯片并不一定越小越便宜,而是产量越大越便宜,有时候小碗可能还比大碗贵= =

P.S. 大部分嵌入式主控芯片都自带大小不等的片内Flash(~1MB)和RAM(数十KB),如果程序不是特别大的话甚至可以不用考虑外接存储芯片。

二 原理图制作

选定了芯片之后,实际上电路的设计就完成了一大半了。这是因为芯片针脚一般具有固定功能,并且极少有冗余的情况(毕竟要追求小而精嘛,wx:你再骂?),完成功能所需的连线模式可以说是完全固定下来了,原理图所需要做的事情就是在上述提到的抽象模块图的基础上标出确切的针脚,然后指明不同模块之间的针脚连接方式。到这一步为止,电路设计还是停留在原理阶段。

三 焊盘制作

焊盘就是电路板上用于焊接贴片元件的焊接点,一方面将芯片针脚引出,参与电路板电气网络的互联;另一方面,焊接点能够提供足够的粘合力,将原件固定在电路板上。如果说原理图提供了元器件针脚的定义和连接方式的话,焊盘则依据器件的引脚物理排布为电路板设计焊接点——尺寸、形状必须与实物完全一致。

如果见过集成电路板的话,很容易能够理解焊盘就是电路板上裸露出来的、通常是黄偏红色的金属部分。实际上这就是电路板上用于焊接原件和导电的覆铜部分(其余部分刷有颜料以绝缘)。与传统电路板不同的是,嵌入式集成电路板使用贴片元件较多,贴片封装一方面减小封装体积,另一方面减小焊接难度,然而这给手动焊接带来了不小麻烦,所以一般不建议自己手焊贴片元件= =

贴片焊盘的制作在Altium Designer中可以使用Tools菜单下名为IPC Compliant Footprint Wizard的工具,快速利用现有模板生成贴片元件的焊盘,元器件的对应参数可以查其生产商的数据手册获得。至于5V电源接口这种带过孔的我就不会画了,可以考虑去网上找。这里推荐一个叫SnapEDA的网站,注册个账号就能免费下载不少常用的焊盘。

由于焊盘这块我大部分是抄作业(指用别人做好的)和工具生成,经验积累不多,故在此简略带过。

四 焊盘布局

电路板绘制通常包含焊盘绘制与布线两个部分。显然原理图中的每个模块都是要对应到电路板上的真实器件的,或许是芯片、插口(如圆口电源、USB等)、排针,也可能是电容、电阻、晶振等基础电子元件。因而在电路板绘制时,要把原理图中元件对应的焊盘实例化到电路板上,然后再完成布线工作。

手动在电路板上为每一个元件生成焊盘是一件麻烦的事情,即使焊盘已经实现制做好了。好在Altium Designer提供了自动生成焊盘的功能:在完成上一步的焊盘设计后,我们可以回到更前一步的原理图设计中,将每个元件的原理图与对应的焊盘关联,然后在PcbDoc中使用Design菜单下的Import Changes From ***.PrjPcb一键导入原理图中所有元件对应的焊盘。

焊盘生成完成后,便要做好布局。焊盘布局通常遵循以下规则:

  1. 遵循先大后小的原则(先确定大元器件的位置,小的可以挤一挤);
  2. 主控放在电路板中央;
  3. 晶振离主控尽可能近,离电路板边缘尽可能远;
  4. 电源接口放在电路板边缘(废话)

此时只需要排布好焊盘之间的大致相对位置即可,精细对齐工作可以在摆好所有焊盘之后一起完成,Altium Designer提供了对齐的快捷键,可以在做这一步工作的时候去具体学习一下。

五 布线

布线是电路板设计中的最后一步,也可以说是自动化难度最高的一步。布线要做的事情就是按照原理图中的设计完成线网的连接。布线通常受到电路板的限制——在低成本情况下,所使用的印刷电路板是双层或四层的,在这两种电路板中,通常只留有两层可供信号走线(四层板的另外两层分别用作电源和接地)。在这种情况下,为了防止线与线之间交叉,通常需要使用过孔,使得线路适时更换所在覆铜层以避免线路的直接交叉。但是,过孔毕竟有着不同于导线的物理形式,会对某些场景产生意想不到的副作用,比如高频信号场景下,过孔近似于设立了一根天线,因而要根据线路的电气属性合理设置过孔的数量和位置。通常布线遵循如下规则:

  1. 线路拐角为135°(方便制作印刷电路板时液体材料的流动,EDA软件通常原生遵循这个规则);
  2. 高频线路尽量不走过孔,一般信号线路的过孔尽量控制在2个以内;
  3. 电源线适当地比信号线粗一点;
  4. 没有走线的大片空白区域最好以电源或地电位覆铜。

其余的规则还包括线与线之间要保持足够的距离(工艺要求+防止干扰)等等,专业的总结应该会提到这些注意点的。

布完线之后,可以使用Reports里的Board Information,勾选Routing Information后生成报告,就可以检查还有没有遗漏没连接的线路了。具体怎么找漏线的位置可以再去搜一下相关文章。

六 导出生产用文件

完成布线后的电路板与后续实际生产出来的电路板就基本一致了,实际上我们也可以将设计电路板时所使用的一系列PcbDoc直接交给PCB厂用于生产。然而,防人之心不可无,为了避免某些情况下PCB厂无意泄露或有意窃取电路板上的设计信息,对电路板设计者产生知识产权上的利益损失,一般来说设计者希望提交一份不可修改、附加信息尽可能少的文件用于生产。所幸资本家早早考虑到了这一点,目前用于PCB生产的有一种名为Gerber的文件格式,类似于PCB界的PDF,也是一种与软件无关的PCB电路板描述格式。

给PCB厂(电路板生产)一般需要准备如下文件:Gerber(电路板蚀刻)、RectHoles(钻孔)、TestPoint(电气测试)、PickPlace(焊盘信息)。

如果只是做PCB板而不需要一起焊接元件的话,这一步骤的成本是很低的,因为现在(2022年初),大部分PCB厂都提供了低价或免费打样的服务,比如嘉立创、捷配、华秋等,官网就提供在线下单的选项。需要注意的是,不同的制作工艺有不同的价格和制造周期,比如二层板1-2天出货,而四层板要4-5天;加急的话需要额外加钱。我用的是第二个,因为看了一圈只有他家无铅工艺是免费的 XD

七 电路板生产与焊接

给SMT厂(元器件贴片)一般需要准备如下文件:BOM(元器件物料)、GTP(钢网层,用于刷焊锡膏)、PickPlace(焊盘信息)、Assembly Drawing(装配图)。

丁 软件开发的具体步骤

由于我使用的芯片是STM32F103系列,所以使用了STM32CubeIDE作为开发环境。在这一点上,ST做的还是挺好的,将绝大部分硬件访问封装成库,还提供了图形化界面帮助用户配置多用途针脚。STM32CubeIDE是基于Eclipse的二次开发产品,相信稍微偏软件一点的人用起来都是有手就行。

当然,其他的开发环境,比如Keil、IAR等等都是没有问题的,可以用STM32CubeMX生成项目框架后再导入。只不过我图偷懒省事,直接用了他自家的配套产品=w=

一 项目创建

由于嵌入式开发具有硬件依赖性,因而需要在创建项目的时候就指定好主控芯片的型号,尤以核心架构、封装类型为主。在STM32CubeIDE中,可以直接通过图形化界面搜索、选择所需要的MCU型号。

二 引脚功能配置

STM32CubeIDE内置了STM32CubeMX,因而相同地使用.ioc文件完成对主控引脚功能的配置。不过该IDE还提供了非常贴心的保存后自动生成代码的功能,用起来手感不错。不过有一点要非常注意:生成的代码文件里的注释,只有特定注释区间内的代码会在更新配置时保留,写错了地方的代码直接就被删掉了,最好做个备份。

三 代码编写

可以说是再常规不过的C/C++代码编写过程,绝大部分标准库可以正常使用。调用硬件的话,不需要再像传统方法一样自己手动去找寄存器映射的内存地址,直接使用HAL_开头的库方法即可,比如GPIO的HAL_GPIO_WritePin、UART的HAL_UART_Transmit、经典延迟函数HAL_Delay等等。

四 编译、烧录与运行

IDE内的编译操作向来是十分简便的,轻点一下Build Project等待结果出现即可。在STM32CubeIDE中,默认布局在右下角还会展示ROM/RAM的使用情况,方便开发者了解自己编写的程序究竟占用了多少资源。

编译生成的文件可以有多种格式,如bin、hex,甚至Verilog等,STM32CubeIDE默认生成的是bin格式,如果需要其余格式可以去右键菜单Properties>C/C++ Build>Settings>Tool Settings>MCU Post build outputs里勾选需要的Convert to ...选项,就能生成对应格式的固件文件了。

烧录方式的话,常用的有三种:通用的JTAG、ST家的ST-Link和UART ISP。JTAG因为需要的针脚数太多,一般在小型芯片上用的不多,ST-Link就是ST家对UART做的优化,只需要4根引脚就可完成少些、调试等工作。与需要额外购买硬件的JTAG和ST-Link相比,UART ISP属于ST家芯片的内置功能,通过修改芯片的启动模式,可以使用厂家内置的代码启动,由预置代码通过UART协议为用户将数据写入Flash中。对于一穷二白,买东西不但包邮还得倒贴200的学生来说,我自然使用的是无需额外硬件的UART ISP模式烧录程序了。

UART ISP的大致流程是修改[BOOT1:BOOT0]01,然后通过USART1的RX/TX写入数据。工具的话可以使用一个PC端的USB转串口模块(比如CP2102、CH431A等),然后在PC上用软件写入。我使用的工具是FlyMCU,支持.hex格式的二进制文件写入。写入之前可以使用该软件的“读器件信息”功能检查线是否接对了。

完成烧录之后,将所有的器件连接完成,一个嵌入式系统就算制作完成了。这里要强调的是,如果使用杜邦线搭建原型系统,供电线路的链接一定要慎重加慎重。惨痛的教训是,我因为不小心把外置模块的电源极性接反了,结果把电路板的稳压电路给烧了。。。

由于时间有限、钻研不深,本文叙述可能不是十分准确和全面,权当为各位看官对嵌入式开发提供从整体的角度来了解嵌入式的入门性介绍。欢迎跳进嵌入式的大坑!

最近终于快把课设搞完了,之前立下的给电脑清灰换硅脂的flag终于快到头了。由于之前没有专门准备工具,把CPU螺丝拧花了一次,结果去维修点花了大价钱才给修好,留下了心理阴影。这次决定现在网上买一套合适的工具,毕竟俗言 工欲善其事,必先利其器 嘛。

本文一共简要介绍三个点,品牌、材质和批头型号。

品牌

网上冲浪发现,大家推荐的品牌大多有三种,便宜一档的是南旗(Nanch),贵一档的是wiha和PB Swiss Tools。以本文写作时间(2022年3月)为例,南旗的22合1铬钒合金(Cr-V)套装50-60可以拿下来,S2合金版的大约100;PB Swiss Tools光是一把PB00的就要60多。

然而wiha这里出了个奇葩,跟小米合作做了米加螺丝刀套装,据网友说是硬生生拉低了自己的身价,达到了与南旗相同的百元价位。不过合作上似乎有点小插曲,最开始的套装盒子上是印了wiha商标的,后来新版本给整没了。但是官方宣传说除开更换了几个批头型号之外,性能没有任何变化,姑且相信吧。

材质

做螺丝刀批头的常见的材质有两种,分别是铬钒合金(Cr-V)和S2合金。因为不是专业相关人士,具体参数也无法做详尽的解释与分析,这里主要只看一个参数,硬度。

钢材硬度通常通过洛氏硬度(HRC)来判定,格式为HRCxx,其中xx为两位数,数字越大硬度越高。铬钒合金的批头硬度通常在HRC52左右,S2的在HRC58左右,即S2材质的批头硬度更大,本身更不容易变形。(大概这也是S2批头价格高的原因吧)

通常只说了Cr-V材质的螺丝刀直接按照HRC52看就行。更便宜的可能就是碳钢了,HRC只有40多,指不定稍微优秀点的螺丝都能把批头给你刮花喽(

批头型号

电子产品上的螺丝头常用的是十字形的(然而感觉上明明一字型的更好用力?)。十字形批头的编号为PH,数字越大直径越大,电路板上常用的以PH0/PH1居多。(命名规则:PH000/PH00/PH0/PH1/PH2/PH3,以此类推)

起因是之前闲着没事,自己动手编译了PyTorch和TensorFlow,然后把生成的轮子(.whl)留了下来,打算在之后需要使用的时候自己安装。这里顺便吐槽一句,PyTorch官方的wheel包里自带了cudaRT和CUDNN,搞得安装包动不动上1G(自己编译的非自带版本仅200M+),而且服务器还不稳定,下着下着就自动断线了= =

然后经典安装本地轮子命令:

pip install torch-1.9.0a0+gitdfbd030-cp36-cp36m-win_amd64+cu101.whl

然后直接提示说不支持我装这个包。Python版本是对的,编译和安装wheel时用的也都是64位版本,网上那些Python版本都搞不对的低级错误我怎么可能犯,当时一度怀疑是pip出了问题。试图到网上找pip的verbose选项,结果发现对于pip install指令并不支持这个功能,于是只好回到无头苍蝇状态,继续搜索有关轮子装不上的问题。

然后恰好看到这个提问,里面说pip会使用文件名来判断当前wheel是否支持目标平台。(使用pip debug --verbose可以查看当前平台pip支持的所有目标平台标识符)

然后仔细一想,为了区分CPU和不同CUDA版本下编译出来的轮子,我在每个文件末尾添加了CUDA版本信息,如上面命令中的+cu101,于是猜测是不是这个动作干扰了pip对轮子信息的判定。结果删了之后马上就好了= =

于是得出结论,不要轻易修改python wheel的文件名;如果一定要添加辅助信息的话,可以在版本号后面用加号+链接添加所需要的信息,例如PytTorch自己编译时自动添加的git版本号。