5

关于投票
51单片机执行指令的过程【转贴】
 

为了加深初学者对51单片机指令的理解,现在把指令执行的过程在此详细说明,希望对你有启发!

单片机执行程序的过程,实际上就是执行我们所编制程序的过程。即逐条指令的过程。计算机每执行一条指令都可分为三个阶段进行。即取指令-----分析指令-----执行指令。

取指令的任务是:根据程序计数器PC中的值从程序存储器读出现行指令,送到指令寄存器。

分析指令阶段的任务是:将指令寄存器中的指令操作码取出后进行译码,分析其指令性质。如指令要求操作数,则寻找操作数地址。
计算机执行程序的过程实际上就是逐条指令地重复上述操作过程,直至遇到停机指令可循环等待指令。

一般计算机进行工作时,首先要通过外部设备把程序和数据通过输入接口电路和数据总线送入到存储器,然后逐条取出执行。但单片机中的程序一般事先我们都已通过写入器固化在片内或片外程序存储器中。因而一开机即可执行指令。

下面我们将举个实例来说明指令的执行过程:

开机时,程序计算器PC变为0000H。然后单片机在时序电路作用下自动进入执行程序过程。执行过程实际上就是取出指令(取出存储器中事先存放的指令阶段)和执行指令(分析和执行指令)的循环过程。

例如执行指令:MOV A,#0E0H,其机器码为
74H E0H,该指令的功能是把操作数E0H送入累加器,

0000H单元中已存放74H0001H单元中已存放E0H。当单片机开始运行时,首先是进入取指阶段,其次序是:
1 程序计数器的内容(这时是0000H)送到地址寄存器;

2 程序计数器的内容自动加1(变为0001H);

3 地址寄存器的内容(0000H)通过内部地址总线送到存储器,以存储器中地址译码电跟,使地址为0000H的单元被选中;

4 CPU使读控制线有效;

5 在读命令控制下被选中存储器单元的内容(此时应为74H)送到内部数据总线上,因为是取指阶段,所以该内容通过数据总线被送到指令寄存器。至此,取指阶段完成,进入译码分析和执行指令阶段。

   由于本次进入指令寄存器中的内容是74H(操作码),以译码器译码后单片机就会知道该指令是要将一个数送到A累加器,而该数是在这个代码的下一个存储单元。所以,执行该指令还必须把数据(E0H)从存储器中取出送到CPU,即还要在存储器中取第二个字节。其过程与取指阶段很相似,只是此时PC已为0001H。指令译码器结合时序部件,产生74H操作码的微操作系列,使数字E0H0001H单元取出。因为指令是要求把取得的数送到A累加器,所以取出的数字经内部数据总线进入A累加器,而不是进入指令寄存器。至此,一条指令的执行完毕。单片机中PC="0002H"PCCPU每次向存储器取指或取数时自动加1,单片机又进入下一取指阶段。这一过程一直重复下去,直至收到暂停指令或循环等待指令暂停。CPU就是这样一条一条地执行指令,完成所有规定的功能。

系统分类: 单片机
用户分类: MSC-51单片机
标签: 无标签
来源: 转贴
发表评论 阅读全文(184) | 回复(0)

2

关于投票
51单片机系统设计与实际应用

基本原则
质量是关键。没有人会对很差的工作感到满足。当完成高质量的工作时,你会为此而感到骄傲。不管你是否知道,你都会因为你的高质量工作而得到信誉。因此,要想为自己所做的事感到骄傲,就需要建立个人标准,并为达到这一标准而努力奋斗。在达到这些标准时,再提高标准并继续努力。挑战自己去完成更优良的工作,你将会为自己的成就而感到惊讶。
1.1 了解单片机的能力
【规则1】设计满足要求的最精简的系统。
 

正确估计单片机的能力,知道单片机能做什么,最大程度的挖掘单片机的潜力对一个单片机系统设计者来说是至关重要的。我们应该有这样一个认识,即单片机的处理能力是非常强大的。早期的PC机,其CPU(8086)处理能力和8051 相当,却能处理相当复杂的任务。单片机的能力的关键就在软件设计者编写的软件上。只有充分地了解到单片机的能力,才不会做出“冗余”的系统设计。而采用许多的外围芯片来实现单片机能实现的功能。这样做,即增加了系统成本,也可能会降低了系统的可靠性。

1.2 系统可靠性至关重要

【规则2】使用看门狗。

看门狗电路通常是一块在有规律的时间间隔中进行更新的硬件。更新一般由单片机来完成,如果在一定间隔内没能更新看门狗,那看门狗将产生复位信号,重新复位单片机。更新看门狗的具体形式多是给看门狗芯片相关引脚提供一个电平上升沿或读写它的某个寄存器。使用看门狗电路将在单片机发生故障进行死机状态时,重新复位单片机。当前有多种看门狗的芯片,如MAXIM 公司的MAX802,MAX813 等。而且,有好多种单片机中本身就集成有看门狗。一个外部的看门狗是最好的,因为它不依赖于单片机。如果可能的话,看门狗更新程序不应该放在中断或是子程序中,原则上应该放在主程序中。我曾经见过一个工程师,他所调试的程序在运行时偶而会引起看门狗的复位动作,于是他干脆在每10ms 就中断一次的时钟中断程序中清看门狗。我相信他也知道使看门狗失去作用,可他却没有不是去查明引起这个现象的真正原因。因此,我想提醒大家:不论什么理由,绝对不要忽略系统故障的真正原因。高质量的产品来自于高素质的工程师,高质量的产品造就高素质的工程师。

【规则3】确定系统的复位信号可靠。

这是一个很容易忽略的问题。当你在设计单片机系统时,你脑中有这个概念吗?什么样的复位信号才是可靠的吗?你用示波器查看过你设计的产品的复位信号吗?不稳定的复位信号可能会产生什么样的后果?你有没有发现过你所设计的单片机系统,每次重新上电启动后,数据变得乱七八糟,并且每一次现象并不相同,找不出规律,或者有时候干脆不运行,或者有时候进入一种死机状态,有时候又一点事都没有正常运行?在这种情况下,你应该查一下你的系统的复位信号。一般在单片机的数据手册(Datasheet)中都会提到该单片机需要的复位信号的要求。一般复位信号的宽度应为。复位电平的宽度和幅度都应满足芯片的要求,并且要求保持稳定。还有特别重要的一点就是复位电平应与电源上电在同一时刻发生,即芯片一上电,复位信号就已产生。不然,由于没有经过复位,单片机中的寄存器的值为随机值,上电时就会按PC 寄存器中的随机内容开始运行程序,这样很容易进行误操作或进入死机状态。

【规则4】确定系统的初始化有效。

系统程序开始应延时一段时间。这是很多单片机程序设计中的常用方法,为什么呢?因为系统中的芯片以及器件从上电开始到正常工作的状态往往有一段时间,程序开始时延时一段时间,是让系统中所有器件到达正常工作状态。究竟延时多少才算合适?这取决于系统的各芯片中到达正常工作状态的时间,通常以最慢的为准。一般来说,延时20-100毫秒已经足够。对于系统中使用嵌入式MODEM 等“慢热”型的器件来说,则应更长。当然,这都需要在系统实际运行中进行调整。

【规则5】上电时对系统进行检测。

上电时对系统中进行检测是单片机程序中的一个良好设计。在硬件设计时也应该细细考虑将各个使用到的芯片、接口设计成容易使用软件进行测试的模式。很多有经验的单片机设计者都会在系统上电时(特别是第一次上电时)进行全面的检测,或者更进一步,将系统的运行状态中分为测试模式和正常运行模式,通过加入测试模式对系统进行详细的检测,使得系统的批量检测更为方便容易。另外要注意的是,一个简单明了的故障显示界面也是颇要费得心思的。比如:系统的外部RAM(数据存储器)是单片机系统中常用的器件。外部RAM 如果存在问题,程序通常都会成为一匹脱缰的野马。因此,程序在启动时(至少在第一次上电启动时)一定要对外部RAM 进行检测。检测内容包括:1)检测RAM 中的单元。这主要通过写入和读出的数据保持一致。2)检测单片机与RAM 之间的地址数据总线。总线即没有互相短路,也没有连接到“地”上。另外,很多芯片,都提供了测试的方法。如串行通信芯片UART,都带环路测试的功能。

【规则6】按EMC 测试要求设计硬件。

EMC 测试要求已经成为产品的必需。有很多的文章关于这方面的。

1.3 软件编程和调试

【规则7】尽可能使用Small 模式编译

对比起Large模式和Compact 模式,Small 模式能生成更为紧凑的代码。在Small 模式下,C51 编译器将没有使用关键词,如idata、pdata、xdata特殊声明的变量通通放在data单元中。在编程中,对于在的数据区,可以指定放在外部存储器中。

【规则8】在仿真前做好充分的准备

单片机硬件仿真器给单片机开发者带来了极大的方便,同时也很容易造成人的依赖性。很多时候,没有仿真器却能促使工程师写出更高质量的程序。也许在硬件仿真调试之前,下面准备工作将会对你有用:

1)程序编完后,对代码仔细逐行检查。检查代码的错误,建立自己的代码检查表,对经常易错的地方进行检查。检查代码是否符合编程规范。 2)对各个子程序进行测试。测试的方法:用程序测试程序,编制一个调用该子程序的代码,建立要测试子程序的入口条件,再看看它是否按预期输出结果。

3)如果代码有修改,再次对代码进行检查。

4)有可能的话,进行软件仿真——Keil C 的软件仿真功能十分强大。软件仿真可以防止因硬件的错误,如器件损坏、线路断路或短路,而引起调试的错误。

5)开始硬件仿真。

【规则9】使用库函数

重用代码,尤其是是标准库的代码,而不是手工编写你自己的代码。这样更快、更容易也更安全。KeilC 中提供了多个库函数,这些库函数的用法在KeilC 的帮助文件中有详细的描述。

【规则10】使用const。

这一点在很多经典的关于C 和C++的书籍中是必谈的要点。在《Exceptional C++》一书中,对这点有很精彩的描述,现摘录如下:“没有正确的安全意识的枪手在世界上是不可能活的很长的。const 观念不正确的程序员也是一样和没有时间戴紧帽子的正确,没有时间检查带电电线的电工一样不会活的很长。”

在C 语言中,const 修饰符表示告诉编译器此函数将不会改变被修饰的变量的指向的任何值(除了强制类型转换)。当把指针作为参数传递时,总是合适地使用const,不仅可以防止你无意中错误的赋值,而且还可以防止在作为参数将指针传递给函数时可能会修改了本不想改变的指针所指向的对象的值。如: const int num = 7;

num = 9; file://有/可能得到编译器的警告。

const char *ptr,则表示该指针所指向的内容不会被改变,如果在程序中被发生对其赋值的操作,编译时将出错误提示。如:

const char *ptr = “hello”;

*ptr = ‘H’; file://错/误,所指内容不可改变也可将const 放在星号后面来声明指针本身不可改变。如:

char* const ptr;

ptr++; file://错/误,指针本身不可改变

也可同时禁止改变指针和它所引用的内容,其形式如下: const char* const ptr;

【规则11】使用static

static是一个能够减少命名冲突的有用工具。将只在一个模块文件中的变量和函数使用static 修饰,将不会和其他模块可能具有相同名称的函数和变量在模块连接时不会产生名称冲突。一般来说,只要不是提供给其它模块使用的函数,和非全局变量,均应使用static修饰。将子程序中的变量使用static 修饰时,表示这个变量在程序开始时分配内存,在程序结束时释放,它们在程序执行期间保持它们的值。如:

void func1(void)

{

static int time = 0;

time++

}

void func2(void)

{

static int time = 0;

time++;

}

两个子程序中的time 变量使用static 修饰,所以它们是静态变量,每调用一次time将进行加1,并保持这个值。它们的功能与下面程序相似:

int time1 = 0;

int time2 = 0;

void func1(void)

{

time1++

}

void func2(void)

{

time2++;

}

我们可以看出,使用static修饰后,模块中的全局变量减少,使得程序的更为简单。

【规则12】不要忽视编译器的警告。

编译器的给出的警告都是有的放矢,在没有查清引起警告的真正原因之前,不要忽视它。 【规则13】注意溢出问题,写安全的代码。

1.4 KeilC 编程

【规则14】深入了解你所用的工具。仔细查看KeilC 附带的帮助文件,你能找到你期待已久的东西。KeilC 是当前最好用的单片机开发软件。要充分利用该软件的功能,就必须对它深入的进行了解。

【规则15】不要使用语言的冷僻特性,并且记住,耍小聪明会贻害无穷。最重要的是编写你理解的代码,理解你编写的代码,你就可能会做得很好。

2 推荐书目

要成为一个优秀的单片机系统产品设计工程师,兴趣、热情、责任心至关重要。

2.1 单片机技术学习

《微机原理及应用(从16 位到32 位) 》戴梅萼等著清华大学出版社。学校教材,也是当年我学习单片机的启蒙书。

2.2 C51 编程学习

《单片机高级语言C51 Windows 环境编程与应用》作者:徐爱钧彭秀

华电子工业出版社。这本书几乎覆盖了C51 编程的方方面面,最新版本对当前使用最广的keilC 也有很详细的讲述。对于刚学C51 编程的同志,本书是上上之选,强力推荐。比起现今书市上的所谓什么“C51 编程圣经”之类的书强得多。


2.3 C 语言编程必读

《C 陷阱与缺陷》Andrew Koenig著

《C 专家编程》Peter Van Der Linden 著

C 语言开发技术经典之作,C 程序员必读之书,数十年来经久不衰。如果你想对C 语言全面的掌握,真正了解C 语言的精髓,这两本书是必读之作。由人民邮电出版社出版的中文译本也还不错。

2.4 程序设计技术方面

《数据结构》, 严蔚敏, 清华大学出版社。清华大学出版社的教材质量稳定,中规中矩,价格相对来说也便宜一点。

《程序设计实践》Brian W. Kernighan, Rob Pike著;《代码大全》(网上有下载)。这两本是能让你看后,感觉有大突破的那种书籍,千万别吝惜银子。

3 后记

从事单片机开发工作已经有差不多三年时间了,自己感觉积累了一些经验和体会。这篇文章就算是一个总结吧。本来想写的更为详细一些,加入C51中指针及uvision 软件仿真的一些使用体会,以及自己的一些开发实践,但一想,keilc中的说明书已经够详细了,而我的开发产品所有权又不属于我本人,因此,并没有深入下去。由于本人水平有限,这次也是抱着与各位交流学习的目的,非常欢迎各位与我联系交流,共同探讨。

系统分类: 单片机
用户分类: MSC-51单片机
标签: 无标签
来源: 转贴
发表评论 阅读全文(104) | 回复(0)

2

关于投票
C51的一些误区和注意事项

1)C忌讳绝对定位。 常看见初学者要求使用_at_,这是一种谬误,把C当作ASM看待了。在C中变量
的定位是编译器的事情,初学者只要定义变量和变量的作 用域,编译器就把一个固定地址给这个
变量。怎么取得这个变量的地址?要用指针。比如unsigned char data x;后,x的地址就是
&x, 你只要查看这个参数,就可以在程序中知道具体的地址了。所以俺一看见要使用绝对定位的
人,第一印象就是:这大概是个初学者。

2)设置SP的问题。 原因和1差不对,编译器在把所有变量和缓冲区赋予地址后,自动把最后一
个字节开始的地方,作为SP的开始位置,所以初学者是不必 要去理会的。这体现C的优越性,很
多事情C编译时候做了。

3)用C的主程序结构: #include <reg52.h> void main(void) { while(1); } 这是个最小的
成功的C程序,包括头部文件和程序主体。 头部文件的名词解释:引用的外部资源文件,这个文
件包括了硬件信息和外部模块提供的可使用的函数和变量的说明。可以用文本方 式打开
reg52.h,仔细研究下,会有一些写程序的体会。

4)这样构成一个C项目 在C中,常用项目来管理。项目一般分为两大块:C文件块和头部文件
块。 我们常把不同功能写在不同的C文件中,依靠项目的管理,最后把所有文件连接起来,这样
就可以得到可以烧录的HEX文件或BIN文件。 这些C文件中,有且只有唯一一个包括main()函数,
和3)中一样的C文件。 用头部文件把各个不同的C互相连接起来。一个C文件基本上要对应有一个
H头部文件,这个H文件就包含本C文件中可以提供给外面使 用的变量和函数,没有在H文件中列出
的文件,可以算是该C文件的内部函数和变量,外部C不能使用。 例子:a.C: unsigned char
i; unsigned char mWork; void Test1(void) { mWork++; } void Test2(void) { i++; }
a.h文件中: extern unsigned char i; extern void Test1(void); 这样主程序M.c中:
#include <reg52.h> /*C编译器内部自带的H文件,使用<>*/ #include "a.h" /*自定义的H文
件,一般用""*/ void main(void) { Test1(); /*使用a.c模块文件中的函数*/ while(1){
i++; /*使用a.c模块文件中的变量*/ } }

5)51家族 核心都是基于8031的,有很多在此核心上进行扩展,有的把程序存储器放在内部:89c
(S)51..,有的增加了RAM:89c(S)52..,有的增加 了一些专用硬件80C552...,有的改变时钟时
序W77E58...。市面上现在常用的主要有ATMEL公司的AT89X系列,PHILIPS的P87(89)x,台 湾
WINBOND的w77(78)x系列,Cygnal的C8051Fx系列。

6)51单片机结构的C描述 这里不讲51的具体结构,只是引导初学者快速理解51单片机的物理结
构。寄存器和IO及其它硬件设备的地址名称,在相应的C头部文件 中可以找到。51为reg51.h,52
为reg52.h,以次类推,比如winbond的78E58就为w78e58.h这些H文件中的描述: srf,定义一个8
位的设备。 srf16,定义一个16位的设备。 sbit,定义一个位的设备。 用这些语句定义后,就可
以在C中象汇编一样使用这些硬件设备,这是单片机应用比标准C特殊的地方,其它差别很少。

7)在51系列中data,idata,xdata,pdata的区别 data:固定指前面0x00-0x7f的128个RAM,可以
用acc直接读写的,速度最快,生成的代码也最小。 idata:固定指前面0x00-0xff的256个RAM,
其中前128和data的128完全相同,只是因为访问的方式不同。idata是用类似C中的指针方式 访
问的。汇编中的语句为:mox ACC,@Rx.(不重要的补充:c中idata做指针式的访问效果很好)
xdata:外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。 pdata:外部扩展RAM的低
256个字节,地址出现在A0-A7的上时读写,用movx ACC,@Rx读写。这个比较特殊,而且C51好象
有对此BUG, 建议少用。但也有他的优点,具体用法属于中级问题,这里不提。

8)startup.a51的作用 和汇编一样,在C中定义的那些变量和数组的初始化就在startup.a51中
进行,如果你在定义全局变量时带有数值,如unsigned char data xxx="100";,那startup.a51
中就会有相关的赋值。如果没有=100,startup.a51就会把他清0。(startup.a51==变量的初始
化)。 这些初始化完毕后,还会设置SP指针。对非变量区域,如堆栈区,将不会有赋值或清零动
作。 有人喜欢改startup.a51,为了满足自己一些想当然的爱好,这是不必要的,有可能错误
的。比如掉电保护的时候想保存一些变量, 但改startup.a51来实现是很笨的方法,实际只要利
用非变量区域的特性,定义一个指针变量指向堆栈低部:0xff处就可实现。, 为什么还要去
改? 可以这么说:任何时候都可以不需要改startup.a51,如果你明白它的特性。

系统分类: 单片机
用户分类: MSC-51单片机
标签: 无标签
来源: 转贴
发表评论 阅读全文(182) | 回复(0)

1

关于投票
C语言常用语法的简单摘要

 1、标识符
  可由字母,数字和下划线组成。标识符必须以字母或下划线开头。大,小写的字母分别认为是两个不同的字符。不同的系统对标识的字符的字符数有不同的规定,一般允许7个字符。

 2。常量

 可以使用:

 1整型常量

 十进制常数。

 八进制常数(以0开头的数字序列)。

 十六进制常数(以0X开头的数字序列)。

 长整型常数(在数字后加字符L或L)。

 2字符常量

 用单撇号括起来的一个字符,可以使用转义字符。

 3实型常量(浮点型常量)

 小数形式。

 指数形式。

 4字符串常量

 用双撇号括起来的字符序列。

3 表达式

(1)算术表达式

 整型表达式:参加运算的运算量是整型量,结果也是整型数。

 实型表达式:参加运算的运算是实型量,运算过程中先转换成double型,结果为double型。

(2)逻辑表达式

 用逻辑运算符连接的整型量,结果为一个整数0或1。逻辑表达式可以认为是整型表达式的一种特殊形式。

 (3)字位表达式

 用位运算符连接的整型量,结果为整数。字位表达式也可以认为是整型表达式的一种特殊形式。

 (4)强制类型转换表达式

  用“(类型)”运算符使表达式的类型进行强制转换。

 (5)逗号表达式(顺序表达式)

 形式为

 表达式1,表达式2 表达式n

 顺序求出表达式1,表达式2 表达式n的值。结果为表达式n的值。

 (6)赋值表达式

 将赋值号“=”右侧表达式的值赋值号左边的变量。赋值表达式的值为执行赋值后被赋值的变量的值。

 (7)条件表达式

 形式为

 逻辑表达式?表达式1:表达式2

 逻辑表达式的若为非零,则条件表达式的值等于表达式1的值;若逻辑表达式的值为零,则条件表达式的值等于表达式2的值。

 (8)指针表达式

对指针类型的数据进行运算。例如,p-2,p1-p2等(其中p,P1,P2均已定义为指向数组的指针变量,p1与p2指向同一数组中的元素),结果为指针类型。

以上各种表达式可以包含有关的运算符,也可以是不包含任何运算符的初等量(例如,常数是算术表达式的最简单的形式)。

4 数据定义

 对程序中用到的所有变量都需要进行定义。对数据要定义其数据类型,需要时要指定其存储类别。

 (1)类型标识符可用

  int

  short

  long

  unsigned

  char

  float

  double

 struct 结构体名

  union  共用体名

  enum   枚举型名

  用typedef定义的类型名

 结构体与共同体的定义形式为

  struct  结构体名

   {成员表列};

  union  共用体名

    {成员表列};

用typedef定义新类型名的形式为

 typedef  已有类型  新定义类型;

 如:

typedef int COUNT;//就是在有INT的地方都可以用COUNT代替

 (2)存储类别可用

  auto//一般默认
  static

  register

  extren

  (如不指定存储类别,作auto处理)

  变量的定义形式为

  存储类别  数据类型   变量表列;

  例如:

  static  float  a,b,c;

注意外部数据定义只能用extern或static,而不能用auto或register.

 5  函数定义

 形式为

 存储类别  数据类型  函数名(形参表列)

 函数体

 函数的存储类别只能用extern或static.函数体是用花括弧括起来的,可包括数据定义和语句.函数的定义举例如下:

  static  int  max (int,int y)

   { int z;

     z="x">y?x:y;

     return (z);

    }

 6 变量的初始化

  可以在定义时对变量或数组指定初始值.

   静态变量或外部变量如未初始化,系统自动使其初值为零或空.对自动变量或寄存器变量,若未初始化,则其初值为一不可预测的数据.

 7语句

 (1)表达式语句;

 (2)函数调用语句;

 (3)控制语句;

 (4)复合语句;

 (5)空语句.

 其中控制语句包括:

 (1)if(表达式)语句

 或

if(表达式)语句1

else 语句2

(2)while(表达式)语句

(3)do语句

 while(表达式);

(4)for(表达式1;表达式2;表达式3)

  语句

(5)switch(表达式)

    {case  常量表达式1:  语句1;

     case  常量表达式2:  语句2;

     case  常量表达式n:  语句n;

     default;语句n+1;

    }

  前缀case和default本身并不改变控制流程,它们只起标号作用,在执行上一个case所标志的语句后,继续顺序执行下一个case前缀所所标志的语句,除非上一个语句中最后用break语句使控制转出switch结构。

(6)break 语句

(7)continue  语句

(8)return  语句

(9)goto    语句

8  预处理命令

  # define 宏名   字符串

  # define 宏名(参数1,参数2……参数n)字符串

  # undef  宏名

  #include “文件名”(或〈文件名〉)

  #if       常量表达式

  #ifdef     宏名

  #ifndef    宏名

  #else

  #endif

系统分类: 单片机
用户分类: MSC-51单片机
标签: 无标签
来源: 转贴
发表评论 阅读全文(98) | 回复(0)
总共 , 当前 /