分享_简易广告牌_课设(Proteus+51单片机_仿真设计实现)(16*16LED点阵)(电子线路实践)(点阵屏显示原理)

之前做的过程中遇到一些困难,最后解决,在此梳理,记录~~~

点阵屏(Dot matrix display)

一、基本构成

点阵屏由许多小点(像素)组成的矩阵构成,每个小点都可以独立地显示亮或灭的状态。这些像素点通过排列组合,可以显示出各种文字、数字、符号和图形等图像。实物图是第一张,Proteus中是第二张(由于当时没有找到16*16的点阵屏,便用8*8的点阵屏进行了拼接)

二、显示原理

  1. 点阵生成:点阵的生成通常使用集成电路(IC)驱动。IC驱动器内部包含晶体管和电容器等元件,通过控制这些元件的导通和断开来控制每个像素点的亮灭状态。
  2. 数据转换:在显示图像时,电子设备会将要显示的信息转换成一个由0和1组成的二进制数据。这个数据就是控制每个像素点显示状态的信号。
  3. 逐行扫描:对于一个N行M列的点阵屏,电子设备会先将二进制数据按照从左到右、从上到下的顺序逐个传递给屏幕控制器。控制器会按照逐行扫描的方式来控制每个像素的亮度和颜色。具体来说,控制器会在第一行的像素点上输出0或1的电平,然后在短暂的时间内将这些像素点的状态暂存起来。接着,控制器会向下移动到第二行,将第二行像素点的状态进行输出并暂存。这个过程会重复进行,直到所有行的像素点状态都被扫描过一次。
  4. 刷新显示:为了保持图像的连续性,这个过程会以很快的速度完成。通过不断刷新,点阵屏上的图像就会呈现为连续、稳定的显示。

三、驱动方式

驱动方式主要有静态驱动和动态驱动两种:

  1. 静态驱动:静态驱动方式下,每个像素点都有独立的驱动电路。这种方式原理简单、控制方便,但硬件接线复杂,成本较高。
  2. 动态驱动:动态驱动方式下,多个像素点共享驱动电路。通过逐行或逐列扫描的方式,实现对所有像素点的控制。这种方式硬件接线简单,成本也低,在实际应用更多。                      还有不清楚的可以参考这些:
  3. LED点阵广告屏发光的原理,_哔哩哔哩_bilibili
  4. 51单片机第25讲-8x8点阵编码及滚动显示_哔哩哔哩_bilibili

通过控制每个像素点的亮灭状态来实现文字、图像等内容的显示。通过逐行扫描技术,点阵屏能够呈现出连续、稳定的图像效果。蛮好玩的

当时的题目要求:

能够显示不同字符;
按键切换不同的显示效果(如闪烁,静止,平移等);
按键切换不同的显示内容;
能够显示图形或自定义字符;

实现过程:

1.Proteus硬件的连接方式关系着字模提取的设置(这个不懂可以先看后面~~)

       文字数据(字模)存储在数组中,每个字由一系列行数据组成,每行数据表示该行上哪些LED应该点亮。 为了适配LED点阵的驱动方式(高电平有效或低电平有效),在发送数据前需要对字模数据进行处理(如取反)。

2.串行转并行——74HC595:

        因为单片机的I/O口数量有限,当需要控制多个外部设备,如LED矩阵、数码管等时,会占用大量的IO口。为了解决这个问题,可以使用74HC595这样的串行输入/并行输出(SIPO)移位寄存器芯片,通过串行方式输入数据,然后并行输出到多个外部设备,从而节省IO资源。

(这里还用到了I/O口模拟SPI大家查一下,这里提供一些:

        I/O口模拟SPI(Serial Peripheral Interface,串行外设接口)发送数据是一种在没有内置SPI硬件模块或需要额外SPI接口的场合下,通过软件编程控制GPIO(General Purpose Input/Output,通用输入输出)引脚来模拟SPI通信协议的时序,从而实现数据在主机(Master)和从机(Slave)之间的全双工通信。

多个74HC595芯片之间可以进行级联连接,可以满足更大规模的数据处理需求,大家根据需求可以自行拓展,不懂的可以去看看原理:74HC595原理讲解,嵌入式系统原理及设计,单片机开发_哔哩哔哩_bilibili

3.准备程序(重定义函数,全局I/O,变量,函数声明等)

(显示的数组太多,就不搞出来了,大家需要显示什么,用取模软件做一下就行)
#include <reg51.h>
#include <intrins.h>
#define  GPIO_KEY P1  //相当于读取键值的函数,
//--重定义函数变量--//
#define uchar unsigned char
#define uint  unsigned int
#define ulong unsigned long
//控制用到的I/O口
sbit MOSIO = P3^4;
sbit R_CLK = P3^5;
sbit S_CLK = P3^6;
//---全局变量声明--//
ulong column;   //点阵列
ulong row;      //点阵行
ulong dt;
//点阵显示数组
uchar code tab0[] = {0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80,
                             0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00}; 
//--大--//
uchar code tab1[] = {0,0,16,32,16,32,16,16,16,8,16,6,208,1,63,0,208,1,16,6,16,8,16,16,16,32,16,32,0,0,0,0};
//--学-//
uchar code tab2[] ={0,0,56,2,8,2,9,2,74,2,204,2,72,3,74,63,73,34,72,34,74,2,73,2,8,2,56,2,0,0,0,0};
//--课--//
uchar code tab3[] =  {0,0,128,16,190,8,170,4,170,2,254,63,170,2,170,4,190,8,0,18,0,4,246,15,17,0,16,0,0,0,0,0};
//--设--//
uchar code tab4[] ={0,0,32,32,160,33,158,18,130,20,130,8,130,20,158,19,160,32,64,36,0,8,230,31,33,0,32,0,0,0,0,0};

uchar *p[] = {tab1,tab2,tab3,tab4,tab5,tab6,tab7,tab8,tab9,tab10};	
uchar *c[] = {char1,char2,char3,char4,char5,char6};	
//函数声明
void HC595SendData(  uchar BT3, uchar BT2,uchar BT1,uchar BT0);
void translation(uchar num);//由上而下平移		
void twinkle1(uchar num);	//文字闪烁
void twinkle2(uchar num);	//符号闪烁
void Delay10ms(uint c);//延时10ms	
unsigned char Key_Scan();//读取键值
int k, i,j, ms;
uchar keyNum;

4.模块划分:

  1. 按键扫描模块 (Key_Scan 函数):
    • 扫描按键输入,返回按键值。
    • 包括按键消抖处理和按键松开检测。
//读取键值
unsigned char Key_Scan()
{
	unsigned char keyValue = 0 , i; //保存键值
	//--检测按键1--//
	if (GPIO_KEY != 0xFF)		//检测按键K1是否按下
	{
		Delay10ms(1);	//消除抖动

		if (GPIO_KEY != 0xFF)	//再次检测按键是否按下
		{
			keyValue = GPIO_KEY;
			i = 0;
			while ((i<50) && (GPIO_KEY != 0xFF))	 //检测按键是否松开
			{
				Delay10ms(1);
				i++;
			}
		}
	}
	return keyValue;   //将读取到键值的值返回
}
  1. 延时模块 (Delay10ms 函数):
    • 提供精确到10毫秒的延时功能。
    • 通过嵌套循环实现延时,虽然这种方法不够精确,但在很多嵌入式系统中是可行的。
//延时十毫秒
void Delay10ms(uint c)   //误差 0us
{
    unsigned char a, b;

	//--c已经在传递过来的时候已经赋值了,所以在for语句第一句就不用赋值了--//
    for (;c>0;c--)
	{
		for (b=38;b>0;b--)
		{
			for (a=130;a>0;a--);
		}     
	}       
}
  1. 74HC595数据发送模块 (HC595SendData 函数):
    • 向74HC595移位寄存器发送四个字节的数据。
    • 每个字节代表LED点阵的一行(或列,取决于电路连接方式)的亮灭状态。
//通过74HC595发送四个字节的数据
void HC595SendData(uchar BT3, uchar BT2,uchar BT1,uchar BT0)
{  
	uchar i;
	//--发送第一个字节--//
	for(i=0;i<8;i++)
	{
		MOSIO = BT3 >> 7 ;	//从高位到低位
		BT3 <<= 1;

		S_CLK = 0;
		S_CLK = 1;		
	}
	//--发送第一个字节--//
	for(i=0;i<8;i++)
	{
		MOSIO = BT2 >>7;		//从高位到低位
		BT2 <<= 1;

		S_CLK = 0;
		S_CLK = 1;	
	}
	//--发送第一个字节--//
	for(i=0;i<8;i++)
	{
		MOSIO = BT1 >> 7;		//从高位到低位
		BT1 <<= 1;
		S_CLK = 0;
		S_CLK = 1;	
	}
	//--发送第一个字节--//
	for(i=0;i<8;i++)
	{
		MOSIO = BT0 >> 7;		//从高位到低位
		BT0 <<= 1;
		S_CLK = 0;
		S_CLK = 1;
	}
	//--输出--//
	R_CLK = 0; //set dataline low
	R_CLK = 1; //片选
	R_CLK = 0; //set dataline low
}
  1. 显示效果模块
    • 文字闪烁 (twinkle1 和 twinkle2 函数):
      • 通过循环和延时实现文字的闪烁效果。
      • twinkle1 和 twinkle2 函数的区别在于处理的文字数量和字模数组。
//符号闪烁
void twinkle2(uchar num)
{
	while(keyNum!=0x7F)
	{
		for(i = 0; i <num; i++)		//总共5个字
		{
			for(ms = 50; ms > 0; ms--)	//显示50次,即肉眼可识别的停留时间
			{
				for(k = 0; k < 16; k++)			//显示一个字
				{	 						
					//--因为字模软件取的数组是高电平有效,所以列要取反--//
					HC595SendData(~(*(c[i] + 2*k + 1)),~(*(c[i] + 2*k )),
												 tab0[2*k],tab0[2*k + 1]); 
					keyNum=Key_Scan();
					if(keyNum==0x7F)
						break;
				}
				HC595SendData(0xff,0xff,0,0);				//清屏
				if(keyNum==0x7F)
						break;
			}
			if(keyNum==0x7F)
						break;
		}
	}
}
//文字闪烁
void twinkle1(uchar num)
{
	while(keyNum!=0x7F)
	{
		for(i = 0; i < num; i++)		//总共9个字
		{
			for(ms = 50; ms > 0; ms--)	//显示50次,即肉眼可识别的停留时间
			{
				for(k = 0; k < 16; k++)			//显示一个字,16代表16行led
				{	 						
					//--因为字模软件取的数组是高电平有效,所以列要取反--//
					HC595SendData(~(*(p[i] + 2*k + 1)),~(*(p[i] + 2*k )),
												 tab0[2*k],tab0[2*k + 1]); 
					keyNum=Key_Scan();
					if(keyNum==0x7F)
						break;
				}
				HC595SendData(0xff,0xff,0,0);				//清屏	
				if(keyNum==0x7F)
					break;				
			}
			if(keyNum==0x7F)
				break;
		}
	}
}
    • 平移效果 (translation 函数):
      • 将文字逐行向下移动,模拟平移效果。
      • 通过改变行数据的索引来实现逐行移动。
//由上而下平移
void translation(uchar num)
{
	j=0;
	while(keyNum!=0x7F)
	{
		for(ms = 10; ms > 0; ms--)	//移动定格时间设置
		{
			for(k = 0; k < 16; k++)	//显示一个字,16代表16行led
			{	 						
				HC595SendData(~(*(p[0] + 2*(k+j) + 1)),~(*(p[0] + 2*(k+j) )),
				tab0[2*k],tab0[2*k + 1]); 
				//因为字模软件取的数组是高电平有效,所以列要取反	  
				keyNum=Key_Scan();
					if(keyNum==0x7F)
						break;
			}			
			HC595SendData(0xff,0xff,0,0);										   //清屏	
			if(keyNum==0x7F)
				break;			
		} 	
		j++;
		if(j == ((num-1)*16) )
		{
			j = 0; 
		}
	}
}

最后是主函数:

void main(void)
{  
   while(1)
   {
		 HC595SendData(0xff,0xff,0,0);				//清屏	
		 keyNum=Key_Scan();
		 switch (keyNum)
		{
			case(0xFE) :	  //返回按键K1的数据
				translation(10);//平移
				break;
			case(0xFD) :	  //返回按键K2的数据
				twinkle1(10);		//文字闪烁
				break;
			case(0xFB) :	  //返回按键K3的数据
				twinkle2(6);		//符号闪烁
				break;
//			case(0xF7) :	  //返回按键K4的数据
//				;
//				break;
//			case(0xEF) :	  //返回按键K5的数据
//				;
//				break;
//			case(0xDF) :	  //返回按键K6的数据
//				;
//				break;
//			case(0xBF) :	  //返回按键K7的数据
//				;
//				break;
			case(0x7F) :	  //返回按键K8的数据
				HC595SendData(0xff,0xff,0,0);				//清屏
				break;
			default:
				break;
		}		 	
   }
}

最后效果展示:

关于源程序和字符取模软件:

通过百度网盘分享的文件:课设_简易广告牌.zip
链接:https://pan.baidu.com/s/1eBKqroqM4Y5GLJmknjgMxg 
提取码:e3s2 
--来自百度网盘超级会员V2的分享

取模软件的操作方法参考这个前辈:

16*16LED点阵广告牌设计_led点阵广告牌程序简单设计-CSDN博客

在此鸣谢过程中 zh_j_wei 的文章帮助,在此做总结,并加上我的收获

51单片机课程设计——led点阵广告牌程序设计_单片机电子广告牌设计原理图和代码-CSDN博客

如有问题,感谢各位批评指正;问题提出,我立即改正,促进不断进步!(抱拳)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值