2016年9月9日 星期五

Published 晚上7:13 by with 0 comment

[實驗] STM8與低通濾波器

進行一些控制操作的時候,
需要透過ADC讀取一些外部sensor資訊。

有時候會遇到sensor狀態不穩,
也因此讀取的數值就會莫名飄動。

在這種時候會需要一些簡單的濾波器。
關於濾波器的技術,一些前輩都寫過了。

例如

以8051為基礎的軟體濾波器操作
http://chamberplus.blogspot.tw/2010/05/ad.html

濾波器的數學樣式
http://blog.xuite.net/juinghuei/twblog/99597535

而在這邊我們使用 STM8 重現軟體濾波器實驗。

首先準備個500K的可變電阻裝在麵包板上,
並且和STM8的AD腳連接起來。




為了避免一些電源的浮動什麼的影響,所以另外接了3.3v 的電源。
在我們選用的這塊核心板,AD 專用電源 VDDA 在 PB7 而 VSSA 在PB6。

基本的系統程式使用前面談過的time system,把要用的功能放入以利操作。

STM8 的ADC有個奇異的特點,就是從ADON 到真正要開始取樣資料的時候,
要再一次下達ADON的指令才會開始。

根據原廠文件 AN2658 內的時序圖
所以必須在初始化設定ADON 位元為1,然後到真正要啟動取樣的時候再設定一次。
我們這裡採用的是 Continuous conversion mode,這樣不用每次都重複設定兩次。

所以啟動ADC是寫成這樣:
void ADCInit(void) { ADC1->CR1=0x03; // fADC=fMaster/2, CONT=1, ADON=1 ADC1->CR2=0x08; // Right alignment ADC1->CR3=0x00; ADC1->CSR=0x00; ADC1->TDRL = 0x01; // PB0 Schmitt trigger disable GPIOB->DDR &= 0xFE; // PB0 output mode GPIOB->CR1 &= 0xFE; // PB0 floating input GPIOB->CR2 &= 0xFE; // External interrupt disable }
對應的AD讀取腳為PB0,必須將 Schmitt trigger 關掉,並且定為 floating input 。

添加一個接收 UART 送來的資訊並且判斷動作的程式,方便我們下指令啟動ADC開跑。
void RXProcess(unsigned char *p) { if((*p)=='$') { SysFlag.RXDone=1; if((*(p+1)=='A')&&(*(p+2)=='D')&&(*(p+3)=='C')) { SysFlag.ADCGo=1; } } else { SysFlag.RXDone=0; SysFlag.ADCGo=0; } }
這是將收資料矩陣的指標位置傳入來做處理。當確定指令有效時更動對應旗標。
在這裡的啟動指令是 $ADC 。

在 main.c 內的主迴圈中,放入收指令判讀確定後的運作程式。
if((SysFlag.RXDone)&&(SysFlag.ADCGo)) { unsigned int ADCvalue,FiltedADCvalue; if(ADCTimer>5) { ADC1->CR1 |=0x01; while(((ADC1->CSR)&0x80)!=0x80); ADC1->CSR &= 0x7F; ADCvalue=(((unsigned int)ADC1->DRH)<<8) | ((unsigned int)ADC1->DRL); FiltedADCvalue = LowPassFilter(ADCvalue); OldFilterValue=FiltedADCvalue; printf("%d,%d,\n",ADCvalue,FiltedADCvalue); ADCCounter++; ADCTimer=ADCTimer-5; } if(ADCCounter==100) { //__asm__("bres _SysFlag,#1\n"); ADC1->DRH=0;ADC1->DRL=0;OldFilterValue=0; SysFlag.RXDone=0; SysFlag.ADCGo=0; RXIndex=0; ADCCounter=0; } }
其中 ADCTimer 被添加到時間中斷中計時,這樣可以做成每50mS取樣一次。
而 ADCCounter 當作取樣總數,在這裡我們總共取100筆。

原始的一階濾波方程式的數學是:



其中K是濾波截止頻率的參數,fs 是取樣頻率的一半、而 fBW  則是頻寬。
由於這是低通濾波器,因此把頻寬當作截止頻率即可。將K事先計算好直接利用。

把計算的數學式減少乘法的運算次數,稍微精簡一下。
根據式子,計算時就是把新取得的ADC值 ADC new ,減去上一次濾波計算的值 f(n-1),乘以K倍之後再加一次上次濾波的值。

但是這個K的範圍是 0 ~ 1 之間,也就是必須動用浮點數了。為了要能夠給沒有乘除法的單晶片利用,我們改為固定範圍的值,也就是進行移位乘除法。

以本次範例ADC來說,我們的取樣頻率是20,fs 就是10。截止頻率目標訂在5Hz的話,經過計算的 K 值為 0.2078。如果用10bit當分母為底,那大約就是 212/1024。

若要採用移位乘除,我們取個接近的數值也就是 256/1024 當作 K 值好了。
再反推算回去,截止頻率座落在 4.41 Hz,也能概略符合需求。

所以再把式子精簡一下:
最後的結果,就是只要右移運算兩次即可。
但因為我們的操作是無號數,所以必須先判讀ADC取值或是上次濾波值誰比較大,大的值減小的值這樣才不會造成號數問題。

副程式 LowPassFilter   則是疊代計算的濾波程式。
unsigned int LowPassFilter(unsigned int NewADCValue) { unsigned int FiltedValue; /* half Sampling rate=10, BW = 4.41Hz Filter factor = exp(-pi*BW / Fs) = exp (-3.14*4.4/10) = 0.251 = 256/1024 = (2^8) / (2^10) right shift two times */ if(NewADCValue < OldFilterValue) { FiltedValue=OldFilterValue-NewADCValue; FiltedValue=FiltedValue>>2; FiltedValue=OldFilterValue-FiltedValue; } else if(NewADCValue > OldFilterValue) { FiltedValue=NewADCValue-OldFilterValue; FiltedValue=FiltedValue>>2; FiltedValue=OldFilterValue+FiltedValue; } return FiltedValue; }

這是經過精簡計算後的程式。

濾波程式只需要靠移位和加減法即可完成。

在下指令開啟ADC讀取,我們旋轉可變電阻記錄下五秒的資料。
可以看到原始資料與濾波後的結果。

從結果可以看到,濾波後的(紅線)對於短的雜訊可以有抵抗效果。
完整實驗程式已放在 GitHub



Read More
      edit

2016年9月8日 星期四

Published 下午4:59 by with 3 comments

[實驗] Raspberry Pi 3 之 LED 閃亮

為了要能夠讓樹莓派做點簡單的小事,
我們先嘗試驅動GPIO來點亮LED。

根據樹莓派驅動GPIO的網頁說明,有幾種模式:

1. C語言
2. C#語言
3. Ruby
4. Perl
5. Python
6. Scratch
7. Java
8. Shell

我們採用最接近單晶片形式的C語言來開發。


根據一份GPIO速度實驗的報告顯示


直接使用 C Native library 的速度最快,BCM 2835 則次之。
而乍看之下 BCM 2835 library 的複雜度也不算太高,跟 C Native library 相比是簡單多了。

於是根據 BCM 2835 library 網站的操作流程,下載函式庫並且解壓縮安裝:

tar zxvf bcm2835-1.35.tar.gz
cd bcm2835-1.35
./configure
make
sudo make check
sudo make install


並且使用了這段程式碼

經過編譯並且執行的結果卻是完全沒反應

於是用盡了各種檢查手段,並且使用示波器確認信號,
示波器信號顯示完全是零,沒有反應就是沒有反應。

但是使用了這邊寫的以sysfs來操作GPIO卻是可以點亮。
這顯示硬體沒有壞,其實一切是正常的,那代表函式庫可能有問題了。

經過一番資料搜查,發現在一開始的頁面裡有段字句:

Note: For Raspberry Pi 2, change BCM2708_PERI_BASE to 0x3F000000 for the code to work.
並且底下附的程式碼是:
硬體的位址在 0x20000000 這似乎是針對 Pi 1 使用的。

於是檢查BCM2835的原始碼,也就是 bcm2835.h,裡面關於硬體位址則是
所以說,硬體位址不一樣,導致存取成功但是並沒有GPIO輸出嗎?

經過搜索,在樹莓派的專屬論壇有人提出類似問題,找到了這段對話
也就是說變更位址後應該就可以使用了吧。

於是將  bcm2835.h 內的位址資料加以變更
把圓框標記的位址變更為 Pi 2 的位址,也就是 0x3F000000。
並且把整個函式庫重新compiler一次,按照前面所講的安裝程序弄一次。

寫好的程式也要跟新的函式庫鏈結重新compiler一次。
而這次執行編譯完的檔案,LED就會閃亮了。




Read More
      edit

2016年9月7日 星期三

Published 下午4:25 by with 0 comment

[實驗] RS-485 初步探討

本次實驗的主角是RS-485。

這是個MAX485晶片所組成的RS-485模組,方便我們使用。

RS485是一種優秀的通訊機制,可以進行長距離傳送、穩定可靠,
據說波音飛機上就使用這種機制當作控制用的通訊。

根據 MAX485 的 datasheet,可以看到基本的點對點通訊是這樣連接的。
其中的控制接腳 RE 與 DE 分別代表 接收 與 發送 的信號方向切換。
我們在實際操作的時候,會將 RE 與 DE 相連在一起由一個 IO 信號去切換通訊。
也就是像這樣連接:
旁邊那顆 limit switch 是用來手動切換電位的。


如果多機通訊,可以有這樣的連接方式

如圖所示可以做成多機通訊模式。
為了簡化系統,我們設定某一台為主機,其他為從機。
主機的  RE 與 DE 預設電位為 High ,發完訊號後切為 Low 等待從機回應。
從機的  RE 與 DE 預設電位為 Low
收到信號後與機碼核對是不是給自己的訊息,確認後切為 High 後發送。

根據這樣的方式,將模組和線路布置在麵包板上

然後每一個 MAX485 的模組都接上 CP2102 來讀取訊號或發送。

通訊實驗的結果如下:
主機發送了 "Host" 這個字串,三隻從機都有收到。

把主機按鈕壓下,使得RE和DE電位成為Low,成為接收模式。
同時從機第一號按鈕也壓著,使得RE和DE電位成為High,成為發送模式。
從機一號發送字串 " Slave1" 使得所有通訊端都能收到。



同理,控制主機和從機的發送與接受切換,就可以像這樣自由的互相通訊。
之後正式的多機通訊,則必須使用單晶片做通訊切換。


Read More
      edit