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

2016年8月2日 星期二

Published 下午4:42 by with 1 comment

[基礎] OpenOCD 與 STM32


在單晶片開發時,大部分都採用燒錄後執行程式,
並且透過UART吐出數據的方式來debug。

不過隨著系統的複雜度上升,這樣的開發方式逐漸會遇到瓶頸。
也因此,比較大的晶片例如32位元的,通常會內建 JTAG 裝置,
也就是一種後門,方便進行debug。

在此我們採用一種 On-Chip debugger 軟硬體搭配,
簡單來說就是能夠單步執行並且隨時能知道記憶體內容。

各家晶片都有自己的 On-Chip Debugger 裝置,對應的 JTAG 硬體也各有不同。

原本打算採用的是 100 ASK 公司出品的 OpenJtag
但是該公司的這支硬體呢,在Win10下的驅動程式似乎怪怪的不能安裝,
搜尋了一些教學顯示有些麻煩的設定要做,所以暫時先算了。

既然打算要用STM32,那就用先前操作 STM8 的 STLink2 繼續用吧。

在 STM32 的開發板都會把 JTAG 介面開出來,根據接腳圖可以確認是這樣:
這是對應標準的 JTAG 的腳位,適用於像是 ULink  或是 JLink 之類的硬體套件。
如果是要用ST公司的 STLink2 去連接,則會有點不一樣。

根據 這邊的說明 ,只要利用四支街腳就足夠了。如圖所示:
也就是把 STLink2 的 SWDIO 接往 TMS、 SWCLK接往TCK。
然後 GND 和 3V 接好即可通訊使用。

我們接好之後如下圖:

單純的只用杜邦線做銜接。

接好之後,只要確定STLink2的驅動沒問題,那麼只要將中介層的軟體弄好即可。

我們這邊採用的中介層是 OpenOCD
使用了這個中介軟體,就可以將 GDB送來的指令轉換到真正的硬體上。

在OpenOCD的原始網站上,提供的基本是原始碼而已,因為要做跨平台,
所以開放給使用者自已下載回去,在自己的作業系統上編譯。

要在Windows下自己編議OpenOCD可以說是相當麻煩,必須先安裝Cygwin,
也就是模擬Linux環境才能做。詳見這個網站有說明。

由於自己編譯太麻煩,所以根據OpenOCD網站的建議,
這裡可以拿到編好的安裝檔。

下載來之後的安裝畫面就像這樣
基本上都按下一步就能安裝完成了。

但是這個OpenOCD是沒有GUI,完全使用文字介面下指令操作的。
安裝的位置如果在  C:\Program Files 底下的話,之後操作讀取script檔時就會因為那個空格而讀不到。所以我們將OpenOCD安裝在 C:\OpenOCD 以利使用,打字也比較不累。

把所有的東西連線做好,當然開發板的電源記得要接上。


啟動命令列視窗,然後下命令

 cd  C:\OpenOCD\bin

進入OpenOCD執行檔的所在位置,接著執行

openocd -f C:\OpenOCD\scripts\interface\stlink-v2.cfg -f C:\OpenOCD\scripts\target\stm32f1x_stlink.cfg

這行的意思就是啟動OpenOCD並且以 stlink2 銜接 STM32F103 的意思,
所以才要隨之讀取附屬 script 檔。

成功啟動後會出現這樣的訊息:
游標停在那邊不是當機,是OpenOCD已經開始執行。
這時要另外啟動一個文字介面的視窗,並且打字輸入

telnet localhost 4444

就會出現

這樣子代表已經可以進入指令模式操作這顆晶片了。
如果使用GDB的話,則不必啟動這個telnet。

如果你的系統沒有telnet,可以參考這邊的教學來啟動Telnet。






Read More
      edit

2016年8月1日 星期一

Published 下午2:23 by with 1 comment

[基礎] STM32與開源軟體

這一次的主角是STM32。

照片中是一塊STM32開發板,搭配的 debugger 是 ST-Link-V2 。
STM32具有優異的性能,各種強大的設計使得開發產品非常容易,而且這晶片也不貴。
這次作為實驗對象的是 STM32F103vet6。

在前面的章節不斷的使用 Code Blocks 這個優秀的 IDE ,而本次的實驗要用 Code Blocks 來進行整合開發環境的建置,並且能夠單步執行。

首先要取得 Compiler 。可以先閱讀 How to install the ARM toolchain 這網站的指示。


然後到 GNU ARM Embedded Toolchain 網站去下載安裝檔


或者去  CodeSourcery 網站取得也行。

接著是STM32要運作時的一些必備函式庫,可以去STM32原廠網站找
下載並且解壓縮後,會有一個名為 STM32F10x_StdPeriph_Lib_V3.5.0 的資料匣
我下載的是3.5版所以會有這樣尾數命名。


在 STM32F10x_StdPeriph_Driver 這個目錄底下有原廠先寫好的函式庫方便操作硬體,我們將複製使用。

在CMSIS目錄下,有著原廠寫好的核心啟動程式碼,也就是STM32這種32位元啟動複雜的行為不必我們自己寫。


以上就是行前預備。


安裝 Compiler 完成之後,啟動 Code Blocks 做點設定。

從 Settings -> Compiler...  進入


Selected compiler 選擇 GNU GCC compiler for ARM  的項目,根據你自己安裝compiler位置設定好。

然後進入 Settings -> Debugger 設定新的 debugger 專門針對STM32的

記得 Do "not" run the debugee 這個項目要打勾,避免在非預期狀態下被啟動。

 然後進入 Tools -> Configure tools ,設定一個快速工具好讓OpenOCD啟動。
這樣就可以在 GDB debugger 啟動前先快速的啟動 OpenOCD 以利連線。

開啟一個新專案來做STM32的範例。
一樣使用空白的專案,但下一步之後的Compiler選項記得點選 ARM 專用的。

專案開好後,專案設定的一些東西要變更。

首先是 Projetc -> Build options


由於選定的晶片,STM32屬於 Cortex-M3 系列的,所以要勾選 M3

然後在 Other options 底下要增加文字
-gdwarf-2 -mthumb -fomit-frame-pointer -Wall -Wstrict-prototypes -fverbose-asm -Os


在 #defines 下要增加
STM32F10X_HD
USE_STDPERIPH_DRIVER


在 Linker settings 的頁籤下,other linker options 要添加
-mthumb -nostartfiles -Tstm32f103vet6.ld -Wl,-Map=main.map,--cref,--no-warn-mismatch


最後是 Pre/post build steps 頁籤項目裡,Post-build steps 要增加
C:\Program Files (x86)\CodeBlocks\GunARM2016q2\bin\arm-none-eabi-objcopy -O ihex ${TARGET_OUTPUT_DIR}${PROJECT_NAME}.elf  ${TARGET_OUTPUT_DIR}${PROJECT_NAME}.hex
cmd /c "C:\Program Files (x86)\CodeBlocks\GunARM2016q2\bin\arm-none-eabi-objdump" -S ${TARGET_OUTPUT_DIR}${PROJECT_NAME}.elf > ${TARGET_OUTPUT_DIR}${PROJECT_NAME}.asm
C:\Program Files (x86)\CodeBlocks\GunARM2016q2\bin\arm-none-eabi-size ${TARGET_OUTPUT_DIR}${PROJECT_NAME}.elf


這段是為了在編譯完成後,自動產生可燒錄檔以及組合語言碼方便找錯誤。

接著進入  Project-> Properties ,在 Build targets 頁籤裡做這樣的設定

在 Debugger 頁籤加上遠端遙控的設定, IP位址是 localhost 而 port 是 3333。

同樣在 Debugger 頁籤下, Additional GDB commands 添加連線後的指令設定
monitor halt
load bin/STM32_Hello.elf
monitor sleep 1000
monitor reset
這段指令的意思就是,啟動後先停止,然後載入執行檔,睡眠 1000mS 再 Reset。

這些設定完成後,預備寫程式的檔案必須要有:
core_cm3.c
core_cm3.h
system_stm32f10x.c
system_stm32f10x.h
stm3210x.h
stm32x.conf.h
startup_stm32f10x_hd.s
stm32f103vet6.ld
以上構成基本的開機啟動功能。然後
main.c  放我們自己的程式
stm32f10x_gpio.c 和  stm32f10x_gpio.h 做 IO操作
stm32f10x_rcc.c 和  stm32f10x_rcc.h 做振盪器控制操作

但是在ARM compiler 裡,似乎有個辨識上的 bug  會使得 compiler 不會過關。
這兩個小小需要修改的點在 core_cm3.c 裡面。分別是
STREXB 和 STREXH

在程式內  "=r" (result)  會導致編譯失敗,因此改為  "=&r" (result)
添加一個 & 符號解決。
該問題的修改法參考來自此
http://www.cesareriva.com/fix-registers-may-not-be-the-same-error/

以上的程式建置進專案後,build 完成,就可以啟動單步除錯器了。

當然硬體要先接好


首先透過 Tools 啟動 OpenOCD
當出現這些訊息時,代表OpenOCD已經成功啟動。等待GDB去做連線。
這時只要按下
紅色的三角鍵,Code Blocks 就會啟動 GDB 去做連線。按下後log視窗會出現


可以看到 load 指令後載程式到晶片的資訊。
再按一次紅色的三角鍵就會開始運作,跑到斷點才會停止
然後可以看見點亮LED的情景

當斷點停止的時候,可以在OpenOCD的視窗內看到被停止的位址


以上範例程式碼放在 GitHub
Code Blocks 的操作設定參考自 http://www.hackvandedam.nl/blog/?p=707
OpenOCD 的操作可參考自 詹姆士酷斯拉





Read More
      edit