一、实验目的
1. 掌握AVR单片机开发平台的搭建方法。
2. 掌握GPIO的编程方法。
二、实验内容
1. 验证课堂例题
编译下载运行课堂例题——“LED流水灯”和“按键控制LED亮灭”,查看运行结果。
2. 设计程序
参考上述例题的编程思路,在图1-1所示电路中实现“按键控制流水灯样式”的程序编写与调试。
具体要求如下:
(1)初始状态下,所有LED灯点亮;
(2)K1为启/停键,按下后控制LED流水灯按照当前指定样式运行或停止;
(3)K2为流水灯样式选择键,分两种样式,默认为样式1。每次按下K2当前样式立即转变。
样式1:每次仅一个LED熄灭,熄灭LED从L0至L7转移,如此循环运行。
样式2:每次仅一个LED熄灭,熄灭LED从L7至L0转移,如此循环运行。
在设计报告中,需进行总体设计思路描述,给出所涉及到的主程序、中断程序、关键功能子函数的流程图和设计代码。描述程序调试结果及出现的问题。
图1-1 实验电路图
3. 实验结果
(1)描述所设计程序(“按键控制流水灯样式”)的总体设计思路
在不使用中断的前提下,为了实现LED的两种流水效果,按题目要求,两个按键,可以呈现(开,开)(开,关)(关,开)(关,关)四种状态,两种流水模式分别对应两种状态,暂停情况对应剩余两种状态,通过扫描按键状态的变化来确定下一时刻的工作模式,检测到按键变化时跳出当前状态进入下一轮扫描来达到题目要求。
程序主要分为:主函数,初始化函数,流水模式函数,暂停函数,按键扫描函数,状态查询函数。
在IO口初始化函数中,将PORTB口全部设设置为输出模式来控制流水灯的亮灭;将PA2设置为输出模式来控制三极管的开关;将PD2,3设置为输入模式并使能上拉电阻,配合按键来实现状态转换。
在流水模式函数中,通过for循环搭配左移和右移命令实现正向流水以及反向流水。
在暂停函数中,通过按键状态进入死循环,直至按键状态再次发生变化时跳出死循环以此实现暂停的目的。
在判断函数中,通过扫描两个按键的标志位的变化,以此判断按键所对应的状态。(开,开)状态3;(开,关)状态2;(关,关)状态0;(关,开)状态1;
图1-2-2 判断函数流程图
在主函数中,首先调用初始化函数并将PA口全部置于低电平即满足于题目要求默认全亮;之后判断按键1是否按下,按下后进入流水模式;之后开始扫描按键状态,通过状态确定运行模式,即:状态2正向流水;状态3反向流水;状态1暂停;状态0暂停;且每次按键发生变化时跳出当前模式并重新扫描。
主函数流程图如下图所示
图1-2-3 实验流程图
实验代码:
#include
#include
#define uchar unsigned char
#define uint unsigned int
unsigned int flag1=0; //定义按键状态标志位1
unsigned int flag2=0; //定义按键模式标志位2
unsigned int i,state; //定义计数变量和模式变量
void delay(unsigned int ms) //定义延时函数
{
unsigned int p,q;
for(p=0;p>=1;
delay(30); //每次流水延时时间
if(PIND==0B11111110)break; //检测按键1:如果按下则跳出循环
if(PIND==0B11111101)break; //检测按键2:如果按下则跳出循环
}
}
void suspend(void) //定义暂停函数
{
while(flag1==0) //当开关1为打开状态时,死循环以暂停
{
if(PIND==0B11111110)break; //当开关1再次按下时跳出循环
}
}
void key_scan1(void) //定义按键1扫描函数
{
if(PIND==0B11111110) //检测按键1是否被按下
{
delay(10); //延时消抖
flag1=~flag1 ; //按键1标志位取反
}
while(PIND==0B11111110); //判断按键1松开
}
void key_scan2(void) //定义按键1扫描函数
{
if(PIND==0B11111101) //检测按键1是否被按下
{
delay(10); //延时消抖
flag2=~flag2; //按键2标志位取反
}
while(PIND==0B11111101); //判断按键1松开
}
void key_state(void) //判断状态函数
{
key_scan1(); //扫描按键1
key_scan2(); //扫描按键2
if(flag1==0)
{
if(flag2==0)
state=0; //如果两个按键标志位分别为0,0,则为模式0
else
state=1; //如果两个按键标志位分别为0,1,则为模式1
}
else
{
if(flag2==0)
state=2; //如果两个按键标志位分别为1,0,则为模式2
else
state=3; //如果两个按键标志位分别为1,1,则为模式1
}
}
void main(void) //主函数
{
Init(); //执行初始化程序
while(flag1==0) //当按键1未被按下,即初始状态,按键1被按下时,进入主循环
{
PORTB=0x00; //低电平,点亮所有LED
key_scan1(); //扫描按键1
key_scan2(); //扫描按键2
}
while(1) //主循环
{
key_state(); //判断下一时刻状态模式
if(state==0) //如果为模式0,即开关1被断开
suspend(); //调用暂停函数
else if(state==1) //如果为模式1,即开关1被断开
suspend(); //调用暂停函数
else if(state==2) //如果为模式2,即开关1被闭合,且开关2断开
LED_1(); //调用流水模式1,即每次仅一个LED熄灭,熄灭LED从L0至L7转移,如此循环运行
else //如果为模式2,即开关1被闭合,且开关2闭合
LED_2(); //调用流水模式2,即每次仅一个LED熄灭,熄灭LED从L7至L0转移,如此循环运行
}
}