2019-01-19 09:26:11 qq_20222919 阅读数 795
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

最近在调试系统的时候,发现单片机老是复位,于是想着用程序来判断一个单片机的复位信号是来自于哪里。查找资料发现STM8单片机的复位源总共有9种

●NRST引脚产生的外部复位
● 上电复位(POR)
● 掉电复位(BOR)
● 独立看门狗复位
● 窗口看门狗复位
● 软件复位
● SWIM复位
● 非法操作码复位
● EMS复位:当一些关键的寄存器被破坏或错误加载时产生的复位

但是这9种复位源通过复位寄存器能看到的只有5种

复位状态寄存器(RST_SR)

位7:5 保留,必须保持为0。
位4 EMCF:EMC复位标志
位3 SWIMF:SWIM复位标志位
位2 ILLOPF:非法操作码复位标志位
位1 IWDGF:独立型看门狗复位标志位
位0 WWDGF:窗口型看门狗复位标志位
 

由于单片机没有用到看门狗,所以这两种看门狗复位用不到,SWIM复位是烧写程序或者仿真时用的。那就只剩下一个EMC复位、非法操作码复位可以判断。但是更希望看到的是单片机是电源不稳引起的掉电复位还是单片机复位引脚被干扰引起的NRST复位。但是寄存器中没有可以判断这两种复位源的标志位。那么能不能自己想办法来判断这两种复位源呢?

  首先分析一下掉电复位和NRST复位的区别,掉电复位也就是单片机完全断电,复位后内存中所有的东西会丢失。NRST复位时内存数据没有丢失,内存中的东西还是复位前存储的数据。那么根据这个特性是不是可以在内存中某个地址存数一个标志。复位后去读取这个标志,如果这个标志和存放的一样,说明单片机没有掉电,属于NRST复位。如果读取到的值不是存储的值那么说明单片机断电了,内存中存储的内容丢失了,属于掉电复位。那么这样就能区分开是掉电复位还是NRST复位了。

写个程序测试了一下,测试结果符合预期。

测试代码如下:

#include "iostm8s103F3.h"
#include "main.h"
#include "uart.h"
#include "stdio.h"

void SysClkInit( void )
{
    CLK_SWR = 0xe1;                  //HSI为主时钟源  16MHz CPU时钟频率
    CLK_CKDIVR = 0x00;               //CPU时钟0分频,系统时钟0分频
}

void main( void )
{
    __asm( "sim" );                             //禁止中断
    SysClkInit();
    Uart1_Init( 9600 );
    __asm( "rim" );                             //开启中断
    
    //复位源判断
    if( ( * ( u16* )( 0x000102 ) ) == 0x55 )    //判断0x000100这个地址中存储的值是不是0x55
    {
        printf( " key reset!\r\n" );                     //值等于0x55说明是程序写进去的,属于按键复位
    }
    else                                                        //如果这个地址的值不等于0x55 说明单片机是刚上电
    {
      printf( " power reset!\r\n" );                  //属于上电复位 然后将指定的值写入指定的地址,若单片机不断电,这个值就会一直保持不变
        * ( u16* )( 0x000102 ) = 0x55;
    }
    if( RST_SR_WWDGF )
    {
        printf( "wwdg reset!\r\n" );
    }
    if( RST_SR_IWDGF )
    {
        printf( "iwdg reset!\r\n" );
    }
    if( RST_SR_ILLOPF )
    {
        printf( "illop reset!\r\n" );
    }
    if( RST_SR_SWIMF )
    {
        printf( "swim reset!\r\n" );
    }
    if( RST_SR_EMCF )
    {
        printf( "emcf reset!\r\n" );
    }
    //WWDG_CR = 0x80;                    //启动独立看门狗
    //  IWDG_KR = 0xCC;                   //启动窗口看门狗
    while( 1 )
    {
    }
}


每次上电后先判断 0x000102 这个地址的值是不是0x55,如果不是说明单片机是刚上电,内存中的值是随机的。于是将0x000102这个地址中的值改为0x55,如果下一次单片机复位后读到这个地址的值是0x55的话,说明单片机没有掉电。属于复位引脚引起的复位。0x000102这个地址必须保证不会被程序用到,要不然在程序执行过程中值被改写,下次单片机复位后读到的值就不是0x55了。

其余的复位源直接去读取寄存器就可以了。

 

2017-05-14 17:59:38 afiych 阅读数 5858
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

       单片机的复位引脚RST(全称RESET)出现2个机器周期以上的复位电平时,单片机就执行复位操作。如果RST持续为复位电平,单片机就处于循环复位状态。当单片机处于正常电平时就正常转入执行程序。

单片机复位电路原理 

图1:当单片机上电瞬间由于电容电压不能突变会使电容两边的电位相同,此时RST为低电平,之后随着时间推移电源通过电阻对电容充电,充满电时RST为高电平。正常工作为高电平,低电平复位。即上电低电平,然后转向高电平。 

图2:当单片机上电瞬间由于电容电压不能突变会使电容两边的电位相同,此时RST为高电平,之后随着时间推移电源负极通过电阻对电容放电,放完电时RST为低电平。正常工作为低电平,高电平复位。

 

上电复位:上电后,电容两端电压不能突变,VCC通过复位电容(10μF电解)给单片机复位脚施加高电平5V,同时,通过10KΩ电阻向电容器放电,使复位脚电压逐渐降低。经一定时间后(约10毫秒)复位脚变为0V,单片机开始工作。

手动复位:按下复位按钮,复位脚得到VCC的高电平,单片机复位,按钮松开后,单片机开始工作。

单片机复位电路原理

现在的单片机即使不要复位电路也能工作了,现在好多单片机内部集成有上电复位电路,这种单片机不需要外接上电复位电路

2013-08-08 17:46:55 Cs1275 阅读数 1390
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

复位电路中,放电二极管D不可缺少。当电源断电后,电容通过二极管D迅速放电,待电源恢复时便可实现可靠上电自动复位。若没有二极管D,当电源因某种干扰瞬间断电时,由于C不能迅速将电荷放掉,待电源恢复时,单片机不能上电自动复位,导致程序运行失控。电源瞬间断电干扰会导致程序停止正常运行,形成程序“乱飞”或进入“死循环”。若断电干扰脉冲较宽,可以使RC迅速放电,待电源恢复后通过上电自动复位,使程序进入正常状态;若断电干扰脉冲较窄,断电瞬间RC不能充分放电,则电源恢复后系统不能上电自动复位。


这里有个更有意思的解释:

如果RESET端对地没那个电容,

也就不需要这个二极管了。

只要电源接通,那么这个电容就会“逐渐充满电”,这个过程必须要有,正是这个过程保证了CPU正确地“RESET”。

当电容充满电之后我们把电源开关断开了,这个电容中的电“何去何从”呢?VCC和GND之间接了N多的器件,所有的器件都对它说:“把你那点电给我吧,我还能坚持一下。”电容说:“给你们没问题,可是我他娘脑袋上有个电阻挡我的财路,你们先别急,我慢慢把电放给你们。”

当电容刚刚要把电通过那个上拉电阻放出来,电源开关突然又接通了。CPU开始冲电容吼:“孙子!你Y的那个充电过程怎么没啦?我还要复位呢!”电容不干了:“废话,我上次充的电还没放呢这他妈电源又通啦!”CPU急了:“那我怎么办?我得复位啊!”电容眼珠一翻:“管你Y怎么办,死去吧你!”

2020-01-08 21:12:44 imxlw00 阅读数 17
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

单片机通电时,从初始态开始执行程序,称为上电复位。单片机死机时,通过手动按“重启”键使其从初始态开始执行程序,称为手工复位。复位电路是单片机应用电路中的重要组成部分。

单片机复位的条件:使单片机的RST端(引脚9的RESET端)加上持续两个机器周期的高电平。例如,若时钟频率为12MHz,每机器周期为1ms,则只需在RST引脚出现2ms以上时间的高电平,就可以使单片机复位。
在这里插入图片描述

  • 图3-14(a)所示为上电复位电路,它是利用电容充电来实现的。在通电瞬间,RESET端的电位与VCC相同,随着充电电流的减少,RESET的电位逐渐下降。只要保证RESET为高电平的时间大于两个机器周期,就能正常复位。
  • 图3-14(b)所示为按键复位电路,需复位时,按下RESET键,此时电源VCC经电阻R1、R2分压,在RESET端产生一个复位高电平使单片机复位。
2007-08-17 15:06:37 SmallBox00 阅读数 235
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

今天在网上看到这么一个单片机复位的程序,这个程序据说是一个大三的学生写出来的,不错,写的很有一定的道理,其C编程也达到了一定程度了【小盒子我还是很佩服这个人的】。下面我们来看看这个代码:

void main(void)
{
   unsigned char code rst[]={0xe4,0xc0,0xe0,0xc0,0xe0,0x32};  // 复位代码


   (*((void (*)())(rst)))();  // 执行上一行代码,将rst数组当函数调用
}

第一句定义一个数组rst[],数组内数据就是完成复位功能的汇编机器码,具体对应关系为:

clr a == 0xe4、push acc == 0xc0,0xe0、reti ==0x32
可以看出其程序起到复位的作用,完全就是汇编机器码的功劳。

而单片机复位的更好方法
clr a                      //清除ACC=0
push acc               //压0到堆栈——8位
push acc               //再压0到堆栈——再8位
reti                        //返回到0地址,从新执行。

这种复位方法比较麻烦,更加简单的复位写法是(摘自《C缺陷与陷阱》):
(     * (  void (*)( )  )0      ) ( );

看过上面更简单的复位方法,让我们多加考虑一下,为什么要写成0?别的不行吗?换成别的后会是什么样的效果呢?抱着这个想法,我亲自经过KEIL V2.4.0编译后的汇编程序:

可以看出若将(     * (  void (*)( )  )0     ) ( );  

改成(     * (  void (*)( )  )3      ) ( );

 

则程序会跳转到main()函数开始,避开startup文件的初始化……

 只所以我说的是会从main()开始,是因为我看过编译后的汇编文件,找到main的实际物理地址而已,否则我也不会写成3了。呵呵……下面就是编译后的汇编结果

C:0x0003    E4       CLR      A
C:0x0004    F508     MOV      0x08,A
C:0x0006    F509     MOV      0x09,A
    14:    while(1) {
    15:         if(i == 10) {
    16:            //(    *(   ( void (*)( ) )   (rst)    )    )();  // 执行上一行代码,将rst数组当函数调用
C:0x0008    E509     MOV      A,0x09
C:0x000A    640A     XRL      A,#0x0A
C:0x000C    4508     ORL      A,0x08
C:0x000E    7005     JNZ      C:0015
    17:            (    *(   ( void (*)( ) )   (3)    )    )();  // 执行上一行代码,将rst数组当函数调用
C:0x0010    120003   LCALL    main(C:0003)
    18:         } else {
C:0x0013    80F3     SJMP     C:0008
    19:                 i++;
C:0x0015    0509     INC      0x09
C:0x0017    E509     MOV      A,0x09
C:0x0019    70ED     JNZ      C:0008
C:0x001B    0508     INC      0x08
    20:         }

 

为了进行给大家一个很好的比较,从视觉上得到一定的感觉,我又再次将3改回成0,大家看看编译后的汇编结果是什么样子的;

下面的代码是函数(     * (  void (*)( )  )0     ) ( );   这个编译后的结果

C:0x0003    E4       CLR      A
C:0x0004    F508     MOV      0x08,A
C:0x0006    F509     MOV      0x09,A
    14:    while(1) {
    15:         if(i == 10) {
    16:            //(    *(   ( void (*)( ) )   (rst)    )    )();  // 执行上一行代码,将rst数组当函数调用
C:0x0008    E509     MOV      A,0x09
C:0x000A    640A     XRL      A,#0x0A
C:0x000C    4508     ORL      A,0x08
C:0x000E    7005     JNZ      C:0015
    17:            (    *(   ( void (*)( ) )   (0)    )    )();  // 执行上一行代码,将rst数组当函数调用
C:0x0010    120000   LCALL    C_STARTUP(C:0000)
    18:         } else {
C:0x0013    80F3     SJMP     C:0008
    19:                 i++;
C:0x0015    0509     INC      0x09
C:0x0017    E509     MOV      A,0x09
C:0x0019    70ED     JNZ      C:0008
C:0x001B    0508     INC      0x08
    20:         }

请大家注意红色的部分。

若大家都能亲自动手实际操作一下,会让你理解的更多,体会的也会更多的。。。具体的还有什么好的方法和好的建议,希望多多留言大家共同进步。【小盒子 2007.08.17     PM17:14】

单片机软硬件复位

阅读数 2625

没有更多推荐了,返回首页