运行环境:在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后的撰写:
sfr P1=0x80; // 将地址 0x80 定义为 P1 特殊寄存器
因为 P1 是一个特殊寄存器,其地址不能被修改,因此不能使用指针来访问 P1。
int a = 10;
int *p = &a; // 将 a 的地址赋值给指针 p
通过指针 p,我们可以访问和修改变量 a 的值。
存储溢出不会导致无法实现目标,但是可能会导致程序出现不可预期的错误或崩溃。例如,当我们定义一个 char 类型的变量,但是给它赋值超过了其所能存储的范围时,就会发生溢出。如果此时将这个变量的值赋给一个 int 类型的变量,那么这个 int 变量的值就会变成一个错误的值。
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大模型与博主波罗歌共同编写:
答:sfr P1是一个特殊寄存器,可以使用指针来访问它,但需要使用特殊的指针类型__sfr。例如,可以使用以下代码访问P1:
__sfr __at(0x90) P1_ptr;
P1_ptr = 0x00; // 将P1设置为全灭状态
P1_ptr = 0xff; // 将P1设置为全亮状态
答:整型变量在内存中也有自己的地址,在程序中可以使用&运算符获取它们的地址。例如,可以使用以下代码获取变量m的地址:
int m;
int* m_ptr = &m;
二进制存储后输出的值并不是真正的地址,而是它的二进制表示。存储溢出通常是指在使用一个变量时,它的值超过了它能够存储的范围,导致发生意料之外的错误。在上述代码中,存储溢出并不是影响实现目标的原因。
答:可以使用一个变量来记录P1的值。例如,在代码中加入以下语句:
int m = P1;
这将把P1的值保存在变量m中。然后,可以使用以下语句将m的值赋给P1:
P1 = m;
使用一个变量而不是数组的好处是它占用的内存更少,并且可以更方便地进行读写操作。但是,如果需要保存多个值,或者需要对值进行排序或搜索等操作,那么数组可能更适合。
如果我的回答解决了您的问题,请采纳!