一、实验目的
1. 掌握多位数码管动态显示编程方法。
2. 掌握矩阵键盘编程方法。
二、实验内容
1. 验证课堂例题
编译下载运行课堂例题——“LED数码管动态显示”(IO_ex4)和“矩阵键盘显示按键编号”(IO_ex5),查看运行结果。理解程序的编程思想和程序执行过程。
2. 设计程序
参考上述例题的编程思路,参考图1-1(实验1LED控制电路)、图2-1和图2-2(实验2指导),自行设计LED、数码管和矩阵键盘电路,对所设计的电路进行编程,实现按键控制流水灯模式并在数码管进行模式显示的程序编写与调试。
具体要求如下:
(1)初始状态下,数码管显示“000001”,LED按照模式1进行运行;
(2)按下1键后,LED按照模式1进行运行,数码管显示“000001”;
(3)按下2键后,LED按照模式2进行运行,数码管显示“000002”;
(4)按下其余按键,当前LED运行状态和数码管显示无变化。
样式1:每次仅一个LED熄灭,熄灭LED从L0至L7转移,如此循环运行。
样式2:每次仅一个LED熄灭,熄灭LED从L7至L0转移,如此循环运行。
在设计报告中,需进行总体设计思路描述,给出所涉及到的主程序、中断程序、关键功能子函数的流程图和设计代码。描述程序调试结果及出现的问题。
三、实验结果
1. 在下方贴出使用Proteus绘制的电路原理图。
图1-1 实验电路图
2. 描述所设计程序的总体设计思路
本次程序设计主要是实现矩阵键盘对LED流水灯与数码管的控制,其中为节约IO,数码管的动态扫描依托于锁存器实现。总体思路为:主循环开始,默认状态为数码管显示“000001”,LED按照模式1进行运行。每次LED移位的过程中,对矩阵键盘进行扫描,判断按键状态,以此来决定下一状态数码管的显示和LED的流水状态。
因为使用的单片机为单核心单线程,所以如果LED流水与数码管刷新显示交替进行,流水延时较小时可以保证数码管动态显示频率足够,但是流水间隔太短无法达到流水效果。流水延时较大时流水效果正常,但是数码管动态刷新频率不足,导致数码管频闪,因此本次编程中,将延时语句替换为两次数码管刷新,这样既保证了流水灯正常显示,也保证了动态刷新数码管所需要的频率。
- 根据所设计的程序顶层结构,对主函数、关键功能子函数的设计思路进行简单描述,并给出各函数的流程图
方法一(查询法):
程序主要分为:主函数;延时函数;按键检测函数;按键定位函数;LED流水模式函数;初始化函数;数码管扫描函数。
主函数思路简单,初始化矩阵键盘,数码管,LED后进入主循环,判断工作模式状态,状态一下数码管显示“000001”,LED部分每次仅一个LED熄灭,熄灭LED从L0至L7转移,如此循环运行(初始为状态一)。当检测到按键“2”按下,工作模式标志位取反,并跳出当前循环模式,按模式二(状态二下数码管显示“000002”,LED部分每次仅一个LED熄灭,熄灭LED从L7至L0转移,如此循环运行)进行。
数码管扫描函数:采用动态扫描,首先输出位选信号,选中一位数码管后锁存器控制端拉低,锁存位选信号,此时输出段选信号,确定该位数码管输出的数字或者字母,后锁存器控制端拉低,锁存段选信号。循环扫描实现数码管动态刷新,同时,实现了IO复用。
按键检测函数:矩阵键盘连接方式为同一组IO的高低四位分别作为行线和列线,分别给行线和列线不同的电平值,当有按键按下时,IO口的电平状态发生变化,扫描这个值即可得知有按键被按下。
按键定位函数:采用行列扫描,通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键被按下。按键定位后改变相应按键对应模式的模式标志位。并返回该按键对应的显示码。
LED流水模式函数:流水的模式上,每次切换下一个灯的间隙进行数码管刷新,同时判断按键状态是否发生变化,如果是,立即跳出当前循环模式。
图2-1 主函数流程图
2-2 流水模式流程图
#include <iom16v.h>
#include <macros.h>
//宏定义
#define uchar unsigned char
#define uint unsigned int
//全局变量定义
//数码管使用
uchar const DuanXuan[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//共阴级数码管显示0-F
uchar const WeiXuan[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};//从左到右第0-5位数码管
uchar LEDBuf[6]={0,0,0,0,0,1};//存放数码管显示数据,0-9数字格式
uint LED_model=1;//定义LED流水模式标志
//函数声明
void delay(uint ms); //延时函数
void LEDshow(uchar LEDBuf[6]); //数码管显示函数
uchar key_press(); //按键检测函数
uchar key_where(); //按键定位函数
void LED_1() ; //LED流水模式一
void LED_2() ; //LED流水模式二
void Init(); //初始化函数
//**************************************************************************************
//主函数
//**************************************************************************************
void main(void)
{
Init();
while(1)
{
if (LED_model)
LED_1();
else
LED_2();
}
}
//**************************************************************************************
//函数名:延时函数
//输入参数:ms
//**************************************************************************************
void delay(uint ms)
{
uint p,q;
for(p=0;p<ms;p++)
{
for(q=0;q<1141;q++);
}
}
//**************************************************************************************
//数码管模块子函数
//**************************************************************************************
//**************************************************************************************
//函数名:LEDshow
//功能:在数码管上显示出对应的数字。0-5位数码管显示数据放在LEDBuf[]中,存放0-9数字格式
//输入参数:LEDBuf[]每个元素对应6位数码管每位的显示数字(0-F)
//**************************************************************************************
void LEDshow(uchar LEDBuf[6])
{
uchar i;
for(i=0;i<6;i++) //循环扫描一次
{
//数码管位选程序
PORTB=WeiXuan[i]; //选择第i位数码管
PORTA|=BIT(PA4); //PA4输出高电平,位选端拉高
PORTA&=~BIT(PA4); //PA4输出低电平,位选端拉低,位选锁存
//数码管段选程序
PORTB=DuanXuan[LEDBuf[i]]; //输出段选
PORTA|=BIT(PA3); //PA3输出高电平,段选端拉高
PORTA&=~BIT(PA3); //PA3输出低电平,段选端拉低,段选锁存
delay(1);
}
}
//**************************************************************************************
//矩阵键盘模块子函数
//**************************************************************************************
//**************************************************************************************
//函数名:key_press
//功能:检测是否有低电平出现,来判断是否有按键按下
//返回值:1-有键按下,0-无键按下
//**************************************************************************************
uchar key_press(void)
{
uchar temp;
PORTC=0xF0; //高4位输出高电平,低4位输出低电平
//DDRC=0x0F; //高4位为输入,低4位为输出
temp=PINC; //读取PC口
if(temp==0xF0) //输入端未出现低电平(没有按键按下)
{
return 0; //输出0
}
else //有按键按下
{
return 1; //输出1
}
}
//**************************************************************************************
//函数名:key_where
//功能:读行线列线电平,根据按键键值进行定位
//返回值:按键代表的十六进制数字(0-F)
//**************************************************************************************
uchar key_where(void)
{
uchar Code,j; //存储PINC值,即引脚读入值,j为计数值
uchar Hang=0X7F; //行线输出值
uchar Lie; //列线值,4位
delay(10);
if(key_press()) //调用按键检测函数
{
do
{
//DDRC=0X0F; //高4位(列线)输入,低4位(行线)输出
Hang=((Hang<<1)|(Hang>>7)); //循环左移,每一次只输出一个低电平
PORTC=Hang; //低四位在C口输出,行线逐位输出0
Code=PINC;
Lie=Code&0xF0; //读取key高四位(列线)
}while(Lie==0xF0); //列线出现全1为不在该行,列线出现0说明该键在该行
while(key_press()); //等待按键松开
if(Code==0xEE||Code==0xDE) //有2种情况
{
if(Code==0xEE) //1110 1110 第1行第1列(1,1)
{
Code=0x1; //显示1
LED_model=1; //流水灯模式为模式一
}
else
{
Code=0x2; //显示2
LED_model=0; //流水灯模式为模式二
}
}
else
Code=LEDBuf[5]; //其它按键按下,数码管与LED状态不变
for( j=0;j<2;j++)
LEDshow(LEDBuf);
}
return Code;
}
//**************************************************************************************
//函数名:LED_1
//功能:每次仅一个LED熄灭,熄灭LED从L0至L7转移,如此循环运行
//**************************************************************************************
void LED_1(void)
{
uchar i,j,temp;
DDRD=0xFF;
temp=0x01;
for(i=0;i<8;i++)
{
PORTD=temp;
temp<<=1;
for( j=0;j<2;j++)
LEDshow(LEDBuf); //每次LED切换时刷新数码管
if(key_press()) //判断键盘中是否有按键按下
{
LEDBuf[5]=key_where(); //按下后显示在第五位
break;
}
LEDshow(LEDBuf); //数码管刷新
}
}
//**************************************************************************************
//函数名:LED_2
//功能:每次仅一个LED熄灭,熄灭LED从L7至L0转移,如此循环运行
//**************************************************************************************
void LED_2(void)
{
uchar i,j,temp;
DDRD=0xFF;
temp=0x80;
for(i=0;i<8;i++)
{
PORTD=temp;
temp>>=1;
for(j=0;j<2;j++)
LEDshow(LEDBuf); //每次LED切换时刷新数码管
if(key_press()) //判断键盘中是否有按键按下
{
LEDBuf[5]=key_where();
break; //按下后显示在第五位
}
LEDshow(LEDBuf); //数码管刷新
}
}
//**************************************************************************************
//函数名:Init
//功能:初始化数码管,三极管,LED,矩阵键盘
//**************************************************************************************
void Init(void)
{
//初始化数码管
DDRA|=BIT(3); //PA3设置为输出
DDRA|=BIT(4); //PA4设置为输出
DDRB=0XFF; //PB口设置为输出
//初始化三极管
DDRA|=BIT(0); //PA0设置为输出
PORTA&=~BIT(0); //PA0输出低电平导通三极管
//初始化LED
DDRD=0xFF; //PD设置为输出模式
//初始化键盘
DDRC=0x0F; //高4位为输入,低4位为输出
PORTC&=0xF0; //低4位输出低电平
}