单片机学习心得_学习单片机心得体会
单片机学习心得由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“学习单片机心得体会”。
Medwin软件使用
计算机仿真对单片机指令系统学习的帮助主要在于帮助理解,加强记忆,适当应用。能够在单片机指令系统学习中的软件主要是MedWin,因为其操作简单,而且可以直观地看到结果。关于MedWin软件的一些基本操作和用法,请参阅博文:单片机仿真开发利器——MedWin软件。下面我们来介绍使用MedWin软件仿真学习单片机指令系统的基本方案。
1.数据传送指令:
数据传送类指令主要包括:MOV、MOVX、MOVC、PUSH、POP、XCH等。下面我们通过一个简单的汇编程序实例来学习这些指令。例1.汇编语言源文件如下图所示:
在MedWin中编辑好源文件以后,以“.asm”为后缀保存为汇编源文件。然后进行编译、汇编并将代码装入内存进行仿真。由于本程序对寄存器、特殊功能寄存器、内部存储器、外部数据存储器都进行了操作,所以需要首先点击“查看”菜单下的相应子菜单打开相应的窗口,即寄存器、特殊功能寄存器、数据区IData、数据区XData,并可以根据需要点击“窗口”菜单下的选项来横向或纵向平铺窗口。
而且因为本程序需要一步一步地详细查看相应指令执行的结果,所以我们需要在执行指令时点击“指令跟踪(F7)”或“指令单步(F8)”按钮来单步执行。指令执行中的一个画面如下图所示:
从图中我们能够清晰地观察到每一条指令执行的每一个结果,从而加深我们对这些指令的理解与记忆。除以上实例程序中所书写的以外,我们还可以使用其他的指令书写程序并在MedWin中仿真,比如PUSH、POP、MOVC等,相信你会得到一个很好的结果,而且MedWin肯定会提高你学习指令的兴趣。2.算术操作类指令
算术操作类指令主要有:ADD、ADDC、DA A、SUB、INC、DEC、MUL、DIV等。算术操作类指令比较复杂,掌握起来比较困难,但在实际的单片机项目应用中很少涉及,尤其是MUL、DIV两条指令,在51系列单片机中更是被束之高阁,很少使用。
此处,我们不再像上节那样逐条书写并仿真课本上的程序,如果感兴趣,你可以仿照上节自己书写程序并进行仿真,观察并体会每一条指令执行的结果以及对系统的影响。这里我们通过一个比较实用的例子来演示仿真算术类指令的操作。
例2.两个压缩BCD码求和:将两个BCD码(每个占4位)分别放在一个字节的高4位和低4位即组成压缩BCD码。本例中有两个压缩BCD码数字,都是四位数,第一个数的高两位放在20H,低两位放在21H中;第二个高低位分别放在30H、31H中。要求所得结果放在40H、41H中。汇编源程序如下图: 在MedWin中编辑好源文件后,以“.asm”为后缀将其保存为汇编文件,然后进行编译、汇编并将代码装入内存进行仿真。你可以像上例那样步进观察其详细执行过程,分析原因;当然也可以在“ljmp $”处设置断点,然后全速运行。需要注意程序中使用到了内部存储器,所以你需要将数据区“IData”窗口调出来进行观察。运行的最后结果如下图所示:
从图中我们能够很清楚地看到内部存储器相应单元的内容,进而观察到程序执行的结果,即:2097+4559=6656。3.逻辑运算指令、控制转移类指令
逻辑运算指令,顾名思义,是用于逻辑运算的指令。主要包括:CLR、CPL、ANL、ORL、XRL等常用逻辑指令以及循环移位指令如:RL、RLC、RR、RRC等。
控制转移类指令是指在程序中根据具体的条件(或者没有条件)使程序转移到相应的入口的指令。它主要包括三类指令:一是无条件转移指令,比如:AJMP、SJMP、LJMP等;二是条件转移指令,比如:JZ、CJNE、DJNZ等;三是子程序调用返回指令,比如:ACALL、LCALL、RET、RETI等。
鉴于单独针对逻辑运算指令进行的仿真十分简单(事实上与数据传送类指令相同),可以很容易、很方便地自己针对相应的指令设计程序来观察结果,进行学习。所以此处不再针对逻辑运算指令举例仿真。而控制转移类指令又不可能单独使用,往往与其他指令结合使用来组成相应的程序,所以也无法单独进行仿真。所以下面我们就将逻辑运算类指令与控制转移类指令相结合来编写仿真程序,通过一个实例同时来仿真这两类指令的应用。
例3.十六进制整数转化为BCD码整数:4位十六进制整数高低位依次放在R3、R4中,要求转换后的BCD整数按高低位顺序放在R5、R6、R7中。程序源文件如下图:
在MedWin中编辑好源文件以后,将其以“.asm”为后缀保存为汇编源文件,然后编译、汇编并将产生的代码装入内存进行仿真调试。仿真前需要注意首先输入R3、R4设置十六进制初始值,具体设置方法如下。首先调出寄存器窗口,然后在需要修改的寄存器名称或者数值上双击,这时其内容将变为可修改,我们在其中填入需要设置的数值(字母大小写均可)即可,如下图所示:
设置好初始值以后,你可以单步观察几步以便明白其原理,然后就可以设置一个断点全速运行了,最后就可以看到所得到的结果,比如我们输入8FD6,将得到结果:R5=03,R6=68,R7=22。4.位操作指令
位操作比较简单,我们也不再写实例进行仿真,如果你感兴趣,可以自己写一些针对相应指令的小程序来仿真之。
这里我们主要说明一下仿真位操作指令与其他指令的不同及注意事项。
位操作指令是对单片机内部存储器的位地址空间进行的相应操作,所以我们查看相应结果时应该打开相应窗口。单片机的位地址空间可以这样来打开:点击“查看”菜单下面的“数据区 Bit”子菜单。位地址空间窗口如下图所示:
除此之外,在相应的位操作中如果我们需要查看各种位标志时,可以点击上图中下侧的“位”标签,将“字节”标签换过来进行查看。
单片机仿真开发利器——MedWin软件 MedWin是万利电子有限公司开发的一款集编辑、编译/汇编源程序、在线及模拟调试为一体的单片机高性能集成开发环境(Integrated Development Environment, IDE)。因其具有强大的功能、简洁的界面、方便的操作而备受单片机学习者的喜爱,在单片机学习开发者中流传甚广。你可以从万利公司的网站上(http://www.daodoc.com)下载到该软件。鉴于MedWin的最新版本(V3)操作相对复杂,不利于我们的入门,所以本文博主将选用操作十分方便简单的MedWin旧版本(V2.39中文版)。
MedWin是一款标准的Windows软件,其安装和其他软件类似,在这里我们就不详细说明了。下面我们主要介绍一下其基本操作。
首先我们启动MedWin,在它启动过程中会弹出一个窗口(如下图所示)供我们选择开发方式。因为我们没有仿真器,所以在这里只需选择“模拟仿真”就可以了:
选择并点击“模拟仿真”按钮后,将进入MedWin集成开发环境,如下图:
进入集成开发环境以后我们就可以进行仿真学习了。首先我们点击“文件-新建”菜单或者点击工具栏中的新建按钮,系统将弹出“新建文件”窗口,我们可以新建一个源文件,如下图所示:
注意在命名新创建的文件时一定要注意写好后缀,系统将通过你的文件名的后缀来识别你创建的文件的类型,比如你用“.asm”做后缀,系统就认为你创建了一个汇编语言源文件;而如果你使用“.c”做后缀,系统则认为你创建了一个C51源文件。在我们写好文件名并点击“打开”按钮以后,系统将弹出源代码编辑窗口,这时我们就可以在窗口中编辑我们需要创建的源代码了。源代码编辑窗口有一个比较好的特性,它会将MCS-51指令系统中的指令和数字以特殊颜色显示,一旦发现相应的指令没有特殊显示,那就说明我们的源代码书写失误了,从而可以迅速更正,这显著提高了我们编写源程序的准确度。如下图所示:
程序源代码编辑完以后,应该首先保存,以防发生意外。然后我们就可以进行汇编、产生代码并且调试了。如上第二图红色方框内工具栏框中最左侧的按钮是“产生代码”按钮,用来产生HEX代码;中间的是“编译/汇编”按钮,能够对编辑好的源代码进行编译、汇编操作;最右边的是“产生代码并装入”按钮,主要用来产生代码装入系统进行调试,或者将已经产生的代码直接装入系统调试。当然,你也可以从“项目管理”菜单下面找到并使用这些功能菜单,或者使用相应的快捷键。从其功能可以看出,“产生代码并装入”按钮的功能最强,可以一下完成编译、汇编、产生代码和装入系统全部功能;而“编译/汇编”按钮的功能最少,只能完成编译、汇编。所以有时我们为了方便,如果你需要对编写好的源代码进行调试,只点击“产生代码并装入”按钮就可以了;而如果只需产生代码,那么你点击“产生代码”按钮就可以了,十分快捷方便。
在这里我们为了说明各个按钮的作用,我们循规蹈矩,一步一步来操作。首先点击“编译/汇编”按钮进行编译、汇编操作,我们可以在汇编结果窗口中查看汇编中的警告和错误的次数,如果均为0,就说明我们的源代码没有错误,通过了编译和汇编。如下图所示:
汇编通过以后,我们来产生代码。点击“产生代码”按钮来产生代码,同样我们可以在产生代码结果窗口中查看操作结果,如下图,我们可以在窗口中看到产生代码成功的提示,这就说明我们的源代码产生.HEX代码成功,我们就可以进行下一步操作了:
接下来我们点击“产生代码并装入”按钮将代码装入便可以进行各种调试了。
装入代码后我们还需根据程序内容和需要来调出相应的窗口,调出窗口可以通过“查看”菜单来进行。在该菜单下有“寄存器”、“特殊功能寄存器(SFR)”、“数据区”等选项,我们可以通过它们来查看相应的内容。除此以外,在“外围部件”菜单下你还能找到定时器/计数器、中断、串行口等窗口,用来在调试和仿真程序时查看相应的内容。如果你觉得调出的窗口排列不太利于自己查看的话,还可以通过“窗口”菜单下的层叠窗口、横向平铺窗口或纵向平铺窗口来进行调整。
本程序中我们的程序比较简单,所以只需调出内部数据存储器(IData)窗口就可以了,然后我们选择“纵向平铺窗口”使窗口排列整齐,以便我们观察。最终软件的程序界面如下图所示:
在仿真调试以前,我们先来一起熟悉一下调试工具栏,如下图:
调试工具栏中的工具从左到右依次是: 全速运行 禁止断点并运行 指令跟踪 指令单步 执行到光标处
执行到函数/子程序结束 自动运行 停止运行 复位
设置/清除断点 命令功能很容易从名称上看出,使用时只需单击相应按钮就可以了。在这里我们主要说明一下几点:
1、设置/清除断点:设置断点可使程序在全速运行情况下运行到断点处停止(断点所在行不运行)。
2、指令跟踪和指令单步:它们的区别主要在对子程序的执行上。指令跟踪可以实现在子程序内部进行单步执行;而指令单步则会一次将整个子程序执行结束,从而跳到子程序的下一个语句上。
通过以上介绍我们不难发现,它们中有的功能几乎相同,我们可以任意选择,来仿真调试我们的程序。
在我们这个例子中,为了清除地观察每一条指令的执行结果,我们选择单步执行程序;又因为程序中没有子程序,所以指令跟踪或者指令单步我们都可以选用。由于此程序仿真调试十分简单,我们在这里不再详细说明仿真步骤与结果。
另外由于文章篇幅有限,而且文中使用的功能有限,我们在这里不再对软件进行深入说明与研究,如果有问题,请参阅万利电子公司的软件说明书。
while(1)的意思是除非你赶它出去(在循环体内设置条件,break or retun),否则它会永远循环.while(1){ if(今天发工效)break;}
波特率: 在使用串口做通讯时,一个很重要的参数就是波特率,只有上下位机的波特率 一样时才可以进行正常通讯。波特率是指串行端口每秒内可以传输的波特位数。有一些初学 的朋友认为波特率是指每秒传输的字节数,如标准9600 会被误认为每秒种可以传送9600 个字节,而实际上它是指每秒可以传送9600 个二进位,而一个字节要8 个二进位。
红外遥控解码
(M50560电视遥控器)一体化接收头输出端拉P3.2(int0),P1为控制输出端。
可以扩展到32路或更多 输出为低电平有效
#include
void InitCom(void);void ComOutChar(unsigned char OutData);
void DelayA(void);void DelayB(void);
void main(void)
{
unsigned int TempCyc;
InitCom();//初始化串口 EA = 1;//允许CPU中断 IT0 = 1;//INT0下降沿有效 EX0 = 1;//开INT0中断;
ComOutChar(1);ComOutChar(5);ComOutChar(3);
do { for(TempCyc=0;TempCyc
P3_7 = 0;for(TempCyc=0;TempCyc
P3_7 = 1;//工作指示LED
} while(1);
} //INT0中断
void INT0Fun(void)interrupt 0 using 2
{
unsigned char IRCode[2], IROK;unsigned int TempCyc, TempCycB, TempCycA;
EX0 = 0;//外部中断0关闭
IROK = 0;DelayA();//延时等待引导码的前半部结束
DelayA();if(!P3_2)//检验前半部是否过早结束,防干扰
{ for(TempCycA=0;TempCycA
{ DelayA();if(P3_2)//检验前半部是否过早结束,防干扰
{ for(TempCyc=0;TempCyc
if(TempCyc
{
for(TempCyc=0;TempCyc
{ while(!P3_2);//等待P3_2拉高,开始位的下部分
DelayB();//这里没设超时,实际应用在多功能的设计时应设超时
IRCode[TempCycA] = IRCode[TempCycA]>>1;if(P3_2)//当延时750us后P3_2仍为高则当前位为1
{ IRCode[TempCycA] = IRCode[TempCycA] | 0x80;for(TempCycB=0;TempCycB99)//之前已延时了750us, 所以超时应大于1.5ms-750us
goto endchk;//这里最大为1ms
} } } else
goto endchk;//超时
} else goto endchk;IROK++;//当自定码和数据码都完成时为2
} } endchk: if(IROK==2)
{
ComOutChar(IRCode[0]);ComOutChar(IRCode[1]);//连接PC串口查看自定义码和数据码
if(IRCode[1]==0x10)//1号键//只演示点亮2只LED,读者可以自行扩展控制别的器件
P1_7 = ~P1_7;
if(IRCode[1]==0x11)//2号键
P1_6 = ~P1_6;for(TempCyc=0;TempCyc
DelayA();//延时
} EX0 = 1;
} //向串口输出一个字符(非中断方式)void ComOutChar(unsigned char OutData)
{
SBUF = OutData;//输出字符
while(!TI);//空语句判断字符是否发完
TI = 0;//清TI
} //串口初始化 晶振为12M 方式1 波特率4800
void InitCom(void)
{
SCON = 0x50;//串口方式1,允许接收
TMOD = 0x21;//定时器1定时方式2,定时0为模式1,16位模式
TCON = 0x40;//设定时器1开始计数 TH1 = 0xF3;//设波特率为4800
TL1 = 0xF3;PCON = 0x80;//波特率加倍控制,SMOD位
RI = 0;//清收发标志
TI = 0;
TR1 = 1;//启动定时器
}
void DelayA(void)
{
unsigned int TempCyc;for(TempCyc=0;TempCyc
} void DelayB(void)
{
unsigned int TempCyc;for(TempCyc=0;TempCyc
}
SJMP
$ 就是“原地踏步走”的意思,即当程序执行的该处时,什么事也不做。一般用来进行延时作用.就是当前行,就是跳转到当前行继续执行。即一个死循环。主要用处就是等待内外的中断。
DA A
该指令的功能是对BCD码的加法结果进行调整。两个压缩型BCD码按二进制数相加之后,必须经此指令的调整才能得到压缩型BCD码的和数。
例如:
执行下面的指令:
MOV A, #86H
ADD A, #47H
结果:(A)=0CDH,CY=0,AC=0
所得结果并不是BCD码,若接着执行以下指令:
DA A 则结果:(A)=33H,CY=1,AC=1
十进制调整是指BCD码的。比如BCD码的21H,表示的就是十进制的21。所以十进制的码里面就没有大于9的。计算机在做加法时,对于BCD码在计算时还按照二进制计算的,所以算完后要进行十进制调整,这就是DA了。
对5个中断源的优先次序安排如下:
外部中断0、定时器/计数器T0溢出中断、外部中断
1、定时器/计数器T1溢出中断、串行口中断。
MCS-51单片机中有1个中断优先级寄存器IP,字节地址为B8H。对于每1个中断源,均可通过对IP的设置来确定其优先等级,置1为高优先级,清0为低优先级。在设计和执行中断程序时必须注意以下问题: a:保护中断现场与恢复现场 b: 中断源有请求 c:CPU开中断
d:多中断程序设计时注意中断优先级的设定