信号发生器

目录

 

一、概述... 1

1.1 项目概述... 1

1.2 项目分析... 1

二、硬件设计... 2

2.1 硬件电路的总体设计... 2

2.2 STC12C5A60S2 单片机与最小系统设计... 3

2.2.1 复位设计电路... 5

2.2.2 时钟晶振电路... 5

2.3 数模转换电路... 7

2.4 运放电路... 9

2.5 按键电路... 11

2.6OLED 电路... 12

三、软件设计... 14

3.1 主函数设计... 14

3.2 各个波形的程序设计... 15

3.2.1 修改频率的设计思路... 15

3.2.2 方波... 16

3.2.3 三角波... 17

3.2.4 锯齿波... 18

3.2.5 正弦波... 19

3.2.6 按键扫描... 21

3.2.7 定时器中断... 21

3.2.8 外部中断... 22

四、仿真演示... 23

一、概述

1.1 项目概述

这次的函数信号发生器的项目以 STC12C5A60S2 为核心,用 DAC0832、LM358 运放电路等实现了输出三角波、方波、正弦波和锯齿波,同时每个波形都可以通过按键改变频率和幅值以及步进值。其主要实现的功能如下:

  1. 利用单片机实现幅值可调(1V~5V),频率可调(100Hz~10KHz),波形可调(正弦波、三角波、锯齿波、方波)的函数信号发生器。
  2. 系统由单片机+若干按键,OLED 显示屏等外设组成。
  3. 各输出波形可同步在 OLED 屏上显示。

1.2 项目分析

通过项目概述进行分析,运用单片机输出数字信号给 DAC0832,通过 DAC0832 这个数模转换的模块,将数字信号转换成模拟信号,再通过 LM358 的两级放大输出(都是反向比例运算放大器),其中第一级放大是为了将电流信号转换成电压,第二级放大有两个目的,第一个目的是为了把负电压转换成正电压,第二个就是对电压进行放大,采用电位器进行手动调幅值,当然,在软件部分也设计了可以通过按钮调节幅值的程序(1~5V)配合电位器可实现最高至 10V 的调节。根据设置输出的波形信号,同时在 OLED 屏上显示相应图形、幅值、频率等信息。该信号发生器系统框图如(图 1-1)所示:

信号发生器

图 1-1 信号发生器系统框图

STC12C5A60S2 单片机是整个波形发生器的核心部分,通过程序的编写和执行,产生各种各样的信号,并从键盘接收数据,进行各种功能的转换和信号频率的调节。当数字信号经过 I/O 口电路到达 DAC0832 转换电路,将其转换成模拟信号也就是所需要的输出波形。

二、硬件设计

2.1 硬件电路的总体设计

仿真图见(图 2-1)

信号发生器

图 2-1

2.2 STC12C5A60S2 单片机与最小系统设计

在最早的设计中,我采用的是 STC89C51 这一芯片,本来想着也够用,但是在后来的实践中发现了很大的问题,一是内存不太够,二是这一芯片的不够达到 10KHZ 的要求(具体为什么不能请往后看),这让我想到使用有 1T 模式的 STC12C5A60S2 来代替,这样就足够满足这个要求。

其中 STC12C5A60S2 中有 1T 的工作模式,如下图所示 AUXR 是调节 1T 和 12T 模式的特殊寄存器,T0X12 和 T1X12 分别是控制定时器 0 和 1 的工作模式。AUXR 不能按位寻址,只能运用字节寻址,拿定时器 0 举例:T0X12 为 0 是 12T 模式,为 1 是 1T 模式默认为 12T 模式

TIPS:在 1T 模式下用软件生成的延时函数需要修改,因为 1T 运行速度是 12T 的 12 倍

void init0()//定时器初始化
{
  AUXR|=0X80//开 1T 模式
  TH0=0X01;
  TL0=0X0A;
  EA=1;
  ET0=1;
  TR0=1;
}

信号发生器

拿 10KHZ 举例说明:

  1. 12MHZ 的外部晶振,使用 12T 模式,一个机器周期为 1uS,而需要的延时确实 0.39uS,所以 12T 模式不行
  2. 1T 模式一个机器周期的时间是 1/12*10^6=0.08uS

信号发生器

2.2.1 复位设计电路

因单片机内置上电复位(POR)且本项目无需复位电路,所以本项目中,没有设置单片机的复位电路,因此给 RST 置高电平 1(见图 2-2)即可。

信号发生器

图 2-2

2.2.2 时钟晶振电路

CPU 工作时都必须有一个时钟脉冲。时钟电路在单片机系统中起着非常重要的作用,是保证系统正常工作的基础。晶振频率的大小决定了单片机系统工作的快慢。有两种方式可以向 STC12C5A60S2 提供时钟脉冲: 一是外部时钟方式,即使用外部电路向 12C 提供始终脉冲,见(图 2-3);二是内部时钟方式,即使用品振由 12C 内部电路产生时钟脉冲。一般常用第二种方法,其电路见(图 2-4)。仿真图见(图 2-5)。

信号发生器

图 2-3

信号发生器

图 2-4

图 2-3 中: XO 一般为石英晶体,其频率由系统需要和器件决定,在频率稳定度要求不高。

C1、C2: 使用石英晶体时,C1=C2=30 (士 10) pF

信号发生器

图 2-5

2.3 数模转换电路

DAC0832 是一个具有 2 个数据寄存器的 8 位分辨率的 D/A 转换芯片,其内部由 8 位输入锁存器、8 位 DAC 寄存器、8 位 D/A 转换器电路及转换控制电路构成,通过两级数据输入锁存器。DAC0832 内部结构框图见(图 2-6)。

信号发生器图 2-6

DAC0832 有三种工作模式:

  1. 单缓冲方式。单缓冲方式是控制输入寄存器和 DAC 寄存器同时接收资料,或者只用输入寄存器而把 DAC 寄存器接成直通方式。此方式适用只有一路模拟量输出或几路模拟量异步输出的情形。
  2. 双缓冲方式。双缓冲方式是先使输入寄存器接收资料,再控制输入寄存器的输出资料到 DAC 寄存器,即分两次锁存输入资料。此方式适用于多个 D/A 转换同步输出的情节。
  3. 直通方式。直通方式是资料不经两级锁存器锁存,即 CS*,XFER* ,WR1* ,WR2* 均接地,ILE 接高电平。此方式适用于连续反馈控制线路和不带微机的控制系统,不过在使用时,必须通过另加 I/O 接口与 CPU 连接,以匹配 CPU 与 D/A 转换。

在本设计中让 DAC0832 工作在直通方式,连接方式如上述直通方式相同,具体见(图 2-7),VREF 接+5V 电压,ILE 接+5V 电压,在系统正常工作时,输入的数字信号,不经过寄存器,直接输出完成 D/A 转换。

DAC0832 的分辨率为 8 位,也就是 256,本项目中基准电压设置为 5V,所以本项目中 DAC0832 理论精度为 0.01953125V,约等于 20mV。

信号发生器

图 2-7

2.4 运放电路

由于 DAC0832 的输出信号是电流型的,这里需要用到 LM358 芯片构成运放电路,实现对 DAC0832 输出的小信号的放大,并进行电流/电压的转换。

LM358 是双运算放大器。内部包括有两个独立的、高增益、内部频率补偿的运算放大器,适合于电源电压范围很宽的单电源使用,也适用于双电源工作模式,在推荐的工作条件下,电源电流与电源电压无关。它的使用范围包括传感放大器、直流增益模块和其他所有可用单电源供电的使用运算放大器的场合。引脚图见(图 2-8)。

LM358 的特点如下:

  1. 内部频率补偿
  2. 直流电压增益高(约 100dB)
  3. 单位增益频带宽(约 1MHz)
  4. 电源电压范围宽:单电源(3—30V)
  5. 双电源(±1.5 一±15V)
  6. 压摆率(0.3V/us)
  7. 低功耗电流,适合于电池供电· 低输入偏流
  8. 低输入失调电压和失调电流
  9. 共模输入电压范围宽,包括接地
  10. 差模输入电压范围宽,等于电源电压范围
  11. 输出电压摆幅大(0 至 Vcc-1.5V)

信号发生器

图 2-8

项目中的运放电路仿真如图 2-9.

信号发生器

图 2-9

2.5 按键电路

本项目中的按键电路由四个独立按键组成,分别与单片机的 P1.4~P1.7 引脚连接,并经过 74LS00(实现四输入与门)与单片机的 P3.2 引脚(外部中断 0)连接,利用中断和扫描的方式来实现波形的切换和信号频率的调整。其中,P1.2 按键对应方波、三角波锯齿波、正弦波的切换,根据 P1.3 按下的次数切换不同的模式(步进值、频率、幅值),在相应的模式下,按下加或减键进行相应的调节。仿真见图(2-10)。

信号发生器

图 2-10

2.6OLED 电路

OLED,即有机发光二极管( Organic Light Emitting Diode)。OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示 OLED 效果要来得好一些。在此项目中使用的是中景园电子的 0.96 寸 OLED 显示屏,该屏有以下特点:

分辨率:128*64

多种接口方式:OLED 裸屏总共 5 种接口包括:6800、8080 两种并行接口方式、3 线或 4 线的串行 SPI 接口方式、IIC 接口方式(只需要 2 根线就可以控制 OLED 了)

 响应快:OLED 显示屏的响应时间超过 TFT—LCD 液晶屏。TFT—LCD 的响应时间大约是几十毫秒,现在做得最好的 TFT—LCD 响应时间也只有 12 毫秒,而 OLED 显示屏的响应时间大约是几微秒到几十微秒。

本项目中的 OLED 采用 8080 并行、七针 SPI 接口方式来显示对应波形及其相关信息。这个 OLED 可以焊接电阻中的某几个来选择 IIC 或者 SPI 协议,详见(图 2-11)。仿 5 真图见(图 2-12)

信号发生器

图 2-11

信号发生器

图 2-12

三、软件设计

3.1 主函数设计

信号发生器

由于按键扫描、OLED 显示、定时器都在相关中断程序中,所以主函数只有 OLED 和定时器初始化以及一个 while 死循环。

3.2 各个波形的程序设计

3.2.1 修改频率的设计思路

将一个周期的信号分离成 256 个点(按 X 轴等分,三角波为 512 个点), 每两点之间的时间间隔为∆_T,用单片机的定时器产生,其表示式为:∆_T=T/256 (其中三角波为∆_T=T/512 )。如果单片机的晶振为12MHz, 采用定时器方式1,则定时器的初值为: 

X=2^16-∇_T/Tmec

X=2^16-∇_T/Tmec

定时时间常数为:

 TH0=(65536-∆_T)/256

TL0=(65536-∆_T )%256

所以由初值计算公式导出频率调整公式:

X=65536-10^6/(N×FREQ)

其中 N 为信号被分离的点数,FREQ 为波形的频率。通过按键修改 FREQ 的值来改变装入定时器的计数初值,从而改变波形的频率。

3.2.2 方波

信号发生器

  1. 对于方波来说只有两种状态,分别是高和低这两种状态
  2. 为了实现幅值可调,即波形的峰值可调,所以给的高的状态需要用一个变量来进行控制
  3. 值得注意的是运用两个 IF 判断,确保高和低的时间都占 1/2,确保了占空比是固定的

信号发生器

3.2.3 三角波

 

信号发生器

三角波的实现是设置一个初值,然后进行加数,加到 max(max 就是设定的电压值)之后再进行减数,减到初值之后就再返回到先前的操作。

3.2.4 锯齿波

信号发生器

3.2.5 正弦波

信号发生器

        

把正弦波 360/fuzhi,就是规定了正弦波采样的数量为 256 个点(可以变化),其中引用了 math 库里的 sin 函数,angle 为角度,0.0174 是信号发生器/180,把角度转换成弧度,sin 函数识别弧度。

3.2.6 按键扫描

信号发生器

3.2.7 定时器中断

信号发生器

当定时器溢出后,进入定时器中断服务,程序会先用之前记录好的定时器初值对定时器重装初值,然后根据选择的波形输出对应波形,再返回之前的断点。

3.2.8 外部中断

信号发生器

当按下按键进入外部中断后。会先对按键扫描,然后记录按下按键后的幅值和频率,再把记录下的数据传入 OLED 相关函数,生成坐标轴和波形,按键扫描后的 OLED 清屏函数是为了把之前的波形清除,否则会有残留,接着返回之前断点。

四、仿真演示

信号发生器

信号发生器

#include <STC12C5A60S2.H>
#include <intrins.h>
#include <math.h>
#define uint unsigned int
#define uchar unsigned char 
#define PI 3.14
sbit s1_up=P1^0;
sbit s2_down=P1^1;
sbit s3_select=P1^2;
sbit s7=P1^6;

unsigned char o=0;
float hd=0.0;
float angle=0.0;
unsigned int tem=0;
//sinbo shengcheng dingyi
uint pinlv=100,bujin=10;
uchar num,boxing,mode,dianya=5,temp,temp1,fuzhi=255;
uchar TIME_H,TIME_L;
long int l;
bit flag;

//12C delay function time should be changed if the mode is 1T
void delay(uchar m)
{
    while(m--);
}
void squwave()  
{  //fuzhi
	num++;
	if(num<=fuzhi/2)
		P2=0X00;
	else if(num<=fuzhi)
	{
		P2=fuzhi;
	}
	else 
		num=0;
}                                                      
void juchi()
{//fuzhi
	P2=num;
	num++;
	if(num>=fuzhi)
	{
		num=0;
	}
}
void triwave()
{//2*fuzhi 
	if(!flag)
	{
		P2=num;
		num+=1;
		if(num>fuzhi)
		{
			num=0;
		}
		if(num==0)
		{
			num=fuzhi;
			flag=1;
		}
	}
	if(flag)
	{
		P2=num;
		num-=1;
		if(num<=0)
		{
			num=1;
		}
		if(num==1)
		{
			num=0;
			flag=0;
		}
	}
}
void sin_tap()
{
	angle=360.00/256;
	o++;
	hd=angle*o;
	hd=hd*0.01744;
	P2=(fuzhi/2)*(1+sin(hd));
	if(o>255)
	{
		o=0;
	}
}
void initT0()
{
	AUXR|=0X80;
	//1t MODE
	TMOD=0x01;
	TH0=TIME_H;
	TL0=TIME_L;
	IT0=1;//DOWNSIDE TRIGGER
	EX0=1;//OUTSIDE INTERRUPT 0
	EA=1;
	ET0=1;
	TR0=1;//TIMER 0
}
void keyscan()
{
	if(s1_up==0)
	{
		delay(5);
		if(s1_up==0)
		{
			while(!s1_up);
      switch(mode)
      {
				case 0:
					pinlv+=100;
					if(pinlv>10000){pinlv=1000;}
					l=65536-11111111/fuzhi/pinlv;
					TIME_H=l/256;
					TIME_L=l%256;
					break;
				case 1:
					temp=dianya;
					temp1=fuzhi;
					dianya+=1;
					if(dianya>5){dianya=5;}
					fuzhi=(dianya*temp1)/temp;
					break;
			}				
     /*if(mode==0)			
			{
				pinlv+=100;
				if(pinlv>10000){pinlv=100;};
				l=65536-11111111/fuzhi/pinlv;
				TIME_H=l/256;
				TIME_L=l%256;
			}
			else if(mode==1)
			{
				temp=dianya;
				temp1=fuzhi;
				dianya+=1;
				if(dianya>5){dianya=5;}
				fuzhi=(dianya*temp1)/temp;
			}*/
		}
	}
	if(s2_down==0)
	{
		delay(5);
		if(s2_down==0)
		{
			while(!s2_down);
      switch(mode)
      {
				case 0:
					pinlv-=100;
					if(pinlv<100){pinlv=100;}
					l=65536-11111111/(fuzhi*pinlv);
					TIME_H=l/256;
					TIME_L=l%256;
					break;
				case 1:
					temp=dianya;
					temp1=fuzhi;
					dianya-=1;
					if(dianya<1){dianya=1;}
					fuzhi=(dianya*temp1)/temp;
					break;
			}				
	/*		if(mode==1)
			{
				temp=dianya;
				temp1=fuzhi;
				dianya-=1;
				if(dianya<1){dianya=1;}
				fuzhi=(dianya*temp1)/temp;				
			}
			if(mode==2)
			{
				bujin-=10;
			}			
			if(mode==0)
			{
				pinlv-=bujin;
				if(pinlv<100){pinlv=100;}
				l=65536-1000000/(tempx*pinlv);
				TIME_H=l/256;
				TIME_L=l%256;
			}		
*/
		}
	}	
	if(s3_select==0)
	{
		delay(5);
		if(s3_select==0)
		{
			while(!s3_select);
			boxing++;
			if(boxing>3)
			{
				boxing=0;
			}
		}
	}

	
}
void judge()
{//UP AND DOWN MODE CHOSE 
	if(s7==0)
	{
		delay(60);
		if(s7==0)
		{
			while(!s7);
			mode++;
			if(mode==2){mode=0;}
		}
	}	
}

void main()
{
	l=65536-11111111/fuzhi/pinlv;
	TIME_H=l/256;
	TIME_L=l%256;
	initT0(); //INTIALIZE TIMER
	while(1)
	{
		judge();
	}
}

void int_out()interrupt 1
{
	switch(boxing)
	{
		case 0:TH0=TIME_H;TL0=TIME_L;squwave();break;
		case 1:TH0=TIME_H;TL0=TIME_L;juchi();break;
		case 2:TH0=TIME_H;TL0=TIME_L;sin_tap();break;
		case 3:TH0=TIME_H;TL0=TIME_L;triwave();break;
		default:break;
	}
}
void inte_2()interrupt 0
{
	keyscan();
}

链接:https://pan.baidu.com/s/125iVONiDh07nDZvPx5GfBQ 
提取码:iiuc

实物仍存在问题,第一次做请大家指教!!!仿真和代码在上述连接

上一篇:中国OLED材料市场现状及投资潜力分析报告2022-2027年


下一篇:字符设备驱动的框架和理解(一)