找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7646|回复: 29
收起左侧

求教STC15F2K60S2 ADC采集问题,辛苦各位群友

  [复制链接]
ID:164172 发表于 2018-4-2 10:36 | 显示全部楼层 |阅读模式
30黑币
1、ADC采集出现+-1的偏移量;
例:采集到的数值始终在570  571 572 三个数值中跳变
2、采集目标为NTC温感电阻
3、原理图如下

NTC原理

NTC原理


4、ADC采集及滤波代码如下
  1. <div>#include <adc.h>
  2. #include "math.h"

  3. #define ADC_POWER   0x80            //ADC电源控制位
  4. #define ADC_FLAG    0x10            //ADC完成标志位
  5. #define ADC_START   0x08            //ADCADC起始控制位
  6. #define ADC_SPEEDLL 0x00            //540个始终周期
  7. #define FILTER_N 12
  8. const float Rp=10000.0; //10K
  9. const float T2 = (273.15+25.0);//T2
  10. const float Bx = 3950.0;//B
  11. const float Ka = 273.15;
  12. /*----------------------------
  13. 读取ADC结果
  14. ----------------------------*/
  15. uint GetADCResult(uchar ch)
  16. {
  17.                 uint b;
  18.                 uint c;
  19.     ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
  20.     _nop_();                        //等待
  21.     _nop_();
  22.     _nop_();
  23.     _nop_();
  24.     while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成
  25.     ADC_CONTR &= ~ADC_FLAG;         //Close ADC
  26.                 b = ADC_RES;
  27.                 b = b << 2;
  28.                 c = ADC_RESL;
  29.                 b = b | c ;
  30.                 ADC_RES = 0;
  31.                 ADC_RESL = 0;
  32.     return b;                 //返回ADC结果
  33. }

  34. /****************滤波*****************/
  35. </div><div>
  36. uint filter(uchar chnn)
  37. {
  38.          uint i,j;
  39.          uint filter_temp, filter_sum = 0;
  40.          uint filter_buf[FILTER_N];
  41.          for(i = 0; i < FILTER_N; i++)
  42.          {
  43.                         filter_buf[i] = GetADCResult(chnn);
  44.                          Delay2(2);
  45.          }
  46.          for(j = 0; j < FILTER_N - 1; j++)
  47.          {
  48.         for(i = 0; i < FILTER_N - 1 - j; i++)
  49.                 {
  50.         

  51.                 if (filter_buf[i] > filter_buf[i + 1])
  52.                         {
  53.                                 filter_temp = filter_buf[i];
  54.                                 filter_buf[i] = filter_buf[i + 1];
  55.                                 filter_buf[i + 1] = filter_temp;
  56.                          }
  57.          }
  58.          }
  59.          
  60.                 for(i = 1; i < FILTER_N - 1; i++)
  61.                                         filter_sum += filter_buf[i];
  62.                 return filter_sum / (FILTER_N - 2);
  63. }
  64. /********AD数值转换阻值**********/
  65. double AD_R(uchar chn)
  66. {
  67.         double ADR;
  68.         double b;
  69.         double a;
  70.         ADR = filter(chn);
  71.         b = 1023 - ADR;
  72.         a = 10000/b*ADR;
  73.         return a;
  74. }

  75. /************阻值转换温度******************/
  76. float Get_Temp(uchar chhhh)
  77. {
  78.                 float Rt;
  79.                 float temp;
  80.                 Rt = AD_R(chhhh);
  81.                 //like this R=5000, T2=273.15+25,B=3470, RT=5000*EXP(3470*(1/T1-1/(273.15+25)),  
  82.                 temp = Rt/Rp;  //Rp=10000.0; //10K
  83.                 temp = log(temp);//ln(Rt/Rp)
  84.                 temp/=Bx;//ln(Rt/Rp)/B
  85.                 temp+=(1/T2);
  86.                 temp = 1/(temp);
  87.                 temp-=Ka;
  88.                 temp = temp*10;
  89.                 return temp;
  90. }
  91. /*----------------------------
  92. 初始化ADC
  93. ----------------------------*/
  94. void InitADC()
  95. {
  96.     P1ASF = 0xff;                   //设置P1口为AD口
  97.     ADC_RES = 0;                    //清除结果寄存器
  98.     ADC_CONTR = ADC_POWER | ADC_SPEEDLL;
  99.     Delay2(2);                       //ADC上电延时



  100. /*----------------------------
  101. 延时
  102. ----------------------------*/
  103. void Delay2(uint n)
  104. {
  105.     uint x;

  106.     while (n--)
  107.     {
  108.         x = 5000;
  109.         while (x--);
  110.     }
  111. }</div><div>
  112. </div><div>请教如何能让计算得到的温度变得稳定,或者滤波后的ADC数值稳定。。。</div><div>希望各位大神能帮忙一二

  113. </div>
复制代码



回复

使用道具 举报

ID:164172 发表于 2018-4-2 20:40 | 显示全部楼层
那位大神来帮帮我啊 怎么一个人都没有呢   求救啊
回复

使用道具 举报

ID:164172 发表于 2018-4-4 10:56 | 显示全部楼层
不是说好了  24小时必答吗???求指教啊……那位前辈高人上帝老大来帮我一下  卡了好多天了
回复

使用道具 举报

ID:251640 发表于 2018-4-4 13:14 | 显示全部楼层
电源对其精度影响也比较大,弄个电源基准再试试.

评分

参与人数 1黑币 +3 收起 理由
大米饭 + 3 在我的计算中并没有考虑到基准电压,计算采.

查看全部评分

回复

使用道具 举报

ID:213173 发表于 2018-4-4 15:42 | 显示全部楼层
NTC热敏电阻的温阻曲线是非线性的,用浮点计算的方法误差很大。一般用厂家提供的温阻表为基础制作数组表格,用ADC转换结果通过查表法获取温度值为佳。

评分

参与人数 1黑币 +3 收起 理由
大米饭 + 3 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:159139 发表于 2018-4-4 20:21 来自手机 | 显示全部楼层
采用查表法,并使用较稳定电源。
回复

使用道具 举报

ID:286600 发表于 2018-4-4 20:53 来自手机 | 显示全部楼层
AD转换时尽可能不改变单先机各脚电平,再一个是用基准电压也做AD转换,根据基准电压值,两个转换值,求所测电压。转换值应是多次测量弃最大最小后得的均值。

评分

参与人数 1黑币 +3 收起 理由
大米饭 + 3 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:298533 发表于 2018-4-5 13:18 | 显示全部楼层
你采集的值要用滤波器算法,这样的值才稳定

评分

参与人数 1黑币 +3 收起 理由
大米饭 + 3 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:96072 发表于 2018-4-5 13:47 | 显示全部楼层
网上有详细的滤波器算法说明,找找下
回复

使用道具 举报

ID:164172 发表于 2018-4-7 10:00 | 显示全部楼层
wulin 发表于 2018-4-4 15:42
NTC热敏电阻的温阻曲线是非线性的,用浮点计算的方法误差很大。一般用厂家提供的温阻表为基础制作数组表格 ...

讨论的问题并不是温度是否准确,而是ADC采集的数据出现浮动漂移,谢谢你的回答
回复

使用道具 举报

ID:164172 发表于 2018-4-7 10:02 | 显示全部楼层
nklug 发表于 2018-4-4 20:53
AD转换时尽可能不改变单先机各脚电平,再一个是用基准电压也做AD转换,根据基准电压值,两个转换值,求所测 ...

我的代码中,包含了软滤波,主要是ADC采集数值一直在570   571  572 中来回跳转,致使计算得出的温度一直跳变
回复

使用道具 举报

ID:164172 发表于 2018-4-7 10:04 | 显示全部楼层
15861476366 发表于 2018-4-5 13:18
你采集的值要用滤波器算法,这样的值才稳定

有滤波算法,依旧出现温度跳变,这个不知道怎么样才能解决,或者有什么更好的滤波算法
回复

使用道具 举报

ID:164172 发表于 2018-4-7 10:04 | 显示全部楼层
HEIZI555 发表于 2018-4-5 13:47
网上有详细的滤波器算法说明,找找下

代码中已经包含滤波算法,并且尝试了多种滤波算法  均不能保证每次滤波后出现稳定数值
回复

使用道具 举报

ID:213173 发表于 2018-4-7 21:18 | 显示全部楼层
大米饭 发表于 2018-4-7 10:00
讨论的问题并不是温度是否准确,而是ADC采集的数据出现浮动漂移,谢谢你的回答

不管你采用什么方法滤波都不容易得到非常稳定的数值,“采集数值一直在570   571  572 中来回跳转”说明你得到的数据已经足够稳定了,只是你对数据的后期处理不到位,才致使你感觉不稳定。
回复

使用道具 举报

ID:303420 发表于 2018-4-8 03:47 | 显示全部楼层
可以查表,然后换个算法试试。
回复

使用道具 举报

ID:273583 发表于 2018-4-8 07:14 | 显示全部楼层
NTC热敏电阻的温阻曲线是非线性的,用浮点计算的方法误差很大。一般用厂家提供的温阻表为基础制作数组表格,用ADC转换结果通过查表法获取温度值为佳。
与7楼意见相同!
回复

使用道具 举报

ID:301968 发表于 2018-4-8 09:35 | 显示全部楼层
如果数值在一个范围内稳定变化,可以在三个值取平均值,那不就很稳定了吗?
回复

使用道具 举报

ID:301968 发表于 2018-4-8 09:36 | 显示全部楼层
三个值相加,取平均值使用。
回复

使用道具 举报

ID:303835 发表于 2018-4-8 17:24 | 显示全部楼层
求学,跟着各位大佬学习
回复

使用道具 举报

ID:164172 发表于 2018-4-12 10:47 | 显示全部楼层
邓文雄ABC 发表于 2018-4-8 09:35
如果数值在一个范围内稳定变化,可以在三个值取平均值,那不就很稳定了吗?

并不是每次都是这样的,采集三次 可能出现的全都是572  572 572   也可能是573 572 572   
回复

使用道具 举报

ID:644988 发表于 2019-11-23 15:25 | 显示全部楼层
你这个是硬件问题,103P不要串联在电阻上,换成106P一边接地当滤波电容,这样就不会跳了。
回复

使用道具 举报

ID:245004 发表于 2019-11-23 22:50 | 显示全部楼层
21楼的建议值得参考,虽然不一定是那个原因。电容是对于交流是导通的,你接到VCC起到相反的作用。
建议给Avcc接一个大电解。
前面有个别回答是不对的,这个电路不必苛求供电电压多精准,或者需要什么基准电压,你可以根据公式推导一下就知道为什么了。
抱歉的是晚上真的没精力看楼主的程序了,不过记得之前曾这样做这种平滑电压:例如,一个数组有N个变量,每次把最新的和之前的N-1个变量平均,每次淘汰最早的一个,这样波动会降到最小。改天有精力再学习楼主的程序。


评分

参与人数 1黑币 +40 收起 理由
admin + 40 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:245004 发表于 2019-11-24 16:45 | 显示全部楼层
刚才看了这位朋友的程序,大概过程看明白了,那个温度函数没用过,详细原理暂时不了解。对这段程序做了个简单的修改。
主要是对ADC做了一点修改:因为每次要关闭ADC,所以下次打开应该等待稳定延时;
对计算函数做了一点简化:因为偏置电阻阻值10K等于Rp,所以抵消了一步计算;
对滤波做了一点修改:因为用左移代替除法能更快一点;
以上修改对结果估计也没啥帮助,只是共师兄习一下。因为没有条件测试,如有错误望网友及时指正。


#include <adc.h>
#include "math.h"

#define ADC_POWER 0x80    // ADC电源控制位
#define ADC_FLAG 0x10     // ADC完成标志位
#define ADC_START 0x08    // ADC起始控制位
#define ADC_SPEEDLL 0x00  // 540个时钟周期
// #define BUFF_LENGTH 12
const float Rp = 10000.0;          // 10K
const float T2 = (273.15 + 25.0);  // T2
const float Bx = 3950.0;           // B
const float Ka = 273.15;
/*----------------------------
读取ADC结果
----------------------------*/
uint GetADCResult(uchar ch) {
  ADC_CONTR = ADC_POWER | ADC_SPEEDLL;
  Delay2(2);  // ADC上电延时
  ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
  while (!(ADC_CONTR & ADC_FLAG)) {
  }                                        //等待ADC转换完成
  ADC_CONTR &= ~ADC_FLAG;                  // Close ADC
  return ((uint)ADC_RES << 2 | ADC_RESL);  //返回ADC结果
}

/****************滤波*****************/
#define BUFF_LENGTH 18  // 过滤函数数组长度
uint filter(uchar chnn) {
  uint i;
  uint j;
  uint uiTemp;
  uint uiSum;
  uint uiBuf[BUFF_LENGTH];

  for (i = 0; i < BUFF_LENGTH; i++) {
    uiBuf[i] = GetADCResult(chnn);
    Delay2(2);
  }
  // 排序:把最大值移到数组最后,最小值留到数组最前
  for (j = 0; j < BUFF_LENGTH - 1; j++) {
    for (i = 0; i < BUFF_LENGTH - 1 - j; i++) {
      if (uiBuf[i] > uiBuf[i + 1]) {
        uiTemp = uiBuf[i];
        uiBuf[i] = uiBuf[i + 1];
        uiBuf[i + 1] = uiTemp;
      }
    }
  }
  // 取中间(BUFF_LENGTH - 2)个数,求平均值
  for (i = 1; i < BUFF_LENGTH - 1; i++) {
    uiSum += uiBuf[i];
  }

  // uiSum = uiSum / (BUFF_LENGTH - 2);
  uiSum >>= 4;  // uiSum / 16
  return uiSum;
}


/********AD数值转换阻值**********/
/*double AD_R(uchar chn) {
  double adcDataTR;  // 温敏电阻上的分压数据
  double adcDataR;   // 偏置电阻上的分压数据
  double rT;         // 温敏电阻阻值
  adcDataTR = filter(chn);
  adcDataR = 1023 - adcDataTR;
  rT = 10000 / adcDataR * adcDataTR;
  return rT;
}*/

/************阻值转换温度******************/
float Get_Temp(uchar chhhh) {
  // float Rt;
  float adcDataTr;  // 温敏电阻上的分压的ADC数据
  float temp;
  // Rt = AD_R(chhhh);
  adcDataTr = filter(chhhh);
  // like this R=5000, T2=273.15+25,B=3470,
  // RT=5000*EXP(3470*(1/T1-1/(273.15+25)),
  // temp = Rt / Rp;
  // = 10000 / adcDataR * adcDataTr / Rp;
  // 因为(Rp=上拉偏置电阻10000)
  // temp = adcDataTr / adcDataR;
  temp = adcDataTr / (1023 - adcDataTr);
  temp = log(temp);  // ln(Rt/Rp)
  temp /= Bx;        // ln(Rt/Rp)/B
  temp += (1 / T2);
  temp = 1 / (temp);
  temp -= Ka;
  temp += 0.5;  // 加0.5误差矫正
  temp = temp * 10;
  return temp;
}
/*----------------------------
初始化ADC
----------------------------*/
void InitADC(void) {
  P1ASF = 0xff;  //设置P1口为AD口
  CLK_DIV &= ~0x20;
  ADC_RES = 0;  //清除结果寄存器
  ADC_RESL = 0;
}

/*----------------------------
延时
----------------------------*/
void Delay2(uint n) {
    uint x;

    while (n--)
    {
        x = 5000;
        while (x--);
    }
}



回复

使用道具 举报

ID:25310 发表于 2019-11-24 20:51 来自手机 | 显示全部楼层
这是误差之内的数字可以接受啊,后起处理就行了
回复

使用道具 举报

ID:25310 发表于 2019-11-25 08:09 | 显示全部楼层
哪个并在上拉电阻上的电容有点诡异,并到NTC上倒可以理解!
回复

使用道具 举报

ID:586438 发表于 2019-11-25 08:16 | 显示全部楼层
我也遇到这样的问题  我是用温度来控制继电器的打开和关闭   因为跳变导致继电器一直嗒嗒嗒
回复

使用道具 举报

ID:644988 发表于 2019-11-25 16:40 | 显示全部楼层
bh2030693 发表于 2019-11-23 22:50
21楼的建议值得参考,虽然不一定是那个原因。电容是对于交流是导通的,你接到VCC起到相反的作用。
建议给A ...

其实我司在做项目时都是这样做,一般做锂电池电量检测开关保护都是在单片机采样脚前(尽量离IC脚要近)串个1K再用一个106P滤波,效果很好。如果要实时显示,做个一表或取个平均值就行。一个项目要软硬结合才是最好解决方法。
回复

使用道具 举报

ID:262356 发表于 2019-11-26 21:23 | 显示全部楼层
你硬件改下试试
无标题.jpg
回复

使用道具 举报

ID:25310 发表于 2019-11-28 09:12 | 显示全部楼层
snchj 发表于 2019-11-26 21:23
你硬件改下试试

这个硬件同我的想法一样,我的AD也用了47uF,对反应不用太快的检测大电容确实有效。
回复

使用道具 举报

ID:25310 发表于 2019-11-28 09:24 | 显示全部楼层
最近我也在调试一个AD相关的项目,刚好测试了这段滤波代码,感觉确实波动很大。用了一个最基本的平均数滤波也比这个效果好。楼主可以试一试。
        u16 Get_Adc_Average(u8 ch,u8 times)
        {
         u32 temp_val=0;
          
         u8 t;
               
        for(t=0;t<times;t++)
        {         
        temp_val+= Get_ADC10bitResult(ch);
               
        delay_us(10);
        }
        return temp_val/times;
        }
       
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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