基于Atmega16的GPIO实验(以流水灯为例)

一、实验目的

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的两种流水效果,按题目要求,两个按键,可以呈现(开,开)(开,关)(关,开)(关,关)四种状态,两种流水模式分别对应两种状态,暂停情况对应剩余两种状态,通过扫描按键状态的变化来确定下一时刻的工作模式,检测到按键变化时跳出当前状态进入下一轮扫描来达到题目要求。

  1. 根据所设计的程序顶层结构,对主函数、中断函数、关键功能子函数的设计思路进行简单描述,并给出各函数的流程图

 

程序主要分为:主函数,初始化函数,流水模式函数,暂停函数,按键扫描函数,状态查询函数。

在IO口初始化函数中,将PORTB口全部设设置为输出模式来控制流水灯的亮灭;将PA2设置为输出模式来控制三极管的开关;将PD2,3设置为输入模式并使能上拉电阻,配合按键来实现状态转换。

在流水模式函数中,通过for循环搭配左移和右移命令实现正向流水以及反向流水。

在暂停函数中,通过按键状态进入死循环,直至按键状态再次发生变化时跳出死循环以此实现暂停的目的。

在判断函数中,通过扫描两个按键的标志位的变化,以此判断按键所对应的状态。(开,开)状态3;(开,关)状态2;(关,关)状态0;(关,开)状态1;

                 

 

图1-2-2 判断函数流程图

在主函数中,首先调用初始化函数并将PA口全部置于低电平即满足于题目要求默认全亮;之后判断按键1是否按下,按下后进入流水模式;之后开始扫描按键状态,通过状态确定运行模式,即:状态2正向流水;状态3反向流水;状态1暂停;状态0暂停;且每次按键发生变化时跳出当前模式并重新扫描。

主函数流程图如下图所示

          

 

图1-2-3 实验流程图

实验代码:

#include <iom16v.h>
#include <macros.h>
#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<ms;p++)
	{
	     for(q=0;q<1141;q++);
		 if(PIND!=0xff)break;
    }
}	

void Init(void)               //IO初始化函数    
{  
	DDRA|=BIT(2);             //PA2设置为输出
	PORTA&=~BIT(2);           //PA2输出低电平导通三极管
	
	DDRB=0xFF;                //PB设置为输出模式
             
	DDRD = 0x00;			  //配置PD输入模式
	PORTD = 0xFF; 			  //配置PD上拉电阻      
}

void LED_1(void)       //流水模式1:每次仅一个LED熄灭,熄灭LED从L0至L7转移,如此循环运行
{
    uchar i,temp;
	DDRB=0xFF;
	temp=0x01;
	for(i=0;i<8;i++)
	 {
         PORTB=temp;
		 temp<<=1;
		 delay(30);                    //每次流水延时时间
		 if(PIND==0B11111110)break;    //检测按键1:如果按下则跳出循环
		 if(PIND==0B11111101)break;    //检测按键2:如果按下则跳出循环
	
	 }
}

void LED_2(void)       //流水模式2:每次仅一个LED熄灭,熄灭LED从L7至L0转移,如此循环运行
{
    uchar i,temp;
	DDRB=0xFF;
	temp=0x80;
	for(i=0;i<8;i++)
	 {
         PORTB=temp;
		 temp>>=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转移,如此循环运行
   }
}

  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DongLe.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值