找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 17255|回复: 5
打印 上一主题 下一主题
收起左侧

单片机简易函数信号发生器设计课程设计(论文)

[复制链接]
跳转到指定楼层
楼主
辽宁工业
单片机原理及接口技术课程设计(论文)
题目:简易函数信号发生器设计
院(系): 电气工程学院
专业班级:自动化134
学    号:130*2115
学生姓名:宋*鹏
指导教师:(签字)
课程设计(论文)任务及评语
院(系):电气工程学院                                           教研室:自动化
学 号
130*115
学生姓名
宋*鹏
专业班级
自动化134班
课程设计(论文)题目
简易信号发生器设计
课程设计(论文)任务
课题完成的功能、设计任务及要求、技术参数
实现功能
(1)设计一个基于单片机的信号发生器;
(2)能够输出四种波形,用按键控制输出正弦波、三角波、方波、锯齿波,输出波形时分别对应指示灯亮;
(3)能够通过按键进行波形参数设置;
(4)能够通过显示模块输出波形的主要参数,如频率。
设计任务及要求
1、分析系统功能,选择控制方案,绘制系统组成框图;
2、设计系统的硬件电路,包括单片机最小系统、指示灯电路、键盘电路和显示电
路以及完整的电路,并进行实物的制作;
3、编写相应的软件,写完控制系统的控制要求,并编写完整程序;
4、上机调试、完善程序;
5、按学校规定格式,撰写、打印设计说明书一份;设计说明书应在4000字以上。
技术参数
波形幅值5V,频率1Hz-50Hz之间。
进度计划
1、布置任务,查阅资料,确定系统设计方案(2天)
2、系统硬件设计及实物制作(3天)
3、系统软件设计及编写功能程序及调试(3天)
4、撰写、打印设计说明书(1天)
5、验收及答辩(1天)
指导教师评语及成绩
平时:论文质量:答辩:
总成绩:                         指导教师签字:
                                                年    月    日
注:成绩:平时20%   论文质量60%   答辩20%  以百分制计算

摘 要
本课题是采用低成本的MCS-51系列单片机构成具有高可靠性的函数信号发生器的应用设计,采用具有51内核的STC12C5A60S2单片机芯片通过单片机运算产生二进制控制信号去控制有 总线结构并且同时具有AD/DA功能的 PCF8591集成电路产生正弦波、锯齿波、三角波、梯形波4种函数波形。
本系统以51单片机为控制核心,由电源模块、单片机最小系统模块、中断键盘模块、函数信号发生模块、显示模块组成。通过加入按键电路来切换不同波形的输出同时用相应的指示灯来表示,并且通过设置按键来改变频率。在波形和频率改变的同时通过形参之间的传递将此时刻的波形类型和频率大小通过液晶LCD12864来显示其参数的改变状态。
用其示波器采集观察和检测波形的输出,制作的实物电路简单、操作方便、运行稳定可靠、抗干扰性强、功耗低、成本低、易实现、其频率值精准、对采用总线结构设计信号发生器具有参考价值。


目 录
第1章 绪论
第2章 课程设计的方案
2.1 概述
2.2 系统组成总体结构
第3章 硬件设计
3.1 单片机最小系统设计
3.2 按键电路
3.3 信号发生电路
3.4 显示电路
第4章 软件设计
4.1 系统总流程
4.2 系统子流程图
第5章 系统测试与分析
5.1 波形种类测试与分析
5.2 频率大小测试与分析
5.3 实物焊接与整体效果展示
第6章 课程设计总结
参考文献
附录Ⅰ系统总原理图
附录Ⅱ程序代码
第1章      绪论
近年来随着计算机在社会领域的渗透,单片机的应用正在不断地走向深入,同时带动传统函数信号发生器日新月益更新。传统的函数信号发生器大多数采用了模拟锁相环、数字锁相环、小数分频锁相环(fractional-N PLL Synthesis)技术,但是随着科技的进步,出现了直接数字合成(Direct Digital Synthesis-DDS)的FS技术。单片集成的DDS产品是一种可代替锁相环的快速频率合成器件。DDS是产生高精度、快速变换频率、输出波形失真小的优先选用技术。DDS以稳定度高的参考时钟为参考源,通过精密的相位累加器和数字信号处理,通过高速D/A变换器产生所需的数字波形,这个数字波经过一个模拟滤波器后,得到最终的模拟信号波形。通过高速DAC产生数字正弦数字波形,通过带通滤波器后得到一个对应的模拟正弦波信号。在人们认识自然、改造自然的过程中,经常需要对各种各样的电子信号进行测量,因而如何根据被测量电子信号的不同特征和测量要求,灵活、快速的选用不同特征的信号源成了现代测量技术值得深入研究的课题。信号源主要给被测电路提供所需要的已知信号(各种波形),然后用其它仪表测量感兴趣的参数。可见信号源在各种实验应用和实验测试处理中,它不是测量仪器,而是根据使用者的要求,作为激励源,仿真各种测试信号,提供给被测电路,以满足测量或各种实际需要。波形发生器就是信号源的一种,能够给被测电路提供所需要的波形。传统的波形发生器多采用模拟电子技术,由分立元件或模拟集成电路构成,其电路结构复杂,不能根据实际需要灵活扩展。随着微电子技术的发展,运用单片机技术,通过巧妙的软件设计和简易的硬件电路,产生数字式的正弦波、方波、三角波、锯齿等幅值可调的信号。与现有各类型波形发生器比较而言,产生的数字信号干扰小,输出稳定,可靠性高,特别是操作简单方便。
函数发生器亦称信号发生器,主要作为实验用信号源,是现今各种电子电路实验设计应用中必不可少的仪器设备之一。目前,市场上常见的波形发生器多为纯硬件的搭接而成,波形种类多为锯齿、正弦、方波、三角等波形。用分立元件组成的函数发生器,通常是单函数发生器且频率不高,其工作不很稳定,不易调试;用集成芯片的函数发生器,可达到较高的频率和产生多种波形信号,但电路较为复杂且不易调试。利用单片集成芯片的函数发生器,能产生多种波形,达到较高的频率,且易于调试;利用专用直接数字合成DDS 芯片的函数发生器,能产生任意波形并达到很高的频率,但成本较高。
函数发生器作为一种常见的应用电子仪器设备,传统的一般可以完全由硬件电路搭接而成,如采用555振荡电路发生正弦波、三角波和方波的电路便是可取的路径之一,不用依靠单片机。但是这种电路存在波形质量差,控制难,可调范围小,电路复杂和体积大等缺点。在科学研究和生产实践中,如工业过程控制,生物医学,地震模拟机械振动等领域常常要用到低频信号源。而由硬件电路构成的低频信号其性能难以令人满意,而且由于低频信号源所需的RC要很大。大电阻,大电容在制作上有困难,参数的精度亦难以保证。体积大,漏电,损耗显著更是其致命的弱点。一旦工作需求功能有增加,则电路复杂程度会大大增加。
利用单片机采用程序设计方法来产生低频信号,其频率底线很低。具有线路相对简单,结构紧凑,价格低廉,频率稳定度高,抗干扰能力强,用途广泛等优点,并且能够对波形进行细微调整,改良波形,使其满足系统的要求。只要对电路稍加修改,调整程序,即可完成功能升级。


第2章      课程设计的方案2.1   概述
本次设计主要是综合应用所学知识,设计简易函数信号发生器,能够产生正弦波、锯齿波、三角波、梯形波4种函数波形。并在实践的基本技能方面进行一次系统的训练。能够较全面地巩固和应用“单片机”课程中所学的基本理论和基本方法,并初步掌握小型单片机系统设计的基本方法。
应用场合:在人们认识自然、改造自然的过程中,经常需要对各种各样的电子信号进行测量,因而如何根据被测量电子信号的不同特征和测量要求,灵活、快速的选用不同特征的信号源成了现代测量技术值得深入研究的课题。信号源主要给被测电路提供所需要的已知信号(各种波形),然后用其它仪表测量感兴趣的参数。
系统功能介绍:通过按键切换不同波形的输出同时相应的指示灯亮,并且通过设置按键调整延时函数的时间达到调节信号频率。
2.2   系统组成总体结构
本次设计系统如图2.1所示,以51单片机为控制核心,由电源模块、单片机最小系统模块、中断键盘模块、函数信号发生模块、显示模块组成。





图2.1 控制结构框图
第3章      硬件设计3.1   单片机最小系统设计
单片机最小系统或者称为最小应用系统,是指用最少的元件组成的单片机可以工作的系统。对51系列单片机来说,最小系统一般包括:单片机、晶振电路、复位电路、电源电路。
3.1.1  单片机的选择
根据需要本次设计选择了一片与51系列兼容的STC12C5A60S2,其引脚与STC89C52相同,图中用其89C52代替,其引脚图如图3.1所示。



图3.1 CPU引脚图

3.1.2  晶振电路
晶振电路:典型的晶振取11.0592MHz(因为可以准确地得到9600波特率和19200波特率,用于有串口通讯的场合)/12MHz(产生精确的us级时间,方便定时操作)其中与晶振并联的电容根据经验一般选取10pF~30pF,本次设计选取了11.0592MHz的晶振和20pF的电容,本次设计原理图中晶振两端的连线采用了网络标号的方法,可以使得电路比较清晰,模块化,易于观看,后面电路也是一样,将不再叙述,图中X1与单片机的19引脚相连,X2与18引脚连接,原理图如图3.2所示。


图3.2 晶振电路

3.1.3  复位电路
复位电路:由电容串联电阻构成,由“电容电压不能突变”的性质,可以知道,当系统一上电,RST脚将会出现高电平,并且这个高电平持续的时间由电路的RC值来决定。典型的51单片机当REST脚的高电平持续两个机器周期以上就将复位,所以适当组合RC的取值就可以保证可靠的复位。一般教科书推荐C 取10uF,R取10K。当然也有其他取法的,原则就是要让RC组合可以在RST脚上产生不少于2个机周期的高电平。至于如何具体定量计算,可以参考电路分析相关书籍。本次设计选取电容为10uF,电阻为10K。电路如图3.3所示。



图3.3 复位电路

3.1.4  电源电路
本次课程设计中,通过外来+5V电源,接一个六角开关,当开关按下时,电流流过R1,经过LED,此时表示电路导通,加入LED灯起了提示的作用,当电源指示灯亮时表示电源接通。六角开关下端,也就是R1的上端接线触为整个电路的VCC,图中VCC未画出,在此声明,以后将不再叙述,电源电路如图3.4所示。


图3.4 电源电路

3.2   按键电路
键盘从结构上分为独立式键盘与矩阵式键盘。一般按键较少时采用独立式键盘,按键较多时采用矩阵式键盘。本次设计只用三个按键,因此选取独立按键,矩阵按键没有用到故而将不介绍。
独立式键盘:在由单片机组成的测控系统及智能化仪器中,用的最多的是独立式键盘。这种键盘具有硬件与软件相对简单的特点,其缺点是按键数量较多时,要占用大量口线。图3.5是一个利用MCS-51单片机的P2口的低三位设计的编码键盘,其编码分别为P20、P21、P22。CPU对应的I/O接口外部接了上拉电阻原因跟灌电流大小有关,其实P2口内部也有上拉电阻,这里不冲突,当按键没按下时,其输入为高电平;当某键被按下后,对应的I/O接口变为低电平。只要在程序中判断I/O接口的状态,即可知道哪个键处于闭合状态。



图3.5 按键电路

3.3   信号发生电路
本次设计中选取PCF8591芯片作为DA转换,其原因是因为是它具有总线接口,这样大大节约了单片机的I/O口。本次设计芯片的接线如图3.6,采用了经典电路,其中芯片14引脚接5V基准电压。


图3.6 信号发生电路

总线是Philips公司推出的新型单片机系统。它采用串行总线,主控器与外围器件仅靠两条线进行信息传输,一条称为时钟线(SCL),另一条位数据线(SDA)。总线单片机系统较通用单片机系统电路简单。由普通CPU芯片同专用器件组成的系统为模拟系统,它性能稳定,价格较低,目前已得到广泛应用。
3.4   显示电路
本次设计中显示电路包括了两部分,一部分为液晶LCD1284显示其参数,另一部分为三个LED灯用来指示此时的波形种类。
3.4.1  液晶显示电路
设计中选用带中文字库的128X64作为参数显示,带中文字库的128X64是一种具有4位/8位并行、2线或3线串行多种接口方式,内部含有国标一级、二级简体中文字库的点阵图形液晶显示模块;其显示分辨率为128×64,内置8192个16*16点汉字,和128个16*8点ASCII字符集。利用该模块灵活的接口方式和简单、方便的操作指令,可构成全中文人机交互图形界面。可以显示8×4行16×16点阵的汉字,也可完成图形显示。低电压低功耗是其又一显著特点。
系统中当用按键来切换波形和调整频率的时候,用液晶LCD12864来显示此时波形的种类和频率值的大小,其引脚接线图如图3.7所示。


图3.7 液晶显示电路

3.4.2  LED显示电路
本次设计中当用其按键来切换波形时,除了在液晶12864上来显示参数,还用了这个LED显示电路,当切换到某一个波形时,与其相对应的小灯就会亮来提示现在是什么波形输出,方便的解决了没有用示波器采集时依然能准确判断出输出的波形的种类,其电路图如图3.8所示。



图3.8 LED显示电路

第4章      软件设计4.1   系统总流程
本设计上电或复位时,将默认输出10Hz的正弦波,并开始在主程序里一直扫描按键程序,当检测到有按键下,通过查询P2口状态,便知道是对应的哪个按键按下,并给按键赋上键值,赋完键值以后,立刻将P2口还原,以便下一次检测。然后通过查询键值便可知道是哪一个按键按下,以键值为标志,传递给相应的子程序,从而实现频率的增减和波形的切换,将频率的值和波形作为变量,再传递给相应的程序,从而可以实现在液晶LCD上显示此时的频率和波形,同样当波形改变时,赋上标志位,对应的标志位用来实现LED的显示,达到波形改变时对应的LED显示,其系统总流程图如图4.1所示。

图4.1 系统总流程图

4.2   系统子流程图
本次设计中,由上面总流程图中可以清晰的看出将程序进行了模块化,分为了几大部分,其中可以大致分为波形发生、按键扫描、LCD12864液晶显示、LED显示四部分组成。
4.2.1  波形发生
设计中采用PCF8591芯片作为D/A转换,芯片PCF8591与单片机通过总线与单片机相连,其地址端A2、A1、A0接地,基准电压接5V电压,其连线图具体见图3.6,开始时单片机通过在总线上发送一个器件地址,当PCF8591与总线上发送的这个地址相匹配时,通过总线给单片机发送一个应答信号,单片机识别到这个应答信号时将在总线上发送一个控制字,此时由于前一个地址已经与PCF8591相匹配,那么此时的控制字则用来控制PCF8591,使其进行相应的动作,当芯片收到控制字后再次给单片机发送一个应答信号表示收到,最后单片机通过总线发送数据,使其芯片进行工作。器件工作流程图如图4.2所示。


图4.2 波形产生流程图

4.2.2  按键扫描
设计中通过中断对按键进行扫描,当有按键下时,扫描程序中查询P2口状态,便可知道是对应的按键按下,并给按键赋上键值,赋完键值以后,立刻将P2口还原,以便下一次检测。然后通过查询键值,以键值为标志,传递给相应的子程序,从而实现频率的增减和波形的切换。其按键扫描流程图如图4.3所示。



图4.3 按键扫描流程图

4.2.3  参数显示
本设计当按键按下时除了给按键赋键值以外,还在按键按下时给程序中加入了表示频率参数的加减来和波形的切换,此时的频率加减对应着实际频率值,再将表示频率和波形参数的变量通过形参传递给液晶显示程序中的实参,从而实现了频率和波形在液晶上的显示。
与此同时,当按键按下时,对应的键值被赋值,在表示波形切换的那个赋值的键值程序中,我们进行了变量i++,当变量i=0时,实现正弦波输出,i=1时,三角波输出,i=2时锯齿波输出,i=3时,方波输出,然后将i清零,如此循环,进而实现了对波形的切换,当对应的i值改变时,令其对应的I/O口变为低电平,使得LED灯发光,从而实现了不同的LED发光表示了不同的波形,由于此节比较简单,流程图进行省略。
第5章      系统测试与分析5.1   波形种类测试与分析
通过示波器的采集,当上电复位时,观察到的波形如图5.1所示,此时没有按键按下,观察到此时的波形为正弦波,频率为10Hz,与设计中的相符合,然而当仔细观察时便可以看到正弦波并不平滑,而是有一格一格的小齿,这是因为在正弦波发生器时,一个周期只赋值了32个点,如果点数够多,将会使得波形更加平滑,但这并不影响,可以在后波形输出加上一个低通滤波电路,进而可以让其波形变得平滑可观,但是此设计中并没有包含滤波电路。当按下波形切换按键时,可观察到三角波、锯齿波、方波波形,对应图5.2,5.3,5.4。


图5.1 正弦波波形图



图5.2 三角波波形图




              图5.3 锯齿波波形图



                            图5.4 方波波形图

5.2   频率大小测试与分析
通过按键按下,可以调整波形频率大小的输出,此次测试中以正弦波为例,由图5.1正弦波波形图可以通过示波器测出此时的频率为10Hz,通过按键调整,可以得到如图5.5所示的频率35Hz和图5.6所示的频率50Hz,在图中可以看出与理论值大小只有很小很小的误差,这充分展示了本设计的合理性。



图5.5 35Hz方波波形图


              图5.6 50Hz方波波形图

5.3   实物焊接与整体效果展示
如图5.7与5.8展示了整体电路的显示效果与焊接。在图中可以通过LCD12864清晰的看到此时的波形为正弦波,频率值为10Hz,同样对应的红色小灯发光,作为此时为正弦波。





图5.7 实物显示效果



图5.8 实物焊接

第6章      课程设计总结
本次系统以设计一个频率可调,波形可调的信号发生器为目的。在设计中通过按键进行波形切换和频率改变,并且能够通过液晶显示其参数的变化,相应的有其指示灯来表示此时的波形种类。
在芯片选择上选择具有IIC总线的D/A转换芯片PCF8591进行数模转换,通过对总线的操作实现与单片机的通信。此芯片与单片机只进行了两个I/O口连接,大大节约了单片机的I/O口,在进行波形切换和频率改变上,采用了独立按键的选择,因为涉及的按键比较少,这样的选择直观,电路简单,程序也方便,显示电路中,选择了LCD12864对参数进行显示,虽然在成本上可能会高了些,不过让整个设计显得比较大气。
程序中将各个部分的子程序进行了模块化,使得程序清晰,条理清楚,便于修改,程序中巧妙的应用了形参和实参的传递将参数传递给子程序,实现了液晶显示和波形改变,频率改变时能够达到同步,在设计过程中,遇到的问题先独立思考,然后一顿百度,自己解决不了时再通过询问身边的师哥师姐得以解决。

参考文献
[1]陈贞. 简易函数信号发生器的设计与实现[J].武汉工程职业技术学院学报,2014,(03):75-78
[2]孙鹏. 51单片机综合学习系统——12864点阵型液晶显示篇[J].电子制作,2008,(02):22-24
[3]罗佰绥,熊小民,熊锴. 简易函数信号发生器与计数器设计[J].国外电子元器件,2008,(07):49-51
[4]张晓增,赵曰峰,董鸿江.低频信号发生器的设计[J].现代电子技术,2009,(06):1-3
[5]陶炳坤,石龙宇,黄天辰,高秀峰. 基于AT89S51和MAX038的函数信号发生器的设计[J].现代电子技术,2013,(09):165-167
[6]赵道新,李炳辰. 基于PCF8591的IIC总线多路温度测量系统[J].制造业自动化,2013,(05):36-37+55
[7]任晓芳. PCF8591在数据采集过程中的应用[J].自动化应用,2015,(10):42-43
[8]彭志刚. 基于PCF8591简易低频信号发生器的设计[J].自动化与仪器仪表,2015,(11):41-42+45
[9]柴西林,刘远聪,车玮. 基于STC89C52的低频信号发生器设计[J].自动化与仪器仪表,2016,(03):11-12+14
[10]罗俊杰. 单片机的简易信号发生器的设计[J].林区教学,2013,(11):79-80
[11]江世明. 12864图形液晶显示模块与51系列单片机接口技术[J].电子世界,2005,(06):35-36
[12]周剑利,郭建波,崔涛. 具有IIC总线接口的A/D芯片PCF8591及其应用[J].微计算机信息,2005,(07):150-151
[13]河北 梁廷贵. A/D芯片PCF8591原理及其应用[N]. 电子报,2005-10-23014
[14]辛阿阿,厉善亨. 基于12864液晶模块的动态波形显示实现方法[J].仪器仪表用户,2010,(05):56-57
[15]李金群. 基于51单片机的12864液晶图文显示研究[J].机电信息,2010,(36):139-140
附录Ⅰ
系统总原理图
附录Ⅱ
程序代码
程序设计
头文件<12864.h>

  1. #ifndef __12864_H__
  2. #define __12864_H__
  3. #define uint unsigned int
  4. #define uchar unsigned char
  5. void mainxianshi();
  6. void ready();
  7. void write_com(unsigned char com);
  8. void write_date(unsigned char date);
  9. void lcd_pos(unsigned char X,unsigned char Y);
  10. void init();
  11. void delay(unsigned int ms)        ;
  12. void main_sinwave();
  13. void main_triwave();
  14. void main_sawwave();
  15. void main_squwave();
  16. void main_freqwave(unsigned char i);
  17. #endif
  18. /******************************************/
复制代码


主函数main.c
单片机源程序如下:
  1. /* 可调频率的信号发生器
  2. :针对STC12系列单片机修改定时器中断延时补偿参数
  3. * 1、由按键切换不同波形,由上下键增减频率
  4. * 2、频率可调范围限定在1~50Hz,因受限于PCF8591访问速度而无法做到很高
  5. *****************************************************************/
  6. #include
  7. #include "12864.H"
  8. #include
  9. unsigned char code SinWave[] = {  //正弦波波表
  10.     127, 152, 176, 198, 217, 233, 245, 252,
  11.     255, 252, 245, 233, 217, 198, 176, 152,
  12.     127, 102,  78,  56,  37,  21,   9,   2,
  13.       0,   2,   9,  21,  37,  56,  78, 102,
  14. };
  15. unsigned char code TriWave[] = {  //三角波波表
  16.       0,  16,  32,  48,  64,  80,  96, 112,
  17.     128, 144, 160, 176, 192, 208, 224, 240,
  18.     255, 240, 224, 208, 192, 176, 160, 144,
  19.     128, 112,  96,  80,  64,  48,  32,  16,
  20. };
  21. unsigned char code SawWave[] = {  //锯齿波表
  22.       0,   8,  16,  24,  32,  40,  48,  56,
  23.      64,  72,  80,  88,  96, 104, 112, 120,
  24.     128, 136, 144, 152, 160, 168, 176, 184,
  25.     192, 200, 208, 216, 224, 232, 240, 248,
  26. };
  27. unsigned char code SquWave[] ={  //方波波表
  28.           0,   0,         0,         0,          0,   0,        0,         0,
  29.           0,   0,        0,         0,          0,   0,        0,         0,
  30.         255, 255, 255, 255, 255, 255, 255, 255,
  31.         255, 255, 255, 255, 255, 255, 255, 255,
  32. };

  33. unsigned char fWave = 10;   //波形频率
  34. unsigned char code *pWave;  //波表指针
  35. unsigned char T0RH = 0;  //T0重载值的高字节
  36. unsigned char T0RL = 0;  //T0重载值的低字节
  37. unsigned char T1RH = 1;  //T1重载值的高字节
  38. unsigned char T1RL = 1;  //T1重载值的低字节
  39. //void draw_point(uchar x,uchar y,uchar colour);
  40. void ConfigTimer0(unsigned int ms);
  41. void SetWaveFreq(unsigned char freq);
  42. extern void KeyScan();
  43. extern void KeyDriver();
  44. extern void I2CStart();
  45. extern void I2CStop();
  46. extern bit I2CWrite(unsigned char dat);
  47. sbit R_LED = P1^5;
  48. sbit Y_LED = P1^4;
  49. sbit G_LED = P1^3;

  50. void main()
  51. {   
  52.         R_LED = 0;
  53.         Y_LED = 0;
  54.         G_LED = 0;
  55.     EA = 1;           //开总中断
  56.     ConfigTimer0(1);  //配置T0定时1ms
  57.     pWave = SinWave;  //默认正弦波
  58.     SetWaveFreq(fWave);  //默认频率10Hz            
  59.         mainxianshi();
  60.     while (1)
  61.     {        
  62.         KeyDriver();  //调用按键驱动
  63.     }
  64. }

  65. /* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */
  66. void KeyAction(unsigned char keycode)
  67. {
  68.     static unsigned char i = 0;
  69.    
  70.     if (keycode == '1')  //1键,切换波形
  71.     {
  72.         //在3种波形间循环切换
  73.         if (i == 0)
  74.         {
  75.             i = 1;
  76.             pWave = TriWave;
  77.                         main_triwave();
  78.                                         R_LED= 1;
  79.                         Y_LED = 0;
  80.                         G_LED = 0;

  81.         }
  82.         else if (i == 1)
  83.         {
  84.             i = 2;
  85.             pWave = SawWave;
  86.                         main_sawwave();
  87.                                         R_LED = 0;
  88.                         Y_LED = 1;
  89.                         G_LED = 0;
  90.         }
  91.                 else if (i == 2)
  92.         {
  93.             i = 3;
  94.             pWave = SquWave;
  95.                         main_squwave();
  96.                         
  97.             R_LED = 0;
  98.             Y_LED = 0;
  99.             G_LED = 1;
  100.         }
  101.         else
  102.         {
  103.             i = 0;
  104.             pWave = SinWave;
  105.                         main_sinwave();
  106.         }
  107.     }
  108.     else if (keycode == '3')  //向上键,增加频率
  109.     {
  110.         if (fWave < 1000) //达到最大值50之前可递增频率值
  111.         {
  112.             fWave++;               
  113.             SetWaveFreq(fWave);
  114.                         main_freqwave(fWave);
  115.         }
  116.     }
  117.     else if(keycode == '4')  //向下键,降低频率
  118.     {
  119.         if (fWave > 1) //达到最小值1之前可递减频率值
  120.         {
  121.             fWave--;
  122.             SetWaveFreq(fWave);                        
  123.                         main_freqwave(fWave);
  124.         }
  125.     }
  126. }
  127. /* 设置DAC输出值,val-设定值 */
  128. void SetDACOut(unsigned char val)
  129. {
  130.     I2CStart();
  131.     if (!I2CWrite(0x48<<1)) //寻址PCF8591,如未应答,则停止操作并返回
  132.     {
  133.         I2CStop();
  134.         return;
  135.     }
  136.     I2CWrite(0x40);         //写入控制字节
  137.     I2CWrite(val);          //写入DA值  
  138.     I2CStop();
  139. }
  140. /* 设置输出波形的频率,freq-设定频率 */
  141. void SetWaveFreq(unsigned char freq)
  142. {
  143.     unsigned long tmp;
  144.    
  145.     tmp = (11059200/12) / (freq*32); //定时器计数频率,是波形频率的32倍
  146.     tmp = 65536 - tmp;               //计算定时器重载值
  147.     tmp = tmp + 6;                   //修正中断响应延时造成的误差
  148.     T1RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节
  149.     T1RL = (unsigned char)tmp;
  150.     TMOD &= 0x0F;   //清零T1的控制位
  151.     TMOD |= 0x10;   //配置T1为模式1
  152.     TH1 = T1RH;     //加载T1重载值
  153.     TL1 = T1RL;
  154.     ET1 = 1;        //使能T1中断
  155.     PT1 = 1;        //设置为高优先级
  156.     TR1 = 1;        //启动T1
  157. }
  158. /* 配置并启动T0,ms-T0定时时间 */
  159. void ConfigTimer0(unsigned int ms)
  160. {
  161.     unsigned long tmp;  //临时变量
  162.    
  163.     tmp = 11059200 / 12;      //定时器计数频率
  164.     tmp = (tmp * ms) / 1000;  //计算所需的计数值
  165.     tmp = 65536 - tmp;        //计算定时器重载值
  166.     tmp = tmp + 5;            //补偿中断响应延时造成的误差
  167.     T0RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节
  168.     T0RL = (unsigned char)tmp;
  169.     TMOD &= 0xF0;   //清零T0的控制位
  170.     TMOD |= 0x01;   //配置T0为模式1
  171.     TH0 = T0RH;     //加载T0重载值
  172.     TL0 = T0RL;
  173.     ET0 = 1;        //使能T0中断
  174.     TR0 = 1;        //启动T0
  175. }
  176. /* T0中断服务函数,执行按键扫描 */
  177. void InterruptTimer0() interrupt 1
  178. {
  179.     TH0 = T0RH;  //重新加载重载值
  180.     TL0 = T0RL;
  181.     KeyScan();   //按键扫描
  182. }
  183. /* T1中断服务函数,执行波形输出 */
  184. void InterruptTimer1() interrupt 3
  185. {
  186.     static unsigned char i = 0;
  187.    
  188.     TH1 = T1RH;  //重新加载重载值
  189.     TL1 = T1RL;
  190.     //循环输出波表中的数据
  191.     SetDACOut(pWave[i]);
  192.     i++;
  193.     if (i >= 32)
  194.     {
  195.         i = 0;
  196.     }
  197. }


  198. 按键keyboard.c
  199. #include"reg52.h"
  200. sbit K1 = P2^2;
  201. sbit K2 = P2^1;
  202. sbit K3 = P2^0;
  203. unsigned char code KeyCodeMap[3] = { '1','4','3'};
  204. unsigned char pdata KeySta[3] =  {1, 1, 1};
  205. extern void KeyAction(unsigned char keycode);

  206. void KeyDriver()
  207. {
  208.     unsigned char i;
  209.     static unsigned char pdata backup[3] = {1, 1, 1};  //按键值备份,保存前一次的值  
  210.     for (i=0; i<3; i++)
  211.     {
  212.                 if (backup[i] != KeySta[i])    //检测按键动作
  213.                 {
  214.                         if (backup[i] != 0)           //按键按下时执行动作
  215.                         {
  216.                                 KeyAction(KeyCodeMap[i]); //调用按键动作函数
  217.                         }
  218.                         backup[i] = KeySta[i];     //刷新前一次的备份值
  219.                 }
  220.     }
  221. }

  222. /* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */
  223. void KeyScan()
  224. {
  225.     unsigned char i;
  226.     static unsigned char keybuf[3]={0xFF, 0xFF, 0xFF};  //矩阵按键扫描缓冲区
  227.     //将一行的4个按键值移入缓冲区
  228.     keybuf[0] = (keybuf[0] << 1) | K1;
  229.     keybuf[1] = (keybuf[1] << 1) | K2;
  230.     keybuf[2] = (keybuf[2] << 1) | K3;
  231.     //消抖后更新按键状态
  232.     for (i=0; i<3; i++)  //每行4个按键,所以循环4次
  233.     {
  234.         if ((keybuf[i] & 0x0F) == 0x00)
  235.         {   //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
  236.             KeySta[i] = 0;
  237.         }
  238.         else if ((keybuf[i] & 0x0F) == 0x0F)
  239.         {   //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
  240.             KeySta[i] = 1;
  241.         }
  242.     }
  243. }


  244. LCD12864显示12864xianshi.c
  245. #include"reg52.h"
  246. #define LCDP0 P0
  247. #include
  248. #include
  249. #include "12864.h"

  250. sbit RS = P1^0;
  251. sbit RW = P1^1;
  252. sbit E = P1^2;
  253. unsigned char num;
  254. unsigned char code dis0[]={"090  115"};
  255. unsigned char code dis1[]={"简易低频信号源"};
  256. unsigned char code dis2[]={"波形:"};
  257. unsigned char code dis3[]={"频率:"};
  258. unsigned char code dis4[]={"学号:"};
  259. unsigned char code dis5[]={"SinWave"};
  260. unsigned char code dis6[]={"TriWave"};
  261. unsigned char code dis7[]={"SawWave"};
  262. unsigned char code dis8[]={"SquWave"};
  263. unsigned char code dis9[]={"HZ"};

  264. void delay(unsigned int ms)               
  265. {
  266.         unsigned int s,t;
  267.         for(s=ms;s>0;s--)
  268.         for(t=110;t>0;t--);
  269. }

  270. void ready()                                                //读状态
  271. {
  272.         unsigned char sta;
  273.         LCDP0 = 0xFF;
  274.         RS    = 0 ;
  275.         RW    = 1 ;
  276.         do        {
  277.                         E   = 1;
  278.                         sta = LCDP0;
  279.                         E   = 0;
  280.                 }
  281.                 while(sta&0x80);
  282. }

  283. void write_com(unsigned char com)                        //写命令
  284. {
  285.         ready();
  286.         RS  = 0;
  287.         RW  = 0;
  288.         LCDP0 = com;
  289.         E = 1;
  290.         E = 0;
  291. }

  292. void write_date(unsigned char date)                        //写数据
  293. {
  294.         ready();
  295.         RS = 1;
  296.         RW = 0;
  297.         LCDP0 = date;
  298.         E = 1;
  299.         E = 0;
  300. }

  301. void lcd_pos(unsigned char X,unsigned char Y)
  302. {
  303.         unsigned char pos;
  304.         if(X==0)
  305.         {
  306.                 X = 0x81;
  307.         }
  308.         else if(X==1)
  309.         {
  310.                 X = 0x90;
  311.         }
  312.         else if(X==2)
  313.         {
  314.                 X = 0x88;
  315.         }
  316.         else if(X==3)
  317.         {
  318.                 X =0x98;
  319.         }
  320.         pos = X+Y;
  321.         write_com(pos);
  322. }

  323. void init()                                                        //初始化        
  324. {
  325.         write_com(0x30);
  326.         write_com(0x0C);
  327.         write_com(0x06);
  328.         write_com(0x01);
  329. }

  330. void mainxianshi()
  331. {
  332.         unsigned char i;
  333.         init();
  334.         lcd_pos(0,0);
  335.         i = 0;
  336.         while(dis1[i]!='')
  337.         {
  338.                 write_date(dis1[i]);
  339.                 i++;
  340.         }
  341.         lcd_pos(1,0);
  342.         i = 0;
  343.         while(dis2[i]!='')
  344.         {
  345.                 write_date(dis2[i]);
  346.                 i++;
  347.         }
  348.         lcd_pos(2,0);
  349.         i = 0;
  350.         while(dis3[i]!='')
  351.         {
  352.                 write_date(dis3[i]);
  353.                 i++;
  354.         }
  355.                 lcd_pos(3,0);
  356.         i = 0;
  357.         while(dis4[i]!='')
  358.         {
  359.                 write_date(dis4[i]);
  360.                 i++;
  361.         }
  362.                 lcd_pos(3,3);
  363.         i = 0;
  364.         while(dis0[i]!='')
  365.         {
  366.                 write_date(dis0[i]);
  367.                 i++;
  368.         }
  369.                 lcd_pos(1,3);
  370.         i = 0;
  371.         while(dis5[i]!='')
  372.         {
  373.                 write_date(dis5[i]);
  374.                 i++;
  375.         }
  376.                 lcd_pos(2,5);
  377.         i = 0;
  378.         while(dis9[i]!='')
  379.         {
  380.                 write_date(dis9[i]);
  381.                 i++;
  382.         }
  383. }

  384. void main_sinwave()
  385. {
  386.         unsigned char i;
  387.         lcd_pos(1,3);
  388.         i = 0;
  389.         while(dis5[i]!='')
  390.         {
  391.                 write_date(dis5[i]);
  392.                 i++;
  393.         }
  394. }
  395. void main_triwave()
  396. {
  397.         unsigned char i;
  398.         lcd_pos(1,3);
  399.         i = 0;
  400.         while(dis6[i]!='')
  401.         {
  402.                 write_date(dis6[i]);
  403.                 i++;
  404.         }
  405. }
  406. void main_sawwave()
  407. {
  408.         unsigned char i;
  409.         lcd_pos(1,3);
  410.         i = 0;
  411.         while(dis7[i]!='')
  412.         {
  413.                 write_date(dis7[i]);
  414.                 i++;
  415.         }
  416. }
  417. void main_squwave()
  418. {
  419.         unsigned char i;
  420.         lcd_pos(1,3);
  421.         i = 0;
  422.         while(dis8[i]!='')
  423.         {
  424.                 write_date(dis8[i]);
  425.                 i++;
  426.         }
  427. }
  428. void main_freqwave(unsigned char i)
  429. {
  430.         unsigned int a;
  431.         unsigned int value1, value2;
  432.         a=i;
  433.         if(a>0x09)
  434.         {
  435.                 value1 = a/10 + 0x30;
  436.                 value2 = a%10 + 0x30;
  437.         }
  438.         else
  439.         {
  440.                 value1 = 0 + 0x30;
  441.                 value2 = a + 0x30;
  442.         }
  443.         lcd_pos(2,3);
  444.         write_date(value1);
  445.         write_date(value2);
  446. }
  447. I2c总线I2C.c
  448. /*
  449. * 文件名:I2C.c
  450. * 描  述:I2C总线驱动模块
  451. * 版本号:v1.0.0
  452. * 备  注:基于IO口模拟实现,总线时序延时等皆由软件方式实现
  453. *         软件延时函数的准确度基于STC12系列单片机在12M晶振的情况
  454. *******************************************************************************
  455. */
  456. #include
  457. #include

  458. #define I2CDelay()   Delay_us(5)
  459. sbit I2C_SCL = P1^6;
  460. sbit I2C_SDA = P1^7;
  461. void Delay_us(unsigned char us)
  462. {
  463.     do {
  464.         _nop_();
  465.         _nop_();
  466.         _nop_();
  467.         _nop_();
  468.         _nop_();
  469.         _nop_();
  470.         _nop_();
  471.         _nop_();
  472.     } while (--us);
  473. }
  474. /* 产生总线起始信号 */
  475. void I2CStart()
  476. {
  477.     I2C_SDA = 1; //首先确保SDA、SCL都是高电平
  478.     I2C_SCL = 1;
  479.     I2CDelay();
  480.     I2C_SDA = 0; //先拉低SDA
  481.     I2CDelay();
  482.     I2C_SCL = 0; //再拉低SCL
  483. }
  484. /* 产生总线停止信号 */
  485. void I2CStop()
  486. {
  487.     I2C_SCL = 0; //首先确保SDA、SCL都是低电平
  488.     I2C_SDA = 0;
  489.     I2CDelay();
  490.     I2C_SCL = 1; //先拉高SCL
  491.     I2CDelay();
  492.     I2C_SDA = 1; //再拉高SDA
  493.     I2CDelay();
  494. }
  495. /* I2C总线写操作,dat-待写入字节,返回值-从机应答位的值 */
  496. bit I2CWrite(unsigned char dat)
  497. {
  498.     bit ack;  //用于暂存应答位的值
  499.     unsigned char mask;  //用于探测字节内某一位值的掩码变量

  500.     for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
  501.     {
  502.         if ((mask&dat) == 0)  //该位的值输出到SDA上
  503.             I2C_SDA = 0;
  504.         else
  505.             I2C_SDA = 1;
  506.         I2CDelay();
  507.         I2C_SCL = 1;          //拉高SCL
  508.         I2CDelay();
  509.         I2C_SCL = 0;          //再拉低SCL,完成一个位周期
  510.     }
  511.     I2C_SDA = 1;   //8位数据发送完后,主机释放SDA,以检测从机应答
  512.     I2CDelay();
  513.     I2C_SCL = 1;   //拉高SCL
  514.     ack = I2C_SDA; //读取此时的SDA值,即为从机的应答值
  515.     I2CDelay();
  516.     I2C_SCL = 0;   //再拉低SCL完成应答位,并保持住总线

  517.     return (~ack); //应答值取反以符合通常的逻辑:
  518.                    //0=不存在或忙或写入失败,1=存在且空闲或写入成功
  519. }
  520. /* I2C总线读操作,并发送非应答信号,返回值-读到的字节 */
  521. unsigned char I2CReadNAK()
  522. {
  523.     unsigned char mask;
  524.     unsigned char dat;

  525.     I2C_SDA = 1;  //首先确保主机释放SDA
  526.     for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
  527.     {
  528.         I2CDelay();
  529.         I2C_SCL = 1;      //拉高SCL
  530.         if(I2C_SDA == 0)  //读取SDA的值
  531.             dat &= ~mask; //为0时,dat中对应位清零
  532.         else
  533.             dat |= mask;  //为1时,dat中对应位置1
  534.         I2CDelay();
  535.         I2C_SCL = 0;      //再拉低SCL,以使从机发送出下一位
  536.     }
  537.     I2C_SDA = 1;   //8位数据发送完后,拉高SDA,发送非应答信号
  538.     I2CDelay();
  539.     I2C_SCL = 1;   //拉高SCL
  540.     I2CDelay();
  541.     I2C_SCL = 0;   //再拉低SCL完成非应答位,并保持住总线

  542.     return dat;
  543. }
  544. /* I2C总线读操作,并发送应答信号,返回值-读到的字节 */
  545. unsigned char I2CReadACK()
  546. {
  547.     unsigned char mask;
  548.     unsigned char dat;

  549.     I2C_SDA = 1;  //首先确保主机释放SDA
  550.     for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
  551.     {
  552.         I2CDelay();
  553.         I2C_SCL = 1;      //拉高SCL
  554.         if(I2C_SDA == 0)  //读取SDA的值
  555.             dat &= ~mask; //为0时,dat中对应位清零
  556.         else
  557.             dat |= mask;  //为1时,dat中对应位置1
  558.         I2CDelay();
  559.         I2C_SCL = 0;      //再拉低SCL,以使从机发送出下一位
  560.     }
  561.     I2C_SDA = 0;   //8位数据发送完后,拉低SDA,发送应答信号
复制代码

论文中有详细介绍且富有原理图,程序,和测试效果,电路焊接,完整论文下载(word格式 可编辑):
http://www.51hei.com/bbs/dpj-85798-1.html




分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:351405 发表于 2018-6-14 09:45 | 只看该作者
兄弟,有没有“produes”原理图
回复

使用道具 举报

板凳
ID:320325 发表于 2018-7-16 21:54 | 只看该作者
好东西,感谢分享!
回复

使用道具 举报

地板
ID:253767 发表于 2018-7-17 07:53 | 只看该作者
谢谢分享,学习了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表