搜索
bottom↓
回复: 20

单片机软复位,(*(void (*)(void))0()详解

[复制链接]

出0入0汤圆

发表于 2013-6-25 19:01:17 | 显示全部楼层 |阅读模式
这几天在做一个东西,一个普通按键,一个复位按键。由于某些原因,硬复位按键有时候不方便引出来,为了通用起见,打起了软复位的主意。
说是软复位,其实说是想让单片机跳到0地址开始执行。在不使用汇编的情况下,想办法用C语言实现。
记得好像是<<C专家编程>>里提供过一种方法,有点印像,但是记不起来了。反正就是使用函数指针。琢磨了一会,于是有了以下文字:
首先是函数指针。声明一个函数指针的语法如下:
  1. void (*p)(void);        // 声明了一个函数指针p, 指向无返回值无参数的函数
复制代码
下面定义一个函数:
  1. void foo(void)
  2. {
  3.     return;
  4. }
复制代码
使得函数指针p指向foo:
  1. p = foo;    // 函数名其实就是函数的地址
复制代码
然后就可以使用函数指针调用函数了:
  1. (*p)();     // 等于foo()
复制代码
所以,理所当然的,我们想到这个办法:
  1. p = 0x00;
  2. (*p)();
复制代码
这们应该可以行得通,但是类型不匹配,编译器可能会有警告。

ps:后来我试了一下,编译器居然没有警告。想了一下,明白了,应该是编译器将NULL定义为0,所以这一句就相当于:
  1. p = NULL;
复制代码
所以没有报错。
然后使用任意一个非0值给p赋值,警告就出现了。

自然我们会想到强制类型转换。但函数指针的类型是什么呢?
先看看普通变量:
  1. char i;         // 变量类型为 char
  2. int *p;         // 变量类型为 char *
  3. const char *j;  // 变量类型为 const *
复制代码
很明显,将变量声明时的变量类型去掉,就是变量类型了。
所以我们声明的那个函数指针的类型应该为:
  1. void (*)(void);
复制代码
强制转换:
我们知道将一个int型的变量bar强制转换为char可以使用如下方式:
  1. (char)bar;
复制代码
函数指针一样。下面我们将0强制转换为函数指针类型:
  1. (void (*)(void))0;
复制代码
然后:
  1. p = (void (*)(void))0;
  2. (*p)();
复制代码
完成。

等等,好像还不美!

注意函数指针调用函数的方式:
  1. (*p)();
复制代码
*p什么意思?取p的内容。
那p的内容是什么东西?
很明显 p = (void (*)(void))0。
那我们把p换成(void (*)(void))0看看。
于是有:
  1. (*(void (*)(void))0)();
复制代码
完成,连指针变量都省了。
在Keil中试了一下,编译后汇编语句就一条:
  1. LCALL C_STARTUP(C:0000)
复制代码
实际试了一下,可以完成单片机复位,但会不会有什么问题?

阿莫论坛20周年了!感谢大家的支持与爱护!!

该献的血还是要献的。你不献他不献。难道让我去献? --- 出自坛友:lovejp1981

出0入0汤圆

发表于 2013-6-25 19:04:51 | 显示全部楼层
这是瞎扯淡,我上个世纪就验证过这个问题,单片机受干扰死机以后内部寄存器状态已经异常了,只有硬件复位才能把它拉回正常,软件跳到0开始执行是没有意义的。

出0入0汤圆

发表于 2013-6-25 19:23:20 | 显示全部楼层
STM32好像不行。。。

出0入0汤圆

 楼主| 发表于 2013-6-25 19:24:22 | 显示全部楼层
i55x 发表于 2013-6-25 19:04
这是瞎扯淡,我上个世纪就验证过这个问题,单片机受干扰死机以后内部寄存器状态已经异常了,只有硬件复位才 ...

我需要的就是从0开始执行,任务很简单,跑飞也没事~

出110入0汤圆

发表于 2013-6-25 19:32:41 | 显示全部楼层
触发各种狗,让单片机自己硬复位

出0入0汤圆

发表于 2013-6-25 19:41:05 | 显示全部楼层
那么从复位起,加入一段初始化寄存器的代码就是了喽,让寄存器有个确定的状态。

是不是进入复位就应该关闭中断啊,初始化万一被中断,就有可能会乱。

出0入0汤圆

发表于 2013-6-25 19:41:07 | 显示全部楼层
这种软复位RAM里面的值是不变的,有时候会死的更惨的

出0入0汤圆

发表于 2013-6-25 19:52:05 | 显示全部楼层
可以考虑打开看门狗,然后饿死它,系统自然就复位了。

出0入0汤圆

发表于 2013-6-25 20:43:12 | 显示全部楼层
功能可以实现.但不是对付干扰的办法.

出0入0汤圆

发表于 2013-6-25 20:43:40 | 显示全部楼层
M0,M3系列MCU不能使用void (*p)(void)吧

出0入0汤圆

 楼主| 发表于 2013-6-25 20:47:43 | 显示全部楼层
coleyao 发表于 2013-6-25 19:52
可以考虑打开看门狗,然后饿死它,系统自然就复位了。

AT89C2051,没有狗。
实际上是个简单的测试程序,要求不高。

出0入0汤圆

 楼主| 发表于 2013-6-25 20:50:52 | 显示全部楼层
stevenli 发表于 2013-6-25 19:41
这种软复位RAM里面的值是不变的,有时候会死的更惨的

是的。
我想分享的是解决这个看似复杂的问题的过程。实用性....真不好说。

出0入0汤圆

 楼主| 发表于 2013-6-25 20:52:36 | 显示全部楼层
LiuShengAn 发表于 2013-6-25 20:43
M0,M3系列MCU不能使用void (*p)(void)吧

void (*p)(void)
如果是函数指针的话应该可以,这个是C语言层面上支持的。
其它就不清楚了,M0和M3都没用过....

出10入23汤圆

发表于 2013-6-26 00:55:14 来自手机 | 显示全部楼层
void (*p)(void) = (void (*)(void))0; p(); 可以实现跳转到零地址,一般启动代码中有对堆栈和变量赋初值,但是寄存器不会复位,这个很难办,稍不注意就要出乱子,个人觉得可以用个IO口加个外围电路,控制复位引脚,这样实现你的功能应该可以

出0入0汤圆

发表于 2013-6-26 02:08:47 | 显示全部楼层
LZ请看我在这个帖子中70楼的回复,相信对你会有帮助。

出0入0汤圆

发表于 2013-6-26 15:21:05 | 显示全部楼层
用复位来补救软件编写的错误,这是很离谱的思想,如果你家娃坐的校车用木板来补底盘,你会怎么想?而这段代码恰恰只能解决软件逻辑错误。

出0入0汤圆

发表于 2013-11-1 09:31:32 | 显示全部楼层
这个帖子的意义在于:复位是轻,形式是重。在很多其他地方都有用到这种C的书写形式,我就是在看linux内核时不太理解而找到这里来的

出0入0汤圆

发表于 2013-11-1 09:41:02 | 显示全部楼层
应该用WDT复位,不要直接跳转。

出0入0汤圆

发表于 2013-11-1 12:36:43 | 显示全部楼层
路过,学习了。。。

出0入0汤圆

发表于 2013-11-1 13:23:30 | 显示全部楼层
对于ARM,直接解锁,使用复位功能就可以了……
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-9-3 10:46

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表