atmega8多个按键控制多个LED
#include <iom8v.h>
#include <macros.h>
#define uchar unsigned char
#define uint unsigned int
// 按键和LED定义
#define KEY_PORT PIND
#define LED_PORT PORTC
#define DEBOUNCE_DELAY 10 // 消抖时间10ms
#define SLEEP_DELAY 5000 // 5秒无操作进入睡眠
// 延时函数
void delay_ms(uint ms) {
uint i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 114; j++); // 约1ms@8MHz
}
// 初始化外部中断(用于唤醒睡眠)
void wakeup_init(void) {
// 配置INT0~INT2(PD0~PD2)为下降沿触发
MCUCR |= (1 << 1) | (1 << 3); // ISC01=1(INT0下降沿), ISC11=1(INT1下降沿)
MCUCSR &= ~(1 << 6); // ISC21=0(INT2下降沿)
GICR |= (1 << 6) | (1 << 7) | (1 << 5); // 使能INT0、INT1、INT2中断
}
// 进入睡眠模式(空闲模式):通过机器码直接执行sleep指令
void enter_sleep(void) {
// 配置为空闲模式(SM1=0, SM0=0)并允许睡眠(SE=1)
MCUCR &= ~((1 << 1) | (1 << 0)); // 空闲模式
MCUCR |= (1 << 7); // 允许睡眠
// 写入sleep指令的机器码(0x94, 0x8F是AVR sleep指令的16位机器码)
*(volatile unsigned short *)0x0000 = 0x948F;
}
// 外部中断服务程序(仅用于唤醒)
#pragma interrupt_handler ext_int0_isr:2
void ext_int0_isr(void) {}
#pragma interrupt_handler ext_int1_isr:3
void ext_int1_isr(void) {}
#pragma interrupt_handler ext_int2_isr:4
void ext_int2_isr(void) {}
void main(void) {
uchar i;
uchar key_flag[5] = {0};
uchar key_last[5];
uchar led_state = 0xFF;
uint idle_cnt = 0; // 空闲计数器
// 配置IO口
DDRD &= ~0x1F; // PD0~PD4输入
PORTD |= 0x1F; // 使能上拉
DDRC |= 0x1F; // PC0~PC4输出
LED_PORT = led_state;
// 初始化按键状态
for(i = 0; i < 5; i++) {
key_last[i] = (KEY_PORT & BIT(i)) ? 1 : 0;
}
// 初始化唤醒中断和全局中断
wakeup_init();
SREG |= (1 << 7); // 使能全局中断
while(1) {
uchar key_activity = 0; // 按键活动标记
// 按键检测逻辑
for(i = 0; i < 5; i++) {
uchar key_current = (KEY_PORT & BIT(i)) ? 1 : 0;
if(key_current == 0 && key_last[i] == 1) { // 按下边缘
delay_ms(DEBOUNCE_DELAY);
if((KEY_PORT & BIT(i)) == 0 && key_flag[i] == 0) {
led_state ^= BIT(i);
LED_PORT = led_state;
key_flag[i] = 1;
key_activity = 1;
}
} else if(key_current == 1 && key_last[i] == 0) { // 释放边缘
delay_ms(DEBOUNCE_DELAY);
if((KEY_PORT & BIT(i)) != 0) {
key_flag[i] = 0;
key_activity = 1;
}
}
key_last[i] = key_current;
}
// 无活动时进入睡眠
if(key_activity) {
idle_cnt = 0;
} else {
idle_cnt++;
if(idle_cnt >= SLEEP_DELAY) {
enter_sleep(); // 进入睡眠,等待按键唤醒
idle_cnt = 0;
}
}
delay_ms(1); // 计数基准
}
}
|