论坛风格切换切换到宽版
  • 5583阅读
  • 23回复

51C的迷惑 [复制链接]

上一主题 下一主题
离线BA5RW
 
发帖
48263
只看楼主 倒序阅读 0楼 发表于: 2007-12-05
俺编了个简单的演示程序与学生一起学数组和指针,可是出现了怪现象,演示程序如下:
void main()
{      unsigned char flw[]={0x0f,0xee,0xcc,0xf0};      
     unsigned char *fl;
     unsigned int m;
     fl=&flw[0];
     for(m=0;m<3;m++)
     {      p0=*fl;
           mdelay(1000);
           fl++;
     }      
}

这段程序的目的是花样LED亮灯演示。
我的演示实验板用89S51单片机,P0和P2口接LED,LED公共端接电源端,口线低电平亮灯。以上程序应该只对P0口动作,但是每一次循环都会使P2口闪一下,P0口上应该不亮的LED也会闪一下,也就是这段演示程序实际上在循环的开始或结束会让各P口短暂拉成低电平,请教坛中朋友,有何办法防止这个现象发生?
离线BG4XCR
发帖
33381
只看该作者 1楼 发表于: 2007-12-06
watchdog or auto-self-reset???

check the fuse setup or the power supply.
离线BA5RW
发帖
48263
只看该作者 2楼 发表于: 2007-12-06
感谢楼上的答复,89S51没有fuse setup ,power supply是我最开始怀疑的,但没有问题,因此肯定是KEIL C的问题,希望继续指教!
在线BG4UVR
发帖
11288
只看该作者 3楼 发表于: 2007-12-06
  1. void main()
  2. {
  3.   unsigned char flw[]={0x0f,0xee,0xcc,0xf0};
  4.   unsigned char *fl;
  5.   unsigned int m;
  6.   fl=&flw[0];
  7.   for(m=0;m<3;m++)
  8.   {
  9.       p0=*fl;
  10.       mdelay(1000);
  11.       fl++;
  12.   }
  13. }


我把您的代码重新编辑了一下缩进,这样可以看得更清楚些。有没有发现问题?

你这个代码没有主程序的死循环,所以在执行完for语句后,pc会跑到flash后面的未用空间,直到0xffff,然后再到0x0000(这是因为16位的地址线,最大只能到0xffff)重新启动执行。(<----经楼下的老师指导,这个理解值得验证)因为keil自带初始化的代码,所以你会看到p2口闪一下。

你可以这样改一下:
  1. ...
  2. while(1)
  3. {
  4.   fl=&flw[0];
  5.   for(m=0;m<3;m++)
  6.   {
  7.       p0=*fl;
  8.       mdelay(1000);
  9.       fl++;
  10.   }
  11. }
离线xcf93
发帖
1330
只看该作者 4楼 发表于: 2007-12-06
应该是c51吧
离线BA5RW
发帖
48263
只看该作者 5楼 发表于: 2007-12-06
'
  1. void main()
  2. {
  3.   unsigned char flw[]={0x0f,0xee,0xcc,0xf0};
  4.   unsigned char *fl;
  5.   unsigned int m;
  6.   fl=&flw[0];
  7.   for(m=0;m<3;m++)
  8.   {
  9.       p0=*fl;
  10.       mdelay(1000);
  11.       fl++;
  12.   }
  13. }

我把您的代码重新编辑了一下缩进,这样可以看得更清楚些。有没有发现问题?
你这个代码没有主程序的死循环,所以在执行完for语句后,pc会跑到flash后面的未用空间,直到0xffff,然后再到0x0000(这是因为16位的地址线,最大只能到0xffff)重新启动执行。因为keil自带初始化的代码,所以你会看到p2口闪一下。
你可以这样改一下:
  1. ...
  2. while(1)
  3. {
  4.   fl=&flw[0];
  5.   for(m=0;m<3;m++)
  6.   {
  7.       p0=*fl;
  8.       mdelay(1000);
  9.       fl++;
  10.   }
  11. }
'
是的,今天我在前面加了for(;;)之后,初始化现象消除了,但是之后把for(;;)去掉,开帖现象也没了,要调整一行语句后又出现,有点见鬼了.不过已经解决问题了,看来演示程序要记住加一个总循环.谢谢!
离线xidongs
发帖
149
只看该作者 6楼 发表于: 2007-12-07
起码可以考虑有这样的问题
  fl=&flw[0]; 可以写成 fl=flw;

  因为数组的首地址就是flw[0].

  而循环
  for(m=0;m<3;m++)

    实际上只是执行了3次。而不是4次。
离线BA5RW
发帖
48263
只看该作者 7楼 发表于: 2007-12-09
'
起码可以考虑有这样的问题
  fl=&flw[0]; 可以写成 fl=flw;
  因为数组的首地址就是flw[0].
  而循环
  for(m=0;m<3;m++)
    实际上只是执行了3次。而不是4次。
'
是4次啊,我用了四种花样.
离线xidongs
发帖
149
只看该作者 8楼 发表于: 2007-12-12
'
是4次啊,我用了四种花样.
'



你用了4中花样,只是在数组里面定义了4种,而循环负责取出,对吧。
至少for(m=0;m<3;m++),的次数为3   既,m=0 1 2
当m=3的时候,已经不能满足条件m<3了。

我并不想责怪你的c水平不高,而是想说你不认真。
离线BA5RW
发帖
48263
只看该作者 9楼 发表于: 2007-12-13
'
你用了4中花样,只是在数组里面定义了4种,而循环负责取出,对吧。
至少for(m=0;m<3;m++),的次数为3   既,m=0 1 2
当m=3的时候,已经不能满足条件m<3了。
我并不想责怪你的c水平不高,而是想说你不认真。
'
晕啊,我刚开始尝试用C。但是我是通过实践得到的结果,循环取出的的确是4种,m=3时候的确是第3次循环了,但是m=3偏偏满足m<3的条件(不是<=,这很重要的) 所以循环还要继续一次,于是4种花样就调出来了。
我面向的是中小学生,不能用太抽象跨度太大的思维方法和他们一起学习实验研究的,但这样做思路更清晰,有许多细节就一念之差有人能成功有人即使是高手也会郁闷的。所以一定要实践,要提倡从小养成动手检验想法的习惯……这是一个典型的案例。
在线BG4UVR
发帖
11288
只看该作者 10楼 发表于: 2007-12-13
哈,晕……
离线FMer
发帖
2549
只看该作者 11楼 发表于: 2007-12-13
for(m=0;m<3;m++)
{
  ....;
}
会循环4次??
第一次,m=0;执行循环体内容;m+1
第二次,m=1;执行循环体内容;m+1
第三次,m=2;执行循环体内容;m+1
这个时候m已经等于3了,
然后再在for语句的小括号里判断m<3吗?不,m=3于是结束循环.
证明完毕.
离线BA5RW
发帖
48263
只看该作者 12楼 发表于: 2007-12-13
'
哈,晕……
'

'
for(m=0;m<3;m++)

{
....;
}
会循环4次??
第一次,m=0;执行循环体内容;m+1
第二次,m=1;执行循环体内容;m+1
第三次,m=2;执行循环体内容;m+1
这个时候m已经等于3了,
然后再在for语句的小括号里判断m<3吗?不,m=3于是结束循环.
证明完毕.
'

刚收到bg6agb短信,重新按照所发的帖子加了无限循环后整理的代码如下:

void main()
{
  unsigned char flw[]={0x0f,0xee,0xcc,0xf0};
  unsigned char *fl;
  unsigned int m;
  for(;;)
  {
  fl=&flw[0];
  for(m=0;m<3;m++)
      {
          p0=*fl;
          mdelay(1000);
          fl++;
      }
  }
}

重新搭建了实验电路,发现不是原先的四种花样了,哈!俺意识到反驳丛老师的态度和问题的严重性不亚于嫦娥拼图的错误!为了严惩自己学习作风极不严谨的缺陷,俺决定如下:
本人郑重在此向丛喜东老师致以崇高的敬意和歉意!也感谢以上发出疑问和提醒的各位老师朋友,并在此做深刻的检讨!是我错了!

我说过我现在刚开始和学生一起学习C语言的,发现自己的错误自然会及时反馈给我的学生们,但是我未经检验冒然反驳丛喜东老师的疑问致使几位朋友陷入迷惘这是俺的不可饶恕的罪过,为了改正自己的学习和技术探讨的缺点,谨以此帖敬醒自己,并希望得到众弟兄的帮助,以最快的速度把C学会学好,同时能传给我的那些可爱的学生们!希望大家宽宏大量,继续指导,谢谢大家!
在线BG4UVR
发帖
11288
只看该作者 13楼 发表于: 2007-12-14
实际我觉得5rw老师能为了这个问题,再次专门做一下实验,说明还是非常严谨的,值得我们晚辈学习。
离线FMer
发帖
2549
只看该作者 14楼 发表于: 2007-12-14
a,mr.chou,you are so cute.咔咔咔~
离线xidongs
发帖
149
只看该作者 15楼 发表于: 2007-12-18
'
刚收到bg6agb短信,重新按照所发的帖子加了无限循环后整理的代码如下:
void main()
{
  unsigned char flw[]={0x0f,0xee,0xcc,0xf0};
  unsigned char *fl;
  unsigned int m;
  for(;;)
  {
  fl=&flw[0];
  for(m=0;m<3;m++)
      {
          p0=*fl;
          mdelay(1000);
          fl++;
      }
  }
}
重新搭建了实验电路,发现不是原先的四种花样了,哈!俺意识到反驳丛老师的态度和问题的严重性不亚于嫦娥拼图的错误!为了严惩自己学习作风极不严谨的缺陷,俺决定如下:
本人郑重在此向丛喜东老师致以崇高的敬意和歉意!也感谢以上发出疑问和提醒的各位老师朋友,并在此做深刻的检讨!是我错了!
我说过我现在刚开始和学生一起学习C语言的,发现自己的错误自然会及时反馈给我的学生们,但是我未经检验冒然反驳丛喜东老师的疑问致使几位朋友陷入迷惘这是俺的不可饶恕的罪过,为了改正自己的学习和技术探讨的缺点,谨以此帖敬醒自己,并希望得到众弟兄的帮助,以最快的速度把C学会学好,同时能传给我的那些可爱的学生们!希望大家宽宏大量,继续指导,谢谢大家!
'


ba5rw 您好,您客气了,这个问题很常见的,只是对语言不熟悉造成的,我也只不过是在多年的程序开发过程中有了一点点经验而已,作为一名普通的无线电爱好者,我对您的认真、执着、坦诚表示由衷的敬佩,希望以后多交流。
离线xidongs
发帖
149
只看该作者 16楼 发表于: 2007-12-18
'
  1. void main()
  2. {
  3.   unsigned char flw[]={0x0f,0xee,0xcc,0xf0};
  4.   unsigned char *fl;
  5.   unsigned int m;
  6.   fl=&flw[0];
  7.   for(m=0;m<3;m++)
  8.   {
  9.       p0=*fl;
  10.       mdelay(1000);
  11.       fl++;
  12.   }
  13. }

我把您的代码重新编辑了一下缩进,这样可以看得更清楚些。有没有发现问题?
你这个代码没有主程序的死循环,所以在执行完for语句后,pc会跑到flash后面的未用空间,直到0xffff,然后再到0x0000(这是因为16位的地址线,最大只能到0xffff)重新启动执行。因为keil自带初始化的代码,所以你会看到p2口闪一下。
你可以这样改一下:
  1. ...
  2. while(1)
  3. {
  4.   fl=&flw[0];
  5.   for(m=0;m<3;m++)
  6.   {
  7.       p0=*fl;
  8.       mdelay(1000);
  9.       fl++;
  10.   }
  11. }
'

大家好,关于这个问题,我想在说明一下。
通常的c编译器,在c的程序到达理论结束的时候,因为防止跑偏,汇编指令会生成自循环跳转的代码,以保持单片机现在的状态。

举个例子:
void main(void)
{
     
char i;
   
i=0;
while (i<10)
    {
    // place your code here
 
      i+=1;
    };
}


这个程序的汇编指令如下:


_main:
;     19
;     20 char i;
;     21
;     22 i=0;
;      i -> r17
     ldi r17,low(0)
;     23 while (i<10)
_0x3:
     cpi r17,10
     brsh _0x5
;     24     {
;     25     // place your code here
;     26
;     27       i+=1;
     subi r17,-low(1)
;     28     };
     rjmp _0x3
_0x5:
;     29 }
_0x6:
     rjmp _0x6



在最后的两句

_0x6:
     rjmp _0x6

很明显看出是自跳转的代码。
至少,avr的编译器是这样的。至于51吗,我并没又测试,几年前用51,现在没有那个开发环境了。我认为keil是个比较成熟的环境,应该可以考虑跑偏问题的。

也希望那位有51环境的朋友帮助测试一下,89s51带有看门狗还是很好的。
离线BA5RW
发帖
48263
只看该作者 17楼 发表于: 2007-12-19
void main(void)
{

char i;

i=0;
while (i<10)
{
// place your code here

i+=1;
};
}
在KEIL中生成的汇编指令整理如下:
ljmp c:0003
c mov r0,#0x7f
clr a
c mov @r0,a
djnz r0,c:0006
mov sp,#0x07
ljmp main(c:000f)
main
c:000f:clr a
mov r7,a
cinc r7
cjne r7,#0x0a,c:0011
ret
可能是先入为主,年龄又大了,暑假前想学AVR和C,结果一头雾水,看似懂了一点,能编些简单的小程序,但一忙起来两个月后再看AVR又是茫然,想想是战略性失误,因为不熟悉AVR的硬件结构,拿51的思路去学AVR的C语言更是对不上号,最近几天回到51上来,经过包括丛老师在内的朋友们的指点和批评,显然又领悟了不少东西,现在能自己写些器件的应用文件,并引用到自己的编程实验代码中来,感到C的确比汇编功能强大,效率高!不过我还是感觉学生开头学某型号的汇编极为重要,不知道看法是否偏激?
在线BG4UVR
发帖
11288
只看该作者 18楼 发表于: 2007-12-19
'
大家好,关于这个问题,我想在说明一下。
通常的c编译器,在c的程序到达理论结束的时候,因为防止跑偏,汇编指令会生成自循环跳转的代码,以保持单片机现在的状态。
举个例子:
void main(void)
{
     
char i;
   
i=0;
while (i<10)
    {
    // place your code here
 
      i+=1;
    };
}
这个程序的汇编指令如下:
_main:
;     19
;     20 char i;
;     21
;     22 i=0;
;      i -> r17
     ldi r17,low(0)
;     23 while (i<10)
_0x3:
     cpi r17,10
     brsh _0x5
;     24     {
;     25     // place your code here
;     26
;     27       i+=1;
     subi r17,-low(1)
;     28     };
     rjmp _0x3
_0x5:
;     29 }
_0x6:
     rjmp _0x6
在最后的两句
_0x6:
     rjmp _0x6
很明显看出是自跳转的代码。
至少,avr的编译器是这样的。至于51吗,我并没又测试,几年前用51,现在没有那个开发环境了。我认为keil是个比较成熟的环境,应该可以考虑跑偏问题的。
也希望那位有51环境的朋友帮助测试一下,89s51带有看门狗还是很好的。
'

看了您的测试后,我也用winavr编译了一个简单的测试程序。经过查看汇编代码,结果和您所说的相同,即执行完主程序后,代码进入了一个跳转到本身地址的死循环。

但请注意,这只是一个跳转到本身地址的死循环,它并不会反复执行main函数。所以大家在写程序的时候,为了能使您的代码能一直按照你设计的逻辑工作,主程序循环,请一定要使用死循环。不然后果就是,每次上电,你的代码只执行一次,这实际正是我们大多数时候不想要的。
离线BG6AGB
发帖
915
只看该作者 19楼 发表于: 2007-12-19
在keil下简单的写了个测试代码,主要看看不加while(1),程序会编译成什么样子
下面是两段程序,分别是加循环和不加循环的
  1. c:0x0000   020003   ljmp   c:0003
  2. c:0x0003   787f   mov     r0,#0x7f
  3. c:0x0005   e4     clr     a
  4. c:0x0006   f6     mov     @r0,a
  5. c:0x0007   d8fd   djnz   r0,c:0006
  6. c:0x0009   758108   mov     sp(0x81),#0x08
  7. c:0x000c   02000f   ljmp   main(c:000f)
  8.   6: void main(void)
  9.   7: {
  10.   8:       unsigned char temp;
  11.   9:       temp=1;
  12.   10: //     while(1);
  13. c:0x000f   750801   mov     0x08,#0x01
  14.   11: }
  15. c:0x0012   22     ret    
  16. c:0x0013   00     nop    
  17. c:0x0014   00     nop    
  18. c:0x0015   00     nop    



  1. c:0x0000   020003   ljmp   c:0003
  2. c:0x0003   787f   mov     r0,#0x7f
  3. c:0x0005   e4     clr     a
  4. c:0x0006   f6     mov     @r0,a
  5. c:0x0007   d8fd   djnz   r0,c:0006
  6. c:0x0009   758108   mov     sp(0x81),#0x08
  7. c:0x000c   02000f   ljmp   main(c:000f)
  8.   6: void main(void)
  9.   7: {
  10.   8:       unsigned char temp;
  11.   9:       temp=1;
  12. c:0x000f   750801   mov     0x08,#0x01
  13.   10:       while(1);
  14. c:0x0012   80fe   sjmp   c:0012
  15.   11: }
  16. c:0x0014   22     ret    
  17. c:0x0015   00     nop    
  18. c:0x0016   00     nop    
  19. c:0x0017   00     nop    


看来keil 没有 gcc聪明啊