找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3526|回复: 1
收起左侧

热偶真空计设计

[复制链接]
ID:71259 发表于 2014-12-29 02:23 | 显示全部楼层 |阅读模式
学院真空实验室的热偶真空计表头损坏了,托人找了许多仪器仪表商店、公司,买不到型号相符的。前些天,王博找到我,让我做一个数字式表头取代之(原表头乃是指针式的),已知基本数据如下:
        热偶内阻:未知
        表头内阻:要求在40R~~300R之间的某一值,但该值未知
        仪器处于“测量”档位时,热偶的输出电压可能通过了一个限流电阻加在了表头上(阻值未知),通过表头的电压:0~~10mV; 电压对应于真空室的压强:260.00Pa~~0.10Pa(详见数据表格)
        仪器处于“加热”档位时,表头与一个未知阻值电阻并联,要求正确显示干路总电流(范围:20.0~40.0mA),以便对热偶真空规管的工作电流进行控制、校准,使之正常工作。由于外部并联的小电阻阻值未知,通过表头的电流亦不能事先确定(根据实际测试数据估算,应不大于2mA)
        热偶真空计输出电压与电流关系:
        原始表格:  
电压(mV)  压强(Pa)
0.8  266.000  
0.9  200.500  
1.0  201.191  
1.1  136.381  
1.3  106.386  
1.4  88.344  
1.6  76.475  
1.7  67.260  
1.9  59.942  
2.0  54.052  
2.2  49.441  
2.4  45.134  
2.5  40.935  
2.7  37.576  
2.8  34.458  
3.0  31.893  
3.1  29.704  
3.3  27.514  
3.5  25.616  
3.6  23.932  
3.8  22.389  
3.9  21.004  
4.1  19.798  
4.2  18.532  
4.4  17.518  
4.5  16.542  
4.7  15.645  
4.9  14.834  
5.0  14.072  
5.2  13.319  
5.3  12.594  
5.5  11.869  
5.6  11.142  
5.8  10.460  
5.9  9.912  
6.1  9.364  
6.3  8.841  
6.4  8.321  
6.6  7.824  
6.7  7.364  
6.9  6.903  
7.0  6.445  
7.2  5.991  
7.3  5.536  
7.5  5.119  
7.7  4.744  
7.8  4.368  
8.0  3.994  
8.1  3.659  
8.3  3.325  
8.4  2.990  
8.6  2.657  
8.8  2.350  
8.9  2.042  
9.1  1.735  
9.2  1.428  
9.4  1.080  
9.5  0.837  
9.7  0.569  
9.8  0.294  
10.0  0.010

原始曲线:


        首先,需要数据表格转换成电压以0.1mV步进的新表格:使用origin75绘图软件,把原始数据拟合成一条电最相近的曲线,然后对曲线进行重新取点,得出以0.1mV电压步进的“电压—压强”数据表格。
新的数据表格:
电压(mV)     压强(Pa)
0.8  260.00  
0.9  210.94
1.0  164.99
1.1  138.95
1.2  117.94
1.3  101.91
1.4  90.34
1.5  81.51
1.6  75.29
1.7  69.62
1.8  64.18
1.9  59.63
2.0  55.56
2.1  52.29
2.2  49.56
2.3  46.44
2.4  44
2.5  41.09
2.6  38.88
2.7  36.96
2.8  34.95
2.9  33.03
3.0  31.65
3.1  29.96
3.2  28.81
3.3  27.29
3.4  26.15
3.5  25
3.6  24.11
3.7  22.86
3.8  22.06
3.9  21.07
4.0  20.27
4.1  19.42
4.2  18.62
4.3  18.03
4.4  17.4
4.5  16.74
4.6  16.13
4.7  15.64
4.8  15.1
4.9  14.64
5.0  14.16
5.1  13.62
5.2  13.16
5.3  12.68
5.4  12.22
5.5  11.76
5.6  11.28
5.7  10.82
5.8  10.36
5.9  10.06
6.0  9.69
6.1  9.26
6.2  9.1
6.3  8.73
6.4  8.39
6.5  8.02
6.6  7.69
6.7  7.4
6.8  7.11
6.9  6.84
7.0  6.55
7.1  6.25
7.2  5.92
7.3  5.65
7.4  5.36
7.5  5.1
7.6  4.84
7.7  4.62
7.8  4.4
7.9  4.14
8.0  3.92
8.1  3.71
8.2  3.49
8.3  3.27
8.4  3.06
8.5  2.84
8.6  2.64
8.7  2.46
8.8  2.25
8.9  2.07
9.0  1.85
9.1  1.67
9.2  1.46
9.3  1.26
9.4  0.87
9.5  0.9
9.6  0.73
9.7  0.55
9.8  0.37
9.9  0.19
10.0  0.01

新的曲线:


硬件电路:
        电路比较简单:使用OP07进行两级同相放大,输出电压送至M8的AD0中进行AD采样(内部2.56V参考电压),经M8处理之后,送到液晶进行显示。第一级同相输入端通过一个1K的精调电阻并接到地,以便控制表头的输入电阻(内阻)为0R~1000R的任意值。

软件算法律:
        AD采样的值经M8内部处理,转换成电压显示数据,一方面进行显示,一方面换算成对应的压强与电流数据,分别进行显示。值得一提的是,电流与电压之间必然具有线性关系,表现为具有一个等效电阻R=V/I,由于R的值未知,可能在0.00~600.00R之间,应此另外设置按键控制R的值在0.00~600.00之间,当然,设置的值必须存于M8内部非易失性存储器EEPROM中,以便永久保存。
        由于M8引IO引脚较少,只设置两个控制键以节省IO口。两个控制键,一个加,一个减,完全可以实现控制。为了使提高控制速度,每个控键均具有长按、短按、连发功能。短按一次,加0.01或减0.01;长按(长于1s属于),则每隔300ms加1或减1。
        另外,为了减少运算量,几乎所有的小数运算皆代以整数运算进行。例如,100.72-50.61,在程序中表示为10072-5061,运算结果为5011,最终显示时,先显示5011/100(整数部分),再显示“.”(打小数点),最后显示5011%100(小数部分),显示实际效果为:50.11,与原始运算结果一致。这种显示方式显然稍复杂一些,但由于不使用浮点运算,大大减少了程序代码及运算时间,提高了程序效率。
        本设计的滤波设计颇有特点(主要是指软件滤波)。除硬件上注重退耦滤波之外,在软件上设置采样频率为50HZ(也可以是100HZ、200HZ等),这样可以去除市电工频工扰,使显示相当的稳定。测试7.4mV电压,若以30Hz、48Hz、60Hz等与50不成整倍数关系的采样频率,显示电压将在7.2mV~7.6mV之间随机跳动;而若以25、50Hz、100Hz进行采样,则显示将稳定在7.2mV,连续观测60s未出现波动,比数字万用表的测量结果还稳定。除此之外,为进一步提高测量精度与稳定性(主要是稳定性),采取连续采样50次求平均值的办法。

        这里指出一个屡试不爽的经验结论:采样测量系统中,使用过采样技术,具有微量纹波的信号比纯净的稳定信号的测量更精确。例如,使用过采样技术,10的AD以128倍于信号纹波频率的频率采样,理论上可以获得最高17位的精度!曾有人使用AVR的10AD做电阻测量,做到了16位精度。故而,某些时候,为了用较低位数的AD获得较高的采样精度,甚至需要给被测信号人为加入某种频率的纹波。令人震惊!——为什么呢?因为,过采样技术可以提高采样精度——但对于纯净的直流信号无效!!

        好了,不罗嗦了,下面发程序:

/*****************************************************************************
      单        位:广西民族大学物理与电子工程学院07物本班
      文件名称:ZD0-2热偶真空计表头.c
      摘        要:ZD0-2热偶真空计表头主程序文件
      当前版本:V2.0
      作        者:白  羽
      完成日期:2010年10月5日
*****************************************************************************/

#define  CRYSTAL   7.3728        //设置晶振频率

#define  LCD_RS    P(PORTD,2)    //设置液晶与M8的引脚连接
#define  LCD_EN    P(PORTD,3)
#define  LCD_DN    P(PORTD,H)
#include<iom8v.h>
#include<eeprom.h>
#include<白羽AVR库\LCD.h>
#include<白羽AVR库\Timer.h>
const UINT16 VPdata[93] = {                                     //“电压—压强”表格
26000,21094,16499,                                             /* 0.8--1.0 */
13895,11794,10191,9034,8151,7529,6962,6418,5963,5556,          /* 1.1--2.0 */
5229 ,4956 ,4644 ,4400,4109,3888,3696,3495,3303,3165,          /* 2.1--3.0 */
2996 ,2881 ,2729 ,2615,2500,2411,2286,2206,2107,2027,          /* 3.1--4.0 */
1942 ,1862 ,1803 ,1740,1674,1613,1564,1510,1464,1416,          /* 4.1--5.0 */
1362 ,1316 ,1268 ,1222,1176,1128,1082,1036,1006,969 ,          /* 5.1--6.0 */
926  ,910  ,873  ,839 ,802 ,769 ,740 ,711 ,684 ,655 ,          /* 6.1--7.0 */
625  ,592  ,565  ,536 ,510 ,484 ,462 ,440 ,414 ,392 ,          /* 7.1--8.0 */
371  ,349  ,327  ,306 ,284 ,264 ,246 ,225 ,207 ,185 ,          /* 8.1--9.0 */
167  ,146  ,126  ,87  ,90  ,73  ,55  ,37  ,19  ,1              /* 9.1--10.0*/
};
UINT8  flag = 0;                       //10ms中断标志
UINT8  count = 0;                      //10ms累加标志
UINT16  AdData = 0;                    //电压 (mV)*10
UINT32 AdSum = 0;                      //50次AD采样和
UINT16 Rin = 1254;                     //输入电阻*100
UINT16 KeyCount = 0;                   //控键按下时间
union EUINT16
{
UINT16 edata;
UINT8  echar[2];
};
void StartAdc(void);                    //启动模数转换
void RingBell(UINT8 Pascal);            //低压强时响铃
void LcdShowVoltage(UINT16 AdData);     //显示电压数据
void LcdShowCurrent(UINT16 AdData);     //显示电流数据
void LcdShowPascal(UINT8 AdData);       //显示压强数据
UINT8 ScanfKey(void);                   //按键处理程序

void EEPROMWriteData(UINT16 data)
{
union EUINT16 eRin;
eRin.edata = data;
EEPROMWriteBytes(10,eRin.echar,2);           //写temp[3]到以10为首的eeprom中
}
UINT16 EEPROMReadData(void)
{
union EUINT16 eRin;
EEPROMReadBytes(10,eRin.echar,2);           //写temp[3]到以10为首的eeprom中
return eRin.edata;
}

void main(void)
{
LcdInit();
Rin = EEPROMReadData();           //读取eeprom值
Timer1Period(2,21702);            //10ms定时
StartAdc();                       //启动AD转换
Enable_OC1A();
Enable_INTERRUPTS();
while(1)
{
  if(flag == 1)
  {
   flag = 0;
   count++;
   AdSum += ADC;
   StartAdc();
   if(count == 50)
   {
    static UINT8 VIflag = 0;
    count = 0;
    AdData = AdSum/75.1;     //50次的平均电压值(mV)*10 [8--100]
    AdSum = 0;
    /**********************************/         //500mS
    if(AdData < 120)        //小于12.0mV,是电压采样信号
    {
     if(VIflag == 0)
     {
      VIflag = 1;
      Disable_INTERRUPTS();
      LcdInit();
      Enable_INTERRUPTS();
     }
     LcdLocate(1,12);LcdShowString("  V ");
     LcdLocate(2,12);LcdShowString("Heat");
     LcdLocate(1,3); LcdShowVoltage(AdData);   //显示电压
     LcdLocate(2,2); LcdShowPascal(AdData);    //显示压强
    }
    else                    //大于12.0mV,是电流采样信号
    {
     if(VIflag == 1)
     {
      VIflag = 0;
      Disable_INTERRUPTS();
      LcdInit();
      Enable_INTERRUPTS();
     }
     LcdLocate(1,12);LcdShowString("  I ");
     LcdLocate(2,12);LcdShowString("Test");
     LcdLocate(1,3); LcdShowVoltage(AdData);   //显示电压
     LcdLocate(2,3); LcdShowCurrent(AdData);   //显示电流
    }
    /**********************************/
   }
   /***************************************************/      //10mS
   {
    UINT8 key = ScanfKey();
    if(key == BIT(2))                       //左键
    {
     if(KeyCount == 2)                    //短按
     {
      Rin++;
     }
     else if((KeyCount > 50) && (KeyCount <= 60000))             //长按
     {
      if((KeyCount%15) == 0)            //每500ms加100
      {
       Rin += 100;
      }
     }
     EEPROMWriteData(Rin);
    }
    else if(key == BIT(0))          //右键
    {
     if(KeyCount == 2)
     {
      Rin--;
     }
     else if((KeyCount > 50) && (KeyCount <= 60000))             //长按
     {
      if((KeyCount%15) == 0)            //每500ms加100
      {
       Rin -= 100;
      }
     }
     EEPROMWriteData(Rin);
    }
   }
   /***************************************************/
  }
}
}
//启动AD转换
void StartAdc(void)
{
ADMUX = BIT(6)|BIT(7) + 0;                  //通道0、内部2.56V
ADCSRA = BIT(ADEN)|BIT(ADSC)|BIT(ADIF) + 6; //ADC使能、开始转换、清标志
}
//显示电压(mV):两位小数
void LcdShowVoltage(UINT16 AdData)
{
if(AdData >= 100)
{
  LcdShowNumber(AdData/10,2);
}
else
{
  LcdShowChar(' ');
  LcdShowNumber(AdData/10,1);
}
LcdShowChar('.');
LcdShowNumber(AdData%10,1);
LcdShowString("0mV");
}
//显示电流(mA):两位小数
void LcdShowCurrent(UINT16 AdData)
{
UINT32 Current = (UINT32)AdData * 10000 / Rin;
if(Current%10 >= 5)
{
  Current += 10;
}
Current /= 10;

if(Current >= 10000)
{
  LcdShowString(" I=****");
  return;
}
if(Current >= 1000)
{
  LcdShowNumber(Current/100,2);
}
else
{
  LcdShowChar(' ');
  LcdShowNumber(Current/100,1);
}

LcdShowChar('.');
LcdShowNumber(Current%100,2);
LcdShowString("mA ");
}
//显示压强
void LcdShowPascal(UINT8 AdData)
{
UINT16 Pascal;
static UINT8 flag = 0;

//电压数据变换成压强数据
if(AdData < 8)          //压强很大(电压小于0.8mV)
{
  Pascal = 26001;      //用一个很大的值表示超量程
}
else if(AdData <= 100)  //正常压强范围
{
  Pascal = *(VPdata + AdData - 8);
}
else                    //压强很小(电压大于10.0mV)
{
  Pascal = 0;          //用一个很小的值表示小压强
}

//压强数据小于10(即压强小于0.1Pa)时提醒0.5秒
if(Pascal <= 10)
{
  if(flag < 2)
  {
   DDRB  |= BIT(1);
   PORTB |= BIT(1);
   flag++;
  }
  else
  {
   PORTB &= ~BIT(1);
   flag = 2;
  }
}
else
{
  flag = 0;
}

//将压强数据送至液晶进行显示
if(Pascal > 26000)
{
  LcdShowString(" P=**** ");
  return;
}
else if(Pascal >= 10000)
{
  LcdShowNumber(Pascal/100,3);
}
else if(Pascal >= 1000)
{
  LcdShowChar(' ');
  LcdShowNumber(Pascal/100,2);
}
else
{
  LcdShowString("  ");
  LcdShowNumber(Pascal/100,1);
}
LcdShowChar('.');
LcdShowNumber(Pascal%100,2);
LcdShowString("Pa");
}

UINT8 ScanfKey(void)
{
UINT8 flag = 0;
static UINT8 NewKey = 0, OldKey = 0;
DDRB  |= BIT(0)|BIT(2);
PORTB |= BIT(0)|BIT(2);
NewKey = PINB & (BIT(0) | BIT(2));
if(NewKey == OldKey)              //两次相等
{
  if(NewKey == (BIT(0) | BIT(2)))            //两次相等但为空
  {
   flag = 0;               //返回空
   KeyCount = 0;
  }
  else                       //两次相等且非空
  {
   flag = 1;               //返回键值
   KeyCount++;
  }
}
OldKey = NewKey;
return (flag == 1) ? NewKey : 0;
}

#pragma interrupt_handler Timer1_10ms_ISR:iv_OC1A
void Timer1_10ms_ISR(void)
{
flag = 1;
}

/********************************END END END END *******************************************************/

通过设计这个表头,我有两点体会:
一、深切体会到设计一个完整理的系统与单独完成一个简单的功能,那是相差很远的两种境界。后者通常简单,由此很容易得出前者“也不难”的结论。而实际上,前者比后者难实现多了!完成前者,需要一整套的理论知识、一整套的工程思想、一整套处理困难的方法,一整套的实践经验,还要相当的细心、考察现实中所有可能够的情形并区别对待,要关注尽可能多的Bug(系统漏洞)并做好相应的防范措施。系统还要做足硬件保护,不仅要保护本身,还要保护与这衔接的外部系统;当两者矛盾时,还要懂得折中处理。
二、软件编程上,强烈需要一个多任务操作系统。没有这个系统,多任务的程序编写将大受限制。目前,技术领域中,AVR单片机尚未有成熟的操作系统,我还是有必要实现一个,哪怕有一点Bug。

回复

使用道具 举报

ID:70769 发表于 2014-12-29 03:15 | 显示全部楼层
GOOD!!!
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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