一、实验目的
1. 理解单片机中断的概念。
2. 掌握外部中断的编程方法。
二、实验内容
1. 验证课堂例题
编译下载运行课堂例题——“按键次数显示”(INT_EX1)和“按键次数显示清零”(INT_EX2),查看运行结果。理解程序的编程思想和程序执行过程。
2. 设计程序
参考上述例题的电路和编程思路,自行设计电路,包括三个按键K1、K2和K3,分别连接外部中断INT0-INT2,6位数码管显示,1个LED报警灯;编程实现K1按键次数的显示,显示数据清零和次数越限报警等功能。
具体要求如下:
(1)初始状态下,数码管显示“200000”,其中后两位用于K1按键次数显示,前两位用于当前按键次数报警阈值显示(报警阈值初始为20);
(2)每当K1按下时,后两位数码管显示K1按下次数,计数到报警阈值时不再进行累加;
(3)当计数超过当前设置的报警阈值时,LED进行点亮报警,此时再按下K1不再进行计数;
(4)K2按下时,显示计数值清零并且LED熄灭,即解除报警。
(5)K3每按下一次,按键次数报警阈值在20、15、10、5间进行循环转换,并在数码管前两位进行当前报警阈值的显示,并且显示计数归零。
在设计报告中,需进行总体设计思路描述,给出所涉及到的主程序、中断程序、关键功能子函数的流程图和设计代码。描述程序调试结果及出现的问题。
三、实验结果
1. 在下方贴出使用Proteus绘制的电路原理图。
图3-1 实验电路图
2. 描述所设计程序的总体设计思路(包括根据任务划分的程序顶层结构、各任务的设计思路、各任务间的数据传递,如何解决关键问题等)
本系统的主要函数有阈值判断函数,数码管显示函数,主函数以及三个外部中断函数。通过在主函数中依次循环调用数码管显示与阈值判断函数,来实现数码管的动态显示,以及阈值报警点亮LED。在按键中断0中,每次进入一次按键中断0 就是实现计数值加1 在按键中断1中,通过设置一个全局计数变量来实现计数值的清零。在按键中断2中,通过改变之前设置好的阈值变量来改变当前的阈值。
3. 根据所设计的程序顶层结构,对主函数、中断函数
主函数:循环调用动态数码管函数与阈值判断函数,并比较阈值与当前计数值来当计数值大于阈值时,关闭外部中断0停止计数,并亮起LED进行报警。
阈值判断函数:即根据当前的阈值模式来选择当前的阈值。
数码管显示函数:采用动态扫描,首先输出位选信号,选中一位数码管后锁存器控制端拉低,锁存位选信号,此时输出段选信号,确定该位数码管输出的数字或者字母,后锁存器控制端拉低,锁存段选信号。循环扫描实现数码管动态刷新,同时,实现了IO复用。
按键中断0回调函数:每次进入中断中对当前的计数值加1并对要显示的数码管数组进行数据处理。
按键中断1回调函数:将当前的计数值清零并再次开启外部中断1同时熄灭报警灯。
按键中断2回调函数:改变当前的阈值模式,并设置新的阈值,之后在主循环中通过不断的将当前的计数值与当前的阈值进行比较实现报警的的功能
4. 给出(3)中相关函数的实现代码(在ICCAVR中进行编程,以截屏贴图方式放入实验报告中,注意规范性,必须加注释)
//********************************************************************************************************
//文件名:外部中断实验
//修改日期:2021年10月12日
//功能描述:按下不同的按键,分别实现计数,清零,控制阈值
//硬件连接:K1,K2,K3分别连接INT0,INT1,INT2,PA3和PA4分别接两个74HC573锁存器,再接数码管,PA1连接一个LED
//调试状态:正常运行,正常执行功能
//*********************************************************************************************************
#include <iom16v.h>
#include <macros.h>
#define uchar unsigned char
#define uint unsigned int
//全局变量定义
uchar LEDBuf[6]={2,0,0,0,0,0};//存放数码管显示数据,0-9数字格式
uchar const DuanXuan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//段选数组0~9
uchar const WeiXuan[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};//从左到右第0-5位数码管
uint threshold=0;//阈值模式标志量
uint thresholdvalue;//阈值变量
uint count=0;//按键1计数
//函数声明
void delay(uint ms); //声明延时函数
void Init(void); //声明IO初始化函数
void Jud_thr(void); //判断阈值函数
void LEDshow(uchar LEDBuf[6]); //LED显示函数
//**************************************************************************************
//主函数
//**************************************************************************************
void main(void)
{
Init(); //调用IO初始化函数
while(1)
{
LEDshow(LEDBuf); //调用数码管显示函数
Jud_thr(); //调用判断阈值函数,获取当前阈值
if(count>=thresholdvalue) //比较当前阈值与计数值
{
PORTA=0x01; //点亮LED
GICR= 0xA0; //关闭外部中断INT0
}
delay(1);
}
}
//**************************************************************************************
//函数名:延时函数
//输入参数:ms
//**************************************************************************************
void delay(uint ms) //延时函数
{
uint p,q;
for(p=0;p<ms;p++)
{
for(q=0;q<1141;q++);
}
}
//**************************************************************************************
//函数名:IO初始化函数
//函数功能:初始各IO口
//**************************************************************************************
void Init(void)
{
DDRA|=BIT(PA0); //PA0设置为输出状态
DDRA|=BIT(PA3); //PA3设置为输出状态
DDRA|=BIT(PA4); //PA4设置为输出状态
DDRC=0xFF; //PC口设置为输出状态
DDRD = 0X00; //K1 K2按键设置为输入端口
PORTD = 0XFF; //使能上拉电阻
DDRB = 0X00; //K3按键设置为输入端口
PORTB = 0XFF; //使能上拉电阻
MCUCR = 0x00; //下降沿触发
GICR = 0xE0; //开启INT0,INT1,INT2中断开关
SREG|=0x80; //开启中断总开关
}
//**************************************************************************************
//函数名:判断阈值函数
//函数功能:通过当前阈值模式确定当前阈值
//**************************************************************************************
void Jud_thr(void)
{
if(threshold==0)
thresholdvalue=20;
if(threshold==1)
thresholdvalue=15;
if(threshold==2)
thresholdvalue=10;
if(threshold==3)
thresholdvalue=5;
}
//**************************************************************************************
//函数名:数码管显示函数
//函数功能:确定数码管显示值
//**************************************************************************************
void LEDshow(uchar LEDBuf[6])
{
uchar i;
for(i=0;i<6;i++) //循环扫描一次
{
PORTC=WeiXuan[i]; //选择第i位数码管
PORTA|=BIT(PA4); //PA4输出高电平,位选端拉高
PORTA&=~BIT(PA4); //PA4输出低电平,位选端拉低,位选锁存
PORTC=DuanXuan[LEDBuf[i]]; //输出段选
PORTA|=BIT(PA3); //PA3输出高电平,段选端拉高
PORTA&=~BIT(PA3); //PA3输出低电平,段选端拉低,段选锁存
delay(1);
}
}
//**************************************************************************************
//函数名:外部中断INT0
//函数功能:按下KEY1计数值加一
//**************************************************************************************
#pragma interrupt_handler int0_isr:iv_INT0
void int0_isr(void)
{
LEDBuf[5]++; //个位加一
count++; //计数值加一
if(LEDBuf[5]==10) //逢十进一
{
LEDBuf[5]=0;
LEDBuf[4]++;
if(LEDBuf[4]==10)
{
LEDBuf[4]=0;
}
}
}
//**************************************************************************************
//函数名:外部中断INT1
//函数功能:按下KEY2计数值清零,数码管清零
//**************************************************************************************
#pragma interrupt_handler int1_isr:iv_INT1
void int1_isr(void)
{
uchar i;
count=0; //计数值清零
for(i=2;i<6;i++) //将2-5位数码管清零
{
LEDBuf[i]=0;
}
GICR=0xE0; //打开外部中断INT0
PORTA=0x00; //熄灭LED
}
//**************************************************************************************
//函数名:外部中断INT2
//函数功能:改变阈值模式
//**************************************************************************************
#pragma interrupt_handler int2_isr:iv_INT2
void int2_isr(void)
{
uchar i;
threshold++;
if(threshold==4)
{
threshold=0;
}
if(threshold==0)
{
LEDBuf[0]=2;
LEDBuf[1]=0;
}
else if(threshold==1)
{
LEDBuf[0]=1;
LEDBuf[1]=5;
}
else if(threshold==2)
{
LEDBuf[0]=1;
LEDBuf[1]=0;
}
else
{
LEDBuf[0]=0;
LEDBuf[1]=5;
}
count=0; //计数值清零
for(i=2;i<6;i++) //将2-5位数码管清零
{
LEDBuf[i]=0;
}
GICR=0xE0; //打开外部中断INT0
PORTA=0x00; //熄灭LED
}