首页 > 其他分享 >51单片机完全学习——LED点阵

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

时间:2024-07-27 11:58:33浏览次数:10  
标签:取模 LED 16 0x00 51 单片机 0x08 我们

一、原理图分析

通过看下面的原理图我们发现,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;    //每循环依次刷新一行
	}
}

标签:取模,LED,16,0x00,51,单片机,0x08,我们
From: https://blog.csdn.net/2401_83010734/article/details/140579054

相关文章

  • 单片机CPU运行原理
    一、CPU大致工作原理单片机(MCU)是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一......
  • 51nod-基因匹配+luogu-【模板】最长公共子序列
    https://www.luogu.com.cn/problem/P1439https://class.51nod.com/Html/Textbook/ChapterIndex.html#textbookId=126&chapterId=338以上两个都是特例,一个是每个元素不重复,一个是每个元素均有5个。正确性说明参考:https://www.luogu.com.cn/article/1bcs9052由于一般情况可能出......
  • 零基础STM32单片机编程入门(二十二) ESP8266 WIFI模块实战含源码
    文章目录一.概要二.ESP8266WIFI模块主要性能参数三.ESP8266WIFI模块芯片内部框图四.ESP8266WIFI模块原理图五.ESP8266WIFI模块与单片机通讯方法1.硬件连接2.ESP8266模块AT指令介绍六.STM32单片机与ESP8266WIFI模块通讯实验1.硬件准备2.软件工程3.软件主要代码4.实验......
  • 【深海王国】小学生都能玩的单片机!番外1:Arduino家族Uno-Mega-Nano-Pro Mini-ATtiny85
    Hi٩(๑^o^๑)۶,各位深海王国的同志们,早上下午晚上凌晨好呀~辛勤工作的你今天也辛苦啦(o゜▽゜)o☆今天大都督继续为大家带来单片机的番外系列——小学生都能玩的单片机!番外1带你快速学习认识Arduino家族:Uno、Mega、Nano、ProMini、ATtiny85,了解它们的使用场景与优......
  • L1-11-第五单元-for循环(25~26课)518: T454429 乘方计算
    初学c++的同学,对乘方运算不熟悉,我也是走过几次弯路才写对程序代码,大伙药注意仔细看程序代码。理解其中的奥妙!题目内容给出一个整数 a 和一个正整数 n,求乘方 an。输入格式一行,包含两个整数 a 和 n。−1000000≤a≤1000000,1≤n≤10000。输出格式一个整数,即乘方结果......
  • 「杂题乱刷2」CF1513C
    duel到的。题目链接CF1513CAddOne(luogu)CF1513CAddOne(codeforces)解题思路我们发现,初始数列中的每个数字变为\(10\)必定只需要至多\(10\)次,于是我们可以直接预处理出\(10\)这个数字经过\(i\)次变化后能形成多少位的数字即可。状态为\(dp_{i,j}\)表示\(1......
  • 论文阅读:TKDP: Threefold Knowledge-Enriched Deep Prompt Tuning for Few-Shot Named
    将深度提示调优框架与三重知识(即TKDP)相结合,包括内部上下文知识和外部标签知识和语义知识。引言现有的少样本NER可分为3种:基于词-语义的方法、基于标签-语义的方法和基于提示的方法。基于词语义的方法完全依赖于输入词及其上下文。基于标签语义的方法需要额外利用标签知识。......
  • [ABC363G] Dynamic Scheduling 与 P4511 [CTSC2015] 日程管理
    思路:对于插入操作,设插入\(\{t,p\}\):若当前\(1\simt\)有空位,那么就放进去。否则,\(1\simt\)是被塞满了的:首先容易想到的是找到\(1\simt\)中贡献最小的那个工作,若贡献比\(p\)还小,可以与之替换掉。但是假了,考虑这样一种情况:在\(1\simt\)外有一个更小的......
  • 第九天|字符串| 151.翻转字符串里的单词,卡码网:55.右旋转字符串,28. 实现 strStr(),459.
    边写边更中Day9花了我好长时间,由于一道题有好几种方法,感觉今天上午下午都在做Day9,心态有点崩,因为今天还没有时间科研。我决定休息一下,先更到这里。气死我了151.翻转字符串里的单词方法1_fff:定义一个新的字符串str,遍历s,从后往前找到每个单词添加到str中classSolu......
  • 51nod-3978列车
    https://class.51nod.com/Html/Textbook/Problem.html#problemId=3978&textbookChapterId=724https://class.51nod.com/Html/Textbook/ChapterIndex.html#textbookId=126&chapterId=337这里一次发车的转移是\([j+1,i]\),出发时间\(+s\)为\(j+1\)启程返回,偏移\(i-j-1\)就......