数组移位加定时器实现流水灯问题
#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赋值后面好像都一样的现象
在实现流水灯效果时,通过数组移位和定时器控制是一种常见且高效的方法。以下是对该问题的详细分析和实现思路。
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控制场景。
[本文内容由国芯人工智能辅助生成,仅供参考] 初始化函数为什么放到while大循环里面? void KeyScan(void)
{
if (P10 == 0) // 假设按键连接到P1.0
{
keypresscount++; // 按键按下计数
while (P10 == 0); // 等待按键松开
}
}
按键死等没有实用价值,影响主循环添加的新任务 jwd 发表于 2025-4-9 10:42
初始化函数为什么放到while大循环里面?
嗯,放while前,main里面执行,每500ms执行一次,定时器,自动重装载,不需要放while里面,谢谢 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判断按键是否松开 cbai 发表于 2025-4-9 13:51
#include "ai8051u.h"
#include "intrins.h"
#include "stc32_stc8_usb.h"
一直按下按键,key_count值会溢出,并重复等于5 Ayb_ice 发表于 2025-4-9 14:12
一直按下按键,key_count值会溢出,并重复等于5
重新赋值0,可否? cbai 发表于 2025-4-9 13:50
嗯,放while前,main里面执行,每500ms执行一次,定时器,自动重装载,不需要放while里面,谢谢 ...
{:handshake:} cbai 发表于 2025-4-9 14:58
重新赋值0,可否?
加到一定值就不加了,比如小于250就+1
页:
[1]
2