|
濾波功能的子程式 分享一小段原程式碼分析
====================================
不知該放 哪一版 DIY / 軟體 版大若是覺得不妥 移版或刪文
因為自動控制版 有討論到8051 我就放到這裡 文筆差請各位原諒
====================================
程式來源是PHILIPS 充電器示範用程式碼, 中擷取出一小段, 濾波功能的子程式
程式結構非常簡單, 背後卻是大有深意, 了解之後對DIY同好有相當助益
原配合電路以RC積分加上電壓OPA比較器, 用來與待測ANALOG 電壓 作比較 ,
並以16bit timer 0xffff 導數計時 .
計算 RC積分電壓 從清除開始要昇到等同待測電壓 , 所必須花費的時間 .
此時timer 上的數值 , 即為待測電壓的數位量化值 .
~~就是最簡單的ADC~~
輸入值若是遇到雜訊干擾 , ADC 產生異常數值
以下就是將數位量化值 經過濾波演算的子程式
事實上是新數值連同7組舊數值以連續平均值運算
將偶爾異常數值(雜訊) 均分掉 進而提昇系統容錯能力
整個功能像不像電容濾波濾雜訊?
若是用在DIY 的LCR 頻率計 DC_SEVO相位檢測修正……等等
不方便直接使用電容濾雜訊或是電容不能放太大場合 可以拿來參考修改
以下藍色部分就是將數位量化值 經過濾波演算的子程式
中文注解是 我加上的
1.宣告 全域變數 用來蓄存
word this_period 最新量化值
word last1~ word last7 前次7組資料
2. 程式演算
將最新數值 與前次7組資料作平均值運算後
推移更新前次7組資料 並且將運算值回存到最新數值
/* 宣告 全域變數 使用RAM */
/* global variables */
word this_period; /* 最新量化值 */
word last1; /* 前1次量化值 */
word last2; /* 前2次量化值 */
word last3; /* 前3次量化值 */
word last4; /* 前4次量化值 */
word last5; /* 前5次量化值 */
word last6; /* 前6次量化值 */
word last7; /* 前7次量化值 */
word
filter (
word last0
) { /* 區域變數 word last0 = 2 byte */
word temp1, temp2, temp3, temp4; /* 區域變數 4 word= 8 byte */
word result1, result2; /* 區域變數 2 word= 4 byte */
temp1 = ((last0 / 2) + (last1 / 2));
temp2 = ((last2 / 2) + (last3 / 2));
temp3 = ((last4 / 2) + (last5 / 2));
temp4 = ((last6 / 2) + (last7 / 2));
result1 = ((temp1 / 2) + (temp2 / 2));
result2 = ((temp3 / 2) + (temp4 / 2));
last7 = last6;
last6 = last5;
last5 = last4;
last4 = last3;
last3 = last2;
last2 = last1;
last1 = last0;
return((result1 / 2) + (result2 / 2));
}
======================================
以下是我個人想法分析
======================================
temp1 = ((last0 / 2) + (last1 / 2)) ;
表面上temp1 是 last0 和 last1 平均數
那為何不直接寫成 temp1 = ((last0 + last1) / 2) ;
這是因為 last0 + last1 直接相加 可能造成溢位錯誤
(好一點的compiler會發出溢位警告 若是組和語言會不警告你)
假設 last0 = 1??? ???? ???? ????
last1 = 1??? ???? ???? ????
那麼該數值相加後最高位元 將產生進位而超出最高位元 產生溢位錯誤
一般在PC環境執行的軟體手法上 會直接宣告 以更長更大double word 雙字元組
來承載 兩個字元組 之和
Double word temp1;
temp1 = ((last0 + last1) / 2) ;
奈何這裡使用的是8位元單晶片 可供蓄存變數的 RAM 記憶體 少的可憐
不仿想像成 at89c2051 之類 [宣告double word ] 無疑是個敗家子行為
將某一數值作 (數值/2) 這樣的運算在表面上是除以2
實際上是右移1位 因此可以寫成
temp1 = ((last0 >> 1 ) + (last1 >> 1 ));
只是說 程式用意是求平均數 使用數學運算式表達 更易於清楚辨識程式目的
將來修改維護時 不會一頭霧水 引起誤解
說到誤解 我曾經改寫如下
/* 新平均數 = ( 舊7組數值 + 新1組數值 ) /8
= ( 舊7組數值 / 8 ) + ( 新1組數值 /8 )
舊7組數值 改用1組舊平均數值last_n代表 (7/8) = ( 1-1/8)
新1組數值佔平均數比例 1/8 新數值
*/
last0 = (((last_n - (last_n / 8)) + last0 / 8 ) ;
結果濾波器程式 濾很大一些細微變化
last0最後面3個位元 全被(數值/8)這個動作砍了
last0 新數值 若是只有最後面3個位元有變化時 全被(數值/8)砍了
因此連續幾次輸入新數值 整組平均數根本沒變化 表面上很穩定
系統輸入的靈敏度解析度 變得非常遲鈍 動態表現很差
所以原式全段皆使用(數值/2) 只砍最後面1個位元 就是希望最大限度保留精細度
|
評分
-
4
查看全部評分
-
|