51单片机引脚默认电平中为什么判断电平要判断两次而且其中还要用delay函数?

查看: 5021|回复: 47
发个延时不用delay()函数也不用定时器的无聊瞎折腾程序
本帖最后由 tbkmcjl 于
16:58 编辑
做个小程序,效果要让灯像手机的待机灯那样,快速闪烁0.5秒左右,然后关闭几秒,然后再快速闪烁....如此类推。
看来,需要两个计时的工具了吧。
由于对延时的精确度要求不是太高,但又不希望用那种恶心的delay()函数做死循环来占用程序持续运行的时间。
用定时器其实也可以,但系统里几个定时器都有其他的用途了,不能占用。
事先声明:以下程序仅能用在对时间精度完全没任何要求的场合,比如待机闪烁灯,千万别用来做定时炸弹这样精密的东西。
而且应用前提是不能用delay()函数影响程序运行、也没有任何空闲的定时器资源可供利用的情况下。
综合网友的意见,我增加自我点评:这基本上是一段瞎折腾的程序,延时效果不确切,除非实在好奇想试一下,否则请勿采用
不知道其他人有没有发过类似的程序,这个我不管啦。
#include &reg52.h&
sbit led_locked=P1.2;//定义待机灯的引脚,低电平驱动
unsigned long flash_lei=0;//定义循环次数累计变量
unsigned char phase=0;//定义待机状态灯所处阶段变量
#define tim1 10000& & //阶段1持续时间,数值与实际时间的对应需要调试&&//大约0.5秒,11.0592MHz,1T单片机
#define tim2 200000 // 阶段2持续时间 ,数值与实际时间的对应需要调试 ,这个数字大约几秒,11.0592MHz,1T单片机
void waitflash(void)& &
& & & & & & & && &if(phase==0)& &//状态0,用来瞬间点亮一下灯
& & & & & & & && &{
& & & & & & & && && & flash_lei++;
& & & & & & & & & & & && &if(flash_lei&tim1 )led_locked=0;
& & & & & & & & & & & && &if(flash_lei&=tim1 ) //
& & & & & & & & & & & && &{
& & & & & & & & & & & && &flash_lei=0;
& & & & & & & & & & & && &phase=1;
& & & & & & & & & & & && &}
& & & & & & & && &}
& & & & & & & && &if(phase==1)//状态1,用来长时间关闭灯
& & & & & & & && &{
& & & & & & & && & & & & & led_locked=1;
& & & & & & & & & & & & flash_lei++;
& & & & & & & & & & & & if(flash_lei&tim2 )
& & & & & & & & & & & & {
& & & & & & & & & & & & flash_lei=0;
& & & & & & & & & & & & phase=0;
& & & & & & & & & & & & }
& & & & & & & && &}
void main()&&//主函数
& && &while(1)
& && &//&&其他程序体();& &//你的其他程序
& && &&&waitflash();
& && &简单地说,就是用主函数循环执行一次就将循环次数记录一次,等循环次数到达一定次数后再执行相应的动作,那么只要在实际应用中调试好这个循环次数,就相当于不用任何定时器以及delay函数就实现了一个简单的延时功能了。
& && &由于只是几条简单的条件语句,所以占用程序运算的时间基本上可以忽略不计。当然了,如果主程序比较复杂,有些部分偶尔要占用大量计算时间,可能会影响定时的精度。
& && &这其实就是定时器原理的雏形——计数器,只不过没有独立硬件资源的支持罢了。
这种延时在对延时时间要求不是很严格时是可以这么用的,但要求非常精确的场合是不行的!!
guowanling8061 发表于
这种延时在对延时时间要求不是很严格时是可以这么用的,但要求非常精确的场合是不行的!! ...
上面都说了是不精确的用途啊....
tbkmcjl 发表于
上面都说了是不精确的用途啊....
啊!是我没仔细看!!
本帖最后由 holts2 于
16:01 编辑
这个用来教教新手玩玩是可以的,实际工作中是不行的,那种恶心的delay()函数做死循环来占用程序持续运行的时间好过你这个,用你的方法,延时不是不精确,是不确定,这是糟糕的设计,看不出程序逻辑的严谨。
这样破坏程序整体结构的可能性太大了,得不偿失!
holts2 发表于
这个用来教教新手玩玩是可以的,实际工作中是不行的,那种恶心的delay()函数做死循环来占用程序持续运行的 ...
这个没打算用来控制定时炸弹,只是显示一个待机闪烁的指示灯。&&
应用条件本来就定位于不怕误差的场合
lz的想法是不错的,方法不对,实际上lz的意思是不需要死等延时,否则干不了事情了,希望像操作系统一样延时,看看这个
protothread,咱论坛也有资料
本帖最后由 holts2 于
16:34 编辑
tbkmcjl 发表于
这个没打算用来控制定时炸弹,只是显示一个待机闪烁的指示灯。& &
应用条件本来就定位于不怕误 ...
不是误差,如果是误差它应该是一个固定的差额,你这个永远不知道下次闪烁何时发生,楼上的建议很好,你去看看protothread,那才叫范例。
holts2 发表于
不是误差,如果是误差它应该是一个固定的差额,你这个永远不知道下次闪烁何时发生,楼上的建议很好,你去 ...
我的原标题是“示范”程序。
示范,我的本意不是“榜样”,而是一个例子、一个example的意思。
看来我的中文词用错了,立刻改正。
纯属瞎折腾,误导新手,真还不如delay()呢。
tbkmcjl 发表于
我的原标题是“示范”程序。
示范,我的本意不是“榜样”,而是一个例子、一个example的意思。
看来我的 ...
是的我就是看到范例,火就一下上来了,因为这样的范例会毁了下一代的,如果是交流不应该用范例,如果你的思路好,不写范例它也是范例。
i55x 发表于
纯属瞎折腾,误导新手,真还不如delay()呢。
按照你的建议,我把我的软件里的这个瞎折腾的延时函数替换成delay()了,
但不知道为什么按照你的建议后检测按键的功能就失效了,怎么回事呢?
holts2 发表于
是的我就是看到范例,火就一下上来了,因为这样的范例会毁了下一代的,如果是交流不应该用范例,如果你的 ...
不是专门学语文的,案例、范例、实例、示例、这几个词都是混用的,木办法......英文里就一个example常用
思路是好的
本帖最后由 xizi 于
06:51 编辑
我刚入门时也用过这种方法,但很快就变成过去时了。这种的不确定性太大,功能复杂一点儿,你就死定了。因为任何代码的变动以及任何操作的执行都影响延时。最后你会得到一个让人疯狂的延时效果。一会儿快闪,一会儿慢闪,像幽灵一样。
我感觉楼主发这个还是挺好的,可以促进我们这些新手的思考。
但是我有一点没想明白,为什么前大约0.5秒会闪烁呢?第一次进入程序的时候P1.2会从默认高电平变为1,灯开始亮,在循环里也是等待phase==1的时候才灭灯,不应该是前半段时常为亮,后半段时常为灭么?怎么个闪烁,我看了好一会儿也没看出来。。。
另外这个程序的意义在哪里?delay函数里不也是相加循环,你这个不也是相加循环?小弟不懂,请赐教。。
哎,这也是延时函数,现在终于清楚脑残是咋样的了,哈哈
本帖最后由 tbkmcjl 于
19:05 编辑
gshuang1 发表于
哎,这也是延时函数,现在终于清楚脑残是咋样的了,哈哈
记录一下,心情不好的时候可以举报一下封ID,顺便加莫元。
在帖子中已经说明了这段程序的特殊背景:不准用定时器、不准用延时函数。(为什么那么多不准?当然是有原因的了.至于说用操作系统的,就免了,连定时器都不准用还用操作系统?)
如果你可以提出一种符合以上“不准”又能实现我说的功能的程序,我就不举报”人身攻击“了,也给广大网友学习。
限时24小时内。如果提不出解决方案,又不想被我举报封ID的,就修正犯下的错误。
楼主悲剧了。
继续加油。
其实定时器中搞一个计数器,可以做很多花样的。
tiger5 发表于
楼主悲剧了。
继续加油。
感觉你是以为我不会用定时器内多个变量累加的方式来实现多个延时值功能了........
呵呵。发帖的时候本来就是有个特殊背景的,那个背景就是,上家不让我访问任何硬件级的资源(定时器的路子没了),也不可以影响其他程序结构的运行(延时函数的路子也没了),不能重新开发(时间片操作系统的路子更堵死了)只让我做一个不计较时间精度的闪烁灯以及计算一个特殊的电化学公式的程序。
这个程序要放在主程序里作为一个函数调用。
tbkmcjl 发表于
记录一下,心情不好的时候可以举报一下封ID,顺便加莫元。
呵,别生气,我没说你
楼主的程序挺好。
除了影响程序结构这一点,不过限制这么多的情况下,加一个小功能,也没更好的办法了。
前提条件都写清楚了,就是有人不看,闭眼喷。
gshuang1 发表于
呵,别生气,我没说你
狡辩么?如果你说的不是我,那么就是说其他网友,那样照样是人身攻击,我作为第三方举报,也照样有奖励的。
在帖子中已经说明了这段程序的特殊背景:不准用定时器、不准用延时函数。(为什么那么多不准?当然是有原因的了。至于说用操作系统的,就免了,连定时器都不准用还用操作系统?)
如果你可以提出一种符合以上“不准”又能实现我说的功能的程序,我就不举报”人身攻击“了,也给广大网友学习。
限时24小时内。如果提不出解决方案,又不想被我举报封ID的,就自己修正犯下的错误。
本帖最后由 tbkmcjl 于
19:13 编辑
zhujinliang2124 发表于
楼主的程序挺好。
除了影响程序结构这一点,不过限制这么多的情况下,加一个小功能,也没更好的办法了。
他们估计是没碰到过那些找第三方增加功能,却因为保密原因而不能把主要的源代码程序提供给第三方的案例了。
当然了,这段增加的程序除了延时值调试的功能我是开放外,里面涉及核心算法(一段电化学公式)的代码也是用lib库的方式提供给对方的,这样对方接触不到我的算法,但又能在产品定型的时候调试出符合他们要求的待机灯延时值。
tbkmcjl 发表于
感觉你是以为我不会用定时器内多个变量累加的方式来实现多个延时值功能了........
呵呵。发帖的时候本来 ...
原来是高手。失敬。
tiger5 发表于
原来是高手。失敬。
高手倒不至于。
其实这段程序涉及的技术含量,真是非常低的。
能不能用、好不好用,关键还是看具体要求和应用领域。丰田车够先进了,照样出了自动巡航模式下电脑失控导致车主高速路开了一个多小时的新闻。
就拿我自己也鄙视的delay()函数来说,我就知道国内一个销量很好的产品,开发者用的就是delay()函数来给按键消抖,售后方面也没碰到什么问题。相反那些操作系统都用上了的同类产品,bug一个接一个。
顶楼主,有些人见到好东西却不识货!还自我感觉良好
tbkmcjl 发表于
高手倒不至于。
其实这段程序涉及的技术含量,真是非常低的。
能不能用、好不好用,关键还是看具体要求和 ...
说的对,华丽的技术不一定能做出稳定的产品。
说实话,楼主的这种结构在某些场合是非常有用的,我就拿来用做设置时钟日期等场合,此时液晶的闪烁就靠它来控制,不会影响到按键的响应。因为这个时候往往是在一个函数,一个死循环里做的,所以不会存在破坏结构的说法。
楼上很多网友反映延时要好,请问,你用延时就能解决确定性的问题吗?肯定也不能,任务多的时候照样会使得延时不确定。
人身攻击,就没必要了,也不存在误导新手的事,有它的应用场合。
It is a state machine.
Fairly advanced.
cuizheng_2004 发表于
我感觉楼主发这个还是挺好的,可以促进我们这些新手的思考。
但是我有一点没想明白,为什么前大约0.5秒会闪 ...
#include &reg52.h&
sbit led_locked=P2^0;//定义待机灯的引脚,低电平驱动
unsigned long flash_lei=0;//定义循环次数累计变量
unsigned char phase=0;//定义待机状态灯所处阶段变量
#define tim1 3000& & //阶段1持续时间,数值与实际时间的对应需要调试&&12T的普通51单片机是0.5秒左右
#define tim2 50000 // 阶段2持续时间 ,数值与实际时间的对应需要调试 ,12T的普通51单片机是3秒左右
//上面这两个时间由于是依赖主程序循环操作的,所以要想最终确定效果,就要在所有程序编写完成后,再调试
//以上数值对应的时间
//目前的参数实现的效果是,亮0.5秒后关闭,三秒后再亮0.5秒后关闭.....如此循环下去
void waitflash(void)& &
& && && && && && &if(phase==0)& &//状态0,用来瞬间点亮一下灯
& && && && && && &{
& && && && && && && & flash_lei++;
& && && && && && && && &&&if(flash_lei&tim1 )led_locked=0;
& && && && && && && && &&&if(flash_lei&=tim1 )
& && && && && && && && &&&{
& && && && && && && && &&&flash_lei=0;
& && && && && && && && &&&phase=1;
& && && && && && && && &&&}
& && && && && && &}
& && && && && && &if(phase==1)//状态1,用来长时间关闭灯
& && && && && && &{
& && && && && && && && && &led_locked=1;
& && && && && && && && &flash_lei++;
& && && && && && && && &if(flash_lei&tim2 )
& && && && && && && && &{
& && && && && && && && &flash_lei=0;
& && && && && && && && &phase=0;
& && && && && && && && &}
& && && && && && &}
void main()&&//主函数
& && &while(1)
& && &//&&其他程序体();& &//你的其他程序
& && &&&waitflash();
上面的程序,你可以完整复制下来,然后编译到你硬件上(注意更改LED的端口对应),然后看看运行效果,再对比代码,应该就能看出你要问的问题了。
至于delay函数,它是循环相加,没错,但它是死循环的形式相加,也就是说,在加到尽头后,它才让出CPU给其他程序用。
如果你用delay(10秒)这样,10秒内如果有个按钮按下要报警的话,那么就没办法报警了,因为这10秒根本就没机会去处理按键,都在死循环了。
而我这种是用变量状态的方法,在每个主程序的循环里对变量+1,+1后,程序就让出位置给其他程序,直到其他程序处理一次后,下次主程序循环再+1,也就是一个“不自私”的函数。
当然了,由于delay函数太自私了,它可以独占cpu,自然能实现精确的定时,无论你其他程序怎么变,delay()都能给同样的延时。
而我这个是依赖于主程序循环的速度,如果主程序的程序复杂,那么循环一次速度就慢,调试的数值就要减少点。如果主程序简单,循环速度快,那么调试的数值就要增加点。
如果主程序涉及到特殊情况的交互,比如如果有人按下某个按钮执行了某段程序(如报警发短信程序),那么这次主程序循环要1秒,而下次没人按下按钮,程序也不用发短信,那么主程序循环只要0.7秒了,这样就造成了时间上的误差。
所以我这个程序的目的不是精确延时,而是“制作出有延时效果的待机灯”。
至于定时器,完全可以实现精确、不自私的定时器功能,但开头已经说了,不准用定时器。为什么不准用?因为老板说了算。不准用的原因还有很多,如几个人配合一起开发的情况下,为了保密,是各自为政的,你不能觊觎其他人的硬件资源,只能乖乖完成自己的部分。
这种方法的确没有delay那么恶心,不过效果受很多方面的影响,比如中断,在循环相加的时候,单片机响应中断,此时,变量循环便会停止,但灯的状态依然不变,我还是傾向于定时器,即使定时器被占,但依然可以实现,比如定时器用于1秒的延时,但中断可能只有50ms,你完全可以定义一个变量,每次中断使其加1,你完全可以让负责定时器的人在中断里加上几句话,根本不会影响其他人的程序,也不会知道其他人的硬件资源
tbkmcjl 发表于
#include &reg52.h&
sbit led_locked=P2^0;//定义待机灯的引脚,低电平驱动
。。懂了。主要是楼主最开始说“快速闪烁0.5秒左右,然后关闭几秒,然后再快速闪烁....如此类推。”我就一直在想那0.5m的快速闪烁是怎么个闪法。。另外这种状态的方法我明白了,原来用过一个类似道理的按键扫描。谢谢楼主。
ps:我觉得楼主共享这些很不错,不理解那些说风凉话的啥心态。
tbkmcjl 发表于
按照你的建议,我把我的软件里的这个瞎折腾的延时函数替换成delay()了,
但不知道为什么按照你的建议后 ...
你这是讽刺吧, 看你的回复不是初学者啊.
tbkmcjl 发表于
记录一下,心情不好的时候可以举报一下封ID,顺便加莫元。
在帖子中已经说明了这段程序的特殊背景:不准 ...
大哥,你别再自以为是了!!!一个只能在自己项目用的指示灯函数,美其名曰什么不用定时器不用delay的延时函数,还真以为是什么新发明。
大哥,你做延时处理,不用定时器不行滴,用delay函数的根本就浪费CPU资源,程序功能一多就折腾死人了,还以为是51速度太慢了,要ARM11才能实现那么复杂的功能;利用程序周期来cnt++就更不可取了,一个程序跑一圈需要多长时间你能算得出来?
好吧,楼主已经愤怒了,我随手写一段给你:
#define LED_PIN XXX&&//LED控制口,1为亮,0为灭
uint32 timer1
timer __irq()
{&&//定时器中断函数
& &...&&//记得51要关中断的
& & timer1mscnt++;
& &...//开中断
uint32 GetTime(uint32 start_time, uint32 now_time)
& & if(start_time&now_time)
& && &return (start_time-now_time);
& &&&return (now_time-start_time);
void 手机LED指示灯函数(void)& &//亮0.5秒,灭3秒
& & & & static uint32 time4led=0;
& && && && && &if(LED_PIN==0)
& & & & {&&//手机led灯灭了
& & & & & & & & if(GetTime(time4led,timer1mscnt)&3000)
& & & & & & & & {&&
& & & & & & & & & & & & time4led=timer1
& & & & & & & & & & & & LED_PIN=1;& & & &
& & & & & & & & }& & & & & & & & & & & &
& & & & else
& & & & {&&//LED灯又亮了
& & & & & & & & if(GetTime(time4ledoff,timer1mscnt)&500)
& & & & & & & & {
& & & & & & & & & & & & time4led=timer1
& & & & & & & & & & & & LED_PIN=0;& & & &
& & & & & & & & }& & & &
本帖最后由 tbkmcjl 于
02:38 编辑
gshuang1 发表于
大哥,你别再自以为是了!!!一个只能在自己项目用的指示灯函数,美其名曰什么不用定时器不用delay的延 ...
世界上只有违背老板的话是不行的,其余的没有不行的事情。不用定时器不行,只是你的瞎想。
当然,这种方式是有缺点的。问题是,世界上什么东西没缺点?包括你,在一个版规明说不能人身攻击的论坛动辄就以为发现了别人的命门为了彰显自己的优越感而破口而出“脑残”,难道这就不是你的BUG?
如果老板说不准用定时器,那你就不准用定时器。如果你连这么简单的道理都想不通,那真不知道谁是脑残了。明明说了条件不能用定时器, 结果你贴了一大段自以为高明的程序,又把定时器用上了,这又是如何的“高明”?
你这种做法就跟老板让你写一个RC充电时间计算当A/D的程序,你跑去跟老板说:”不行的,这种做法很多缺点的,一定要用专门的A/D芯片才科学!“
定时器这么简单的功能,连一个初学者买个学习板也能学会,如果能用,我还跟你费劲扯什么?用用脑子好不好。
本帖最后由 tbkmcjl 于
03:04 编辑
背西风酒旗 发表于
这种方法的确没有delay那么恶心,不过效果受很多方面的影响,比如中断,在循环相加的时候,单片机响应中断 ...
你说的这种情况在初学者的程序里可能会出现,就是大段程序放在中断里,这样中断响应需要占用相当长的时间。
比如初学者写串口程序,就容易犯这个缺点,数据包接收、协议处理、校验,都放串口中断服务程序里了,这样肯定会极大地影响主程序循环。
不过有经验的人一般都只会在中断程序里放一个标志,进入中断的时候就改变这个标志,然后在主程序里判断这个标志再决定是否执行这个程序,效率就大大提高,所以不会出现你说的那种迟迟没回去主程序循环而导致状态永远改变不了的情景。
至于让别人加定时器语句,世界上并不是什么事都由自己决定的。当年有个案例,硬件上加一个电阻就能解决的事情,人家就不肯去处理,结果要在软件抗干扰的问题上多花了一个星期的时间。再说了,定时器又不是什么值钱的东西,随便一个学生买块学习板用几分钟就能掌握的功能,如果真的有机会用上,我还求之不得呢,将waitflash()函数直接放到定时器中断服务函数里面,再将TIM1 TIM2设置对应中断时间的数值,这就实现精确定时了。能这么做的话,这个帖子早就不存在了。
为啥多数都是批判呢。其实这个方法对于要求不高的场合不错的啊,主程序还可以做其他事情,中断什么的也没问题阿。事先计算好大概多久调用一次就ok了。
而且,这是个状态机的雏形,在复杂的操作系统中也是必用的技术之一。
小日本的一种商业化的单片机操作系统是这样的, 用timer 定时 比如100ms, 然后定义几个task,比如100ms 定时运行的task,500ms 定时运行的task,1s定时运行的task。 然后在timer里面做count 增量, 在main里面 根据count值判断循环调用这几个task, 大概就是这样,几年前用的了。这个的前提是你的task体耗时不太多。 不过用了这种的话,可以把delay这种东西省掉,
猜到了& && && &
其实 lz这就是一种思路 换一种角度解决下小问题。挺好的
我觉得如果说限定特殊条件的话&&这个思路还是可取的
编辑原因 :修改错别字
tbkmcjl 发表于
世界上只有违背老板的话是不行的,其余的没有不行的事情。不用定时器不行,只是你的瞎想。
哥,我是被你这种标题党骗进来的,不然我懒得去理你,明明是一个根本无法移值毫无参考意义的LED指示灯程序,就说是一个不用定时器不用delay的延时函数,还真以为这创新颠覆了大自然规律。
老板要求不要用定时器?连老板都要干涉这些的,建议你赶紧走人好了。试问还有MCU没有定时器的吗?有MCU用就有定时器用,如果老板叫我不能用定时器,我真的会说不用它我不爽,你准备招人吧,项目大局可以完全按照的思路去设计,实现细节你不该干涉。老板要求不能用AD芯片我可以理解,是为了降低成本(10几年前的事了吧,如今大多数MCU都带AD了),但是你既然已经用了MCU,有定时器都不用,不仅没有降低成本,反而增加了研发投入,钻牛角尖啊?如果你说的“老板”实质是项目负责人,你就跟他说你做好你的资料就行了,管太多了。
本帖最后由 tbkmcjl 于
10:57 编辑
gshuang1 发表于
哥,我是被你这种标题党骗进来的,不然我懒得去理你,明明是一个根本无法移值毫无参考意义的LED指示灯程 ...
你直接说:如果加了这么多条件,你没法完成任务,不就行了?
扯这么远干嘛?
一个标题就把你骗进来了,老板的话你却可以想这样那样的理由去推脱(实际上完全可以实现),你说你到底是什么性格?如果老板说成本太敏感,只能花一毛钱的温度探头,让你用二极管测温,你是不是也要说,标定困难啊,不如换
个PT100好了。如果都是你这种思维,市场上90%的产品都不会存在了。
另外,目前市场上大部分国产温控器,用的还是RC充电时间算A/D的,如果你这都不知道的话,就走到现实世界看看吧,别总是活在自己想象的理想世界中。还单片机自带A/D呢,你知道电阻+电阻多少毛钱一个?
带有A/D的单片机型号又要贵多少元?
再给个题目你:如果老板让你改软件代码,来降低电路板去实验室测试时的EMI指标,你是不是也说“不可能!!怎么可能呢?一定要改电路板设计!一定要买低EMI的单片机!”&&
事实上,一个实习生就完成了这个任务。还是在几个工程师都想得头晕的情况下。其实这几个工程师都知道这个知识,就是压根想不到利用起来。
看来引起了大讨论!速度围观!
看過& &路過!!
阿莫电子论坛, 原"中国电子开发网"
, 原www.ourdev.cn, 原www.ouravr.com单片机中用写delay函数做延时和用定时器做延时有什么区别?_百度知道
单片机中用写delay函数做延时和用定时器做延时有什么区别?
利用循环处理的方式实现的延时,并不精确。1、当中断发生时,CPU被打断先执行中断服务。导致执行时间变长。2、使用高级语言时,编译的结果受优化等级等多方面因素干扰,难以计算。3、在有MMU和Cache的MCU中,指令的执行速度还会受到缓存的影响。利用定时器延时:1、因为定时器累加与CPU无关,即使中断,仍然会保持计数。不受上述问题影响2、缺点需要占用定时器资源。3、功耗也会高一些
采纳率:73%
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。豆丁微信公众号
君,已阅读到文档的结尾了呢~~
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
基于51单片机的智能温控电扇设计
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='http://www.docin.com/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口51单片机教程(从原理开始基于汇编)及单片机C语言教程
我的图书馆
51单片机教程(从原理开始基于汇编)及单片机C语言教程
1.单片机可以做什么?&&&目前单片机渗透到我们生活的各个领域,几乎很难找到哪个领域没有单片机的踪迹。小到电话,玩具,手机,各类刷卡机,电脑键盘,彩电,冰箱,空调,电磁炉,大到汽车,工业自动控制,机器人,导弹导航装置,甚至是美国的火星车,这些设备里面都含有一个或者多个单片机 。单片机的数量不仅远超过PC机,甚至比人类的数量还要多。因此,单片机的学习、开发与应用将造就一大批软硬件工程师。&2.&学习单片机需要哪些基本条件?&& 模拟电路,数字电路基础,对C语言或汇编语言有一定的了解。当然,这些也可以在学习过程中掌握。对此这套教程均有讲解.& 硬件条件的话:电脑一台,实验开发平台一套。3、单片机的结构&&& 一台能够工作的计算机要有这样几个部份构成:CPU(进行运算、控制)、RAM(数据存储-内存)、ROM(程序存储)、输入/输出设备(例如:串行口、并行输出口等)。在个人计算机上这些部份被分成若干块芯片,安装一个称之为主板的印刷线路板上。而在单片机中,这些部份,全部被做到一块集成电路芯片中了,所以就称为单片机。&& 单片机是一种控制芯片,一个微型的计算机,而加上晶振,存储器,地址锁存器,逻辑门,七段译码器(显示器),按钮(类似键盘),扩展芯片,接口等那是单片机系统。& 天!PC中的CPU一块就要卖几千块钱,这么多东西做在一起,还不得买个天价!再说这块芯片也得非常大了。& 不,价格并不高,从1元人民币到几十元人民币,体积也不大,一般用40脚封装,当然功能多一些单片机也有引脚比较多的,如68引脚,功能少的只有10多个或20多个引脚,有的甚至只8只引脚。为什么会这样呢?& 功能有强弱,打个比方,市场上面有的组合音响一套才卖几百块钱,可是有的一台功放机就要卖好几千。另外这种芯片的生产量很大,技术也很成熟,51系列的单片机已经做了十几年,所以价格就低了。& 既然如此,单片机的功能肯定不强,干吗要学它呢?话不能这样说,实际工作中并不是任何需要计算机的场合都要求计算机有很高的性能,一个控制电冰箱温度的计算机难道要动用一台台式电脑不成?应用的关键是看是否够用,是否有很好的性能价格比。所以8051出来十多年,依然没有被淘汰,还在不断的发展中。4、MCS51单片机和、89C51等的关系&& 我们平常老是讲8051,又有什么8031,现在又有89C51,89s51它们之间究竟是什么关系?&& MCS51是指由美国INTEL公司(对了,就是大名鼎鼎的INTEL)生产的一系列单片机的总称,这一系列单片机包括了好些品种,如,,等,其中8051是最早最典型的产品,该系列其它单片机都是在8051的基础上进行功能的增、减、改变而来的,所以人们习惯于用8051来称呼MCS51系列单片机,而8031是前些年在我国最流行的单片机,所以很多场合会看到8031的名称。INTEL公司将MCS51的核心技术授权(卖)给了很多其它公司,所以有很多公司在做以8051为核心的单片机,当然,功能或多或少有些改变,以满足不同的需求,其中89C51就是这几年在我国非常流行的单片机,以后我们将用89C51单片机来完成一系列的教学实验。并会不定期在&&更新和修正,如有错误请在本文评论中批评指正.5.该学汇编还是学C语言?&&& 掌握用C语言单片机编程很重要,毕竟C容易写,可读性强,可移植性强。但是光会C语言也还是不够的,你还得了解汇编,实际上不是了解汇编指令,而是为了了解单片机的体系结构。可以肯定的说,最好的C语言单片机工程师都是从汇编走出来的编程者,所以我以为学习单片机,先学点汇编是必要的,但在以后的开发中主要用C语言,所以本系列教程将兼顾汇编和c两种语言.学习中有问题可联系笔者:2课:单片机引脚介绍&单片机的40个引脚大致可分为4类:电源、时钟、控制和I/O引脚。⒈ 电源:&&&&&&&&& ⑴ VCC - 芯片电源,接+5V;&&&&&&&& ⑵ VSS - 接地端;⒉ 时钟:XTAL1、XTAL2 - 晶体振荡电路反相输入端和输出端。⒊ 控制线:控制线共有4根,&&⑴ ALE/PROG:地址锁存允许/片内EPROM编程脉冲&&&&&&① ALE功能:用来锁存P0口送出的低8位地址&&&&& ② PROG功能:片内有EPROM的芯片,在EPROM编程期间,此引脚输入编程脉冲。& ⑵ PSEN:外ROM读选通信号。&&⑶ RST/VPD:复位/备用电源。&&&& & ① RST(Reset)功能:复位信号输入端。&& & &&② VPD功能:在Vcc掉电情况下,接备用电源。& ⑷ EA/Vpp:内外ROM选择/片内EPROM编程电源。&&&& &&① EA功能:内外ROM选择端。&&&&& &② Vpp功能:片内有EPROM的芯片,在EPROM编程期间,施加编程电源Vpp。⒋ I/O线&&80C51共有4个8位并行I/O端口:P0、P1、P2、P3口,共32个引脚。P3口还具有第二功能,用于特殊信号输入输出和控制信号(属控制总线)。&&&& 拿到一块芯片,想要使用它,首先必须要知道怎样连线,我们用的一块称之为89C51的芯片,下面我们就看一下如何给它连线。1、&电源:这当然是必不可少的了。单片机使用的是5V电源,其中正极接40管脚,负极(地)接20管脚。&2、&振蒎电路:单片机是一种时序电路,必须供给脉冲信号才能正常工作,在单片机内部已集成了振荡器,使用晶体振荡器,接18、19脚。只要买来晶体震荡器,电容,连上就能了,按图1接上即可。&3、&复位管脚:按图1中画法连好,至于复位是何含义及为何需要复要复位,在单片机功能中介绍。&4、&EA管脚:EA管脚接到正电源端。 至此,一个单片机就接好,通上电,单片机就开始工作了。& 我们的第一个任务是要用单片机点亮一只发光二极管LED,显然,这个LED必须要和单片机的某个管脚相连,不然单片机就没法控制它了,那么和哪个管脚相连呢?单片机上除了刚才用掉的5个管脚,还有35个,我们将这个LED和1脚相连。(见图1,其中R1是限流电阻)& 按照这个图的接法,当1脚是高电平时,LED不亮,只有1脚是低电平时,LED才发亮。因此要1脚我们要能够控制,也就是说,我们要能够让1管脚按要求变为高或低电平。即然我们要控制1脚,就得给它起个名字,总不能就叫它一脚吧?叫它什么名字呢?设计51芯片的INTEL公司已经起好了,就叫它P1.0,这是规定,不能由我们来更改。&& 图1& 名字有了,我们又怎样让它变'高'或变'低'呢?叫人做事,说一声就能,这叫发布命令,要计算机做事,也得要向计算机发命令,计算机能听得懂的命令称之为计算机的指令。让一个管脚输出高电平的指令是SETB,让一个管脚输出低电平的指令是CLR。因此,我们要P1.0输出高电平,只要写SETB P1.0,要P1.0输出低电平,只要写&CLR P1.0就能了。& 现在我们已经有办法让计算机去将P10输出高或低电平了,但是我们怎样才能计算机执行这条指令呢?总不能也对计算机也说一声了事吧。要解决这个问题,还得有几步要走。第一,计算机看不懂SETB&CLR之类的指令,我们得把指令翻译成计算机能懂的方式,再让计算机去读。计算机能懂什么呢?它只懂一样东西——数字。因此我们得把SETB P1.0变为(D2H,90H ),把CLR P1.0变为 (C2H,90H ),至于为什么是这两个数字,这也是由51芯片的设计者--INTEL规定的,我们不去研究。第二步,在得到这两个数字后,怎样让这两个数字进入单片机的内部呢?这要借助于一个硬件工具"编程器"。如果你还不知道是什么是编程器,我来介绍一下,就是把你在电脑上写出来来的代码用汇编器等编译器生成的一个目标烧写到单片机的eprom里面去的工具,80c51这种类型的单片机编程是一件很麻烦的事情,必要要先装到编程器上编程后才能在设备上使用,而目前最新的AT89s51或者STC89C51单片机能支持在线编程(isp)功能,不用拔出来利用简单的电路就可以实现把代码写入单片机内部,本站有详细的下载。&下面我们来实战一下:下图(图2)所示的软件名字叫keil&,是用来编写程序代码并生成一个可以写入到单片机芯片的Hex文件(我们人要控制单片机只能用汇编语言或者C语言而单片机不认识怎么办呢?所以keil这个软件就把程序语言转换成了一种特定格式的Hex文件,只要把这个文件下载到单片机芯片单片机就会按我们程序的思路来运行)图2&(keil编程软件)&—&&&&&&&& &图3(下载软件)&—&&&&& 好了…… ,我们把&SETB P1.0&这条汇编语句用keil软件编译生成Hex文件,然后用图3所示的软件和图1所示的硬件(用串口连接电脑)下载到我们已经做好的电路板上的单片机芯片里去(图省事的话建议直接买个开发板),然后接通电源……什么?灯不亮?这就对了,因为我们写进去的指令就是让P10输出高电平,灯当然不亮,要是亮就错了。所以要将keil编缉区的内容改为CLR P1.0,重新编译生成Hex文件,重新下载,接电,好,灯亮了。因为我们写入的Hex就是让P10输出低电平的指令。这样我们看到,硬件电路的连线没有做任何改变,只要改变写入单片机中的内容,就能改变电路的输出效果。3课:单片机存储器结构单片机内部存储结构分析&&& 我们来思考一个问题,当我们在编程器中把一条指令写进单片要内部,然后取下单片机,单片机就可以执行这条指令,那么这条指令一定保存在单片机的某个地方,并且这个地方在单片机掉电后依然可以保持这条指令不会丢失,这是个什么地方呢?这个地方就是单片机内部的只读存储器即ROM(READ ONLY MEMORY)。为什么称它为只读存储器呢?刚才我们不是明明把两个数字写进去了吗?原来在89C51中的ROM是一种电可擦除的ROM,称为FLASH ROM,刚才我们是用的编程器,在特殊的条件下由外部设备对ROM进行写的操作,在单片机正常工作条件下,只能从那面读,不能把数据写进去,所以我们还是把它称为ROM。&& 数的本质和物理现象:我们知道,计算机能进行数学运算,这可令我们非常的难以理解,计算机吗,我们虽不了解它的组成,但它总只是一些电子元器件,怎么能进行数学运算呢?我们做数学题如37+45是这样做的,先在纸上写37,然后在下面写45,然后大脑运算,最后写出结果,运算的原材料:37、45和结果:82都是写在纸上的,计算机中又是放在什么地方呢?为了解决这个问题,先让我们做一个实验:这里有一盏灯,我们知道灯要么亮,要么不亮,就有两种状态,我们能用’0’和’1’来代替这两种状态,规定亮为’1’,不亮为’0’。现在放上两盏灯,一共有几种状态呢?我们列表来看一下:状态表达0 00 11 01 1& 请大家自已写上3盏灯的情况000 001 010 011 100 101 110 111& 我们来看,这个000,001,101 不就是我们学过的的二进制数吗?本来,灯的亮和灭只是一种物理现象,可当我们把它们按一按的次序排更好后,灯的亮和灭就代表了数字了。让我们再抽象一步,灯为什么会亮呢?看电路1,是因为输出电路输出高电平,给灯通了电。因此,灯亮和灭就能用电路的输出是高电平还是低电平来替代了。这样,数字就和电平的高、低联系上了。(请想一下,我们还看到过什么样的类似的例程呢?(海军之)灯语、旗语,电报,甚至红、绿灯)什么是位:&& 通过上面的实验我们已经知道:一盏灯亮或者说一根线的电平的高低,能代表两种状态:0和1。实际上这就是一个二进制位,因此我们就把一根线称之为一“位”,用BIT表示。什么是字节:& 一根线能表于0和1,两根线能表达00,01,10,11四种状态,也就是能表于0到3,而三根能表达0-7,计算机中常常用8根线放在一起,同时计数,就能表过到0-255一共256种状态。这8根线或者8位就称之为一个字节(BYTE)。不要问我为什么是8根而不是其它数,因为我也不知道。(计算机世界是一本人造的世界,不是自然界,很多事情你无法问为什么,只能说:它是一种规定,大家在以后的学习过程中也要注意这个问题)存储器的工作原理:1、存储器构造&& 存储器就是用来存放数据的地方。它是利用电平的高低来存放数据的,也就是说,它存放的实际上是电平的高、低,而不是我们所习惯认为的1234这样的数字,这样,我们的一个谜团就解开了,计算机也没什么神秘的吗。图2图3& 让我们看图2。单片机里面都有这样的存储器,这是一个存储器的示意图:一个存储器就象一个个的小抽屉,一个小抽屉里有八个小格子,每个小格子就是用来存放“电荷”的,电荷通过与它相连的电线传进来或释放掉,至于电荷在小格子里是怎样存的,就不用我们操心了,你能把电线想象成水管,小格子里的电荷就象是水,那就好理解了。存储器中的每个小抽屉就是一个放数据的地方,我们称之为一个“单元”。& 有了这么一个构造,我们就能开始存放数据了,想要放进一个数据12,也就是,我们只要把第二号和第三号小格子里存满电荷,而其它小格子里的电荷给放掉就行了(看图3)。可是问题出来了,看图2,一个存储器有好多单元,线是并联的,在放入电荷的时候,会将电荷放入所有的单元中,而释放电荷的时候,会把每个单元中的电荷都放掉,这样的话,不管存储器有多少个单元,都只能放同一个数,这当然不是我们所希望的,因此,要在结构上稍作变化,看图2,在每个单元上有个控制线,我想要把数据放进哪个单元,就给一个信号这个单元的控制线,这个控制线就把开关打开,这样电荷就能自由流动了,而其它单元控制线上没有信号,所以开关不打开,不会受到影响,这样,只要控制不一样单元的控制线,就能向各单元写入不一样的数据了,同样,如果要某个单元中取数据,也只要打开对应的控制开关就行了。2、存储器译码& 那么,我们怎样来控制各个单元的控制线呢?这个还不简单,把每个单元元的控制线都引到集成电路的外面不就行了吗?事情可没那么简单,一片27512存储器中有65536个单元,把每根线都引出来,这个集成电路就得有6万多个脚?不行,怎么办?要想法减少线的数量。我们有一种办法称这为译码,简单介绍一下:一根线能代表2种状态,2根线能代表4种状态,3根线能代表几种,256种状态又需要几根线代表?8种,8根线,所以65536种状态我们只需要16根线就能代表了。(图4)3、存储器的选片及总线的概念&& 至此,译码的问题解决了,让我们再来关注另外一个问题。送入每个单元的八根线是用从什么地方来的呢?它就是从计算机上接过来的,一般地,这八根线除了接一个存储器之外,还要接其它的器件,如图4所示。这样问题就出来了,这八根线既然不是存储器和计算机之间专用的,如果总是将某个单元接在这八根线上,就不好了,比如这个存储器单元中的数值是0FFH另一个存储器的单元是00H,那么这根线到底是处于高电平,还是低电平?岂非要打架看谁历害了?所以我们要让它们分离。办法当然很简单,当外面的线接到集成电路的管脚进来后,不直接接到各单元去,中间再加一组开关(参考图4 )就行了。平时我们让开关关闭着,如果确实是要向这个存储器中写入数据,或要从存储器中读出数据,再让开关接通就行了。这组开关由三根引线选择:读控制端、写控制端和片选端。要将数据写入片中,先选中该片,然后发出写信号,开关就合上了,并将传过来的数据(电荷)写入片中。如果要读,先选中该片,然后发出读信号,开关合上,数据就被送出去了。注意图4,读和写信号同时还接入到另一个存储器,但是由于片选端不一样,所以虽有读或写信号,但没有片选信号,所以另一个存储器不会“误会”而开门,造成冲突。那么会不一样时选中两片芯片呢?只要是设计好的系统就不会,因为它是由计算控制的,而不是我们人来控制的,如果真的出现同时出现选中两片的情况,那就是电路出了故障了,这不在我们的讨论之列。&& 从上面的介绍中我们已经看到,用来传递数据的八根线并不是专用的,而是很多器件大家共用的,所以我们称之为数据总线,总线英文名为BUS,总即公交车道,谁者能走。而十六根地址线也是连在一起的,称之为地址总线。半导体存储器的分类& 按功能能分为只读和随机存取存储器两大类。所谓只读,从字面上理解就是只能从里面读,不能写进去,它类似于我们的书本,发到我们手回之后,我们只能读里面的内容,不能随意更改书本上的内容。只读存储器的英文缩写为ROM(READ ONLY MEMORY)所谓随机存取存储器,即随时能改写,也能读出里面的数据,它类似于我们的黑板,我能随时写东西上去,也能用黑板擦擦掉重写。随机存储器的英文缩写为RAM(Random Access Memory)这两种存储器的英文缩写一定要记牢。注意:所谓的只读和随机存取都是指在正常工作情况下而言,也就是在使用这块存储器的时候,而不是指制造这块芯片的时候。不然,只读存储器中的数据是怎么来的呢?其实这个道理也很好理解,书本拿到我们手里是不能改了,能当它还是原材料——白纸的时候,当然能由印刷厂印上去了。顺便解释一下其它几个常见的概念。&&PROM,称之为可编程存储器。这就象我们的练习本,买来的时候是空白的,能写东西上去,可一旦写上去,就擦不掉了,所以它只能用写一次,要是写错了,就报销了。(现在已经被淘汰)&&EPROM,称之为紫外线擦除的可编程只读存储器。它里面的内容写上去之后,如果觉得不满意,能用一种特殊的办法去掉后重写,这就是用紫外线照射,紫外线就象“消字灵”,能把字去掉,然后再重写。当然消的次数多了,也就不灵光了,所以这种芯片能擦除的次数也是有限的——几百次吧。(现在已经被淘汰)&&&EEPROM,也叫&E2PROM称之为电可擦可编程只读存储器,它和EEPROM类似,写上去的东西也能擦掉重写,但它要方便一些,不需要光照了,只要用电就能擦除或者重新改写数据,所以就方便许多,而且寿命也很长(几万到几十万次不等)。&&FLASH,称之为闪速存储器,属于EEPROM的改进产品,它的最大特点是必须按块(Block)擦除(每个区块的大小不定,不同厂家的产品有不同的规格), 而EEPROM则可以一次只擦除一个字节(Byte)。FLASH现在常用于大容量存储,比如u盘& 再次强调,这里的所有的写都不是指在正常工作条件下。不管是PROM还是EPROM,它们的写都要有特殊的条件,一般我们用一种称之为“编程器”的设备来做这项工作,一旦把它装到它的工作位置,就不能随便改写了。4课:第一个单片机小程序作者:佚名&&&来源:本站原创&&&点击数:&187661&&&更新时间:日&& 【字体:&&】  上一次我们的程序实在是没什么用,要灯亮还要重写一下片子,下面我们要让灯持续地闪烁,这就有一定的实用价值了,比如能把它当成汽车上的一个信号灯用了。怎样才能让灯持续地闪烁呢?实际上就是要灯亮一段时间,再灭一段时间,也就是说要P10持续地输出高和低电平。怎样实现这个要求呢?请考虑用下面的指令是否可行:SETB P1.0CLR P1.0&……这是不行的,有两个问题,第一,计算机执行指令的时间很快,执行完SETB P1.0后,灯是灭了,但在极短时间(微秒级)后,计算机又执行了CLR P1.0指令,灯又亮了,所以根本分辨不出灯曾灭过。第二,在执行完CLR P10后,不会再去执行SETB P1.0指令,所以以后再也没有机会让灭了。  为了解决这两个问题,我们能做如下设想,第一,在执行完SETB P1.0后,延时一段时间(几秒或零点几秒)再执行第二条指令,就能分辨出灯曾灭过了。第二在执行完第二条指令后,让计算机再去执行第一条指令,持续地在原地兜圈,我们称之为"循环",这样就能完成任务了。以下先给出程序(后面括号中的数字是为了便于讲解而写的,实际不用输入):;主程序:LOOP:&SETB P1.0&& &;(1)熄灭灯LCALL DELAY&&&&&&&&;(2)延时一段时间CLR P1.0&&&&&&&&&&&;(3)点亮灯LCALL DELAY&&&&&&&&;(4)延时一段时间AJMP LOOP&&&&&&&&&&;(5)跳转到第一句LOOP处;以下子程序DELAY:&MOV R7,#250&;(6)D1:&MOV R6,#250&&&&;(7)D2:&DJNZ R6,D2&&&&&;(8)&&&&&& DJNZ R7,D1&&;(9)&&&&&&&RET&&&&&&&&&;(10)END&&&&&&&&&&&&&&&&;(11)本例按上面的设想分析一下前面的五条指令。  第一条是让灯灭,第二条应当是延时,第三条是让灯亮,第四条和第二条一模一样,也是延时,第五条应当是转去执行第一条指令。第二和第四条实现的原理稍后谈,先看第五条,AJMP是一条指令,意思是转移,往什么地方转移呢?后面跟的是LOOP,看一下,什么地方还有LOOP,对了,在第一条指令的前面有一个LOOP,所以很直观地,我们能认识到,它要转到第一条指令处。这个第一条指令前面的LOOP被称之为标号,它的用途就是给这一行起一个名字,便于使用。是否一定要给它起名叫LOOP呢?当然不是,起什么名字,完全由编程序的人决定,能称它为A,X等等,当然,这个时候,第五条指令AJMP后面的名字也得跟着改了。  第二条和第四条指令的用途是延时,它是怎样实现的呢?指令的形式是LCALL,这条指令称为调用子程序指令,看一下指令后面跟的是什么,DELAY,找一下DELAY,在第六条指令的前面,显然,这也是一个标号。这条指令的作用是这样的:当执行LCALL指令时,程序就转到LCALL后面的标号所标定的程序处执行,如果在执行指令的过程中遇到RET指令,则程序就返回到LCALL指令的下面的一条指令继续执行,从第六行开始的指令中,能看到确实有RET指令。在执行第二条指令后,将转去执行第6条指令,而在执行完6,7,8,9条指令后将遇到第10条令:RET,执行该条指令后,程序将回来执行第三条指令,即将P10清零,使灯亮,然后又是第四条指令,执行第四条指令就是转去执行第6,7,8,9,10条指令,然后回来执行第5条指令,第5条指令就是让程序回到第1条开始执行,如此周而复始,灯就在持续地亮、灭了。  在标号DELAY标志的这一行到RET这一行中的所有程序,这是一段延时程序,大概延时零点几秒,至于具体的时间,以后我们再学习如何计算。 程序的最后一行是END,这不是一条指令,它只是告诉我们程序到此结束,它被称为"伪指令"。&& 单片机内部结构分析:为了知道延时程序是如何工作的,我们必需首先了解延时程序中出现的一些符号,就从R1开始,R1被称之为工作寄存器。什么是工作寄存器呢?让我们从现实生活中来找找答案。如果出一道数学题:123+567,让你回答结果是多少,你会马上答出是690,再看下面一道题:123+567+562,要让你要上回答,就不这么不难了吧?我们会怎样做呢?如果有张纸,就不难了,我们先算出123+567=690,把690写在纸上,然后再算690+562得到结果是1252。这其中1252是我们想要的结果,而690并非我们所要的结果,但是为了得到最终结果,我们又不得不先算出690,并记下来,这其实是一个中间结果,计算机中做运算和这个类似,为了要得到最终结果,一般要做很多步的中间结果,这些中间结果要有个地方放才行,把它们放哪呢?放在前面提到过的ROM中能吗?显然不行,因为计算机要将结果写进去,而ROM是不能写的,所以在单片机中另有一个区域称为RAM区(RAM是随机存取存储器的英文缩写),它能将数据写进去。 特别地,在MCS-51单片机中,将RAM中分出一块区域,称为工作寄存器区,上面程序用到的R6,R7就是在这个区里面,这我们会在有详细的介绍。其实如果我们用C语言来写程序的话用根本不用了解工作寄存器这个概念了因为C编译器会自动处理.看上面的程序如果用c来写就是#include &at89x52.h&void&DELAY()&& //延时函数{ unsigned char i,j;&&& for(i=0;i&250;i++)&&& {&&&&&&& for(j=0;j&200;j++);&&& }&}void main()&//程序从这里开始执行{while(1)&//这句的作用就是反复的执行下面这个{}中包含的4句&&& {&&&& P1_0=1;&&//(1)熄灭灯&&&&&DELAY();&//(2)延时一段时间&&&& P1_0=0;&&//(3)点亮灯&&&&&DELAY();&//(4)延时一段时间&&& }}&& 在汇编例子中程序是从第一条语句开始执行的,而c不同在c语言里程序是从main()&这里开始执行的,关于(1)(2)(3)(4) 这几句的解释和上面的汇编一样,不再敖述。循环部分这里是用了一个while(1) 语句下面打了一个大括号,这样大括号中的这4条语句就会按(1)-&(2)-&(3)-&(4) -&(1)-&(2)-&(3)-&(4) -&(1)-&(2)-&(3)-&(4)……永远不停的执行下去。这样灯就会持续的亮灭再亮再灭实现了闪烁效果,关于延时函数下节课再叙。有什么不懂的大家可以在下面提问5课:单片机延时程序分析作者:佚名&&&来源:本站原创&&&点击数:&130026&&&更新时间:日&& 【字体:&&】上一次课中,我们已经知道,程序中的符号R7、R6是代表了一个个的RAM单元,是用来放一些数据的,下面我们再来看一下其它符号的含义。DELAY:&&&MOV R7,#250   ;(6)D1:&&&MOV R6,#250&  && ;(7)D2:&&&DJNZ R6,D2&  &  ;(8)DJNZ R7,D1   &&&&&&&& ;(9)   RET&      &&&&&&&  ;(10)〈单片机延时程序〉&&MOV:这是一条指令,意思是传递数据。说到传递,我们都很清楚,传东西要从一本人的手上传到另一本人的手上,也就是说要有一个接受者,一个传递者和一样东西。从指令MOV R7,#250中来分析,R7是一个接受者,250是被传递的数,传递者在这条指令中被省略了(注意:并不是每一条传递指令都会省的,事实上大部份数据传递指令都会有传递者)。它的意义也很明显:将数据250送到R7中去,因此执行完这条指令后,R7单元中的值就应当是250。在250前面有个#号,这又是什么意思呢?这个#就是用来说明250就是一个被传递的东西本身,而不是传递者。那么MOV R6,#250是什么意思,应当不用分析了吧。&&DJNZ:这是另一条指令,我们来看一下这条指令后面跟着的两个东西,一个是R6,一个是D2,R6我们当然已知是什么了,查一下D2是什么。D2在本行的前面,我们已学过,这称之为标号。标号的用途是什么呢?就是给本行起一个名字。DJNZ指令的执行过程是这样的,它将其后面的第一个参数中的值减1,然后看一下,这个值是否等于0,如果等于0,就往下执行,如果不等于0,就转移,转到什么地方去呢?可能大家已猜到了,转到第二个参数所指定的地方去(请大家用自已的话讲一下这条语句是怎样执行的)。本条指令的最终执行结果就是,在原地转圈250次。& 执行完了DJNZ R6,D2之后(也就是R6的值等于0之后),就会去执行下面一行,也就是DJNZ R7,D1,请大家自行分析一下这句话执行的结果。(转去执行MOV R6,#250,同时R7中的值减1),最终DJNZ R6,D2这句话将被执行250*250=62500次,执行这么多次同一条指令干吗?就是为了延时。一个问题:如果在R6中放入0,会有什么样的结果。二、时序分析:& 前面我们介绍了延时程序,但这还不完善,因为,我们只知道DJNZ R6,D2这句话会被执行62500次,但是执行这么多次需要多长时间呢?是否满足我们的要求呢?我们还不知道,所以下面要来解决这个问题。& 先提一个问题:我们学校里什么是最重要的。(铃声)校长能出差,老师能休息,但学校一日无铃声必定大乱。整个学校就是在铃声的统一指挥下,步调一致,统一协调地工作着。这个铃是按一定的时间安排来响的,我们能称之为“时序��时间的次序”。一个由人组成的单位尚且要有一定的时序,计算机当然更要有严格的时序。事实上,计算机更象一个大钟,什么时候分针动,什么时候秒针动,什么时候时针动,都有严格的规定,一点也不能乱。计算机要完成的事更复杂,所以它的时序也更复杂。& 我们已知,计算机工作时,是一条一条地从ROM中取指令,然后一步一步地执行,我们规定:计算机访问一次存储器的时间,称之为一个机器周期。这是一个时间基准,好象我们人用“秒”作为我们的时间基准一样,为什么不干脆用“秒”,多好,很习惯,学下去我们就会知道用“秒”反而不习惯。一个机器周期包括12个时钟周期。下面让我们算一下一个机器周期是多长时间吧。设一个单片机工作于12M晶体震荡器,它的时钟周期是1/12(微秒)。它的一个机器周期是12*(1/12)也就是1微秒。(请计算一个工作于6M晶体震荡器的单片机,它的机器周期是多少)。& MCS-51单片机的所有指令中,有一些完成得比较快,只要一个机器周期就行了,有一些完成得比较慢,得要2个机器周期,还有两条指令要4个机器周期才行。这也不难再解,不是吗?我让你扫地的执行要完成总得比要你完成擦黑板的指令时间要长。为了恒量指令执行时间的长短,又引入一个新的概念:指令周期。所谓指令周期就是指执行一条指令的时间。INTEL对每一条指令都给出了它的指令周期数,这些数据,大部份不需要我们去记忆,但是有一些指令是需要记住的,如DJNZ指令是双周期指令。& 下面让我们来计算刚才的延时。首先必须要知道晶体震荡器的频率,我们设所用晶体震荡器为12M,则一个机器周期就是1微秒。而DJNZ指令是双周期指令,所以执行一次要2个微秒。一共执行62500次,正好125000微秒,也就是125毫秒。&如果你对上面几句还是不懂请看这里的详解:&关于用C语言写延时函数的详解请看这:练习:设计一个延时100毫秒的延时程序。要点分析:1、一个单元中的数是否能超过255。2、如何分配两个数。三、复位电路一、复位方式⒈ 复位条件&&&&&&& RST引脚保持2个机器周期以上的高电平。⒉ 复位电路〈单片机复位电路〉⒊ 复位后CPU状态&&&&&& PC:& 0000H &&&&&&& TMOD: 00H&&&&&& Acc: 00H&&&&&&&&&& TCON: 00H&&&&&& B:& &00H&& &&&& & &TH0:& 00H&&&&&& PSW: 00H&&&&&&&&& &TL0:& 00H&&&&&& SP:& 07H&&&&&&&&&& TH1:& 00H&&&&&& DPTR:0000H&&&&&&&&&TL1:& 00H&&&&&& P0~P3:FFH&&&&&&&&&SCON: 00H&&&&&& IP:×××00000B&&& SBUF: 不定&&&&&& IE:0××00000B&&&& PCON: 0×××0000B& 任何单片机在工作之前都要有个复位的过程,复位是什么意思呢?它就象是我们上课之前打的预备铃。预备铃一响,大家就自动地从操场、其它地方进入教室了,在这一段时间里,是没有老师干预的,对单片机来说,是程序还没有开始执行,是在做准备工作。显然,准备工作不需要太长的时间,复位只需要5ms的时间就能了。如何进行复位呢?只要在单片机的RST管脚上加上高电平,就能了,按上面所说,时间不少于5ms。为了达到这个要求,能用很多种办法,这里供给一种供参考,见图1。实际上,我们在上一次实验的图中已见到过了。& 这种复位电路的工作原理是:通电时,电容两端相当于是短路,于是RST管脚上为高电平,然后电源通过电阻对电容充电,RST端电压慢慢下降,降到一定程序,即为低电平,单片机开始正常工作。6课:单片机并行口结构作者:佚名&&&来源:本站原创&&&点击数:&105699&&&更新时间:日&& 【字体:&&】&& 上两次我们做过两个实验,都是让P1.0这个管脚使灯亮,我们能设想:既然P1.0能让灯亮,那么其它的管脚可不能呢?看一下,它是8031单片机管脚的说明,在P1.0旁边有P1.1,P1.2….P1.7,它们是否都能让灯亮呢?除了以P1开头的外,还有以P0,P2,P3开头的,数一下,一共是32个管脚,前面我们以学过7个管脚,加上这32个这39个了。它们都以P字开头,只是后面的数字不一样,它们是否有什么联系呢?它们能不能都让灯亮呢?在我们的实验板上,除了P10之外,还有P11 -& P17都与LED相连,下面让我们来做一个实验,程序如下:MAIN: MOV P1,#0FFHLCALL DELAYMOV P1,#00HLCALL DELAYLJMP MAINDELAY:MOV R7,#250D1: MOV R6,#250D2: DJNZ R6,D2DJNZ R7,D1RETEND& 将这段程序烧写到单片机中,结果如何?通电以后我们能看到8只LED全部在闪动。因此,P10-&P17是全部能点亮灯的。事实上,凡以P开头的这32个管脚都是能点亮灯的,也就是说:这32个管脚都能作为输出使用,如果不用来点亮LED,能用来控制继电器,能用来控制其它的执行机构。程序分析:这段程序和前面做过的程序比较,只有两处不一样:第一句:原来是SETB P1.0,现在改为MOV P1,#0FFH,第三句:原来是CLR P1.0,现在改为MOV P1.0,#00H。从中能看出,P1是P1.0-&P1.7的全体的代表,一个P1就表示了所有的这八个管脚了。当然用的指令也不一样了,是用MOV指令。为什么用这条指令?看图2,我们把P1作为一个整体,就把它当作是一个存储器的单元,对一个单元送进一个数能用MOV指令。二、第四个实验除了能作为输出外,这32个管脚还能做什么呢?下面再来做一个单片机实验,源程序如下:MAIN: MOV P3,#0FFHLOOP: MOV A,P3MOV P1,ALJMP LOOP& 先看一下这个实验的结果:所有灯全部不亮,然后我按下一个按钮,第(1)个灯亮了,再按下另一个按钮,第(2)个灯亮了,松开按钮灯就灭了。从这个实验现象结合电路来分析一下程序。& 从硬件电路的连线能看出,有四个按钮被接入到P3口的P32,P33,P34,P35。第一条指令的用途我们能猜到:使P3口全部为高电平。第二条指令是MOV A,P3,其中 MOV已经知道,是送数的意思,这条指令的意思就是将P3口的数送到A中去,我们能把A当成是一个中间单元(看图3),第三句话是将A中的数又送到P1口去,第四句话是循环,就是持续地重复这个过程,这我们已见过。当我们按下第一个按钮时,第(3)只灯亮了,所以P12口应当输出是低电平,为什么P12口会输出低电平呢?我们看一下有什么被送到了P1口,只有从P3口进来的数送到A,又被送到了P1口,所以,肯定是P3口进来的数使得P12位输出电平的。P3口的P32位的按钮被按下,使得P32位的电平为低,通过程序,又使P12口输出低电平,所以P3口起来了一个输入的作用。验证:按第二、三、四个按钮,同时按下2个、3个、4个按钮都能得到同样的结论,所以P3口确实起到了输入作用,这样,我们能看到,以P字开头的管脚,不仅能用作输出,还能用作输入,其它的管脚是否能呢?是的,都能。这32个管脚就称之为并行口,下面我们就对并行口的结构作一个分析,看一下它是怎样实现输入和输出的。并行口结构分析:1、输出结构&并行口结构图&& 先看P1口的一位的结构示意图(只画出了输出部份):从图中能看出,开关的打开和合上代表了管脚输出的高和低,如果开关合上了,则管脚输出就是低,如果开关打开了,则输出高电平,这个开关是由一根线来控制的,这根数据总线是出自于CPU,让我们回想一下,数据总线是一根大家公用的线,很多的器件和它连在一起,在不一样的时候,不一样的器件当然需要不一样的信号,如某一时刻我们让这个管脚输出高电平,并要求保持若干时间,在这段时间里,计算机当然在忙个不停,在与其它器件进行联络,这根控制线上的电平未必能保持原来的值不变,输出就会发生变化了。怎么解决这个问题呢?我们在存储器一节中学过,存储器中是能存放电荷的,我们不妨也加一个小的存储器的单元,并在它的前面加一个开关,要让这一位输出时,就把开关打开,信号就进入存储器的单元,然后马上关闭开关,这样这一位的状态就被保存下来,直到下一次命令让它把开关再打开为止。这样就能使这一位的状态与别的器件无关了,这么一个小单元,我们给它一个很形象的名字,称之为“锁存器”。2、输入结构& 这是并行口的一位的输出结构示意图,再看,除了输出之外,还有两根线,一根从外部管脚接入,另一根从锁存器的输出接出,分别标明读管脚和读锁存器。这两根线是用于从外部接收信号的,为什么要两根呢?原来,在51单片机中输入有两种方式,分别称为‘读管脚’和‘读锁存器’,第一种方式是将管脚作为输入,那是真正地从外部管脚读进输入的值,第二种方式是该管脚处于输出状态时,有时需要改变这一位的状态,则并不需要真正地读管脚状态,而只是读入锁存器的状态,然后作某种变换后再输出。& 请注意输入结构图,如果将这一根引线作为输入口使用,我们并不能保证在任何时刻都能得到正确的结果(为什么?)参考图2输入示意图。接在外部的开关如果打开,则应当是输入1,而如果闭合开关,则输入0,但是,如果单片机内部的开关是闭合的,那么不管外部的开关是开还是闭,单片机接受到的数据都是0。可见,要让这一端口作为输入使用,要先做一个‘准备工作’,就是先让内部的开关断开,也就是让端口输出‘1’才行。正因为要先做这么一个准备工作,所以我们称之为“准双向I/O口”。& 以上是P1口的一位的结构,P1口其它各位的结构与之相同,而其它三个口:P0、P2、P3则除入作为输入输出口之外还有其它用途,所以结构要稍复杂一些,但其用于输入、输出的结构是相同的。看图()。对我们来说,这些附加的功能不必由我们来控制,所以我们就不去关心它了。& 你如果你装了office软件的话 你可以打开这个ppt动态演示文件看下加深对并口的理解:作者(胡琴)点评:根据本文的评论来看,大家感觉开始越来越难学了,其实并行口的原理我们并不需要去深入理解,不需要把内部的工作原理及电路都完全弄懂,我们是要用单片机不是设计单片机芯片的,所以我们只需要会用就行了,比如说 给一个 CLR P3.5 这个指令就是为了让P3.5输出0,这一条 MOV P3,#0FFH 就是让整个P3口(从P3.0-P3.7)全部都输出1.7课:单片机的特殊功能寄存器作者:佚名&&&来源:本站原创&&&点击数:&92730&&&更新时间:日&& 【字体:&&】&& 通过前面的学习,我们已知单片机的内部有ROM、有RAM、有并行I/O口,那么,除了这些东西之外,单片机内部究竟还有些什么,这些个零碎的东西怎么连在一起的,让我们来对单片机内部的寄存器作一个完整的功能分析吧!&&下图中我们能看出,在51单片机内部有一个CPU用来运算、控制,有四个并行I/O口,分别是P0、P1、P2、P3,有ROM,用来存放程序,有RAM,用来存放中间结果,此外还有定时/计数器,串行I/O口,中断系统,以及一个内部的时钟电路。在一个51单片机的内部包含了这么多的东西。 & 对上面的图进行进一步的分析,我们已知,对并行I/O口的读写只要将数据送入到对应I/O口的锁存器就能了,那么对于定时/计数器,串行I/O口等怎么用呢?在单片机中有一些独立的存储单元是用来控制这些器件的,被称之为特殊功能寄存器(SFR)。事实上,我们已接触过P1这个特殊功能寄存器了,还有哪些呢?看下表1符号地址功能介绍BF0HB寄存器ACCE0H累加器PSWD0H程序状态字IPB8H中断优先级控制寄存器P3B0HP3口锁存器IEA8H中断允许控制寄存器P2A0HP2口锁存器SBUF99H串行口锁存器SCON98H串行口控制寄存器P190HP1口锁存器TH18DH定时器/计数器1(高8位)TH08CH定时器/计数器1(低8位)TL18BH定时器/计数器0(高8位)TL08AH定时器/计数器0(低8位)TMOD89A定时器/计数器方式控制寄存器TCON88H定时器/计数器控制寄存器DPH83H数据地址指针(高8位)DPL82H数据地址指针(低8位)SP81H堆栈指针P080HP0口锁存器PCON87H电源控制寄存器& 表1&&&特殊功能寄存器地址映象表(一)&&&特殊功能寄存器地址映象表(二)&&&特殊功能寄存器地址映象表(三)&下面,我们介绍一下几个常用的SFR,看图2。1、ACC:累加器,常常用A表示。这是个什么东西,可不能从名字上理解,它是一个寄存器,而不是一个做加法的东西,为什么给它这么一个名字呢?或许是因为在运算器做运算时其中一个数一定是在ACC中的缘故吧。它的名字特殊,身份也特殊,稍后我们将学到指令,能发现,所有的运算类指令都离不开它。2、B:一个寄存器。在做乘、除法时放乘数或除数,不做乘除法时,随你怎么用。3、PSW:程序状态字。这是一个很重要的东西,里面放了CPU工作时的很多状态,借此,我们能了解CPU的当前状态,并作出对应的处理。它的各位功能请看表2D7D6D5D4D3D2D1D0CYACF0RS1RS0OV P&&&&& 表2PSW也称为标志寄存器,了解这个对于了解单片机原理非常的重要,存放各有关标志。其结构和定义如下:&& 下面我们逐一介绍sfr各位的用途(1)CY:进位标志。用于表示Acc.7有否向更高位进位。8051中的运算器是一种8位的运算器,我们知道,8位运算器只能表示到0-255,如果做加法的话,两数相加可能会超过255,这样最高位就会丢失,造成运算的错误,怎么办?最高位就进到这里来。这样就没事了。例:78H+97H(10111)(2)AC:辅助进位标志也叫半进位标志。& 用于表示Acc.3有否向Acc.4进位例:57H+3AH(11010)(3)F0:用户标志位,由我们(编程人员)决定什么时候用,什么时候不用。(4)RS1、RS0:工作寄存器组选择位。这个我们已知了。&&&&&&&&&&&&& RS1、RS0 = 00 —— 0区(00H~07H)&&&&&&&&&&&&& RS1、RS0 = 01 —— 1区(08H~0FH)&&&&&&&&&&&&& RS1、RS0 = 10 —— 2区(10H~17H)&&&&&&&&&&&&& RS1、RS0 = 11 —— 3区(18H~1FH)&(5)0V:溢出标志位。 表示Acc在有符号数算术运算中的溢出,什么是溢出我们稍后再谈吧。(6)P:奇偶校验位:它用来表示ALU运算结果中二进制数位“1”的个数的奇偶性。若为奇数,则P=1,不然为0。例:某运算结果是78H(),显然1的个数为偶数,所以P=0。4、DPTR(DPH、DPL):数据指针,能用它来访问外部数据存储器中的任一单元,如果不用,也能作为通用寄存器来用,由我们自已决定如何使用。16位,由两个8位寄存器DPH、DPL组成。主要用于存放一个16位地址,作为访问外部存储器(外RAM和ROM)的地址指针。5、P0、P1、P2、P3:这个我们已经知道,是四个并行输入/输出口的寄存器。它里面的内容对应着管脚的输出。6、SP:堆栈指针。(专用于指出堆栈顶部数据的地址。) && 堆栈介绍:日常生活中,我们都注意到过这样的现象,家里洗的碗,一只一只摞起来,最晚放上去的放在最上面,而最早放上去的则放在最下面,在取的时候正好相反,先从最上面取,这种现象我们用一句话来概括:“先进后出,后进先出”。请大家想想,还有什么地方有这种现象?其实比比皆是,建筑工地上堆放的砖头、材料,仓库里放的货物,都是“先进后出,后进先出”,这实际是一种存取物品的规则,我们称之为“堆栈”。&& 在单片机中,我们也能在RAM中构造这样一个区域,用来存放数据,这个区域存放数据的规则就是“先进后出,后进先出”,我们称之为“堆栈”。为什么需要这样来存放数据呢?存储器本身不是能按地址来存放数据吗?对,知道了地址的确就能知道里面的内容,但如果我们需要存放的是一批数据,每一个数据都需要知道地址那不是麻烦吗?如果我们让数据一个接一个地放置,那么我们只要知道第一个数据所在地址单元就能了(看图2)如果第一个数据在27H,那么第二、三个就在28H、29H了。所以利用堆栈这种办法来放数据能简化操作&& 那么51中堆栈什么地方呢?单片机中能存放数据的区域有限,我们不能够专门分配一块地方做堆栈,所以就在内存(RAM)中开辟一块地方,用于堆栈,但是用内存的哪一块呢?还是不好定,因为51是一种通用的单片机,各人的实际需求各不相同,有人需要多一些堆栈,而有人则不需要那么多,所以怎么分配都不合适,怎样来解决这个问题?分不好干脆就不分了,把分的权利给用户(编程者),根据自已的需要去定吧,所以51单片机中堆栈的位置是能变化的。而这种变化就体现在SP中值的变化,看图2,SP中的值等于27H不就相当于是一个指针指向27H单元吗?当然在真正的51机中,开始指针所指的位置并非就是数据存放的位置,而是数据存放的前一个位置,比如一开始指针是指向27H单元的,那么第一个数据的位置是28H单元,而不是27H单元,为什么会这样,我们在学堆栈命令时再说明。其它的SFR,我们在用到时再介绍。作者点评:这一课可能比较难懂了,看大家的评论很多都这样说,其实并不需要把上面的这些东西都死记硬背在脑子里面,这是很难的,我们只需要理解他的工作原理,一次看不懂就要多看几次,等到要写程序的时候,我们再把这些表格拿出来查阅所以建议你把本页保存为书签.笔者在开始学习单片机的时候也是这样做的.8课:单片机寻址方式与指令系统作者:佚名&&&来源:本站原创&&&点击数:&75402&&&更新时间:日&& 【字体:&&】&& 通过前面的学习,我们已经了解了单片机内部的结构,并且也已经知道,要控制单片机,让它为我们干学,要用指令,我们已学了几条指令,但很零散,从现在开始,我们将要系统地学习8051单片机的指令部份。一、概述1、指令的格式&& 我们已知,要让计算机做事,就得给计算机以指令,并且我们已知,计算机很“笨”,只能懂得数字,如前面我们写进机器的75H,90H,00H等等,所以指令的第一种格式就是机器码格式,也说是数字的形式。但这种形式实在是为难我们人了,太难记了,于是有另一种格式,助记符格式,如MOV P1,#0FFH,这样就好记了。 这两种格式之间的关系呢,我们不难理解,本质上它们完全等价,只是形式不一样而已。2、汇编& 我们写指令使用汇编格式,而计算机和单片机只懂机器码格式,所以要将我们写的汇编格式的指令转换为机器码格式,这种转换有两种办法:手工汇编和机器汇编。手工汇编实际上就是查表,因为这两种格式纯粹是格式不一样,所以是一一对应的,查一张表格就行了。不过手工查表总是嫌麻烦,所以就有了计算机软件,用计算机软件来替代手工查表,这就是机器汇编。二、单片机的寻址& 让我们先来复习一下我们学过的一些指令:MOV P1,#0FFH,MOV R7,#0FFH这些指令都是将一些数据送到对应的位置中去,为什么要送数据呢?第一个因为送入的数能让灯全灭掉,第二个是为了要实现延时,从这里我们能看出来,在用单片机的编程语言编程时,经常要用到数据的传递,事实上数据传递是单片机编程时的一项重要工作,一共有28条指令(单片机共111条指令)。下面我们就从数据传递类指令开始吧。& 分析一下MOV P1,#0FFH这条指令,我们不难得出结论,第一个词MOV是命令动词,也就是决定做什么事情的,MOV是MOVE少写了一个E,所以就是“传递”,这就是指令,规定做什么事情,后面还有一些参数,分析一下,数据传递必须要有一个“源”也就是你要送什么数,必须要有一个“目的”,也就是你这个数要送到什么地方去,显然在上面那条单片机指令中,要送的数(源)就是0FFH,而要送达的地方(目的地)就是P1这个寄存器。在数据传递类指令中,均将目的地写在指令的后面,而将源写在最后。& 这条指令中,送给P1是这个数本身,换言之,做完这条指令后,我们能明确地知道,P1中的值是0FFH,但是并不是任何时候都能直接给出数本身的。例如,在我们前面给出的单片机延时程序例是这样写的:MAIN: SETB P1.0     ;(1)   LCALL DELAY ;(2)    CLR P1.0      ;(3)   LCALL DELAY   ;(4)    AJMP MAIN    ;(5);以下子程序DELAY: MOV R7,#250   ;(6)D1: MOV R6,#250   ;(7)D2: DJNZ R6,D2    ;(8)   DJNZ R7,D1   ;(9)   RET        ;(10)   END        ;(11)表1-----------------------------------------------------&MAIN: SETB P1.0     ;(1)   MOV 30H,#255    LCALL DELAY ;    CLR P1.0      ;(3)    MOV 30H,#200    LCALL DELAY   ;(4)    AJMP MAIN    ;(5);以下子程序DELAY: MOV R7,30H   ;(6)D1: MOV R6,#250   ;(7)D2: DJNZ R6,D2    ;(8)   DJNZ R7,D1   ;(9)   RET        ;(10)   END        ;(11)表2 这样一来,我每次调用延时程序延时的时间都是相同的(大致都是0.13S),如果我提出这样的要求:灯亮后延时时间为0.13S灯灭,灯灭后延时0.1秒灯亮,如此循环,这样的程序还能满足要求吗?不能,怎么办?我们能把延时程序改成这样(见表2):调用则见表2中的主程,也就是先把一个数送入30H,在子程序中R7中的值并不固定,而是根据30H单元中传过来的数确定。这样就能满足要求。&&& 从这里我们能得出结论,在数据传递中要找到被传递的数,很多时候,这个数并不能直接给出,需要变化,这就引出了一个概念:如何寻找操作数,我们把寻找操作数所在单元的地址称之为寻址。在这里我们直接使用数所在单元的地址找到了操作数,所以称这种办法为直接寻址。除了这种办法之外,还有一种,如果我们把数放在工作寄存器中,从工作寄存器中寻找数据,则称之为寄存器寻址。例:MOV A,R0就是将R0工作寄存器中的数据送到累加器A中去。提一个问题:我们知道,工作寄存器就是内存单元的一部份,如果我们选择工作寄存器组0,则R0就是RAM的00H单元,那么这样一来,MOV A,00H,和MOV A,R0不就没什么区别了吗?为什么要加以区别呢?的确,这两条指令执行的结果是完全相同的,都是将00H单元中的内容送到A中去,但是执行的过程不一样,执行第一条指令需要2个周期,而第二条则只需要1个周期,第一条指令变成最终的目标码要两个字节(E5H 00H),而第二条则只要一个字节(E8h)就能了。&& 这么斤斤计较!不就差了一个周期吗,如果是12M的晶体震荡器的话,也就1个微秒时间了,一个字节又能有多少?&& 不对,如果这条指令只执行一次,也许无所谓,但一条指令如果执行上1000次,就是1毫秒,如果要执行1000000万次,就是1S的误差,这就很可观了,单片机做的是实时控制的事,所以必须如此“斤斤计较”。字节数同样如此。再来提一个问题,现在我们已知,寻找操作数能通过直接给的方式(立即寻址)和直接给出数所在单元地址的方式(直接寻址),这就够了吗?看这个问题,要求从30H单元开始,取20个数,分别送入A累加器。&& 就我们目前掌握的办法而言,要从30H单元取数,就用MOV A,30H,那么下一个数呢?是31H单元的,怎么取呢?还是只能用MOV A,31H,那么20个数,不是得20条指令才能写完吗?这里只有20个数,如果要送200个或2000个数,那岂不要写上200条或2000条命令?这未免太笨了吧。为什么会出现这样的状况?是因为我们只会把地址写在指令中,所以就没办法了,如果我们不是把地址直接写在指令中,而是把地址放在另外一个寄存器单元中,根据这个寄存器单元中的数值决定该到哪个单元中取数据,比如,当前这个寄存器中的值是30H,那么就到30H单元中去取,如果是31H就到31H单元中去取,就能解决这个问题了。怎么个解决法呢?既然是看的寄存器中的值,那么我们就能通过一定的办法让这里面的值发生变化,比如取完一个数后,将这个寄存器单元中的值加1,还是执行同一条指令,可是取数的对象却不一样了,不是吗。通过例程来说明吧。MOV R7,#20   MOV R0,#30HLOOP:MOV A,@R0   INC R0   DJNZ R7,LOOP&&& 这个例程中大部份指令我们是能看懂的,第一句,是将立即数20送到R7中,执行完后R7中的值应当是20。第二句是将立即数30H送入R0工作寄存器中,所以执行完后,R0单元中的值是30H,第三句,这是看一下R0单元中是什么值,把这个值作为地址,取这个地址单元的内容送入A中,此时,执行这条指令的结果就相当于MOV A,30H。第四句,没学过,就是把R0中的值加1,因此执行完后,R0中的值就是31H,第五句,学过,将R7中的值减1,看是否等于0,不等于0,则转到标号LOOP处继续执行,因此,执行完这句后,将转去执行MOV A,@R0这句话,此时相当于执行了MOV A,31H(因为此时的R0中的值已是31H了),如此,直到R7中的值逐次相减等于0,也就是循环20次为止,就实现了我们的要求:从30H单元开始将20个数据送入A中。这也是一种寻找数据的办法,由于数据是间接地被找到的,所以就称之为间址寻址。注意,在间址寻址中,只能用R0或R1存放等寻找的数据。9课:单片机数据传递类指令作者:佚名&&&来源:本站原创&&&点击数:&62526&&&更新时间:日&& 【字体:&&】单片机数据传递类指令(3)以直接地址为目的操作数的指令MOV direct,A&例:&MOV 20H,AMOV direct,Rn MOV 20H,R1MOV direct1,direct2 MOV 20H,30HMOV direct,@Ri MOV 20H,@R1MOV direct,#data MOV 20H,#34H(4)以间接地址为目的操作数的指令MOV @Ri,A&例:MOV @R0,AMOV @Ri,direct MOV @R1,20HMOV @Ri,#data MOV @R0,#34H(5)十六位数的传递指令MOV DPTR,#data168051是一种8位机,这是唯一的一条16位立即数传递指令,其功能是将一个16位的立即数送入DPTR中去。其中高8位送入DPH,低8位送入DPL。例:MOV DPTR,#1234H,则执行完了之后DPH中的值为12H,DPL中的值为34H。反之,如果我们分别向DPH,DPL送数,则结果也一样。如有下面两条指令:MOV DPH,#35H,MOV DPL,#12H。则就相当于执行了MOV DPTR,#3512H。数据传递类指令综合练习:给出每条指令执行后的结果上机练习:MOV 23H,#30HMOV 12H,#34HMOV R0,#23HMOV R7,#22HMOV R1,12HMOV A,@R0MOV 34H,@R1(23h)=30h(12h)=34h(R0)=23H(R7)=22H(R1)=12H(A)=30H(34H)=34HMOV 45H,34HMOV DPTR,#6712HMOV 12H,DPHMOV R0,DPLMOV A,@R0(45H)=34H(DPTR)=6712H(12H)=67H(R0)=12H(A)=67H说明:用括号括起来代表内容,如(23H)则代表内部RAM23H单元中的值,(A)则代表累加器A单元中的值。进入DOS状态,进入WAVE所在的目录,例D:\WAVE键入MCS51,出现如下画面&单片机数据传递指令&图1按File-&Open,出现对话框后,在Name处输入一个文件名(见图2),如果是下面列表中已存在的,则打开这个文件,如果不存在这个文件,则新建一个文件(见图3)图2在空白处将上面的程序输入。见图4。用ALT+A汇编通过。用F8即可单步执行,在执行过程中注意观察屏幕左边的工作寄存器及A累加器中的值的变化。图4内存中值的变化在此是看不到的,可以用如下方法观察(看图5):将鼠标移到DATA,双击,则光标进入此行,此时可以键盘上的上下光标键上下翻动来观察内存值的变化。本行的最前面DATA后面的数据代表的是“一段”的开始地址,如现在为20H,再看屏幕的最上方,数字从0到F,显示两者相加就等于真正的地址值,如现在图上所示的内存20H、21H、22H、23H中的值分别是FBH&、0EH、E8H、30H。图56、当运行完程序后,即进入它的反汇编区,不是我们想要的东西。为了再从头开始,可以用CTRL+F2功能键复位PC值。注意此时不会看到原来的窗口,为看到原来的窗口,请用ALT+4或ALT+5等来切换。当然以上操作也可以菜单进行。CTRL+F2是程序复位,用RUN菜单。窗口用WINDOWS菜单。此次大家就用用熟这个软件吧,说实话,我并不很喜欢它,操作起来不方便,但给我的机器只能上这个,没办法,下次再给网友单独介绍一个好一点的吧。现在最好的是keil 这个软件本站有详细的教程请看:&&&&&&10课:单片机数据传送类指令作者:佚名&&&来源:本站原创&&&点击数:&58041&&&更新时间:日&& 【字体:&&】单片机的累加器A与片外RAM之间的数据传递类指令MOVX A,@RiMOVX @Ri,AMOVX A,@DPTRMOVX @DPTR,A说明:1)在51系列单片机中,与外部存储器RAM打交道的只能是A累加器。所有需要传送入外部RAM的数据必需要通过A送去,而所有要读入的外部RAM中的数据也必需通过A读入。在此我们能看出内外部RAM的区别了,内部RAM间能直接进行数据的传递,而外部则不行,比如,要将外部RAM中某一单元(设为0100H单元的数据)送入另一个单元(设为0200H单元),也必须先将0100H单元中的内容读入A,然后再传送到0200H单元中去。要读或写外部的RAM,当然也必须要知道RAM的地址,在后两条单片机指令中,地址是被直接放在DPTR中的。而前两条指令,由于Ri(即R0或R1)只是一个8位的寄存器,所以只供给低8位地址。因为有时扩展的外部RAM的数量比较少,少于或等于256个,就只需要供给8位地址就够了。使用时应当首先将要读或写的地址送入DPTR或Ri中,然后再用读写命令。例:将单片机外部RAM中100H单元中的内容送入外部RAM中200H单元中。MOV DPTR,#0100HMOVX A,@DPTRMOV DPTR,#0200HMOVX @DPTR,A程序存储器向累加器A传送指令MOVC A,@A+DPTR&&&& 本指令是将ROM中的数送入A中。本指令也被称为单片机查表指令,常用此指令来查一个已做好在ROM中的表格&&&&& 说明:此条指令引出一个新的寻址办法:变址寻址。本指令是要在ROM的一个地址单元中找出数据,显然必须知道这个单元的地址,这个单元的地址是这样确定的:在执行本指令立脚点DPTR中有一个数,A中有一个数,执行指令时,将A和DPTR中的数加起为,就成为要查找的单元的地址。查找到的结果被放在A中,因此,本条指令执行前后,A中的值不一定相同。例:有一个数在R0中,要求用查表的办法确定它的平方值(此数的取值范围是0-5)MOV DPTR,#TABLEMOV A,R0MOVC A,@A+DPTRTABLE: DB 0,1,4,9,16,25设R0中的值为2,送入A中,而DPTR中的值则为TABLE,则最终确定的ROM单元的地址就是TABLE+2,也就是到这个单元中去取数,取到的是4,显然它正是2的平方。其它数据也能类推。&标号的真实含义:从这个地方也能看到另一个问题,我们使用了标号来替代具体的单元地址。事实上,标号的真实含义就是地址数值。在这里它代表了,0,1,4,9,16,25这几个数据在ROM中存放的起点位置。而在以前我们学过的如LCALL DELAY单片机指令中,DELAY 则代表了以DELAY为标号的那段程序在ROM中存放的起始地址。事实上,CPU正是通过这个地址才找到这段程序的。能通过以下的例程再来看一看标号的含义:MOV DPTR,#100HMOV A,R0MOVC A,@A+DPTRORG 0100H.DB 0,1,4,9,16,25如果R0中的值为2,则最终地址为100H+2为102H,到102H单元中找到的是4。这个能看懂了吧?那为什么不这样写程序,要用标号呢?不是增加疑惑吗?如果这样写程序的话,在写程序时,我们就必须确定这张表格在ROM中的具体的位置,如果写完程序后,又想在这段程序前插入一段程序,那么这张表格的位置就又要变了,要改ORG 100H这句话了,我们是经常需要修改程序的,那多麻烦,所以就用标号来替代,只要一编译程序,位置就自动发生变化,我们把这个麻烦事交给计算机��指我们用的电脑去做了。堆栈操作PUSH directPOP direct第一条指令称之为推入,就是将direct中的内容送入堆栈中,第二条指令称之为弹出,就是将堆栈中的内容送回到direct中。推入指令的执行过程是,首先将SP中的值加1,然后把SP中的值当作地址,将direct中的值送进以SP中的值为地址的RAM单元中。例:MOV SP,#5FHMOV A,#100MOV B,#20PUSH ACCPUSH B则执行第一条PUSH ACC指令是这样的:将SP中的值加1,即变为60H,然后将A中的值送到60H单元中,因此执行完本条指令后, 内存60H单元的值就是100,同样,执行PUSH B时,是将SP+1,即变为61H,然后将B中的值送入到61H单元中,即执行完本条指令后,61H单元中的值变为20。POP指令的在单片机中执行是这样的,首先将SP中的值作为地址,并将此地址中的数送到POP指令后面的那个direct中,然后SP减1。接上例:POP BPOP ACC则执行过程是:将SP中的值(现在是61H)作为地址,取61H单元中的数值(现在是20),送到B中,所以执行完本条指令后B中的值是20,然后将SP减1,因此本条指令执行完后,SP的值变为60H,然后执行POP ACC,将SP中的值(60H)作为地址,从该地址中取数(现在是100),并送到ACC中,所以执行完本条指令后,ACC中的值是100。这有什么意义呢?ACC中的值本来就是100,B中的值本来}

我要回帖

更多关于 单片机检测高电平输入 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信