打印

向高手求助,让一个51单片机流水灯和PWM调光难住了。

[复制链接]
7298|28
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lshf0|  楼主 | 2011-4-2 20:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
有二个问题如下。问了好多人,都没得到解答。
1流水灯
void main()
{
    uchar temp;
    temp=0xfe;
    while(1)
    {
        uchar x,y,m;
        for(x=8;x>0;x--)
        {
            P1=temp;
            delay_10ms(100);
            temp=_crol_(temp,1);       //循环左移
        }
        P1=0xff;                       //熄灭
        delay_10ms(100);
        for(y=8;y>0;y--)
        {  
            temp=_cror_(temp,1);       //循环右移
            P1=temp;
            delay_10ms(100);
        }
        P1=0xff;
        delay_10ms(100);
        for(m=2;m>0;m--)           //闪二次
        {
        P1=0;
        delay_10ms(100);
        P1=0xff;
        delay_10ms(100);
        }
    }
}
这程序看上去没错,可用起来,左移完了,右移,只有最右第一个灯亮,一直到闪烁开始。不会右移。原因是最后一个闪二次的FOR语句,不用这个FOR就正常,一用就不行。不知道为什么?

2PWM调光,也是单片机上接一个LED.程序如下。
void main()
#define pwm_h 0xff;
#define pwm_l 0;
{   
    uint cycle=500;         //周期
    P1=0xff;
    delay_10ms(50);
    while(1)
    {   
        uint i;
        //P1=0xff;
        //delay_10ms(200);
        for(i=1;i<=cycle;i++)     先渐亮
        {
            P1=pwm_l;
            delay_pwm(i);
            P1=pwm_h;
            delay_pwm(cycle+400-i);
        }
       // delay_10ms(100);
        for(i=1;i<=cycle;i++)      再渐灭
        {
            P1=pwm_l;
            delay_pwm(cycle-i);
            P1=pwm_h;
            delay_pwm(i+400);               
        }
        P1=0xff;                       灭灯等5S
        delay_10ms(500);
        
    }   
}
结果是渐灭后,会再宛然亮一下。再灭等5S。灭了后再亮一下,不知道从哪来的????

相关帖子

沙发
jinkeqi| | 2011-4-2 20:44 | 只看该作者
帮顶..期待高手帮你解决.

使用特权

评论回复
板凳
lfb112| | 2011-4-2 23:17 | 只看该作者
作为新手,要先学会自己调试,不要一遇到问题自己都没调,就问别人,像你的这种情况完全可以通过断点调试就可以解决了,如果不会调试赶紧找个会的人帮你,并且一定要学会,因为以后会遇到很多莫名其妙的问题的。

使用特权

评论回复
地板
patson| | 2011-4-3 00:23 | 只看该作者
对于第一个问题.你可以参考一下下面的程序.其原理都是一样的了.你尽量使用左移或者右移命令来实现它,令代码更加简洁明了.要实现左移完后实现右移的话可以使用右移命令">>"同时增加一条判断命令,右移代码也和左移命令是一样的了.而led的初值则应定为0x80.最后建议你在编写代码时如果是重复使用的代码尽量做到使用子函数.因为你的代码写得太乱了,我也不知道为什么了会出现你所说的情况了.虽然程序小.但是在大程序时.就会令自己很难看得懂了.对于第二个问题.我建议你使用数组来实现它,定义一组数组.然后通过循环来实现它.
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
void delay(uint i) //延时2ms子程序
{
uchar j,k;
for(i;i>0;i--)
        for(j=4;j>0;j--)
                for(k=248;k>0;k--);
}
void main(void)
{
uchar n,led=0x01;
        while(1)
        {
                delay(250);//延时0.5s
                P0=~(led<<n);//左移命令,同时LED灯是低电平亮
                n++;
                if (n>=8)
                n=0;
        }
}

使用特权

评论回复
5
stycx| | 2011-4-3 00:26 | 只看该作者
二楼说的没错  但是仿真器不一定每个人都有

不过在我看来 问题在于楼主还无法理解程序中每句的含义
流水灯程序中
temp=0xfe;
应该放在
    while(1)
    {

不然 后面 闪灯部分那句 temp=0xff;
会把灯关了

使用特权

评论回复
6
stycx| | 2011-4-3 00:41 | 只看该作者
PWM程序中
声明   uint i;
应该放在程序的开头

就楼主的描述程序不是很清楚  建议分别调试 逐亮 逐灭  然后再合起来

使用特权

评论回复
7
lshf0|  楼主 | 2011-4-3 04:36 | 只看该作者

RE: 向高手求助,让一个51单片机流水灯和PWM调光难住了。

有二个问题如下。问了好多人,都没得到解答。
1流水灯
void main()
{
    uchar temp;
    temp=0xfe;
    while(1)
    {
        uchar x,y,m;
        for(x=8;x>0;x--)
        {
            P1=temp;
            delay_10ms(100);
            temp=_crol_(temp,1);       //循环左移
        }
        P1=0xff;                       //熄灭
        delay_10ms(100);
        for(y=8;y>0;y--)
        {  
            temp=_cror_(temp,1);       //循环右移
            P1=temp;
            delay_10ms(100);
        }
        P1=0xff;
        delay_10ms(100);
        for(m=2;m>0;m--)           //闪二次
        {
        P1=0;
        delay_10ms(100);
        P1=0xff;
        delay_10ms(100);
        }
    }
}
这程序看上去没错,可用起来,左移完了,右移,只有最右第一个灯亮,一直到闪烁开始。不会右移。原因是最后一个闪二次的FOR语句,不用这个FOR就正常,一用就不行。不知道为什么?

2PWM调光,也是单片机上接一个LED.程序如下。
void main()
#define pwm_h 0xff;
#define pwm_l 0;
{   
    uint cycle=500;         //周期
    P1=0xff;
    delay_10ms(50);
    while(1)
    {   
        uint i;
        //P1=0xff;
        //delay_10ms(200);
        for(i=1;i<=cycle;i++)     先渐亮
        {
            P1=pwm_l;
            delay_pwm(i);
            P1=pwm_h;
            delay_pwm(cycle+400-i);
        }
       // delay_10ms(100);
        for(i=1;i<=cycle;i++)      再渐灭
        {
            P1=pwm_l;
            delay_pwm(cycle-i);
            P1=pwm_h;
            delay_pwm(i+400);               
        }
        P1=0xff;                       灭灯等5S
        delay_10ms(500);
        
    }   
}
结果是渐灭后,会再宛然亮一下。再灭等5S。灭了后再亮一下,不知
道从哪来的????


补充:
谢谢大家的回复
1,这二个问题是测试中无意发现的。就想搞明白,调试过了,调试的结果和实际的结果是一样的。流水灯,R3寄存器,在有FOR语句时,其值在右移是不会变化。观察汇编程序发现有一点点不同,因为不太熟悉汇编,还是研究当中。PWM也是最后会出一个都为低电平 的值。不知道从哪来的,搞了一天也没研究出来。毕竟不是太熟悉。
2 当然这个程序只要稍修改就可以完成功能,就是因为发现有这个问题,但又不知道,问题在哪,才写成这样的。
3 关于下面的

使用特权

评论回复
8
lshf0|  楼主 | 2011-4-3 06:10 | 只看该作者
有二个问题如下。问了好多人,都没得到解答。
1流水灯
void main()
{
    uchar temp;
    temp=0xfe;
    while(1)
    {
        uchar x,y,m;
        for(x=8;x>0;x--)
        {
            P1=temp;
            delay_10ms(100);
            temp=_crol_(temp,1);       //循环左移
        }
        P1=0xff;                       //熄灭
        delay_10ms(100);
        for(y=8;y>0;y--)
        {  
            temp=_cror_(temp,1);       //循环右移
            P1=temp;
            delay_10ms(100);
        }
        P1=0xff;
        delay_10ms(100);
        for(m=2;m>0;m--)           //闪二次
        {
        P1=0;
        delay_10ms(100);
        P1=0xff;
        delay_10ms(100);
        }
    }
}
这程序看上去没错,可用起来,左移完了,右移,只有最右第一个灯亮,一直到闪烁开始。不会右移。原因是最后一个闪二次的FOR语句,不用这个FOR就正常,一用就不行。不知道为什么?

2PWM调光,也是单片机上接一个LED.程序如下。
void main()
#define pwm_h 0xff;
#define pwm_l 0;
{   
    uint cycle=500;         //周期
    P1=0xff;
    delay_10ms(50);
    while(1)
    {   
        uint i;
        //P1=0xff;
        //delay_10ms(200);
        for(i=1;i<=cycle;i++)     先渐亮
        {
            P1=pwm_l;
            delay_pwm(i);
            P1=pwm_h;
            delay_pwm(cycle+400-i);
        }
       // delay_10ms(100);
        for(i=1;i<=cycle;i++)      再渐灭
        {
            P1=pwm_l;
            delay_pwm(cycle-i);
            P1=pwm_h;
            delay_pwm(i+400);               
        }
        P1=0xff;                       灭灯等5S
        delay_10ms(500);
        
    }   
}
结果是渐灭后,会再宛然亮一下。再灭等5S。灭了后再亮一下,不知
道从哪来的????


补充:
谢谢大家的回复
1,这二个问题是测试中无意发现的。就想搞明白,调试过了,调试的结果和实际的结果是一样的。流水灯,R3寄存器,在有FOR语句时,其值在右移是不会变化。观察汇编程序发现有一点点不同,因为不太熟悉汇编,还是研究当中。PWM也是最后会出一个都为低电平 的值。不知道从哪来的,搞了一天也没研究出来。毕竟不是太熟悉。
2 当然这个程序只要稍修改就可以完成功能,就是因为发现有这个问题,但又不知道,问题在哪,才写成这样的。
3关于5楼 说的,本来就是想左移完,有个全灭,再开始右移,不然最后一个灯会亮时间久些再右移。6楼说的,我加哪都没影响,都试过了。程序是一级一级调的。
4 我把PWM的程序简化一下,大家可以自己试验一下。这个程序看不出有什么错,调试时也没错,一直没有全为低电平的时候,但实际到板子上在最后一步点一个LED前会有一个全亮的过程。本来是由亮到渐灭的,灭以后,应该直接就亮一个。可中间有个全亮的不知道从哪来的。大家自己试验下。

#include<reg52.h>
#define uint  unsigned int     
void delay(uint t)
{
     while(--t);
}
void main()
{   
    uint i;
    for(i=1;i<=300;i++)
    {
        P1=0;
        delay(300-i);
        P1=0XFF;
        delay(i);
    }
    P1=0xfe;
    while(1) ;
}

使用特权

评论回复
9
wpj5188| | 2011-4-3 08:38 | 只看该作者
编译下把汇编代码截个图

使用特权

评论回复
10
stycx| | 2011-4-3 12:04 | 只看该作者
早上有空把流水灯代码测试一下 只要把右移代码部分的

temp=_cror_(temp,1);       //循环右移
P1=temp;

两句次序倒一下就可以
原因待查

使用特权

评论回复
11
jd吕凯| | 2011-4-3 12:10 | 只看该作者
你看看c:0x0018行,A里保存的是你左移后的数值(RL A),最后赋给R3,再到下一次循环时,R3是更新过的值,比如你第一次循环开始时(R3)=0xfe,到下一次就被更新到0xfd。这样左移是对的。你在看右移时的程序:c:0x0030——c:0x0033,A里的0xfe在右移一位后直接给了P1,却没有给R3更新,这样P0.7会点亮,但到下一次循环时R3仍然是把0xfe给了A,就这样循环8次,所以每一次都是同样的P0.7亮。
这个小问题把右移程序里的temp=_cror_(temp,1);       //循环右移
            P1=temp;
这两条语句换一下位置就解决了。
我也是初学者,说的不好,通过你这个问题,我也发现在C语言里的问题在对应的汇编程序就能看出来。不过还是希望高手能进一步指导!

无标题1.jpg (66.17 KB )

无标题1.jpg

无标题2.jpg (65.65 KB )

无标题2.jpg

无标题3.jpg (65.39 KB )

无标题3.jpg

使用特权

评论回复
12
lshf0|  楼主 | 2011-4-3 14:20 | 只看该作者
11楼说的对,R3是没有更新。把TEMP=0XFE;这句放到WHILE(1)里面就会更新。放到外面不会更新。这是一个问题?研究发现还有一个问题就是
我还是把TEMP=0XFE;放到外面,是不更新的,这时我只要把后面闪程序里的FOR语句去了,就OK了,或者把FOR语句换成IF语句完成相同的功能也能OK。大家可以试一下。不知道是为什么?希望有高手可以来解决一下。

使用特权

评论回复
13
lshf0|  楼主 | 2011-4-3 14:22 | 只看该作者
为了方便研究程序可用如下简单一下,问题是一样的。
void main()
{ uchar temp;   
     temp=0xfe;
     while(1)
     {
        uchar i,x=8;
        // temp=0xfe;
        for(i=8;i>0;i--)
        {     
            temp=_cror_(temp,1);
             P1=temp;
            delay_10ms(100);
        }
        //for(x=8;x>0;x--);
     }
}

使用特权

评论回复
14
chenhao198723| | 2011-4-3 16:24 | 只看该作者
好啊 !!!!!

使用特权

评论回复
15
aihe| | 2011-4-3 21:23 | 只看该作者
建议楼主用Keil里的模拟仿真器,单步运行,查看变量值就知道原因所在了
说实话,楼主基本功还待加强

使用特权

评论回复
16
stycx| | 2011-4-3 23:11 | 只看该作者
试了一下 我也觉得奇怪了  到底是编译器没设置好还是 有臭虫呀

使用特权

评论回复
17
aihe| | 2011-4-4 00:51 | 只看该作者
把编译器优化选项调低就正常了

使用特权

评论回复
18
jd吕凯| | 2011-4-4 11:30 | 只看该作者
18# aihe

我也是新手!请求高手详细解释一下,还有我在11L的疑问:为什么把     temp=_cror_(temp,1);       //循环右移
            P1=temp;
这两条调顺序又可以了。

使用特权

评论回复
19
lshf0|  楼主 | 2011-4-4 14:06 | 只看该作者
找到问题原因了,编译器的优化问题,第四级优化下面的东西

寄存器变量:如有可能,自动变量和函数参数分配到寄存器上。为这些变量保留的存储区就省略了。
局部公共子表达式删除:如果用一个表达式重复进行相同的计算,则保存第一次计算结果,后面有可能就用这结果。多余的计算就被删除

具体是哪个变量,哪个式子怎么产生的,还不太清楚,对汇编不熟悉。只知道能不能测过,就看在把累加器A中的值给P1前会不会先给R3更新一下。好像是R3的值要给R7,R7的值要给A,A再右移,然后再给R3,再给P1。如此循环就可以。如果最后A不给R3更新,A每次都保持一样。

有搞得清楚的请分析一下,是不是这样。是哪里跟哪里重复而被优化掉了

使用特权

评论回复
20
huangqi412| | 2011-4-4 20:57 | 只看该作者
直接用KEIL软仿真查问题吧。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

15

帖子

0

粉丝