cbai 发表于 2025-4-9 10:15:41

数组移位加定时器实现流水灯问题

#include "ai8051u.h"
#include "intrins.h"
#include "stc32_stc8_usb.h"
#include "stdio.h"

/*
任务3:按键1按一下,LED通过数组移动一下
注意事项:
按键不能在通过while判断是否按下松开了
可以通过按键按下计数

*/

/*
LED1 LED2 LED3 LED4 LED5 LED6 LED7 LED8
0    1    1    1    1    1    1    1
1    0    1    1    1    1    1    1
1    1    0    1    1    1    1    1
1    1    1    0    1    1    1    1
1    1    1    1    0    1    1    1
1    1    1    1    1    0    1    1
1    1    1    1    1    1    0    1
1    1    1    1    1    1    1    0

*/
// 实现思路:每500毫秒,按照上面的矩阵电平赋值给相应的LED灯
#define u8 unsigned char
#define LED_NUM 8
u8 Led_state = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};


void Timer0_Init(void)                //500毫秒@24.000MHz
{
        TM0PS = 0x0F;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
        AUXR &= 0x7F;                        //定时器时钟12T模式
        TMOD &= 0xF0;                        //设置定时器模式
        TL0 = 0xDC;                                //设置定时初始值
        TH0 = 0x0B;                                //设置定时初始值
        TF0 = 0;                                //清除TF0标志
        TR0 = 1;                                //定时器0开始计时
        ET0 = 1;                                //使能定时器0中断
}
u8 i = 0;
void main(void) {
        int flag = 0;
        int bonus = 0;
        WTST = 0;                                                                                //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1;                                                                                 //扩展寄存器(XFR)访问使能
    CKCON = 0;                                                                                 //提高访问XRAM速度
        // 按照原理图,点亮8个LED灯需要按下P40总开关和给8个LED灯低电平
        // 1、相应的GPIO口初始化
        P0M1 = 0; P0M0 = 0;
        P1M1 = 0; P1M0 = 0;
        P2M1 = 0; P2M0 = 0;
        P3M1 = 0; P3M0 = 0;
        P4M1 = 0; P4M0 = 0;
        P5M1 = 0; P5M0 = 0;
        P6M1 = 0; P6M0 = 0;
        P7M1 = 0; P7M0 = 0;
        usb_init();
        IE |= 0x80; // 允许USB中断
       
        EA = 1; // 开启中断总开关
       
        P40 = 0;
       
       
        // 2、P40低电平
        // 3、P00 P01 P02 P03 P04 P05 P06 P07低电平
       
       
        while(1) {
                if (bUsbOutReady)
      {
//            USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
//            printf_usb("1. Read Num:%d\n", OutNumber);
//            printf_usb("2. Read Num:%d\n", OutNumber);
//            printf_usb("3. Read Num:%d\n", OutNumber);
//            printf_usb("4. Read Num:%d\n", OutNumber);

            usb_OUT_done();
      }
               
                P0 = ~Led_state;
                Timer0_Init(); // 定时器初始化
        }
}

void Timer0_Isr(void) interrupt 1
{
        i++;
        if(i > 7)
        {
                i = 0;
        }
}

以上是我的流水灯代码,可是在烧录观察灯的状态时,中间有一两个灯亮灭间隔和其他不一致,稍微有停顿,怎么回事?判断i是否大于7的逻辑我放在中断里面和P0赋值后面好像都一样的现象

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

在实现流水灯效果时,通过数组移位和定时器控制是一种常见且高效的方法。以下是对该问题的详细分析和实现思路。

1. 问题分析
任务要求通过按键控制LED灯的流水效果,每次按键按下时,LED灯的状态按照预定义的数组进行移位。需要注意的是,按键的检测不能通过while循环判断是否按下或松开,而是通过按键按下计数来实现。

2. 实现思路
LED状态数组:定义一个数组Ledstate,用于存储LED灯的状态。每个元素对应一个LED灯的状态,通过移位操作实现流水效果。
定时器初始化:初始化定时器,设置定时时间为500毫秒。定时器中断服务程序中更新LED灯的状态。
按键检测:通过按键按下计数来检测按键事件,每次按键按下时,更新LED状态数组的索引,实现LED灯的移位。

3. 代码实现

c
include "ai8051u.h"
include "intrins.h"
include "stc32stc8usb.h"
include "stdio.h"

define u8 unsigned char
define LEDNUM 8

u8 Ledstate = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
u8 currentindex = 0; // 当前LED状态索引
u8 keypresscount = 0; // 按键按下计数

void Timer0Init(void) // 500毫秒@24.000MHz
{
    TM0PS = 0x0F; // 设置定时器时钟预分频
    TM0C = 0x00; // 设置定时器模式
    TM0L = 0x00; // 设置定时器初值低字节
    TM0H = 0x00; // 设置定时器初值高字节
    TM0IE = 1; // 使能定时器中断
    TM0CR = 1; // 启动定时器
}

void Timer0ISR(void) interrupt 1
{
    TM0IF = 0; // 清除定时器中断标志
    P0 = Ledstate; // 更新LED状态
    currentindex = (currentindex + 1) % LEDNUM; // 更新索引
}

void KeyScan(void)
{
    if (P10 == 0) // 假设按键连接到P1.0
    {
      keypresscount++; // 按键按下计数
      while (P10 == 0); // 等待按键松开
    }
}

void main(void)
{
    Timer0Init(); // 初始化定时器
    EA = 1; // 使能全局中断

    while (1)
    {
      KeyScan(); // 按键检测
      if (keypresscount > 0)
      {
            keypresscount = 0; // 清零按键计数
            currentindex = (currentindex + 1) % LEDNUM; // 更新LED状态索引
      }
    }
}

4. 代码说明
定时器初始化:Timer0Init函数初始化定时器,设置定时时间为500毫秒,并启动定时器。
定时器中断服务程序:Timer0ISR函数在定时器中断时更新LED灯的状态,并更新当前LED状态索引。
按键检测:KeyScan函数检测按键是否按下,并通过按键按下计数来记录按键事件。
主循环:在主循环中,检测按键事件,并根据按键按下计数更新LED状态索引。

5. 总结
通过数组移位和定时器控制,可以实现高效的流水灯效果。按键检测通过按键按下计数来实现,避免了使用while循环判断按键状态的问题。该方法具有较高的可扩展性和灵活性,适用于多种LED控制场景。

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

jwd 发表于 2025-4-9 10:42:20

初始化函数为什么放到while大循环里面?

Ayb_ice 发表于 2025-4-9 11:27:02

void KeyScan(void)
{
    if (P10 == 0) // 假设按键连接到P1.0
    {
      keypresscount++; // 按键按下计数
      while (P10 == 0); // 等待按键松开
    }
}

按键死等没有实用价值,影响主循环添加的新任务

cbai 发表于 2025-4-9 13:50:40

jwd 发表于 2025-4-9 10:42
初始化函数为什么放到while大循环里面?

嗯,放while前,main里面执行,每500ms执行一次,定时器,自动重装载,不需要放while里面,谢谢

cbai 发表于 2025-4-9 13:51:35

Ayb_ice 发表于 2025-4-9 11:27
void KeyScan(void)
{
    if (P10 == 0) // 假设按键连接到P1.0


#include "ai8051u.h"
#include "intrins.h"
#include "stc32_stc8_usb.h"
#include "stdio.h"

/*
任务3:按键1按一下,LED通过数组移动一下
注意事项:
按键不能在通过while判断是否按下松开了
可以通过按键按下计数

*/

/*
LED1 LED2 LED3 LED4 LED5 LED6 LED7 LED8
0    1    1    1    1    1    1    1
1    0    1    1    1    1    1    1
1    1    0    1    1    1    1    1
1    1    1    0    1    1    1    1
1    1    1    1    0    1    1    1
1    1    1    1    1    0    1    1
1    1    1    1    1    1    0    1
1    1    1    1    1    1    1    0

*/
// 实现思路:每500毫秒,按照上面的矩阵电平赋值给相应的LED灯
#define u8 unsigned char
#define LED_NUM 8
u8 Led_state = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
u16 Count_ms = {0};

u8 key_count = 0;
u8 i = 0;

void Timer0_Init(void)                //1毫秒@24.000MHz
{
        TM0PS = 0x00;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
        AUXR &= 0x7F;                        //定时器时钟12T模式
        TMOD &= 0xF0;                        //设置定时器模式
        TL0 = 0x30;                                //设置定时初始值
        TH0 = 0xF8;                                //设置定时初始值
        TF0 = 0;                                //清除TF0标志
        TR0 = 1;                                //定时器0开始计时
        ET0 = 1;                                //使能定时器0中断
}



void Delay10ms(void)        //@24.000MHz
{
        unsigned long edata i;

        _nop_();
        _nop_();
        _nop_();
        i = 59998UL;
        while (i) i--;
}

void main(void) {
        int flag = 0;
        int bonus = 0;
        WTST = 0;                                                                                //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1;                                                                                 //扩展寄存器(XFR)访问使能
    CKCON = 0;                                                                                 //提高访问XRAM速度
        // 按照原理图,点亮8个LED灯需要按下P40总开关和给8个LED灯低电平
        // 1、相应的GPIO口初始化
        P0M1 = 0; P0M0 = 0;
        P1M1 = 0; P1M0 = 0;
        P2M1 = 0; P2M0 = 0;
        P3M1 = 0; P3M0 = 0;
        P4M1 = 0; P4M0 = 0;
        P5M1 = 0; P5M0 = 0;
        P6M1 = 0; P6M0 = 0;
        P7M1 = 0; P7M0 = 0;
        usb_init();
        IE |= 0x80; // 允许USB中断
        Timer0_Init();
        EA = 1; // 开启中断总开关
       
        P40 = 0;
       
       
        // 2、P40低电平
        // 3、P00 P01 P02 P03 P04 P05 P06 P07低电平
       
       
        while(1) {
                if (bUsbOutReady)
      {
//            USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
//            printf_usb("1. Read Num:%d\n", OutNumber);
//            printf_usb("2. Read Num:%d\n", OutNumber);
//            printf_usb("3. Read Num:%d\n", OutNumber);
//            printf_usb("4. Read Num:%d\n", OutNumber);
            
            usb_OUT_done();
      }
//                P0 = ~Led_state;
//                if(P32 == 0){
//                        Delay10ms();
//                        if(P32 == 0){
//                                while(P32==0);
//                                i++;
//                        }       
//                }
//                if(i > 7){
//                        i = 0;
//                }
                P0 = ~Led_state;
                if(Count_ms >= 10){
                        Count_ms = 0;
                        if(P32 == 0){
                                key_count++;
                                if(key_count == 5){
                                        i++;
                                }
                        }
                        else{
                                key_count = 0;
                        }
                }
                if(i > 7){
                        i = 0;
                }
               
        }
}

void Timer0_Isr(void) interrupt 1
{
//        for(i = 0; i < 3; i++){
//                Count_ms++;
//        }
        Count_ms++;
       
}

这个,不适用while判断按键是否松开

Ayb_ice 发表于 2025-4-9 14:12:47

cbai 发表于 2025-4-9 13:51
#include "ai8051u.h"
#include "intrins.h"
#include "stc32_stc8_usb.h"


一直按下按键,key_count值会溢出,并重复等于5

cbai 发表于 2025-4-9 14:58:25

Ayb_ice 发表于 2025-4-9 14:12
一直按下按键,key_count值会溢出,并重复等于5

重新赋值0,可否?

jwd 发表于 2025-4-9 15:27:37

cbai 发表于 2025-4-9 13:50
嗯,放while前,main里面执行,每500ms执行一次,定时器,自动重装载,不需要放while里面,谢谢 ...

{:handshake:}

Ayb_ice 发表于 2025-4-9 16:01:11

cbai 发表于 2025-4-9 14:58
重新赋值0,可否?

加到一定值就不加了,比如小于250就+1
页: [1] 2
查看完整版本: 数组移位加定时器实现流水灯问题