51流水灯操作时遇到的简单问题,想要问的在最下面,望解答。

运行环境:在51系列中使用的是AT89C52芯片,编译软件为keil4。
目的:在定时器流水灯项目中,按下按钮使led全灭,再按一次使led在熄灭位置亮起,继续进行流水灯操作。
问题:在实现过程中,设定了一个整形变量m,为了去保存P1的状态,然后再把m赋予P1实现原位置亮起,但发现不成,于是单独写了个定时流水灯程序,不加按钮,探究整形m与P1的关系
代码如下:


#include<reg52.h>
#include<intrins.h>

int i = 0;
void timer1_init()
{
    TMOD = 0x10; 
    TH1 = 0x4C;    
    TL1 = 0x00; 
    EA = 1;            
    ET1 = 1;        
    TR1 = 1;         
}

void main()
{
    int x=0,m;

    timer1_init();
     P1 = 0xfe;
    while(1)
    {
        if(i==40)
        {
             P1 =_crol_(P1,1);
          i = 0; 
            x++;
        }
        if(x==2)
        {
            m=P1;
            P1=0xff;
        }
        if(x==4)
        {
            P1=m;
        }
        if(x==6)
        {
            P1=0xfe;
            x=0;
        }
    }
}

void timer1() interrupt 3
{
    TH1 = 0x4C;    
    TL1 = 0x00;
    i++;
}

现象:流水灯正常工作,在第二位时熄灭,等待后未在原位亮起继续流水灯操作,长时间等待后,从第一位亮起
想要了解的问题 :一.sfr P1=0x80特殊寄存器是否能被指针指向
二.整形变量保存的地址是以二进制存储后输出来是否仍为地址,是否是存储溢出导致无法实现目标
三.怎么做才能用一个量,而不是数组来实现目标

不知道有没有有缘人看到这个问题,问的比较基础,希望能够解答一二。

参考chatGPT的内容和自己的思路,
1.可以将特殊功能寄存器(SFR)P1的地址存储在指针中,并通过指针对其进行操作。例如,可以使用以下代码将P1的地址存储在指针p中,并通过p对其进行操作:
unsigned char *p;
p = 0x80;
*p = 0xff;
2.整型变量保存的地址是一个十六进制数,可以通过使用%p格式符将其以十六进制形式输出。如果地址存储溢出,那么程序将会出现错误,无法正常工作。

3.可以使用一个标志变量来实现目标,例如将变量flag初始化为0,当按下按钮时将flag设置为1,当再次按下按钮时将flag设置为0,并将LED灯在熄灭位置亮起。然后在主循环中检查flag的值,如果为1,则不执行流水灯操作,如果为0,则执行流水灯操作。以下是代码:

#include <reg52.h>
#include <intrins.h>

int i = 0;
int flag = 0;

void timer1_init()
{
TMOD = 0x10;
TH1 = 0x4C;
TL1 = 0x00;
EA = 1;
ET1 = 1;
TR1 = 1;
}

void main()
{
int x = 0;
int m = 0;
timer1_init();
P1 = 0xfe;

while(1)
{
    if (flag == 0)
    {
        if (i == 40)
        {
            P1 =_crol_(P1,1);
            i = 0; 
            x++;
        }
    
        if (x == 6)
        {
            P1 = 0xfe;
            x = 0;
        }
    }
    
    if (P3_0 == 0)
    {
        flag = 1;
        P1 = 0xff;
        while (P3_0 == 0);
    }
    
    if (flag == 1 && P3_0 == 0)
    {
        flag = 0;
        P1 = 0x7f;
        while (P3_0 == 0);
    }
}
}

void timer1() interrupt 3
{
TH1 = 0x4C;
TL1 = 0x00;
i++;
}

在这个代码中,当按下按钮时,程序将flag设置为1,将LED全灭。当再次按下按钮时,程序将flag设置为0,并将LED在熄灭位置亮起。在主循环中,如果flag为1,则不执行流水灯操作,如果为0,则执行流水灯操作。

回答不易,还请一定采纳!!!

“Devil组”引证GPT后的撰写:

  • 1.sfr P1=0x80特殊寄存器是否能被指针指向?
    sfr (Special Function Register) 是一种特殊的寄存器,用于控制硬件模块的操作,例如控制 LED 灯的亮灭、串口通信等。在 Keil C 中,我们可以使用 sfr 关键字来定义和访问这些特殊寄存器。

sfr P1=0x80; // 将地址 0x80 定义为 P1 特殊寄存器

因为 P1 是一个特殊寄存器,其地址不能被修改,因此不能使用指针来访问 P1。

  • 2.整形变量保存的地址是以二进制存储后输出来是否仍为地址,是否是存储溢出导致无法实现目标?
    整形变量在内存中是以二进制形式存储的,可以通过取地址符号 & 来获取其在内存中的地址。例如:

int a = 10;
int *p = &a; // 将 a 的地址赋值给指针 p

通过指针 p,我们可以访问和修改变量 a 的值。

存储溢出不会导致无法实现目标,但是可能会导致程序出现不可预期的错误或崩溃。例如,当我们定义一个 char 类型的变量,但是给它赋值超过了其所能存储的范围时,就会发生溢出。如果此时将这个变量的值赋给一个 int 类型的变量,那么这个 int 变量的值就会变成一个错误的值。

  • 3.怎么做才能用一个量,而不是数组来实现目标?
    可以使用一个变量来保存 P1 寄存器的状态,例如:
int m = 0xff; // 初始化为全亮
while (1) {
if (i == 40) {
P1 = crol(P1, 1);
i = 0;
}
if (x == 2) {
m = P1; // 保存 P1 的状态
P1 = 0xff; // 全灭
}
if (x == 4) {
P1 = m; // 恢复 P1 的状态
}
if (x == 6) {
P1 = 0xfe; // 在熄灭位置亮起
x = 0;
}
}

用变量 m 来保存 P1 的状态,当需要全灭时,将 P1 的值设为 0xff,此时将当前的 P1 状态保存到变量 m 中。当需要恢复原来的状态时,将变量 m 的值赋给 P1 即可。这样就可以通过一个变量来保存 P1 的状态,而不需要使用数组。

该回答引用ChatGPT

1、sfr P1=0x80特殊寄存器是否能被指针指向?

sfr P1=0x80是特殊功能寄存器,它对应的是单片机的P1口,可以用来控制单片机的外设。对于sfr寄存器,不能使用指针直接访问其内部,需要使用它的名称来访问。因此,不能将指针指向sfr P1=0x80寄存器。

2、整形变量保存的地址是以二进制存储后输出来是否仍为地址,是否是存储溢出导致无法实现目标?

整型变量在内存中存储的地址是以二进制的形式表示的,输出时会将其转换为十六进制或者十进制等其他表示方式。存储的地址是不会因为输出方式的不同而改变的。

在代码中,整型变量m的值是通过将P1的状态保存在其中,再赋值给P1实现原位置亮起。如果存储溢出导致无法实现目标,则可能是由于m的类型不够大,无法保存P1的状态。在这种情况下,可以考虑使用更大的数据类型,如长整型或者使用一个数组来保存P1的状态。

3、怎么做才能用一个量,而不是数组来实现目标?

可以将P1的状态用一个字节的二进制位来表示,每个二进制位代表P1的一个引脚的状态。这样就可以用一个字节的变量来保存P1的状态。例如,如果P1的0号引脚为高电平,1号引脚为低电平,那么用一个字节的变量保存P1的状态可以表示为0b11111110。在需要保存P1状态时,将P1的状态写入该变量中,需要恢复时,将该变量的值写入P1中即可。

参考GPT和自己的思路,
1 关于特殊寄存器P1能否被指针指向的问题:
特殊寄存器P1是8051芯片的一个8位IO口,其物理地址为0x90,它是一个特殊的内部寄存器,不能被程序访问。因此,不能将指针指向特殊寄存器P1,但可以直接使用P1的符号名称进行访问。
2 关于整形变量的存储地址问题:
整形变量在内存中占用2个字节,保存的是其值而不是地址。因此,将整型变量赋值给P1,实际上是将其值赋给了P1寄存器,而不是将其存储地址赋给了P1。如果存在存储溢出的问题,那么赋值给P1的值可能会不正确,但这并不会影响整型变量本身的存储。

3 如何用一个变量实现目标:
可以使用一个标志变量来表示LED的状态,如果标志变量为0,则LED处于熄灭状态;如果标志变量为1,则LED处于点亮状态。当按下按钮时,将标志变量取反即可实现LED的开关操作。例如:

#include<reg52.h>
#include<intrins.h>

sbit LED=P1^0; //使用sbit定义LED引脚

void timer1_init()
{
    TMOD = 0x10; 
    TH1 = 0x4C;    
    TL1 = 0x00; 
    EA = 1;            
    ET1 = 1;        
    TR1 = 1;         
}

void main()
{
    int i = 0;
    bit flag = 1; //初始状态为LED点亮
    timer1_init();
    while(1)
    {
        if(i==40)
        {
            P1 =_crol_(P1,1);
            i = 0; 
        }
        if(P3_2 == 0) //检测按键是否按下
        {
            delay(10); //延时去抖动
            if(P3_2 == 0) //再次检测按键是否按下
            {
                flag = ~flag; //取反标志位
                while(!P3_2); //等待按键松开
            }
        }
        if(flag)
        {
            LED = 1; //LED点亮
        }
        else
        {
            LED = 0; //LED熄灭
        }
    }
}

void timer1() interrupt 3
{
    TH1 = 0x4C;    
    TL1 = 0x00;
    i++;
}

void delay(int xms) //延时函数
{
    int i,j;
    for(i=xms;i>0;i--)
        for(j=110;j>0;j--);
}

这样就可以用一个变量来控制LED的开关,而不需要使用一个数组或者额外的存储空间了。

根据你的描述,问题是当按下按钮后,P1的状态没有成功保存在变量m中,再按一次按钮时不能恢复到原来的状态。根据你提供的代码,似乎没有实现按钮的部分,因此我假设你的问题是无法将P1的状态保存在变量m中,然后再恢复到原来的状态。

在你的代码中,你使用了整型变量m来保存P1的状态。但是,你并没有在代码中实现保存和恢复P1状态的功能。因此,m的值永远是0,因为你没有给它赋值。在你的代码中,你应该在需要保存P1状态的地方将P1的值赋给m,然后在需要恢复P1状态的地方将m的值赋给P1。例如:

if(x == 2) {
    m = P1; // 保存P1的状态
    P1 = 0xff; // 关闭所有LED灯
}
if(x == 4) {
    P1 = m; // 恢复P1的状态
}

此外,你应该检查你的代码逻辑是否正确。在你的代码中,x的值只会在i等于40时增加1。当x等于2、4或6时,你的代码会改变P1的状态。因此,你需要确保当x等于2时,P1的状态已经保存在m中。当x等于4时,你需要确保m的值是先前保存的P1的状态。

以下是一个可能的完整代码示例。该代码使用一个变量ledOn来记录LED当前是否处于开启状态。按下按钮时,它会将ledOn的值取反,并相应地控制LED的状态。

#include <reg52.h>

// 初始化定时器
void timer_init() {
    TMOD = 0x01;  // 定时器0,工作方式1
    TH0 = 0xFC;   // 65536 - 5000
    TL0 = 0x18;   // 5000 的低8位
    ET0 = 1;      // 开启定时器0中断
    EA = 1;       // 开启总中断允许
    TR0 = 1;      // 开始计时
}

// 延时函数,用于使LED流水灯效果更加明显
void delay(unsigned int t) {
    while (t--);
}

void main() {
    unsigned char led = 0xFE; // 初始 LED 灯状态
    bit ledOn = 1;  // 初始状态 LED 开启
    timer_init();
    while (1) {
        // 按钮按下,取反 LED 状态
        if (!P3_0) {
            ledOn = !ledOn;
            while (!P3_0);  // 等待按钮松开
        }
        // 根据 LED 开启状态设置 LED 灯状态
        if (ledOn) {
            P1 = led;
        } else {
            P1 = 0xFF;
        }
        // LED 流水灯效果
        delay(20000);
        led = _crol_(led, 1);
    }
}

// 定时器中断,用于更新 LED 灯状态
void timer_isr() interrupt 1 {
    TH0 = 0xFC;
    TL0 = 0x18;
} 

注意,以上代码只是一个示例,您可能需要根据您的具体硬件和需求进行适当修改。另外,如果您使用的是 AT89C52 芯片,请根据数据手册中的定时器配置参数来调整代码中的相关设置。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:

  1. sfr P1=0x80特殊寄存器是否能被指针指向?

答:sfr P1是一个特殊寄存器,可以使用指针来访问它,但需要使用特殊的指针类型__sfr。例如,可以使用以下代码访问P1:

__sfr __at(0x90) P1_ptr;
P1_ptr = 0x00; // 将P1设置为全灭状态
P1_ptr = 0xff; // 将P1设置为全亮状态

  1. 整形变量保存的地址是以二进制存储后输出来是否仍为地址,是否是存储溢出导致无法实现目标?

答:整型变量在内存中也有自己的地址,在程序中可以使用&运算符获取它们的地址。例如,可以使用以下代码获取变量m的地址:

int m;
int* m_ptr = &m;

二进制存储后输出的值并不是真正的地址,而是它的二进制表示。存储溢出通常是指在使用一个变量时,它的值超过了它能够存储的范围,导致发生意料之外的错误。在上述代码中,存储溢出并不是影响实现目标的原因。

  1. 怎么做才能用一个量,而不是数组来实现目标?

答:可以使用一个变量来记录P1的值。例如,在代码中加入以下语句:

int m = P1;

这将把P1的值保存在变量m中。然后,可以使用以下语句将m的值赋给P1:

P1 = m;

使用一个变量而不是数组的好处是它占用的内存更少,并且可以更方便地进行读写操作。但是,如果需要保存多个值,或者需要对值进行排序或搜索等操作,那么数组可能更适合。
如果我的回答解决了您的问题,请采纳!