|
基于aduc842瞬态波形存储器 作者:杨程泽
刘强 谢婷 地址:华中师范大学物理学院电子信息科学与技术专业 摘 要
本作品由增益放大与控制模块、极性转换模块、触发以及以aduc841/
aduc831为核心的单片机最小系统4个部分构成,由单片机实现控制与波形存储回放功能。 一、方案论证与设计 1. 增益放大与测量方案 方案一:将输入信号经放大后送入窗口比较器,上限1V,下限0.1V。若小于0.1V,则通过单片机(p0.2=1,p0.3=0)控制增益放大电路将信号放大10倍,再输入到控制模块与0.1V和1V比较,若仍小于0.1V,则通过单片机控制增益放大电路将信号放大100倍;若大于1V,则通过单片机(在一个
周期内出现p0.2=1,p0.3=0;p0.2=1,p0.3=1;p0.2=0,p0.3=1这几种情况)控制增益放大电路将信号衰减10倍(当前信号已经放大100倍或10倍),如果信号放大1倍,则衰减为零,阻止通过;若在0.1V和1V之间,则通过单片机(在一个
周期内出现p0.2=1,p0.3=0;p0.2=1,p0.3=1这两种种情况)控制增益放大电路将信号无增益的输出。通过此电路使输出信号幅度控制在0.2V至2V之间。
方案二:将放大后的信号其中一路转换为直流信号,再将此信号给窗口比较器,上限2V,下限0.2V。若小于0.2V,则通过单片机(p0.2=1,p0.3=0)控制增益放大电路将信号放大10倍,再输入到控制模块与0.2V和2V比较,若仍小于0.2V,则通过单片机控制增益放大电路将信号放大100倍;若大于2V,则通过单片机(p0.2=0,p0.3=1)控制增益放大电路将信号衰减为零,阻止通过;若在0.2V和2V之间,则通过单片机(p0.2=1,p0.3=1)控制增益放大电路将信号无增益的输出。通过此电路使输出信号幅度控制在0.2V至2V之间。
对以上两种方案进行比较,方案一虽在软件控制方面稍繁,但也易于实现,而方案二在将交流变为直流的硬件方面较为复杂,故采用方案二。 2.
采样方案
方案一:通过触发模块提供给单片机一个周期信号,通过软件实现定时采样。
方案二:先将信号经过100倍频之后,送给单片机作为采样触发信号,也就是说每个周期固定采100个点。但是同步回放频率难以控制。
对以上两种方案进行比较,方案二虽然在采样方面比较精准,但回放难以实现,而方案一在两方面均可兼顾。故采用方案一。 二、原理分析与硬件电路图
根据题目要求,该调理系统包括增益放大与控制模块、极性转换模块、触发3个模块,由于3个模块相对独立,其中增益放大与控制模块是重点。以下分别对其进行原理分析与电路设计。 1.增益放大与循环取样模块 (1)原理分析 设输入信号幅度为A,将输入信号输入循环取样模块与0.1V和1V比较:若小于0.1V,则通过单片机控制增益放大电路将信号放大10倍,再输入到控制模块与0.1V和1V比较,若仍小于0.1V,则通过单片机控制增益放大电路将信号放大100倍;若大于1V,则通过单片机控制增益放大电路将信号衰减10倍(当前信号已经放大100倍或10倍),如果信号放大1倍,则衰减为零,阻止通过;若在0.1V和1V之间,则通过单片机控制增益放大电路将信号无增益的输出。通过此电路使输出信号峰峰值控制在0.2V至2V之间。 (2)原理电路 ①循环取样模块。本电路采用专用比较器LM339,使用三端稳压管TL431提供2.5V的稳压源,通过调节滑动变阻器使LM339的6脚和5脚分别得到0.1V和1V的电平。将取样信号送给由LM339构成的窗口比较器,可以从1脚和2脚得到随信号变化的TTL电平,从而将比较结果送给单片机,以便单片机做出判决,发出控制信号。该增益控制模块电路的基本原理如下图所示。
②增益控制模块。根据单片机发出的控制信号送给CD4051,选择X0,X1,X2,X3导通,从而改变运放U2B的放大倍数。通过滑动变阻器的微调使放大倍数达到指标(1倍、10倍、100倍)。该增益放大模块电路的基本原理如下图所示。
2.极性转换模块 (1)原理分析 将输入信号通过极性转换模块(含有一加法器和一提供稳定电压的稳压源),使信号幅度均抬高1.25V,从而将输入的双极性信号转换成单极性信号输出。 (2)原理电路 使用三端稳压管TL431向TL084的正极提供1.25V的稳压源,由TL084组成加法器,使输入信号与1.25V相加。该极性转换模块电路的基本原理如下图所示。
3.触发模块 (1)原理分析 将输入信号通过触发模块(含有比较器),一周期的输入信号得到一上升沿触发。 (2)原理电路 将输入信号接入比较器LM339的正极,负极接入一可通过滑动变阻器微调的电压,通过与8脚电压比较,得到一个周期与输入信号相同的方波,作为触发信号送给单片机。该触发模块电路的基本原理如下图所示。
三、软件设计与流程
四、总结 在这个电路中,通过模拟处理模块和单片机配合实现对输入信号的自动放大,当输入信号在0.002V至2V之间, 使单片机采样信号始终保持在0.2V至2V之间。同时利用ADuC842内部的AD和DA对信号采样并同步输出, 在这个过程中,把采样值保存在数组中,通过按键实现回放。 附件: #include
"zlg7290b.h" //SCLK--SCL
MOSI--SDA INT--INT0 #define
uchar unsigned char #define
uint unsigned int bit
con_caiji=0; bit
tongb_hf=0; sbit
P0_0=P2^0; sbit
P0_1=P2^1; sbit
P0_2=P0^2; sbit
P0_3=P0^3; unsigned
int k,tb_shuzu; unsigned
int j=0,i=0; unsigned
char caiji_2=100; unsigned
char panduan,panduan1; unsigned
int xdata tongbu[4000]={0}; /****************************************** 函数:void
DispValue(unsigned char x, unsigned char dat) 功能:通过ZLG7290
显示一个字符型数据 说明:显示范围:0--255 参数:x,显示起始位置。dat,需要显示的数据 ******************************************/ void
DispValue(unsigned char x, unsigned char dat) {
unsigned char d;
d = dat / 10;
ZLG7290_Download(x,0,0,d);
d = dat - d * 10;
ZLG7290_Download(x+1,0,0,d); } /****************************************** 函数:unsigned int
ADC(unsigned char channel) 功能:控制单片机的AD的工作 说明:无 参数: channel,AD通道选择 ******************************************/ unsigned
int ADC(unsigned char channel) {
ADCCON2 = channel; //选择通道
SCONV = 1; //启动转换
while(SCONV); //等待转换完成
return((ADCDATAH & 0x0F) * 256 + ADCDATAL);
} /****************************************** 函数:void
DAC1(unsigned int da_data) 功能:控制单片机DA1的工作 说明:无 参数:da_data,AD转换的数字量 ******************************************/ void
DAC1(unsigned int da_data)
{
DAC1H = da_data >> 8;
DAC1L = da_data; } /****************************************** 函数:INT0_SVC() 功能:ZLG7290
键盘中断服务程序 说明:中断触发方式选择负边沿触发,
因此不必等待中断请求信号恢复为高电平 参数:无 ******************************************/ void
INT0_SVC() interrupt 2 {
unsigned char KeyValue=1;
unsigned char RepeatCnt=0;
unsigned char FunctionKey=0;
EA=0;
FunctionKey=gets2(ZLG7290_FunctionKey);
//读功能计数器的值
KeyValue=gets2(ZLG7290_Key);
//读键值
RepeatCnt=gets2(ZLG7290_RepeatCnt);
//读连击计数器
DispValue(0,KeyValue);
//显示连键值
if(KeyValue==1)
{
tongb_hf=0;
caiji_2=0;
}
if(KeyValue==2)
{
tongb_hf=1;
j=0;
}
P0_0=P0_0;
P0_1=P0_1;
EA=1; } /****************************************** 函数:caiji_data() 功能:数据采集触发 说明:无 参数:无 ******************************************/ void
caiji_data() interrupt 0 {
EA=0;
caiji_2++;
panduan++;
panduan1++;
if(caiji_2==4)
{
con_caiji=1;
i=0;
}
if(caiji_2==60)
{
con_caiji=0;
tb_shuzu=i;
}
if(caiji_2>=200)
{
caiji_2=80;
}
if(panduan==30)
{
panduan=0;
}
EA=1; } /****************************************** 函数:ad_start() 功能:AD转换,同时DA进行,实现同步输出 说明:无 参数:无 ******************************************/ void
ad_start() interrupt 1 {
uint temp_data;
EA=0;
TH0=(65536-1800)/256;
TL0=(65536-1800)%256;
temp_data = ADC(5);
if(tongb_hf==0)
{
DAC1(temp_data);
if(con_caiji==1)
{
tongbu[i]=temp_data;
i++;
}
}
if(tongb_hf==1)
{
DAC1(tongbu[j]);
j++;
if(j==tb_shuzu)
{
j=1;
}
}
EA=1; } void
main() {
uint k;
uchar x1,x2,x3,x4;
uchar fanda;
SystemInit();
//7290复位
PLLCON = 0x00;
//for 842
I2CCON=0xa8;
//I2C初始化
I2C_Init();
//I2C初始化 7290用
ClearAll();
//7290全部清除
ADCCON1 = 0xBC;
//ADC上电,内部基准,clk 8分频
DACCON = 0x16;
//DAC采用内部ref做基准
TMOD=0x01;
//定时器0方式1
TH0=(65536-1000)/256;
TL0=0;
IT0 = 1;
//负边沿触发中断
EX0 = 1;
//允许外部中断
EA = 1;
//开中断
ET0=1;
//开c0的中断
EX1=1;
//开中断 int1
TR0=1;
//启动c0记时
PT0=1;
//同步回放中断优先
k=5000;
while(k--)
{
P2=0x00;
}
while(1)
{
while(panduan!=28)
{
if(P0_3!=1)
{
x1++;
}
if(P0_2!=0)
{
x2++;
}
}
if(panduan==29)
{
x3=x1;
x4=x2;
x1=0;
x2=0;
}
if((fanda==10)&&(x4==0)&&(x3==0))
{
P0_0=1;
P0_1=0;
fanda=100;
}
if(P0_2==1)
{
P0_0=0;
P0_1=0;
}
if(x4==0&&x3==0)
{
if(P0_0==1&& P0_1==0)
{
P0_0=1;
P0_1=0;
}
else
{
P0_0=0;
P0_1=1;
fanda=10;
}
}
} } 头文件1 /******************************************************************** ZLG7290.c 数码管显示与键盘管理芯片ZLG7290
的标准80C51 驱动程序C 文件 Copyright
(c) 2005,广州周立功单片机发展有限公司 All
rights reserved. 本程序仅供学习参考,不提供任何可靠性方面的担保;请勿用于商业目的 ******************************************************************/ #include
"i2c.h" #include
"zlg7290.h" /**************************************************** 函数:ZLG7290_WriteReg() 功能:向ZLG7290
的某个内部寄存器写入数据 参数: RegAddr:ZLG7290
的内部寄存器地址 dat:要写入的数据 返回: 0:正常 1:访问ZLG7290
时出现异常 *************************************************/ /*bit
ZLG7290_WriteReg(unsigned char RegAddr, char dat) { bit
b; b
= I2C_Puts(ZLG7290_I2C_ID,RegAddr,1,&dat,1); return
b; }*/ /*********************************************** 函数:ZLG7290_cmd() 功能:向ZLG7290
发送控制命令 参数: cmd0:写入CmdBuf0
寄存器的命令字(第1 字节) cmd1:写入CmdBuf1
寄存器的命令字(第2 字节) 返回: 0:正常 1:访问ZLG7290
时出现异常 **************************************************/ bit
ZLG7290_cmd(char cmd0, char cmd1) { bit
b; char
buf[2]; buf[0]
= cmd0; buf[1]
= cmd1; b
= I2C_Puts(ZLG7290_I2C_ID,ZLG7290_CmdBuf,1,buf,2); return
b; } /****************************************************** 函数:ZLG7290_SegOnOff() 功能:段寻址,单独点亮或熄灭数码管(或LED)中的某一段 参数: seg:取值0~63,表示数码管(或LED)的段号 b:0
表示熄灭,1 表示点亮 返回: 0:正常 1:访问ZLG7290
时出现异常 说明: 在每一位数码管中,段号顺序按照“a,b,c,d,e,f,g,dp”进行 *******************************************************/ /*bit
ZLG7290_SegOnOff(char seg, bit b) { char
cmd; cmd
= seg & 0x3F; if
( b ) cmd |= 0x80; return
ZLG7290_cmd(0x01,cmd); }*/ /************************************************* 函数:ZLG7290_Download() 功能:下载数据并译码 参数: addr:取值0~7,显示缓存DpRam0~DpRam7
的编号 dp:是否点亮该位的小数点,0-熄灭,1-点亮 flash:控制该位是否闪烁,0-不闪烁,1-闪烁 dat:取值0~31,表示要显示的数据 返回: 0:正常 1:访问ZLG7290
时出现异常 说明: 显示数据具体的译码方式请参见ZLG7290
的数据手册 *************************************************/ bit
ZLG7290_Download(char addr, bit dp, bit flash, char dat) { char
cmd0; char
cmd1; cmd0
= addr & 0x0F; cmd0
|= 0x60; cmd1
= dat & 0x1F; if
( dp ) cmd1 |= 0x80; if
( flash ) cmd1 |= 0x40; return
ZLG7290_cmd(cmd0,cmd1); } /******************** 函数:SystemInit() 功能:系统初始化 ********************/ void
SystemInit() //等待7290初始化完毕 {
unsigned int i;
for(i = 0;i < 60000;i ++); } /************************* 函数:ClearAll() 功能:清除所有显示 *************************/ void
ClearAll() { unsigned
char x; for
( x=0; x<8; x++ ) {
ZLG7290_Download(x,0,0,31);
//00011111,表示不显示,循环8次,将8个数码管显示都清除 } } 头文件2 #include<ADuC842.h> #include
<stdio.h> #define
I2C_SCL MCO
//ADUC831的I2CCON寄存器中的1位,时钟位 #define
I2C_SDA_O MDO
//ADUC831的I2CCON寄存器中的1位,数据输出 #define
I2C_SDA_I MDI
//ADUC831的I2CCON寄存器中的1位,数据输入 #define
IO_ENABLE MDE
//ADUC831的I2CCON寄存器中的1位,数据输入/输出的使能 #define
ZLGWR 0x70
//ZLG7290的写地址 #define
ZLGRD 0x71
//ZLG7290的读地址 /************************* I2C延时程序 *************************/ void
I2C_Delay() {
char i;
for(i=0;i<50;i++); } /********************************************************* 函数:I2C_Init() 功能:I2C
总线初始化,使总线处于空闲状态 说明:在main()函数的开始处,通常应当要执行一次本函数 ************************************************************/ void
I2C_Init() { IO_ENABLE=1;
//使能为1,则为输出;为0,则为输入 I2C_SCL
= 1; I2C_Delay(); I2C_SDA_O
= 1; I2C_Delay(); } /************************************************************** 函数:I2C_Start() 功能:产生I2C
总线的起始状态 说明: SCL处于高电平期间,当SDA
出现下降沿时启动I2C 总线 不论SDA 和SCL
处于什么电平状态,本函数总能正确产生起始状态 本函数也可以用来产生重复起始状态 本函数执行后,I2C
总线处于忙状态 ******************************************************************/ void
I2C_Start() { IO_ENABLE=1; I2C_SDA_O
= 1; I2C_Delay(); I2C_SCL
= 1; I2C_Delay(); I2C_SDA_O
= 0; I2C_Delay(); I2C_SCL
= 0; I2C_Delay(); } /***************************************** 函数:I2C_Write() 功能:向I2C
总线写1 个字节的数据 参数: dat:要写到总线上的数据 ********************************************/ void
I2C_Write(char dat) {
unsigned char t = 8;
IO_ENABLE=1;
do
{
I2C_SDA_O = (bit)(dat & 0x80);
dat <<= 1;
I2C_SCL = 1;
I2C_Delay();
I2C_SCL = 0;
I2C_Delay();
} while ( --t != 0 ); } /************************************** 函数:I2C_Read() 功能:从从机读取1
个字节的数据 返回:读取的一个字节数据 **************************************/ char
I2C_Read() { char
dat=0; unsigned
char t = 8; do { I2C_SCL
= 0; I2C_Delay(); I2C_SCL
= 1; I2C_Delay(); dat
<<= 1; IO_ENABLE=0; if
( I2C_SDA_I ) dat |= 0x01; I2C_SCL
= 0; I2C_Delay(); }
while ( --t != 0 ); return
dat; } /****************************************************** 函数:I2C_GetAck() 功能:读取从机应答位 返回: 0:从机应答 1:从机非应答 说明: 从机在收到每个字节的数据后,要产生应答位 从机在收到最后1
个字节的数据后,一般要产生非应答位 ***********************************************************/ bit
I2C_GetAck() { bit
ack; IO_ENABLE=1; I2C_SDA_O
= 1; I2C_Delay(); I2C_SCL
= 1; I2C_Delay(); IO_ENABLE=0; ack
= I2C_SDA_I; I2C_SCL
= 0; I2C_Delay(); return
ack; } /******************************************************** 函数:I2C_PutAck() 功能:主机产生应答位或非应答位 参数: ack=0:主机产生应答位 ack=1:主机产生非应答位 说明: 主机在接收完每一个字节的数据后,都应当产生应答位 主机在接收完最后一个字节的数据后,应当产生非应答位 *********************************************************/ void
I2C_PutAck(bit ack) { IO_ENABLE=1; I2C_SDA_O
= ack; I2C_Delay();
I2C_SCL
= 1; I2C_Delay(); I2C_SCL
= 0; } /*************************************************************** 函数:I2C_Stop() 功能:产生I2C
总线的停止状态 说明: SCL处于高电平期间,当SDA
出现上升沿时停止I2C 总线 不论SDA 和SCL
处于什么电平状态,本函数总能正确产生停止状态 本函数执行后,I2C
总线处于空闲状态 ******************************************************************/ void
I2C_Stop() { unsigned
int t =1; IO_ENABLE=1; I2C_SDA_O
= 0; I2C_Delay(); I2C_SCL
= 1; I2C_Delay(); I2C_SDA_O=
1; I2C_Delay(); while
( --t != 0 ); //在下一次产生Start 之前,要加一定的延时 } /***************************************************** 函数? 功能:将I2C总线置于空闲状态,即将时钟线和数据线都拉高 ******************************************************/ void
idle() {
I2C_SCL=1;
IO_ENABLE=1;
I2C_SDA_O=1;
IO_ENABLE=0; } /******************************************************************** 函数:I2C_Puts() 功能:I2C
总线综合发送函数,向从机发送多个字节的数据 参数: SlaveAddr:从机地址(7
位纯地址,不含读写位) SubAddr:从机的子地址 SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址 *dat:要发送的数据 Size:数据的字节数 返回: 0:发送成功 1:在发送过程中出现异常 说明: 本函数能够很好地适应所有常见的I2C
器件,不论其是否有子地址 当从机没有子地址时,参数SubAddr
任意,而SubMod 应当为0 **********************************************************************/ bit
I2C_Puts(unsigned char SlaveAddr,unsigned int SubAddr,unsigned char SubMod,char
*dat,unsigned int Size) { unsigned
char i;
//定义临时变量 char
a[3]; if
( Size == 0 ) return 0; //检查长度 a[0]
= (SlaveAddr << 1);
//准备从机地址 if
( SubMod > 2 ) SubMod = 2; //检查子地址模式 switch
( SubMod ) //确定子地址 {
case 0:break;
case 1:a[1] = (char)(SubAddr);break;
case 2:a[1] = (char)(SubAddr >> 8);a[2] = (char)(SubAddr);break;
default:break; } a[1]
= (char)(SubAddr); SubMod++;
//发送从机地址,接着发送子地址 I2C_Start(); for
( i=0; i<SubMod; i++ ) {
I2C_Write(a[i]);
if ( I2C_GetAck() )
{
I2C_Stop();idle();
return 1;
} } do
//发送数据
{
I2C_Write(*dat++);
if ( I2C_GetAck() ) break;
} while ( --Size != 0 );
I2C_Stop(); idle();
//发送完毕,停止I2C
总线,并返回结果
if ( Size == 0 ) return 0;
else return 1; } /******************************************************************** 函数:I2C_Gets() 功能:I2C
总线综合接收函数,从从机接收多个字节的数据 参数: SlaveAddr:从机地址(7
位纯地址,不含读写位) SubAddr:从机的子地址 SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址 *dat:保存接收到的数据 Size:数据的字节数 返回: 0:接收成功 1:在接收过程中出现异常 说明: 本函数能够很好地适应所有常见的I2C
器件,不论其是否有子地址 当从机没有子地址时,参数SubAddr
任意,而SubMod 应当为0 ********************************************************************/ /*bit
I2C_Gets(unsigned char SlaveAddr,unsigned int SubAddr,unsigned char SubMod,char
*dat,unsigned int Size) { unsigned
char i;
//定义临时变量 char
a[3]; if
( Size == 0 ) return 0; //检查长度 a[0]
= (SlaveAddr << 1); //准备从机地址 if
( SubMod > 2 ) SubMod = 2; //检查子地址模式 if
( SubMod != 0 ) //如果是有子地址的从机,则要先发送从机地址和子地址
{
if ( SubMod == 1 )
//确定子地址
{
a[1] = (char)(SubAddr);
}
else
{
a[1] = (char)(SubAddr >> 8);
a[2] = (char)(SubAddr);
} } SubMod++;
t0=1;
//发送从机地址,接着发送子地址 I2C_Start(); for
( i=0; i<=SubMod; i++ )
{
I2C_Write(a[i]);
} } I2C_PutAck(1); I2C_Stop();
t0=0; I2C_Start();
//这里的I2C_Start()对于有子地址的从机是重复起始状态
//对于无子地址的从机则是正常的起始状态 I2C_Write(a[0]+1);
//发送从机地址 if
( I2C_GetAck() ) { I2C_Stop(); return
1; } for
(;;) //接收数据 { while(
I2C_GetAck() ); *dat
= I2C_Read(); if
( --Size == 0 ) { I2C_PutAck(1); I2C_PutAck(0); break; } } I2C_Stop();
//接收完毕,停止I2C 总线,并返回结果 return
0; }*/ char
gets2(unsigned char subaddr) {
char i,dat;
I2C_Start();
I2C_Write(ZLGWR);
I2C_GetAck();
I2C_Write(subaddr);
I2C_SCL=1;
for(i=0;i<2;i++){;}
I2C_SCL=0;
I2C_Stop();
for(i=0;i<5;i++){;}
I2C_Start();
I2C_Write(ZLGRD);
I2C_GetAck();
dat=I2C_Read();
I2C_PutAck(1);
I2C_Stop();
idle();
return dat; }
|