搜索
bottom↓
回复: 66

使用24位ADC差分测量多次,如何计算平均值

[复制链接]

出0入10汤圆

发表于 2016-1-31 22:38:21 | 显示全部楼层 |阅读模式
24位ADC输出是2进制补码。当负端电平高于正端时,ADC输出的最高位是1,此时如何计算多次测量结果的平均值?

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2016-1-31 22:48:31 | 显示全部楼层
最直接的方法就是把AD数值加上0xFFFFFF,转换为无符号的数再滤波。   

出0入0汤圆

发表于 2016-1-31 23:07:49 | 显示全部楼层
not_at_all 发表于 2016-1-31 22:48
最直接的方法就是把AD数值加上0xFFFFFF,转换为无符号的数再滤波。

不对吧? 加上0xFFFFF对补码来说相当于-1

应当是:转成整数(取反+1)  0-7FFFFF表示负数,800000表示0, 800001-FFFFFF表示正数

出0入0汤圆

发表于 2016-1-31 23:16:50 | 显示全部楼层
做个判断就行了,假设是24位ADC

  1. int32_t code;

  2. code = Adc_read_data();

  3. if ((code & 0x800000) != 0)
  4. {
  5.   code -= 0x1000000;
  6. }

  7. ......
复制代码

出0入0汤圆

发表于 2016-1-31 23:23:27 | 显示全部楼层
gamalot 发表于 2016-1-31 23:16
做个判断就行了,假设是24位ADC

或者先向左移8位,再除以256

这样不需要判断,看上去很有技巧的样子,逼格略高

但是俺不喜欢这样的代码,因为它不直观

  1. int32_t code;

  2. code = Adc_read_data();

  3. code <<= 8;
  4. code /= 256;

  5. .......
复制代码

出10入23汤圆

发表于 2016-1-31 23:31:34 来自手机 | 显示全部楼层
gamalot 发表于 2016-1-31 23:16
做个判断就行了,假设是24位ADC

借你的代码出个优化简洁版本:
int32_t code;
code = Adc_read_data();
code -= (code & 0x800000)  << 1;

出0入0汤圆

发表于 2016-1-31 23:37:54 | 显示全部楼层
zouzhichao 发表于 2016-1-31 23:31
借你的代码出个优化简洁版本:
int32_t code;
code = Adc_read_data();

你这个实际上就是最常用的方法,最简洁高效,缺点和就是更加不够直观

出10入23汤圆

发表于 2016-1-31 23:40:08 来自手机 | 显示全部楼层
gamalot 发表于 2016-1-31 23:23
或者先向左移8位,再除以256

这样不需要判断,看上去很有技巧的样子,逼格略高

int32_t code;
code = Adc_read_data();
code = (int32_t)(((uint32_t)code) << 8);
code >>= 8;
这里的移位需要用到强制转换或者使用union吧?

出10入23汤圆

发表于 2016-1-31 23:40:58 来自手机 | 显示全部楼层
gamalot 发表于 2016-1-31 23:37
你这个实际上就是最常用的方法,最简洁高效,缺点和就是更加不够直观

  ...

哈哈,习惯就好

出0入0汤圆

发表于 2016-1-31 23:54:35 | 显示全部楼层

老糊涂了俺,看见你的语句最后有个1,就以为是最近本的负数补码转源码算法,取反加一

出0入0汤圆

发表于 2016-2-1 00:05:32 | 显示全部楼层
本帖最后由 gamalot 于 2016-2-1 00:08 编辑
zouzhichao 发表于 2016-1-31 23:40
int32_t code;
code = Adc_read_data();
code = (int32_t)(((uint32_t)code) >= 8;


不用阿,左移的时候,它其实就是个正数

而且,C语言左移一定是逻辑左移(如果俺没记错的话   )

出10入23汤圆

发表于 2016-2-1 00:07:22 来自手机 | 显示全部楼层
gamalot 发表于 2016-1-31 23:54
老糊涂了俺,看见你的语句最后有个1,就以为是最近本的负数补码转源码算法,取反加一

  ...

我就是把你的那个if化简了而已
(code & 0x800000)无非两个结果,0或者0x800000
0: code -= 0
0x800000: code -= 0x1000000

0 = 0 << 1;
0x1000000 = 0x800000 << 1;
化简就成了code -= (code & 0x800000) << 1;

出0入0汤圆

发表于 2016-2-1 00:11:55 | 显示全部楼层
zouzhichao 发表于 2016-2-1 00:07
我就是把你的那个if化简了而已
(code & 0x800000)无非两个结果,0或者0x800000
0: code -= 0

是的,俺刚才没看的仔细,俺这里凌晨3点多了 ......

你这个很好,就是看起来不够直接而已

出10入23汤圆

发表于 2016-2-1 00:15:41 来自手机 | 显示全部楼层
gamalot 发表于 2016-2-1 00:05
不用阿,左移的时候,它其实就是个正数

而且,C语言左移一定是逻辑左移(如果俺没记错的话    ...

谢谢提醒,你是对的,刚验证了

出0入0汤圆

发表于 2016-2-1 07:18:23 来自手机 | 显示全部楼层
babysnail 发表于 2016-1-31 23:07
不对吧? 加上0xFFFFF对补码来说相当于-1

应当是:转成整数(取反+1)  0-7FFFFF表示负数,800000表示0,  ...

24位补码,应该是0x7ffff为正23位最大值,0x800000为负23位最小值,0xffffff为—1,0x000000为0值,0x000001为1值,其他依此类推。

出0入25汤圆

发表于 2016-2-1 08:21:55 | 显示全部楼层
我是冲着24位ADC进来的.

出0入0汤圆

发表于 2016-2-1 08:58:53 | 显示全部楼层
楼主搞定了没有,正为这个犯愁呢,谢谢

出0入0汤圆

发表于 2016-2-1 09:19:15 | 显示全部楼层
gamalot 发表于 2016-1-31 23:16
做个判断就行了,假设是24位ADC

收下了,拿去试试,这样可以直接和正数相加了是不,那和正数求平均怎么处理,直接除以相加的数量,比如四个正数和负数相加就除以4,谢谢

出0入0汤圆

发表于 2016-2-1 09:35:05 来自手机 | 显示全部楼层
gamalot 发表于 2016-1-31 23:16
做个判断就行了,假设是24位ADC

为什么不直接取平均?还要做这样的转换?

出0入0汤圆

发表于 2016-2-1 09:40:07 来自手机 | 显示全部楼层
wiser803 发表于 2016-2-1 07:18
24位补码,应该是0x7ffff为正23位最大值,0x800000为负23位最小值,0xffffff为—1,0x000000为0值,0x000 ...

那这样应该就不需要楼上的那些坛友的程序吧?直接求和取平均就可以了吧。

出0入0汤圆

发表于 2016-2-1 10:30:54 | 显示全部楼层
其实 MCU 用于计算的负数表示形式就是 二进制补码,其实基本不用转换的只要把24位转换为32位直接计算即可

uint32_t adc_read = adc_readdata();
int32_t adc_val = adc_read;
if (adc_val & bit(23)) {adc_val |= 0xff000000;}

出0入0汤圆

发表于 2016-2-1 10:54:40 来自手机 | 显示全部楼层
knight_avr 发表于 2016-2-1 10:30
其实 MCU 用于计算的负数表示形式就是 二进制补码,其实基本不用转换的只要把24位转换为32位直接计算即可

...

你的程序应该是对的。

出0入0汤圆

发表于 2016-2-1 14:33:58 | 显示全部楼层
PCBBOY1991 发表于 2016-2-1 09:35
为什么不直接取平均?还要做这样的转换?

不转换肯定是不行的,因为从ADC读回的是24位的补码,不能直接拿来用

区别只是大家转换的方法,俺的比较笨拙,其他几位网友的方法比较巧妙

出0入0汤圆

发表于 2016-2-1 14:48:16 | 显示全部楼层
ersha4877 发表于 2016-2-1 09:19
收下了,拿去试试,这样可以直接和正数相加了是不,那和正数求平均怎么处理,直接除以相加的数量,比如四 ...

一般就是窗口平滑滤波,取最后N个读取值的平均,稍微复杂一点的可以去掉一个最高值和一个最低值

一般来说N取值越大滤波效果越好,但是反应速度越慢,具体多少要根据你的应用的情况自己进行调整

再复杂一点的可以判断数值是不是在大幅度变化,是的话就丢弃前面的数据加快反应速度,总之花样还挺多的 ......

出0入0汤圆

发表于 2016-2-1 14:54:58 来自手机 | 显示全部楼层
gamalot 发表于 2016-1-31 23:23
或者先向左移8位,再除以256

这样不需要判断,看上去很有技巧的样子,逼格略高

左移8位再除以256本质不就是右移三位吗?

出0入0汤圆

发表于 2016-2-1 14:57:18 来自手机 | 显示全部楼层
gamalot 发表于 2016-1-31 23:23
或者先向左移8位,再除以256

这样不需要判断,看上去很有技巧的样子,逼格略高

( ఠൠఠ )啊  不对  左移已经补零了

出0入0汤圆

发表于 2016-2-1 15:29:39 | 显示全部楼层
gamalot 发表于 2016-2-1 14:48
一般就是窗口平滑滤波,取最后N个读取值的平均,稍微复杂一点的可以去掉一个最高值和一个最低值

一般来 ...

谢谢了,最近在弄20位的CS5513和楼主同样的问题,负数正数相加求平均的问题,谢谢你的方法了

出0入0汤圆

发表于 2016-2-1 20:27:19 | 显示全部楼层
gamalot 发表于 2016-2-1 14:33
不转换肯定是不行的,因为从ADC读回的是24位的补码,不能直接拿来用

区别只是大家转换的方法,俺的比较 ...

补码不就是可以直接计算去平均的么?
转换后还能知道AD输入的正负?

出0入0汤圆

发表于 2016-2-1 20:32:57 | 显示全部楼层
PCBBOY1991 发表于 2016-2-1 20:27
补码不就是可以直接计算去平均的么?
转换后还能知道AD输入的正负?

ADC返回的是24位补码,但是C语言里是没有这个数据类型的,要先转换成32位才能进行其他运算

出0入0汤圆

发表于 2016-2-1 20:37:33 | 显示全部楼层
gamalot 发表于 2016-2-1 20:32
ADC返回的是24位补码,但是C语言里是没有这个数据类型的,要先转换成32位才能进行其他运算   ...

那你的4楼代码是不是少个零?
21楼的代码也是对的吧?

出0入0汤圆

发表于 2016-2-1 20:39:35 | 显示全部楼层
gamalot 发表于 2016-2-1 14:48
一般就是窗口平滑滤波,取最后N个读取值的平均,稍微复杂一点的可以去掉一个最高值和一个最低值

一般来 ...

可以用滑动滤波,这样速度基本不变的。

出0入0汤圆

发表于 2016-2-1 20:47:47 | 显示全部楼层
PCBBOY1991 发表于 2016-2-1 20:37
那你的4楼代码是不是少个零?
21楼的代码也是对的吧?

数了一下,不少0

21楼的代码和4楼的代码本质上是一样的

出0入0汤圆

发表于 2016-2-1 20:48:52 | 显示全部楼层
PCBBOY1991 发表于 2016-2-1 20:39
可以用滑动滤波,这样速度基本不变的。

所谓的“窗口滤波”就是“滑动滤波”,一回事儿

不知道你说的速度不变是啥意思 ......

出0入0汤圆

发表于 2016-2-1 21:08:05 | 显示全部楼层
gamalot 发表于 2016-2-1 20:48
所谓的“窗口滤波”就是“滑动滤波”,一回事儿

不知道你说的速度不变是啥意思 ...... ...

就是平均后的结果的时间和数据更新的时间一样啊,不像取多次平均是数据更新的好多倍。

出0入0汤圆

发表于 2016-2-1 21:23:26 | 显示全部楼层
PCBBOY1991 发表于 2016-2-1 21:08
就是平均后的结果的时间和数据更新的时间一样啊,不像取多次平均是数据更新的好多倍。 ...

哦,俺说的就是你说的“滑动滤波”,当参与平均的数据比较多(窗口队列比较长)的时候,反应会变慢

如果既要滤除干扰、平滑噪声,又要输入跳变时反应迅速,就需要加入判断才行 ......

出0入0汤圆

发表于 2016-2-2 10:47:04 | 显示全部楼层
計算機的強項是即時計算, 而且還要24位的, 移動平均法不合適, 窗口越大越效率差.
做一階或二階的LPF濾波算法才是合適的, 一個當前的輸入量, 一個輸出量,  一個疊代的算法變量, 就三個變數, 不需要更多的RAM, 這個從8位機到64位機都好使

出0入0汤圆

发表于 2016-2-2 11:33:13 | 显示全部楼层
xiaolaba 发表于 2016-2-2 10:47
計算機的強項是即時計算, 而且還要24位的, 移動平均法不合適, 窗口越大越效率差.
做一階或二階的LPF濾波算 ...

请问能举一个例子吗?

出0入0汤圆

发表于 2016-2-2 11:59:55 | 显示全部楼层
也关心楼上的问题。

出0入4汤圆

发表于 2016-2-2 14:00:32 | 显示全部楼层
zouzhichao 发表于 2016-1-31 23:31
借你的代码出个优化简洁版本:
int32_t code;
code = Adc_read_data();

这个办法好,mark一下。24位补码转32位补码。

出0入4汤圆

发表于 2016-2-2 14:10:01 | 显示全部楼层
lgc150 发表于 2016-2-1 14:54
左移8位再除以256本质不就是右移三位吗?

首先除以256 不是右移,除法会保持符号位不变,右移就不是这样了。
其次,256应该是2^8吧  。        <( ̄ˇ ̄)/

出0入0汤圆

发表于 2016-2-2 20:58:47 来自手机 | 显示全部楼层
xiaolaba 发表于 2016-2-2 10:47
計算機的強項是即時計算, 而且還要24位的, 移動平均法不合適, 窗口越大越效率差.
做一階或二階的LPF濾波算 ...

希望给个例子,谢谢

出0入0汤圆

发表于 2016-2-2 21:31:36 | 显示全部楼层
xiaolaba 发表于 2016-2-2 10:47
計算機的強項是即時計算, 而且還要24位的, 移動平均法不合適, 窗口越大越效率差.
做一階或二階的LPF濾波算 ...

软件滤波算法常见的就有十余种之多,不能简单地说哪一种更好,而是要看具体情况下哪一种更适合

出0入50汤圆

发表于 2016-2-2 22:47:36 | 显示全部楼层
老实讲,CS5513的性价比实在不行了,还不如用ADS1232,把内部PGA置1倍,比5513那个不是好了一点点啊,价格也便宜许多。更要命的是5513的输出速率个体误差很大。

双极性AD码,直接把负向满量程值作为0点,所有采集到的数据,统一加上23位的值,统一向上平移,转换到无符号整型变量操作,就方便多了,尤其是51这样的8位机使用起来,速度会快很多。

在51里应用,如果对速度要求高,我常常会把读取AD的变量定义在bdata,再把这个变量的bit23做sbit定义,这样判断AD值的正负极性,就从判断一个32位变量,转成判别一个bit的状态,速度快很多,相关后续处理也会快速简便多了。

出0入4汤圆

发表于 2016-2-2 23:31:26 | 显示全部楼层
ilikemcu 发表于 2016-2-2 22:47
老实讲,CS5513的性价比实在不行了,还不如用ADS1232,把内部PGA置1倍,比5513那个不是好了一点点啊,价格 ...

你的意思是ad值都加上0x00800000然后再作处理吗?
如果ad真值为-1那么返回数据为FFFFFF 加800000偏移了以后好像不对啊。

出0入0汤圆

发表于 2016-2-2 23:47:02 来自手机 | 显示全部楼层
xiaolaba 发表于 2016-2-2 10:47
計算機的強項是即時計算, 而且還要24位的, 移動平均法不合適, 窗口越大越效率差.
做一階或二階的LPF濾波算 ...

这个软件滤波方法不错,请问能给个参考的代码吗?

出10入23汤圆

发表于 2016-2-3 08:45:21 来自手机 | 显示全部楼层
kation122 发表于 2016-2-2 23:47
这个软件滤波方法不错,请问能给个参考的代码吗?

http://www.amobbs.com/forum.php?mod=viewthread&tid=5639187&page=1#pid8906258

出0入4汤圆

发表于 2016-2-3 09:04:06 | 显示全部楼层
int32_t  adc;

adc=GetAdc();
adc<<=8;
adc>>=8;
看看有符号的整型移位的规则。

出0入4汤圆

发表于 2016-2-3 09:05:27 | 显示全部楼层
gamalot 发表于 2016-1-31 23:23
或者先向左移8位,再除以256

这样不需要判断,看上去很有技巧的样子,逼格略高

呵呵 俺就是这样做的

出0入0汤圆

发表于 2016-2-3 10:38:13 | 显示全部楼层
ilikemcu 发表于 2016-2-2 22:47
老实讲,CS5513的性价比实在不行了,还不如用ADS1232,把内部PGA置1倍,比5513那个不是好了一点点啊,价格 ...

可否像  gamalot兄样给个代码参考啊,谢谢

出0入0汤圆

发表于 2016-2-3 10:56:49 | 显示全部楼层
ilan2003 发表于 2016-2-3 09:04
int32_t  adc;

adc=GetAdc();

右移八位和除256的效果是一样的吧?但是前者貌似计算速度更快唉~

出0入0汤圆

发表于 2016-2-3 11:00:01 | 显示全部楼层
本帖最后由 PCBBOY1991 于 2016-2-3 11:01 编辑
zouzhichao 发表于 2016-2-1 00:07
我就是把你的那个if化简了而已
(code & 0x800000)无非两个结果,0或者0x800000
0: code -= 0


0x1000000 = 0x800000 << 1;
这个是怎么得来的?

擦,看错了,你是对的。

出0入10汤圆

 楼主| 发表于 2016-2-3 11:08:23 | 显示全部楼层
24位补码转换成32位补码,应该是解决办法

出0入0汤圆

发表于 2016-2-3 11:39:17 | 显示全部楼层
47楼好办法,

出100入101汤圆

发表于 2016-2-3 11:50:17 | 显示全部楼层
43楼讲解不错!

出0入0汤圆

发表于 2016-2-3 12:41:09 | 显示全部楼层
ilan2003 发表于 2016-2-3 09:04
int32_t  adc;

adc=GetAdc();

看了一下,俺之前的代码就是这样写的,利用左移和右移的规则的不同

出0入4汤圆

发表于 2016-2-4 19:25:01 | 显示全部楼层
PCBBOY1991 发表于 2016-2-3 10:56
右移八位和除256的效果是一样的吧?但是前者貌似计算速度更快唉~

不一样的,有符号的右移,如果最高位是1,补1,如果最高位是0补零,和无符号不一样

出0入0汤圆

发表于 2016-2-4 19:38:10 来自手机 | 显示全部楼层
ilan2003 发表于 2016-2-4 19:25
不一样的,有符号的右移,如果最高位是1,补1,如果最高位是0补零,和无符号不一样 ...

恩。
我指的情况就是有符号数里的负数左移八位和除256是一样的吧?

出0入4汤圆

发表于 2016-2-4 21:02:24 | 显示全部楼层
PCBBOY1991 发表于 2016-2-4 19:38
恩。
我指的情况就是有符号数里的负数左移八位和除256是一样的吧?

右移和除以256一样的

出0入0汤圆

发表于 2016-3-13 10:18:47 | 显示全部楼层
本帖最后由 jackwang123 于 2016-3-13 10:32 编辑

补码转换成有符号数,然后再取平均值或者滑动滤波.

出0入0汤圆

发表于 2016-3-13 10:31:10 | 显示全部楼层
ilikemcu 发表于 2016-2-2 22:47
老实讲,CS5513的性价比实在不行了,还不如用ADS1232,把内部PGA置1倍,比5513那个不是好了一点点啊,价格 ...


ilikemcu 老兄说的对,对24位AD研究的比较透彻,不知道在不在上海,有个12路24位AD采集的板子想付费请你优化一下.

出0入50汤圆

发表于 2016-3-13 11:10:43 | 显示全部楼层
jackwang123 发表于 2016-3-13 10:31
ilikemcu 老兄说的对,对24位AD研究的比较透彻,不知道在不在上海,有个12路24位AD采集的板子想付费请你优 ...

我在上海,但是抱歉,对于这种擦屁股的事情,或者给已经做好了硬件,需要做软件的活儿,我坚决不碰,纯粹的吃力不讨好,还望见谅。

出0入0汤圆

发表于 2016-3-13 12:57:29 | 显示全部楼层
ilikemcu 发表于 2016-3-13 11:10
我在上海,但是抱歉,对于这种擦屁股的事情,或者给已经做好了硬件,需要做软件的活儿,我坚决不碰,纯粹 ...

不是擦屁股,付费指导一下也可以,帮忙看看电路和布线.
现在是AD的噪声没处理好,布线改来改去已经无能为力了,1个板子上3个CS5532的数据采集20位的精度没问题,但是一个板子上12个AD的数据采集实在是一筹莫展了.
以前是共用晶振,现在分别用晶振精度还是不够.独立使用参考源也不行.
我在金桥这边,如果方便的话,我到你那里请教一下也可以.

出0入10汤圆

发表于 2018-10-24 09:14:05 | 显示全部楼层
gamalot 发表于 2016-1-31 23:16
做个判断就行了,假设是24位ADC

你好,对于楼主的疑问“当负端电平高于正端时,ADC输出的最高位是1,此时如何计算多次测量结果的平均值?”,此时不是应该把得到的补码数据转换成原码再参与计算吗?
按照你给出的这个公式,假如code = Adc_read_data();读到的数据是code=0x812345,那么执行
if ((code & 0x800000) != 0)
{
  code -= 0x1000000;
}
后得出code=0xFF812345;不等于0x812345的原码啊?请问是我哪里理解的不对吗?还请指教!

出0入10汤圆

发表于 2018-10-24 11:50:08 | 显示全部楼层
ilikemcu 发表于 2016-2-2 22:47
老实讲,CS5513的性价比实在不行了,还不如用ADS1232,把内部PGA置1倍,比5513那个不是好了一点点啊,价格 ...

大神,你的这句话“双极性AD码,直接把负向满量程值作为0点,所有采集到的数据,统一加上23位的值,统一向上平移,转换到无符号整型变量操作,就方便多了”,加上这句Count=Count^0x800000就可以了吧?
负满量程          0mV      正满量程
0x000000     0x800000   0xffffff

出0入8汤圆

发表于 2018-10-24 18:02:38 | 显示全部楼层

出110入109汤圆

发表于 2018-10-24 20:41:32 | 显示全部楼层
是说ADC输入是差分接法,信号有+/-?都24bit了的,为何要做多次(滑动)平均?

出0入0汤圆

发表于 2018-10-25 13:01:22 | 显示全部楼层
zouzhichao 发表于 2016-1-31 23:31
借你的代码出个优化简洁版本:
int32_t code;
code = Adc_read_data();

谢谢你的代码,正好要用到
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-6-18 02:41

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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