一、代码是如何控制硬件的

这是我最喜欢的文章1

先说代码:

我们是用电脑的键盘来输入的指令,每一个指令都对应一个ASCII码,而这里的ASCII码就是有序的电压的高低(或电流的有无,下面只提电压的高低),即我们输入的是电压的高低,你所看到代码是这些电压的高低控制显示器所显示的图像,其实电脑也不知道它是什么,只知道这样显示。
结论:代码其实就是存储在存储器(内存、硬盘或者闪存等等)中有序的电压的高低。

再说编译:
编译是一个有序的电压的高低向另一种有序的电压高低的一种转换过程,下面以52单片机为例,我们编译是从表示ASCII码的那种有序电压高低转换为52单片机能够识别的另一种规定好的有序电压高低,即表示HEX文件的电压高低。
结论:编译出的结果还是电脑中存储的有序电压高低。
程序烧录到单片机:
接下俩就是烧录,理解了上面两点就很容易理解下面的内容,烧录就是电脑中的有序电压高低通过数据线传输到单片机中的ROM中。
接下来ROM就可以释放其中的电压来控制外围的电路。
总结:从代码的编辑到最后对电路的控制都是电压在起作用,只是为了方面我们而给我们展现的形式不一样而已,而其本质都是电压,这样也就不存在转换。
理解这句话:世界上没有软件,软件只是对硬件的一种反映,就像意识是对世界的一种反映是一样的!
相信这样就很容易理解了。

单片机中的0与1:
只要你提到0/1,提到软件,这个问题就没法理解...因为软件【包括0/1】和硬件始终存在一道无法跨越的鸿沟;
你说你在单片机中写0,请问你是如何写0的?在键盘上敲个0?实际还是电平【和我们理解的数字没关系】,那个0只是你在电脑显示器上电平的呈现形式,那个所谓的0【实质是电平】可以传输到单片机中的ROM中,电平控制电平没什么疑问吧,这样就输出低电平了...

翻开数字电路相关教材,最前面几页。

一般它都会告诉你,三极管/场效应管类似继电器(一种通过线圈产生磁场、然后用磁场控制物理开关的通断与否的设备);在它一个管脚上输入/切断电压信号,另一个管脚就会出现高/低电平。

PS:继电器是一种利用电磁铁控制的开关;当向电磁铁通电时就产生磁场,而这个磁场就会吸合或者分离开关,从而实现“以微弱电流控制另一条电路的通断”这个功能。
其中,平常触点接触使得被控制电路导通、给控制它的电磁铁通电后就使得开关断开的那种继电器,就等效于非门。三极管拿来当开关使用时,和这种继电器效果几乎一样。

以上,就是数字电路的基础。

指令

你敲入的任何东西,最终就是通过类似的东西/机制储存的;所谓“指令”,其实就是“某个命令码“(一般叫机器码),这个”命令码”会改变CPU内部一堆“开关”的状态,以激活不同的电路;然后数据(前面提到过,它也是用三极管/场效应管的导通与否“记忆”的)利用类似的机制,被送入这个被“指令”激活的电路——这些电路是工程师们利用最最基础的三极管控制原理,用一大堆三极管组合出来的:当数据(某种高低电平的组合)经过这些电路后,就会变成另外一组高低电平的组合:这个组合刚好和“指令”代表的功能所应该给出的结果一致。

这段话可能有点难以理解。那么,看下最简单的与门吧:数据有两个,分别通过两条不同的线路进入与门;输出只有一个,必须给它输入两个高电平,它才会输出高电平;否则就输出低电平(这一般简化表述为:只有输入两个1,它才输出1,否则输出0)。

——这就是所谓的“与”逻辑;一组这样的“与”逻辑就与计算机指令/高级语言里的“按位与”直接对应。
——而按位与这个指令,意思就是选择一组线路,把数据导通到这组“与”逻辑电路之上;然后这组与逻辑电路就会输出两组数据的按位与的结果。

——类似的,二进制加法,1+1=0(同时进位);1+0=1;0+1=1;0+0=0:这可以用一个异或电路来模拟(因为异或电路的规则就是1+1=0、1+0=1、0+1=1、0+0=0);但这样(同时进位)这个说明就会丢失了,所以需要同时用一个与门模拟高位进位(前面说过,与门就是只有两个1才会输出1,其它输出0;综合异或的说明:这是不是就和二进制加法的规则刚好一致了呢?)

然后更高一位就成了两根输入线上的数据相加、再加上进位数据……依此类推:这就是用开关做加法的思路。

更多位数的数字的加法,只不过是对应位的二进制加法再加上前一位的进位位罢了,没什么特别的——这样堆起来的一组开关,就叫加法器。

——add指令呢,就是选中上面做的那一堆用来做加法的开关们;然后给它们输入数据(不要忘了,两组高低电平而已),这些数据就驱动着构成加法器的那些开关们,噼里啪啦一阵乱响之后(嗯,如果是老掉牙的继电器计算机的话:还记得BUG的故事吗?),电路就稳定在某个状态了:此时,加法器的输出,恰恰就是输入数据的和(当然是这样了。前面讲过,我们是刻意用异或门和与门精心组合,让它们刚好和加法的效果一致)。

——其它种种指令,莫不大同小异(更复杂/高级的时钟、流水线啥的……暂时就无视吧)

你可以翻翻课本,讲过加法器的实现。

而加法器和另外一些逻辑电路加起来,就是所谓的ALU(算术逻辑单元,一下子就高大上了有木有)。(当然了,实际上没这么简单。比如至少还要加上时钟信号来打拍子协调开关们的动作、加上锁存器来暂存数据之类——前面提到过,给加法器输入数据,构成加法器的一堆开关需要噼里啪啦一阵才能进入稳定态,然后就可以读出答案:时钟信号就是用来协调这些开关,保证它们都能得到足以达到稳定态的时间用的)

简而言之,代码在计算机内部,本身就是一组特定的高低电平组合;而计算机是精心设计的、海量的、用高低电平控制通断的开关组;当给这个开关组输入不同的电平组合时,就会导致它内部出现复杂的开关动作,最终产生另外一组高低电平的组合作为输出;这些开关动作经过精心设计,使得它的行为是可解释、可预测的——解释/预测的规则,就是CPU的指令集。

——换言之,在机器内部,一切本来就是高低电平,不存在转换问题。
——反而是键盘/鼠标/mic的输入要经过机械过程到数字信号的转换;而视频、音频之类的输出,要经过数模转换再通过其它机制才能变成人可辨识的信息

图灵机原理——CPU的三板斧

图灵的贡献就是,他证明了,如果一台机器,可以接受一系列的输入、并按输入指示完成运算;那么,当这台机器可支持的操作满足“图灵完备”的要求时,它就可以模拟任何其它数学/逻辑运算!

这实在是太关键了。要知道,人类早就想利用机械装置代替一些脑力工作了。比如说,算盘,按照口诀机械的一阵摆弄,答案就出来了;还有老外的各种机械计算器,比如手摇计算机到炮兵用的弹道计算机、再到德军的机械加/解密机等等,这种尝试可以说是数不胜数。

但,再怎么的,这些东西也只能解决特定的问题。想做能解决全部问题的通用机?天哪,那得有多复杂。

而图灵,就在这时候,为人类指出了一条通向机械智能的可行道路……

——一台只会做加法的机器,只要能想办法让它实现“连续做指定次数加法”,那它就可以模拟一台乘法机(模拟二进制乘法会更容易一些)。而能够模拟任何数学/逻辑运算的机器,并不比加法机复杂太多。

换句话说,要搞出一台“无所不能”的计算机器,并不需要穷尽一切可能,而是只要支持程序输入、再支持少的令人发指的几条指令,就可以办到了。

比如说,CPU,它根本上其实只会三招:与、或、非。

与就是全为真,则输出真;或是只要一个为真,则输出真;非则是输入真它就输出假、输入假就输出真——所谓的真假,一般写作1、0,在计算机内部就是高低电平。

别看CPU只会这三板斧;可当它们巧妙的组合起来后(构造成计数器、指令寄存器等等等等再组合成CPU),就达到了图灵完备的要求,产生了质变——比如,前面提到过的加法器,就是“如何用这类基本逻辑模拟多位二进制数的加法”的一个实例。

更具体是怎么做的,这就不是三言两语能说清楚的了。还是仔细看看自己的数字电路这本书吧。

——数字电路研究的,就是如何用与或非这三板斧,来实现各种高级运算甚至CPU指令集这么复杂的事物(甚至是直接实现某些算法,如加密、视频编码等等)
——而CPU指令集呢,则形成了另外一个强大得多的图灵机(体现在能够支持更多比原始的与或非更”高阶“的操作上):这就是机器码(和汇编指令几乎一一对应)
——然后呢,诸如c/c++、java等高级语言,就是利用CPU指令集形成的、另一个更加强大的图灵机(编译器/解释器负责两种图灵机之间的翻译工作)。
——而程序员们研究的,就是如何用编程语言这样一个强大的图灵机,去实现office、photoshop、wow甚至人工智能这样复杂的事物。

这是一个层层模拟的过程。

————————————————————————————
总之,开关的通断是基础;而各种神奇的功能是如何用这么简单的东西组合出来的呢,那就必须理解“程序”原理(也就是图灵机原理)了。

如果说,计算机是一个人,那么,软件就是他掌握的知识。这个知识使得他不仅能掰着手指头数数(相当于硬件直接提供的基础功能),甚至还可以去洞悉宇宙的奥秘(相当于利用软件“模拟”出来的、无穷无尽的扩展功能)。

————————————————————————————————
具体一些,人是怎样开车的呢?

首先,他要知道车的控制原理(知识/软件);然后,基于这些知识,大脑向他的四肢肌肉发出神经冲动,驱使他完成转方向盘、挂挡、踩离合器/油门等种种动作,最终达到开车这个目的。

软件控制硬件的原理

前面说过,程序本身就是高低电平的组合;它通过在CPU上执行来模拟各种决策过程;同时,计算机就是一堆开关;那么,通过指令向某些地址写出数据(访问特定地址是通过各种寻址机制/指令完成的,归根结底也可以说是通过开关切换,改变了电路拓扑),就等于开启/关闭了对应地址上的某个开关;这个开关可以是类似CPU内部那样的一组三极管,也可以是通向另外一个继电器的信号线——这个信号就促使继电器闭合,于是电机导通……

就好象人开汽车一样,神经发出的微不足道的电脉冲经过肌肉放大,影响了涉及数百甚至数千马力的能量洪流的发动机/变速箱的运转,然后汽车就开走了。

计算机也一样:它通过向控制特定地址上的开关输出0/1(高低电平),就可以通过事先准备的物理设施驱动诸如航模电机、舵机等等机构,这就完成了航模控制。

完整的控制回路甚至可以是:

航模上的传感器采集飞行姿态、地形、位置等等数据(最终转换成高低电平构成的信号)----信号通过某些端口送到CPU-----CPU执行程序,程序读取传感器发来的信号,决定下一步的行动-----经过程序的智能判断后,通过控制特定地址上的开关(前面提过,向这个地址发一组高低电平构成的数据就行了),驱动诸如航模电机、舵机等等机构,完成航模控制。

这,就是所谓的“机器人”(当然,只是最简化的机器人原理而已)。

我们就用代码展示一下怎么会显示低电平

以51单片机举例

我把题主的意思先用51单片机C语言写出来,可以在keil中运行的

嵌入式分享合集145~干货篇_寄存器

好了,题主说在单片机控制里,写0就会输出低电平,是这样的。

题主说的输出低电平就是在其中的一个引脚上输出低电平

我想看不懂代码的人也能够看到代码第七行里,p1.0这个变量被赋予了0值

那么咱们深入的看一下给他赋0值单片机内部发生了什么变化

首先给大家展示一下单片机一个引脚内部到底是什么东东,如下图。

嵌入式分享合集145~干货篇_单片机_02

左边的大家就不用看了,右边给大家解释一下,最右边的就是引脚了

虽然引脚是一个,但是大家可以看到

右边是有两个装置的,上边的装置是用来保持内部输出到引脚的电平不会被外部的信号所干扰。下边的装置会把从外部收集来的信号临时存储起来,这里存的不是0就是1。怎么判断?大于某一电压就是1,小于某一电压就是0。这两个装置互不干扰

第七行的代码就是将某一引脚输出低电平并用上边的保持元件将其维持到低电平。

那么,就有人想问了,为什么写成这样单片机就会认识呢?还会奇怪为什么单片机认识的语言和程序员认识的语言一样呢?

这里就牵扯到了计算机组成原理了。我就简单的介绍一下:

首先,我写的这段代码会在一个软件里运行,这个软件会编译我的代码形成枯燥难懂但是70年代时会被人认为高大上的汇编语言,类似下图这样的(除绿色字部分,解释用的):

嵌入式分享合集145~干货篇_数据_03

这还不够,形成这样的语言会让计算机中的低等编译器认识,低等编译器会将代码翻译成如下图所示的东东,如下图。 

嵌入式分享合集145~干货篇_嵌入式硬件_04

注意,这是16进制的数,具体怎么转化为二进制我就不详细展开了。为什么要编译成图3的语言再编译呢?说白了我感觉就是跟水厂一样,水厂把我制作的水放到一个通用的大水管里然后通到不同单片机的家里,单片机按照自己家的情况把水引到厨房等地。(就是这样吧 - -)

那么,我们就可以让单片机或者叫做计算机来执行这段代码了。

对不起,现在才进入到计算机组成原理(对不起计组老师)

现如今,大家所用到的计算机都是冯诺依曼型计算机。

什么是冯诺依曼型计算机?书上解释说:

采取 存储程序的方式让 控制器存储器中读取二进制并解释然后让 运算器去计算数值。

我来再解释一下,首先让我们了解运算器是什么东东,如下图。

嵌入式分享合集145~干货篇_寄存器_05

最下面的就是运算器运算器能够进行加减乘除逻辑运算,控制器会从存储器中读取数据放到上图运算器上边的框框里,一个框框放一个数据。

怎么放?

看到左右的两条道道了吗?数据会在控制器的控制下被放到这些框框里,当然控制器会控制最下面的运算器做出各种运算然后放回到上边的框框里

那么数据是怎么回去的呢?

废话,当然是怎么来就怎么滚了,通过左右两条道道啊亲

让我们来解释一下最开始楼主说的输出低电平,上边的框框有一些是不能随便放数据的,这些框框用来引出引脚,即有些框框里的数据连接着引脚啊亲

讲到这里,我想我已经比较清楚的解释了0是怎么控制低电平的了。

如果哪些地方没讲明白,大家可以交流一下,我会再详细讲讲我理解的一些内容。

首先看一下“低电平”是怎么形成的。

嵌入式分享合集145~干货篇_寄存器_06

可以知道,引脚输出的电平来自右下方那一对互补输出级

所以当PMOS关断,NMOS导通,那么I/O口输出低电平

这个“控制信号”来自单片机的输出寄存器(output control)。
那么这个信号的根源是怎么来的呢?

以STM32控制器为例

STM32是ARM系列RISC的微处理器。

嵌入式分享合集145~干货篇_数据_07

我们看到I/O的部分链接在APB(peripheral bus 外围总线)上。

看来这个信号就是来自这个总线。

不难知道,所有数据的调度都来自STM32的核心--Cortex-M3.

我们就可以从微处理器如何执行指令的角度去看。

嵌入式分享合集145~干货篇_数据_08

嵌入式分享合集145~干货篇_数据_09

实际上,每一段程序都被保存在ROM里,这个ROM里保存的就是我们软件传达下来所赋予的“信息”,微处理器通过总线在ROM里提取所需要的指令,然后再一定的时钟调配下,最后执行指令的。

而指令的实质居然是

嵌入式分享合集145~干货篇_嵌入式硬件_10

没有错,mem[]里表述的是地址,而右边的二进制码“16‘hd000”就是本质的0和1的组合,是能够被机器识别的,故称机器码。
我这里注释的是他们分别代表的含义亦即汇编语言。
具体起来,这段"机器码"的不同位置的01排序代表不同的含义
当然,这个含义是约定俗成的,就是指令集嘛! 

嵌入式分享合集145~干货篇_数据_11

这样我们知道了,其实在储存器里保存的01序列,我们通过机器识别即取指令,可以了解到不同含义,进而执行不同的操作。

那么这个储存器的01序列怎么来的呢? 

嵌入式分享合集145~干货篇_数据_12

一般就储存原理来说,每一个ROM都是一个个小房间,而房间的排列组合就是信息,他们是有序的。是通过一定模式或者条件下“烧写进去”的。即使没有外部触发,依旧能保持原有电位。

EEPROM存储原理

EEPROM基本存储单元电路的工作原理如下图所示。与EPROM相似,它是在EPROM基本单元电路的浮空栅的上面再生成一个浮空栅,前者称为第一级浮空栅,后者称为第二级浮空栅。可给第二级浮空栅引出一个电极,使第二级浮空栅极接某一电压VG。若VG为正电压,第一浮空栅极与漏极之间产生隧道效应,使电子注入第一浮空栅极,即编程写入。若使VG为负电压,强使第一级浮空栅极的电子散失,即擦除。擦除后可重新写入。

隧道效应:量子力学则认为,即使粒子能量小于阈值能量,很多粒子冲向势垒,一部分粒子反弹,还会有一些粒子能过去,好象有一个隧道,称作“量子隧道(quantum tunneling)”。

嵌入式分享合集145~干货篇_数据_13

那这个有序的信息就是"0"和“1”的组合。

这个序列如何烧写进单片机,当然是有外围电路啦,具体就是通过一定的时序打开单片机的储存通路,然后把信息烧写进去。

如何烧写呢?
JTAG(Joint Test Action Group;联合测试工作组)是一种国际标准测试协议(IEEE 1149.1兼容),主要用于芯片内部测试。现在多数的高级器件都支持JTAG协议,如DSP、FPGA器件等。标准的JTAG接口是4线:TMS、TCK、TDI、TDO,分别为模式选择、时钟、数据输入和数据输出线。

嵌入式分享合集145~干货篇_单片机_14

然后那些高高低低的01信息就这样在时序里被输出、被接收。

接下来我们说软件。

我们写的软件都是高级语言,离机器很远,但是很容易被人的逻辑所理解,聪明的我们由一些翻译官完成更繁杂的工作:
这些翻译官就是编译器,比如KEIL,就是把C语言翻译成汇编语言,再通过汇编器,把汇编语言变成机器码,然后把机器码烧写进单片机的ROM,单片机上电之后,运行程序,读取指令也就是那些信息,然后执行,控制IO口的寄存器,最后使IO口接地,哈哈,低电平就这样完成了。

 我们写的软件经过这几个步骤 高级语言-->汇编语言--->机器语言。机器语言是二进制的,每一种指令操作都有对应的二进制编码,比如我们执行 ADD R1,R2 指令, ADD有一个唯一的二进制编码假设为编码1 ,R1 R2是CPU寄存器地址也有唯一的编码设为编码2 编码3.这些编码的具体格式和数值是根据指令格式和具体cpu架构确定的。比如arm的指令字长就固定为32位,特定的位代表着条件码操作码等。arm的指令可参考《arm 体系结构与编程》杜春雷编
我们的程序就是以这种二进制编码格式存储在cpu的存储器里。
有了这几个唯一编码之后呢?cpu就开始译码操作,进行一些数字电路的组合运算,假设编码1是 0x10(只是假设,实际各个指令集编号不同),当译码电路发现指令的操作码是0x10时就知道是进行加法运算,此时会输出一个有效信号选通加法器;同时也对编码2和编码3进行译码,选通对应的寄存器(哪一个是源寄存器哪一个是目标寄存器是由指令集格式规定的),然后就将寄存器输出的数据通过CPU内部的数据线送入加法器进行加法运算,运算的结果送入目标寄存器。这就运行了一个加法运算。
直接回答题主的问题,当你在程序中对IO管脚的寄存器写0时,单片机将通过类似上述的步骤对指令进行译码,然后将0这个数据写入到IO管脚寄存器中。寄存器的数值如何送到对应的IO管脚?一般是通过D 触发器(如图):

嵌入式分享合集145~干货篇_寄存器_15

在单片机内部IO寄存器的数据口连接到D触发器的D管脚(实际上还有其他电路,用来增大驱动能力等),D管脚下面有小三角的管脚是时钟信号管脚,当时钟信号上升沿来临时,D触发器D端口的数据将输出到Q端口,Q端口是连接着外部的管脚的。所以只要IO寄存器不改变,Q管脚将一直保持着高电平或者低电平,即你程序表现出来的写0就使管脚输出低电平。

总结一下:

你的程序编写完后通过编译器将变成一堆二进制的机器编码----->单片机对这些编码进行译码,知道你要对哪一个寄存器进行什么样的操作----->对应的寄存器被写入正确的值,如果是IO管脚的话将根据时钟将寄存器的值输出到外部IO管脚。所以实际上单片机也就是一堆数字电路的组合,只不过我们人为的规定什么样的编码要进行什么样的操作而已。

cpu内部就是一堆门电路,门电路导通和闭合对应着输出为1或者0;
那怎么让它导通呢?用电压让它导通,你可以认为这个电压是一个能量,用能量驱动这个道理很通俗了吧;

那你可能又要说了,那电压导通那它输出是一个具体的电压啊它也不是1呀,这个就是数电和模电之间的联系和区别了:我们之所以制造数字电路,是要通过数字电路得到一种逻辑实现,而模电才是想得到一个电压输出,这就是模电和数电的天壤之别。那什么又是逻辑实现?简单的说其实也就是数学实现,所谓编程就是把我们的需求变成数学问题,用编程语言编辑出来,给到cpu,让它计算并驱动终端,最终把我们的逻辑显示出来。

至于数模之间的联系,它们之间的联系就是器件都是靠电压驱动,那你又要问了,那么电压为什么可以驱动半导体器件?well,这个你要去看电磁场+半导体物理,可能还得看一点量子力学,我也都没看呢Orz;

说到这总结一下:我们制造数字电路,就是想得到一个能让我们自由表达逻辑,并能让我们眼睁睁看见我们的逻辑实现了的一个工具,至于这个工具是数字电路,还是量子路,还是光路,只要你低功耗性能好,是啥都无所谓,最好是真空才好呢,对人类来说,空气都智能了才好呢。

接下来就要说说cpu架构+指令集。

我们常常听说,一种cpu架构对应着一种指令集,那这是为什么呢?
我们说所谓数字系统,其实很简单,你给我输入,我就给你输出;你想要什么样的输出,那你就要分析分析你要给到我什么样的输入我才能输出你想要看到的输出,编程也就是这个过程;可是问题来了,你随随便便给我什么输入我都hold住吗?很明显是hold不住的,这个例子,我就不举了。。。。

给这段下个结论就是:所谓指令集,其实就是给cpu这个数字系统一套驱动编码

说到这其实大的框架就差不多了,剩下的比较重要的部分就是布尔代数和数学之间的联系,数学和实际需求之间的联系,然后就是显示这一部分,慢慢来吧

从高级语言网下到晶体管开关都有直接的映射关系,于是代码就这样控制硬件了。
详细说一下,高级语言可以通过编译器转换成汇编语言。汇编语言就是硬件的指令,可以直接转换成0101010101。而这些010101就是电路中的低电平和高电平。这些电平控制开关的打开关断,于是各种组合就产生了复杂的逻辑电路。

嵌入式分享合集145~干货篇_寄存器_16


二、五种单片机固件自更新的方法

汽车软件Boot程序的主要作用是刷新App程序。在一个具体客户项目中,Boot也是客户需求的一部分,跟随项目也有软件开发计划(有的为了和其它Boot区分,把项目上的Boot称作CB, Customer Boot)。

对于已经下线盒盖的控制器,无论是在供应商或者客户手里测试,只能通过CB刷新App。如果需要CB自刷新,就需要额外的方法。

1、规范:

整车厂只有对App程序刷新的规范,没有对Boot自刷新的规范。因为规范是针对量产车的,售后只负责App程序的升级,不对Boot升级(也不允许Boot升级)。

所以,Boot的自刷新只存在于项目开发阶段,且由供应商自行提供方案。本文分析五种Boot自更新方式的优缺点。

方式一,SB更新CB:

如图1-a,有的软件架构是两级Boot:SB+CB,Start Boot只检查CPU最小系统,与具体项目的外围电路无关,它独立于客户需求,由供应商自行维护,在Pilot项目早期就应开发完成。因为程序启动顺序是SB->CB->App,这样在SB里增加刷新逻辑可以更新CB。通常情况下运行CB更新App程序,特殊情况下程序启动后一直停留在SB里,更新CB。

优点:

1.逻辑结构简单清晰,软件分工明确。

2.一次刷新,操作简易。

缺点:

1.需要较大的Flash空间在SB里存放刷新逻辑,项目SOP后又要禁止这种刷新方式,造成额外的浪费。

2.软件分三级启动,结构复杂,开发和维护成本较高。对于不需要SB的控制器是一种负担。

3.万一SB也需要更新怎么办?按照这种策略,还得做个SSB?显然不现实。

嵌入式分享合集145~干货篇_单片机_17

方式二、RAM+Flash Reboot更新

如图2-a,不存在SB情况下,程序启动顺序是CB->App。需要刷新Boot时,首先把Reboot程序下载到不用的RAM里(图2-b),然后在RAM环境下运行ReBoot,下载新的CB(图2-c)

优点:

1. 不需要额外的Flash空间,Boot程序运行只需要少量的RAM,因此为App设计的RAM临时可以保存Reboot程序。

2. RAM擦写速度很快,则下载ReBoot的速度会很快。

缺点:

在CB更新过程中万一CPU掉电,重新上电后Reboot内容全无,CB已经破损,程序不能正常启动,控制器瘫痪,只能开盖用JTAG烧写程序。

嵌入式分享合集145~干货篇_单片机_18

方式三、RAM+RAM ReBoot更新(对方式二的改进)

首先把ReBoot(蓝色)+NewCB(紫色)一起都下载到RAM里(图3-a),然后运行ReBoot,擦除CB Flash区域,将RAM中NewCB复制到CB Flash区域(这一步内部完成)。最后,重新上电复位,RAM中的ReBoot和NewCB自动丢失,程序从新的CB开始运行。

优点:

 1.相比方式二少了一步刷新(因为ReBoot和CB是绑在一起的)。

 2.相比方式二CB更新全部在CPU内部执行,不受外界干扰,耗时更短。

缺点:

 1. 相比方式二需要更大的RAM空间存储ReBoot+NewCB。

 2. 和方式二一样存在CB更新阶段掉电后控制器瘫痪的风险 。

 

嵌入式分享合集145~干货篇_单片机_19

方式四、借助App程序Flash空间

刷新分三步:1.图4-b运行CB,擦除App,把ReBoot下载到App区域。2.图4-c运行ReBoot,擦除旧CB,刷入新CB。3.图4-d运行新CB,刷回App。

优点:

1.不需要额外的Flash和RAM资源。

2.稳定可靠,通过优化设计,可以保证在任何一个步骤突然掉电,上电后可以继续操作,控制器不会刷死。(详细设计方法请看附录)

3.对CB做稍微改造就可以成为Reboot程序,开发快速。

缺点:

1.步骤繁多,为了更新CB必须要先擦除App,最后恢复App,至少三次刷新。对不熟悉步骤的操作者容易搞混乱。

2.整体刷新时间会较长,两次Boot+一次App

嵌入式分享合集145~干货篇_数据_20

方式五、借助额外Flash空间

相比方式四,需要一块和CB一样大小的额外Flash空间,刷新分三步:

  1. 图5-b,运行CB,刷入ReBoot到额外Flash。
  2. 图5-c,运行ReBoot,更新CB。
  3. 运行新的CB,破坏ReBoot(全部擦除,或只擦除ReBoot有效性标志)

优点:相比方式四,不需要破坏App程序,也省去了这部分更新时间。

缺点:相比方式四,需要额外的Flash空间,且必须是独立的Block。

 

嵌入式分享合集145~干货篇_单片机_21

小结:

本质上只有三种:

  1. 依赖启动程序SB(方式一),当CPU的Flash资源很富余且项目需要两级Boot时,用该方法最节省时间。
  2. 借助RAM(方式二、三)3.借助Flash(方式四、五)。只需要单级Boot(CB)时,可以容忍因Boot刷新瘫痪必须要给控制器开盖带来时间,人力,物力的成本损耗的情况下用方式二,三较方便。
  3. 只需要单级Boot(CB)时,不允许或不方便控制器开盖,但可以容忍Boot更新步骤繁多时间较长的情况下用方式四、五最可靠。

综上,工程师需要根据整体软件架构,CPU资源,时间人力物料等成本因素综合考虑一种适合自己产品及项目的Boot自刷新方法。

背景:

对于方式四借助Flash刷新【不存在刷死风险,在任何一个步骤中控制器突然掉电,上电后可以继续操作。】的结论,是有条件的。笔者给出这个结论是从最理想的前提思考的,即只要控制器中至少有一个Boot存在(即使一个是坏的),程序就可以从任何一正常的Boot启动运行。这里就有一个问题,CPU怎么判断哪个Boot是好的,哪个是坏的?现在分析一下存在控制器刷死这种风险的情况和几种对策方案。

两级启动地址介绍:

如下图示,CPU上电后程序按地址顺序,检查BootSector的有效性,如果BOOT_ID合法则从指定的地址开始执行,否则检查下一个BootSector。

嵌入式分享合集145~干货篇_寄存器_22

考虑CPU至少具备两个启动地址的情况,如图1-a,当且仅当启动地址1有效时(App为空),程序启动后自动进入Boot。如图1-b,当且仅当启动地址2有效时(不带Boot测试),程序启动后自动进入App。如图1-c,当启动地址1,2都有效势,程序优先从地址1启动,在Boot里检查App程序有效时,再靠跳转指令Jump到启动地址2,开始运行App。 

嵌入式分享合集145~干货篇_数据_23

嵌入式分享合集145~干货篇_数据_24

嵌入式分享合集145~干货篇_单片机_25

方式四控制器刷死情况分析:

如图 2-a,运行Reboot更新CB途中断电。重新上电后,如图2-b,由于启动地址1的内容是在刷新开始就被更新了是有效的,程序会进入CB运行,但是CB不完整,必然运行出错,程序不会跳入ReBoot里,从而不能再刷新(即刷死)。假设从擦除完旧CB开始到刷入新CB完成的时间有10S,在此期间掉电的可能性也不能忽略。

嵌入式分享合集145~干货篇_数据_26

 对策一、Boot有效性标志与启动地址重合

考虑最普遍情况,CPU只能整块(Block)的擦出(16K,32K,64K...),可以最少4字节单位写,没有顺序限制,现在CB只用了一个Block。现在调整刷新顺序:擦出成功后,先刷新橙色区域,最后一步刷新启动地址1有效性标志(灰色区域)。这样,即使在更新橙色区域过程中掉电。

重新上电后,程序依然从启动地址2开始运行,即重新运行Reboot继续等待刷新CB指令,如图3-a所示。具体操作时也不需要更改下载流程,使用$34,36服务按顺序从上位机传输数据到CPU中,先把启动地址1的有效性标志放到RAM里,当把橙色区域都下载到Flash后,再从RAM里把启动地址1的有效性标志写到Flash里(这一步10ms以内即可完成,完全可以忽略在此时间内掉电的可能性)

如果最后一步启动地址1刷新成功,再重新上电后,程序从启动地址1开始运行新的Boot。即启动地址1起了Boot有效性标志的作用(最先擦,最后写),如图3-b所示。

嵌入式分享合集145~干货篇_寄存器_27

对策二、Boot有效性标志独立置尾,增加Boot有效性检查逻辑

如图4-a,把Boot分成2个段,Sec1里仅存放少量的启动自检查逻辑,当它检测到置于Sec2末尾的CB_ValidFlg无效时,即认为Boot是不完整的,则程序控制跳转到启动地址2继续运行ReBoot,重新刷新Boot。

如图4-b,当Sec1的逻辑检测到CB_ValidFlg有效时,即认为Boot刷新完成,则程序控制跳转入Sec2里,此时由于App(ReBoot)末尾的App_ValidFlg是无效的,程序并不会跳转入ReBoot里,接下来就可以刷入新的App了。

这种方法只需要对CB的逻辑和段分配做一下调整,不需要更改刷新顺序。Sec1里的启动自检查逻辑可以做的尽量小,则只要保证刷新Sec1段的过程中不掉电,控制器就不会刷死,大大降低风险。但是对量产软件,检查CB_ValidFlg无效就直接跳转入App是不合理的,所以当Boot最终定型后,应该把这个跳转逻辑关闭。

嵌入式分享合集145~干货篇_数据_28

 三、EEPROM和Flash

存储器分为两大类:ram和rom,ram就不讲了,主要讨论rom。

    rom最初不能编程,出厂什么内容就永远什么内容,不灵活。

    后来出现了prom,可以自己写入一次,要是写错了,只能换一片,自认倒霉。人类文明不断进步,终于出现了可多次擦除写入的EPROM,每次擦除要把芯片拿到紫外线上照一下,想一下你往单片机上下了一个程序之后发现有个地方需要加一句话,为此你要把单片机放紫外灯下照半小时,然后才能再下一次,这么折腾一天也改不了几次。

    历史的车轮不断前进,伟大的EEPROM出现了,拯救了一大批程序员,终于可以随意的修改rom中的内容了。

    EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。是相对于紫外擦除的rom来讲的。但是今天已经存在多种EEPROM的变种,变成了一类存储器的统称。

狭义的EEPROM

    这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。

    例如我们常见的24C02:

嵌入式分享合集145~干货篇_数据_29

广义的EEPROM

    flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。

    flash做的改进就是擦除时不再以字节为单位,而是以块为单位,一次简化了电路,数据密度更高,降低了成本。上M的rom一般都是flash。如W25Q128JVSIQ:

嵌入式分享合集145~干货篇_寄存器_30

flash分为nor flash和nand flash

nor flash:

    nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。依然W25Q128JVSIQ

nand flash:

    nand flash同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。(nandflash按块来擦除,按页来读,nor flash没有页),例如:W29N01HVSINA

嵌入式分享合集145~干货篇_数据_31

    由于nand           flash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand           flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的。小容量的2~12M的flash多是nor型的。

    使用寿命上,nor flash的擦除次数是nand的数倍。而且nand flash可以标记坏块,从而使软件跳过坏块。nor flash 一旦损坏便无法再用。

    因为nor flash可以进行字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。

四、 如何避免电源设计中的电感饱和

电感是DC / DC电源中的重要组成部分。选择电感需要考虑很多因素,例如电感值、DCR、尺寸和饱和电流。电感的饱和特性常会被人们误解而带来麻烦。本文将探讨电感如何达到饱和、饱和如何影响电路,以及检测电感饱和的方法。

电感饱和的原因

先直观的认识下什么是电感饱和,如图1:

嵌入式分享合集145~干货篇_单片机_32

  • 我们知道当图1线圈中通过电流时,线圈会产生磁场;
  • 磁芯在磁场的作用下会被磁化,其内部磁畴会慢慢旋转;
  • 当磁芯被完全磁化时,磁畴方向全部和磁场一致,即使再增加外磁场,磁芯也没有可以旋转的磁畴了,此时的电感就进入了饱和状态。

从另一个角度来看,如图2所示的磁化曲线,磁通密度B与磁场强度H之间满足图2中右侧公式:

  • 当磁通密度达到Bm时,磁通密度不再随磁场强度的增大而大幅度增大,此时电感达到饱和。

由电感与磁导率µ的关系式可知:

  • 当电感饱和后,µ会大幅度减小,最终导致电感量大幅降低,失去抑制电流的能力。

嵌入式分享合集145~干货篇_嵌入式硬件_33

 判断电感饱和的诀窍

在实际应用中有没有判断电感饱和的诀窍呢?

可以总结为两大类:理论计算和实验测试。

  • 理论计算可从最大磁通密度和最大电感电流入手;
  • 实验测试主要关注电感电流波形和一些其他初步判断方法。

嵌入式分享合集145~干货篇_寄存器_34

下面就一一介绍这些方法。

计算磁通密度

此方法适用于利用磁芯来设计电感的场景。磁芯参数包括磁路长度le,有效面积Ae等。磁芯的型号还决定了相应的磁材牌号,磁材对磁芯损耗,饱和磁通密度等做了相应规定。

嵌入式分享合集145~干货篇_寄存器_35

有了这些材料,我们就能根据实际设计情况来计算最大磁通密度,公式如下: 

嵌入式分享合集145~干货篇_单片机_36

实际中可简化计算,用ui来代替ur;最后与磁材饱和磁通密度相比较,就能判断设计的电感是否有饱和的风险。

计算最大电感电流

此方法适用于直接利用成品电感来设计电路。

不同的电路拓扑对电感电流计算有不同的公式。

以Buck芯片MP2145为例,可以按照如下公式计算,将计算结果与电感规格值相比较就能判断电感是否会饱和。

嵌入式分享合集145~干货篇_数据_37

通过电感电流波形判断

此方法也是工程实际中最常见和最实用的的方法。

还是以MP2145为例,使用MPSmart仿真工具进行仿真,从仿真波形可以知道,当电感没有饱和时,电感电流是一个斜率一定的三角波,当电感饱和时电感电流波形会有一个明显畸变,这是由于饱和后感量降低造成的。

嵌入式分享合集145~干货篇_寄存器_38

我们在工程实际中就可以基于此观察电感电流波形是否存在畸变,来判断电感是否饱和。

下面是在MP2145 Demo板上实测波形,可以看到饱和后有明显的畸变,与仿真结果一致。

嵌入式分享合集145~干货篇_单片机_39

测量电感是否异常升温,听是否有异常啸叫

在工程实际中还有很多情况,我们可能不能准确知道磁芯型号,也很难知道电感饱和电流大小,有时候也不能方便的测试电感电流;这时候我们还可以通过测量电感是否有异常温升,或者听是否有异常啸叫等手段来初步判断是否发生了饱和。

嵌入式分享合集145~干货篇_寄存器_40