51单片机完全学习——LED点阵

一、原理图分析

通过看下面的原理图我们发现,LED点阵的每个引脚并没有直接接在单片机的IO口上面,而是和74HC595芯片接在了一起,我们通过查看资料发现,74HC595芯片是一个串行输入转并行输出的一个芯片。那它是如何进行串行转并行的呢?首先这个芯片需要一定的时序才能正常工作,我们主要使用它的3个引脚,分别是RCLK(锁存时钟)、SCLK(移位时钟)、SER(串行采样输入)。当SCLK每一个上升沿的时候,会采样SER引脚电平的高低,这样就将1位数据传了进来,重复8次就可以将一个字节传入,然后RCLK在产生一个上升沿就可以将这些数据锁存住了。数据在锁存的一瞬间会从8位的并行输出端口一起被输出出去。我们这里将4个74HC595芯片串联了起来,这样我们每传输完32位数据,在进行锁存,这样我们的点阵就可以显示一些东西了,至于显示什么要看你让那个部位的灯亮了。这里我们需要注意的是:当我们给一个行也就是POS1是高电平,一个列也就是NEG1是低电平的时候,这时候亮的是左上角的一颗LED也就是L1。显示一个是没有什么问题的,但是如果我们只想点亮L1和L18,那么我们需要给POS1和POS2都是高点平,NEG1和NEG2也都是低电平。但是我们这样的操作亮的可不止这两颗L2和L17也都会被点亮。那我们如何解决这个问题呢?我们知道我们点亮一颗LED的时候,是没有问题的,其实更准确的来讲,我们独立的点亮一行或者一列或者一行或一列中的某些LED都是可以的,那么我们就可以利用人眼的视觉暂留效应,我们一行一行或者一列一列的依次点亮一些LED,只不过我们需要间隔很短的时间来进行这个操作,这样我们看到的就是我们想要的效果了,总之这里和动态控制数码管的原理是差不多的,只不过这里分两张一种是按行,另一种是按列。

二、点亮整个LED屏幕

我们通过点亮整个屏幕,先学会如何操作74HC595芯片,然后在进行其他的显示操作。

#include <reg51.h>
#include <intrins.h>


sbit RCLK = P0^0;
sbit SCLK = P0^1;
sbit SER  = P0^2;

void main(void)
{
	

  unsigned char i = 0, j = 0;
  while(1)
  {
    RCLK = 0;              //首先给RCLK一个低电平为了之后产生上升沿
    for (i=0; i<4; i++)
	{
	  for (j=0; j<8; j++)
	  {
	    SCLK = 0;         //给SCLK一个低电平
		_nop_();          //延时一小段时间,让电平平稳
		if (i == 0 || i == 1)
		{
			SER = 0;      //给SER引脚放上数据 这里放的比较简单所有的行都是高电平
		}                 //所有的列都是低电平 这样所有LED都会被点亮
		else
		{
			SER = 1;
		}
				
		SCLK = 1;	     //SCLK产生上升沿 这样就将SER引脚的电平高低给移了进来
        _nop_();				
	  }
	}
	RCLK = 1;           //等所有32位数据传输完毕,然后锁存就可以在一瞬间将这些数据一起输出出去
  }
}

三、点亮指定的行和列

我们只需要在上面的基础之上,编写一个函数这个函数有4个参数,前两个控制的是列,后两个控制的是行。需要注意的是:如果是列的话,那么需要这一位是0才能点亮,而如果是行的话,则需要这一位是1才能点亮。主要是因为列对应的是LED的负极,而行对应的LED的正极。我们所编写的函数如下,根据原理图我们知道第一个参数是9到16列 第二个参数是1到8列。第三个参数是9到16行,第四个参数是1到8行。如果我们想要点亮最左上上角的一颗那么我们需要的是第一行和第一列,也就是第一行这一位一定要为1,第一列这一位要为0。那么我们需要传入的参数就是

SendData(0xff, 0x7f, 0x00, 0x80);

至于其他的我们按照这个原理进行分析就行了,这里就不进行赘述了。

void SendData(u8 NEG9_16, u8 NEG1_8, u8 POS9_16, u8 POS1_8)
{
	u8 i = 0, j = 0;
	
	RCLK = 0;
	SCLK = 0;
	for (i=0; i<4; i++)    //我们总共4个字节数据,一次发送一个字节
	{
		for (j=0; j<8; j++) //将1个字节的数据,按照从低位到高位依次发送
		{
			SCLK = 0;      //先拉低点平方便之后产生上升沿
			if (i == 0)    //主要是用来判断是第几个字节的数据。
			{
				SER = (NEG9_16 >> j) & 0x01;
			}
			else if(i == 1)
			{
				SER = (NEG1_8 >> j) & 0x01;
			}
			else if(i == 2)
			{
				SER = (POS9_16 >> j) & 0x01;
			}
			else
			{
				SER = (POS1_8 >> j) & 0x01;
			}		
			SCLK = 1;	
			_nop_();				
		}
	}
	RCLK = 1;
}

 四、在LED屏幕上显示汉字

当我们能完成上一步的时候,我们就可以点亮自己想要的某一行,或者某一列,或者一行中的几个,一列中的几个。那么当我们一行一行按照一定的规律将这些LED点亮,是不是就可以显示出汉字呢。首先我们需要借助软件来生成字模,也可以自己手动搞,但是太浪费时间了。这里我们就使用软件来搞。我们需要注意一些问题:由于我们使用的是16*16的点阵,因此我们选择字体大小为12号是最合适的。我一般是用宋体应为宋体的显示效果比较好。你也可以试试其他的字体。还是教教大家如何使用这个软件吧。这个软件还有好多的功能我们后面用到再说。这里我们需要注意的是不同的取模方式,不同的设置这个生成的字模都会不同。我们需要对他生成的字模分析,然后再编写相应的显示函数才能正确的显示,我们不能随便拿一个函数,然后给里面放入生成的字模,有很大的可能显示是不正常的。我们一定要先进行分析。


/*--  文字:  赵  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x02,0x12,0x12,0x12,0xFF,0x12,0x12,0x00,0x20,0x10,0x0C,0x03,0x0C,0x70,0x00,0x00,
0x01,0x06,0xF8,0x04,0xFE,0x22,0x22,0x02,0x12,0x22,0xC2,0x02,0xC2,0x32,0x02,0x00

我们现在对字模进行分析。首先我们分析第一个数字将他转换成二进制也就是0000 0010。我们看第一列的上半部分的组成方式和我们这个数字的二进制就很像。我们就可以猜到这个取模方式应该是纵向取模,但是取完第一列的上半部分然后去取第二列的上半部分,还是取完第一列的上半部分在取第一列的下半部分呢?我们在进一步进行分析第二个数字就知道了。我们发现我们如果将白色的点看成0,黑色的点看成1,那么这个二进制数将是0001 0010我们将这个数字转换成十六进制就是0x12也就是我们生成的字模的第二个数字,然后我们就能得出结论:他这里的取模方式是先取第一列的上半部分然后再取第二列的上半部分,然后再取第3列的上半部分呢。以此类推。那既然它可以纵向进行取模,那我们猜想一下,是不是还存在另外一种取模方式也就是横向取模。

其实关于如何取模这个软件里面是可以进行设置的:

 然后我们选择横向取模生成的字模如下:

/*--  文字:  赵  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x08,0x00,0x08,0x04,0x08,0x84,0x7E,0x44,0x08,0x28,0x08,0x28,0xFE,0x10,0x08,0x10,
0x28,0x28,0x28,0x28,0x2E,0x44,0x28,0x84,0x28,0x00,0x58,0x00,0x4F,0xFE,0x80,0x00

我们可以发现这两种取模方式生成的字模完全不同。按照上面的进行分析就行了。我在这里就直接写结论了。横向取模的方式是:先取第一行的前半部分,在取第一行的后半部分。然后依次类推即可,这里就不再进行赘述。

那么我们相应的就会产生两种函数,一种是纵向取模,另一种是横向取模。也就是说一种是纵向进行刷新,一种是横向进行刷新。我们先拿横向刷新举例子,首先我们选中第一行的这一位让他为1,这样当列为0时,对应的就会亮,为1的就不亮。但是这样有一个问题,我们刚才在生成字模的时候,我们所对应的亮的点为1,不亮的为0.其实这个问题很简单,我们只需要将我们生成的那个数字按位取反就可以达到我们想要的目的了。比如我们可以写如下代码

SendData(~0x00, ~0x08, 0x00, 0x80);   //显示第一行
SendData(~0x04, ~0x08, 0x00, 0x40);   //显示第二行
SendData(~0x84, ~0x08, 0x00, 0x20);   //显示第三行
SendData(~0x44, ~0x7e, 0x00, 0x10);   //显示第四行
SendData(~0x28, ~0x08, 0x00, 0x08);   //显示第五行

我们这里只显示了字的上半部分,想要全部显示按照这个规律写就可以了。但是我们这里需要注意一个小问题就是:我们的第一个参数其实是我们生成的数组奇数位的数字,第二个参数是生成数组的偶数位的数字。参照我们的数组,一下就看明白了。但我们要这样写的话就太浪费了,我们可以写一个循环,然后将我们生成的字模放到一个数组里面,这样就可以使用数组的下标进行访问了,至于第三个和第四个参数,我们也可以写一个数组放到里面去。访问方式也是一样的,但是我并不打算使用这种方式,我打算使用一种新的方式来达成这一目的:具体思路是由于我们每次都是从第1行道第16行刷新,每次只有一位是1,其他位是0,那么我们就可以创建一个二进制数:1000 0000 0000 0000 总共是16位,具体流程是将这个数强制类型转换赋值给POS9_16,然后在将这个数右移8位,在强制类型转换赋值给POS1_8,然后将这个数右移一位,覆盖掉之前的这个数。然后继续按照流程操作赋值即可。具体实现的代码如下:

void hengxiang(u8 zimo[])
{
	u8 i = 0;
	unsigned int temp = 0x8000;  //按行进行刷新
	for (i=0; i<16; i++)
	{
		SendData(zimo[2 * i + 1], zimo[2 * i], (u8)temp, (u8)(temp >> 8));
		temp >>= 1;    //每循环依次刷新一行
	}
}

至于纵向刷新同理即可代码如下:

需要注意的是纵向刷新的时候0是亮,需要进行按位取反操作。

void zongxiang(u8 zimo[])
{
	u8 i = 0;
	unsigned int temp = 0x8000;  //按列进行刷新
	for (i=0; i<16; i++)
	{
		SendData((u8)~temp, (u8)~(temp >> 8), zimo[i + 16], zimo[i]);
		temp >>= 1;    //每循环依次刷新一行
	}
}

最后附上我的接线图:

  • 20
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值