LED音乐频谱设计与制作
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
LED音乐频谱设计与制作
摘要:电子信息技术几乎主宰了整个电器行业的发展,随着电子技术的进步发展在功率放大器的设计上功能也不断更新。
功率放大器在家电、数码产品中的应用也越来越广泛,与我们日常生活有着密切关系。
随着生活水平的提高,人们越来越注重视觉,音质的享受。
音频频谱显示器不但能够直观的显示信号输入的状况,而且在美学方面给予人好的视觉享受和,在某一方面来说频谱显示器赋予了音乐的动态美,让原本抽象的电信号,具体化,实质化。
本文通过使用单片机和数字信号处理技术,实现了单片机控制的音乐频谱显示系统。
系统分为,声音采集单元,声音处理单元和显示单元。
其中声音采集单元包括声音接收和 A/D 转换系统。
声音接收单元接收到的音频模拟信号,经过 A/D 转换为数字信号,送给下一级处理单元处理。
声音处理单元采用C52单片机进行快速傅立叶变换处理。
显示单元接收处理后的信号,控制16*16LED板进行显示。
软件方面使用单片机进行DFT,从而判断出该时间段音频信号的主谱,跟随着音乐节拍和强弱的变化来控制LED亮度和暗灭的实时变化,实现音乐的频谱分析并加以灯光显示。
关键字:单片机功率放大器 A/D转换快速傅立叶变换
LED Design and Production of the Music Spectrum Abstract:Electronic information technology has occupied almost the entire appliance industry, with the advances and development of electronic technology on the design of power amplifier features are constantly updated. Closely related to our daily life, power amplifiers in applications of home appliances and digital products are increasingly widespread. With the improvement of living standards, there is a growing emphasis on visual and audio enjoyment. Audio spectrum display can not only show the situation of the signal input but also bring people a perfect visual enjoyment in aspect of aesthetics. In other words, audio spectrum displays are endowed with musical beauty of dynamic, which make abstract electric signal to be reification and substantiation.
This paper discusses that using the single-chip and digital signal processing technology could make music spectrum display system controlled by single-chip microcomputer possible. The system is divided into voice acquisition unit, the voice processing unit and display unit. V oice acquisition unit includes the sonic receiving and A/D conversion system. The audio analog signal which is received by sonic receiver unit is transformed by A/D conversion system to be a digital signal and, after that, is sent to the next level of processing. V oice processing unit takes C52 single-chip microcomputer for Fast Fourier Transform Algorithm processing. Display unit receives the final signal to display by controlling 16*16 LED Display Board. In the aspect of software, take the single-chip microcomputer to have DFT, in order to determine the main audio signal spectrum during this period and to control the real time changes of LED brightness and antique with the music beats and the strength, which can make the music spectrum analysis and light show possible.
Keywords: SCM singlechip power amplifier A/D conversion
Fast Fourier Transform Algorithm
目录
1 引言 (1)
2 LED音乐频谱设计 (2)
2.1设计思路 (2)
2.2硬件及电路设计部分 (2)
2.2.1单片机电路设计 (2)
2.2.2 16*16LED显示板设计 (5)
2.2.3 A/D转换模块设计 (6)
2.2.4行驱动器 (7)
2.2.5 列驱动器 (8)
2.2.6 音频放大电路 (9)
2.2.7 继电器切换电路 (10)
3 程序设计部分 (11)
3.1 音频信号的采集和预处理 (11)
3.1.1 采样频率 (11)
3.1.2 样本大小 (11)
3.2 音频频谱算法 (11)
3.2.1 倒位序及其优化算法 (11)
3.2.2 蝶形运算及其优化算法 (12)
3.3 源程序设计 (13)
3.3.1 快速傅立叶变换程序 (13)
3.3.2 AD采样控制程序 (14)
3.3.3 系统初始化和循环控制部分 (15)
3.3.4 文字滚动程序 (15)
4 设计总结 (25)
参考文献 (26)
致谢 ....................................................................................................... 错误!未定义书签。
附录 (27)
LED音乐频谱设计与制作
1 引言
LED灯已经充斥于人们生活中的方方面面,例如广场、火车站、机场等公共场所显示重要信息的LED显示屏;商场、电影院外设的LED广告屏;工厂生产线的指示器;人们生活中用到的手电筒等等。
因此LED作为一种能耗低,亮度高,寿命长的灯具,在今后一定会成为主流产品。
而单片机作为一个已经发展了很久的微型计算机,已经具有可靠性高,处理功能强,速度快,低电压,低功耗,控制功能强等特点。
本课题为基于单片机的LED音乐频谱显示器的设计与制作,利用了单片机的处理能力强等特点,通过声音采集,声音处理之后在LED显示屏上显示出跳动的音符。
声音采集单元收集音频信号,送给A/D转换器进行处理,随之输出不同电压的电信号给作为主处理器的89C52单片机。
通过设计并输入程序,是这些电信号在单片机内部进行快速傅立叶变换(主要运算方法为蝶形运算)。
在经过运算处理之后,由行列驱动器驱动16*16LED点阵,控制LED灯的明暗。
这样一来人们听到的音频就成功的转化为可以被视觉接受的跳动的图像画面。
本次设计中,不仅仅可以显示音乐的频谱,还额外添加了图文显示功能。
图文显示功能是通过动态扫描来完成的。
利用人眼的视觉暂留现象,快速的刷新LED的明灭次序,这样就可以展现出一个稳定或者滚动的文字图形。
开机的滚动文字,和图形变换,可以根据输入单片机的程序来改变,这样一来就使使器件更具人性化,也使得设计提升一个档次。
下面就详细介绍该课题的设计与制作过程。
2 LED音乐频谱设计
2.1设计思路
本次设计的目的是显示音乐频谱,因此设计电路分成以下几个部分:(1)单片机;(2)LED显示屏;(3)行、列驱动器;(4)A/D转换器;(5)显示电路。
原理方框图如图2.1:
图2.1 音乐频谱显示器原理方框图
2.2硬件及电路设计部分
2.2.1单片机电路设计
2.2.1.1 STC89C52单片机
本次设计利用STC89C52单片机,这是一种低功耗、高性能CMOS8位微控制器,具有 8K 在系统可编程Flash 存储器,拥有8位CPU以及在系统可变成Flash。
其具
有以下标准功能:8K字节Flash,512字节RAM,32位I/O口线,看门狗定时器,内置4KB EEPROM,MAX810复位电路,三个16 位定时器/计数器,一个6向量2级中断结构,全双工串行口。
图2.2 89C52引脚说明
2.2.1.2电源电路设计
单片机的供电运用LM78L05稳压芯片配合直流稳压电源构成。
直流输入低于15V 的电源,输出为5V(误差小于5%),这样一来就可以满足单片机和其他芯片的供电要求。
单片机的电源电路如图2.3:
图2.3 单片机电源电路
2.2.1.3 晶振电路设计
时钟电路设计一般有内部时钟方式和外部时钟方式两种。
此次设计中采用内部时钟方式,将XTAL1与XTAL2之间跨接一个石英晶振和一个微调电容,形成一个稳定的自激震荡器。
电容值取30pF左右,它的大小会影响震荡频率、振荡器稳定和起振的速度。
为了减少导线之间的寄生电容,晶振和电容必须尽可能靠近单片机来安装,这样才能保证晶振能够稳定可靠的工作。
单片机晶振电路如图2.4:
图2.4 单片机晶振电路
2.2.1.4 复位电路设计
复位电路是用来维持单片机最小系统运行的基本电路。
复位电路分为上电复位和手动复位两种方式。
图2.5 上电复位图2.6 手动复位有的时候系统在运行过程中会出现程序出错的情况,在程序开发过程中,经常需要手动复位。
所以设计中一般选择手动复位。
高频率的时钟有利于程序更快的运行,也可以实现更高的信号采样率,也能够实现更多的功能。
但这样也对系统本身的要求更高,而且功耗比较大,运行环境的条件
也更多。
因为单片机本身用于一般工业控制时,并非高速信号采样处理,所以选取合适的频率即可。
合适频率的晶振对于选频信号强度与准确度都有好处,本次设计采用8.000M无源晶振接入XTAL1和XTAL2两个管脚。
并联两个30pF瓷片电容来帮助起振。
在本次设计中,采用上电复位电路,通过对外部复位电路的电容充电,使得电源Vcc的上升时间不超过1ms来完成复位。
该电路采用一个1K左右的电阻和一个10μF 的电容串联完成,如图2.7:
图2.7 复位电路
2.2.2 16*16LED显示板设计
本设计中的LED显示板是由共196个发光二极管(即LED)组成,形成一个16*16矩阵的LED显示屏。
之前所提及的行列驱动器的设计在此进行说明。
引16根行驱动器线接至发光二极管的正极,引16根列驱动器线接至发光二极管的负极。
这样就能完成单片机对LED显示屏的控制。
其原理图见附录A。
2.2.2.1 图文显示
图文显示是通过动态扫描来完成的。
动态扫描就是一列一列轮流点亮LED灯。
首先输出对应第一列发光管亮灭的数据并保持,然后选通第一列,使其点亮一定时间之后熄灭;再送出第二列的数据并保持,之后选通第二列使其点亮相同的时间,熄灭;以此类推,第十六列之后,再重新点亮第一列,如此循环。
当这样循环的速度足够快的时候(每秒24次以上),因为人眼有视觉暂留现象,所以可以看到LED显示屏上稳定的图文信息。
2.2.3 A/D转换模块设计
本设计采用TLC0820型A/D转换器。
TLC0820是采用先进LinCMOS工艺制造的8位A/D转换器 ,它由2个4位的闪速(FLASH)转换器 ,1个4位的数模转换器 ,1个计算误差放大器 ,控制逻辑电路和结果锁存电路组成。
其特点为:(1)采用8位并行输出;(2)读方式最快转换时间为2.5µs;(3)不需要外部时钟和震荡元件;(4)片内具有跟踪和保持电路;(5)单电源供电。
在本设计中,单片机的P2口A/D会读入转换完成的数据,同时,它也控制控制16*16点阵行线。
在模式0中,只需显示汉字,不用读取D转换器输出的数据,为使A/D转换器的输出不影响P2口输出,所以必须将A/D转换器输出置高阻态;在模式1-3中,A/D转换器将采集数据,P2口要先将A/D转换器所采集到的数据读入单片机,经由单片机处理后,通过P2口输出。
因为在读取A/D转换器数据时,LED显示屏仍然要正常显示,所以一定要把P2口读取A/D转换器以前输出的数据进行锁存。
当单片机把数据处理完成之后,P2口才送出显示数据。
下面说明一下模式0时和模式1-3时的设置。
电路工作在模式0的时候,不读A/D 转换器数据,可令OE=1,这样A/D转换器输出将不会影响P2的输出。
而当电路工作在模式1-3时,读A/D转换器数据之前,令LE=0,把P2口输出的数据锁存,然后让OE=0,P2口读A/D转换器输出的数据,数据读完之后再使OE=1,等单片机数据处理完毕后,P2口输出数据,这时可令LE=1,点阵显示P2口送出的数据。
电路图如图2.8:
图2.8 A/D转换器电路图
由于单片机引脚的驱动能力有限,故12行线用74系列芯片驱动,前八行用74LS573驱动,后四行由CMOS的四2输入与门74HC08驱动。
74LS573原理:74LS573 的八个锁存器都是透明的D 型锁存器,当电平为高时,Q 输出将随数据(D)输入而变。
当电平为低时,输出将锁存在已建立的数据电平上。
输出控制不影响锁存器的内部工作,即老数据可以保持,甚至当输出被关闭(OE=1)时,新的数据也可以置入。
这种电路可以驱动大电容或低阻抗负载,可以直接与系统总线接口并驱动总线,而不需要外接口。
特别适用于缓冲寄存器,I/O 通道,双向总线驱动器和工作寄存器。
在本设计中由于P2口需分时复用,所以74LS573的输入数据需要锁存,锁存控制端由单片机控制。
74HC08为四输入与门,在本设计中将两输入端并在一起,相当于一个缓冲器的功能。
74LS573,7HC08输出高点平为5V,所以驱动发光二级管中间串了一个电阻限流。
行驱动器原理图如图2.9:
图2.9 行驱动器电路图
74LS138 为3 线-8 线译码器,其工作原理如下:
(1)当一个选通端(E1)为高电平,另两个选通端((E2)和(E3))为低电平时,可将地址端(A0、A1、A2)的二进制编码在Y0至Y7对应的输出端以低电平译出。
比如:A2A1A0=110时,则Y6输出端输出低电平信号。
(2)利用 E1、E2和E3可级联扩展成 24 线译码器;若外接一个反相器还可级联扩展成 32 线译码器;(3)若将选通端中的一个作为数据输入端时,74LS138还可作数据分配器;(4)可用在8086的译码电路中,扩展内存。
在采用扫描方式显示时,由于每列要带动十六个二极管,每列电流较大。
若每个二极管安5mA计算,十六个二极管就得80mA电流,超出单片机管脚的承受范围,因此每列都加有一个驱动器,如果列驱动用十六个PNP型三极管。
三极管的发射极接5V电压,集电极接点阵的列线,而它的基级接单片机,但该接线方式占用为了十六个单片机管脚,为了节省单片机管脚,本设计中的16*16点阵的16列由74LS138驱动。
只需4个引脚即可驱动16列线。
因一个74LS138只有8个译码输出端,所以可用两个3-8译码器接成4-16译码器,其原理图如图2.10所示:
图2.10 列驱动器电路图
由图可得:当A3=0时,U4被关闭,U4的8位输出全部为高,但U3正常工作,即可正常译出输入码A3A2A1A0为0000-0111的八种码,即实现3线-8线译码功能;
当A3=1时,U3被关闭,U3的8位输出全部为高,但U4正常工作,即可正常译出输入码A3A2A1A0为1000-1111的八种码,即实现3线-8线译码功能。
所以该电路可译出输入码A3A2A1A0为0000-1111的16种码,即实现4线-16线译码功能。
本设计电路中点阵有16列,故16位译码输出分别对应输入码A3A2A1A0为:0000-1111。
2.2.6 音频放大电路
手机音频信号只有10几毫伏,所以必须要经过合适的放大后才能供ADC采集和驱动扬声器。
TPA741是一款TI公司的低压供电的音频功率放大器。
该TPA741是一个桥式推挽(BTL)音频功率放大器,可以驱动RL = 8 Ω的扬声器负载,其内部结构如图2.11所示:
图2.11 TPA741内部结构电路图
由图可知到:其输出是差分的;所以该放大器的放大倍为A=-2*(Rf/R1);在本设计中,R1=10K,Rf为100K的滑动变阻器,最大放大倍数为20倍。
因为该运放是差分输出的,且输出的交流分量输出信号峰值还没达到5V,不能直接送给ADC,所以在TPA741后接了一个三极管放大电路,一方面将双端信号变为单端信号,另一方面将信号进一步放大,以达到ADC的采样要求。
三极管与TPA741输出采用交流耦合方式,经过调试之后,选择R16=7.7K,R14=R17=1.2K,可使三极管放大电路静态时的Vc=1.2V。
这样可使没有外接手机信号时,供AD采样的电压为1.2V,对应的点阵点亮最低四位。
音频放大电路原理图如图2.12:
图2.12 音频放大电路
2.2.7 继电器切换电路
在本设计中音频信号有两个来源,在不同的电路模式中,将对不同的音频信号进行采集,所以可通过继电器切换来改变音频信号。
使用的继电器是5V驱动的内阻为160Ω的继电器,正常工作的电流为31mA;单片机无法驱动继电器,所以可用三极管来驱动。
基极电阻R11=20K,当Relay为高电平时,基极电流为Ib=[(5-0.7)/20]mA=0.215mA,三极管放大倍数B=140,所以可得Ic=B*Ib=30.1mA,可以正常驱动继电器。
继电器切换电路如图2.13:
图2.13 继电切换电路
3 程序设计部分
3.1 音频信号的采集和预处理 3.1.1 采样频率
根据香农采样定理,一般采样频率至少应为所采样音频信号最高频率的2倍。
由于人耳能够感受的频率为20Hz~20kHz ,所以理论上采样频率最高取40kHz 。
目前工业上广泛采用的采样频率大致有三种:44kHz 、16bit 的声音叫做CD 音质;22kHz 、16bit 的声音效果近似于立体声广播的广播音质;11kHz 、8bit 的声音被称为电话音质。
在此次设计当中,为了提高频谱计算的精度,决定采用40kHz 的采样频率和8bit 的数据位长。
3.1.2 样本大小
采样频率确定后,还需确定样本值,即完成一次FFT 运算所需的采样点数。
根据数字信号处理的基本原理,假设采样频率为Fs ,采样点数为N ,则FFT 运算后,第n 点所表示的频率为:
()[]N Fs n Fn ⨯-=1 ()N n ≤≤1 (3.1)
Fn 若要精确到Hz ,则需采样长度为(1/f)s 的信号。
提高频率分辨率,需增加采样点数,但这在一些实际应用中是不现实的,则采用有频率细分法,即采样比较短时间的信号,然后在后面补充一定数量的0,使其长度达到所需的点数,再作FFT ,这在一定程度上能够提高频率分辨率。
由于该系统是将音频信号频谱划分成14段显示,因此采用16点FFT 运算,去掉第1点和第16点的结果即可。
3.2 音频频谱算法
音频频谱值的计算采用快速傅立叶算法FFT(Fast Fourier Transform),为了提高显示器的刷新频率,系统每隔10ms 读取16次A/D 转换值,得到16点实数序列,紧接着完成16点FFT 运算得到16点复数序列。
3.2.1 倒位序及其优化算法
基2-FTT 算法将原始数据倒位序存储,但运算后的结果则按正常顺序输出。
原始采样数据放在数组float datalm[16]中,datalm[0]存放第1次读取的A/D 转换值,
datalm[1]存放第2次读取的A/D 转换值,以此类推,可见第n(n=(b3b2b1b0)b)次读取的A/D 转换值存放在dataIm[n]中。
倒序操作后采样数据存储在float dataRe[16]中,原来第n 次读取的A/D 转换值存放在datalm[n](n=(b0b1b2b3)b)中。
根据样本大小在系统代码段中建立倒序表数组,利用查表方式来实现快速倒序操作,比移位操作等方法的运算速度相比,查表方式的预算速度明显加快。
3.2.2 蝶形运算及其优化算法
根据基2-FFT 算法,N 点FFT 运算可以分成log2N 级,每一级都有N/2个蝶形运算,如图3.2.2所示。
图3.1 蝶式运算
蝶形运算公式的推导过程如下:
()()P
N W b k X k X k X )(+'+'= (3.2)
()()P
N W b k X k X b k X )(+-'=+ (3.3)
式中,()()N P j N P W P N ππ2sin 2cos -=。
将式(3.2)化简成实部与虚部的形式得:
()()()()()()N P b k X N P b k X k X k X I R R R ππ2sin 2cos +'
++'+'= (3.4) )2cos()()2sin()()()(N P b k X N P b k X k X k X I R I I ππ+'++'-'= (3.5) 同理,将(3.3)式简化可得:
)2sin()()2cos()()()(N P b k X N P b k X k X b k X I R R R ππ+'
-+'-'=+ (3.6)
)2cos()()2sin()()()(N P b k X N P b k X k X b k X I R I I ππ+'
-+'-'=+ (3.7) 可见每个蝶形运算的输出都是由其输入值与某一正弦函数和余弦函数的乘积累加得到的。
由式(3.4)~式(3.7)编制正弦和余弦表,每次做蝶形运算时可查表加快运算速度。
3.3 源程序设计
本次设计中的源程序采用C语言进行设计。
51系列单片机的编程语言常用的有二种,一种是汇编语言,一种是C语言。
汇编语言的机器代码生成效率很高但可读性却并不强,复杂一点的程序就更是难读懂。
而C语言作为一种编译型程序设计语言,它具备了多种高级语言的特点,并且具有汇编语言的功能。
C语言有功能强大的库函数、运算速度快、编译效率高、有良好的可移植性,而且可以直接实现对系统硬件的控制。
C语言是一种结构化程序设计语言,它支持当前程序设计中广泛采用的由顶向下结构化程序设计技术。
此外,C语言程序具有完善的模块程序结构,从而为软件开发中采用模块化程序设计方法提供了有力的保障。
因此,使用C语言进行程序设计已成为软件开发的一个主流。
用C语言来编写目标系统软件,会大大缩短开发周期,且明显地增加软件的可读性,便于改进和扩充,从而研制出规模更大、性能更完备的系统。
用单片机C语言程序设计是单片机开发与应用的必然趋势,因此,本次设计采用的C语言来设计开发程序。
3.3.1 快速傅立叶变换程序
for(i=0;i<fft_control_2;i++){ //各级
gap=1<<i; //计算间隔
or(j=0;j<=gap-1;j++){ //蝶形运算1,间隔中的各蝶形wn=j*(1<<(fft_control_2-1-i)); //旋转因子序数
for(k=j;k<fft_control_1;k+=gap<<1){ //蝶形运2
Temp_Real1=Fft_Real[k];
Temp_Image1=Fft_Image[k];
Temp_Real2=Fft_Real[k+gap];
Temp_Image2=Fft_Image[k+gap];
Real_buffer=((Temp_Real2*FFT_costable[wn]))-((Temp_Image2*FFT_sintabl e[wn]));
Image_buffer=((Temp_Real2*FFT_sintable[wn]))+((Temp_Image2*FFT_costab le[wn]));
Fft_Real[k]=Temp_Real1+Real_buffer;
Fft_Image[k]=Temp_Image1+Image_buffer;
Fft_Real[k+gap]=Temp_Real1-Real_buffer;
Fft_Image[k+gap]=Temp_Image1-Image_buffer;
Fft_Image[k]/=2;
Fft_Real[k]/=2;
Fft_Image[k+gap]/=2;
Fft_Real[k+gap]/=2;}
}
}
3.3.2 AD采样控制程序
#defineADC_Sample_F40000L //采用频率宏定义
#defineStart_Tim0()TR0=1 //定时器0启动
#defineStop_Tim0()TR0=0 //定时器0停止计时#defineadc_point64 //采样点数
#definegain4 //去干扰系数
/**************ADC中断服务函数***************/
voidADC_1_ISR(void)interrupt5using1{
unsignedchartempl,temph,t;
templ=ADC_RESL;
temph=ADC_RES;
t=FFT_Inver_Table[counter++];//查倒序表,counter为全局变量,计数当前点
Fft_Real[t]=(unsignedint)(templ&0x3|((unsignedint)(temph)<<gain))-512 ;
if(counter==adc_point){ //判断是否达到规定的采样点数counter=0;Stop_Tim0(); //关闭控制频率定时器
flat=1;} //用于标记,采集完成
ADC_CONTR&=~0X10; //清楚中断标志位}
/**********使用定时器0控制采样频率************/
voidTim0_ISR(void)interrupt1using1 //工作在8位自动重载方式
{
ADC_CONTR|=0X08; //启动ADC转换
}
3.3.3 系统初始化和循环控制部分
voidmain(void){
Systim_CLK_Config(); //STC时钟配置
Peripheral_Init(); //包括定时器0,1,ADC模块的初始化工作fft_init(); //fft运算初始化暂存数据
my_GPIO_Init(); //STCIO口配置
LCD_Init(); //OLED显示配置
LCD_CLS(); //清屏
counter=0; //全局变量初始化
flat=0;
EA=1;
Start_Tim0(); //启动定时器
0Start_Tim1(); //启动定时器
1Start_ADC(); //启动ADC模块
while(1){
if(flat){ //采集完成后进行FFT运算
flat=0;
FFT();
fft_init();
Start_Tim0(); //重新开始定时器0
}
}}
3.3.4 文字滚动程序
/**************16*16点阵屏的移动显示****************/
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define DATE_OUT P1 //指定P1口做为输出
sbit DATA_595=DATE_OUT^0; //列数据输出位
sbit RCK_595=DATE_OUT^1; //列扫描时钟位
sbit SCK_595=DATE_OUT^2; //列数据锁存位
sbit CLK_164=DATE_OUT^3; //行数据输出位
sbit AB_164 =DATE_OUT^4; //行扫描时钟位
unsigned char date[32]; //32字节RAM做为16*16点阵
屏显示缓存
void display(); //做为点阵扫描函数,将显示缓
存的数据输出到点阵屏void display_time(unsigned int timer); //指定时间扫描显示
void displaymove(unsigned char *lp,unsigned char c,unsigned char timer); //显示汉字内容的移动效果,LP指向要显示第一
个字的首地址,C表示显示字的个数,timer是移
动的速度
void displaymovetb(unsigned char din,unsigned char *lp,unsigned char
timer); //上下移动内容,din:1为向下,0为向上,lp
指向要移入的内容,timer为移动速度
void delay(unsigned int a); //延时子函数
code uchar gan[32]={ //感
0x00,0x50,0x00,0x48,0x7F,0xFC,0x40,0x40,0x7F,0xC0,0x40,0x48,0x5F,0x48
,0x51,0x50,0x51,0x22,0x5F,0x52,0x90,0x8E,0x02,0x00,0x29,0x90,0x28,0xAC,0x
48,0x24,0x07,0xE0};
code uchar xie[32]={ //谢
0x02,0x08,0x44,0x88,0x2F,0xC8,0x28,0x88,0x0F,0x88,0x08,0xFE,0xEF,0x88
,0x28,0x88,0x28,0xC8,0x3F,0xA8,0x21,0xA8,0x2A,0x88,0x34,0x88,0x28,0x88,0x
12,0xA8,0x01,0x10};
code uchar shi[32]={ //使
0x10,0x40,0x10,0x44,0x1F,0xFE,0x20,0x40,0x27,0xFC,0x64,0x44,0xA4,0x44
,0x24,0x44,0x27,0xFC,0x24,0x44,0x22,0x40,0x21,0x80,0x20,0xC0,0x21,0x30,0x 26,0x0E,0x28,0x04};
code uchar yong[32]={ //用
0x00,0x08,0x3F,0xFC,0x21,0x08,0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08 ,0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,0x21,0x08,0x21,0x08,0x41,0x08,0x 41,0x28,0x80,0x10};
code uchar yin[32]={ //音
0x02,0x00,0x01,0x10,0x3F,0xF8,0x08,0x20,0x04,0x20,0x04,0x44,0xFF,0xFE ,0x00,0x10,0x1F,0xF8,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x10,0x10,0x 1F,0xF0,0x10,0x10};
code uchar yue[32]={ //乐
0x00,0x20,0x00,0xF0,0x1F,0x00,0x10,0x00,0x11,0x00,0x11,0x00,0x21,0x04 ,0x7F,0xFE,0x01,0x00,0x01,0x00,0x09,0x20,0x09,0x10,0x11,0x08,0x21,0x0C,0x 45,0x04,0x02,0x00};
code uchar pin[32]={ //频
0x10,0x04,0x13,0xFE,0x54,0x20,0x5E,0x44,0x51,0xFE,0x55,0x04,0xFF,0x24 ,0x11,0x24,
0x55,0x24,0x55,0x24,0x55,0x24,0x95,0x24,0x08,0x20,0x10,0x50,0x20,0x8C ,0x43,0x04};
code uchar pu[32]={ //谱
0x01,0x10,0x40,0xA0,0x27,0xFC,0x20,0xA0,0x02,0xA8,0x01,0xB0,0xEF,0xFE ,0x20,0x08,0x23,0xFC,0x22,0x08,0x22,0x08,0x23,0xF8,0x2A,0x08,0x32,0x08,0x 23,0xF8,0x02,0x08};
code uchar xian[32]={ //显
0x00,0x10,0x1F,0xF8,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x10,0x10 ,0x1F,0xF0,0x14,0x50,0x44,0x44,0x34,0x4C,0x14,0x50,0x04,0x40,0x04,0x44,0x FF,0xFE,0x00,0x00};
code uchar si[32]={ //示
0x00,0x10,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xFF,0xFE ,0x01,0x00,0x01,0x00,0x09,0x20,0x19,0x18,0x21,0x0C,0x41,0x04,0x01,0x00,0x 05,0x00,0x02,0x00};
code uchar qi[32]={ //器
0x3E,0xF8,0x22,0x88,0x22,0x88,0x22,0x88,0x3E,0xF8,0x01,0x20,0x01,0x14 ,0xFF,0xFE,0x02,0x80,0x0C,0x60,0x30,0x18,0xFE,0xFE,0x22,0x88,0x22,0x88,0x 22,0x88,0x3E,0xF8};
void main(void) //主入口函数{
unsigned char i=0,j=0;
for(i=0;i<32;i++)
date[i]=gan[i]; //将"感"复制到显示缓存while(1){
display_time(2); //显示约2秒displaymove(xie,10,7); //将从"谢"开始的四个汉字从右向左移动
display_time(1); //等持约1秒displaymovetb(1,0,10); //将点阵上显示的内容向下以10的速度移出,补上0,即清空显示屏
display_time(1); //等待1秒
displaymovetb(0,gan,10); //将"感"字以10的速度向上移动
displaymovetb(0,xie,10); //将"谢"字以10的速度向上移动
displaymovetb(0,shi,10); //将"使"字以10的速度向上移动
displaymovetb(0,yong,10); //将"用"字以10的速度向上移动
displaymovetb(0,yin,10); //将"音"字以10的速度向上移动
displaymovetb(0,yue,10); //将"乐"字以10的速度向上移动
displaymovetb(0,pin,10); //将"频"字以10的速度向上移动
displaymovetb(0,pu,10); //将"谱"字以10的速度向上移动
displaymovetb(0,xian,10); //将"显"字以10的速度向上移动
displaymovetb(0,si,10); //将"示"字以10的速度向上移动
displaymovetb(0,qi,10); //将"器"字以10的速度向上移动
displaymovetb(0,0,10); //以10的速度向上清空
display_time(1); //等待1秒
displaymovetb(1,gan,10); //将"gan"字以10的速度向下移动
}
}
void display() //显示
{
unsigned char i,ia,j,tmp; //定义变量
DATE_OUT=0XFF; //置位高电平做准备
AB_164=0; //行数据位清0,准备移位for(i=0;i<16;i++){ //循环输出16行数据CLK_164=0; //行移位
RCK_595=0; //列锁存
for(ia=2;ia>0;){ //每行16个点,循环位移两个字节
ia--; //循环两次
tmp=~date[i*2+ia]; //读取点阵数据做输出,是先读取点
阵数据的第二位字节,因一行16
个点由两个字节组成,电路中的
移位寄存器最后一位对应最后
一列,所以要先输出一行中的第
二个字节数据
for(j=0;j<8;j++){ //循环两次,每次移一个字节,SCK_595=0; //为列移位做准备
DATA_595=tmp&0x01; //将数据低位做输出,由电路图可
知,移位寄存器的最后一位对
应最后一列,因此先移最后一
位
tmp>>=1; //将数据缓冲右移一位,为下次输出做准备
SCK_595=1; //将DATA上的数据移入寄存器} //移入单字节结束
} //移入两个字节结束
DATE_OUT|=0X0A; //此句可以用以下两句来理解,如果不将
两句合为一句,将出现拖影现像RCK=1; //RCK拉高,行数据移位,相应行拉低,
三极管导通输出电量到相应行点阵管
阳极(共阳)
CLK=1; //CLK拉高,将数据锁存输出到相应列的点
阵发光管显示,显示一行后将保持到下
一行显示开始
AB_164=1; //行数据位只在第一行时为0,其它时候都
为1,当将这个0移入寄存器后,从第
一位开始一直移位最后一位,移位的过
程,AB就必需是1,这是因为不能同时
有两个及两个以上0的出现,否则显示
出乱
}
j=64;
while(j--); //每一行的显示,保持了两字节的移位时
间,因此,最后一行的显示,也要加入
保持时间,补尝显示的亮度CLK_164=0;
CLK_164=1; //将最后一行数据移出
}
void display_time(unsigned int timer) //指定时间扫描显示{
unsigned char i;
while(timer--){ //当timer=1时,大约1秒时间i=130;
while(i--)
display();
}
}
void displaymove(unsigned char *lp,unsigned char c,unsigned char timer) //显示汉字内容的移动效果,LP指向要显示第一个字的首地址,C表示显示字的个数,是移动的速度//
{
unsigned char i=0,j=0,ia=0;
unsigned int tmp=0,timerc=0;
unsigned char tmp2[16];
c*=2; //因一个汉字由32字节组成,而移
位显示,要分开半个汉字16字节
处理,因此将这里乘以2 for(i=0;i<16;i++)
tmp2[1]=0; //将缓冲区清0,
while(c){ //循环处理
if(lp!=0){ //当lp指向的地址为0时,直接用
组缓冲0补上,效果是将当前显示
的内容移出
tmp=c%2; //取余,目的是为了判断处理汉字
的前半部份还是后半部份
for(i=0;i<16;i++){
tmp2[i]=lp[i*2+tmp]; //取半个汉字点阵数据,16字节}
if(tmp) //当tmp为1时,表时一个字数组
处理完成,将地址转到下一个字lp+=32;
}
//--------------
tmp=8; //变量再次利用
while(tmp){ //循环8次,将下一个字的前半部
份的数据移入显示缓冲
ia=0;
for(i=0;i<16;i++){ // 16行同时移
date[ia]<<=1; //移动当前显示缓冲的前半行字节if(date[ia+1]&0x80) //判断后半行字节的高位是否为1,是
移入前半行字节低位,否则不处理date[ia]++;
ia++;
date[ia]<<=1; //移当前显示缓冲的后半行字节
if(tmp2[i]&0x80) //判断下一个要显示汉字的前半行
字节的高位是否为1,是则移入,
否则不处理
date[ia]++;
ia++;
tmp2[i]<<=1; //下一个要显示汉字的半行字节向
高位移一位,准备下一次取位}
tmp--;
timerc=timer; //处理完闭,调用显示函数更新点阵
while(timerc--) //循环做为处理的速度
display();
}
//----------
c--;
}
}
void displaymovetb(unsigned char din,unsigned char *lp,unsigned char timer);
//上下移动内容,din:1为向下,0为向上,lp指向要移入的内容,timer为移动速度//
{
unsigned char i=0,j=0,ia=0;
unsigned int tmp=0,timerc=0;
if(din){ //判断移动方向,向下。