我心飞扬 发表于 2025-4-10 18:46:59

请教高手:擎天柱核心板-Ai8051U-32K64-实现流水灯程序为何显示有些乱?

擎天柱核心板-Ai8051U-32K64-实现流水灯程序为何显示有些乱?

情景介绍:

学习了Ai8951U的教学视频,用擎天柱核心板自己做实验,编写了流水灯程序。
开发环境是按照教学视频中安装在Windows10的笔记本上。
所有软件都是在国芯官网和论坛中下载。
点单个灯的程序正常。
编写的流水灯程序是按照自己的思路写的,结果是灯的亮灭不是预期的结果,但是灯被操作的顺序应该是正常的。

程序思路:

核心思路是初始先给P20口设置为0,也就是点亮,然后延时500ms。
之后进入循环,从P20到P27口;将当前端口状态复制给下一个端口,并将这个所说的下一个端口的状态取反后复制给当前端口(也就是交换两个端口的状态)。

程序代码:


//流水灯程序 main.c
#include "ai8051u.h"

#define OnOff_X_500ms(A, B) {A = B; B = ~A; Delay500ms();}

void Delay500ms(void);

void main(void)
{
        P2M0 = 0;                //P2端口(P20-P27)为准双向口
        P2M1 = 0;
       

        P20 = 0;
        Delay500ms();
        while(1)
        {
                OnOff_X_500ms(P21, P20);
                OnOff_X_500ms(P22, P21);
                OnOff_X_500ms(P23, P22);
                OnOff_X_500ms(P24, P23);
                OnOff_X_500ms(P25, P24);
                OnOff_X_500ms(P26, P25);
                OnOff_X_500ms(P27, P26);
                OnOff_X_500ms(P20, P27);
        }
}

void Delay500ms(void)        //@40.000MHz
{
        unsigned char data i, j, k;

        _nop_();
        _nop_();
        i = 76;
        j = 255;
        k = 241;
        do
        {
                do
                {
                        while (--k);
                } while (--j);
        } while (--i);
}


困惑:

编译、下载的程序中设置没有特殊的,都是按照教学视频及论坛中的文章设置的。
这个程序检查过很多遍,逻辑上没找到问题,但是灯的显示与期望的不一致。
该如何修改程序代码使显示正常?
问题是什么引起的呢?

期望:

我对MCU是个新手,希望论坛中的高手伸出帮助之手,先说声谢谢了!

2025年4月10日

国芯人工智能 发表于 2025-4-10 18:48:00

您好,针对您在擎天柱核心板-Ai8051U-32K64上实现流水灯程序时遇到的问题,以下是一些可能的原因及解决方案:

1. 端口状态操作逻辑问题
您的程序逻辑是通过宏OnOffX500ms交换两个端口的状态,并延时500ms。然而,宏定义中的操作顺序可能导致状态更新不符合预期。具体分析如下:
c
define OnOffX500ms(A, B) {A = B; B = ~A; Delay500ms();}
A = B:将B的状态赋值给A。
B = ~A:将A取反后的状态赋值给B。

由于A的值在B = ~A时已经被更新为B的原始值,因此B最终会被赋值为~B,而不是预期的~A。这可能导致端口状态更新混乱。

解决方案:
修改宏定义,确保状态交换逻辑正确:
c
define OnOffX500ms(A, B) {unsigned char temp = B; B = ~A; A = temp; Delay500ms();}
这样,B会被赋值为~A,而A会保留B的原始值,确保状态交换正确。

2. 端口初始化问题
在程序开始时,您将P20设置为0,并延时500ms。然而,其他端口(P21到P27)的初始状态未明确设置,可能导致初始状态不确定。

解决方案:
在进入主循环之前,将所有端口初始化为高电平(熄灭状态):
c
P2 = 0xFF; // 初始化P2端口为高电平
P20 = 0;   // 点亮P20
Delay500ms();

3. 延时函数精度问题
如果Delay500ms函数的实现不够精确,可能导致延时时间不准确,从而影响流水灯效果。

解决方案:
检查Delay500ms函数的实现,确保其精度满足需求。例如,使用定时器实现精确延时:
c
void Delay500ms(void) {
    unsigned int i, j;
    for (i = 0; i < 500; i++) {
      for (j = 0; j < 120; j++);
    }
}
或者使用定时器中断实现更精确的延时。

4. 端口模式配置问题
您将P2M0和P2M1设置为0,将P2端口配置为准双向口。这种模式下,端口的驱动能力较弱,可能导致LED亮度不足或响应不稳定。

解决方案:
将P2端口配置为推挽输出模式,以提高驱动能力:
c
P2M0 = 0xFF; // 配置P2端口为推挽输出
P2M1 = 0x00;

5. 硬件连接问题
如果硬件连接存在问题(如LED限流电阻不合适或接触不良),也可能导致流水灯效果异常。

解决方案:
检查硬件连接,确保LED和限流电阻的阻值合适,并确保所有连接稳固。

修改后的完整代码
c
include "ai8051u.h"

define OnOffX500ms(A, B) {unsigned char temp = B; B = ~A; A = temp; Delay500ms();}

void Delay500ms(void);

void main(void) {
    P2M0 = 0xFF; // 配置P2端口为推挽输出
    P2M1 = 0x00;
    P2 = 0xFF;   // 初始化P2端口为高电平
    P20 = 0;   // 点亮P20
    Delay500ms();

    while (1) {
      OnOffX500ms(P21, P20);
      OnOffX500ms(P22, P21);
      OnOffX500ms(P23, P22);
      OnOffX500ms(P24, P23);
      OnOffX500ms(P25, P24);
      OnOffX500ms(P26, P25);
      OnOffX500ms(P27, P26);
    }
}

void Delay500ms(void) {
    unsigned int i, j;
    for (i = 0; i < 500; i++) {
      for (j = 0; j < 120; j++);
    }
}

总结
通过修正状态交换逻辑、初始化端口状态、优化延时函数、配置端口模式以及检查硬件连接,您的流水灯程序应能实现预期效果。如果问题仍未解决,建议进一步调试或检查硬件电路。希望以上分析对您有所帮助!

[本文内容由国芯人工智能辅助生成,仅供参考]

我心飞扬 发表于 2025-4-10 19:23:32

上面由国芯人工智能辅助生成的帖子没解决问题,还引起其他问题,8个灯长亮。

神农鼎 发表于 2025-4-10 20:02:51



https://www.stcaimcu.com/forum.php?mod=redirect&goto=findpost&ptid=12839&pid=130776

我心飞扬 发表于 2025-4-10 20:17:36

神农鼎 发表于 2025-4-10 20:02
https://www.stcaimcu.com/forum.php?mod=redirect&goto=findpost&ptid=12839&pid=130776
你好!
谢谢回复。
你指示的应该是工程目录:01-跑马灯中的程序吧。
我已经看过。
里面有如下部分:
void HardwareMarquee(void)
{
    P20 = 0;                //LED On
    delay_ms(250);
    P20 = 1;                //LED Off
    P21 = 0;                //LED On
    delay_ms(250);
    ......   
    delay_ms(250);
    P22 = 1;                //LED Off
    P21 = 0;                //LED On
    delay_ms(250);
    P21 = 1;                //LED Off
}
这段代码的方式我也实现了,没问题。
我上面问题是另外一种思路,不明白什么原因得不到想要的结果。
请协助运行我的代码试试,找到原因。
再次感谢你的回复及帮助!

DebugLab 发表于 2025-4-10 20:49:30

你的方法程序如下:

bit temp;
temp=A;
A=B;
B=temp;



A=!A;
B=!B;

我心飞扬 发表于 2025-4-10 21:41:42

DebugLab 发表于 2025-4-10 20:49
你的方法程序如下:

bit temp;

谢谢你的回复及帮助。
按照你的方案,将宏定义改为如下两种情况都正常实现了流水灯。
第一种:
#define OnOff_X_500ms(A, B) {bit temp = A; A = B; B = temp; Delay500ms();}                //ok
第二种:
#define OnOff_X_500ms(A, B) {A = !A; B = !B; Delay500ms();}                //ok
第一种为何要在宏定义中另外定义一个变量才行?我之前就是不想再定义一个变量,利用已有的端口寄存器进行状态数据交换。
第二种利用端口寄存器进行自己对自己的状态数据反转,实现了不用再引入变量。挺巧妙!
我百思不得其解的是我原来的方式:
#define OnOff_X_500ms(A, B) {A = B; B = ~A; Delay500ms();}
从逻辑上看不出有任何问题,而且与你第二种方法很神似,但是我的方法没成功。
请帮我分析一下我程序的逻辑出纰漏了吗?
我原来的代码问题到底出在哪里呢?
希望得到你的回复。
谢谢!

DebugLab 发表于 2025-4-11 07:44:01

我心飞扬 发表于 2025-4-10 21:41
谢谢你的回复及帮助。
按照你的方案,将宏定义改为如下两种情况都正常实现了流水灯。
第一种:


可能由于速度较快,IO来不及响应导致,LED有结电容会限制IO的速度
A=B和B=!A之间加延时试一下

我心飞扬 发表于 2025-4-11 12:49:59

DebugLab 发表于 2025-4-11 07:44
可能由于速度较快,IO来不及响应导致,LED有结电容会限制IO的速度
A=B和B=!A之间加延时试一下 ...

谢谢你的回复及帮助。
我将代码改成如下:
#define OnOff_X_500ms(A, B) {A = B; B = ~A; B = ~A; Delay500ms();}                //ok了。
只是增加了一次“B = ~A”,预期的程序效果就出现了,流水灯正常跑起来了!
你的解释我仔细理解后觉得说到本质原因了。
👍
我的代码中“A = B; B = ~A; ”是刚给A赋值,接着取A值并进行位运算非操作,接口的硬件电子性能跟不上MCU的运算时序,造成取到的A的值可能不稳定,取到的是错误的值。于是再一次“B = ~A;”就能取到正常值并给B赋值了。
由此也能在程序时序上理解你给出的“A = !A; B = !B;”隔离了对同一个端口的赋值后下一个指令周期进行读取操作可能由于电子性能问题出现的程序出错。
再次感谢你的帮助!

另外还有一个小问题向你咨询一下。
你给的“!A”是逻辑运算操作,我用的“~A”是位运算操作,结果都相同。
我看到很多示例代码中都是用“!A”这种方式,从效率、本质及合理性上讲,它们有什么不同吗?
我直观感觉用位运算更合理,但别人用逻辑运算这么多,一定有其道理。
我是这方面新手,对此有些疑惑,请帮助解答一下,期待你的回复。
谢谢!

我心飞扬 发表于 2025-4-11 12:57:56

DebugLab 发表于 2025-4-11 07:44
可能由于速度较快,IO来不及响应导致,LED有结电容会限制IO的速度
A=B和B=!A之间加延时试一下 ...

又想到一个问题,如果我改变MCU的运行频率,是不是将现在40MHz的频率降频到某个频率就可以了?
我想降频也是一个解决办法。
对吗?
页: [1] 2
查看完整版本: 请教高手:擎天柱核心板-Ai8051U-32K64-实现流水灯程序为何显示有些乱?