C51玩8x8LED点阵:PointGame

**

前言

**
我借同学的光驱把光盘中的文件拷出来后,开始尝试玩一玩单片机。
目前做的比较难的是8X8Led点阵的使用,元件感觉有点多啊。

我用的是普中科技HC6800-ES V2.0的开发板,很多人说什么普中垃圾,我觉得还是他们自己C语言和数电等知识还没弄懂之类的吧。还有老师的教程到8X8LED点阵这里,很多人说有问题,我甚至见过一篇博客写这个教程但是在结果后写:没有显示 … \dots
也是,视频里说了有个地方需要短接的,至于是哪里,请往下看!

像以往一样,做个预想,这篇结束时,可以尽量在LED点阵上显示文字等。


**

8X8LED阵列

**
C51玩8x8LED点阵:PointGame_第1张图片
我用的单片机中的LED阵列是左边这种。
行,是D7~D0,而列是P07 ~ P00。
如果想点亮哪盏灯,需要找到那盏灯对应的行和列。行输出1,而对应的列输出0就行了。原理是相当简单。

比如说我想点亮左上角第一个LED,行输出为 0x80 (D7~D0 = 1000 0000),列输出为 0x7f (P07 ~P00 = 0111 1111) 。

**

74HC595

**
C51玩8x8LED点阵:PointGame_第2张图片
74HC595是中间那片。

74HC595是硅结构的CMOS器件。兼容低电压TTL电路,遵循JEDEC标准。具有8位移位寄存器和一个存储寄存器,三太输出功能。
而两个寄存器有不同的时钟,但都是根据上升沿触发。

74HC595原理和使用

C51玩8x8LED点阵:PointGame_第3张图片
单片机芯片通过P35\P34\P36三个IO口来控制D0~D7的输出。
当然如果不用74HC595而直接让LED点阵连接单片机也可以,但这里不谈那个。

  • 值得注意的一点是,OE这个口为低电平有效。如果其有效,那么输出就有效。这个有JOE这个开关来控制。单片机短接片一般没被操作时,短接片是OE短接VCC,为了让OE有效,应该让其短接GND。

C51玩8x8LED点阵:PointGame_第4张图片

C51玩8x8LED点阵:PointGame_第5张图片

同一种芯片的引脚的名字可能不同,但是引脚是一样的。

当我想要使用这块芯片实现,串行输入并行输出的时候,

  1. 电源口就不说了,输入口(SER)准备好输入数字(8个)的第一个。两个时钟信号(SRCLK、RCLK)初始化为0。(非SRCLR)复位口为1(不复位)。JOE段子接GND,让OE为低电平(输出有效)。
  2. 移位寄存器时钟信号(SRCLK)出现上升沿,串行输入口输入一个数字
  3. 第二步运行8次,8次后,需要输入的8位数字就都放在了对应的位置上
  4. 存储寄存器时钟信号(RCLK)出现上升沿,8个并行输出口同时输出。

点亮阵列左上角的LED灯

sbit类型是取端口(一位),将其命名为一个变量。

  1. 还需要注意的是,每次编程直接使用SER、data、RCLK是不行的,容易出现重定义。每次自己还是取另类一点的变量名。
#include
#include
typedef unsigned int u16;
typedef unsigned char u8;


sbit My_SRCLK = P3^6;//移位寄存器时钟信号
sbit My_RCLK = P3^5;//存储寄存器时钟信号
sbit My_SER = P3^4;//串行输入口

void Hc595_SendByte(u8 My_data){
	
	u8 a;
	My_SRCLK = 0;
	My_RCLK=0;
	for(a=0;a<8;++a){
		 My_SER = My_data>>7;	//每次只输入一位
		 My_data<<=1;	
		 My_SRCLK = 1;//出现上升沿     串行输入一位
		 _nop_();//延迟一毫秒(一个指令周期)
		 _nop_();//延迟一毫秒(一个指令周期)
		 My_SRCLK = 0;
	}
	My_RCLK=1;//出现上升沿    并行输出8位
	_nop_();
	_nop_();
	My_RCLK = 0;
	
}

void main(){
	Hc595_SendByte(0x80);//行输入 1000 0000
	P0 = 0x7f;//列输入 0111 1111
	
	while(1){}
	
}

显示结果:
C51玩8x8LED点阵:PointGame_第6张图片

如果JOE与GND没短接的话会出问题。
C51玩8x8LED点阵:PointGame_第7张图片
去掉JOE处的短接片后,如果用手指触碰接口的针多次,LED阵列的那盏灯会灭(如果按复位按钮,等也会灭)。解决方法是按USB口旁边的开关重启就行了。

显示数字零

普中HC6800-ES V2.0开发板资料\常用辅助开发软件\文字取模软件
里有一个取字模软件,可以选择特定的阵列,然后处理后得到自己的位选和段选分别的十六进制。

这里准备一个:链接:https://pan.baidu.com/s/1yKKo6xzj9cvhLAgz6MTz4Q
提取码:s297
复制这段内容后打开百度网盘手机App,操作更方便哦

打开软件后,新建8X8的图像,模拟动画,放大格点,用鼠标在白色窗口中点上自己想要的,取模方式,C51

C51玩8x8LED点阵:PointGame_第8张图片

如果选择的是纵向(行选),那么列选是通用的

0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe

如果选择的是横向(列选),那么行选是通用的

0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01

更改纵向还是横向在参数设置-》其他选项里
C51玩8x8LED点阵:PointGame_第9张图片

1.为了显示正确,必须先选通用的,再选另外一边
2.一般另外一边在显示和延迟后需要消隐

#include
#include
typedef unsigned int u16;
typedef unsigned char u8;


sbit My_SRCLK = P3^6;
sbit My_RCLK = P3^5;
sbit My_SER = P3^4;


 
u8 led_wei[]={
	0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe
};

u8 led_duan[]={
	0x00,0x00,0x3e,0x41,0x41,0x41,0x3e,0x00
};

//延迟
void delay(u16 i){
	while(i--){}
}
void Hc595_SendByte(u8 My_data){
	
	u8 a;
	My_SRCLK = 0;
	My_RCLK=0;
	for(a=0;a<8;++a){
		 My_SER = My_data>>7;	
		 My_data<<=1;
		 My_SRCLK = 1;
		 _nop_();
		 _nop_();
		 My_SRCLK = 0;
	}
	My_RCLK=1;
	_nop_();
	_nop_();
	My_RCLK = 0;
		
}

void main(){
	u8 a=0;
	while(1){
		P0=0x7f;
		for(a=0;a<8;++a){
			P0=led_wei[a];
			Hc595_SendByte(led_duan[a]);
			delay(100);
			Hc595_SendByte(0x00);//消隐
		}

	}	
}

显示结果:
C51玩8x8LED点阵:PointGame_第10张图片
同样如果不短接JOE和GND,还是会出错(输出无效)。但是不碰JOE端子,显示还是正常的。

有这么个有趣的结果,如果不短接JOE和GND,用手指按着JOE端子VCC和JOE上。

手指接触VCC和JOE时,输出无效,一部分灯就没亮(不是灭了)。
当手指没有接触时,又恢复正常(一部分)。
然后陷入长久的循环中。

**

Point_Game

**

64个灯中,我打算选一个灯做Point Bird,这个Bird只会上升和下降。会有障碍从右边生成并移向左边,如果Bird撞到障碍或掉了下来,那么就判断为输。

一共3个模块,

  • keyproc : 独立按键的控制
  • show:列选和行选
  • logic:游戏初始化,倒计时,移动,显示等

keyproc.h

#ifndef _KEY_PROC_H_
#define _KEY_PROC_H_   


#include"reg52.h"
#include"intrins.h"


typedef unsigned char u8;
typedef unsigned int u16;


sbit k1 = P3^1;//RXD
//sbit k2 = P3^0;//TXD


static int key = 0;
void delay(u16 i);
u8 Key_Process();
void Key_Zero();


#endif

keyproc.c

#include"keyproc.h"



void delay(u16 i){
	while(i--){}
}


void Key_Zero(){
	key = 0;
}

u8 Key_Process(){
	
	if(k1==0){
			delay(1000);
			if(k1==0)key=~key;
			delay(1000);
		}
	
	return key;
}

跨文件不要直接使用和传递全局变量,鉴于C语言的特性你也传递不了。跨文件只能用函数传递和返回变量(其实是可以的)
传递跨文件变量看我另一篇文章:C51:解决ERROR L104: MULTIPLE PUBLIC DEFINITIONS

show.h

#ifndef _SHOW_H_
#define _SHOW_H_  

#include"keyproc.h"

sbit g_SRCLK = P3^6;//移位寄存器时钟
sbit g_RCLK = P3^5;//存储寄存器时钟
sbit g_SER = P3^4;//串行输入



void ChooseRows(u8 iData);
void ChooseCols(u8 iData);


#endif

show.c

#include"show.h"


/*
LED阵列
		P07 	P06 	P05 	P04 	P03 	P02	 P01	P00
D7 
D6
D5
D4 
D3 
D2 
D1 
D0
       
*/
void ChooseRows(u8 iData){
	u8 a ;
	g_RCLK = 0;
	g_SRCLK = 0;
	for(a=0;a<8;++a){
		g_SER = iData >> 7;
		iData<<=1;

		g_SRCLK = 1;
		_nop_();
		_nop_();
		g_SRCLK = 0;
		
	}
	g_RCLK = 1;
	_nop_();
	_nop_();
	g_RCLK = 0;
	
}
void ChooseCols(u8 iData){

	P0 = iData;
}

logic.h

#ifndef _LOGIC_H_
#define _LOGIC_H_

#include"show.h"
#include"stdlib.h"
#include"keyproc.h"


//Point  Bird的位置
static u8 bird_y = 4;

//通用的列选
static u8  xdata led_cols[ ]={
	0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe
};

//特殊字符
static u8  xdata led_numbers_rows[6 ][8 ]={
	0x00,0x00,0x3e,0x41,0x41,0x41,0x3e,0x00,  //0
	0x00,0x00,0x22,0x42,0xFE,0x02,0x02,0x00, //1
	0x00,0x21,0x43,0x45,0x49,0x31,0x00,0x00,  //2
	0x00,0x00,0x42,0x81,0x89,0x89,0x76,0x00,  //3
	0x04,0x02,0x01,0x02,0x04,0x08,0x10,0x20, //¡Ì
	0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81 //¡Á

};

//障碍
static u8 xdata led_walls_rows[ ] = {
	0xF0,0xE1,0x07,0x1C,0x33,0x70,0x84,0xCC
};

//显示数组
static u8 xdata leds_no_bird[8]={0};
static u8 xdata leds[8]={0};






void Init_Game();
void Move();
void Display();
void Lose();




#endif

logic.c

#include"logic.h"

void Init_Game(){

	u8 a,b,c;
	

	//初始化显示数组
	for(a=0;a<8;++a)
		leds[a]=0;
	
	//初始化Point Bird的位置
	bird_y = 4;
	
	
	//按k1启动游戏
	while(1)
		if(Key_Process())
			break;
	Key_Zero();
	
	//倒计时 √ 3 2 1 0
	for(a=0;a<5;a++){
			for(b=0;b<100;b++)//ÏÔʾÎȶ¨
			for(c=0;c<8;c++){
						ChooseCols(led_cols[c]);
						ChooseRows(led_numbers_rows[4 - a][c]);
						delay(100);
						ChooseRows(0x00);
					}
				
	}
	
}

	
		

void Move(){
	
	static u8 c = 0;
	u8 a,temp;
	u8 n = rand()%8;
	
	
	//Point Bird的移动
	u8 value = Key_Process();
	Key_Zero();
	if(value){
		if(bird_y ++ >= 7)bird_y = 7;
	}
	else bird_y--;
	
	
	//障碍移动
	for(a=0;a<7;++a){
		leds_no_bird[a]=leds_no_bird[a+1];
	}
	leds_no_bird[7] = 0x00;
	//每隔3个生成一条障碍,并放入数组
	if(c++ ==4 ){					
					leds_no_bird[7] |= led_walls_rows[n];
					c=0;
	}
	
	
	///复制
	for(a=0;a<8;++a)
			leds[a]=leds_no_bird[a];
	
	
	
	//最后处理Point Bird
	temp = leds[1];
	temp |= (1<

main.c

#include"logic.h"

void main(){

	Init_Game();
	
	while(1){
		Move();
		Display();
	}
		
	
}

显示效果:

你可能感兴趣的:(单片机)