摘要,本作品由增益放大与控制模块、极性转换模块、触发以及以aduc841/ aduc831为核心的单片机最小系统4个部分构成,由单片机实现控制与波形存储回放功能。<?XML:NAMESPACE PREFIX = O />
一、方案论证与设计
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=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);
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);
}
}
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;
}
文章评论(0条评论)
登录后参与讨论