基于51的点阵广告屏设计

 一、任务要求

1、开机默认或按下静态按键显示16*16点阵图像“凹凸电子”(如下图所示)

2、按下动态按键后由下至上循环显示指定汉字“闲鱼凹凸电子”(如下图所示)

二、系统原理

以stc89c52为主控芯片,为驱动16*16LED点阵屏,横向1~16引脚通过俩块74HC595芯片级联驱动,而纵向A~P引脚则由两块74HC138操控电平,依赖定时器中断来控制滚动汉字的速度,采用外部中断完成多种模式的切换。

三、整体方案

1、单片机最小系统

社区内有关单片机最小系统科普不少,各位大佬讲的又是通俗易懂,那我就略掉啦,欸嘿(*/ω\*)

哦,对啦~除开单片机的“电源电路”“复位电路”“晶振电路”这三位不可或缺的大神外,同样值得注意的知识点还有单片机的EA引脚,她表示“存取外部程序代码”。当EA = 1时,执行片内程序存储器ROM中的程序,而EA = 0时,执行片外ROM中的程序。虽然该引脚内部置有TLL,在悬空(啥都不接)情况理论调试为高电平,但CMOS本身较为容易受外接干扰等因素,故建议尽量按照原有案例或说明进行连接。

由于proteus自身的特性(缺陷),该软件中的单片机其实不接最小系统压根不影响工作······,然而该接最好还是解一下,就当养成好习惯啦。

2、16*16点阵电路

如上图,初次了解LED点阵屏但了解多位数码管工作原理肯定能很快理解。她们本质上相同,都是由数个二极管整齐排布(数码管摆了个“8.”,而LED点阵则摆的更像矩阵),从横向将各个二极管的正极(负极)相连并引出用来作"行",从纵向将各个二极管的负极(正极)相连并引出用来作"列",控制方式也与多位数码管高度相似,各“行”相当于“公共端”依次赋值位选,各“列”相当于“A-dp”用于发送段码。

3、74HC595(串转并)电路

如果我们只想使用一块74HC595来解决单片机IO口的扩展问题,直接上网搜素该芯片的中文资料手册,我们立即就可以知道想要在程序上实现功能,只需每当spi_shcp上升沿到来时,spi_ds引脚当前电平值在移位寄存器中左移一位,在下一个上升沿到来时移位寄存器中所有位都会向左移一位,这样连续进行8次,就可以把数组中每一个数(8位的数)送到移位寄存器;然后当spi_stcp上升沿到来时,移位寄存器的值将会被锁存到锁存器里,并从Q1-7引脚输出。

然而毕竟是16*16的LED点阵,所以想要实现两块74HC595的级联,我们需要看到串行输出(Q7'),考虑到当spi_shcp上升沿到来的同时Q7’也会串行输出移位寄存器中高位的值,所以只需要将第一块595的Q7'接向第二块595的DS端就没有问题啦。

#ifndef __74HC595_H_
#define __74HC595_H_

/**********************************
包含头文件
**********************************/
#include<reg51.h>
#include "intrins.h"

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif   

/**********************************
PIN口定义
**********************************/
sbit SER = P1^4;    
sbit RCLK = P1^3;    
sbit SRCLK = P1^2;    

/**********************************
函数声明
**********************************/
/*向74HC595发送一个字节的数据*/
void Hc595SendByte_8(uchar dat);					  
/*向74HC595发送两个字节的数据*/
void Hc595SendByte_16(uint dat);

#endif
#include"74HC595.H"

/*******************************************************************************
* 函 数 名         : Hc595SendByte_8(uchsr dat)
* 函数功能		     : 向74HC595发送一个字节的数据
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Hc595SendByte_8(uchar dat)
{
	uint a;

	SRCLK = 1;
	RCLK = 1;
	for(a=0;a<8;a++)  //发送8位数
	{
		SER = dat >> 7;  //从最高位开始发送
		dat <<= 1;
		SRCLK = 0;  //发送时序
		_nop_();
		_nop_();
		SRCLK = 1;	
	}
	RCLK = 0;
	_nop_();
	_nop_();
	RCLK = 1;
}

/*******************************************************************************
* 函 数 名         : Hc595SendByte_16(uchsr dat)
* 函数功能		     : 向74HC595发送两个字节的数据
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Hc595SendByte_16(uint dat)
{
	uint a;

	SRCLK = 1;
	RCLK = 1;
	for(a=0;a<16;a++)  //发送16位数
	{
		SER = dat &0x8000;  //从最高位开始发送
		dat <<= 1;
		SRCLK = 0;  //发送时序
		_nop_();
		_nop_();
		SRCLK = 1;	
	}
	RCLK = 0;
	_nop_();
	_nop_();
	RCLK = 1;
}

可以看到单个595(void Hc595SendByte_8(uchar dat);)与两个595级联(void Hc595SendByte_16(uint dat);)后的驱动代码并没有太大区别,仅仅只是输入的16进制数(dat)的字节数不同罢了。

值得注意的是"uint"与”uchar“的区别,两者占用空间有所不同,uchar的内存占用空间为1个字节,8比特。uint的内存占用空间为2个字节,16比特。(我开发的时候就在这个东西上面吃过亏)

4、74HC138译码器电路

正如前面所说,点阵的工作原理与数码管相似,那么一定存在所谓的公共端,所以这里我们选择”行共阴“作为所谓的公共端,为了驱动点阵的公共端们,必须一个一个轮流赋值低电平,所以为了实现节约IO口的设计,锵锵,还有比74HC138更适合这项工作的嘛。

74HC138的程序设计完全依照真值表(真值表可是数电芯片的灵魂啊,混蛋)

#ifndef __74HC138_H_
#define __74HC138_H_

/**********************************
包含头文件
**********************************/
#include<reg51.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif   

/**********************************
PIN口定义
**********************************/
#define HC138_DATAPINS P2
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;


/**********************************
函数声明
**********************************/
/*改变74HC138数据输出*/
void ZdyHc138_8(uint i);
/*改变两个74HC138数据输出*/
void ZdyHc138_16(uint i);

#endif
#include"74HC138.H"

/*******************************************************************************
* 函 数 名         : ZdyHc138_8(uint i)
* 函数功能		     : 改变74HC138数据输出
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void ZdyHc138_8(uint i)
{
	switch(i)	 
		{
		  case(0):
				LSA = 1;LSB = 1;LSC = 1; break;  //显示第0位
			case(1):
				LSA = 0;LSB = 1;LSC = 1; break;  //显示第1位
			case(2):
				LSA = 1;LSB = 0;LSC = 1; break;  //显示第2位
			case(3):	
				LSA = 0;LSB = 0;LSC = 1; break;  //显示第3位
			case(4):
				LSA = 1;LSB = 1;LSC = 0; break;  //显示第4位
			case(5):
				LSA = 0;LSB = 1;LSC = 0; break;  //显示第5位
			case(6):
				LSA = 1;LSB = 0;LSC = 0; break;  //显示第6位
			case(7):
				LSA = 0;LSB = 0;LSC = 0; break;  //显示第7位	
		}
}

/*******************************************************************************
* 函 数 名         : ZdyHc138_16_16(uint i)
* 函数功能		     : 改变两个74HC138数据输出
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void ZdyHc138_16(uint i)
{
	switch(i)	 
		{
		  case(0):
				HC138_DATAPINS = 0xFD; break;  //显示第0位
			case(1):
				HC138_DATAPINS = 0xDD; break;  //显示第1位
			case(2):
				HC138_DATAPINS = 0xBD; break;  //显示第2位
			case(3):	
				HC138_DATAPINS = 0x9D; break;  //显示第3位
			case(4):
				HC138_DATAPINS = 0x7D; break;  //显示第4位
			case(5):
				HC138_DATAPINS = 0x5D; break;  //显示第5位
			case(6):
				HC138_DATAPINS = 0x3D; break;  //显示第6位
			case(7):
				HC138_DATAPINS = 0x1D; break;  //显示第7位
      case(8):
				HC138_DATAPINS = 0xFE; break;  //显示第8位
			case(9):
				HC138_DATAPINS = 0xFA; break;  //显示第9位
			case(10):
				HC138_DATAPINS = 0xF6; break;  //显示第10位
			case(11):	
				HC138_DATAPINS = 0xF2; break;  //显示第11位
			case(12):
				HC138_DATAPINS = 0xEE; break;  //显示第12位
			case(13):
				HC138_DATAPINS = 0xEA; break;  //显示第13位
			case(14):
				HC138_DATAPINS = 0xE6; break;  //显示第14位
			case(15):
				HC138_DATAPINS = 0xE2; break;  //显示第15位			
		}
}

这个就比较简单了, 只是两块芯片一起工作时要考虑到,行共阴们有且只能有一个低电平,所以需要改变E2与E3的电平来使两块芯片分别使能,只有一块138时就不需要考虑这么多直接接地即可。

 5、按键电路

#ifndef __EXTER_H_
#define __EXTER_H_

/**********************************
包含头文件
**********************************/
#include<reg51.h>   

/**********************************
函数声明
**********************************/
/*设置外部中断0*/
void Int0Init();
/*设置外部中断1*/
void Int1Init();						  

#endif
#include"EXTER.H"

/*******************************************************************************
* 函 数 名         : Int0Init()
* 函数功能	  	   : 设置外部中断0
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Int0Init()  //设置INT0
{
	IT0 = 1;  //跳变沿出发方式(下降沿)
	EX0 = 1;  //打开INT0的中断允许。	
	EA = 1;  //打开总中断	
}

/*******************************************************************************
* 函 数 名         : Int1Init()
* 函数功能		     : 设置外部中断1
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Int1Init()  //设置INT1
{
	IT1 = 1;  //跳变沿出发方式(下降沿)
	EX1 = 1;  //打开INT1的中断允许。	
	EA = 1;  //打开总中断	
}
/*******************************************************************************
* 函 数 名       : Int0() interrupt 0
* 函数功能	  	 : 外部中断 0 的中断函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void Int0() interrupt 0 
{
	Delay_Us(1000);  // 延时消抖
	if(key_exter_0 == 0)
	{
		zt = 0;
	}
}

/*******************************************************************************
* 函 数 名       : Int1() interrupt 2
* 函数功能	  	 : 外部中断 1 的中断函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void Int1() interrupt 2 
{
	Delay_Us(1000);  // 延时消抖
	if(key_exter_1 == 0)
	{
		zt = 1;
	}
}

按键电路实在没啥好讲的,调用了两个外部中断······,其实代码的周期不算长 ,滚动汉字用的延时还是定时器中断,普通的按键完全够用,主要是中断是真好用呀。

四、仿真绘制

五、软件实现

/*******************************************************************************
 * 文件名   :main.c
 * 描述     :基于51的LED点阵。        
 * 实验平台 :Proteus8.6
 * 作者     :凹凸电子
 * 日期     :2023.10
 * 备注     :无

接线说明:

实验操作:

*******************************************************************************/
// 8051单片机头文件
#include <reg51.h>

// 标准输入输出头文件
#include <stdio.h>  

#include"DELAY.H"
#include"74HC138.H"
#include"74HC595.H"
#include"51_LEDDIANZHEN_ZK.H"
#include"TIMER.H"
#include"EXTER.H"

// 定义unsigned char类型的uchar
#define uchar unsigned char
	
// 定义unsigned int类型的uint
#define uint unsigned int 
  
// PIN口定义
sbit key_exter_0 = P3^2;
sbit key_exter_1 = P3^3;

uint j = 0,zt = 0;

// 函数定义
void hz_jt_8_8();
void hz_zy_16_16();

/*******************************************************************************
* 函 数 名       : main
* 函数功能	  	 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main() 
{
	Timer0Init();
	Int0Init();
	Int1Init();
  while (1) 
	{
		while (zt == 0)
			hz_jt_8_8();
		while (zt == 1)
		  hz_zy_16_16();
  }
}

void hz_jt_8_8()
{
	uint i;
		for(i = 0;i < 16;i ++)
	  {
		  ZdyHc138_16(i);
		  Hc595SendByte_16(hz_aoyudianzi_8[i]);  //发送段选数据
		  Delay_Us(100);  //间隔一段时间扫描	
		  Hc595SendByte_16(0x0000);  //消隐
	  }  	
}

void hz_zy_16_16()
{
	uint i;
		for(i = 0;i < 16;i ++)
	  {
		  ZdyHc138_16(15 - i);
		  Hc595SendByte_16(hz_xianyuaoyudianzi_16[i + j]);	//发送段选数据
		  Delay_Us(100);  //间隔一段时间扫描	
		  Hc595SendByte_16(0x0000);  //消隐
	  }  	
}



void Timer0() interrupt 1
{
	static uint i;
	TH0 = 0XFC;  // 给定时器赋初值,定时1ms
	TL0 = 0X18;
	i++;
  if (i >= 500 ) 
	{
		i = 0;
		j ++;	
  }
	if (j >= 112 || zt == 0)
	{
		j = 0;
	}
}

六、作者留言

如你们所见,我本来是干闲鱼上接单的,叫凹凸电子,就这一套,我卖9块8。说实话,我也是一位学生,一位大学生,接单的时候遇到了不少同学的课设,学生单不好做哇,学生对价格敏感,加上好老师不会布置仅仅需要复制的作业,而且由于存在展示和答辩,学生不可以一点都不懂,所以兼职客服与工程师(毕竟整个店就我一人)的我往往很疲惫。

在干完一学生单的时候,我突然回想起自己刚开始接触嵌入式的时候,我是怎么一点一点学到现在的呢?又想起自己遇上的一些客户,他们并没有下单的念头,仅仅只是向我请教问题罢了,而我也并未拒绝。

我曾经也因为缺少一些开源资料而苦恼过,所以有感而发,敲下了这么一篇文章,请诸位学子一同共勉,文章有写得不好之处,果然还是欠功夫,还请大家不要笑话。

七、开源链接

上面的代码还差个定时器中断初始化及延时函数等,所以给个github链接,所有的文件都在里面,Proteus用的是8.6,而且还会不断更新的。

 GitHub - mynameisliuyu/51_project

 再送大家一本物理教材上几句我很喜欢的话

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

「已注销」

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

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

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

打赏作者

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

抵扣说明:

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

余额充值