查看原文
其他

代码究竟是如何控制硬件的?

嵌入式ARM 2021-01-31

来源 :知乎


问:

代码是如何控制硬件的?
比如说在单片机中,你写 0,它输出低电平。

invalid s


既然楼主提到“低电平”,看来对数字电路是有一点了解的。

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

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


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不过是一堆开关的话,它为什么能“听懂”类似“加法”“do...while”这类高大上的复杂指令、甚至做出office、photoshop甚至人工智能这样神奇的东西呢?这些高大上的语义,是怎么被电路所理解的呢?


加法之类简单指令,前面已经介绍过了;而提到更复杂的东西……这就不得不说说图灵的贡献了。


还是从最小儿科的题目开始。假设你从来没听说过乘法表;那么,你怎么算8×9呢?


我们知道,A x B就是B个A相加或A个B相加的意思。那么,要算8×9,我们只要把8个9加起来就够了:7次加法而已。


换句话说,这里有个很好的思想,即:很多“高级”数学计算(如乘法),其实用“低级”方法(如加法)一样是可以算的。


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


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


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


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


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


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


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


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


别看CPU只会这三板斧;可当它们巧妙的组合起来后(构造成计数器、指令寄存器等等等等再组合成CPU),就达到了图灵完备的要求,产生了质变。


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


——数字电路研究的,就是如何用与或非这三板斧,来实现各种高级运算甚至CPU指令集这么复杂的事物(甚至是直接实现某些算法,如加密、视频编码等等)


——而CPU指令集呢,则形成了另外一个强大得多的图灵机(体现在能够支持更多比原始的与或非更”高阶“的操作上):这就是机器码(和汇编指令几乎一一对应)


——然后呢,诸如c/c++、java等高级语言,就是利用CPU指令集形成的、另一个更加强大的图灵机(编译器/解释器负责两种图灵机之间的翻译工作)。


——而程序员们研究的,就是如何用编程语言这样一个强大的图灵机,去实现office、photoshop、wow甚至人工智能这样复杂的事物。

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


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


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


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


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


软件控制硬件,也是类似的原理。


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


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


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


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


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

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


陈标龙,欲速则不达,放慢脚步。


不知道题主学过数字电路没有,学过就比较好理解了。


我们写的软件经过这几个步骤 高级语言-->汇编语言--->机器语言。机器语言是二进制的,每一种指令操作都有对应的二进制编码,比如我们执行 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 触发器(如图):



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


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


随风,从事嵌入式开发!


我怎么发现得票数最多的没有真正回答问题呢....


下面是我的答案:


先说代码:


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


结论:代码其实就是存储在存储器(内存、硬盘或者闪存等等)中有序的电压的高低。


再说编译:


编译是一个有序的电压的高低向另一种有序的电压高低的一种转换过程,下面以52单片机为例,我们编译是从表示ASCII码的那种有序电压高低转换为52单片机能够识别的另一种规定好的有序电压高低,即表示HEX文件的电压高低。


结论:编译出的结果还是电脑中存储的有序电压高低。


到单片机烧录:


接下俩就是烧录,理解了上面两点就很容易理解下面的内容,烧录就是电脑中的有序电压高低通过数据线传输到单片机中的ROM中。


接下来ROM就可以释放其中的电压来控制外围的电路。


总结:从代码的编辑到最后对电路的控制都是电压在起作用,只是为了方面我们而给我们展现的形式不一样而已,而其本质都是电压,这样也就不存在转换。


理解这句话:世界上没有软件,软件只是对硬件的一种反映,就像意识是对世界的一种反映是一样的!


相信这样就很容易理解了。

看到有人赞同了我的观点,很开心,针对题目我再补充一点:


只要你提到0/1,提到软件,这个问题就没法理解...因为软件【包括0/1】和硬件始终存在一道无法跨越的鸿沟;


你说你在单片机中写0,请问你是如何写0的?在键盘上敲个0?实际还是电平【和我们理解的数字没关系】,那个0只是你在电脑显示器上电平的呈现形式,那个所谓的0【实质是电平】可以传输到单片机中的ROM中,电平控制电平没什么疑问吧,这样就输出低电平了...


有错误欢迎指出。


gavin,来自农民讲习所,讲不好请见谅。


刚考完计算机组成原理
对单片机也略懂一二
所以在这里给大家展示一下我对题主疑惑的解答
首先,题主说写0就会输出低电平
那么我们就用代码展示一下怎么会显示低电平
我把题主的意思先用单片机C语言写出来
可以在keil中运行的

#include “reg52.h”sbit p1.0 = P1^0void main(){ while(1) { p1.0 = 0; }}

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


先看什么是单片机:


如图1


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


我想看不懂代码的人也能够看到


代码第七行里,p1.0这个变量被赋予了0值


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


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


如图2


左边的大家就不用看了


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


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


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


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


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


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


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


如图3


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


如图4


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


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


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


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


什么是冯诺依曼型计算机?书上解释说:
采取存储程序的方式让控制器从存储器中读取二进制并解释然后让运算器去计算数值。
我来再解释一下,首先让我们了解运算器是什么东东


如图5


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


怎么放?


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


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


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


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


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


枕水,看见一边倒的答案总想破一下


我来努力写一个让外行人也能看懂的答案。但前提是你得知道啥是多米诺骨牌。


首先,硬件是由各种“门”组成,“门”是个术语,不懂没关系,可以把一个“门”看成是一个多米诺骨牌,它被推倒后能把下一张骨牌推倒。


看过多米诺骨牌视频的都知道,骨牌的巧妙摆放再配合各种机关的话,在推倒后是可以实现许多种功能的,比如演奏音乐,控制灯光甚至开动汽车啦等等。


假设在一个大房间里,已经摆放了这样一堆堆的多米诺骨牌,每一堆骨牌被推倒后,都能实现出特定的功能,但人是不能进来这个房间的,这些骨牌只能靠房间里的一个机械手来推倒。


然后,在房间外面,摆放着有限的几块骨牌,人可以按照不同的排列组合推倒这些骨牌,然后房间里面就会有个机械手按照人的想法来推倒不同的骨牌,实现各种功能。


但是,有时候,人想实现的功能太复杂了,光靠控制机械手推倒骨牌的话,要推倒成千上万块骨牌,太累,那么一种思路是:在房间里事先摆放更多,更复杂的骨牌,这叫增加硬件,但这样很不灵活,只能解决部分问题。


另一种思路是:人每次都临时控制机械手在房间里摆放新的骨牌,然后这些骨牌被推倒后,就可以一次性触发更多的骨牌被推倒。


但实际上房间里并没有新的骨牌,房间里已经摆放了一大堆专用的骨牌,然后机械手只是重新修改了这些骨牌摆放的位置而已。


这个房间,就是一台电脑,那些已经摆放好的骨牌,就是硬件,按照你的意愿推倒一张骨牌,造成连锁反应,实现你想要的功能,叫做控制,通过机械手重新摆放的骨牌,就是软件。那一堆控制机械手的多米诺骨牌,就是你的鼠标键盘。


-END-


推荐阅读

【01】Unix 和 Linux 你不知道的那些历史【02】在华为,加班究竟有多恐怖?【03】该如何学习嵌入式?看大佬的职业规划【04】抛砖引玉,教你学习各种总线技术【05】史上最烂开发项目原来长这样?奇葩代码盘点



免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存