首先上图,有图才有真相。实验中的图片。
1、刚上电,继电器不动作,很稳定。这是工程上需要的结果。
2、按下KEY1,继电器1吸合,其它继电器断开。
3、按下KEY2,继电器2吸合,其它继电器断开。
#include
#include
sbit KEY_IN1 = P2 ^ 4; // 输入按键 接10K上拉电阻
sbit KEY_IN2 = P2 ^ 5;
sbit KEY_OUT1 = P2 ^ 3; // 输出按键
sbit KEY_OUT2 = P2 ^ 2;
sbit BUZZER_OUT = P1 ^ 7; // 蜂鸣器
sbit LED = P0 ^ 7; // LED
sbit HC595_SCK_OUT = P1 ^ 0;// 74HC595数据输入时钟线
sbit HC595_RCK_OUT = P1 ^ 1;// 74HC595输出存储器锁存时钟线
sbit HC595_OE_OUT = P1 ^ 2;// 74HC595输出使能端 接10K上拉电阻
sbit HC595_SI_OUT = P1 ^ 3;// 74HC595数据线
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned char u8;
typedef unsigned int u16;
#define CNT_DELAY_CNT1 25 // 按键去抖动延时阀值
#define CNT_DELAY_CNT2 5 // 按键列输出信号稳定的小延时
#define CNT_BUZZER_TIME 40
void interrupt_init(void);
void key_scan(void);
void key_service(void);
void relay_status_clear(void);
void relay_status_set_1(void);
void relay_status_set_2(void);
void relay_status_set_3(void);
void relay_status_set_4(void);
void relay_drive(void);
void delay(void);
u8 key_step = 1; // 按键扫描步骤变量,在switch()括号里面
u8 key_lock1 = 0; // 按键自锁标志
u8 key_sec = 0; // 按键被触发的变量
u16 delay_cnt1 = 0; // 延时计数器的变量
u16 delay_cnt2 = 0; // 延时计数器的变量
u16 buzzer_time_cnt = 0; // 蜂鸣器声音长短的计数延时
u16 relay_status = 0;// 继电器状态寄存器
int main(void)
{
interrupt_init();
HC595_OE_OUT = 1;// 置1,595输出口为高阻态,ULN2003输入口内部有下拉电阻,所以把595输出口拉低了
relay_status_clear();// 把relay_status清零
relay_drive();// 继电器驱动程序,relay_status映射继电器的状态
HC595_OE_OUT = 0;// 输出使能端拉低,595输出口有效
BUZZER_OUT = 1;// 上电关闭蜂鸣器
while(1)
{
key_service();// 按键服务函数
}
return 0;
}
void timer0_interrupt(void) interrupt 1
{
ET0 = 0;
EA = 0; // 经测试 这里不关闭定时器中断也没有问题 鸿哥这里关闭定时器中断不知道是PIC单片机的特点还是其它原因
key_scan(); // 按键扫描函数
if(buzzer_time_cnt) // 控制蜂鸣器声音的长短
{
BUZZER_OUT = 0; // 开启蜂鸣器
--buzzer_time_cnt; // 蜂鸣器声音长短的计数延时
}
else
{
BUZZER_OUT = 1; // 关闭蜂鸣器
}
TH0 = 0xfe;
TL0 = 0x33;
ET0 = 1;
EA = 1; // 前面关闭定时器中断,这里当然需要开启
}
void interrupt_init(void)
{
TMOD = TMOD | 0x01;
TMOD = TMOD & 0xFD;
TH0 = 0xfe;
TL0 = 0x33; // 定时0.5ms
TR0 = 1;
ET0 = 1; // 开启外部中断
EA = 1; // 开启总中断
}
void key_scan(void)
{
switch(key_step)
{
case 1:// 按键扫描 1、2 号按键
KEY_OUT1 = 0;// 按键列扫描 第一列输出低电平
KEY_OUT2 = 1;// 第二列输出高电平
delay_cnt2 = 0; // 延时计数器清零
key_step++;// 切换到下一个步骤运行
break;
case 2:
delay_cnt2++;
if(delay_cnt2>CNT_DELAY_CNT2)// 小延时,但不是去抖动延时 保证列输出信号稳定
{
delay_cnt2 = 0;
key_step++;// 切换到下一个步骤运行
}
break;
case 3:
if((1==KEY_IN1)&&(1==KEY_IN2))// 如果没有按键按下,则2个IO输入都是高电平
{
key_step++;// 如果没有按键按下,下一个中断扫描另外2个按键
key_lock1 = 0;// 按键自锁标志清零
delay_cnt1 = 0;// 按键去抖动延时计数器清零,此处设计很巧妙
}
else if((0==KEY_IN1)&&(1==KEY_IN2)&&(0==key_lock1))
{
++delay_cnt1;// 延时计数器
if(delay_cnt1>CNT_DELAY_CNT1)// 延时计数器超过阀值
{
delay_cnt1 = 0;
key_lock1 = 1;// 自锁按键置位,避免一直触发,只有松开按键,才会被清零
key_sec = 1;// 触发 1 号按键
}
}
else if((1==KEY_IN1)&&(0==KEY_IN2)&&(0==key_lock1))
{
++delay_cnt1;
if(delay_cnt1>CNT_DELAY_CNT1)
{
delay_cnt1 = 0;
key_lock1 = 1;// 自锁按键置位,避免一直触发,只有松开按键,才会被清零
key_sec = 2;// 触发 2 号按键
}
}
break;
case 4:// 扫描 3、4 号按键
KEY_OUT1 = 1;// 第一列输出高电平
KEY_OUT2 = 0;// 按键列扫描,第二列输出低电平
delay_cnt2 = 0;// 延时计数器清零
key_step++;// 切换到下一步运行
break;
case 5:
delay_cnt2++;
if(delay_cnt2>CNT_DELAY_CNT2)// 小延时,但不是去抖动延时 保证列输出信号稳定
{
delay_cnt2 = 0;
key_step++;// 切换到下一步运行
}