单片机定时中断太频繁_单片机外部中断,定时器中断,串口中断 - CSDN
  • 最近在忙一个项目,使用了STM32F030的单片机,定时器用系统定时器,每10us中断一次。在程序少的时候,没有发现死机情况,但是随着功能的丰富,经常出现死机问题,具体表现为while(1)循环无法执行,但是中断函数...

    最近在忙一个项目,使用了STM32F030的单片机,定时器用系统定时器,每10us中断一次。在程序少的时候,没有发现死机情况,但是随着功能的丰富,经常出现死机问题,具体表现为while(1)循环无法执行,但是中断函数正常或者按键不起作用,程序直接跑分。这时我认为是死机,即程序跑分,但是有时中断正常,这就证明程序依然正常运行,可能是其它原因造成。

    首先,我怀疑是I2C读写DSP出错,于是屏蔽掉DSP初始化,刚开始正常,多开机几次就又不正常了,所以排除写DSP。

    接着,我怀疑读Flash出错,也发现仿真时,有时读Flash,就假死在那儿,于是就又把读Flash屏蔽掉,发现程序又正常了许多,但是多开机几次就又出现假死,所以又排除写Flash。但是仿真时,却说发现程序假死在一些初始化时的延时函数里。于是我仔细检查延时函数是否出错,发现没有错误,这就导致很奇怪了。

    然后,我把优化等级调到-O3,发现出现了局部变量没有初始化就被使用和数组越界的情况(程序是拷贝别人的,没有仔细检查),以为这次可以解决了,在修改掉Bug后,程序假死改善了许多,但是很不幸,多级开关机后,发现程序又假死了,问题还不在这里,不过顺便解决了两个Bug。

    最后,在同事的帮助下,开始检查系统定时器的中断函数,发现中断是每10us触发一次,并且在10us的中断函数里有调用了好几个函数,并且还有对IO口的操作,所以很有可能是由于中断过于频繁,系统被拖死了,导致不停的进入中断(这次中断还没有执行完成,下一次中断事件就产生了),导致while(1)循环根本没有时间执行,就出现了之前的假死现象。于是,在把中断时间改为1ms后,并把函数的操作放在了50ms的中断函数里处理,假死问题得以解决。

    总结:

    1、单片机不能把中断设置的太频繁,否则可能会消耗掉过多的MCU资源,导致while(1)执行的很慢,系统运行出现问题。

          单片机会随着温度的升高,速度回稍微变慢,这也是为什么程序在中断太频繁时,有时运行正常,有时又不正常的原因。

          中断函数里最好不要放函数调用,比较耗费时间,如果确实要用,就快进快出,并且不能在低于1ms的中断函数里调用函数。

          中断函数里最好不要操作IO口,检测IO口的状态可以,但是最好不要写IO口,否则可能会消耗掉过多的MCU资源,导致系统假死。

          局部变量一定要记得在定义时初始化,否则可能会出现不初始化就使用,导致系统异常;数组一定要仔细检查是否可能会出现越界,如果越界了怎么处理,要有保护措施。

    2、从发现问题,到解决问题,用了将近两天的时间,不能不说这是一种浪费。首先,在发现问题后,没有先做深入的分析,而是不停的怀疑这里有问题,而没有用“证据”来证明其一定有问题,这不说一种好的解决问题的思路。其次,在没有串口打印时,没有第一时间把串口焊好,又是自己独自摸索怀疑,增加了调试的难度。最后,对单片机的执行速度没有大致的概念,以为8M晶振倍频到48M,单片机速度非常快了,但是没有考虑到多周期指令和C代码翻译成汇编再翻译成机器码后,会增加20%~30%的代码量,综合考虑,跑一次while(1)循环大概需要几毫秒的时间(中等规模代码,且没有任何延时)。
    --------------------- 
    作者:飞鱼湾 
    来源:CSDN 
    原文:https://blog.csdn.net/kelvinflying/article/details/46674339 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • 一、使用proteus绘制简单的电路图,用于后续仿真 二、编写程序 /******************************************************************************************************************** ...

    一、使用proteus绘制简单的电路图,用于后续仿真

    二、编写程序

    /********************************************************************************************************************
    ----	@Project:	LED
    ----	@File:	main.c
    ----	@Edit:	ZHQ
    ----	@Version:	V1.0
    ----	@CreationTime:	20200505
    ----	@ModifiedTime:	20200505
    ----	@Description:	累计中断次数使LED灯闪烁
    ----	单片机:AT89C52
    ********************************************************************************************************************/
    #include "reg52.h"
    
    /*——————宏定义——————*/
    #define FOSC 11059200L
    #define T1MS (65536-FOSC/12/1000)   /*1ms timer calculation method in 12Tmode*/
    #define const_time_level 500  /*设置LED闪烁频率*/
    
    /*——————变量函数定义及声明——————*/
    /*定义LED口*/
    sbit LED = P3^5;
    /*步骤变量*/
    unsigned char ucLedStep=0; 
    /*统计定时中断次数的延时计数器*/
    unsigned int  uiTimeCnt=0;
    /**
    * @brief  定时器0初始化函数
    * @param  无
    * @retval 初始化T0
    **/
    void Init_T0(void)
    {
    	TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/
    	TL0 = T1MS;                     /*initial timer0 low byte*/
    	TH0 = T1MS >> 8;                /*initial timer0 high byte*/
    }
    /**
    * @brief  定时器0中断函数
    * @param  无
    * @retval 无
    **/
    void ISR_T0(void)	interrupt 1
    {
    	TF0=0;  /*清除中断标志*/
      TR0=0; /*关中断*/
      if(uiTimeCnt<0xffff)  /*设定这个条件,防止uiTimeCnt超范围*/
      {
          uiTimeCnt++;  /*累加定时中断的次数*/
      }	
    	TL0 = T1MS;                     /*initial timer0 low byte*/
    	TH0 = T1MS >> 8;                /*initial timer0 high byte*/
      TR0=1; /*开中断*/	
    }
    /**
    * @brief  外围初始化函数
    * @param  无
    * @retval 初始化外围
    **/
    void Init_Peripheral(void)
    {
    	ET0 = 1;/*允许定时中断*/
    	TR0 = 1;/*启动定时中断*/
    	EA = 1;/*开总中断*/
    	LED = 0;
    }
    
    /**
    * @brief  初始化函数
    * @param  无
    * @retval 初始化单片机
    **/
    void	Init(void)
    {
    	Init_T0();
    }
    /**
    * @brief  LED闪烁函数
    * @param  无
    * @retval 控制LED闪烁
    **/
    void Led_Flicker(void)
    {
    	switch(ucLedStep)
    	{
    		case 0:
    			if(uiTimeCnt >= const_time_level) /*时间到,灯亮*/
    			{
    				ET0 = 0; /*禁止定时中断*/
    				uiTimeCnt = 0; 
    				LED = 1;
    				ucLedStep = 1;/*切换到下一步*/
    				ET0 = 1; /*允许定时中断*/
    			}
    		break;
    		case 1:
    			if(uiTimeCnt >= const_time_level) /*时间到,灯灭*/
    			{
    				ET0 = 0; /*禁止定时中断*/
    				uiTimeCnt = 0; 
    				LED = 0;
    				ucLedStep = 0;/*返回到上一步*/
    				ET0 = 1; /*允许定时中断*/
    			}
    		break;			
    	}
    }
    
    /**
    * @brief  延时函数
    * @param  无
    * @retval 无
    **/
    void Delay_Long(unsigned int uiDelayLong)
    {
       unsigned int i;
       unsigned int j;
       for(i=0;i<uiDelayLong;i++)
       {
          for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/
              {
                 ; /*一个分号相当于执行一条空语句*/
              }
       }
    }
    /*——————主函数——————*/
    /**
    * @brief  主函数
    * @param  无
    * @retval 实现LED灯闪烁
    **/
    void main()
    {
    	/*单片机初始化*/
    	Init();
    	/*延时,延时时间一般是0.3秒到2秒之间,等待外围芯片和模块上电稳定*/
    	Delay_Long(100);
    	/*单片机外围初始化*/	
    	Init_Peripheral();
    	while(1)
    	{
    		/*实现LED闪烁*/
    		Led_Flicker();
    	}
    }
    
    

    三、仿真实现

    把编译出的hex文件写入单片机,点击运行,LED实现闪烁。

    修改程序中的LED闪烁频率的宏,可改变LED闪烁的频率。

    展开全文
  • 第四节:累计定时中断次数使LED灯闪烁。 开场白: 上一节提到在累计主循环次数来实现计时,随着主函数里任务量的增加,为了保证延时时间的准确性,要不断修正设定上限阀值const_time_level 。我们该怎么解决这个...
    第四节:累计定时中断次数使LED灯闪烁。

    开场白:
    上一节提到在累计主循环次数来实现计时,随着主函数里任务量的增加,为了保证延时时间的准确性,要不断修正设定上限阀值const_time_level 。我们该怎么解决这个问题呢?本节教大家利用累计定时中断次数的方法来解决这个问题。这一节要教会大家四个知识点:
    第一点:利用累计定时中断次数的方法实现时间延时
    第二点:展现鸿哥最完整的实战程序框架。在主函数循环里用switch语句实现状态机的切换,在定时中断里累计中断次数,这两个的结合就是我写代码最本质的框架思想。 
    第三点:提醒大家C语言中的int ,long变量是由几个字节构成的数据,凡是在main函数和中断函数里有可能同时改变的变量,这个变量应该在主函数中被更改之前,先关闭相应的中断,更改完了此变量,再打开中断,否则会留下不宜察觉的漏洞。当然在大部分的项目中可以不用这么操作,但是在一些要求非常高的项目中,有一些核心变量必须这么做。
    第四点:定时中断的初始值该怎么设置。不用严格按公式来计算时间,一般取个经验值是最大初始值减去1000就可以了。
    具体内容,请看源代码讲解。

    (1)硬件平台:基于朱兆祺51单片机学习板。

    (2)实现功能:让一个LED闪烁。

    (3)源代码讲解如下:

    #include "REG52.H"

    #define const_time_level 200  

    void initial_myself();    
    void initial_peripheral();
    void delay_long(unsigned int uiDelaylong);
    void led_flicker();
    void T0_time();  //定时中断函数

    sbit led_dr=P3^5;  

    unsigned char ucLedStep=0; //步骤变量
    unsigned int  uiTimeCnt=0; //统计定时中断次数的延时计数器


    void main() 
      {
       initial_myself();  
       delay_long(100);   
       initial_peripheral(); 
       while(1)   
       {
          led_flicker();   
       }

    }

    void led_flicker() ////第三区 LED闪烁应用程序
    {
      
      switch(ucLedStep)
      {
         case 0:
    /* 注释一:
    * uiTimeCnt累加定时中断的次数,每一次定时中断它都会在中断函数里自加一。
    * 只有当它的次数大于或等于设定上限const_time_level时,
    * 才会去改变LED灯的状态,否则CPU退出led_flicker()任务,继续快速扫描其他的任务,
    * 这样的程序结构就可以达到多任务并行处理的目的。这就是鸿哥在所有开发项目中的核心框架。
    */
                      if(uiTimeCnt>=const_time_level) //时间到
                      {

    /* 注释二:
    * ET0=0;uiTimeCnt=0;ET0=1;----在清零uiTimeCnt之前,为什么要先禁止定时中断?
    * 因为uiTimeCnt是unsigned int类型,本质上是由两个字节组成。
    * 在C语言中uiTimeCnt=0看似一条指令,实际上经过编译之后它不只一条汇编指令。
    * 由于定时中断函数里也对这个变量进行累加操作,如果不禁止定时中断,
    * 那么uiTimeCnt这个变量在main()函数中还没被完全清零的时候,如果这个时候
    * 突然来一个定时中断,并且在中断里又更改了此变量,这种情况在某些要求高的
    * 项目上会是一个不容易察觉的漏洞,为项目带来隐患。当然,大部分的普通项目,
    * 都可以不用那么严格,可以不用禁止定时中断。在这里只是提醒各位初学者有这种情况。
    */
                 ET0=0;  //禁止定时中断
                         uiTimeCnt=0; //时间计数器清零
                 ET0=1; //开启定时中断
                 led_dr=1;    //让LED亮
                             ucLedStep=1; //切换到下一个步骤
                      }
                  break;
         case 1:
                      if(uiTimeCnt>=const_time_level) //时间到
                      {
                 ET0=0;  //禁止定时中断
                         uiTimeCnt=0; //时间计数器清零
                 ET0=1;   //开启定时中断
                 led_dr=0;    //让LED灭
                             ucLedStep=0; //返回到上一个步骤
                      }
                  break;
      
      }

    }


    /* 注释三:
    * C51的中断函数格式如下:
    * void 函数名() interrupt 中断号
    * {
    *    中断程序内容
    * }
    * 函数名可以随便取,只要不是编译器已经征用的关键字。
    * 这里最关键的是中断号,不同的中断号代表不同类型的中断。
    * 定时中断的中断号是 1.至于其它中断的中断号,大家可以查找
    * 相关书籍和资料。大家进入中断时,必须先清除中断标志,并且
    * 关闭中断,然后再写代码,最后出来时,记得重装初始值,并且
    * 打开中断。
    */
    void T0_time() interrupt 1
    {
      TF0=0;  //清除中断标志
      TR0=0; //关中断

      if(uiTimeCnt<0xffff)  //设定这个条件,防止uiTimeCnt超范围。
      {
          uiTimeCnt++;  //累加定时中断的次数,
      }

    TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
    TL0=0x2f;
    TR0=1;  //开中断
    }


    void delay_long(unsigned int uiDelayLong)
    {
       unsigned int i;
       unsigned int j;
       for(i=0;i<uiDelayLong;i++)
       {
          for(j=0;j<500;j++)  //内嵌循环的空指令数量
              {
                 ; //一个分号相当于执行一条空语句
              }
       }
    }


    void initial_myself()  //第一区 初始化单片机
    {

    /* 注释四:
    * 单片机有几个定时器,每个定时器又有几种工作方式,
    * 那么多种变化,我们记不了那么多,怎么办?
    * 大家记住鸿哥的话,无论一个单片机有多少内置资源,
    * 我们做系统框架的,只需要一个定时器,一种工作方式。
    * 开定时器越多这个系统越不好。需要哪种定时工作方式呢?
    * 就需要响应定时中断后重装一下初始值继续跑那种。
    * 在51单片机中就是工作方式1。其它的工作方式很少项目能用到。
    */
      TMOD=0x01;  //设置定时器0为工作方式1


      /* 注释五:
    * 装定时器的初始值,就像一个水桶里装的水。如果这个桶是空桶,那么想
    * 把这个桶灌满水的时间就很长,如果是里面已经装了大半的水,那么想
    * 把这个桶灌满水的时间就相对比较短。也就是定时器初始值越小,产生一次
    * 定时中断的时间就越长。如果初始值太小了,每次产生定时中断
    * 的时间分辨率太粗,如果初始值太大了,虽然每次产生定时中断的时间分辨率很细,
    * 但是太频繁的产生中断,不但会影响主函数main()的执行效率,而且累记中断次数
    * 的时间误差也会很大。凭鸿哥多年的江湖经验,
    * 我觉得最大初始值减去2000是比较好的经验值。当然,大一点小一点没关系。不要走
    * 两个极端就行。
    */
    TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
    TL0=0x2f;

      led_dr=0;  //LED灭
    }
    void initial_peripheral() //第二区 初始化外围
    {
      EA=1;     //开总中断
      ET0=1;    //允许定时中断
      TR0=1;    //启动定时中断

    }

    总结陈词:
    本节程序麻雀虽小五脏俱全。在本节中已经展示了我最完整的实战程序框架。
    本节程序只有一个LED灯闪烁的单任务,如果要多增加一个任务来并行处理,该怎么办?
    欲知详情,请听下回分解-----蜂鸣器的驱动程序。

    (未完待续,下节更精彩,不要走开哦)
    展开全文
  • 每个周末丁丁小朋友的父母会要求他独立完成一些家务,来培养他的劳动习惯,家务是固定的三件事情:烧两壶开水、炖一锅排骨、将家里的地板拖一遍。如果单独 完成这些事情,烧一壶开水大概需要十分钟,炖排骨大概...

     

    每个周末丁丁小朋友的父母会要求他独立完成一些家务,来培养他的劳动习惯,家务是固定的三件事情:烧两壶开水、炖一锅排骨、将家里的地板拖一遍。如果单独 完成这些事情,烧一壶开水大概需要十分钟,炖排骨大概二十五分钟,拖地板大概需要三十分钟,烧开水只要等水开了倒进保温瓶里,排骨炖好后关掉火就行。
        第一周,丁丁小朋友先开始烧水和炖排骨,然后去拖地板,为了看水有没有烧开和排骨有没有炖好,拖一会地板就要停下来跑到厨房去看一看,这样看一次需要一分钟,总共看了十次,四十分钟后三样家务全部做完。
    虽然四十分钟把家务全部做完,但丁丁小朋友是隔几分钟才去看一下水有没有烧开,于是水被烧开了一会丁丁小朋友才发现,水烧开后从壶里溢出流到煤气灶上,有点危险,显然从家务完成的质量来看不是很理想。
        第二周,丁丁小朋友吸取了上周的经验,烧水换用水烧开后可以自动鸣笛的壶,排骨有上周的经验知道炖二十五分钟火候差不多,于是炖的时候用一个闹钟定时二十 五分钟,接下来专心开始拖地板。大约十分钟后,第一壶水烧开鸣笛,丁丁小朋友停下拖地板去把水倒进保温瓶接着烧第二壶,继续拖地板;又过了大约十分钟,第 二壶水烧开,丁丁小朋友同样处理;二十五分钟时间到,闹钟响起,丁丁小朋友过去看排骨,已经炖好于是关火,接着拖地板;三十三分钟,地板拖完,家务全部完 成。
        和第一周对比,时间少用了七分钟,而且水一开就去倒掉,消除了潜在危险,完成的质量自然要好一些,看来日常生活中的一些事情,不同的处理方法做出来的效果也会有明显差异。
    丁丁小朋友做家务的例子对应单片机同时需要处理几个工作任务的两种基本方法:轮流查询和中断响应。开水烧开了不马上处理就会有危险,拖地板被打断有延时不会发生什么意外,但烧开水只要把水倒进壶里烧就行,烧的过程中并不需要做其它事情,拖地板则需要一直拖到全部地板拖完。
    如果说第一周的方法是轮流查询那二周的方法就是中断响应,水烧开鸣笛和闹铃为中断发生信号,从丁丁小朋友两周完成的结果可以看出中断响应效果要好过轮流查询。
        序言中就说过单片机技术是一门实用工程技术学科,和日常生活息息相关,正是为了应付丁丁小朋友做家务例子中烧水炖排骨这类问题,单片机有了中断的概念。中 断就是在工作过程中突然有更紧要的事情去要去处理,于是将当前的工作打断,处理好更紧要的事情后再继续当前的工作。单片机的中断可分为两大类:一种是单片 机内部控制电路在某种条件下产生的叫内部中断,另外一种则是由单片机外部器件产生的叫外部中断。
        丁丁小朋友烧水和炖排骨对于他是两个独立的外部事情,这两个外部事情所产生的“中断信号”分别属于外部中断和内部中断。水烧开是水壶主动发出笛声,这个笛 声和丁丁小朋友没有直接的关联,他不知道具体会在什么时候响,只要水开就会由水壶产生并传到丁丁小朋友的耳朵里,笛声是他的“外部中断信号”;闹铃是丁丁 小朋友用他的闹钟来产生的,和炖排骨没有直接联系,只是因为丁丁小朋友知道排骨二十五分钟可以炖好才设置成这个时间,他自己是知道闹铃什么时候会响,只是 他不想频繁地去看时间才用闹钟定时,闹铃声是他的“内部中断信号”。
        通过丁丁小朋友做家务的例子我们明白了中断的原理和方法:单片机在工作的时候往往需要处理多个事情,有些事情只并不需要单片机时刻进行控制,只是需要在某 些特定的条件下由单片机做出相应处理,有些事情则需要单片机花比较多的时间逐步控制,一旦停止控制就无法进行下一步操作,中断的引入可以让单片机面对这样 的问题时有更高的工作效率,对于不需时刻进行控制的事情在需要被干预时发出中断信号让单片机来进行相应处理,需要时刻控制的就由单片机主程序循环持续控制
    展开全文
  • MCS-51 单片机的结构和工作原理一、MCS-51单片机的基本组成MCS-51单片机芯片有许多种,如8051、8031、8751、80c51、80c31等。它由中央处理器(CPU)、时钟电路、程序存储器(ROM/EPROM)、数据存储器(RAM)、并行I/...

    MCS-51 单片机的结构和工作原理

     

    一、MCS-51单片机的基本组成

     

    MCS-51单片机芯片有许多种,如80518031875180c5180c31等。它由中央处理器(CPU)、时钟电路、程序存储器(ROM/EPROM)、数据存储器(RAM)、并行I/O口(P0~P3)、串行口、定时器/计数器及中断系统组成。它们通过单一总线连接,并被集成在一块半导体芯片上,即单片微型计算机(Single-Chip Microcomputer)。

     

    二、8051单片机内部结构和功能

     

    中央处理器CPU

    中央处理器CPU是单片机内部的核心部件,它决定了单片机的主要功能特性,由运算器和控制器两部分组成。

    1、  运算器

        运算器是计算机的运算部件,用于实现算术逻辑运算、位变量处理、移位和数据传送等操作。它是以算术逻辑单元ALU为核心,加上累加器ACC、寄存器B、暂存器TMP1TMP2、程序状态字PSW以及十进制调整电路和专门用于位操作的布尔处理器组成。

       1)算术逻辑单元

        算术逻辑单元ALU用来完成二进制数的四则运算和布尔代数的逻辑运算。此外,通过对运算结果的判断影响程序状态标志寄存器的有关标志位。

       2)累加器

        累加器ACC8位的寄存器,是CPU中使用最频繁的寄存器。它既可以用于存放操作数,也可以存放中间结果。MCS-51中大部分单操作数指令的操作数就取自累加器ACC,许多双操作数指令中的一个操作数也取自累加器ACC,单片机中大部分数据操作都通过累加器ACC进行的。

       3)寄存器B

    寄存器B是个8位寄存器,是为ALU进行乘除运算设置的。在执行乘法运算指令时,寄存器B用于存放其中一个乘数和乘积的高8位。在执行除法时,用于存放除数和余数。还可以作为一般的寄存器来使用。

    4)程序状态字PSW

    程序状态字PSW是个8位的特殊功能寄存器,它的各位包含了程序运行的状态信息,以供程序查询和判断。PSW格式和含义如下:

       1)      Cy  进位标志位。CyPSW中最常用的标志位。由硬件或软件置位和清零。它表示运算结果是否有进位(借位)。如果运行结果在最高位有进位输出(加法)或借位输入(减法)时,则Cy由硬件置“1”;否则Cy被请“0”。

    2)      AC  辅助(半)进位标志。当执行减法运算时,运算结果产生低四位向高四位进位或借位时,由硬件置“1”;否则清“0”。

    3)      F0  用户标志位。由用户定义使用。

    4)      RS1RS0  工作寄存器组选择位。这两位的值决定选择哪一组寄存器作为当前寄存器组。

    5)      OV  溢出标志位。

    6)      未定义。

    7)      P  奇偶标志位。P标志表明ACC1个数的奇偶性。在每条指令执行完后,单片机根据ACC内容对P位自动置位或复位。若累加器ACC中有奇数个1,则P=1;若累加器ACC中有偶数个1,则P=0

         5)布尔处理器

     

    2、  控制器

    控制器是计算机的指挥控制部件,它包括程序计数器PC、指令寄存器IR、指令译码器ID、数据指针DPTR、堆栈指针SP以及定时控制与条件转移逻辑电路等。它对来自存储器中的指令进行译码,并通过定时和控制电路在规定的时刻发出各种操作所需的控制信号,使各种部件协调工作,完成指令所规定的操作。

    1)程序计数器PCProgram Counter

        PC是一个16位计数器。实际上PC是程序存储器的字节地址计数器,其内容是将要执行的下一条指令的地址,寻址范围达64k216=65536=64k)。PC有自动加1的功能,从而实现程序的顺序执行。可以通过转移、调用、返回等指令改变其内容,以实现程序的转移。

       2)指令译码器ID

    当指令取出经指令寄存器IR送至指令译码器ID时,ID对该指令进行译码,即把指令转变成所需的电平信号。CPU根据ID输出的电平信号使定时控制电路定时地产生执行该指令所需的各种控制信号,以使计算机能正确执行程序所要求的各种操作。

    3)数据指针DPTR

    数据指针DPTR16位寄存器。它的功能是存放16位的地址,作为访问外部程序存储器和外部数据存储器的地址。编程时,DPTR即可按16位寄存器使用,也可以按两个8位寄存器使用,即DPHDPTR8位,DPLDPTR的低8位。

     

     

     

    存储系统

    8051单片机在系统结构上采用哈佛结构,将程序和数据分别存放在两个存储器内。它与采用冯·诺依曼结构,程序和数据共用一个存储器的通用计算机不同。8051的存储器在物理结构上分为程序存储器(ROM)和数据存储器(RAM),有四个物理上相互独立的存储空间,即片内ROM和片外ROM,片内RAM和片外RAM

    从用户使用的角度看,8051存储空间分为三类:片内、片外统一编址0000H~FFFFH64k程序存储器地址空间;256字节数据存储器地址空间,地址为:00H~FFH64k片外数据存储器或I/O地址空间。

    上述的三个地址空间是重叠的,即程序存储器中片内外低4k地址重叠;数据存储器与程序存储器64k地址全部重叠;数据存储器中片内外低256字节地址重叠。虽然地址重叠,但由于采用了不同的操作指令及控制信号EAPSEN的选择,因此不会发生混乱。

    1、程序存储器

    程序存储器用来存放程序代码和常数,分为片内、片外两大部分,即片内ROM和片外ROM。其中8051内部有4KROM,地址范围为0000H~0FFFH,片外用16位地址线扩充64KROM,两者统一编址。

    单片机从片内ROM取指令(EA=1)还是从片外ROMEA=0)取指令,取决于CPU引脚EA的电平高低。

    8051从片内ROM和片外ROM取指令时执行速度相同。

    在程序存储器中有些特殊单元。

    0000H~0002H   系统复位向量区

    0003H~000AH   INT0中断地址区

    000BH~0012H   T/C0中断地址区

    0013H~001AH   INT1中断地址区

    001BH~0022H   T/C1中断地址区

    0023H~002AH   串行口中断地址区

     

     

    2、片内数据存储器

    数据存储器用来存放运算的中间结果、标志位,以及数据的暂存和缓冲等。分为片内、片外两部分。8051片内数据存储器按功能划分为低128(00H~7F)单元和高12880~FFH)单元。

     

    3、片外数据存储器

    片外数据存储器,即片外RAM,一般由静态RAM芯片组成。用户可根据需要确定扩展存储器的容量,MCS-51单片机访问片外RAM可用特殊功能寄存器--数据指针DPTR,最大容量为64K

    片外RAM地址范围为0000H~FFFFH,其中0000H~00FFH区间与片内数据存储空间是重叠。CPU使用MOV指令和MOVX指令加以区分。

     

     

    三、MCS-51单片机的引脚功能

     

    MCS-51系列单片机芯片均为40个引脚。引脚功能描述如下:

    1、电源引脚VSSVCC

       VSS为电压接地端,VCC+5V电源端。

    2、时钟电路引脚XTAL1XTAL2

       XTAL1XTAL2是外接晶振引线端。

       当使用芯片内部时钟时,此二引脚外接石英晶体振荡器和电容;当使用外部时钟时,用于接外部脉冲信号。

    3、控制信号引脚ALEPSENEARST

       1ALE/PROG

    此引脚是地址控制锁存控制信号。在访问外部存储器时,ALE用于锁存出现在P0口的低8位地址,以实现低位地址和数据的隔离。

    2PSEN

     此引脚是片外程序存储器的选通信号,低电平有效。在从片外ROM读取指令或常数时,每个机器PSEN两次有效,以实现对ROM单元的读操作。当访问片外RAM时,PSEN信号将不出现。

    3EA/VPP

    此引脚是访问外部程序存储器的控制信号,低电平有效。

    4RST/VPD

    此引脚为复位信号,高电平有效。当此输入端保持2个机器周期以上的高电平,就可以使单片机复位。

    4I/O(输入、输出)端口P0P1P2P3

    1P0口(P0.0~P0.7P0口是一个漏极开路的8位双向I/O口,每位能驱动8LSTTL负载。在访问片外存储器时,P0分时提供低8位地址线和8位双向数据线。当不接片外存储器或不扩展I/O时,P0口可作为一个通用输入输出口。当P0口作为输入口使用时,应先P0口向锁存器写“1”,此时P0口全部引脚浮空,可作为高阻抗输入。当P0口作为输出时,由于输出电路为漏极开路,驱动NMOS电路时必须外接上拉电阻。

    2P1口(P1.0~P1.7P1口是一个带内部上拉电阻的8位准双向I/O口,每位能驱动4LSTTL负载。P1口只能作通用输入/输出口用。当P1口作为输入口使用时,应先向P1口锁存器写“1”,此时P1口引脚由内部上拉电阻拉成高电平。当P1口作为输出口时,已能向外提供推拉电流负载,无需在外接上拉电阻。

    3P2口(P2.0~P2.7P2口是一个带内部上拉电阻的8位准双向I/O,每位能驱动4LSTTL负载。在访问片外存储器时,它输出高8位地址。

    4P3口(P3.0~P3.7P3口为双功能口,除了作为一般的准双向通用的I/O口使用外,每个引脚都有第二功能。

    引脚信号的第二功能引脚

    P3口的第二功能引脚

    引脚

    第二功能

    信号名称

    P3.0

    RxD

    串行数据接收

    P3.1

    TxD

    串行数据发送

    P3.2

    INT0

    外部中断0

    P3.3

    INT1

    外部中断1

    P3.4

    T0

    定时器/计数器0计数输入

    P3.5

    T1

    定时器/计数器1计数输入

    P3.6

    WR

    外部RAM写选项

    P3.7

    RD

    外部RAM读选项

     

     

    四、MCS-51单片机的时序

       

    在执行指令时,单片机通常将一条指令分解为若干基本的微操作。这些微操作对应的脉冲信号在时间上的先后次序称为单片机的时序。单片机的时序可以由片内振荡器(外接晶振和微调电容)或外部时钟信号提供。

    CPU时序

    1、时序定时单位

        MCS-51的时序定时单位从小到大依次为节拍、状态、机器周期和指令周期。

       1)节拍和状态

        把振荡脉冲周期定义为节拍。每两个节拍即为一个状态。

    2)机器周期

    MCS-51采用定时控制方式,有固定的机器周期,为6个状态,即12个振荡周期。当振荡频率为12MHZ时,一个机器周期为1us

    3)指令周期

    指令周期是最大的定时单位,执行一条指令所需的时间称为指令周期。MCS-51的指令周期可分为一、二、四个机器周期。

    2MCS-51指令的取指及执行时序

    按长度MCS-51单片机的指令可分为单字节指令、双字节指令和三字节指令。执行这些指令所需的机器周期数目不同。在单字节指令中只有乘法指令和除法指令是四个机器周期,其余均为单机器周期或双机器周期;双字节指令为单机器周期或双机器周期;三字节指令都是双机器指令。

    3、访问片外ROMRAM的时序

    1)片外ROM读时序

    1P0口作为地址信号输出线,用于输出程序存储器的低8位地址PCLP2口用于输出程序存储器的高8位地址PCHP2口具有锁存功能,而P0口除了输出地址外,还要输入指令,故需要用ALEP0口输出的低8位地址PCL锁存起来。在每个机器周期中允许地址锁存器两次有效,在ALE由高变低时,锁存出现在P0口上的低8位地址PCL

    2从片外ROM读取指令,除了ALE外,还需PSEN(片外ROM选通)信号有效,选通片外ROM芯片的使能端,读出的指令从P0口送入单片机。

    2)片外RAM读写时序

     前述ROM操作是为了取指令,因此把相应的机器周期称为取指周期,而片外RAM的读或写操作则是为了读写数据,属于指令的执行周期。

    说明:

    1、第一个机器周期是读片外ROM的取指时序,第一个ALE锁存由P0送出指令的低8位地址PCLP2口是指令的高8位地址PCH,不需要锁存。在S3S4P1期间,PSEN选通片外ROM,读出指令。

    2、第一个机器周期的S4之后,送出片外RAM地址,第二个ALE锁存P0送出的片外RAM的低8位地址。

    3、在第二个机器周期中,第一个ALE信号不再出现,PSEN也保持高电平无效,但读选通信号(RD)从第二个机器周期S1P1开始有效,选通片外RAM,进行RAM的读操作。被寻址的RAM单元把有效数据送入P0口,CPU通过P0口是读入数据。

    4、第二个机器周期的第二个ALE信号仍然出现,也进行一次片外ROM读操作,但属无效操作。

     

     

    五、MCS-51的最小系统

     

    1、什么是最小系统

    个人理解为:使系统正常工作的必不可少的外围电路所构成的电路系统。如MCS-51系列单片机要正常工作需要电源、时钟电路(外接晶振和微调电容)和复位电路。这样由MCS-51系列芯片如8051、电源、时钟电路和复位电路构成的数字电路系统即为MCS-51的最小系统。

    展开全文
  • 在这个循环中处理所有中断以外的事情,这些事情需要被执行的周期各不一样,有些需要频繁地做 ,有些需要很长时间才做一次。我们把这些事情记为T1,T5,T10 ,T200... T表示task或者time吧,后面的数字表示执行周期...
  • 前面的学习中介绍了CPU定时器与中断系统,下面借助两个实例回顾下之前的学习内容! 实例1:利用蜂鸣器模拟基本音级 1.1 蜂鸣器的工作原理 蜂鸣器发声原理是电流通过电磁线圈,使电磁线圈产生磁场来驱动振动膜发声...
  • 51单片机测频

    2017-09-28 12:13:55
    出于课设需要,用51单片机制作一个频率计,输入信号为标准信号,基本要求100-100... 采用定时器中断和外部中断,外部中断计数,定时器中断定时1秒,1秒内测到的脉冲数(或下降沿数)即测到的频率。(此方法测不了高的
  • 1.时间片轮询思路介绍: 时间片轮询法,在很多书籍中有提到...初始化定时器,这里假设定时器的定时中断为1ms(当然你可以改成10ms,这个和操作系统一样,中断过于频繁效率就低,中断长,实时性差)。 定义一个数值...
  • 51单片机的定时器0和串口同时使用问题
  • 1、程序卡死在非中断的程序中,含有for while等循环体函数参数不正确导致,例如memcpy CRCcheck等。 现象:程序主逻辑无法执行,但是各个中断服务程序能够正常运行。 解决方法: 1)在中断程序中点灯或者打印,判断...
  • c语言编写单片机技巧

    2020-07-29 14:21:23
    1. C语言和汇编语言在开发单片机时各有哪些优缺点? 答:汇编语言是一种用文字助记符来表示机器指令的符号语言,是最接近机器码的一种语言。其主要优点是占用资源少、程序执行效率高。但是不同的CPU,其汇编语言...
  • 单片机抗干扰技术

    2012-05-06 21:45:56
    近年来,单片机在工业自动化、生产过程控制、智能仪器仪表等领域的应用越来越广泛,大大提高了产品的质量,有效地提高了生产效率。但是,测控系统的工作环境往往复杂、比较恶劣,尤其是系统周围的电磁环境,这对系统...
  • STC89C52单片机最小系统电路由复位电路、时钟电路和电源电路。拥有这三部分电路后,单片机即可正常工作。单片机最小系统原理图如下图所示。 ESP8266WIFI模块电路设计电路设计 串口WIFI模块是新一代嵌入式WiFi模块,...
  • 2.中断的执行时间一定是不能超过定时时间的,不然就会中断没处理完又来了下一个中断,造成频率出错。 3.假设100us中断一次,中断程序执行时间40us,则当前中断执行完毕距下一个中断到来还有70us,这剩下的时间就...
  • 如果在主线程和中断频繁改变变量的时候,比如自加、自减,最好先定义一个临时变量,记录自加/减的次数,后面一次性将这个被中断/主线程共享的变量进行加/减,从而减少对共有变量的访问次数,中断中也是如此。...
  • 电子技术的飞速发展,单片机也步如一个新的时代,越来越多的功能各异的单片机为我们的设计提供了许多新的方法与思路。对于莫一些场合,比如:复杂的后台运算及通信与高实时性前台控制系统、软件资源消耗大的系统、...
  • 1 轮询与中断 外部设备与中央处理器交互一般有两种手段:轮询和中断。 (1)轮询(Polling) 很多I/O设备都有一个状态寄存器,用于描述设备当前的工作状态,每当设备状态发生改变时,设备将修改相应状态寄存器...
  • 单片机笔记

    2020-03-23 11:06:32
    将组成微型计算机的个功能部件:中央处理器(CPU)、存储器(RAM、ROM、EPROM)、定时/计数器、串行口、I/O接口电路集合在一块集成电路芯片中从而构成完整的微型计算机,故称:单晶片微型计算机,简称单片机。...
1 2 3 4 5 ... 20
收藏数 638
精华内容 255
关键字:

单片机定时中断太频繁