注解目录
第一章《振南当年入门 C 语言和单片机的那些事儿》
1、注定堕入单片机
1.1 懵懂好奇的我
(小时候好奇的性格经常让我屁股开花。初中开始对计算机产生兴趣,并一发不可收拾。)
1.2 我的 C 语言学习经历
(上大学后自学 C 语言。遇到“能人”加入 ACM 竞赛。感觉 C 语言乐趣多多,程序如人生。)
1.3 C 语言的顶级赛事
(ACM 国际程序设计竞赛在东北被我们发扬光大。ACM 竞赛浙大的一段传奇佳话。振南在关注的 IOCCC 国际混乱 C 代码大赛。网吧包宿学 C 语言惊呆室友。)
1.4 岔路口上选择单片机
(搞纯软件还是搞单片机,这是一个抉择。鬼才杜撰拉我进入单片机快车道。)
1.5 窗户纸破了
(入门阶段的困惑,看破 C 语言与单片机之间的鸿沟。)
2 、看穿单片机
2.1 CPU 模型
(CISC 与 RISC 指令集。CPU 如何执行指令。汇编不是第一代编程语言,打孔纸带才是。)
2.2 存储器模型
(存储器就是一个指令和数据的容器。)
2.3 总线模型
(地址、数据和控制三大总线。贯穿整个单片机芯片的通路。)
2.4 外设模型
3、单片机跑起来
3.1 时钟系统
(时钟是单片机激励和血液。时钟频率不能无限提高。)
3.2 二进制
(为什么单片机采用二进制?振南告诉你如果单片机使用十进制会怎样?)
3.3 中断机制
(中断不是在给 CPU 捣乱。中断对于单片机为什么如何重要?)
单片机跑起来
3.2 二进制
将“二进制”单独拿出来作为一节来讲,是因为它是一个极为基础的概念。但是很多人对二进制并没有形象的认识,甚至有一些已经人门、稍有开发经验的人对它的理解仍然比较模糊。所以振南认为有必要将它以一种更为形象、通俗而又深刻的方式着重来进行阐述,以便给我们以后的学习打下坚实的基础。
我们人类自古以来都在沿用一种被认为非常自然的计数方式,即十进制。它的原理非常简单,即“满十进一”(为什么是十进制,究其根源是因为我们有十根手指)。如果”XY”是一个十进制的2位数的话,那么它的每一个位上将可能出现0~9这十个数字。某一位当前是9,如果再+1便会归0,同时向它的更高一位进1。这就是计数的基础原理(不论进制如何都是如此)。
既然人类已经习惯了使用了十进制,那为什么要在单片机中使用二进制呢?把它设计成十进制不好吗?在计算机间世的初期,或是在一些技术狂热分子中确实有人尝试制造出其他进制的计算机。但不论使用何种进制,振南前面所介绍的CPU体系中的各种基本内容都是必须要遵循和实现的,比如寻指令与执行、总线的操作等等。好,那振南就以总线操作中的个环节一“CPU 向地址总线给出要访问的存储器中的存储单元的地址”为例,用十进制来进行实现。
假设要访问的地址是(3456)₁₀。,请看图1.19。
图1.19 CPU以十进制方式向存储器产生地址
很明显,要以十进制方式传输3456 这个数值,我们就需要用4条地址线,每一条地址线上分别传输3,4、5,6 这四个数字。敢问大家,这该如何传呢?一条线如何能表达0~9这十个数字呢?有人说:“可以啊,我把5V等分为10份,0V~0.5V代表0,0.5V~1V 代表1,依此类推,4.5V~5V 代表 9。”不错,很聪明,这就是传说中的“模拟计算机”的作法。它的信号线上传输的是模拟电压信号,而非数字信号。
虽然上面所说的方法是可行的,但有很多因素决定了人们不会去这样作:
1) 电路的实现上难度比较大,模拟电路的设计比数据逻辑电路要复杂的多;
2) 传输速度不高,模拟信号的产生与采集接数字信号要慢;
3)稳定性和抗干扰能力比较弱,仅仅靠 0.5V的压差来确定传输的数值,极易出现错误;
4) 功耗很难降低,模拟电路的复杂度和规模以及其他因素注定其功耗较大。
······
针对于第 3 条,有人曾经提出过疑问:“我可以把电压抬高啊,可以将 5V 定义为 10V 或20V,这样压差不就拉开了吗?”聪明,不过你考虑过功耗的问题吗?我们还是用二进制的方式来进行实现吧。(3456)1。转化为二进制是(110110000000),请看图 1.20。
二进制是满二进一,将一个十进制的数值转化为二进制其位数一定会变多,所以我们就需要更多的地址线。二进制数的每一位上只能表示 0和1这两个数字,这对应于地址线上使用两个电平即可实现,比如0V和5V(实际可能是0V~2.xV表示 0,2.x~5V表示1)。这样作的好处是显而易见的,电路设计的难度下降了很多,而且抗干扰能力也比较强。更重要的是,信号的传输速率可以作到比较高,最终实现计算机系统整体性能的提升。另外,二进制也使得芯片的功耗可以大幅度地降低,因为我们可以将高电平定义为3.3V、1.8V,甚至是 1.2V。(高电平电压定义得越低,单片机信号从低电平爬升到高电平的速度越快。因此,降低电平电压将有利于时钟频率的提高。
综上所述,大家应该已经比较深刻地认识到计算机系统中使用二进制的重要意义了。二进制是计算机的根基,是底层 CPU 硬件以及很多相关电路实现的基础。所以,在我们所作的与单片机相关的很多开发和研究工作中,会大量涉及到二进制的概念和应用。
图1.20 CPU以二进制方式向存储器产生地址
3.3 中断机制
中断机制在单片机及嵌人式系统中是重中之重,我们必须深入理解。首先我们要明白一点:CPU执行指令代码,并非一直顺序地逐条执行,而是可能突然跳到某段代码上去的。因为这段代码的优先级更高,或者说它更加紧迫,CPU必须暂时放下手上的的工作,立即去执行它,否则就可能导致不良的后果,甚至是严重的事故。这个“突然跳转”有时是可以人为预见的,或者是设计人员故意使然,但有些时候却是随机的,无法事先断定它发生的具体时间。这就是“中断”最为通俗的表述,如图1.21所示。
图1.21 对“中断机制”的表述
有人说:“中断似乎是在给 CPU 捣乱嘛,它总是在打断程序的正常执行。”不错,但是不能说是“捣乱”,因为中断的存在是合理的,是为了解决实实在在的问题而产生的。比如说,一个单片机正在正常工作,它同时还要接收来自于串口的数据,但是它又不知道数据何时会到,为了解决这一问题,我们可以采用CPU轮询方式,即不停地查看是否有新的数据到来,如果有则进行接收。这样作的最大问题在于浪费 CPU的运行时间,这可能会影响到其他任务的执行效率。如果使用中断方式,将使 CPU得以解放,在没有数据到来之前它可以安心地去作其他工作。串口控制器(CPU 外设)在接收到新的数据后主动通知 CPU(这个通知的过程依赖于专门的中断控制电路以及CP 的中断源),CPU 立即跳到事先设计好的处理代码(ISR,中断服务程序)去执行,完成数据的接收和处理。最后再跳回到原来的“断点”处继续完成手上的工作。
关于中断,其实包含有非常多的内容,比如现场维护、中断向量、中断优先级、中断响应速度、中断的嵌套等等。要将这一切融会贯通,我们才能在实际的开发过程中游刃有余。不过,中断很多时候要比我们想像得更加复杂一些:如果主程序在顺序执行过程中产生了中断,CPU立即转向中断服务程序,那如果在执行中断服务程序的过程中,再一次产生了中断,CPU又该何去何从呢?这就是上面所说的“中断嵌套”所要解决的问题(这里只是简单说说。让大家心中有初步的认识)。
好,本章到这里就告一段落了。振南希望通过此章能够让大家从根本上实实在在地认识到CPU与单片机体系结构以及运作机理的本质,在脑中建立起一个形象的模型。有了这个基础,大家对单片机的理解才能真正作到入木三分,学习和领悟才能事半功倍。单片机和C语言其实不难,从某种意义上来说,它只不过是一个“熟练工种”,最重要的是“入门”。基础加上我们的聪明才智,每一个人都能成为高手!