找回密码
 立即注册
查看: 98|回复: 10

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

[复制链接]
  • 打卡等级:偶尔看看I
  • 打卡总天数:14
  • 最近打卡:2025-04-29 15:31:16
已绑定手机

4

主题

37

回帖

154

积分

注册会员

积分
154
发表于 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[8] = {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[i];
                Timer0_Init(); // 定时器初始化
        }
}

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

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

回复

使用道具 举报 送花

3

主题

1160

回帖

1031

积分

等待验证会员

积分
1031
发表于 2025-4-9 10:18:00 | 显示全部楼层
在实现流水灯效果时,通过数组移位和定时器控制是一种常见且高效的方法。以下是对该问题的详细分析和实现思路。

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

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

3. 代码实现
  1. c
  2. include "ai8051u.h"
  3. include "intrins.h"
  4. include "stc32stc8usb.h"
  5. include "stdio.h"
  6. define u8 unsigned char
  7. define LEDNUM 8
  8. u8 Ledstate[LEDNUM] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
  9. u8 currentindex = 0; // 当前LED状态索引
  10. u8 keypresscount = 0; // 按键按下计数
  11. void Timer0Init(void) // 500毫秒@24.000MHz
  12. {
  13.     TM0PS = 0x0F; // 设置定时器时钟预分频
  14.     TM0C = 0x00; // 设置定时器模式
  15.     TM0L = 0x00; // 设置定时器初值低字节
  16.     TM0H = 0x00; // 设置定时器初值高字节
  17.     TM0IE = 1; // 使能定时器中断
  18.     TM0CR = 1; // 启动定时器
  19. }
  20. void Timer0ISR(void) interrupt 1
  21. {
  22.     TM0IF = 0; // 清除定时器中断标志
  23.     P0 = Ledstate[currentindex]; // 更新LED状态
  24.     currentindex = (currentindex + 1) % LEDNUM; // 更新索引
  25. }
  26. void KeyScan(void)
  27. {
  28.     if (P10 == 0) // 假设按键连接到P1.0
  29.     {
  30.         keypresscount++; // 按键按下计数
  31.         while (P10 == 0); // 等待按键松开
  32.     }
  33. }
  34. void main(void)
  35. {
  36.     Timer0Init(); // 初始化定时器
  37.     EA = 1; // 使能全局中断
  38.     while (1)
  39.     {
  40.         KeyScan(); // 按键检测
  41.         if (keypresscount > 0)
  42.         {
  43.             keypresscount = 0; // 清零按键计数
  44.             currentindex = (currentindex + 1) % LEDNUM; // 更新LED状态索引
  45.         }
  46.     }
  47. }
复制代码

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

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

[本文内容由国芯人工智能辅助生成,仅供参考]
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:518
  • 最近打卡:2025-05-01 05:52:49
已绑定手机

46

主题

1694

回帖

2540

积分

金牌会员

积分
2540
发表于 2025-4-9 10:42:20 | 显示全部楼层
初始化函数为什么放到while大循环里面?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:38
  • 最近打卡:2025-04-29 08:28:25
已绑定手机

17

主题

446

回帖

1173

积分

金牌会员

积分
1173
发表于 2025-4-9 11:27:02 | 显示全部楼层
void KeyScan(void)
{
    if (P10 == 0) // 假设按键连接到P1.0
    {
        keypresscount++; // 按键按下计数
        while (P10 == 0); // 等待按键松开
    }
}

按键死等没有实用价值,影响主循环添加的新任务
回复 支持 1 反对 0

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:14
  • 最近打卡:2025-04-29 15:31:16
已绑定手机

4

主题

37

回帖

154

积分

注册会员

积分
154
发表于 2025-4-9 13:50:40 | 显示全部楼层
jw*** 发表于 2025-4-9 10:42
初始化函数为什么放到while大循环里面?

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

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:14
  • 最近打卡:2025-04-29 15:31:16
已绑定手机

4

主题

37

回帖

154

积分

注册会员

积分
154
发表于 2025-4-9 13:51:35 | 显示全部楼层
Ayb_*** 发表于 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[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
u16 Count_ms[3] = {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[0] >= 10){
                        Count_ms[0] = 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[0]++;
       
}

这个,不适用while判断按键是否松开
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:38
  • 最近打卡:2025-04-29 08:28:25
已绑定手机

17

主题

446

回帖

1173

积分

金牌会员

积分
1173
发表于 2025-4-9 14:12:47 | 显示全部楼层
cb*** 发表于 2025-4-9 13:51
#include "ai8051u.h"
#include "intrins.h"
#include "stc32_stc8_usb.h"

一直按下按键,key_count值会溢出,并重复等于5
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:14
  • 最近打卡:2025-04-29 15:31:16
已绑定手机

4

主题

37

回帖

154

积分

注册会员

积分
154
发表于 2025-4-9 14:58:25 | 显示全部楼层
Ayb_*** 发表于 2025-4-9 14:12
一直按下按键,key_count值会溢出,并重复等于5

重新赋值0,可否?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:518
  • 最近打卡:2025-05-01 05:52:49
已绑定手机

46

主题

1694

回帖

2540

积分

金牌会员

积分
2540
发表于 2025-4-9 15:27:37 | 显示全部楼层
cb*** 发表于 2025-4-9 13:50
嗯,放while前,main里面执行,每500ms执行一次,定时器,自动重装载,不需要放while里面,谢谢 ...

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:38
  • 最近打卡:2025-04-29 08:28:25
已绑定手机

17

主题

446

回帖

1173

积分

金牌会员

积分
1173
发表于 2025-4-9 16:01:11 | 显示全部楼层
cb*** 发表于 2025-4-9 14:58
重新赋值0,可否?

加到一定值就不加了,比如小于250就+1
回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 02:15 , Processed in 0.204350 second(s), 109 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表