时间有限,无法一一修改底部目录,请以此目录为准:
向导:总目录:最好的电子、计算机从入门到工程师教程zhuanlan.zhihu.com这一节写单片机一篇入门,拭目以待,必有惊喜!。
不入门的根本原因?
- 简单问题复杂化,
- 复杂问题简单化,
- 头脑迷糊没框架,
- 无头无尾似散沙。
劝退的单片机框架:
我们的单片机框架:
现在个人、工业化领域的PC、单片机、PLC是电子工程师接触最多的器件。
个人计算机(简称PC),现在的笔记本、台式机、平板、智能手机应该都隶属于PC序列,单片机应属于另一序列。以单片机/CPU为内核,标准化、模块化、特化应用其实就是我们常说的PLC(大部分用于工业领域), PLC这一节不讨论。
实际上单片机是把类似于个人计算机的内存、cpu、固态盘、输入输出接口简化功能集成到一个芯片中。
单片机和个人计算机工作原理是类似的。
个人计算机CPU从固态盘/硬盘调入程序和数据进行计算,然后把计算结果存回固态盘/硬盘。
单片机CPU从闪存(程序存储器)调入程序和数据进行计算,然后把计算结果存回闪存。
但是在实践中发现CPU运算速度非常快,固态盘/硬盘/闪存的速度跟不上,严重影响效率。
后来增加了内存这个器件,内存和CPU是一样的处理速度。程序和数据提前调入到内存中进行存取动作,最后保存时才写入固态盘/硬盘/闪存。
所以CPU和固态盘/闪存之间增加的内存是用于大量计算时的缓冲区。
输入输出接口是用于挂接输入设备、输出设备,只是在单片机内部计算不做输出是没有什么用处的,必须输出控制一些设备或者显示出来才行。输入接口就是我们去控制单片机或者单片机检测一些东西预留的接口。
以上我们可以知道,单片机入门的重点:
- 理解闪存是如何存储程序和数据的
- CPU是如何调入调出数据进行运算的
- 内存的数据是如何存取的
- 输入输出接口如何控制的
上一节我们点亮LED灯,大部分都觉得简单,然后我们一直学单片机编程、硬件电路,有心人会觉得总是浮在空中,实际就是把复杂问题简单化。
只要我们能够理清点亮LED这段程序的流程,一级级的深入下去一直到硬件电路是如何支撑这一行程序的,那么立刻就对嵌入式软硬件有了深入的理解。
我们大部分人怎么学的?
知其然不知其所以然,以为懂了,然后学会了控制LED,然后控制三极管,再去研究串口,但对内部原理不去深究,一旦出错,就不会排查,如果长时间的解决不了问题,就成了从入门到放弃。
千鸟在林不如一鸟在手,我们就以LED点亮为例深入程序与硬件内部彻底学通它。然后你的程序框架和硬件框架就能够建立起来,再学习会有如神助。
用什么符号不重要,用什么句子不重要,重要的是你用它们做到了什么!
某些人自称为“程序猿”,实际上在他们的领域,角色定位非常的高大上,这帮人实际是在做造人的工程。女娲娘娘的名讳自然要尊重的,所以程序员真实的代号应该是"程序蛙"。
为什么叫做“程序蛙”?
以上图片从这里来的 http://blog.sina.com.cn/s/blog_4c74e3780100or4j.html
程序蛙会造人,我们来看下面一段最基本程序。
setup_init() //初始设置 只能执行一次
{
人出生设定
黑头发
黄皮肤
黑眼睛
两只手
两只耳朵
两条腿
男
}
loop() //进入高档小盒前的循环 重复执行
{
吃喝拉撒睡
行动坐卧走
没事泡妹子
临时抱佛脚
}
这样我就用程序造了个人出来。游戏里面的人物就是程序员造的,能跑能闹能砍人,如现实我们一样,不过我们是吃五谷杂粮保持loop(),他们要消耗电能。
我们proteus仿真程序也是控制了一种生物,这种生物名字就是单片机(MCU)。
/* Main.ino file generated by New Project wizard
* Main.ino 文件通过一个新工程向导生成
* Created: 周三 1月 1 2020
* 创建日期:2020/1/1 星期三
* Processor: Arduino Uno
* 处理器: Arduino Uno
* Compiler: Arduino AVR (Proteus)
* 编译器: Arduino AVR (Proteus)
*/
int led=13; //创建一个整型变量,名字led,然后让它等于13.
void setup() //初始化函数,大括号内部写初始化代码
{ // put your setup code here, to run once:
//输入你的初始化代码在这里,仅运行一次:
pinMode(led,OUTPUT); //设置单片机的13端口(led口)为输出模式
digitalWrite(led, HIGH); //使单片机13端口输出高电平
}
void loop() //这个无限循环函数 写循环的代码 此次未用
{ // put your main code here, to run repeatedly:
//主要的代码写在这里,循环执行
}
上面这个程序就是我们上一节用来控制LED的。现在是不是有感觉了。 把单片机看成一个人,led看成手电筒,我们程序完成的实际就是人控制手电筒打开的过程。
现在我们深入程序内部去理解...
int led=13;
led 是一个变量,那么变量是什么?
现在我们回忆小学知识:) 总共有4个苹果,两个小朋友分,你拿来2个,还剩几个? 总共有4个苹果,两个小朋友分,你拿来1个,还剩几个?
△+2=4; △=4-2=2; △+1=4; △=4-1=3;
△在这里代表还剩下几个苹果,问题变化了,这个值也变了。 到了初中、高中,我们还是使用 x y 代替△。 到了大学,我们要明白任何符号都可以代替△,也就是剩下几个苹果。
我们一般把一个未知量的公式叫做一元函数 x+1=3 两个的叫做二元函数 x+y=3
变量实际就是一个中间计算过程的替代符号。
为了计算方便,讲解方便,隐藏秘密,我们需要变量。
int led 我们找了led这个符号,来代表整数类型的变量。
整数类型? 继续复习小学知识,这个其实也挺重要的,大部分人同学毕竟还是需要进行下一代培养的:)
我们看科幻电影,机器人已经能达到人的智力,但现实是还差很远,一个简单整数问题,如果不给它明确定义好,它就会计算出错,例如如何让计算机确定整数的范围,加减乘除之后是不是超过计算能力了?
范围从-100 ~ 0 ~ +100 还是-255 ~ 0 ~ +255 这是个大问题 我们看到 int led=13 这一句,int在不同容量的计算机中代表的整数数量是不一样的。
整型数按照位数划分,可以分为8位、16位、32位、64位等。其表示的范围如下。
整型数 范围
int8 -128 ~ 127
uint8 0 ~ 256
int16 -32768 ~ 32767 (3万多)
uint16 0 ~ 65535 (6万多)
int32 -2147483648 ~ 2147483647 (21亿多)
uint32 0 ~ 4294967295 (42亿多)
int64 -9223372036854775808 ~ 9223372036854775807 (922亿亿多)
uint64 0 ~ 18446744073709551615 (1844亿亿多)
我们写一个单片机程序时,要明确定义变量类型,实际上不同的编译器,不同的计算机,int代表的范围是不一样的,一定要特别注意此问题,所以现在一般用int8 int32 明确代替int。 另外这个也不是越大越好,你用一个变量来代表99乘法表的结果。int64 就属于巨大的浪费,用uint8 这个整数类型就可以。因为你每建立一个变量,实际必然会占用单片机的一部分存储空间,当用了大数的变量,空间很容易就会被占满了,其他程序就没地方写了。
除了注释和已经固化的硬件指令,所有我们用软件开始编写程序,每个字符,每个语句,每个变量,每个指令都占用存储器空间,所以资源是有限的,所以写程序特别是单片机程序一定秉承简洁够用清晰的原则。
上面一段不理解没关系,现在我们看计算机怎么把int led=13执行的。
为什么说复杂问题简单化,看起来简单int led=13; 如果深究你可能碰到以下问题:
- [ ? ] 二进制是什么?怎么表示?
- [ ? ] 十六进制是什么?怎么表示?
- [ ? ] 二进制、十进制、十六进制怎么互相转换?
- [ ? ] 汇编语言是什么?反汇编?
- [ ? ] 机器指令是什么?
- [ ? ] 寄存器是什么?
- [ ? ] 堆栈是什么?
- [ ? ] 内存是什么?
- [ ? ] 闪存是什么?
- [ ? ] EEPROM是什么?
- [ ? ] C语言语句与汇编语言如何转换的?
- [ ? ] 汇编语言与机器指令如何对应的?
- [ ? ] 机器指令怎样存储在闪存区域的?
- [ ? ] ......
看起来上面挺复杂的,但是我们把问题拆解出来一个一个搞明白就不难, 另外这些知识是一劳永逸的,学会一次,以后就是反复用的问题。
有时候看起来难的学法其实是容易的,总是浅尝即止的学习方法恰恰是给自己挖坑。
先来感受下,明确复杂问题理解的太简单了,我们是怎样飘起来的,然后无法双脚着地的学习:)
对照上面的问题自问下你能搞懂几个问号,图中的A、B是如何一一对应,那个是地址,那个是数据?是怎样存进去的?是不是感觉到了复杂:)
知乎起名是向导,崇尚框架论,明确框架,我们再学习有了方向才能做到有的放矢。
学单片机与学射箭其实也没什么区别。
有的放矢,的就是目标(箭靶),支靶、弯弓、射箭是流程。如何射箭是方法。有弓箭是条件,箭靶多大,多大的弓是标准/规则。
以上框架可代用于大部分场合。
今天这节课的目标是学会单片机的内部运行机制。
- A 目标: 单片机入门
- B 资源: proteus仿真软件
- C 规则: 程序的写法规则 硬件指令规则
- D 流程: 程序的运行流程
C规则就是单片机原理,内容庞杂,需要逐步学习。如果加快学习进度,关键点是方法、工具、流程。你明白了运行的流程,然后通过工具能够直观的看到,这就是最有效的学习方法。
流程、流程图(flowchart)的掌握是学习各种知识的快捷方法。
高级领导定规则,中级领导写流程,普通职员去执行。
有效的规则流程是实现目标的助力,错误的规则流程是阻力,所以法无定规,有时候打破也是必要的。但你没资源、条件、实力时只能依照已定的规则流程做事。
框架、目标、资源条件、流程图会让我们头脑清楚,知道去准备什么,知道哪里不足,知道去学习什么,知道所谓人上人无非就是有条件的规则制定者。
学习单片机(计算机)实际上应该从太极八卦开始学起(逍遥派传人--查看作者简介,对此算是专业, 祖传单片机编程:)
太极生两仪、两仪生四象、四象生八卦、八卦生万物
程序员既是能造人又是能勘破生死的阴阳师,无图无真相,下图为证:
我们数数 从0~9 然后是10 11变成了两位数,这是因为我们有10个符号来表示。
八卦系统最初始的也是个计数系统,但是它只用长棍和两根短棍表示。到了计算机中就是用0 和 1 两个符号表示,0 1,就必须进位 10 11 。这就是二进制。
一位二进制数 0,1 对应两仪
二位二级制数 00,01,10,11 对应四象
三位二进制数 000,001,010,011,100,101,110,111 对应八卦
四位二进制数怎么表示? 0000,0001,0010,0011,0100,0101,0110,0111 1000,1001,1010,1011,1100,1101,1110,1111
实际上三位二进制数前面加个0,加个1(就组成了上下两排四位二进制数)
那么五位二进制数呢?16个前面再加个0,16个前面再加个1,组成32个数。
二进制数每增加一个0和1进位,可以分别代表2,4,8,16,32,64个数,实际就是2的几次方,2¹,2的二次方,2的3次方...
所以二进制的第三个数不是十进制的3 而是10 (读一零)
那么以此类推 16个符号表示数是十六进制。
16进制和二进制是一个系统下来的,它们可以不需要计算就可以一一对应。
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F 十六个符号代表十六进制的一位数
一位十六进制数和四位二进制数是一一对应的,我们一般会记住它。后面不管多少位的十六进制数我们可以直接用二进制数对出来。
几进制 就是用几个不同的符号代表数,每个进制1位,2位,3位容纳的数的数量是不同的(表示数的多少是不同的)。不同的符号排列组合就能代表不同的数量。实际符号不一定是0、1、9、A 之类的 △、 □ 等等也是可以的,但是大家写习惯了就用这几个符号代表了。
举个例子 假设我们用0,△,2,3,4,5,6,7,8,□代表十进制数
那么91 怎么写? □△,就是了,81呢?8△ 所以符号不重要,重要的是它要表示的是什么。
十进制的个位数 只能容纳0~9 十个数
二进制的个位数 只能容纳0,1 两个数
十六进制的个位数 可以容纳0~9 A B C D E F 16个数 两位十六进制数 可以容纳如下表 256个数 00~FF (表中0x00~0xFF表示)
8位二进制数 对应两位十六进制数和0~255个十进制数,其中0x00,0x1A 这种前面0x代表十六进制数,我们写程序时前面加上0x 用于和十进制数区分,十进制直接写数就可以。
我们懂了十进制 二进制 十六进制那么单片机实际上已经踩到门槛了。
流程、流程、还是流程
单片机或者说嵌入式开发或者硬件驱动开发对开发人员要求比较高的地方是既要懂软件又要懂点硬件。
下面流程是我们如何从写程序到单片机利用程序执行一个操作。
很多同学这时候有疑问了C语言?汇编语言?机器指令?存储器?寄存器?什么鬼。
各种疑问通过下表去理解
一个村子,256户,我是供电厂的电工,现在我要给0~200户断电,我是去扳200次开关还是想个别的办法?
0代表开关关,1代表开关开。 0000 0001(0x01) 是1号家庭,1111 1111(0xFF)是256号家庭,看上面的8位二进制表,代表256个数 写个程序吧
0 00000000 0号家庭电闸关
0 00000001 1号家庭电闸关
0 00000002 2号家庭电闸关
...
0 11001000 200号家庭电闸关
...
1 11111111 255号家庭电闸开
二进制表示太长了 难写 我们尝试用十六进制写写
000
001
002
...
0C8
...
1FF
以上这个十六进制数 就是机器指令我们把0C8发给单片机就能把200号家庭电闸关了。
但是我们看1C8 这种机器指令,能看懂吗 完全是看不懂的情况。
LD R28,01 //开关信号放到R28这个容器内
LD R29,C8 //户号放到R29这个容器内
以上是汇编语言写法 稍微明确点了
open(200); // 打开200号电闸
close(1); // 关闭1号电闸
以上两句是C语言写法,当有了C语言,程序员的数量呈指数式的增长了。
但是隐含的问题是,大家写程序的方法离硬件越来越远了,所以上位机(电脑端、手机)的程序员比较多,嵌入式、单片机的程序员要少很多。
那么是时候把这张图再看看了
我们再看上图是不是能够理解一部分了。 本身C语言代码proteus能够直接转换成机器指令(机器代码)但是为了能够让我们看清楚,c语言代码下面它先转换了下,先翻译成了汇编语言,汇编语言能够看出这个数存到那个存储器里面(内存/SRAM、闪存/Program Memory、寄存器/Registers)
proteus这个软件再怎么赞它都不为过的,上图中它为了让我们看清楚,先把我们写的C语言的代码转换成汇编语言,然后对应汇编语言直接列出机器指令。另外把各种存储器、寄存器的空间和数值都显示出来。并且能够一步步的演示如何存取数据。
我们看机器指令就能对的上内存、程序存储器的代码存放位置(图中的A、B指向)。
B是程序地址(门牌号)为什么这边显示00F6 另一边是00F0 为了好看,排列的整齐,每两位十六进制数算一个,你从左向右数6个(0 1 2 3 4 5 6)那个数正好是93CF 也就是A。 A是程序内容(控制开关的动作)
所以93CF这个机器指令存放到程序存储区地址为00F6的位置。
不理解地址就想象成中药店的盒子,00F6号盒子里面放置的是当归 00F0号盒子里面放置的是车前子。
未完待续 ...
我现在有点恨我自己了 什么单片机一篇入门,这篇要多长才行,后续得改成100篇入门:)
单片机程序 就是把我们写的代码翻译成机器指令存到单片机内部的存储器中,然后每次单片机上电, 就按照程序存储器中的机器指令代码执行,按照一定的时间、一定的顺序打开关闭一系列的内部开关或者通过引脚送出高低的电平信号控制外部设备。
附录:
A:Atmega328 datasheet https://atta.szlcsc.com/upload/public/pdf/source/20140621/1457707095197.pdf
B: C语言中的二进制数、八进制数和十六进制数 http://c.biancheng.net/cpp/html/3421.html
福利内容:
https://zhuanlan.zhihu.com/p/35894131
声明:
文中所有论点、结论皆根据作者经历经验总结,限于知识结构、层次、经历局限性,必然有不少偏颇,甚至部分内容偏激,作者所有的文章也是试错的过程,欢迎批评斧正,如有不同意见或建议,请与讨论区提出讨论,致谢!