目录
2.2 STC12C5A60S2 单片机与最小系统设计... 3
这次的函数信号发生器的项目以 STC12C5A60S2 为核心,用 DAC0832、LM358 运放电路等实现了输出三角波、方波、正弦波和锯齿波,同时每个波形都可以通过按键改变频率和幅值以及步进值。其主要实现的功能如下:
- 利用单片机实现幅值可调(1V~5V),频率可调(100Hz~10KHz),波形可调(正弦波、三角波、锯齿波、方波)的函数信号发生器。
- 系统由单片机+若干按键,OLED 显示屏等外设组成。
- 各输出波形可同步在 OLED 屏上显示。
通过项目概述进行分析,运用单片机输出数字信号给 DAC0832,通过 DAC0832 这个数模转换的模块,将数字信号转换成模拟信号,再通过 LM358 的两级放大输出(都是反向比例运算放大器),其中第一级放大是为了将电流信号转换成电压,第二级放大有两个目的,第一个目的是为了把负电压转换成正电压,第二个就是对电压进行放大,采用电位器进行手动调幅值,当然,在软件部分也设计了可以通过按钮调节幅值的程序(1~5V)配合电位器可实现最高至 10V 的调节。根据设置输出的波形信号,同时在 OLED 屏上显示相应图形、幅值、频率等信息。该信号发生器系统框图如(图 1-1)所示:
图 1-1 信号发生器系统框图
STC12C5A60S2 单片机是整个波形发生器的核心部分,通过程序的编写和执行,产生各种各样的信号,并从键盘接收数据,进行各种功能的转换和信号频率的调节。当数字信号经过 I/O 口电路到达 DAC0832 转换电路,将其转换成模拟信号也就是所需要的输出波形。
仿真图见(图 2-1)
图 2-1
在最早的设计中,我采用的是 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 举例说明:
- 12MHZ 的外部晶振,使用 12T 模式,一个机器周期为 1uS,而需要的延时确实 0.39uS,所以 12T 模式不行
- 1T 模式一个机器周期的时间是 1/12*10^6=0.08uS
因单片机内置上电复位(POR)且本项目无需复位电路,所以本项目中,没有设置单片机的复位电路,因此给 RST 置高电平 1(见图 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
DAC0832 是一个具有 2 个数据寄存器的 8 位分辨率的 D/A 转换芯片,其内部由 8 位输入锁存器、8 位 DAC 寄存器、8 位 D/A 转换器电路及转换控制电路构成,通过两级数据输入锁存器。DAC0832 内部结构框图见(图 2-6)。
图 2-6
DAC0832 有三种工作模式:
- 单缓冲方式。单缓冲方式是控制输入寄存器和 DAC 寄存器同时接收资料,或者只用输入寄存器而把 DAC 寄存器接成直通方式。此方式适用只有一路模拟量输出或几路模拟量异步输出的情形。
- 双缓冲方式。双缓冲方式是先使输入寄存器接收资料,再控制输入寄存器的输出资料到 DAC 寄存器,即分两次锁存输入资料。此方式适用于多个 D/A 转换同步输出的情节。
- 直通方式。直通方式是资料不经两级锁存器锁存,即 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
由于 DAC0832 的输出信号是电流型的,这里需要用到 LM358 芯片构成运放电路,实现对 DAC0832 输出的小信号的放大,并进行电流/电压的转换。
LM358 是双运算放大器。内部包括有两个独立的、高增益、内部频率补偿的运算放大器,适合于电源电压范围很宽的单电源使用,也适用于双电源工作模式,在推荐的工作条件下,电源电流与电源电压无关。它的使用范围包括传感放大器、直流增益模块和其他所有可用单电源供电的使用运算放大器的场合。引脚图见(图 2-8)。
LM358 的特点如下:
- 内部频率补偿
- 直流电压增益高(约 100dB)
- 单位增益频带宽(约 1MHz)
- 电源电压范围宽:单电源(3—30V)
- 双电源(±1.5 一±15V)
- 压摆率(0.3V/us)
- 低功耗电流,适合于电池供电· 低输入偏流
- 低输入失调电压和失调电流
- 共模输入电压范围宽,包括接地
- 差模输入电压范围宽,等于电源电压范围
- 输出电压摆幅大(0 至 Vcc-1.5V)
图 2-8
项目中的运放电路仿真如图 2-9.
图 2-9
本项目中的按键电路由四个独立按键组成,分别与单片机的 P1.4~P1.7 引脚连接,并经过 74LS00(实现四输入与门)与单片机的 P3.2 引脚(外部中断 0)连接,利用中断和扫描的方式来实现波形的切换和信号频率的调整。其中,P1.2 按键对应方波、三角波锯齿波、正弦波的切换,根据 P1.3 按下的次数切换不同的模式(步进值、频率、幅值),在相应的模式下,按下加或减键进行相应的调节。仿真见图(2-10)。
图 2-10
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
由于按键扫描、OLED 显示、定时器都在相关中断程序中,所以主函数只有 OLED 和定时器初始化以及一个 while 死循环。
将一个周期的信号分离成 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 的值来改变装入定时器的计数初值,从而改变波形的频率。
- 对于方波来说只有两种状态,分别是高和低这两种状态
- 为了实现幅值可调,即波形的峰值可调,所以给的高的状态需要用一个变量来进行控制
- 值得注意的是运用两个 IF 判断,确保高和低的时间都占 1/2,确保了占空比是固定的
三角波的实现是设置一个初值,然后进行加数,加到 max(max 就是设定的电压值)之后再进行减数,减到初值之后就再返回到先前的操作。
把正弦波 360/fuzhi,就是规定了正弦波采样的数量为 256 个点(可以变化),其中引用了 math 库里的 sin 函数,angle 为角度,0.0174 是/180,把角度转换成弧度,sin 函数识别弧度。
当定时器溢出后,进入定时器中断服务,程序会先用之前记录好的定时器初值对定时器重装初值,然后根据选择的波形输出对应波形,再返回之前的断点。
当按下按键进入外部中断后。会先对按键扫描,然后记录按下按键后的幅值和频率,再把记录下的数据传入 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
实物仍存在问题,第一次做请大家指教!!!仿真和代码在上述连接