首先上图,有图才有真相。实验中的图片。
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;
}[page]
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++; // 切换到下一步运行
}
break;
case 6:
if((1==KEY_IN1)&&(1==KEY_IN2))
{
key_step++;
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 = 3; // 触发 3 号按键
}
}
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 = 4; // 触发 4 号按键
}
}
break;
}
if(key_step>6) // 判断,第一组与第二组按键反复轮流扫描
{
key_step = 1;
}
}
void key_service(void)
{
switch(key_sec)
{
case 1: // 1 号按键
relay_status_clear(); // 把relay_status清零
relay_status_set_1(); // 把relay_status对应的第一个继电器启动
relay_drive(); // 把relay_status的状态通过595驱动出来
buzzer_time_cnt = CNT_BUZZER_TIME;
key_sec = 0; // 处理完相应按键程序后,把按键选择变量清零,避免一直触发
break;
case 2: // 2 号按键
relay_status_clear(); // 把relay_status清零
relay_status_set_2(); // 把relay_status对应的第一个继电器启动
relay_drive(); // 把relay_status的状态通过595驱动出来
buzzer_time_cnt = CNT_BUZZER_TIME;
key_sec = 0;
break;
case 3: // 3 号按键
relay_status_clear(); // 把relay_status清零
relay_status_set_3(); // 把relay_status对应的第一个继电器启动
relay_drive(); // 把relay_status的状态通过595驱动出来
buzzer_time_cnt = CNT_BUZZER_TIME;
key_sec = 0;
break;
case 4: // 4 号按键
relay_status_clear(); // 把relay_status清零
relay_status_set_4(); // 把relay_status对应的第一个继电器启动
relay_drive(); // 把relay_status的状态通过595驱动出来
buzzer_time_cnt = CNT_BUZZER_TIME;
key_sec = 0;
break;
}
}
void relay_status_clear(void)
{
relay_status = 0;
}
void relay_status_set_1(void)
{
relay_status |= 0x0001;
}
void relay_status_set_2(void)
{
relay_status |= 0x0002;
}
void relay_status_set_3(void)
{
relay_status |= 0x0004;
}
void relay_status_set_4(void)
{
relay_status |= 0x0008;
}
void relay_drive(void)
{
u8 tempdata = 0;
// u16 relay_status_temp = 0;
HC595_SCK_OUT = 0;
HC595_RCK_OUT = 0;
// relay_status_temp = relay_status;
for(tempdata=0; tempdata<16; tempdata++)
{
if(relay_status & 0x8000)
{
HC595_SI_OUT = 1;
}
else
{
HC595_SI_OUT = 0;
}
HC595_SCK_OUT = 0;
_nop_();
_nop_();
HC595_SCK_OUT = 1;
_nop_();
_nop_();
relay_status <<= 1;
}
HC595_RCK_OUT = 0;
_nop_();
_nop_();
HC595_RCK_OUT = 1;
_nop_();
_nop_();
HC595_SCK_OUT = 0; // 拉低,增强抗干扰
HC595_RCK_OUT = 0;
HC595_SI_OUT = 0;
}
void delay(void)
{
u8 t = 0;
for(t=0; t<0x0F; t++)
{;}
}