基于51单片机的LED点阵显示屏设计

news2024/11/23 15:53:18

目录

摘要 II
Abstract III
第一章 绪论 1
1.1 课题背景 1
1.2 选题意义 1
1.3 论文主要内容 1
第二章 方法论证对比 3
2.1 单片机编程语言 3
2.2 控制系统设计 3
2.3 显示方式 3
第三章 系统硬件设计 4
3.1 总体硬件设计 4
3.2 系统各硬件电路介绍 5
3.2.1 电源电路设计介绍 5
3.2.2 复位电路 5
3.2.3 晶振电路 6
3.2.4 按键电路 6
3.2.5 点阵驱动模块设计实现 7
3.2.6 通信系统硬件设计 8
第四章 重要器件介绍 9
4.1 LED点整显示屏介绍 9
4.2 限流电阻 10
第五章 系统软件设计 11
6.1 软件整体介绍 11
6.2 显示驱动程序 12
6.3 系统主程序 13
第六章 系统的创作过程与调试 14
6.1 电路制作过程 14
6.1.1 设计原理图 15
6.1.2设计PCB图 15
6.1.3 硬件仿真 16
6.1.4转印PCB 16
6.1.5腐蚀和打孔 16
6.1.6焊接元器件 17
6.2 硬件调试过程 17
6.3 软件调试过程 17
总结 18
参考文献 19
致谢 20
附录一 21
附录二 23
附录三 24

基于51单片机的LED点阵显示屏设计

摘要

本次设计的核心模控制块所用的芯片型号是STC89C52单片机,我们使用4块带锁存功能的串并转换芯片74HC595外加NPN三极管作为16×16LED点阵显示屏的驱动。用PC机作为上位机与单片机进行通信,实现显示内容的更新。该系统可以实现的功能有字符或图片的动态和静态特效显示,可显示中、英文字符。而且该系统还带有级联扩展端口,需要扩展显示屏的大小时直接连接此端口就能实现。系统采用串行传输方式进行数据传送减少了单片机I/O口的占用,简化了电路走线。本次所选用的是价格便宜,应用最广泛的STC单片机,而且写入程序也非常方便,这就使得整个系统成本降低了许多,也方便了系统的维护和检修。

关键词:单片机STC89C52;74HC595;16x16LED点阵;点阵字库。

Abstract
The core of the design of chip used in the model control block is STC89C52 single-chip microcomputer,we use 4 pieces of string with latch function and conversion chip 74 hc595 are needed as a 16 x 16 plus NPN led dot matrix display driver.Using PC as a PC and single chip microcomputer communication, realize the display content updates.The system can realize the function of the characters or images of dynamic and static effects showed that can show Chinese and English characters.With cascade expansion port, and the system also needs to extend screen size directly connect to this port.Data transfer system adopts serial transmission way for reducing the number of the SCM I/O port, simplify the circuit line.Is selected for the cheaper price, the most widely used on STC microcontroller, and write program is also very convenient, this makes the cost down a lot of, the whole system is convenient for maintenance and overhaul of the system.
Key words:single chip STC89C52; 74HC595; 16x16LED lattice; lattice fonts.

第一章 绪论

1.1 课题背景
LED显示技术是八十年代后期才迅速发展起来的新型显示媒体,LED显示屏Light Emitting Diode:又叫电子显示屏,是由几百甚至几十万个发光二极管组成的阵列。LED点阵显示屏在近几年不论是技术上还是应用范围方面都有了很大的进步,目前已经研究出了能发蓝光和纯绿光的LED。LED之所以应用越来越广泛是因为它有许多其他器件无法相比的有点,例如工作电压低、亮度高、功耗小、寿命长性能稳定、耐冲击、抗震性强等,这些优点使其受到人们的青睐。
目前LED显示屏的应用已十分广泛了,主要包括:(1)机场航班动态信息显示。(2)证券交易、金融信息显示。(3)港口、车站旅客引导信息提示。(4)道路交通信息显示。(5)调度指挥中心信息显示。(6)广告媒体新产品等。
LED显示屏的种类可分为很多种,有室内显示屏、室外显示屏、单色显示显示屏、全彩色显示屏等。
1.2 选题意义
该设计使我们掌握了LED点阵显示屏的显示原理以及其硬件电路的设计方法,同时也了解了LED行业的发展现状和趋势。通过本次设计我们巩固了以前学过的知识特别是硬件电路设计流程这一块,而且对以前所学的Keil、Protel99、Proteus等设计软件也有了进一步的认识和掌握,也使自己的C语言编程的能力得到了提高。其次,本次设计也为今后从事相关方面的工作奠定了一定基础。因此,此课题无论是对自己的就业与今后工作态度的培养都有非常现实与积极的意
义。

1.3 论文主要内容
论文的结构和主要内容安排如下:
(1)设计的总体思路
根据设计的特点,对系统的整体设计思路进行简单介绍,包括硬件部分和软件部分。
(2)方案的论证与对比
通过收集资料,并参照目前通用的设计方法和思路拟定几套设计方案,最后决定选用单片机为核心控制器件,用C语言来编程,以PC机为上位机,外加译码电路和驱动电路的设计及方案。
(3)系统硬件设计
经过论证对比,我们选择STC89C52单片机为控制芯片,74HC595作为译码器件,三极管8550为驱动器件。在论文中详细的列出了这些器件的连接使用方法和重要器件的参数。
(4)重要器件介绍
选择了系统中比较重要的器件对其参数等信息做了详细的介绍。
(5)系统软件设计
针对软件的整体工作流程和各个模块功能做了简单介绍,对系统的扫描驱动程序做了比较详细的说明。
(6)系统的制作过程与调试
分别说明了制作硬件电路的详细过程,包括绘制原理图、绘制PCB、制作电路板和元器件的焊接等几个环节。最后还多调试过程中出现的问题进行了说明。
(7)结论
设计完成后对自己在整个设计过程中所遇到问题,经验教训进行总结。

第二章 方法论证对比

2.1 单片机编程语言
目前我们针对单片机编程这一块我们只学习了汇编语言和C语言两种,由于我们平时主要用C语言进行编程而汇编语言较少使用,所以最终选择用C语言进行编程。
2.2 控制系统设计
方法一:用4块串并转换芯片74LS164将串行数据转换为并行数据,再接锁存器74LS373将数据锁存。
方法二:用4块具有锁存功能的串并转换芯片74LS595直接将串行数据转换为并行数据并锁存。
通过比较方法一所用器件较多,电路较复杂,而方法二所用器件少是电路得到了简化,最终我们选定方法二。
2.3 显示方式
图文显示的方式一般有两种:
方式一:静态显示方式
此种方式的设计思路简单,但连线非常之多,单片机的管脚占用量非常大。直接经点阵显示屏连接到单片机上的话显然是不可能,只有通过锁存器扩展的方式来扩展端口,通过计算16x16的点阵需要256/8=32个锁存器。这就是硬件电路变得非常复杂,而且不利于屏幕的扩展。
方式二:动态扫描方式
动态扫描采用的是逐行轮流点亮的方式对点阵显示屏的端口进行扫描,这样就可实现多行的同名端口共同使用同一套驱动器,从而减少单片机端口的占用率,为单片机扩展其他功能留下了空间。数据传输时可采用串行传输方式,此时只用一根信号线便可将显示数据传送至列驱动器,这无疑使十分方便和经济的。
经过比较,为了简化电路提高效率我们最终选择方式二,采用动态扫描方式。

第三章 系统硬件设计

3.1 总体硬件设计
总体硬件设计框图如下:

在这里插入图片描述

图3.1
本次设计的硬件系统主要有单片机系统、上位机、电源电路、复位电路、按键控制电路和点阵驱动电路。其工作流程如下:单片机与上位机进行串行通信,将显示内容存入单片机内。单片机接收到显示内容之后,由按键控制电路产生中断信号控制显示方式,最后单片机将显示代码送入驱动电路将点阵显示屏点亮实现所需要显示的内容。对该系统所实现的功能有以下几点要求:
(1)LED点阵显示屏最小尺寸为16*16,必须满足至少显示一个汉字的要求;
(2)可实现中英文字符,静态和动态等两种以上特效显示;
(3)编写上位机软件,实现显示内容更新(该功能选作);
(4)具有级联扩展功能;
(5)驱动电路所提供电流、电压要达到点阵屏显示要求,使显示屏亮度适中。

3.2 系统各硬件电路介绍
3.2.1 电源电路设计介绍
在这里插入图片描述

图 3.2.1 电源电路
如图 ,为电源电路原理图,我们采用了5V电源直接给系统供电,用六脚开关控制电源的通断。因为5V电源是直接从电脑上取得的比较稳定,为了简化电路就不需要电容滤波了。
3.2.2 复位电路
在这里插入图片描述

图3.2.2 复位电路
单片机复位只需要在第9脚接持续通高电平2us就可实现,在电路图中,电容的的大小是10uF,电阻的大小是10k。所以根据公式,可以算出电容充电到电源电压的0.7倍(单片机的电源是5V,所以充电到0.7倍即为3.5V),需要的时间是10K*10uF=0.1S。单片机在启动0.1S内会自动复位。当需要手动复位时,按下按钮电容被短路开始放电,经过0.1S R2两端电压变为3.5V,单片机复位。
3.2.3 晶振电路
在这里插入图片描述

图 3.2.3 晶振电路
本次设计所使用的晶振为12MHz,电容大小为33pF。电容的作用是使电路产生谐振,让振荡频率更加稳定。
3.2.4 按键电路
在这里插入图片描述

图3.2.4 按键控制电路
本次设计采用四脚轻触开关,分别接单片机P2.0与P2.1口。按键S6用于选择点阵显示屏的显示方式,S7用于切换显示内容。P2.0与P2.1没有接上拉电阻是因为单片机P2口内部已经接有上拉电阻外部就无需再接。

3.2.5 点阵驱动模块设计实现
在这里插入图片描述
图3.2.5 点阵驱动电路

本设计所使用的行、列译码器均为74HC595,74HC595为带有锁存功能的串并转化器。采用译码器解决了单片机端口不足的问题,为单片机扩展提供了跟多空间。

设计用到了4片74HC595芯片,该芯片的作用是讲串行数据转化为并行数据进行锁存,最后将数据输出到点阵显示,如图3.2.5为该芯片的硬件连接方式。把4片芯片的第11脚相连,然后引出一根线SCK,该线为以为时钟输入引脚;将4片芯片的12脚相连,再引出一根线RCK,该线为存储器时钟输入引脚;将第一片芯片的SER(数据输入)端接单片机端口,Q7端接下一块芯片的SER端,如此级联到第四块芯片。第四块芯片的Q7端作为级联扩展端使用接到图中J3,J3为级联扩展端接口。该显示屏可以通过级联扩展的方式增加显示屏的尺寸从而增加显示内容。J2为点阵显示屏与单片机模块的连接端口。
该模块的具体工作流程为:当给SER端输入串行数据信号后,再给SCK时钟输入引脚输入高电平经延时适当时间后,串行显示数据被移入以为寄存器中。再给RCK端输入高电平后,数据将会被锁存。最后将使能端E接地,就能是显示数据并行输出。

3.2.6 通信系统硬件设计
在这里插入图片描述

图3.2.6
由于单片机与PC机的信号电平不同,PC机在传送数字“1”时C采用的是EIA电平,即传输线上的电平为-3V~-15V;传送数字‘0’时,传输线上的电平为+3V~+15V。而单片机采用的是正逻辑TTL电平,传输数字“1”和“0”时,传输线上的电平分别为+5V和-5V。所以如果要使单片机与PC机进行通信就必须将电平信号进行转换。通过查阅相关资料得知,可用MAX232芯片讲电平信号进行转换,这样就可实现单片机与PC机之间的通信。如图3.2.6所示为串行通信模块原理图,当进行通信使LED灯可闪烁提示。

第四章 重要器件介绍

4.1 LED点整显示屏介绍
在这里插入图片描述

图4.1.1 8×8点阵显示屏实物图
在这里插入图片描述

图4.1.2 单色LED矩阵的内部结构

LED点阵显示屏有两个类型,一种为共阴极另一种为共阳极。LED的显示方式是按照显示码的传送顺序逐行或逐列显示。如图为共阴极点阵显示屏的管脚排列图,轮流给行输出低电平进行扫面,给列输出显示代码进行即可显示所要显示的信息。由于每一行的显示时间在4ms左右,闪烁频率大于人眼的临界闪烁频率,所以人眼看到屏幕显示的内容是稳定的。本次点阵屏正常工作电流为20mA,
本次设计所使用的是共阴极8×8点阵显示屏,由四块组成一块16×16的显示屏,从而增加显示内容。由于该显示屏的实物不好区分是共阴极的还是共阳极的,这就需要用万用表进行测量。具体方法和步骤如下:
1、把万用表转到二极管档位;
2、用表笔去接触点阵显示屏的任意一只管脚,用红表笔和黑表笔皆可;
3、用另一只表笔去测试剩下的所有引脚,看有无二极管被点亮,若只有一种颜色的二极管被点亮就将两只表笔进行兑换;
4、将兑换过的表笔重复上一步,观察是否有两种颜色的二级管被点亮,若有,则看接固定端的是哪只表笔,若为红表笔,就是共阳极;若为黑表笔,就是共阴接发。
4.2、限流电阻
在这里插入图片描述

图4.2 限流电阻
限流电阻的作用是将电流限制在某一范围,为LED提供稳定的工作电流。经过查阅相关资料得知三极管基极正常工作电流为10mA,三极管工作在放大状态基极电压为0.7V,单片机端口电压为3.3V。由公式R=U/A,可得限流电阻为R=(3.3-0.7)/20=130Ω,因此电阻选择150Ω或330Ω皆可。

第五章 系统软件设计

6.1 软件整体介绍
总体软件设计框图如下:

在这里插入图片描述

图6.1 软件设计整体框图

 如图6.1所示,软件部分用模块化的方式来编写,主要包含5个模块:系统主程序、按键检测程序模块、功能函数模块、初始化程序模块和中断程序模块。主程序通过适时调用初始化程序和按键检测程序来实现系统的各相功能,二定时器T0则负责调用其他程序。中断显示程序的主要功能是根据按键产生的中断信号将相应的显代码送入点阵显示屏,并产生各种相应的控制信号,是屏幕按照设定的参数显示。初始化程序的作用的对系统及相关变量进行初始化。

6.2 显示驱动程序
显示驱动程序对定时器T0进行重新赋值之前首先要进入中断,从而使点阵显示屏刷新频率的稳定性得以保证。1/16×T0 溢=1/16×f/12(65536-t),其中f为晶振频率,t等于定时器T0的初值,定时器的工作模式是16位的形式。接下来显示驱动程序开始查询当前点亮行号,通过调用相关功能函数来读取下一行显示数据,并将其打入移位寄存器。由于在换行的时候会有余辉现象产生,在此可参照示波器的显示原理在换行时将信号消隐即关闭显示。等显示数据被锁存时再打开显示即可解决拖尾现象。程序的流程框图如下:
在这里插入图片描述

图6.1 显示驱动程序流程图
6.3 系统主程序
本次设计的程序要求能实现文字和图形的显示,并且还有特殊显示效果例如,静态显示、左移显示、又移显示、上移显示和下移显示等。而且图象显示要清晰不能有乱码。
主程序一开始先是对定时器、中断端口、寄存器等进行初始化;然后调用按键检测程序,当有按键按下时,系统判断当前按下选择了什么播放模式,以及当前需要显示哪一段内容。显示模式和显示内容通过按键选定后,当下一次中断来临时,系统将会在中断中对当前的设置作出回应,进而实现了按键改变显示模式和显示内容的目的。
单元显示板上一级与下一级采用的是并联的形式,即下一级接收到的信息与上一级相同。而且还可以将信息传至下一级,这样就可以将显示屏进行扩展从而显示更多的内容。本程序可随时更改显示内容,只要将原来的显示代码更改即可。

第六章 系统的创作过程与调试

6.1 电路制作过程
系统的电路原理图设计与PCB图绘制好之后,下一步就要是制作电路板。本次设计我们采用的PCB设计软件是Protel99。制作流程图如下:

在这里插入图片描述

图6.1硬件电路开发流程

6.1.1 设计原理图
首先在Protel99中新建工程,然后再建立Schemetic文件。建好后再添加SCH库,并从库中选取相应与器件放在图纸中。等元器件放完后在对其进行布局最后进行连线。连完线再编辑各个与器件的信息例如电阻值、电容值、PCB封装等,最后还得给元器件编流水号,可选择手动编辑和自动编辑两种方式,接下来在Design一栏中选择生成网络表,这样原理图就基本绘制完毕了。

6.1.2设计PCB图
在原来的工程文件夹先建立一个PCB文件夹,建好后再选择丝印层绘一个矩形框,该框的大小即是电路板的实际尺寸。画好框后再打开原理图选择Updata PCB,对PCB进行更新。
更新好后再打开PCB图并选择装入网络表,此时与器件将被导入PCB。之后再改正导入网络表时出现的错误,很多情况下是因为PCB库里面没有相应的封装引起的,需要自己画,画好后再讲库导入软件中使用即可。
改正完所有的错误便可对元器件进行布局,布局时要尽量将发热量大的元器件放置在板子边缘;接下来要调整元器件位置尽量减少交叉线;布局时要使整体结构尽量紧凑减少PCB板的面积。元器件布局完成后便是最重要的一步布线,布线时线宽要设置在0.5mm到1mm之间,特别是电源线和地线,线宽要设置在1mm以上,其余的信号线设置为0.5mm左右,这样既可防止腐蚀的时候断线,又便于检查。
在设置元器件过孔的时候要时刻注意焊盘的大小,焊盘太小可能在打孔的时候就将其打没了最后造成无法焊接。在连接走线的时候不能走直角和锐角,尽量化成钝角。由于本次设计所使用的元器件较少再加上实验室条件有限我们将板子做成单面板,但画PCB时是按照双面板来画的,焊接时的在底层打飞线,这次总共打了54跟飞线。
6.1.3 硬件仿真
原理图和PCB绘制好之后,为了验证理论的可行性,我们用Prpteus7.0对硬件部分进行了仿真。仿真结果最后成功了,仿真结果如图6.1.3所示。这也就证明我们的设计思路没有错,接下来就可以进行下一步工作了。
在这里插入图片描述

图6.1.3 硬件仿真图

6.1.4转印PCB
此次转印PCB我们采用的是热转印法,首先用喷墨打印机将PCB图打印在热转印纸上,再将覆铜板用酒精洗净,用电熨斗将图形印在覆铜板上。
6.1.5腐蚀和打孔
完成热转印后将覆铜板放入腐蚀液进行腐蚀,腐蚀完后再对板子进行打孔。打孔时要特别注意不要将孔打歪了或焊盘打没了,最后得再次检查是否有遗漏的空。

6.1.6焊接元器件
在焊接元器件时要先焊接矮的元件再焊接高的元件,对于有极性的元件要特别注意不要焊反了,例如点阵显示屏、极性电容、三极管等,一旦焊错很可能是整块板子都废掉。对于某些与器件在焊接之前要先放静电,否则静电会使其损坏。在焊接时上锡要适量,过多过少都容易造成虚焊。
6.2 硬件调试过程
硬件调试时首先检查是否有虚焊、漏焊和短路的地方。再对照PCB图看看飞线有没有打错,元器件是否焊接反了,确认无误后再烧录测试程序进行检测。
在硬件调试过程中所出现的问题如下:
1、在电路焊接完成后,发现显示屏根本不会亮,按键也无反应。经过仔细的检查后发现问题出在单片机控制电路电源线还差一更没有连上,后来将其连接好后问题得以解决,显示屏亮了,按键也有反应。
2、在单片机内烧录进测试程序后发现屏幕出现乱码,图象毫无规律,而且很多地方出现明显的供电不足现象。经过反复检查之后发现电路焊接并没有问题,也无明显短路和虚焊,最后我发现原来是点整显示屏焊反了。
由于点阵屏引脚非常多若将其取下的话不是件容易的事,也容易将电路板损坏,但从新做一块板子的话时间又明显不够。最后用同学说的方法还是将点阵屏去了下来,板子也没被损坏。将点整屏从新装过后显示图形变得正常了但还是有一些串码和局部供电不足的现象,经检查发现是取显示屏的时候走线被弄断了。
将断了的走线打上飞线后显示内容正常了,也没有了串码的和供电不足的现象。
这样硬件调试就算成功了。

6.3 软件调试过程
软件调试的过程花了较长时间,首先将测试程序烧录进硬件进行测试,确定硬件没有错误后再将自己写的程序烧录进行测试。在测试的过程中出现了许多问题如显示各种乱码,经过对程序仔细认真的分析过后,最后问题得以解决。最终实现了所要求的各项功能。

总结

在经过我两个多月不断的努力下设计终于终于完成了,本次设计能够实现中英文字符以及图形的显示,并且LED显示屏显示时无串码,亮度均匀、清晰。显示内容可实现以向上、向下、向左、向右滚动显示以及静态显示。系统还可通过扩展的方式来增加所要显示的内容。
本系统具有硬件电路简单,成本低,系统稳定性好等优点。但也存在着很多缺点,例如不能与PC机进行串行通信,实现显示内容的更新。每次跟心内容都得取下单片机到开发板上去烧录程序,这样容易损坏单片机。由于时间有限和老师要求串行通信模块选做,所以这一块就没有做出实物。今后有时间再将通信模块加上相信本设计将会更完美。还有就是电路布局还不合理,扩展接口应该放在电路板上方,这样级联扩展的时候就可以减少屏幕间的缝隙使显示效果得到优化。
在此次设计中我得到了以下收获:

1、在这次设计中我对本专业所学的知识有了实践性的应用和巩固,尤其是keil、protel99、proteus等软件的使用;
2、我对硬件的制作的整个过程从绘制原理图到焊接元器件再到系统调试有了比较全面的了解。对今后从事相关方面的工作有很大的帮助;
3、这次设计过程锻炼了我发现问题、解决问题的能力,也发现了自身的很多不足之处,在今后一定要弥补。

参考文献

[1]张秀关.单片机与计算机串口通信实践[M].北京:电子工业出版社,2013.1.
[2]李江全,魏中岩,姚帅,严海娟.单片机通信与控制应用编程实例[M].北京:中国电力出版社,2011.11.
[3]沈洁.LED封装技术与应用[M].北京:化学工业出版2012.8.
[4]周志敏,纪爱华.漫步LED世界[M].北京:国防工业出版社,2013.8.
[5]杨清德,杨兰云.LED及其应用技术问答[M].北京:电子工业出版社,2011.1.
[6]Vizimuller.P. RF design guide-systems,circuits,and equations. 1995.
[7]R.Dye. Visual Object-Orientated Programming,Dr.Dobbs Macintosh Journal. Sept.1st (1991).
[8]缪思恩. LED大屏幕显示电路设计[J].电子技术应用,1996,(08):56-77.
[9]王亭,李瑞涛,宋召清.在Windows下PC机和单片机的串行通信[J].微型机与应用,2000(1).
[10]关积珍,陆家和. 我国LED显示屏技术和产业发展及展望.现代显示,2004, (02):34-37.
[11]缪思恩. LED大屏幕显示电路设计[J].电子技术应用,1996,(08):56-77.
[12]Dave Jackson. 关于DSP芯片的问与答.今日电子 ,1998,(12):1-7.
[13]卢弥坚.主从分布式LED大屏幕显示系统[J].电脑与信息技术,1997,(04):6-13.
[14]高胜东,梁采,张宏富.一种LED大屏幕显示系统[J].成都气象学院学报,1998,(03):108-120.
[15]张全福.汇编语言程序设计实验教学改革与探索.教学研究,2005,(06):3-5. [9] 文哲雄.用单片机控制LED显示屏[D].佛山:佛山科学技术学院,1995.
[16]彭宁,只佩华.单片机对LED大屏幕显示的控制系统[J].河北大学学报(自然科学版),1993,13(3):86-89

致谢

能顺利完成此次毕业设计,我首先要感谢老师对我的细心指导。每次没有思路的时候我们一堆人就去找老师,老师非常耐心的给我每个人指明了方向并且认真的解决了我们所遇到的问题。在撰写论文的过程中,老师细心严谨的指出我们论文中存在的问题。老师严谨认真的工作态度给我们留下了深刻的印象,在今后的工作中我们也应该以这样的态度去解决问题。我还要感谢学校给我们提供了这样好的平台让我们顺利的完成了毕业设计。毕业设计做完也就意味着我们的大学生活就此结束,回想起这四年里所经历的苦于甜都将成为我人生中最美好的回忆,在此,我要感谢陪我度过大学生活的所有老师和同学!

附录一
PCB
在这里插入图片描述

显示部分PCB
在这里插入图片描述

控制部分PCB

原理图
在这里插入图片描述

显示部分原理图
在这里插入图片描述

控制部分原理图

附录二
实物图

在这里插入图片描述

附录三
完整程序
#include <reg52.h>
#include <intrins.h>
#define NOP() nop() /* 定义空指令 */
#define uchar unsigned char
#define uint unsigned int
//#define clrbit(X,Y) X=~(1<<Y) //将X的第Y位清零
#define Kkey P2 //定义按键
#define STATIC 1
#define LEFT 2
#define RIGHT 3
#define UP 4
#define DOWN 5
#define LEFT_RIGHT 6
//SPI IO
sbit S_CLK =P1^0;
sbit R_CLK =P1^1;
//sbit OUT_OE=P1^4;
sbit MOSIO =P1^2;

sbit key0 =P2^0;
sbit key1 =P2^1;

extern uchar *disptab;
extern unsigned int tab_length;
extern tabchoose(char num);

uint Saom_num;
uchar word_num;
uchar byte_num;
uchar Speed_move;
uchar Speed_static;
uchar HZ_Num;
uchar key_val;
uchar show_Val[2];
uint move_timce;
uint Flg;
uchar left_rightFlg;
uchar Movespeed=2; //移动快慢

void delay(unsigned int i); //函数声名
//void delay1(void);
void HC595SendData(uchar SendVal);
void InitTIMER0(void);
void SaomiaoFun();
void HC595Output();
void StaticFun(unsigned char *byte_P,unsigned char ByteNum);
void Move_left(unsigned char *byte_P,unsigned char ByteNum);
void Move_right(unsigned char *byte_P,unsigned char ByteNum);
void Move_up(unsigned char *byte_P,unsigned char ByteNum);
void Move_down(unsigned char *byte_P,unsigned char ByteNum);
void left_and_right(unsigned char *byte_P,unsigned char ByteNum);
void Init_sys();

void main()
{

uchar Mode_val;
uchar Mode_val0;

Mode_val=0;
Mode_val0=0;
key_val=0;

Init_sys();
while(1)
{
	if(!((Kkey&0x03)==0x03))
	{

// delay(5);
if(!key0)
{
while(!key0);
Mode_val++;
if(Mode_val>=6) //限定模式种类
{
Mode_val=1;
}
switch(Mode_val)
{
case STATIC: {Init_sys();Mode_val0=0;tabchoose(0);HZ_Num=1;key_val=1;break;}
case LEFT : {Init_sys();Mode_val0=0;tabchoose(0);key_val=2;Movespeed=2;break;}
case RIGHT : {Init_sys();Mode_val0=0;tabchoose(0);key_val=3;Movespeed=2;break;}
case UP : {Init_sys();Mode_val0=0;tabchoose(0);key_val=4;Movespeed=2;break;}
case DOWN : {Init_sys();Mode_val0=0;tabchoose(0);key_val=5;Movespeed=2;break;}
case LEFT_RIGHT: {Init_sys();Mode_val0=0;tabchoose(0);key_val=6;Movespeed=1;break;}
default : {Mode_val=0;break;}
}

		}
		if(key_val==1)
		{
			if(!key1)
			{					
				while(!key1);
				Init_sys();
				HZ_Num++;
			}
			if(HZ_Num>(tab_length-2))
			{
				HZ_Num=1;	
			}				
		
		}
		else
		{
		if(!key1)
		{					
			while(!key1);
			Mode_val0++;
			switch(Mode_val0)
			{
				case 1 : {tabchoose(1);Init_sys();break;}

// case 2 : {tabchoose(3);Init_sys();break;}
case 2 : {tabchoose(0);Init_sys();break;}
default: {Mode_val0=0;break;}
}

		}
		}
	}

}

}
//
/
系统初始化函数 /
/
/
void Init_sys()
{
Saom_num=0;
word_num=0;
byte_num=0;
Speed_move=0;
Speed_static=0;
move_timce=0;
left_rightFlg=0;
Flg=0;
// OUT_OE=0; //开HC595输出

InitTIMER0();       //初始化定时器0

}

//
/
静态函数 /
/
/
void StaticFun(unsigned char *byte_P,unsigned char ByteNum)
{
uchar TabB_num;
uint m; //m越大,可显示字节越多

TabB_num=ByteNum;
m=(uint)(byte_num+32*HZ_Num);
show_Val[1]=*(byte_P+m+1);
show_Val[0]=*(byte_P+m);

}
//
/
左移函数 /
/
/
void Move_left(unsigned char byte_P,unsigned char ByteNum)
{
uint m;
if(move_timce==16
(ByteNum-1))//4个字
{
move_timce=0;
}
m=byte_num+move_timce2;
show_Val[1]=
(byte_P+m+1);
show_Val[0]=(byte_P+m);
}
/
/
/
右移函数(写程序时有必要用笔在纸上进行计算) /
/
/
void Move_right(unsigned char byte_P,unsigned char ByteNum)
{
uint m;
if(move_timce==16
(ByteNum-1))//ByteNum个字
{
move_timce=0;
}
///下面是刷屏分配数据的算法
if(Saom_num<(move_timce%16)) //扫描列比移动次数小的情况
{
m=byte_num-2
(move_timce%16)+32*(move_timce/16)+32+32; //括号不能拆开,否则出错!这不是除法!
}
else if(Saom_num>=(move_timce%16)) //扫描列比移动次数大的情况
{
m=byte_num-2*(move_timce%16)+32*(move_timce/16); //括号不能拆开,否则出错!这不是除法!
}
show_Val[1]=(byte_P+m+1);
show_Val[0]=
(byte_P+m);
}
//
/
左右交替移动 /
/
/
void left_and_right(unsigned char byte_P,unsigned char ByteNum)
{
uint m;
/
***********************************************
*中间停留需要的变量传递
************************************************/
if(left_rightFlg96)
{
left_rightFlg=0;
}
if((left_rightFlg
0)||(left_rightFlg48))
{
move_timce=Flg;
}
else if((left_rightFlg
16)||(left_rightFlg==64))
{
Flg=move_timce;
}
/

if(move_timce==16*(ByteNum-1))
{
	move_timce=0;
}

if(left_rightFlg<16)
{
	m=byte_num+move_timce*2;
	show_Val[1]=*(byte_P+m+1);
	show_Val[0]=*(byte_P+m);
}
else if((left_rightFlg>=16)&&(left_rightFlg<48))
{
	m=byte_num+Flg*2;
	show_Val[1]=*(byte_P+m+1);
	show_Val[0]=*(byte_P+m);
}
else if((left_rightFlg>=48)&&(left_rightFlg<64))
{
	///下面是刷屏分配数据的算法
	if(Saom_num<(move_timce%16))		 //扫描列比移动次数小的情况
	{
		m=byte_num-2*(move_timce%16)+32*(move_timce/16)+32+32;	//括号不能拆开,否则出错!这不是除法!
	}
	else if(Saom_num>=(move_timce%16))	 //扫描列比移动次数大的情况
	{
		m=byte_num-2*(move_timce%16)+32*(move_timce/16);        //括号不能拆开,否则出错!这不是除法!	
	}
	show_Val[1]=*(byte_P+m+1);
	show_Val[0]=*(byte_P+m);
			
}
else if((left_rightFlg>=64)&&(left_rightFlg<96))
{
	///下面是刷屏分配数据的算法
	if(Saom_num<(Flg%16))		 //扫描列比移动次数小的情况
	{
		m=byte_num-2*(Flg%16)+32*(Flg/16)+32+32;	//括号不能拆开,否则出错!这不是除法!
	}
	else if(Saom_num>=(Flg%16))	 //扫描列比移动次数大的情况
	{
		m=byte_num-2*(Flg%16)+32*(Flg/16);        //括号不能拆开,否则出错!这不是除法!	
	}
	show_Val[1]=*(byte_P+m+1);
	show_Val[0]=*(byte_P+m);	
}

}

//
/
上移函数 /
/
/
void Move_up(unsigned char byte_P,unsigned char ByteNum)
{
uint d;
uchar a,b;
uchar buff_Val[4];
uint m;
if(move_timce==16
(ByteNum-1))//ByteNum个字
{
move_timce=0;
}

m=byte_num+32*(move_timce/16);

buff_Val[0]=*(byte_P+m);
buff_Val[1]=*(byte_P+m+1);
buff_Val[2]=*(byte_P+m+32);
buff_Val[3]=*(byte_P+m+32+1);
if((move_timce%16)<9)				//移动次数8次以内
{

	d=(buff_Val[1]&0xff)<<8;		//上半屏
	d=d+buff_Val[0];
	d=d>>(move_timce%16);
	a=d&0xff;

	d=(buff_Val[2]&0xff)<<8;		//下半屏
	d=d+buff_Val[1];
	d=d>>(move_timce%16);
	b=d&0xff;

	show_Val[1]=b;				   //列的低字节
	show_Val[0]=a;				   //列的高字节
}
else							   //移动次数8次到16次
{
	d=(buff_Val[2]&0xff)<<8;	   //上半屏
	d=d+buff_Val[1];
	d=d>>(move_timce%16-8);//关键步骤
	a=d&0xff;

	d=(buff_Val[3]&0xff)<<8;	   //下半屏
	d=d+buff_Val[2];
	d=d>>(move_timce%16-8);//关键步骤
	b=d&0xff;

	show_Val[1]=b;				   //列的低字节
	show_Val[0]=a;				   //列的高字节

}

}
//
/
下移函数 /
/
/
void Move_down(unsigned char byte_P,unsigned char ByteNum)
{
uint d;
uchar a,b;
uchar buff_Val[4];
uint m;
if(move_timce==16
(ByteNum-1))//ByteNum个字
{
move_timce=0;
}

m=byte_num+32*(move_timce/16);

buff_Val[0]=*(byte_P+m);			//只要32*(move_timce/16)不变,buff_Val[]的值只与扫描列byte_num有关
buff_Val[1]=*(byte_P+m+1);
buff_Val[2]=*(byte_P+m+32);
buff_Val[3]=*(byte_P+m+32+1);

if((move_timce%16)<9)				//移动次数8次以内
{

	d=(buff_Val[0]&0xff)<<8;		//上半屏
	d=d+buff_Val[3];
	d=d<<(move_timce%16);
	a=(d>>8)&0xff;

	d=(buff_Val[1]&0xff)<<8;		//下半屏
	d=d+buff_Val[0];
	d=d<<(move_timce%16);
	b=(d>>8)&0xff;

	show_Val[1]=b;				   //列的低字节
	show_Val[0]=a;				   //列的高字节
}
else							   //移动次数8次到16次
{

	d=(buff_Val[3]&0xff)<<8;	   //上半屏
	d=d+buff_Val[2];
	d=d<<(move_timce%16-8);//关键步骤
	a=(d>>8)&0xff;

	d=(buff_Val[0]&0xff)<<8;	   //下半屏
	d=d+buff_Val[3];
	d=d<<(move_timce%16-8);//关键步骤
	b=(d>>8)&0xff;

	show_Val[1]=b;				   //列的低字节
	show_Val[0]=a;				   //列的高字节

}

}
//
/
扫一列或一行 /
/
/
void SaomiaoFun()
{
uint Lie_xuan;
uchar LieX0,LieX1;

	switch(key_val)
	{
		case 0: {	show_Val[1]=0xff;
					show_Val[0]=0xff;
					break;
				}
		case 1: {StaticFun(disptab,tab_length);break;}
		case 2: {Move_left(disptab,tab_length);break;}
		case 3: {Move_right(disptab,tab_length);break;}
		case 4: {Move_up(disptab,tab_length);break;}
		case 5: {Move_down(disptab,tab_length);break;}
		case 6: {left_and_right(disptab,tab_length);break;}
		default: {;;break;}  
	 
	}

			Lie_xuan=1<<(15-Saom_num);		       //列选扫描
			LieX0=Lie_xuan&0xff;
			LieX1=(Lie_xuan>>8)&0xff;

			HC595SendData(show_Val[1]);
			HC595SendData(show_Val[0]);	           //调用595驱动程序 把LED的数据送到595
			HC595SendData(LieX1);	               //调用595驱动程序 把LED的列选通信息送到595
			HC595SendData(LieX0);

			HC595Output();

}

//
/
定时器0初始化 /
/
/
void InitTIMER0(void)
{
TMOD&=0x0f; //选择模式1
TMOD|=0x01;
TH0=(-1000)/256; //赋予1ms的初值(赋予10ms的初值,也可以写成 TH0=(-10000)/256;TL0=(-10000)%256;)
TL0=(-1000)%256;
IE=0x82;
TR0=1;
}
/*****************************************************************

  • 延时子程序 *
  •   												 		 *
    

******************************************************************/
void delay(unsigned int i)
{
for(i; i > 0; i–);
}

//
/
定时器中断函数 /
/
/
void tim(void) interrupt 1 //中断,用于行列扫描
{

TH0=(-1000)/256;			//1.0ms(赋予10ms的初值,也可以写成 TH0=(-10000)/256;TL0=(-10000)%256;)
TL0=(-1000)%256;			//(1.5ms以下保证扫屏不闪烁)

SaomiaoFun();

if(key_val)
{
	Saom_num++;         //使用标志位判断扫描第几行/列
	byte_num+=2;
}

if(Saom_num==16)	  //扫完一屏
{
	Saom_num=0;
	byte_num=0;
	Speed_move++;
	if(Speed_move==Movespeed)  //1.0x16xMovespeed毫秒移动一次(移动)
	{
		Speed_move=0;
		move_timce++;
		left_rightFlg++;
	}

	Speed_static++;
	if(Speed_static==40)  //1.0x16xSpeed_static毫秒跳下一个(静止显示)
	{
		Speed_static=0;
		HZ_Num++;
		if(HZ_Num>(tab_length-2))
		{
			HZ_Num=1;	
		}
	}
}

}

/*********************************************************************************************************
** 函数名称: HC595SendData
** 功能描述: 向SPI总线发送数据(发送一字节)(单向传输两线模拟SPI)(三线控制595)S_CLK R_CLK MOSIO
*********************************************************************************************************/
void HC595SendData(unsigned char SendVal)
{
unsigned char i;

for(i=0;i<8;i++) 
{ 
	if((SendVal<<i)&0x80) MOSIO=1;	 //高位在前低位在后
	else MOSIO=0;

	S_CLK=0;
	NOP();	//产生方形波
	NOP();  //调试的时候可以只要一个空指令
	S_CLK=1;
}

}
void HC595Output()
{
R_CLK=0;
NOP(); //产生方形波
NOP(); //调试的时候可以只要一个空指令
R_CLK=1;//上升沿输出锁存数据
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1510961.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝牙系列七:开源蓝牙协议栈BTStack数据处理(Wireshark抓包分析)

继续蓝牙系列的研究。 在上篇博客&#xff0c;通过阅读BTStack的源码&#xff0c;大体了解了其框架&#xff0c;对于任何一个BTStack的应用程序都有一个main函数&#xff0c;这个main函数是统一的。这个main函数做了某些初始化之后&#xff0c;最终会调用到应用程序提供的btst…

prometheus 原理(架构,promql表达式,描点原理)

大家好&#xff0c;我是蓝胖子&#xff0c;提到监控指标&#xff0c;不得不说prometheus&#xff0c;今天这篇文章我会对prometheus 的架构设计&#xff0c;promql表达式原理和监控图表的绘图原理进行详细的解释。来让大家对prometheus的理解更加深刻。 架构设计 先来看看&am…

Docker容器化技术(使用Dockerfile制作镜像)

Docker中的镜像分层 Docker 支持通过扩展现有镜像&#xff0c;创建新的镜像。实际上&#xff0c;Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。 1、Docker 镜像为什么分层 镜像分层最大的一个好处就是共享资源。 比如说有多个镜像都从相…

python 通过代理服务器 连接 huggingface下载模型,并运行 pipeline

想在Python 代码中运行时下载模型&#xff0c;启动代理服务器客户端后 1. 检查能否科学上网 $ curl -x socks5h://127.0.0.1:1080 https://www.example.com <!doctype html> <html> <head><title>Example Domain</title><meta charset"…

Python: 如何绘制核密度散点图和箱线图?

01 数据样式 这是数据样式&#xff1a; 要求&#xff08;我就懒得再复述一遍了&#xff0c;直接贴图&#xff09;&#xff1a; Note&#xff1a;数据中存在无效值NA&#xff08;包括后续的DEM&#xff09;&#xff0c;需要注意 02 提取DEM 这里我就使用gdal去提取一下DEM列…

./ 相对路径与node程序的启动目录有关

node:internal/fs/sync:78 return binding.openSync( ^ Error: ENOENT: no such file or directory, open D:\前端的学习之路\项目\codeHub\keys\private_key.pem at Object.open (node:internal/fs/sync:78:18) at Object.openSync (node:fs:565:…

Java后台面试相关知识点解析

文章目录 JavaJava中四种引用类型及使用场景集合HashMap源码及扩容策略HashMap死循环问题ConcurrentHashMap与HashtableConCurrentHashMap 1.8 相比 1.7判断单链表是否有环&#xff0c;并且找出环的入口 IO线程池线程池的几种创建方式判断线程是否可以回收线程池的7大核心参数线…

菜鸟学会Linux的方法

系统安装是初学者的门槛&#xff0c;系统安装完毕后&#xff0c; 很多初学者不知道该如何学习&#xff0c;不知道如何快速进阶&#xff0c; 下面作者总结了菜鸟学好Linux技能的大绝招&#xff1a; 初学者完成Linux系统分区及安装之后&#xff0c;需熟练掌握Linux系统管理必备命…

蓝桥省赛倒计时 35 天-bfs 和 dfs

#include <iostream> using namespace std; int t; int m,n; char mp[55][55];//不能写成 int 数组 bool vis[55][55]; int dx[ ]{1,0,-1,0},dy[ ]{0,1,0,-1}; int res;void dfs_1(int x,int y){vis[x][y] true;//陆地向四个方向拓展for(int i0;i<4;i){int nx xdx[i…

蓝桥杯练习系统(算法训练)ALGO-973 唯一的傻子

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 腿铮找2255有点事&#xff0c;但2255太丑了&#xff0c;所以腿铮不知道他的长相。正愁不知道到如何找他的时候&#xff0c;…

基于React低代码平台开发:直击最新高效应用构建

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java对AI的调用开发》 《RabbitMQ》《Spring》《SpringMVC》《项目实战》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录…

2024鸿蒙迎来大爆发,有必要转行鸿蒙开发吗?

鸿蒙开发&#xff0c;这个名字最近在科技圈引起了不小的轰动。 那么&#xff0c;鸿蒙开发到底是什么呢&#xff1f;它又能给我们带来怎样的影响呢&#xff1f; 鸿蒙开发&#xff0c;简单来说&#xff0c;就是基于鸿蒙操作系统的一种应用开发方式。鸿蒙系统&#xff0c;作为华为…

记录 Dubbo+Zookeeper 学习Demo

DubboZookeeper ZookeeperZookeeper 下载可能出现的问题 辅助程序下载dubbo-admin项目打包工程打包常见问题 SpringBoot集成Dubbo项目依赖定义服务接口服务端实现服务端配置依赖代码实现 消费端实现服务端配置依赖代码实现 启动 结合Dubbo官网学习如何完成SpringBootDubboZooke…

webstorm 保存自动格式化

webstorm 保存自动格式化 全局安装 prettier npm i -g prettierwebstorm设置

谷歌seo外链重要还是内容重要?

想做网站&#xff0c;内容跟外链缺一不可&#xff0c;如果真的要说哪个更重要&#xff0c;那内容依旧是网站的核心&#xff0c;而外链则是额外的加分项 内容永远是王道&#xff0c;不管谷歌seo的算法怎么变&#xff0c;只要你的内容没问题&#xff0c;那就肯定不会牵扯到你的网…

【牛客】HJ73 计算日期到天数转换

目录 题目链接:计算日期到天数转换_牛客题霸_牛客网 (nowcoder.com) 解题思路: 代码实现: 题目链接:计算日期到天数转换_牛客题霸_牛客网 (nowcoder.com) 解题思路: 用一个数组存放每月的天数 输入的日期天数 当月的天数 当月之前的累积天数 如果包含二月&#xff0c;再去判…

新一代国产人工心脏推出,《2024心衰器械白皮书》重磅发布

2024年3月2日&#xff0c;永仁心医疗“EVA-Pulsar™新品发布会”在京举办。在国内外众多领域知名专家、投资人、企业家的共同见证下&#xff0c;永仁心最新一代EVA-Pulsar™人工心脏&#xff08;心室辅助装置&#xff09;重磅发布。 这款人工心脏集长期植入、超小体积、脉动血…

报错:Nginx 部署后刷新页面 404 问题

文章目录 问题分析解决 问题 在部署完项目后 刷新页面&#xff0c;页面进入了404 分析 加载单页应用后路由改变均由浏览器处理&#xff0c;而刷新时将会请求当前的链接&#xff0c;而Nginx无法找到对应的页面 关键代码try_files,剩下俩如果其他地方配置了则可以省略。 在这…

CentOS 7 基于开源项目制作openssh 9.7p1二进制rpm包(内含ssh-copy-id、显示openssl版本信息)—— 筑梦之路

可参考之前的文章&#xff1a;CentOS 5/6/7 基于开源项目制作openssh 9.6p1 rpm包—— 筑梦之路_centos6 openssh9.6rpm-CSDN博客 2024年3月12日 植树节制作&#xff0c;相关文件见我的资源

【Golang】Windows与Linux交叉编译保姆级教程

【Golang】Windows与Linux交叉编译 大家好 我是寸铁&#x1f44a; 总结了一篇【golang】Windows与Linux交叉编译的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题背景 今天寸铁想将Windows中的程序部到Linux下跑&#xff0c;我们知道在从Windows与Linux下要进行交叉编译…