AVR单片机实践--ATmega16按键控制流水灯

一、实验目的

1.掌握AVR单片机开发平台的搭建方法。
2.掌握GPIO的编程方法。

二、实验内容及结果

1.设计程序
自行设计电路,包括流水灯和4个按键,完成“按键控制流水灯样式”的程序编写与调试。
具体要求如下:
(1)初始状态下,所有LED灯点亮;
(2)K1为启/停键,按下后控制LED流水灯按照当前指定样式运行或停止;
(3)K2为流水灯样式选择键,分两种样式,默认为样式1。每次按下K2当前样式立即转变。
样式1:每次仅一个LED熄灭,熄灭LED从L0至L7转移,如此循环运行。
样式2:每次仅一个LED熄灭,熄灭LED从L7至L0转移,如此循环运行。

2.实验结果
(1)在下方贴出使用Proteus绘制的电路原理图。对应Proteus仿真文件链接.
在这里插入图片描述

图1 实验电路图

(2)描述所设计程序(“按键控制流水灯样式”)的总体设计思路(可包括根据任务划分的程序顶层结构、各任务的设计思路、各任务间的数据传递,如何解决关键问题):
①程序顶层结构:
程序顶层包含各类头文件,定义相应变量,初始化LED灯与按键引脚。
②各任务设计思路:
首先编写延时函数、按键初始化、LED初始化函数,设定按键对应端口为输入端口(方向寄存器置低电平),并设定上拉电阻使能(数据寄存器置高电平),再设定LED对应端口为输出端口(方向寄存器置高电平),并且输出低电平(因为电路图中LED低电平导通)。
然后编写按键1、2、3的扫描函数,首先进行按键消抖,然后进行按键是否按下的判断,对于按键1编写控制启停的程序,按键2编写控制样式变换的程序,按键3编写流水灯速率改变的程序(思考题要求),最后判断按键是否松开。
主函数中首先初始化LED与按键,然后进入while循环,在循环中不断进行按键1、2、3的扫描,当对应按键按下后就执行对应功能。
③解决问题:
a. 流水灯的实现:
本程序通过定义一个含八个16进制数一位数组,使用for循环为八个LED灯对应的端口的数据寄存器进行赋值的方式实现流水灯功能。
b. 实现按下K2时样式立即改变:
在执行for循环进行流水灯的过程中,也在不断进行着按键扫描函数。若是按键2按下,则会通过break语句跳出该模式流水灯的循环。
c. 实现流水灯按照当前指定样式运行或停止:
在执行for循环进行流水灯的过程中,若检测到按键1按下,则会进入一个while(1)的循环,在里面会为LED端口的数据寄存器进行不断的赋值(其值固定),但不再进行for循环,故其会实现流水灯的暂停。在这个过程中,也不短进行着按键检测,若检测到按键1再次按下,则通过break跳出该死循环,继续进行for循环,即接着之前的流水灯程序进行。
(3)根据所设计的程序顶层结构,对主函数、中断函数(如果没有使用中断可不写)、关键功能子函数的设计思路进行简单描述,并给出各函数的流程图

在这里插入图片描述

图2 按键检测函数流程图

在按键检测函数中,通过读取输入引脚寄存器中对应引脚的电平状态可判断按键是否被按下,若被按下,则改变按键对应的按键标志位(按键1对应power,其控制程序的运行或停止;按键2对应led_number,其控制流水灯的样式;按键3对应timer,其控制流水灯速度。)在主循环中,通过不断地按键检测,可实现对流水灯状态的改变。
在这里插入图片描述

图3 主函数流程图

在主函数中,首先进行相关引脚的初始化,程序刚刚运行时,power的值为0,通过按键扫描待到按键1按下开始执行流水灯程序。在执行流水灯的过程中,也在不断进行按键扫描,若检测到按键1或按键2按下时,即可跳出该样式流水灯的执行过程,执行另一样式的流水灯程序(非思考题)。若检测到按键3按下,可通过对延时时间的改变实现对流水灯循环速度进行改变,本程序中,按键3提供三种不同的速度。

4.程序代码

* 【编译环境】: ICCAVR  
* 【程序目的】: 按键控制流水灯
* 【晶   振】:  8M		
* 【芯   片】:  ATMEGA16	                                                  */   
/******************************************************************************/

/*** 头文件定义 ***/
/******************************************************************************/
#include <iom16v.h>
#define uchar unsigned char
#define uint unsigned int
#define Get_Bit(val, bitn) (val &(1<<(bitn)) )
int power=0;
int led_mode=1;
int timer=100;
uint led_number[8]={
    
    0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
int i;
/******************************************************************************/

/*** 延时函数 ***/
/******************************************************************************/ 
void delay_ms(unsigned int a)	    
{
    
    
    unsigned int j;
    while(--a!=0)
    {
    
    
	     for(j=0;j<120;j++);
    }
}
/******************************************************************************/ 

/*** LED初始化函数 ***/
/******************************************************************************/
void LED_Init(void)
{
    
    
 	DDRD=0xFF;   //PD端口皆设置为输出端口
    PORTD=0xFF;  //PD端口输出高电平
}
/******************************************************************************/

/*** 按键初始化函数 ***/
/******************************************************************************/
void Key_Init(void)
{
    
    
    DDRC=0xF0;     //PC高四位设为输出端口,低四位设为输入端口
    PORTC=0xFF;    //PC0-PC3上拉电阻使能
}
/******************************************************************************/

/*** 按键1函数——控制流水灯暂停或继续 ***/
/******************************************************************************/
void Key_1(void)
{
    
    
    if(Get_Bit(PINC,0)==0)//检验PC0的电平状态是否为低电平
    {
    
    
	     delay_ms(20);//延时消抖
         if(Get_Bit(PINC,0)==0)
         {
    
    
		      if(power==0)
              {
    
    
			       power=1;
			  }
              else
              {
    
    
			       power=0;
			  }
         }
    }
    while(Get_Bit(PINC,0)==0);//判断按键是否松开
}
/******************************************************************************/

/*** 按键2函数——模式选择按键 ***/
/******************************************************************************/
void Key_2(void)
{
    
    
    if(Get_Bit(PINC,1)==0)//检验PC1的电平状态是否为低电平
    {
    
    
	     delay_ms(20);//延时消抖
         if(Get_Bit(PINC,1)==0)
         {
    
    
		      if(led_mode==1)
              {
    
    
			        led_mode=2;
			  }
              else
              {
    
    
			        led_mode=1;
			  }
         }
    }  
    while(Get_Bit(PINC,1)==0);//判断按键是否松开
}
/******************************************************************************/

/*** 按键3函数——改变流水灯速度 ***/
/******************************************************************************/
void Key_3(void)
{
    
    
    if(Get_Bit(PINC,2)==0)//检验PC2的电平状态是否为低电平
    {
    
    
	     delay_ms(20);//延时消抖
         if(Get_Bit(PINC,2)==0)
         {
    
    
		      switch(timer)
              {
    
    
			       case 100: timer= 500;break;
                   case 500: timer=1000;break;
	               case 1000: timer=100;break;
              }
         }
    }
    while(Get_Bit(PINC,2)==0);//判断按键是否松开
}
/******************************************************************************/

/*** 主函数 ***/
/******************************************************************************/
void main(void)
{
    
    
    LED_Init();
    Key_Init();
    while(1)
    {
    
    
	    Key_1();
        Key_2();
        Key_3();
		PORTD=0x00;
        if(power==1&&led_mode==1)//流水灯样式为模式一
        {
    
    
		    PORTD=0x00;
		    delay_ms(500);
		    for(i=0;i<=7;i++)
	        {
    
    
			    Key_1();
                Key_2();
	            Key_3();
	            PORTD=led_number[i];
	            delay_ms(timer);
	            if(power==0)//暂停流水灯
	            {
    
    
				    while(1)
				    {
    
    
					    PORTD=led_number[i];
				        Key_1();
				        if(power==1)//从停止位置继续流水灯
				        {
    
    break;}
				    }
				}
				//if(led_mode==2)//流水灯模式切换时跳出该模式,把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求
				//{break;}       //把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求
	        }
        }
		
        if(power==1&&led_mode==2)//总电源打开,且流水灯样式为模式二
        {
    
       
		    PORTD=0x00;
            delay_ms(500);
	        for(i=7;i>=0;i--)
	        {
    
    
		        Key_1();
                Key_2();
	            Key_3();
	            PORTD=led_number[i];
	            delay_ms(timer);
	            if(power==0)//暂停流水灯
	            {
    
    
				    while(1)
				    {
    
     
					    Key_1();
				        PORTD=led_number[i];
				        if(power==1)//从停止位置开始流水灯
				        {
    
    break;}
				    }
				}
			    //if(led_mode==1)//流水灯模式切换时跳出该模式,把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求
				//{break;}       //把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求
	        }
        }
    }
}

三、实验思考题

1.如果要求每次按下 K2 时,运行模式不能马上发生变化,而是要执行完当前模式的一个周期再改变模式,如何进行程序设计?

可以通过注释掉主函数中K2按下时就立即跳出流水灯样式循环的for循环来达到运行模式不马上变化的功能,而是执行完当前的周期再改变

//if(led_mode==2)//流水灯模式切换时跳出该模式,把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求
//{break;}       //把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求

//if(led_mode==1)//流水灯模式切换时跳出该模式,把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求
//{break;}       //把该两句注释掉可完成完成一个周期后再模式变换的要求,现在即能完成要求

2.如果要用 K3 控制 LED 流水灯速度,如何修改程序?

可以通过写一个switch—case语句来控制timer的变动,从而改变delay_ms(timer)的延时时间,进而最终改变流水灯的变化速度。

/*** 按键3函数——改变流水灯速度 ***/
/******************************************************************************/
void Key_3(void)
{
    
    
    if(Get_Bit(PINC,2)==0)//检验PC2的电平状态是否为低电平
    {
    
    
	     delay_ms(20);//延时消抖
         if(Get_Bit(PINC,2)==0)
         {
    
    
		      switch(timer)
              {
    
    
			       case 100: timer= 500;break;
                   case 500: timer=1000;break;
	               case 1000: timer=100;break;
              }
         }
    }
    while(Get_Bit(PINC,2)==0);//判断按键是否松开
}
/******************************************************************************/

四、程序缺点分析

由于流水灯的速度控制是通过延时函数来实现的,故在按下按键的时候需要长按一段时间以防按键按下过快,主程序正执行到延迟函数,无法及时跳出。

猜你喜欢

转载自blog.csdn.net/Insincerity/article/details/115169136