矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 78620|回复: 131

LTC2400六位半表头完整程序及电路

  [复制链接]
     
发表于 2011-7-21 20:34:54 | 显示全部楼层 |阅读模式
    LTC2400六位表头设计安装调试
该电路制作之后,已连续上机测试6天,稳定性良好。于是,今天把非线性改正程序及菜单功能全部加进去,使之变成完整的表头。
    1、LTC2400的电源采用5V独立供电。数据手册给的测试电路,电源与基准共用,我上机实测效果不好。共电方案,造成自检值偏小数百个字。共电时,滤波电容越大,自检值越大,使用0.4uF比0.3uF,会增加100字左右,但还是很难达到5000,000字。当电路对电容敏感,电路稳定性稍差。所以本电路采用78L05对LTC2400独立供电,自检值接近5000,000字。所谓“自检”指的是将表笔输入端接到LTC2400的Vref端。
    2、78L05接了一个1.5k的负载,其作用是:当LTC2400输入电压过高,输入电流通过内部的输入限幅二极管倒灌进78L05,会靠成LTC2400供电电压抬升。接了1.5k负载,以防输入电压抬升过多。
    3、OP07运放输出加了一个2k限流的电阻,防止对LTC2400输入过强电流、电压。
    4、缓冲设计:LTC2400的Vin端输入阻抗很低,而用是非线性的,这是内部开关电容造成的。当输入信号为0V时Vin会有平均电流输出,约0.5uA至1uA,当输入为2.5Vref时,平均电流最小,当输入为等于Vref时Vin会有0.5uA至1uA平均电流流入。这种非稳恒的平均电流,在外部输入电阻上形成压降,会造成严重非线性的误差。为此,本电路采用OP07进行缓冲放大,得到低频段极低输出电阻,驱动LTC2400,消除输入非线性。
    5、LTC2400的信号输入端的0.22u电容的作用:LTC2400内部开关电容,会在输入端形成脉冲电压、电流,频率很高。加入这个电容后,输入端的脉冲电压基本消失,确保内部输入电容得到快速充电,提高AD的稳定性。由于OP07在高频段,输出电阻很大,因此加入了这个0.22u滤波电容后,在全频段内,得到了低阻抗缓冲输出,缓冲器的增益几乎为1,不易受其它不确定因素影响。该电容还可以减小高频干扰。
    6、与LTC2400连接的电容,使用CBB,有的使用独石,以得到稳定的容量、较低的噪声。试验过程中,曾更换为耐高温的高容量的电脑主板上用的贴片电容,噪声非常大,AD转换非常不稳定,这种电容的温漂非常巨大,决不可用于这个AD转换电路。
    7、7905输出接了一个2k的电阻:op07的工作电流很小,7905不能正常工作,所以接了一个负载。如果使用79L05,估计不接上电阻也能工作。
    8、调零后的自检值,应在5000,000字左右,正负误差几十字不要紧。如果小了几百字,可能是电容失效,或不能工作于高频。如果大了几百字,可能存在严重的电容噪声。
    9、菜单使用方法:
      1)K1键,切换换菜单,每按一次,会在菜单0、1、2、3、4之间切换。
      2)默认菜单是0,显示已进行非线性矫正的AD转换结果。在菜单0下,按下K2键,显示未矫正的AD转换结果(将自动在最后一位加上一个小数点,以示区别)。再次按K2,则显示已矫正的结果。
      3)菜单1设置零点偏移字数;菜单2设置中点非线性误差;菜单3设置满量程字数的低4位;菜单4设置满量程字数的高3位
      4)在菜单1至4中,K2是光标移动键,K3是保存键,K4是更改键。光标移动到第6位,更改正负号。如果菜单4(满量程高3位)被置为负数并保存,下次开机时所有参数将被复位。
    10、零点偏移的测量:将表笔短路得到的读数V0,并把V0保存到菜单1
    11、中点非线性误差的测定:
    在被测基准中,有4个分压电阻。R1、R2看作下臂电阻,R3、R4看作上臂电阻。设下臂电阻的压降为V1,上臂电阻的压降为V2,总电压为Va
    如果表头线性度理想,存在关系式Va = V1+V2,如果不理想,中点非线性误差为a = (V1+V2-Va)/2
    测定非线性误差a时,把表头置为无矫正模式(按下K2键)。
    无矫正模式,是未调零的,所以测量Va、V1、V2时,应做零点偏移改正。设零点为读数为V0,那么就有 a = (V1+V2+V0-Va)/2。将夹子接在“0分压”与“2分压”得到V1,将夹子接在“2分压”与“4分压”得到V2,将夹子接在“0分压”与“4分压”得到Va。
    由于OP07的输入电流不可忽略,所以也须改正。OP07的输入电流比较容易测得,分别测量“分压4”端子和“内阻测量端子”的电压,得一两个电压的差值,再把差值除以100,就得到了OP07的输入电流,单位是nA。若测得的输入电流是I纳安,那么所需的改正值是Vr = (1.5+1-0.5)*I = 2I,比如,测得I=3.5nA,那么Vr = 2*3.5 = 7
    最后,a = (V1+V2-Va+V0+Vr)/2,然后把a值保存到菜单2即可。
    如果觉得OP07输入阻抗低,可以试试OP177
    12、测试注意事项
    1)预热20分钟,测量其间,尽量减小空气流动或电路板移动,以免造成温度不稳定。OP07需要较长时间预热才会稳定下来。
    2)夹子不能用手切换,而应使用镊子操作。5个排针及2个夹子全应等温度才行。否则会产生几个uV的热电势,影响测量精度。
    3)在菜单设置了零点偏移参数,可以大大减小偏移,读取电压时,通常无需减去零点值。如果要求测量特别精确,最好将表笔短路,测出零点偏移量,然后在测量结果中减去该偏移值。

评分

2

查看全部评分

     
 楼主| 发表于 2011-7-21 20:35:46 | 显示全部楼层
电路图
电路图.PNG
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2011-7-21 20:36:40 | 显示全部楼层
本帖最后由 xjw01 于 2011-7-21 21:57 编辑
  1. /*************************************
  2. 6位半LTC2400驱动程序
  3. xjw01 于莆田 2011.7
  4. **************************************/
  5. //====================================
  6. #define uchar unsigned char
  7. #define uint  unsigned int
  8. #define ulong  unsigned long
  9. #include <reg52.h>
  10. #include <math.h>

  11. void delay(uint loop) { uint i; for(i=0;i<loop;i++); } //延时函数
  12. //void delay2(uint k){ for(;k>0;k--) delay(10000); } //长延时,k=100大约对应1秒


  13. //============================EEPROW偏程=========================
  14. sfr IAP_data  = 0xC2;
  15. sfr IAP_addrH = 0xC3;
  16. sfr IAP_addrL = 0xC4;
  17. sfr IAP_cmd   = 0xC5;
  18. sfr IAP_trig  = 0xC6;
  19. sfr IAP_contr = 0xC7;
  20. /********************
  21. 写字节时,可以将原有数据中的1改为0,无法将0改为1,只能使用擦除命令将0改为1
  22. 应注意,擦除命令会将整个扇区擦除
  23. *********************/
  24. uchar readEEP(uint k){ //读取
  25. IAP_addrL = k;    //设置读取地址的低字节,地址改变才需要设置
  26. IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
  27. IAP_contr = 0x81; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
  28. IAP_cmd = 1;      //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
  29. IAP_trig = 0x5A;  //先送5A
  30. IAP_trig = 0xA5;  //先送5A再送A5立即触发
  31. return IAP_data;
  32. }
  33. void writeEEP(uint k, uchar da){ //写入
  34. IAP_data = da;    //传入数据
  35. IAP_addrL = k;    //设置读取地址的低字节,地址改变才需要设置
  36. IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
  37. IAP_contr = 0x81; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
  38. IAP_cmd = 2;      //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
  39. IAP_trig = 0x5A;  //先送5A
  40. IAP_trig = 0xA5;  //先送5A再送A5立即触发
  41. }
  42. void eraseEEP(uint k){ //擦除
  43. IAP_addrL = k;    //设置读取地址的低字节,地址改变才需要设置
  44. IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
  45. IAP_contr = 0x81; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
  46. IAP_cmd = 3;      //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
  47. IAP_trig = 0x5A;  //先送5A
  48. IAP_trig = 0xA5;  //先送5A再送A5立即触发
  49. }

  50. xdata struct Ida{
  51. int xz0; //零点偏移
  52. int xz1; //中点偏移
  53. int FC0; //满程低4位
  54. int FC1; //满程高4位
  55. } cs;

  56. void cs_RW(char rw){
  57. uchar i,*p = &cs;
  58. if(rw){
  59.   eraseEEP(0);
  60.   for(i=0;i<sizeof(cs);i++) writeEEP(i,p[i]);
  61. }else{
  62.   for(i=0;i<sizeof(cs);i++) p[i]=readEEP(i);
  63. }
  64. }



  65. /**********
  66. 字形编码图
  67.    32
  68.    -
  69. 64| | 128
  70.    -  16
  71. 1| | 8
  72.    _. 4
  73.    2
  74. **********/
  75. uchar code zk[20]={235,136,179,186,216,122,123,168,251,250}; //字库

  76. uchar disp[7]={235,136,179,186,216,122,123};
  77. sfr P1M1=0x91; //P1端口设置寄存器
  78. sfr P1M0=0x92; //P1端口设置寄存器
  79. sfr P0M1=0x93; //P0端口设置寄存器
  80. sfr P0M0=0x94; //P0端口设置寄存器
  81. sfr P2M1=0x95; //P2端口设置寄存器
  82. sfr P2M0=0x96; //P2端口设置寄存器
  83. sfr P3M1=0xB1; //P3端口设置寄存器
  84. sfr P3M0=0xB2; //P3端口设置寄存器

  85. sbit ds0=P2^1; //数码管扫描口
  86. sbit ds1=P2^2; //数码管扫描口
  87. sbit ds2=P2^3; //数码管扫描口
  88. sbit ds3=P2^4; //数码管扫描口
  89. sbit ds4=P2^5; //数码管扫描口
  90. sbit ds5=P2^6; //数码管扫描口
  91. sbit ds6=P2^7; //数码管扫描口

  92. sbit K0=P3^4; //键盘
  93. sbit K1=P3^5; //键盘
  94. sbit K2=P3^6; //键盘
  95. sbit K3=P3^7; //键盘




  96. //功能程序开始


  97. void cls(){ char i; for(i=0;i<7;i++) disp[i]=0; } //清屏
  98. void showDig(long f){ //显示数字
  99. uchar i;
  100. cls();
  101. for(i=0;i<7;i++) { disp[i]=zk[f%10], f/=10; if(!f) break; }
  102. }


  103. sbit P_SCK=P1^0; //时钟
  104. sbit P_SDO=P1^1; //数据
  105. sbit P_CS =P1^2; //片选
  106. #define en 5     //平均次数
  107. long pv=0;       //AD转换均值
  108. long jz(long v){ //非线性改正
  109.   float n;
  110.   v -= cs.xz0;
  111.   n = v/1.0/(cs.FC0+cs.FC1*10000.0);
  112.   if(n>0&&n<1) v -= cs.xz1*n*(1-n)*4;
  113.   return v;
  114. }
  115. void get_adc(char zz){
  116. char i; long v=0;   //v是AD转换结果
  117. if(P_SDO) return;
  118. for(i=0;i<32;i++){
  119.    P_SCK = 1; delay(1);
  120.    v <<= 1;
  121.    if(P_SDO) v++;
  122.    P_SCK = 0; delay(1);
  123. }
  124. v -= 0x20000000;
  125. v = (cs.FC0+cs.FC1*10000.0)*v/0x10000000;     //尺长变换
  126. if(labs(v-pv/en)>50) pv = v*en;    //限幅
  127. else                 pv = v + pv - pv/en; //一阶滤波
  128. v = pv/en; //取平均值
  129. if(!zz) v = jz(v); //非线性改正
  130. showDig(labs(v));      //显示
  131. if(v<0) disp[6] += 16; //显示负号
  132. if(zz) disp[0]+=4;     //不矫正
  133. }


  134. void zd0(void) interrupt 0 {//int0中断(下降沿)
  135. }



  136. void timerInter(void) interrupt 1 {//T0中断
  137. }

  138. int inc_cs(int a,char d){ //a的d位加1
  139. char i,f=1;
  140. int v=10;
  141. if(d<0) return a;
  142. if(d==5) return -a;
  143. if(a<0) a=-a,f=-1;
  144. for(i=0;i<d;i++) v*=10;
  145. if(a%v+v/10 < v) return f*(a+v/10);
  146. else             return f*(a+v/10-v);
  147. }

  148. main(){
  149. uchar dispN=0; char nx=0; //显示扫描索引
  150. uchar menu=0,gb=-1,kn=0,K,zz=0;
  151. int *p;

  152. TCON=0, TMOD=0x09; //将T0置为16位内部计数,并由外部启动计数。
  153. IT0=1; //使int0下降沿中断有效。
  154. TH0=0, TL0=0;
  155. TR0=1;  //T0启动计数
  156. EX0=1;  //开int0外部中断
  157. ET0=1;  //T0开中断
  158. EA=1;   //开总中断
  159. PT0=1;  //中断优先


  160. P2M0 = 0xFE;    //P2.1234567置为推勉输出
  161. //P1M0 = 0x05;    //P1.02置为推勉输出
  162. //P1M1 = 0x02;  //P1.1置为高阻抗
  163. //P3M0 = 0x0C;    //P3.23置为推勉输出口

  164. delay(4000);
  165. P_CS=1; delay(1);
  166. P_SCK=0;delay(1);
  167. P_CS=0;

  168. cs_RW(0);
  169. if(cs.FC1<0){ cs.xz0=0, cs.xz1=0, cs.FC0=0, cs.FC1=500; cs_RW(1); }
  170. while(1){
  171.   //显示disp
  172.   dispN = (++dispN)%7; //扫描器移动
  173.   nx++; if(nx>100)nx-=200;
  174.   ds0=ds1=ds2=ds3=ds4=ds5=ds6=0;
  175.   if(dispN==0) ds0=1;
  176.   if(dispN==1) ds1=1;
  177.   if(dispN==2) ds2=1;
  178.   if(dispN==3) ds3=1;
  179.   if(dispN==4) ds4=1;
  180.   if(dispN==5) ds5=1;
  181.   if(dispN==6) ds6=1;
  182.   if(dispN==gb && nx>0) P0 = 255; //不显示
  183.   else P0 = ~disp[dispN]; //显示
  184.   K = ( ~(P3>>4) ) & 15;
  185.   if(K) { if(kn<255) kn++; } else kn = 0; //判断是否有按键按下
  186.   if(kn!=20) K=0;  //按下时间不够长,键值无产
  187.   if(K==1) { menu++; if(menu>4) menu=0,gb=-1; else gb=0; } //切换菜单
  188.   if(menu==0) {   //读取AD电压
  189.     if(K==2) zz=(++zz)%2; //设置是否矫正
  190.     get_adc(zz);
  191.   }
  192.   if(menu>=1&&menu<=4){
  193.    if(menu==1) p = &cs.xz0;
  194.    if(menu==2) p = &cs.xz1;
  195.    if(menu==3) p = &cs.FC0;
  196.    if(menu==4) p = &cs.FC1;
  197.    if(K==2) { gb++; if(gb>5) gb=0; }  //光标键
  198.    if(K==4) cs_RW(1);            //保存
  199.    if(K==8) *p = inc_cs(*p,gb);  //改值
  200.    showDig( abs(*p) );
  201.    if(*p<0) disp[5]=16; //显示负号
  202.    disp[6] = zk[menu];  //显示菜单号
  203.    disp[gb]+=4; //用小数点表示光标
  204.   }
  205.   delay(4000);
  206. }//while end
  207. }

复制代码
回复 支持 反对

使用道具 举报

     
发表于 2011-7-21 21:22:28 | 显示全部楼层
顶,很完整的资料,
回复 支持 反对

使用道具 举报

     
发表于 2011-7-21 21:38:28 | 显示全部楼层
记号,以后慢慢学习。
回复 支持 反对

使用道具 举报

     
发表于 2011-7-21 22:06:52 | 显示全部楼层
楼主辛苦,好资料,慢慢学。
回复 支持 反对

使用道具 举报

     
发表于 2011-7-21 22:30:00 | 显示全部楼层
非常不错的学习资料!
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2011-7-25 22:01:05 | 显示全部楼层
vvv.PNG
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2011-7-25 22:02:34 | 显示全部楼层
测了半小时,平均值都是5.0000115伏特
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2011-8-11 21:30:36 | 显示全部楼层
天风雪雨老师调试,发现图画错了。
故重新发一个。
程序也做了改动,原来的程序有点问题,EEPROM读取有时不正常。
电路图.PNG

  1. /*************************************
  2. 6位半LTC2400驱动程序
  3. xjw01 于莆田 2011.7
  4. **************************************/
  5. //====================================
  6. #define uchar unsigned char
  7. #define uint  unsigned int
  8. #define ulong  unsigned long
  9. #include <reg52.h>
  10. #include <math.h>

  11. void delay(uint loop) { uint i; for(i=0;i<loop;i++); } //延时函数
  12. void delay2(uint k){ for(;k>0;k--) delay(10000); } //长延时,k=100大约对应1秒


  13. //============================EEPROW偏程=========================
  14. sfr IAP_data  = 0xC2;
  15. sfr IAP_addrH = 0xC3;
  16. sfr IAP_addrL = 0xC4;
  17. sfr IAP_cmd   = 0xC5;
  18. sfr IAP_trig  = 0xC6;
  19. sfr IAP_contr = 0xC7;
  20. /********************
  21. 写字节时,可以将原有数据中的1改为0,无法将0改为1,只能使用擦除命令将0改为1
  22. 应注意,擦除命令会将整个扇区擦除
  23. *********************/
  24. uchar readEEP(uint k){ //读取
  25. IAP_addrL = k;    //设置读取地址的低字节,地址改变才需要设置
  26. IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
  27. IAP_contr = 0x80; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
  28. IAP_cmd = 1;      //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
  29. IAP_trig = 0x5A;  //先送5A
  30. IAP_trig = 0xA5;  //先送5A再送A5立即触发
  31. return IAP_data;
  32. }
  33. void writeEEP(uint k, uchar da){ //写入
  34. IAP_data = da;    //传入数据
  35. IAP_addrL = k;    //设置读取地址的低字节,地址改变才需要设置
  36. IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
  37. IAP_contr = 0x80; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
  38. IAP_cmd = 2;      //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
  39. IAP_trig = 0x5A;  //先送5A
  40. IAP_trig = 0xA5;  //先送5A再送A5立即触发
  41. }
  42. void eraseEEP(uint k){ //擦除
  43. IAP_addrL = k;    //设置读取地址的低字节,地址改变才需要设置
  44. IAP_addrH = k>>8; //设置读取地址的高字节,地址改变才需要设置
  45. IAP_contr = 0x80; //设置等待时间,1MHz以下取7,2M以下取6,3M取5,6M取4,12M取3,20M取2,24M取1,30M取0,前导1表示许档IAP
  46. IAP_cmd = 3;      //读取值1,写取2,擦除取3,擦除时按所在字节整个扇区撺除
  47. IAP_trig = 0x5A;  //先送5A
  48. IAP_trig = 0xA5;  //先送5A再送A5立即触发
  49. }

  50. xdata struct Ida{
  51. int xz0; //零点偏移
  52. int xz1; //中点偏移
  53. int FC0; //满程低4位
  54. int FC1; //满程高4位
  55. int en;
  56. } cs;

  57. void cs_RW(char rw){
  58. uchar i,*p = &cs;
  59. if(rw){
  60.   eraseEEP(0);
  61.   for(i=0;i<sizeof(cs);i++) writeEEP(i,p[i]);
  62. }else{
  63.   for(i=0;i<sizeof(cs);i++) p[i]=readEEP(i);
  64. }
  65. }

  66. //============================串口偏程=========================

  67. #define reN 8
  68. xdata uchar reB[reN+1]={0}, reC=0; //串口接收缓冲区,reC串接收完成标识。
  69. sfr BRT = 0x9c;   //独立波特率发生器重装寄存器
  70. sfr AUXR = 0x8e;  //BRT控制寄存器
  71. void initial(void){
  72.   SCON=0x50;       //串口工作方式1(8bit),允许接收
  73.   PCON=0x80;       //波特率翻倍
  74.   AUXR=0x15;       //BRT以1T运行,选择BRT,启动BRT
  75.   BRT = 0xef;      //115200
  76.   EA=1;            //开总中断
  77.   ES=1;            //开串口中断
  78.   REN=1;           //允许接收
  79. }
  80. void sendB(char c){ //发送字节
  81.   REN=0; //发送过程中禁止接收数据
  82.   ES=0,  TI=0; //关串口中断,中断标识置0
  83.   SBUF = c;
  84.   while( !TI );
  85.   TI=0, ES=1;  //关串口中断,中断标识置0
  86.   REN=1; //允许接收
  87. }
  88. void sendS(char *B, char n){ //发送数组
  89. char i;
  90. for(i=0; i<n && B[i]; i++) sendB(B[i]);
  91. }
  92. void sendI(long a){ //发送整数
  93. xdata char i, B[10];
  94. if(a<0) sendB('-'), a = -a;
  95. else    sendB('+');
  96. for(i=0;i<10;i++) B[9-i] = a%10+48, a /= 10;
  97. sendS(B,10);
  98. sendB('\r');
  99. sendB('\n');
  100. }
  101. void serial(void) interrupt 4 { //串口中断处理
  102.   static char n=0;
  103.   if(TI){ TI=0; return; } //处理发送中断
  104.   RI=0;                   //清接收中断标志
  105.   if(SBUF=='e') reC=1, reB[n++] = 0, n=0; //行结束标识
  106.   else          reC=0, reB[n++] = SBUF;   //读入数据
  107.   if(n>=reN) n=0;
  108. }


  109. //============================主程序=========================
  110. /**********
  111. 字形编码图
  112.    32
  113.    -
  114. 64| | 128
  115.    -  16
  116. 1| | 8
  117.    _. 4
  118.    2
  119. **********/
  120. uchar code zk[20]={235,136,179,186,216,122,123,168,251,250}; //字库

  121. uchar disp[7]={235,136,179,186,216,122,123};
  122. sfr P1M1=0x91; //P1端口设置寄存器
  123. sfr P1M0=0x92; //P1端口设置寄存器
  124. sfr P0M1=0x93; //P0端口设置寄存器
  125. sfr P0M0=0x94; //P0端口设置寄存器
  126. sfr P2M1=0x95; //P2端口设置寄存器
  127. sfr P2M0=0x96; //P2端口设置寄存器
  128. sfr P3M1=0xB1; //P3端口设置寄存器
  129. sfr P3M0=0xB2; //P3端口设置寄存器

  130. sbit ds0=P2^1; //数码管扫描口
  131. sbit ds1=P2^2; //数码管扫描口
  132. sbit ds2=P2^3; //数码管扫描口
  133. sbit ds3=P2^4; //数码管扫描口
  134. sbit ds4=P2^5; //数码管扫描口
  135. sbit ds5=P2^6; //数码管扫描口
  136. sbit ds6=P2^7; //数码管扫描口

  137. sbit K0=P3^4; //键盘
  138. sbit K1=P3^5; //键盘
  139. sbit K2=P3^6; //键盘
  140. sbit K3=P3^7; //键盘




  141. //功能程序开始


  142. void cls(){ char i; for(i=0;i<7;i++) disp[i]=0; } //清屏
  143. void showDig(long f){ //显示数字
  144. uchar i;
  145. cls();
  146. for(i=0;i<7;i++) { disp[i]=zk[f%10], f/=10; if(!f) break; }
  147. }


  148. sbit P_SCK=P1^0; //时钟
  149. sbit P_SDO=P1^1; //数据
  150. sbit P_CS =P1^2; //片选

  151. xdata long  pv1=0,pv2=0,pvm=0;       //滤波的积分器(算法一)
  152. xdata float av1=0,av2=0; char avn=0; //滤波的积分器(算法二)
  153. xdata long Ux=0;    //滤波输出
  154. char ms=1;    //数据处理模式
  155. char upv=0;

  156. void get_adc(){
  157. //注意:长整形不可直接与float相加,否则损精度,必要时采用强制转换防止损度损失
  158. char i,en1,en2;
  159. long v=0; //AD转换结果
  160. float n;  //插值因子
  161. long  f = cs.FC0+cs.FC1*10000L; //满量程定标值

  162. if(P_SDO) return;    //检测转换状态
  163. for(i=0;i<32;i++){        //读取串行数据
  164.    P_SCK = 1; delay(1);
  165.    v <<= 1;
  166.    if(P_SDO) v++;
  167.    P_SCK = 0; delay(1);
  168. }
  169. v = v/16-0x02000000; //截取24bit,并处理符号位
  170. v*=10;

  171. en1 = cs.en%100; //一阶滤波步长
  172. en2 = cs.en/100; //二阶滤波步长
  173. if(!en1) en1 = 1;
  174. if(en2){ //滞后滤波法
  175.   long vm = v/10000, vc = v%10000;
  176.   if(labs(Ux-v)<1000 ){ //限幅二阶滤波
  177.     vc += (vm-pvm)*10000L;
  178.     pv1 += vc - pv1/en1;
  179.     pv2 += pv1 - pv2/en2;
  180.   }else pvm=vm, pv1=vc*en1, pv2 = pv1*en2;
  181.   Ux = pvm*10000L + pv2/en2/en1;
  182. }else{ //加权平均值法
  183.    av2 += v, avn++;
  184.    if(avn>=en1) avn=0, av1=av2, av2=0;
  185.    Ux = ( av1-av1*avn/en1 + av2 ) / en1;
  186. }

  187. n = 0.1*Ux/0x01000000; if(n<0||n>1) n =0;   //抛物线插值因子
  188. for(i=0,v=0;i<8;i++) v += Ux*(f&7), v>>=3, f>>=3; //尺长变换

  189. if(ms!=0) v -= (int) ( (cs.xz0 + cs.xz1*n*(1-n)*4)*10 );//非线性改正
  190. if(upv) sendI(v);  //上载数据
  191. if(ms!=2) v /= 10; //不扩展字数

  192. showDig(labs(v));      //显示
  193. if(v<0) disp[6] += 16; //显示负号
  194. if(ms==1) disp[0]+=4;  //标识已矫正
  195. if(ms==2) disp[1]+=4;  //标识已矫正并扩展
  196. }


  197. int inc_cs(int a,char d){ //a的d位加1
  198. char i,f=1;
  199. int v=10;
  200. if(d<0) return a;
  201. if(d==5) return -a;
  202. if(a<0) a=-a,f=-1;
  203. for(i=0;i<d;i++) v*=10;
  204. if(a%v+v/10 < v) return f*(a+v/10);
  205. else             return f*(a+v/10-v);
  206. }

  207. main(){
  208. uchar dispN=0; char nx=0; //显示扫描索引
  209. uchar menu=0,gb=-1,kn=0,K,zz=0;
  210. int *p;


  211. P2M0 = 0xFE;    //P2.1234567置为推勉输出
  212. //P1M0 = 0x05;  //P1.02置为推勉输出
  213. //P1M1 = 0x02;  //P1.1置为高阻抗
  214. //P3M0 = 0x0C;  //P3.23置为推勉输出口

  215. delay2(40);
  216. P_CS=1; delay(1);
  217. P_SCK=0;delay(1);
  218. P_CS=0;

  219. cs_RW(0); //读EEPROW须在上电之后200ms
  220. if(cs.en<0){ cs.xz0=0, cs.xz1=0, cs.FC0=0, cs.FC1=500, cs.en=406; cs_RW(1); }
  221. initial();//串口初始化

  222. while(1){
  223.   if(reC){
  224.     if(reB[0]=='a' && reB[1]=='0') upv=1; //发送电压指令
  225.     if(reB[0]=='a' && reB[1]=='1') upv=0; //停止电压指令
  226.     reC=0;
  227.   }
  228.   //显示disp
  229.   dispN = (++dispN)%7; //扫描器移动
  230.   nx++; if(nx>100)nx-=200;
  231.   ds0=ds1=ds2=ds3=ds4=ds5=ds6=0;
  232.   if(dispN==0) ds0=1;
  233.   if(dispN==1) ds1=1;
  234.   if(dispN==2) ds2=1;
  235.   if(dispN==3) ds3=1;
  236.   if(dispN==4) ds4=1;
  237.   if(dispN==5) ds5=1;
  238.   if(dispN==6) ds6=1;
  239.   if(dispN==gb && nx>0) P0 = 255; //不显示
  240.   else P0 = ~disp[dispN]; //显示
  241.   K = ( ~(P3>>4) ) & 15;
  242.   if(K) { if(kn<255) kn++; } else kn = 0; //判断是否有按键按下
  243.   if(kn!=20) K=0;  //按下时间不够长,键值无产
  244.   if(K==1) { menu++; if(menu>5) menu=0,gb=-1; else gb=0; } //切换菜单
  245.   if(menu==0) {   //读取AD电压
  246.     if(K==2) ms=(++ms)%3; //设置显示模式
  247.     get_adc();
  248.   }
  249.   if(menu>=1&&menu<=5){
  250.    if(menu==1) p = &cs.xz0;
  251.    if(menu==2) p = &cs.xz1;
  252.    if(menu==3) p = &cs.FC0;
  253.    if(menu==4) p = &cs.FC1;
  254.    if(menu==5) p = &cs.en;
  255.    if(K==2) { gb++; if(gb>5) gb=0; }  //光标键
  256.    if(K==4) cs_RW(1);            //保存
  257.    if(K==8) *p = inc_cs(*p,gb);  //改值
  258.    showDig( abs(*p) );
  259.    if(*p<0) disp[5]=16; //显示负号
  260.    disp[6] = zk[menu];  //显示菜单号
  261.    disp[gb]+=4; //用小数点表示光标
  262.   }
  263.   delay(4000);
  264. }//while end
  265. }



复制代码


菜单更改如下:

9、菜单使用方法:
1)K1键,切换换菜单,每按一次,会在菜单0、1、2、3、4、5之间切换。
2)菜单0是默认菜单,显示已进行非线性矫正的AD转换结果。在菜单0下,按下K2键,可以分别显示三种结果:未矫正值(无小数点)、已桥正值(末位出现小数点)、已矫正且显示到第7位(倒数第二位出现小数点)
3)菜单1设置零点偏移字数;菜单2设置中点非线性误差;
4)菜单3设置满量程字数的低4位;菜单4设置满量程字数的高3位
5)菜单5,设置滤波器步长。个位和十位设置第一阶滤波器长度,百位和千位设置第二阶滤波器长度。第二阶设置为1,相当于一个一阶滤波器,第二阶设置为0,程序转为多点平均法滤波,平均个数由个位和百位设置。
如,置为310,二阶为3,一阶为10。置为10,是10点平均滑动滤波。置为11,是一阶滤波。
6)在菜单1至5中,K2是光标移动键,K3是保存键,K4是更改键。光标移动到第6位,更改正负号。第7位显示菜单号。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2011-8-11 21:37:52 | 显示全部楼层
这个程序中带有数据采集功能。
通过串口,给单片机发送“a0e”字符,单片机就会不断的把当前电压值发送给电脑。
通过串口,给单片机发送“a1e”字符,单片机就会停止发送数据。
回复 支持 反对

使用道具 举报

     
发表于 2011-8-11 22:08:23 | 显示全部楼层
强大,上几张实物瞧瞧,
回复 支持 反对

使用道具 举报

     
发表于 2011-8-11 22:10:53 | 显示全部楼层
之前想问楼主要的    没想到楼主那么快就公布资料了
请问这个单片机用89s52可以吗?
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2011-8-11 22:14:59 | 显示全部楼层
不能使用89s52,否则得更改程序。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2011-8-14 06:39:00 | 显示全部楼层
等一下我再改一下程序
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-4-30 06:37

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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