实现效果描述:初始状态为全灭,根据按下不同的矩阵键盘键位有不同的流水现象:包括两种速度的拉幕式和闭幕式流水,以及另外四种花式流水现象一共八种现象。
程序清单如下:
/***********************************************************************************************************************************************
按键控制拉闭幕式流水灯有两种速度。
现象:程序下载,上电之后,通过矩阵键盘的八个按键控制可以分别进入两种速度的拉幕式流水灯和闭幕式流水灯共四种,以及另外四种花式流水现象,一共八种。每种现象在运行周期内不会被打断,同一按键做了防误触或长时间按下设置为连按无效。
**********************************************************************************************************************************************/
#include<reg52.h>
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
#define GPIO_LED P0 //宏定义提高可移植性和代码可读性
#define GPIO_KEY P2
//各流水现象对应数组以及防连按的flag值定义
unsigned char code led[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char code led_ON[]={0x00,0x18,0x3c,0x7e};
unsigned char code led_ON1[]={0xe7,0xdb,0xbd,0x7e};
unsigned char code led_OUT[]={0x7e,0x3c,0x18,0x00} ;
unsigned char code led_OUT1[]={0x7e,0xbd,0xdb,0xe7};
static unsigned char flag=100;
static unsigned char flag_now=66;
static unsigned char flag_last=66;
/********************************************************************
* 函 数 名 : delay_ms
* 函数功能 : 毫秒级延时函数
* 入口参数 : 以毫秒为单位的延时时间
********************************************************************/
void delay_ms(unsigned int s)//毫秒级延时
{
unsigned char i;
while(s--)
{
for(i=0;i<125;i++);
}
}
/********************************************************************
* 函 数 名 : keyvalue_get
* 函数功能 : 键盘扫描函数
* 函数说明 : 未设置返回值,键值通过全局变量传递。
********************************************************************/
void keyvalue_get(void) //矩阵键盘扫描
{
char f=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay_ms(10);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
flag_last=flag_now;
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): flag=0;break;
case(0X0b): flag=1;break;
case(0X0d): flag=2;break;
case(0X0e): flag=3;break;
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): flag=flag;break;
case(0Xb0): flag=flag+4;break;
case(0Xd0): flag=flag+8;break;
case(0Xe0): flag=flag+12;break;
}
while((f<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay_ms(10);
f++;
}
flag_now=flag;
}
}
}
/********************************************************************
* 函 数 名 :main
* 函数功能 : 主函数
* 函数说明 : 根据键值全局变量进入不同的流水现象显示模块。
不同速度通过单位延时长短的不同设置实现。
********************************************************************/
void main()
{
int i=0;
while(1)
{
GPIO_LED=0xff;
keyvalue_get();
if((flag==0)&&(flag_last!=flag_now))//闭幕式_较快
{
for(i=0;i<4;i++)
{
GPIO_LED=led_ON[i];
delay_ms(200);
}
}
if((flag==1)&&(flag_last!=flag_now))//开幕式_较快
{
for(i=0;i<4;i++)
{
GPIO_LED=led_OUT[i];
delay_ms(200);
}
}
if((flag==2)&&(flag_last!=flag_now))//跑马灯1
{
for(i=0;i<8;i++)
{
GPIO_LED=led[i];
delay_ms(200);
}
}
if((flag==3)&&(flag_last!=flag_now))//跑马灯2_反向
{
for(i=7;i>0;i--)
{
GPIO_LED=led[i];
delay_ms(200);
}
}
if((flag==4)&&(flag_last!=flag_now))//花式流水3
{
for(i=0;i<4;i++)
{
GPIO_LED=led_ON1[i];
delay_ms(200);
}
}
if((flag==5)&&(flag_last!=flag_now))//花式流水4
{
for(i=0;i<4;i++)
{
GPIO_LED=led_OUT1[i];
delay_ms(200);
}
}
if((flag==6)&&(flag_last!=flag_now))//闭幕式_较慢
{
for(i=0;i<4;i++)
{
GPIO_LED=led_ON[i];
delay_ms(400);
}
}
if((flag==7)&&(flag_last!=flag_now))//开幕式_较慢
{
for(i=0;i<4;i++)
{
GPIO_LED=led_OUT[i];
delay_ms(400);
}
}
}
}
硬件仿真电路如下(proteus)
采用自设计51最小系统,用洞洞板焊接。所需要的矩阵键盘作为外设单独焊接,使用时外接到最小系统板上,一组八个LED灯作为常用外设,直接设计并焊接在了单片机最小版上。
首先是先完成最小系统板的设计与焊接,每一条线路焊接完毕之后都用万用表测试,通过后才进行下一步,来减少硬件问题错误排查的工作量。后面在老师引导下按键控制从独立按键加外部中断控制改为矩阵键盘加键盘扫描控制,所以加了个矩阵键盘,因为不在最初的系统设想内,所以是用的另外焊接的作为外设接入使用。
硬件电路如下:
设计结果及错误分析
设计结果:完整完成题目预期的两种速度下的拉幕式和闭幕式流水,之外还额外加入四种花式流水展示。用到了矩阵键盘,八个LED灯,灯和键盘各占用一组IO口。
错误分析:
1.程序设计思路的调整:之前习惯了整体处在流水灯循环的状态,然后通过外部中断的按键来控制变速或者状态切换,因此就一直陷入这个误区中,加之矩阵键盘的检测是要一直进行扫描的,一度想过把键扫程序嵌入到每一个流水灯交替闪烁的延时函数里,在后来跟老师的交流中明白这样不仅计算困难而且会将原本就不精确的delay函数延时变得更加不确定不稳定,最后在老师的建议和引导下,采用了主函数中键扫程序和单周期流水顺序执行的思路,利用单片机执行速度的优势来近似模拟实时检测按键以及相关控制,同时为了防止误触或长按设置了标志位每次都会和前一次的键值比对。
2.编程风格的调整:除了之前课程设计中就学到并坚持实践的模块化代码编程,这次在细节上比如宏定义的使用上,因为开始实现功能测试时为了排除自己设计焊接的板子的问题我是先在开发板上做的程序,在移植过程中因为端口的采用不尽相同,所以每次更改或多或少会因为遗漏而产生错误,发现并改正之后意识到这样不仅可以提高系统的可移植性,而且用同一有意义的宏定义来代替端口编号的话也同时可以增加代码的可读性。