之前做的过程中遇到一些困难,最后解决,在此梳理,记录~~~
点阵屏(Dot matrix display)
一、基本构成
点阵屏由许多小点(像素)组成的矩阵构成,每个小点都可以独立地显示亮或灭的状态。这些像素点通过排列组合,可以显示出各种文字、数字、符号和图形等图像。实物图是第一张,Proteus中是第二张(由于当时没有找到16*16的点阵屏,便用8*8的点阵屏进行了拼接)
二、显示原理
- 点阵生成:点阵的生成通常使用集成电路(IC)驱动。IC驱动器内部包含晶体管和电容器等元件,通过控制这些元件的导通和断开来控制每个像素点的亮灭状态。
- 数据转换:在显示图像时,电子设备会将要显示的信息转换成一个由0和1组成的二进制数据。这个数据就是控制每个像素点显示状态的信号。
- 逐行扫描:对于一个N行M列的点阵屏,电子设备会先将二进制数据按照从左到右、从上到下的顺序逐个传递给屏幕控制器。控制器会按照逐行扫描的方式来控制每个像素的亮度和颜色。具体来说,控制器会在第一行的像素点上输出0或1的电平,然后在短暂的时间内将这些像素点的状态暂存起来。接着,控制器会向下移动到第二行,将第二行像素点的状态进行输出并暂存。这个过程会重复进行,直到所有行的像素点状态都被扫描过一次。
- 刷新显示:为了保持图像的连续性,这个过程会以很快的速度完成。通过不断刷新,点阵屏上的图像就会呈现为连续、稳定的显示。
三、驱动方式
驱动方式主要有静态驱动和动态驱动两种:
- 静态驱动:静态驱动方式下,每个像素点都有独立的驱动电路。这种方式原理简单、控制方便,但硬件接线复杂,成本较高。
- 动态驱动:动态驱动方式下,多个像素点共享驱动电路。通过逐行或逐列扫描的方式,实现对所有像素点的控制。这种方式硬件接线简单,成本也低,在实际应用更多。 还有不清楚的可以参考这些:
- LED点阵广告屏发光的原理,_哔哩哔哩_bilibili
- 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.模块划分:
- 按键扫描模块 (
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; //将读取到键值的值返回
}
- 延时模块 (
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--);
}
}
}
- 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
}
- 显示效果模块:
- 文字闪烁 (
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博客
如有问题,感谢各位批评指正;问题提出,我立即改正,促进不断进步!(抱拳)