基于单片机的语音存储与回放系统设计

目 录

摘 要 I
Abstract II
引 言 1
1 控制系统设计 3
1.1 系统方案设计 3
1.2 系统工作原理 4
1.2.1 单片机的选择 4
1.2.2 语音芯片的选择 5
2 硬件电路设计 6
2.1 时钟电路 6
2.2 复位电路 6
2.3 显示电路 7
2.4 电源电路 7
2.5 按键模块电路 8
2.6 LM386功放电路 8
2.7 总电路设计图 9
3 软件设计 10
3.1 编程工具软件Keil C51 10
3.2 程序设计原理 10
3.3 系统主程序设计 11
4 系统调试 13
4.1 硬件调试 13
4.2 软件调试 14
4.3 调试结果 14
4.4 调试结果展示 15
结 论 17
参考文献 18
附录 源程序清单 20
致 谢 33

摘 要

在人类的历史长河中,语言的作用尤为重要,人们一直在思考一个问题,那就是如何把语言完全不差的记录下来。当今智能化的仪表仪器的自动控制装置中,如果能够添加语言功能,就可以非常好的提升操作便捷度,在日常生活中,人们在很多场合都需要语言的存储与回放技术,所以将这种技术与生活更好的结合在一起,是现在需要解决的一大难题。传统的语言记录装置体积庞大,使用不方便,在信息处理的过程中受到许多限制,为了解决这些问题,本项目将使用AT89C51单片机和ISD4004语音芯片来设计一款语音的录放系统。ISD4004语音芯片不需要数模转换和压缩就能够直接进行存储,不仅存储时间长,而且没有转换误差,还可以用于多次重复录放。外围核心电路也相对来说十分简单。使用ISD4004语音芯片与单片机相结合,通过软件与硬件的控制,完全可以实现数字化语言的存储和回放。

关键词:AT89C51单片机; 语音存储; 语音回放; ISD4004语音芯片

Abstract

In the long history of human beings, language plays a very important role. People have been thinking about a problem, that is, how to record the perfect language. Present automatic control device of intelligent instrument, if can add language functions, can be very good improve ease of operation, in daily life, people on many occasions require language of storage and playback technology, so this kind of technology and a better life together, is now a big problem to solve. The traditional language recording device is bulky, inconvenient to use, and is limited in the process of information processing. In order to solve these problems, this project will use AT89C51 microcontroller and ISD4004 voice chip to design a voice recording system. ISD4004 voice chip does not need digital to analog conversion and compression to be able to directly store, not only the storage time is long, and no conversion error, but also can be used for repeated recording and playback. The peripheral core circuit is also relatively simple. With the combination of ISD4004 voice chip and single chip microcomputer, the storage and playback of digital language can be realized completely through the control of software and hardware.

Keywords: AT89C51 microcomputer; pronunciation; Audio playback; ISD4004

引 言

人们生活节奏无时无刻都在加快,对于平时日常生活中使用的产品要求也在日益提高,智能化产品也开始普及。将语音录放功能添加到各种智能仪器仪表和工业控制系统中也成为了普遍的现象,在智能化的一期医疗中添加此项功能,不仅可以使这些仪器操作更便捷,同时也方便了一些同时也方便了一些对某些操作有障碍的人,而且是生活节奏,再一次加快,进而使每个人都得到非常多的利益。语言服务行业正在变得越来越广泛,例如卫星导航系统、语音万用表和语音警报系统。可以说语言系统是社会生活中必不可少的东西,它的发展也将促进社会进步。
由于产生了光学录音和磁带才结束了机械录音的时代,随着时代的发展,语音芯片也相应产生,这种方法不但需要软件的控制指令,还需要硬件的支持。语音的采集技术、存储技术、回放技术三个部分组成了云存储与回放系统,任何一个部分都是不可或缺的。那我们的问题也随之而来,怎样去存储语音,存储之后又怎么将其取出呢?所以本次项目中选用了AT89C51单片机和ISD4004语音芯片。单片机的体积小、功耗低,但我们不能因此小看它的功能。原本准备采用A/D转换、D/A转换的方式来转换语音信号,这种方法十分的简单,但是这种方法有很多弊病,比如说操作复杂,语音信号也非常容易失真,在调试的过程中也非常不方便。所以选择了ISD4004语音芯片。ISD4004语音芯片包含了模拟输入输出,所以不需要再使用A/D和D/A转换器,在本次项目中,起初也是想通过先后转换的房子来完成,但由于现在的语音芯片已经可以完成信号转换的操作,所以还是选择了更加先进的语音芯片。而且语音芯片记录声音没有长度的限制,不需要接入电源就可以存储100年。
ISD语音芯片具有外围电路非常简单的优点,它可以和单片机完美相结合,而且它的重量和体积也非常小,可以通过单片机对他的控制完美的进行对语音的录制和放出。单片机主要起到对语音芯片的控制作用。所以基于单片机和语音芯片的语音系统也就相应的产生了。
虽然目前的语音存储与回放技术已经很成熟了,但是我认为该项技术,还有很大的发展空间,比如说更快的速度和更高的质量以及加密方式。或者如何优化处理来进一步减少存储空间。而且智能语言将会是未来发展的主要路线。
语言语音存储与回放系统与数学理论相结合,这些技术,在很多个领域随处可见,我们在生活中就可以看到语言存储和回放技术的重要性,这种技术提高了我们生活的质量,也推动了社会的发展,所以此次研究是很有必要的。

1 控制系统设计

1.1 系统方案设计
方案1:使用AT89C51单片机通过对语音信号的各种转换,来完成对语音的采集与回放。它的原理如图1-1所示。在录音的时候,单片机先将模拟信号转换为电信号,再通过音频的放大器将信号放大,通过滤波器进行滤波再由转换器转换为数字信号,送回到单片机中,然后将其存储。在需要放音的时候,相当于进行一次反向操作,首先读取出数字信号,然后经过模数转换,将数字信号转换成模拟信号,经过放大器进行放大再变成声音播放出来。这种方法虽然过程简单,但是语言信号非常容易受到外界的干扰,而且信号压缩也比较复杂,硬件电路的调试也非常困难。
在这里插入图片描述

图1-1方案1系统原理图
方案2:使用AT89C51单片机来控制整个系统,配合ISD语音芯片完成语言的录放,系统原理图如图1-2所示。通过按键控制单片机,并将指令发送到语音芯片以完成录音和播放。当前的操作状态显示在显示屏上。这种处理方法不但使其抗干扰的能力更强,而且存储也更加方便,调试也更加简单。
在这里插入图片描述

图1-2 方案2系统原理图
1.2 系统工作原理
本次项目使用单片机作为控制核心为基础,通过单片机来完成对按键和液晶显示屏等模块的响应,再控制语音芯片来存储和读取语音。系统结构如图1-3所示。
在这里插入图片描述

图1-3 系统结构框图
1.2.1 单片机的选择
MCU的类型很多,例如PIC MCU,MCS-51 MCU和AVR ATmega128 MCU。因为目前大多数公司都已经推出了51系列单片机的兼容机型,所以它们固然会在很长一段时间内占领大量市场。
由于在单个芯片中结合了多功能8位CTO和闪存,ATMEL的AT89C51和AT89C2051是高级微控制器。两个单片机的性能对比如1-1所示。
表1-1 AT89C51和AT89C2051主要性能对比
单片机型号
对比项目
AT89C51 AT89C2051
存储器 4KB可编程Flash存储器 2KB可编程Flash存储器
存储器保密 三级程序存储器保密 两级程序存储器保密
内部RAM 128字节 128字节
静态工作频率 0Hz~24MHz 0Hz~24MHz
定时/计数器 2个16位定时/计数器 2个16位定时/计数器
串行通讯口 1个串行通讯口 1个串行通讯口
中断源 6个中断源 6个中断源
I/O引线 32条I/O引线 15条I/O引线

通过此表格可以看出,两个单片机性能基本上是相同的,但AT89C2051的IO口线较少,所以没有办法增添ROM和RAM,在这里,考虑到将来的扩展和其他设计,可以选择更多的扩展接口。可以在需要时升级AT89C51,同时扩展其他功能,因此我选择了AT89C51微控制器。
1.2.2 语音芯片的选择
ISD系列芯片在处理音频信号时不需要数模转换和编码压缩。这种存储技术没有A / D转换错误。因为它可以将采样值直接存储,所以在还原真实场景的声音时显得更加完美。它避免了电路引起的噪声、金属声音和音频失真。 ISD4004语音芯片比ISD系列芯片需要更长的时间来记录声音,因此该项目选择了ISD4004语音芯片。 ISD4004语音芯片的引脚如图1-4所示。
在这里插入图片描述

图1-4 ISD4004引脚图片

2 硬件电路设计

该系统可分为三个部分:微控制器控制部分、语音记录和回放部分,功率放大器部分。采用51单片机为控制核心,采用ISD4004进行录音和播放。P0连接按钮用于记录和播放,并连接到LED,显示当前的记录和播放状态。
2.1 时钟电路
时钟电路由晶体振荡器和电容器组成,时钟电路就像平时家里用的时钟一样控制着任何工作的时间。时钟电路的用途很多,时钟电路被广泛使用,它的主要工作是产生与时钟一样精确的振荡器电路。任何工作都按时间顺序排列。产生该时间的电路是时钟电路,如图2-1所示。
在这里插入图片描述

图2-1 时钟电路
2.2 复位电路
复位电路在微型计算机系统中是非常简单的电路,但它非常重要。其主要功能是上电复位,以确保在打开电源后可以正确启动系统。当系统遭受导致系统故障的某些外部原因时,可以通过“重置”按钮将系统重置为正常工作状态。复位电路的电路图如图2-2所示。打开电源时,仅当VCC超过4.75V且低于5.25V时,复位信号才被清除,晶体振荡器稳定工作,微机电路开始正常工作。
在这里插入图片描述

图2-2 复位电路
2.3 显示电路
本次项目使用的是LCD1602显示模块,它可以同时显示32个字符。1602通过D0〜D7的8位数据端子发送数据和命令。该端口连接到微控制器的P0端口。V0端口用于调整显示器的对比度。一个10K可变电阻器连接到此端口,可以通过调整电阻的大小来控制显示屏的亮度。 RS是寄存器选择的控制端子。该端口连接到微控制器的P24引脚。该端口连接到微控制器的P25引脚。图2-3给出了电路原理图。
在这里插入图片描述

图2-3 LCD1602电路原理图
2.4 电源电路
系统电源设计也是系统的重要组成部分。由于该系统的芯片主要由5V USB电源供电,为了获得稳定的DC电压信号,考虑到经济性和耐用性,该系统使用三端稳压器W7805输出+ 5V电压,并使用电容器用于减小输出纹波波形电压,如图2-4所示。
在这里插入图片描述

图2-4 电源电路
2.5 按键模块电路
该项目中的按钮模块使用多个独立的按钮,按钮的一端接地,另一端连接到IO端口。由于微控制器的IO端口具有内部上拉,因此当不按下按钮时,将在高功率下检测到IO。当按下按钮时,Ping等效于IO接地短路。因此,此时微控制器检测到的电平较低,可以通过在不同时间检测IO端口的状态来判断按下的按钮。这四个按钮代表以下功能,可用于控制MCU的记录、暂停、选择和播放操作。第一个是播放按钮,第二个是暂停按钮,第三个是录制按钮,第四个是选择按钮。如图2-5所示。
在这里插入图片描述

图2-5 按键模块电路
2.6 LM386功放电路
在很多情况下,主机不能够通过自己的额定功率来维持整个系统的驱动。那么这个时候,就需要通过功率放大器来将功率进行放大,将不足的功率补充完整。该产品中使用的LM386芯片具有很好的音频放大的功能,增益可以达到200,并且声音质量纯净且无噪音。 LM386可以使用电池作为电源。输入电压范围为4V到12V之间。LM386的内部电路图如图2-6所示。
在这里插入图片描述

图2-6 LM386芯片电路图
2.7 总电路设计图
将以上各个电路图进行组合后为本次设计的最终设计电路图(利用National Instruments中的Multiuse绘制)。如图2-7所示。
在这里插入图片描述

图2-7 总电路图

3 软件设计

该软件的设计基于硬件,并且要由该软件实现的功能基于常规硬件。如果硬件不能正常工作,则意味着没有好的软件可以实现任何功能。如果没有软件,那用再好的硬件也是毫无作用的。将需要实现的功能确定之后,如果调试正确,就会可以开始设计本次项目需要的软件模块了。本次项目使用51单片机作为开发的基础,软件格式采用Keil C51来进行汇编语言的编译。
3.1 编程工具软件Keil C51
Keil C51集成开发环境是基于80C51内核的微处理器软件开发平台。他可以独立完成整个项目的建立,也就是完成整个开发过程。用于单片机应用系统的常用软件编写语言是汇编、C、BASIC、C ++等。对于51单片机而言,使用最广泛的是汇编语言和C语言。
使用Keil C需要以下五个步骤来开发单片机程序:
(1)选择创建一个全新的项目,然后选择本次项目中使用的单片机机型。
(2)μVision3中的文件编辑器功能,可以用来编写源程序文件,并且需要将此文件添加到新建的这个项目中去。
(3)通过μVision3的各种选项,配置Cx51编译器、Ax51宏汇编器、BL51/Lx51连接定位器以及Debug调试器的功能。
(4)使用μVision3软件,将编写后的源程序将编写后的源程序代码进行编译,然后生成HEX格式的可执行文件,如果在编译过程中出现错误,那么就返回到第2步对源程序代码进行修改。
(5)如果编译没有错误,那么就证明源程序代码没有错误,这时就可以将源程序代码导入仿真软件中进行仿真操作。
本次项目中选用的汇编语言可以直接与计算机的底层软件进行交互,它可以直接访问硬件的存储器或者端口,并且没有局限性,能够对二进制代码进行完美的操控,很好的发挥出硬件的优势,运行速度也会大大提高,所以本次项目我选用了汇编语言对这个项目进行调试。
3.2 程序设计原理
对软件的设计可以和硬件的设计相结合,因为不管哪个功能是由硬件完成的,哪个任务是由软件完成的,基本都可以在硬件设计完成后确定。
软件的总体设计思路可以分为两类:其中一类是执行软件,这一类软件可以完成计算测量、显示、打印、输出等非常实质性操作,另一类是起到执行协调作用的监视软件,这是用来起到协调作用的,在整个软件系统中充当调度员的角色。所以软件的设计有两类,他们各具特色,并且软件的设计很偏重算法,还与硬件之间有很大的联系。
在进行软件的设计时,应该将所有的模块列举出来,然后通过数据结构规划好,所有的模块规划好之后,就可以进行监控了,编程比较容易,但是监控就比较难了,这就像是当一名员工容易,但是当一名领导就比较困难。
3.3 系统主程序设计
主程序的设计思路,是考虑到4个按键的接口,通过单片机循环扫描来检测是否有按键被按下去,当任何一个按键被按下后,都会去执行该按键的这个子程序。例如当单片机扫描识别到按键被按下时,这个时候单片机就会去执行录音的程序,当单片机扫描识别到放音按键被按下时,单片机就会停止录音程序,去运行放音的子程序,每个状态在执行时切换下一个状态时都需要按下停止按键清除状态标志。流程图如图3-1所示。
在这里插入图片描述

图3-1 按键检测流程图
单片机依次判断用户按下的按键,因为不同按键在不同的界面有不同的功能,因此系统采用了一个Function Count来记录当前属于哪个界面,根据不同的界面下用户按下的不同按键执行对应的操作。语音播放流程图如图3-2所示。
在这里插入图片描述

图3-2 语音播放流程图
当系统处于播放模式下,单片机会根据当前语音的序号播放对应地址的语音。

4 系统调试

4.1 硬件调试
通过单片机控制语音芯片完成的语音存储与回访系统的电路比较大,而且回涉及到很多的模块电路,比如会涉及到单片机的最小系统、时钟电路、液晶显示模块等等,所以在焊接时要十分注意,涉及到多种模块的这种电路,哪怕只要存在一处的焊接错误,就会导致整个系统的检测无法完成,因为电路中交叉的线路非常多,所以在焊接过程中避免焊接错误和短路现象,如果电路连接错误,将给检测带来极大的不便,并且该电路具有更多的交叉线。请注意处理各种尖锐的针脚,否则会刺破被覆的电线并导致短路。
检查正确性后,我们开始在通用板上焊接。 PCB图如图4-1所示。
在这里插入图片描述

图4-1 PCB图
在系统的硬件调试中遇到了许多问题。回想一下,只要仔细考虑,就可以避免这些问题。以下是主要问题:
一开始,我认为微控制器无法正常驱动ISD4004,因此无法进行记录和播放。
解决方案:搜索相关信息后,必须在微控制器的P0端口上添加一个上拉电阻,以正常驱动外部芯片。
4.2 软件调试
该系统是一个相对复杂的数字系统,因此其程序也相对复杂,因此在编写程序和调试过程中存在许多问题。首先,为了更好地测试效果,在确认没有错误之后,我直接在PROTUES软件下启动了仿真,然后将程序刻录到实际环境中运行。最终,在对模块子例程进行了许多修改之后,它逐渐完成并最终解决了软件中的各种问题。在软件调试过程中遇到的主要问题如下:
1.程序刻录后,LCD屏幕闪烁并且亮度不均匀。
解决方案:首先,逐步修改呼叫延迟,以解决显示闪烁的问题。其次,由于这项工作使用的是在动态扫描模式下显示的数字,因此动态扫描速度非常快,人眼看不到它,但是当调用显示程序时,如果返回时未屏蔽最后一个值,则它将它将看起来非常明亮。现象,因此将屏蔽子命令添加到了显示器的背面,从而最终解决了该问题。
2.当用户按下按钮时,微控制器读取的值与设置值不同。
解决方案:检查一下键盘的连接,并建立新的连接。
4.3 调试结果
测试中如果未显示LCD液晶,需要先使用测试仪测试电路,以观察是否存在漏焊,虚焊或组件损坏。
搜索了一系列问题后,系统最终可以正常工作并完成所有功能。
4.4 调试结果展示
在这里插入图片描述在这里插入图片描述

图4-2 实物图 图4-3 开机后画面
在这里插入图片描述

图4-4 录音状态 图4-5 停止状态
在这里插入图片描述

图4-6 模式选择 图4-7 放音状态

结 论

通过以上分析,此次项目使用单片机作为控制器,实现了设计的所有功能,传统的语言录放设备,要将接收到的语音信号转换为电信号,再将这个信号放大,然后再经过模数转换转换为数字信号,最后再进行处理和存储,放音时还需要数模转换转换成模拟信号,才能够达到放音的目的,这种方法听起来就非常复杂,在实际的制作过程中,还会使声音失真。所以此次项目采用51单片机来控制语音芯片的方法,也熟悉了ISD4004的基本应用,整个系统是采用51单片机对语音芯片的控制,完成了语音的录制和播放,并且描述了整个系统工作流程,各个部件的性能。这个系统不但成本非常低,而且使用起来灵活多变,抗干扰能力十分强,使用起来效果也十分优秀。
本课题的重点、难点总结:
(1)初次接触语音芯片,在使用过程中对各种寄存器的操作不太熟悉;
(2)在播放电路中,语音扩展,抗干扰分析过程应熟悉;
(3)注意调节电路的实现和程序的稳定性。通过本设计,我已经掌握了一些常用的基础理论知识,并对微控制器和PC编程控制的开发和应用有了更深入的了解。

参考文献

[1]彭伟.单片机C语言程序设计实训100例[J].电子工业出版社.2009
[2]金俊甫,陈伯亨.浅谈单片机原理及应用[J].风景名胜,2019,(10)
[3]吴运昌.模拟电子线路基础[M].广州:华南理工大学出版社,2004
[4]高铭阳.单片机的原理及接口技术[J].电子技术与软件工程,2017,(4)
[5]阎石.数字电子技术基础[M].北京:高等教育出版社,1997
[6]张晓丽等.数据结构与算法[J].北京:机械工业出版社,2002
[7]谭文娣,梁德坚,刘玉琼.ISD4004系列语音芯片批量录音实现[J].企业科技与发展,2009,(20)
[8]胡玉庆,舒朝君,陈红.基于ISD4004的电子录放系统设计[J].北京电力高等专科学校学报,2009,(4)
[9]李建忠.单片机原理及应用[J].西安:西安电子科技大学,2002
[10]汪惠毅,马民康.AT89C51单片机的应用[J].工业计量,1997,(6)
[11]韩志军等.单片机应用系统设计[M].机械工业出版社,2004
[12]周润景等.Proteus在MCS-51&ARM7系统中的应用百例[M].电子工业出版社,2006
[13]韦君玉,谢超,莫韦蓬,樊晋杰,柏亚萌,杨扬.基于LM386的应用设计电路[J].通讯世界,2016,(1)
[14] 帅江华,李志宜.浅谈LM386功放电路在实践教学中的应用[J].无线互联科技,2016,(24)
[15]刘树中,孙书膺,王春平.单片机和液晶显示驱动器串行接口的实现[J].微计算机信息,2007
[16]阎志宽.LM386功放IC的使用方法[J].家庭电子,2003,(8)
[17]陈海峰,丁丽丽.基于CPLD的LCD1602显示系统设计与实现[J].电子设计工程,2015,(10)
[18]岂兴明.51单片机编程基础与开发实例详解[M].北京:人民邮电出版社,2008
[19]马忠梅等.单片机的C语言应用程序设计[M].北京航空航天大学出版社,2006
[20]童诗白,华成英.模拟电子技术基础[M].北京:高等教育社,2006

附录 源程序清单

/ISD4004语音播放部分/

#include <reg52.h>

#include “LCD1602.h”
#include “ISD4004.h”
#include “key.h”
unsigned int a,b;
unsigned char flag=0;
void main(void)
{
LCDInit();
LCDDispString(1,1,“No1 Recording”);
flag = 0;
EX0=1; //开外部中断0
IT0=1; //触发方式设置为下降沿
IE0=1; //开外部中断0
EA=1; //开总中断
while(1)
{
key_scan();
}
}

void timer0(void) interrupt 0 //计数器0中断
{
if(FunctionCount == 5 && ModeCount1) //单个曲循环
{
if(NowRec == 0 ) //第一首歌曲
{ LCDDispString(1,2," Record 1 “);play(ADDR1STR);}
if(NowRec == 1) //第2首歌曲
{ LCDDispString(1,2,” Record 2 “);play(ADDR2STR);}
if(NowRec == 2) //第3首歌曲
{ LCDDispString(1,2,” Record 3 “);play(ADDR3STR);}
if(NowRec == 3) //第4首歌曲
{ LCDDispString(1,2,” Record 4 ");play(ADDR4STR);}
}
else if(FunctionCount == 5 && ModeCount
2)//全循环
{
if(NowRec == 0) //第一首歌曲
{ LCDDispString(1,2," Record 1 “);NowRec=1;play(ADDR1STR);}
for(a = 2000 ; a>0 ; a–)
for(b = 110; b>0 ; b–);
if( NowRec == 1) //第2首歌曲
{ LCDDispString(1,2,” Record 2 “);NowRec=2;play(ADDR2STR);}
for(a = 2000 ; a>0 ; a–)
for(b = 110; b>0 ; b–);
if(NowRec == 2) //第3首歌曲
{ LCDDispString(1,2,” Record 3 “);NowRec=3;play(ADDR3STR);}
for(a = 2000 ; a>0 ; a–)
for(b = 110; b>0 ; b–);
if(NowRec == 3) //第4首歌曲
{ LCDDispString(1,2,” Record 4 “);NowRec=0;play(ADDR4STR);}
for(a = 2000 ; a>0 ; a–)
for(b = 110; b>0 ; b–);
}
}
#include"ISD4004.h”
#include <intrins.h>

void delayms(unsigned char ms) // 延时子程序
{
unsigned char j;
while(ms–)
{
for(j = 0; j < 120; j++);
}
}

void play(unsigned int addr)
{
unsigned int y;
SS=0;
MOSI=0;//发送开始
SCLK=0;
for(y=0;y<8;y++)//上电
{
SCLK=0;
if((ISDPOWERUP>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}//发送结束
SS=1;//上电结束
delayms(50);
SS=0;
MOSI=0;//发送地址
SCLK=0;
for(y=0;y<16;y++)
{
SCLK=0;
if((addr>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}//发送地址结束
MOSI=0;//放音
SCLK=0;
for(y=0;y<8;y++)//从指定地址开始放音
{
SCLK=0;
if((PLAYSET>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}
SS=1;
SS=0;
MOSI=0;//放音
SCLK=0;
for(y=0;y<8;y++)//从当前地址开始放音
{
SCLK=0;
if((PLAYCUR>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}
SS=1;
}
void rec(unsigned int addr)
{
unsigned int y;
SS=0;
MOSI=0;//发送开始
SCLK=0;
for(y=0;y<8;y++)
{
SCLK=0;
if((ISDPOWERUP>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}
SS=1;//上电结束
delayms(50);
SS=0;
MOSI=0;//发送开始
SCLK=0;
for(y=0;y<8;y++)
{
SCLK=0;
if((ISDPOWERUP>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}//发送结束
SS=1;//上电结束
delayms(50);
delayms(50);
SS=0;
MOSI=0;//发送地址
SCLK=0;
for(y=0;y<16;y++)
{
SCLK=0;
if((addr>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}//发送地址结束
MOSI=0;
SCLK=0;
for(y=0;y<8;y++) //从指定地址开始录音
{
SCLK=0;
if((0xa0>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}
SS=1;
SS=0;
MOSI=0;
SCLK=0;
for(y=0;y<8;y++) //从当前地址开始录音
{
SCLK=0;
if((0xb0>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}
SS=1;
}



void stop()
{
unsigned char y;
SS=1;
SS=0;
MOSI=0;
SCLK=0;
for(y=0;y<8;y++)//停止当前操作
{
SCLK=0;
if((OPERSTOP>>y)&0x01)MOSI=1;
else MOSI=0;
nop();
nop();
nop();
SCLK=1;
nop();
nop();
nop();
}
SS=1;
}
#include<reg52.h>
#include"key.h"
#include"lcd1602.h"
#include"ISD4004.h"
unsigned char FunctionCount=0;
unsigned char RecordCount=0;
unsigned char ModeCount=0;
unsigned char NowRec=0;
unsigned int CountRed[4]={0};
//毫秒延时函数
void Keydelay(unsigned int z)
{
unsigned int x,y;
for(x = z ; x>0 ; x–)
for(y = 110; y>0 ; y–);
}

void key_scan(void)
{
if(key_1 == 0) //判断第一个按键情况
{
Keydelay(10);
if(key_1 == 0) //按键一
{
FunctionCount++;
if(FunctionCount == 1)
{LCDDispString(1,1,“No2 Recording”);LCDDispString(1,2," “);}
else if(FunctionCount == 2)
{LCDDispString(1,1,“No3 Recording”);LCDDispString(1,2,” “);}
else if(FunctionCount == 3)
{LCDDispString(1,1,“No4 Recording”);LCDDispString(1,2,” “);}
else if(FunctionCount == 4)
{LCDDispString(1,1,” Play Mode “);
if(ModeCount == 0)
LCDDispString(1,2,” Mode A “);
else if(ModeCount == 1)
LCDDispString(1,2,” Mode B “);
else if(ModeCount == 2)
LCDDispString(1,2,” Mode C “);
}
else if(FunctionCount == 5)
{ LCDDispString(1,1,” Play “);LCDDispString(1,2,” “); }
else if(FunctionCount == 6)
{
FunctionCount = 0;LCDDispString(1,1,“No1 Recording”);LCDDispString(1,2,” ");
}

		while(!key_1);
	}
}
if(key_2 == 0)			   //按键2
{
	Keydelay(10);
	if(key_2 == 0)
	{
		
		if(FunctionCount == 0)
		{
			LCDDispString(1,2,"   Start      ");
			rec(ADDR1STR);
		}
		else if(FunctionCount == 1)
		{
			LCDDispString(1,2,"   Start      ");
			rec(ADDR2STR);
		}
		else if(FunctionCount == 2)
		{
			LCDDispString(1,2,"   Start      ");
			rec(ADDR3STR);
		}
		else if(FunctionCount == 3)
		{
			LCDDispString(1,2,"   Start      ");
			rec(ADDR4STR);
		}
		else if(FunctionCount == 4)
		{
			 ModeCount++;
			 if(ModeCount == 1)
				 LCDDispString(1,2,"   Mode B    ");
			 else if(ModeCount == 2)
				 LCDDispString(1,2,"   Mode C    ");
			 else if(ModeCount == 3)
			 {ModeCount = 0;LCDDispString(1,2,"   Mode A    ");}
				 
		}
		else if(FunctionCount == 5)
		{
			 LCDDispString(1,2,"   Record 1    ");
			 play(ADDR1STR);
			 NowRec = 0;
			 if(ModeCount==1) //单曲循环
			 {
			 }
		}
		while(!key_2);
	}
}
if(key_3 == 0)			   //按键3 
{
	Keydelay(10);
	if(key_3 == 0)
	{	
		stop();
		if(FunctionCount == 0)
		{LCDDispString(1,2,"    Stop      ");}
		else if(FunctionCount == 1)
		{LCDDispString(1,2,"    Stop      ");}
		else if(FunctionCount == 2)
		{LCDDispString(1,2,"    Stop      ");}
		else if(FunctionCount == 3)
		{LCDDispString(1,2,"    Stop      ");}
		else if(FunctionCount == 5)
		{LCDDispString(1,2,"    Stop      ");}
		while(!key_3);
	}
}	

if(key_4 == 0)			   //按键4
{
	Keydelay(10);
	if(key_4 == 0)
	{	
		if(FunctionCount == 5 ) //单曲播放
		{
			NowRec ++;
			if(NowRec == 1)
			{LCDDispString(1,2,"   Record 2    ");play(ADDR2STR);}	
			else if(NowRec == 2)
			{LCDDispString(1,2,"   Record 3    ");play(ADDR3STR);}
			else if(NowRec == 3)
			{LCDDispString(1,2,"   Record 4    ");play(ADDR4STR);}
			else if(NowRec == 4)
			{NowRec=0;LCDDispString(1,2,"   Record 1    ");play(ADDR1STR);}	
		}
		while(!key_4);
	}
}

}
#include<reg52.h>
#include"lcd1602.h"

/*********************************************************
Function: void DelayN40us(unsigned int n)
Description: 延时函数,延时40us的整数倍,传入值n代表延时
时间到n *40us,不能实现准确延时,只能实现大
概时间的延时,利用的是指令实现延时功能。
Calls: 没有调用其它函数
Called By: void LCDWriteCommand(unsigned char dat);
void LCDWriteData(unsigned char dat);
Input: unsigned int n — 用于延时时间的确定,取值、
为1~65535
Output: 无
Return: 无
**/
void DelayN40us(unsigned int n)
{
unsigned int i;
unsigned char j;
for(i=n;i>0;i–)
for(j=0;j<2;j++);
}
/

Function: void LCDInit(void);
Description: 液晶初始化函数,对LCD1602进行初始化操作,
在每一次实用液晶前必须用到的,外部可调用
函数。
Calls: void LCDWriteCommand(unsigned char dat);
DelayN40us(unsigned int n);
Called By: main();或其他
Input: 无
Output: 无
Return: 无
**/
void LCDInit(void)
{
LCDWriteCommand(0x38);/设置8位格式,2行,5x7/
LCDWriteCommand(0x38);/设置8位格式,2行,5x7/
LCDWriteCommand(0x38);/设置8位格式,2行,5x7,有的液晶做的不好,写三遍保险!!!!/
LCDWriteCommand(0x0c);/整体显示,关光标,不闪烁/
LCDWriteCommand(0x06);/设定输入方式,增量不移位/
LCDWriteCommand(0x01);/清除屏幕显示/
DelayN40us(100); /清屏延时/
}
/

Function: void LCDWriteCommand(unsigned char dat);
Description:
Calls: DelayN40us(unsigned int n);
Called By: void LCDInit(void);
void LCDDispChar(unsigned char x,unsigned char y,unsigned char dat);
void LCDDispNum(unsigned char x,unsigned char y,unsigned char dat);
Input: unsigned char dat — 写入的命令
Output: 无
Return: 无
**/
void LCDWriteCommand(unsigned char dat)
{
LCD_DB=dat;
LCD_RS=0;/选择写指令/
LCD_RW=0;/写入指令值/
LCD_E=1; /使能LCD1602/
LCD_E=0;
DelayN40us(1);/写命令延时/
}
/

Function: void LCDWriteData(unsigned char dat);
Description: 写数据函数,将数据写入LCD1602中。
Calls: DelayN40us(unsigned int n);
Called By: void LCDInit(void);
void LCDDispChar(unsigned char x,unsigned char y,unsigned char dat);
void LCDDispNum(unsigned char x,unsigned char y,unsigned char dat);
Input: unsigned char dat — 写入的数据
Output: 无
Return: 无
**/
void LCDWriteData(unsigned char dat)
{
LCD_DB=dat;
LCD_RS=1;/选择写数据/
LCD_RW=0;/写入数据值/
LCD_E=1; /使能LCD1602/
LCD_E=0;
DelayN40us(1);/写数据延时/
}
/

Function: void LCDDispChar(unsigned char x,unsigned char y,unsigned char dat);
Description:
Calls: LCDWriteCommand(unsigned char dat);
void LCDWriteData(unsigned char dat);
Called By: main();及其他
Input: X(0-15),y(1-2),dat为显示字符‘a’形式。
Output: 无
Return: 无
**/
void LCDDispChar(unsigned char x,unsigned char y,unsigned char dat)
{
unsigned char address;
if(y==1)
address=0x80+x;
else
address=0xc0+x;
LCDWriteCommand(address); /输入地址/
LCDWriteData(dat); /输入数据/
}
/

Function: void LCDDispNum(unsigned char x,unsigned char y,unsigned char dat);
Description:
Calls: LCDWriteCommand(unsigned char dat);
void LCDWriteData(unsigned char dat);
Called By: main();及其他
Input: X(0-15),y(1-2),dat(0-9)。
Output: 无
Return: 无
**/
void LCDDispNum(unsigned char x,unsigned char y,unsigned char dat)
{
unsigned char address;
if(y==1)
address=0x80+x;
else
address=0xc0+x;
LCDWriteCommand(address); /输入地址/
LCDWriteData(dat+48); /输入数据/
}
/

Called By: main();及其他
Input: X(0-15),y(1-2)
Output: 无
Return: 无
*****/
void LCDDispString(unsigned char x,unsigned char y,unsigned char string)
{
while(string != 0) /没有检测到字符串结尾时/
{
LCDDispChar(x,y,string++);/逐个显示字符串/
x++; /行地址加1/
}
}
/

Function: void LCDDispNumInt(unsigned char x,unsigned char y,unsigned int dat);
Description: 在某个屏幕位置上(x,y)位置显示一个无符号整形数字,可外部调用。
Calls: LCDDispNum(unsigned char x,unsigned char y,unsigned char dat);
Called By: main();及其他
Input: X(0-15),y(1-2),dat(0-65536)。
Output: 无
Return: 无
***********************************************************/
void LCDDispNumInt(unsigned char x,unsigned char y,unsigned int dat)
{
unsigned char temp[5],i = 0,j=0;/分离万千百十个各个位临时存储/
temp[0] = dat/10000%10;
temp[1] = dat/1000%10;
temp[2] = dat/100%10;
temp[3] = dat/10%10;
temp[4] = dat%10;

while((temp[i] == 0)&&(i!=4))
	i++;
for(j=0;j<5-i;j++)			  /*依次显示各个数*/
	LCDDispNum(x+j,y,temp[j+i]);

}

致 谢

在本次毕业设计当中,首先要感谢的就是李作纪老师和陈乃周老师,二位老师在我毕业设计阶段给予我的帮助不计其数,从最开始的选题,到如何收集资料、如何让作品达到要求,再到哪里书写有错误,如何修改,和最后的论文初稿、终稿,两位老师给了我很多很多耐心的指导以及无私的帮助。在这段时间里,尤其当下这种疫情的关键时期,我真正的体会到了“春蚕到死丝方尽,蜡炬成灰泪始干”这种无私奉献的敬业精神。为了指导我们完成此次毕业论文,李作纪老师和陈乃周老师几乎放弃了自己所有的休息时间,在此我向二位老师表达我最诚挚的谢意。在这四年的大学时光里,老师和同学们给予我不计其数的帮助,是他们教会了我专业知识,教会了我如何做人,教会我学习方法,在此也要感谢所有的科任老师和所有同学,没有他们就没有我的进步。向他们表示我由衷的谢意,最后祝愿所有老师培养出越来越多的优秀的人才,桃李满天下,也希望各位老师多注意休息,在以后的日子里每天都健康快乐。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于深度学习YOLOv8+Pyqt5的抽烟吸烟检测识别系统(源码+跑通说明文件)

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;39抽烟 获取完整源码源文件4000张已标注的数据集配置说明文件 可有偿59yuan一对一远程操作跑通 效果展示 基于深度学YOLOv8PyQt5的抽烟吸烟检测识别系统&#xff08;完整源码跑通说明文件&#xff09; 各文件说明 模型评价…

Seurat 中的数据可视化方法

本文[1]将使用从 2,700 PBMC 教程计算的 Seurat 对象来演示 Seurat 中的可视化技术。您可以从 SeuratData[2] 下载此数据集。 SeuratData::InstallData("pbmc3k")library(Seurat)library(SeuratData)library(ggplot2)library(patchwork)pbmc3k.final <- LoadData(…

【机器学习300问】31、不平衡数据集如何进行机器学习?

一、什么是不平衡的数据集&#xff1f; &#xff08;1&#xff09;认识不平衡数据 假如你正在管理一个果园&#xff0c;这个果园里主要有两种水果——苹果和樱桃。如果苹果树有1000棵&#xff0c;而樱桃树只有10棵&#xff0c;那么在收集果园的果实时&#xff0c;你会得到大量…

RocketMQ架构详解

文章目录 概述RocketMQ架构rocketmq的工作流程Broker 高可用集群刷盘策略 概述 RocketMQ一个纯java、分布式、队列模型的开源消息中间件&#xff0c;前身是MetaQ&#xff0c;是阿里研发的一个队列模型的消息中间件&#xff0c;后开源给apache基金会成为了apache的顶级开源项目…

全栈的自我修养 ———— css中常用的布局方法flex和grid

在项目里面有两种常用的主要布局:flex和grid布局&#xff08;b站布局&#xff09;&#xff0c;今天分享给大家这两种的常用的简单方法&#xff01; 一、flex布局1、原图2、中心对齐3、主轴末尾或者开始对其4、互相间隔 二、grid布局1、基本效果2、加间隔3、放大某一个元素 一、…

Nginx请求转发和Rewrite的URL重写及重定向的功能实现移动端和PC端前端服务转发和重定向配置。

应用场景说明一 应用系统分pc端和微信小程序&#xff0c;移动端和pc端分别申请二级子域名&#xff0c;通过Nginx域名解析匹配&#xff0c;将web访问统一转发至对应的域名请求中。部分配置如下所示&#xff1a; 1、WEB访问统一入口域名解析转发配置&#xff0c;PC端和移动端根域…

【论文整理】自动驾驶场景中Collaborative Methods多智能体协同感知文章创新点整理

Collaborative Methods F-CooperV2VNetWhen2commDiscoNetAttFusionV2X-ViTCRCNetCoBERTWhere2commDouble-MCoCa3D 这篇文章主要想整理一下&#xff0c;根据时间顺序这些文章是怎么说明自己的创新点的&#xff0c;又是怎么说明自己的文章比别的文章优越的。显然似乎很多文章只是…

数据结构与算法:链式二叉树

上一篇文章我们结束了二叉树的顺序存储&#xff0c;本届内容我们来到二叉树的链式存储&#xff01; 链式二叉树 1.链式二叉树的遍历1.1二叉树的前序&#xff0c;中序&#xff0c;后序遍历1.2 三种遍历方法代码实现 2. 获取相关个数2.1获取节点个数2.2获取叶节点个数2.3 获取树的…

前端请求到 SpringMVC 的处理流程

1. 发起请求 客户端通过 HTTP 协议向服务器发起请求。 2. 前端控制器&#xff08;DispatcherServlet&#xff09; 这个请求会先到前端控制器 DispatcherServlet&#xff0c;它是整个流程的入口点&#xff0c;负责接收请求并将其分发给相应的处理器。 3. 处理器映射&#xf…

数据库-多表查询

外连接与内连接 -- 查询部门及所属部门名称&#xff0c;隐式内连接 select tb_emp.name,tb_dept.name from tb_emp,tb_dept where tb_emp.dept_idtb_dept.id;-- 起别名 select e.name,q.name from tb_emp e,tb_dept q where e.dept_idq.id;-- 外连接 select tb_emp.name,tb_dep…

GEE图像可视化常用函数

目录 图层操作Map.addLayer&#xff08;&#xff09;Map.centerObject&#xff08;&#xff09; 直方图ui.Chart.image.histogram&#xff08;&#xff09; 趋势线ui.Chart.image.series&#xff08;&#xff09; 图层操作 Map.addLayer&#xff08;&#xff09; Map.addLaye…

python并发编程:异步IO(Asynchronous I/O)

异步IO(Asynchronous I/O) Linux下的asynchronous IO其实用得不多&#xff0c;从内核2.6版本才开始引入。先看一下它的流程&#xff1a; 用户进程发起read操作之后&#xff0c;立刻就可以开始去做其它的事。而另一方面&#xff0c;从kernel的角度&#xff0c;当它受到一个asyn…

RocketMQ、Kafka、RabbitMQ 消费原理,顺序消费问题【图文理解】

B站视频地址 文章目录 一、开始二、结果1、RocketMQ 消费关系图1-1、queue和consumer的关系1-2、consumer 和线程的关系 2、Kafka 消费关系图1-1、partitions和consumer的关系1-2、consumer 和线程的关系 3、RabbitMQ 消费关系图1-1、queue和consumer的关系1-2、consumer 和线程…

爬虫练习:获取某招聘网站Python岗位信息

一、相关网站 二、相关代码 import requests from lxml import etree import csv with open(拉钩Python岗位数据.csv, w, newline, encodingutf-8) as csvfile:fieldnames [公司, 规模,岗位,地区,薪资,经验要求]writer csv.DictWriter(csvfile, fieldnamesfieldnames)writer…

每日OJ题_牛客WY28 跳石板(动态规划)

目录 牛客WY28 跳石板 解析代码 牛客WY28 跳石板 跳石板_牛客题霸_牛客网 解析代码 #include <iostream> #include <vector> #include <climits> #include <cmath> using namespace std;void get_div_num(int n, vector<int>& arr) {for…

selenium元素定位问题

具体网页信息如下&#xff1a; 定位的时候driver.find_element(By.CLASS_NAME, 方法搞不定。 定位方法&#xff1a; 方法一&#xff1a;通过文本定位 driver.find_element(By.XPATH, "//*[text()高分一号]").click() time.sleep(3) 如果是部分文字 #部分文字py…

怎么写品牌方流量打造抖音运营规划方案

【干货资料持续更新&#xff0c;以防走丢】 怎么写品牌方流量打造抖音运营规划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 抖音运营资料合集&#xff08;完整资料包含以下内容&#xff09; 目录 Step 1: 人货沟通策略 人群定位与细分 1. 从品牌及产品…

【备战蓝桥杯系列】蓝桥杯国二选手笔记二:算法模版笔记(Java)

感谢大家的点赞&#xff0c;关注&#xff0c;评论。准备蓝桥杯的同学可以关注一下本专栏哦&#xff0c;不定期更新蓝桥杯笔记以及经验分享。本人多次参加过蓝桥杯&#xff0c;并获得过蓝桥杯国二的成绩。 算法模版笔记&#xff08;Java&#xff09; 这篇文章给大家分享我的蓝桥…

寒假作业Day 10

寒假作业Day 10 一、选择题 1、下列数据结构中&#xff0c;不属于线性表的是( ) A.队列 B.顺序表 C.二叉树 D.链表 A. 队列&#xff1a;队列是一种特殊的线性表&#xff0c;它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08…

【经管数据-更新】华证ESG评级得分数据(2009-2023年)

一、数据说明 参考《经济研究》中方先明&#xff08;2023&#xff09;的做法&#xff0c;将华证ESG评级进行赋值&#xff0c;指标包含C、CC、CCC、B、BB、BBB、A、AA、AAA共9个等级&#xff0c;将上市公司ESG 等级从低到高分别赋值为1至9 二、数据来源&#xff1a;世界银行&am…