Profile - TW003


TW003

具有三網合一之無線感測網路監控平台設計



Semi-Finals


[print]


Project Paper - view as Preliminary(2010/07/21), Final(2010/11/01), Draft, Latest

1. 設計介紹 (Preliminary Paper)

1設計介紹

1.1設計目的

本作品所實現之無線感測網路監控平台主要的特點,在於監督者可透過跨網監控的方式來監控無線感測網路,例如透過有線網際網路的Web BrowserInternet的方式或透過自己的手機走電信網路的方式,而不用監督者一直待在監控平台旁邊。

應用領域無線感測網路、家庭自動化控制與監控、工廠監控與農漁業監控。

1.2作品介紹

本作品為具有三網合一的無線感測網路監控平台,這3大網路分別為

Ø          無線感測網路(WSN)

Ø          有線網際網路(Internet)

Ø          電信網路(Telecom)

在無線感測網路方面,可從遠端接收感測器所偵測到的溫度,顯示在無線監控平台上的7-SEG LEDLCD TEXT與語音緊急播報等近端監控。在有線網際網路方面,則可在有Internet的地方,可利用網際網路連接到無線監控平台上的Web Server上,獲得感測器之最新的資訊達到遠端,以網際網路進行即時監控。若有突發緊急事件時,則可透過電信網路發送短簡訊,使得身在戶外的監督者,亦可即時收到緊急訊息,以利用電信網路實現遠端即時監控。

在無線監督平台設計上,使用ALTERA FPGANios II CPU為基礎開發硬體和軟體。在硬體方面加入了Nios II CPU3大網路IPFPGA中,而實現這3大網路之技術,分別說明如下 :

Ø          1個網路IP TI CC1100無線模組:採用433MHz為無線傳輸之頻帶,CC1100無線模組和FPGA的溝通橋樑為SPI界面,因此在FPGA中,加入控制CC1100SPI電路。

Ø          2個網路IPGSM Modem 的控制IP:利用 FPGA透過UART介面跟GSM Modem溝通,因此在FPGA內加入UART的控制電路。

Ø          3個網路IPDM9000A乙太網路晶片:驅動乙太網路晶片,使監控平台能透過乙太網路連結到外部的Internet,並在FPGA內加入了DM9000A的乙太網路控制電路。

本作品在軟體方面實現以下4個部份:

Ø      植入uClinux作業系統,版本為Kernel 2.6.11,以此作業系統為核心,在上頭啟動Web Server,並透過uClinux TCP/IP StackDM9000A的驅動程式,使用Ethernet介面連上Internet。由於執行CGI程式與監控WSN+簡訊發報的應用程式,這麼複雜的系統,需要有一個作業系統來管理。

Ø      開發應用程式結合無線感測網路,可不斷的接收來自無線感測節點所回傳的感測值,將感測值記錄下來,顯示在硬體平台上或顯示在網頁上,並結合緊急情況的手機短簡訊發報之功能,例如偵測到溫度過高就發出簡訊通知監督者。

Ø      架設Web Server,以供監督者使用Web Browser,並透過Internet連到監控平台,因此必須輸入密碼才可登入。在登入監控網站後,監督者可設定自己的手機號碼,以供監控平台偵測到緊急狀況時,立即發送簡訊到監督者的手機。

Ø      開發CGI程式,將無線感測感測平台所接收到的溫度、瓦斯與震度之結果,顯示在使用者的網頁,並將使用者網頁上所設定監督者的手機號碼,傳送給應用程式,以供發送簡訊的手機號碼。

本作品在FPGA中植入了uClinux作業系統版本為Kernel 2.6.11,並在uClinux中開發無線感測網路應用程式,使無線監控平台可接收感測器所傳遞過來的訊息。在無線監控平台上,架設Web Server,並透過DM9000A乙太網路,連接到外部的Internet,使得身在遠方的監督者,可透過Internet連接到監控平台上的Web Server上,以得知無線感測平台之即時訊息。為讓應用程式和Web Server顯示即時訊息,必須在兩者之間寫CGI程式,使應用程式與Web Server 可相互溝通,以顯示最新數值,若遇到緊急事件,則可透過電信網路發送SMS短簡訊,告知監督者緊急訊息。

接著介紹本作品之使用情境如下:

Ø          使用情境1

        監督者人在無線感測監控平台旁邊,隨時監看監控平台的LCD Text、七段顯示器或透過DE2平台的Audio Out來收聽語音播報,以了解感測器之實況,此為監督者在現場掌控無線感測網路的感測情形。

Ø          使用情境2

若監控平台放在家中,以監控家中的溫度、瓦斯、人體紅外線、光度與震度等訊息,當監督者人不在家中,而在公司辦公、學校或至國、內外出差,只要在有Internet服務到的地方,監督者即可透過電腦連上Internet,在開啟瀏覽器後,即可連接到家中監控平台的Web Server,以得知無線感測網路的感測實況,包括家中溫度、瓦斯、光度與人體紅外線等,各種感測數據,皆可顯示在網頁上,且在網頁上每5秒做一次動態更新。

Ø          使用情境3

監督者人不在無線感測監控平台旁邊,但有攜帶手機,只要在戶外有手機基地台服務的地方,只要無限感測平台感測到異常狀況時,亦可收到當WSN監控平台所發出的短簡訊,以供緊急通報監督者用。

若監督者欲透過手機了解目前監控平台的監控狀態,亦可打電話到監控平台的GSM Modem,即可掛斷電話(不需接通),此時家中監控平台便會比對來電顯示號碼,若手機號碼與監督者相同,監控平台便會發送目前感測到的數據與狀態,透過簡訊回報給監督者。由於手機為目前相當普遍之行動通訊裝置,且可隨使用者移動到任何地方(具移動性),因此透過手機來監控WSN為本專題的一大創意。

 

(Revision: 9 / 2010-09-15 14:50:29)

2. 功能描述 (Final Project Paper)

2、功能描述

2.1 功能介紹

Ø          快速UART介面接收器,用來與GSM Modem溝通,因為GSM Modem是以Packet 方式連續傳送出資料。一般而言,UART內部僅有一個Register,因此僅能存放一個Byte之資料,而無法接收連續2Bytes以上的資料,因此自行設計具有Dual-Port RAM的快速UART介面接收器,並掛載在Avalon Bus下,以做為來電顯示解出電話號碼與讀出簡訊內容(本專題採用Verilog來設計)

Ø          近端監控1:可監控無線感測遠端的溫度、濕度、照度、瓦斯與人體紅外線。

Ø          近端監控2在無線感測網路方面,可從遠端接收感測器所偵測到的溫度,顯示在無線監控平台上的7-SEG LEDLCD Text與語音緊急播報等近端監控。

Ø          遠端監控1:可透過電信網路來監控DE2平台之無線感測網路監控端,若有突發緊急事件時,則可透過電信網路發送短簡訊,使身在戶外的監督者可即時收到緊急訊息,亦可達到遠端即時監控,或由手機發簡訊控制監控平台。

Ø          遠端監控2:可透過有網頁瀏覽器,經有線網際網路來監控DE2平台為基礎之無線感測網路監控端,可監控溫度、濕度、照度、瓦斯與人體紅外線與控制監控平台。

Ø          遠端監控3因手機為目前相當普及之通訊裝置,且可隨使用者移動到任何地方(具移動性),因此透過手機來監控WSN為本作品的一大創意。

2.2 實現方法

A、硬體加速週邊部分:

Ø          Nios II CPU 頻率可操到100MHz

Ø          無線電信網路:使用除頻電路將100MHz輸入頻率除868降為115,200bps所需的時脈(115,200Hz),給予FAST_UART_RX,以解讀GSM Modem發出的UART數據。

Ø          有線網際網路:加入Avalon Slave DM9000A IP (Verilog 撰寫) ,以控制DM9000A 乙太網路晶片收發封包。

Ø          無線感測網路:以PIO 為基礎,建立出SPI 訊號群,並採用MOSIMISOSCKCSN來與CC1100無線模組溝通。

B、軟體實現演算法部分:

Ø          加入uClinux Kernel 2.6作業系統於DE2平台上,以整合三網合一之無線感測網路功能。

Ø          成功驅動DM9000A 乙太網路驅動程式,於DE2平台+uClinux Kernel 2.6作業系統。

Ø          自行在uClinux Kernel 2.6作業系統上,開發無線感測網路偵測的應用程式,以用來接收無線感測節點所回傳的感測數值。

Ø          自行開發驅動SPI 電路的Nios II Firmware,讓感測應用程式能夠控制CC1100晶片來接收遠端傳回來的感測值。

Ø          自行開發驅動GSM ModemNios II Firmware,讓DE2平台能夠發出AT Command控制GSM Modem 來發送短簡訊。

Ø          無線頻段採用433MHz,每個感測節點距離3MHz,由DE2監控平台自行掃頻,以接收要偵測的感測訊號。

Ø          自行開發CGI 程式,以搭起Web Server與感測應用程式(Nios II Firmware)的溝通橋樑。

Ø          架設了BOA Web Server,供監督者使用Web Browser,透過Internet連到監控平台,因此必須輸入密碼才可登入網頁,在登入網頁後,監督者可設定電話號碼來傳送簡訊至所設定的手機上。

 

(Revision: 4 / 2010-09-15 14:51:34)

3. 效能參數 (Final Project Paper)

本作品可實現之效能如下:

ØNios2 CPU 可操作於100MHz之頻率,並執行著uClinux 2.6作業系統與播放音樂。

Ø當緊急狀況發生,可立即發送短簡訊通知系統管理員之功能。

Ø可利用手機發送簡訊來監控 WSN之功能。

Ø可利用手機call監控平台,以取得 WSN監控狀況之功能。

Ø完成BOA Web Server提供網際網路監控,並可透過Web Browser監控 WSN之功能。

Ø完成以下WSN各式無線感測節點:

n溫度

n濕度

n瓦斯煙霧

n地震位移

n太陽能

n人體紅外線

n風扇控制on/off

Ø完成WSN各式無線感測節點,採用不同的頻道來實現。

Ø具緊急狀況發生,透過DE2平台的Audio CODEC 播放警示音樂之功能。

ØFPGA (EP2C35F672C6)內部設計的Avalon Bus,可掛載33IPs

ØFPGA內部Logic Element 使用9,311/33,216(28%)

ØFPGA內部Memory Bits 使用347,520/483,840(72%)

ØFPGA內部PLLs 使用2/4 (50%)

ØFPGA內部Embedded Multiplier 9-bit Elements使用8/70 (11%)

ØFPGA外部Total Pins 使用274/475(58%)

Ø溫度感應可達-10度至+125度。

Ø無線傳輸距離可達戶外70公尺

Ø可程式輸出功率最高達+10dBm(對所有支援的頻率)

ØWSN無線傳輸的Data Rate可達128kbps

ØWSN無線傳輸的中心頻率433MHz

ØWSN無線傳輸的調變方式2-FSK

 

(Revision: 9 / 2010-09-15 20:50:46)

4. 設計架構 (Preliminary Paper)

4. 設計架構

        本作品的設計架構,主要分成系統設計電路圖硬體設計模擬方塊圖軟體流程圖等3大架構,最後詳述本作品目前之實作內容如下。

章節目錄

4.1系統設計電路圖... 4

4.1.1 三網合一無線感測網路系統架構... 4

4.1.2 無線感測網路通訊架構與協定... 5

4.1.3 有線網際網路通訊架構與協定... 7

4.1.4 電信網路通訊架構與協定... 9

4.2硬體設計模擬方塊圖... 11

4.2.1 SPI IP設計與介面訊號模擬... 11

4.2.2 GSM UART_RX IP 設計與介面訊號模擬... 12

4.2.3 無線感測模組轉接電路板Schematic設計... 13

4.2.4 無線感測模組轉接電路板PCB設計... 14

4.2.5 無線感測模組轉接電路板成品... 15

4.2.6 微感測器簡介... 16

(1)、溫度感測器... 16

(2)、濕度感測器... 16

(3)、人體紅外線感測器... 17

(4)、瓦斯煙霧感測器... 18

(5)、照度感測器... 19

(6)、地震移位感測器... 20

4.3軟體流程圖... 21

4.3.1 嵌入式作業系統跨平台編譯器製作流程圖... 22

4.3.2 嵌入式作業系統移植至Nios II平台之DE2 Board. 24

4.3.3 感測器接收之應用程式開發流程圖... 27

4.3.4 Web Server HTML網頁架構... 27

4.3.5 CGI 程式流程圖... 31

4.4實作結果... 33

4.5結論... 35


圖目錄

4.1 Nios II &三網合一設計架構圖... 4

4.2 PIO Input 1bit模組介面... 6

4.3 PIO Output 1bit模組介面... 6

4.4無線感測網路通訊架構與協定... 7

4.5有線乙太網路硬體架構... 8

4.6有線網路驅動程式與軟體層架構... 9

4.7無線電信網路架構... 10

4.8 CC1100應用電路圖... 11

4.9 CC1100晶片操作之SPI時序圖... 12

4.10 SPI模擬波形... 12

4.11 GSM_UART_RX接收到電話鈴聲之SignalTap模擬波形... 13

4.12 利用ORCAD V9.0設計無線感測模組轉接電路板之電路圖... 13

4.13 使用Power PCB V5.0設計之無感測模組轉接電路板Layout 14

4.14 無線感測模組轉接電路板成品... 15

4.15 無線溫度感測模組... 16

4.16濕度感測器... 17

4.17人體紅外線感測器... 17

4.18人體紅外線感測成品... 18

4.19無線瓦斯煙霧感測器(無偵測到瓦斯時) 18

4.20無線瓦斯煙霧感測器(偵測到瓦斯時) 19

4.21無線瓦斯煙霧感測器成品... 19

4.22 照度感測器... 20

4.23無線地震位移感測器(未感測到震動時) 20

4.24無線地震位移感測器(感測到震動時) 21

4.25 WSN系統軟、硬體整合架構... 21

4.26 設置跨平台編譯器的路徑使用者jy的環境變中... 22

4.27 Nios II uClinux Kernel軟體開發架構圖... 23

4.28 執行Nios II Command Shell 24

4.29下載zImageDE2板子... 25

4.30 zImage下載完成... 25

4.31 啟動Nios II-Terminal 25

4.32 DM9000A乙太網路成功在uClinux驅動起來... 26

4.33 DE2_Multi_Sensor程式實現3網連接... 27

4.34 WSN網頁設架構圖... 28

4.35 首頁輸入監督者密碼... 28

4.36 網頁的多功能選單... 29

4.37 溫度顯示網頁... 29

4.38 發送緊急短簡訊的手機號碼設定... 29

4.39 緊急突發狀況之網頁... 30

4.40 留言板網頁... 30

4.41 CGI 程式之輸出入介面... 31

4.42 CGI的片段程式(aa.c) 32

4.43 CGI Makefile內容... 32

4.44 DE2平台與三網連接之架構圖... 33

4.45 溫度發射端與溫度接收端(Nios II + CC1100... 34

4.46 網頁自動顯示出溫度過高的圖示... 34

4.47 當溫度過高時傳送簡訊至監督者手機... 35


4.1系統設計電路圖

4.1.1 三網合一無線感測網路系統架構

本作品之雙Nios II核心 &三網合一無線感測網路系統架構圖,如圖4.1所示。觀察圖4.1可知,在Avalon Bus左側Nios II CPU_0 SDRAM Flash Controller的組合,這個組合為執行嵌入式作業系統與WSN的監控程式,而Avalon Bus右側為無線感測網路、有線網際網路與無線電信網路等3種網路的結合架構。為控制無線感測網路,使用CC1100無線模組的控制器,可透過SOPC_Builder提供的PIO,以達成SPI Bus CC1100晶片溝通,並掛在Avalon Bus上;而UART介面可用來與GSM Modem溝通,以實現電信網路的連接;最後採用DM9000A控制IP,即利用DM9000A乙太網路晶片,以實現溝通有線網際網路之用途。

Nios II CPU_1 搭配自己的onchip RAM執行自己播放音樂的程式,而Nios II CPU_0 是透過Audio_Out PIO輸出4位元的值到Nios II CPU_0所管轄的Audio_In PIO來通知Nios II CPU_0要播放第幾首歌曲。

 

4.1 Nios II &三網合一設計架構圖

為做到產品的獨立性運作(Stand Alone),FPGA的內部硬體架構規劃,包含Nios II CPU的架構,透過「*.pof」檔燒錄到連接Cyclone FPGAEPCS EEPROM中;而軟體部份,Nios II CPU需執行的uClinux Image程式之zImage檔,並結合Boot Loader燒錄到DE2板子上的外部Flash Memory,因此只要打開電FPGA內部的Nios II CPU,即可Flash Memory啟動uClinux作業系統。當uClinux作業系統開機完成後,即可序列的啟動Web ServerCGI程式與DE2_Multi_Sensor程式,將整個WSN監控系統Run起來。

4.1.2 無線感測網路通訊架構與協定

本作品主要在ATERA Cyclone FPGA內打造出SOPC System , 並加入GPIO (General Purpose I/O),以實現出SPI Bus來與無線模組溝通(CC1100晶片)溝通,而這5SPI訊號分別為:

1.          cc1100_sck

2.          cc1100_csn

3.          cc1100_mosi

4.          cc1100_miso

5.          cc1100_GDO0

由於cc1100_sckcc1100_csncc1100_mosi皆屬於Output Pin 之屬性,而cc1100_misocc1100_GDO0屬於Input Pin 之屬性,接著詳細說明這些Output PinInput Pin之功能如下:

(1)     PIO Input 1bit (cc1100_GDO0, cc1100_miso)Other中選取PIO(Parallel I/O)加入Nios II Avalon Bus下,設定cc100_misocc1100_GDO02個的內部參數,並選用1bits Direction 選取Input Ports only,如圖4. 2所示。

 

4.2 PIO Input 1bit模組介面

(2)     PIO Output 1bit (cc1100_csn, cc1100_mosi, cc1100_sck)Other中選取PIO(Parallel I/O)加入CPU內部架構,再設定cc1100_csncc1100_mosicc1100_sck3個的內部參數,選用 1 bits Direction 選取Output Ports only,如圖4. 3所示。

 

4.3 PIO Output 1bit模組介面

由於CC1100晶片之每個暫存器皆佔8位元,而Avalon Bus下的位址皆獨立分配,而對應的每個PIO Pin腳的位址如下:

Ø          cc1100_csn       0x01400185

Ø          cc1100_mosi   0x01400186

Ø          cc1100_sck       0x01400187

Ø          cc1100_miso   0x01400188

Ø          cc1100_GDO0 0x01400189

本作品採用 ALTERA Nios II CPU 來控制無線收發模組 TI CC1100晶片,因此可得無線感測網路的工作狀態圖,4.4所示,其中A.1部份為監控平台初始化CC1100無線模組的暫存器,包含設定無線調變方式、無線工作頻率、資料傳輸速率與是否啟動硬體CRC檢查封包的基本設定,而A.2部份為監控平台操作中的狀態,這部分是將無線CC1100晶片的工作狀態Strobe成為RX 模式,如果有偵測到封包接收進來,則再使用Burst Read的方式將封包從CC1100RX FIFO中取出,接著再進行解讀封包內容的動作,例如解出溫度值。

 

4.4無線感測網路通訊架構與協定

當感測節點在固定的Channel中不斷的發出封包時,監控平台便可不斷的接收封包。若使用監控溫度、濕度、照度、震度與瓦斯等多個感測節點,由於這些感測節點需有各自的無線模組,且使用不同的Channel,方可讓這些感測節點不會因為在同一個頻率下操作而相互影響,因此這些感測節點與監控平台端的關係以星狀方式排列。本作品所設計之感測節點,所使用的Channel間隔為3MHz,因此只要改變監控平台的Channel Register,即可監控不同的感測器。  

4.1.3 有線網際網路通訊架構與協定

在本作品之硬體部份,加入由Verilog 所撰寫的DM9000A IP,並放在Avalon Bus,由於Avalon BusAvalon Slave IP,因此可利用CPU Based方式來操作,讓CPU透過Verilog 所寫的DM9000A IP,以讀寫DM9000A的暫存器或收發封包,因此該IP類似一個Bridge,以負責Avalon Bus介面與DM9000A 的介面,如圖4. 5所示。當接收到封包時,Nios II CPU 自行將封包從DM9000A RX FIFO取出,並放到SDRAM去。當有封包要發送時,Nios II CPU可由SDRAM取出封包,送到DM9000A TX FIFO去,並將封包發送出去。一般而言,控制封包收送之讀寫暫存器與上層的IP/TCP層溝通,需要透過驅動程式來完成。

 

4.5有線乙太網路硬體架構

一般而言,在uClinux作業系統中,必須將DM9000A的驅動程式,加入uClinux Kernel 2.6的核心中,而DM9000A驅動程式主要目的,可在作業系統內驅動DM9000A乙太網路晶片,讓整個平台可連上網際網路,再搭配TCP/IP Stack編譯,以進入uClinux Kernel 核心內。由於IP層為編譯IPV4,因此在Application層加入BOA Web Server,便可讓整個監控平台的Web Server運作起來,讓監督者透過網頁Browser連接進監控平台,且底層的Ethernet介面硬體部份亦需要打通,讓驅動程式運作起來,這樣才能完整實現有線網際網路監控功能,而本作品之有線網路驅動程式與軟體層架構,如4. 6所示。

 

4.6有線網路驅動程式與軟體層架構

4.1.4 電信網路通訊架構與協定

本作品實作一個GSM_UART_RX硬體電路來接收GSM Modem傳過來的資料,如圖4. 7所示。由於採用Verilog硬體描述語言開發,嵌入深度為100Bytes Shift Register所構成的FIFO,而FIFO的輸入部份,用來存放己經解出GSM Modem傳過來的UART數據(單位:1Byte),因為GSM Modem是以Packet Type來傳送,而傳統的UART內部只有一組8位元的Register,若遇到Packet Type,可能會來不及接收,這將會產生後面進來的資料覆蓋掉前面未處理資料之問題。為克服上述之問題,本作品自行開發GSM_UART_RX,使Shift Registers的內容,可經內嵌的Avalon Slave位址解碼後,再Nios II CPU讀出,以供寫Firmware來處理。本作品之IP設計的主要目的,為擷取出當有人打電話或發簡訊到的三網合一無線感測網路監控平台時,能夠將來電之電話號碼與簡訊內容解讀出來。

有關GSM Modem協定處理方面,就要針對AT Command 做介紹,由於GSM Modem是透過UART傳送AT Command,以控制GSM Modem所傳送之數據;因此我們使用DE2 平台提供的UART介面上連接GSM Modem,由Nios II CPUuClinux中執行的DE2_Multi_Sensor應用程式透過UART介面向GSM Modem發送AT 指令集和緊急回報之簡訊內容。收發短簡訊可分為Block模式、Text模式和PDU 模式等3種模式,而本作品採用為Text模式。

 

4.7無線電信網路架構

利用AT Command所發出簡訊,可分為以下4個步驟:

步驟1.          AT+CMGF=1

步驟2.          AT+CMGS="0919179306"

步驟3.          This is Sort Message Content FOR WSN TEST

步驟4.          ^z

其中步1是用來設定要發簡訊為Text模式,而步驟2可用來指定接收簡訊的手機號碼,步驟3則用來輸入簡訊內容,步驟4輸入^z字元,代表簡訊內容輸入完畢。在步驟123的結束時,要記得加入0x0d字元

4.2硬體設計模擬方塊圖

4.2.1 SPI IP設計與介面訊號模擬

在本作品之無線感測網路端,使用CC1100晶片,由於該晶片提供Digital Interface供外部MCU/FPGA來使用,而它的Digital Interface所使用的SPI介面,如圖4.8所示。而SPI 介面主要是由4根訊號所構成,分別為MISOMOSICSN SCK

 

4.8 CC1100應用電路圖

由於CC1100晶片配置暫存器於SPI位址,通常是從0x000x2F之間,且所配置暫存器皆能執行讀和寫之功能。當對暫存器執行寫之功能時,每當一個待寫入的資料位元組傳輸到SI腳時,狀態位元組將被送至SO腳,如圖4.9所示之時序圖。由於透過在位址頭設置Burst位元,即Burst Bit要設為1,這樣封包的內容才可高效率地被存取,亦即僅需設定一次位址,便可從CC1100晶片的TX/RX FIFO連續的寫入或讀出封包全部資料,以加快資料傳輸速度。這個位址在內部位址計數器內設置起始位址,每增加一個新的位元組(每8個時鐘脈衝SCLK),CC1100晶片內的位址計數器值,便會自動增加1。由於封包的收發方式採用Burst Mode,在傳送過程中CSn訊號要維持低準位,當傳送或接收完成之後,則要將CSn訊號拉回高準位,其中CSn訊號之時序圖如圖4.9所示

 

4.9 CC1100晶片操作之SPI時序圖

觀察圖4.10SPI模擬結果可知,前半段波形為Nios II CPU 送出位址後透過MOSI腳將訊號發出給CC1100晶片,看到MOSI腳的訊號全部為High即位址內容為0xff,而後半段為讀出資料由CC1100晶片,負責送出資料在MISO腳上,而Nios II CPUMISO腳將數據讀取進來。

 

4.10 SPI模擬波形

4.2.2 GSM UART_RX IP 設計與介面訊號模擬

本作品採用QUARTUS II提供的Signal Tap II軟體,來針對Verilog HDL所撰寫的GSM_UART_RX IP進行FPGA內部實際訊號時序抓取回來進行驗證。當打電話給GSM Modem時,GSM Modem會打出『RING』字串時,便會在前後包含一些控字元(0x0a0x0d),總共將7個字元連續打出來,並在終止位元連接下一個Byte的起始位元,而ALTERA提供的UART IP根本無法應付,因此自行設計一個GSM_UART_RX IP來接收GSM Modem所發出的Packet

        本作品使用QUARTUS II提供的SignalTap來進行On-chip波形模擬,如圖4.11所示。觀察圖4.11可知,當接收到GSM Modem偵測到電話打進來時,傳送出RING』給我們開發的GSM_UART_RX IP之模擬結果。

 

4.11 GSM_UART_RX接收到電話鈴聲之SignalTap模擬波形

4.2.3 無線感測模組轉接電路板Schematic設計

本作品利用ORCAD V9.0進行無線感測模組轉接電路板的電路設計工作,如圖4.12所示。觀察圖4.12可知,將TI CC1100晶片的SPI訊號與VCC-GND線路畫好,而其它部份畫入DM9000A網路晶片與CypressSL811HST USB晶片、LCD TEXT 16207模組、RS-232 UART介面與時鐘IC等,可作為將來功能擴充之用。

 

4.12 利用ORCAD V9.0設計無線感測模組轉接電路板之電路圖

4.2.4 無線感測模組轉接電路板PCB設計

透過Power PCB V5.0進行無線感測模組轉接電路板Layout設計,如圖4.13所示。觀察圖4.13可知,本作品之PCB設計為4層電路板,最上層與最下層分別放置IC零件與訊號線,第2層則走Ground Plane,而Ground Plane亦可分為Digital GroundAnalog Ground,以CC1100模組為例,僅需連接Digital Ground而網路的Physical Layer部份,則有Analog Ground的需求,第3層則走Power Plane,有5V3.3V2Plane,以CC1100模組為例,僅需連接3.3V2Plane

由於Power PCB軟體為相當複雜與高速印製電路板之最終選擇的設計環境,它為一個強有力的基形狀化(Shape-Based)與規則驅動(Rules-Driven)的佈線設計解決方案,並採用自動與交互式的佈線方法,此方法可實現先進的目標連接與嵌入(OLE)自動化功能,並有系統地集成前後端的設計工具,包括最終的測試、準備和生產製造過程。

 

4.13 使用Power PCB V5.0設計之無線感測模組轉接電路板Layout

在設計電路板時,首先需規劃具有禁止區(Keepouts)與切割區(Cutouts)2個部分。通常允許定義機械的禁止區域,以確保PCB滿足硬件的裝配要求。另採用Power PCB 的鎖定/保護(Lock/Protect)導線功能,即可鎖定或保護電路板所佈的導線,因此部分的導線或不會被重新佈線。

由於Power PCB 提供CAM能力,因此可進行 Direct CAM、鑽孔數據(Drill Data)Gerber數據的輸出與其它標準的數據交換格式。Direct CAM 輸出,可避免需要讀取Gerber文件到CAM系統中。為節省時間,以避免可能的錯誤和對於數據的誤解,最後將Gerber File送交外面的板廠,以製造出本作品所需的電路板。

4.2.5 無線感測模組轉接電路板成品

        無線感測模組轉接電路板送回來後,接下來的工作為上件,英文簡稱進行編號(Circuit Board on Items),並將所準備好的電子零件給予上件服務的工廠,以將相關ICSMD型式的電阻與電容焊接在電路板上,以節省自行焊接所造成的時間浪費與避免焊接品質不佳之情況。在焊接回來後,即可進入測試工作,根據測試之結果,目前CC1100晶片模組可正常工作,而LCD Text 16027,亦可正常工作。接下來將陸續將測試USB SL811HSTEthernet DM9000A、時鐘ICUART幾個電路的功能。經過這一次的訓練,讓我們學會設計電路圖、設計電路板的Layout、送電路板廠製造與電子元件之上件,到最後電路板的測試等一系列流程,皆由本作品之開發過程,相當紮實的走了一遍。而所完成之無線感測模組轉接電路板成品,如4.14所示。

 

4.14 無線感測模組轉接電路板成品

4.2.6 感測器簡介

本作品所開發之微感測器,計有溫度感測器、溼度感測器、人體紅外線感測器、瓦斯煙霧感測器、照度感測與地震位移感測器等6種,接著詳述這些微感測之原理與構造如下。

(1)、溫度感測器

        電路板上加上一個8051微控制器與CC1100無線模組,再加上一個DS18B20溫度感測器,所構成的無線溫度感測節點,以實現顯示溫度值在的板子上,如圖4.15所示。這個無線溫度感測裝置,亦可發送封包夾帶溫度值,以433MHz的無線工作頻率來發送,而監控平台端可自行掃瞄到該頻段,以接收溫度感測器之所感測到溫度資料。

 

4.15 無線溫度感測模組

(2)濕度感測器

在電路板上加上一個RHU-217-5濕度感測器、8051微控制器與CC1100無線模組,即可構成無線濕度感測節點,而本作品所使用之濕度感測器外觀,如圖4.16所示。

 

4.16濕度感測器

(3)人體紅外線感測器

在電路板上加上一個PIR145人體紅外線感測器、8051微控制器與CC1100無線模組,即可構成無線人體紅外線感測器,此種感測器可隨時偵測出異常的溫度變化,即移動的溫度對它而言是無所遁形,如圖4.17所示。此種感測器通常需一個焦點鏡片(Lens)的配合,以增加它的偵測距離,並在輸出加一個小夜燈,當人體接近時,它就會點亮,而離開時便會會因而熄滅,如圖示4.18所示

 

4.17人體紅外線感測器

 

4.18人體紅外線感測成品

(4)瓦斯煙霧感測器

在電路板上加入一個TG135瓦斯煙霧感測器、8051微控制器與CC1100無線模組,即可構成的無線瓦斯煙霧感測器,在正常的環境下(無偵測到瓦斯),通常會顯示999,如圖4.19所示。當發生瓦斯外洩或偵測到有煙霧時,便會顯示111如圖4.20所示,且警報器亦會斷續響。為將接收到的瓦斯迅速排放出去,在出口加裝一套抽、排風系統,如圖4.21所示,當偵測有瓦斯外洩時,風扇便會啟動,此不但可排出瓦斯,亦可抽進來新的空氣,以達到快速換氣的效果。而此無線瓦斯煙霧感測裝置,亦會發送封包夾帶數值以479MHz的無線工作頻率,監控平台端亦會自行掃瞄到該頻段,以接收瓦斯外洩之資料。

 

4.19無線瓦斯煙霧感測器(無偵測到瓦斯時)

 

4.20無線瓦斯煙霧感測器(偵測到瓦斯時)

 

4.21無線瓦斯煙霧感測器成品

(5)照度感測器

 電路板上加上一個SM5626太陽能感測器、8051微控制器與CC1100無線模組,即可構成的無線濕度感測器,如圖4.22所示。

 

4.22 照度感測器

(6)地震移位感測器

在電路板上加上個地震位移感測器、8051微控制器與CC1100無線模組,即可所構成的無線地震位移感測器。在正常的環境下,亦即處於無震動狀態,通常會顯示999,如圖4.23所示。當發生地震、搖晃或位移時,便會顯示111,如圖4.24所示,且警報器與警示燈,亦會跟著斷續響起與閃滅。這個無線地震位移感測裝置,亦會發送封包夾帶數值以509MHz的無線工作頻率來發送,監控台端將會自行掃瞄到該頻段,並將數值接收下來。

 

4.23無線地震位移感測器(未感測到震動時)

 

4.24無線地震位移感測器(感測到震動時)

4.3軟體流程圖

在軟體設計方面,主要使用uClinux的跨平台交叉編譯器Nios2-Linux-gcc來編譯程式與CC1100晶片溝通,以作為驅動CC1100晶片暫存器與接收CC1100晶片溫度封包,便可將Nios2UART端,列印出溫度與RSSI(接收訊號強度)值,於監控平台七段顯示器上與Web 網頁上,而整個監控平台的軟、硬體架構,如圖4.25所示。觀察圖4.25可知,藍色方塊的中間部分為WSN 系統軟體要執行的項目,這些軟體要執行的項目是以uClinux為基礎所發揮出來,而由左至右分別為DE2_Multi_Sensor應用程式、Write Temp FileWeb Server輸入手機號碼與CGI程式到BOA Web Server的啟動。

 

4.25 WSN系統軟、硬體整合架構

4.3.1 嵌入式作業系統跨平台編譯器製作流程圖

ALTERANios II平台上跑uClinux,必須有一個uClinux的内核文件,再由此Kernel來編譯系统。通常Wiki上有已編譯好,並對應DE2Image,以提供下載之用,然隨著板子與IC不同(本作品之板子為DE2),導致硬體I/O位址不同,所要使用到的驅動程式,亦會不一樣,因此需要採用Customize的方式來編譯Kernel。本作品之嵌入式作業系統跨平台編譯器製作,總共分以下3個步驟來進行:

 (1)建立跨平台編譯環境:首先下載二進制包裝Binary Tool Chain,然後在终端機中輸入『sudo tar jxf nios2gcc-20071207.tar.bz2 -C /』,這將會把檔案解壓縮到『/opt/nios』目錄,接著將『/opt/nios2/bin』加入到Path環境變數中,即於『/etc/profile』與『/home/jy/. bashrc』加入『export PATH=$PATH:/opt/nios2/bin』,最後再執行『#>source ~jy/.bashrc』,如圖4.26所示

 

4.26 設置跨平台編譯器的路徑於使用者jy的環境變中

(2) 準備uClinux原始核心程式碼:使用git這個版本控制軟體(據說此軟體為Linus本人維護的一個強大的版本控制軟體),首先安裝git-core作為普通用户,再給電子郵件與用户名稱,而詳細操作步驟如下:

 git config --global user.email you@email.com

 git config --global user.name "Your Name"

接著下載原始uClinux Kernel 2.6核心程式碼包裝,這個檔案大約3400MB(uCLinux-dist-20071107.tar.bz2),再找一個大於2GB的分區目錄,作為解壓與作為以後的編譯做準備,最後解壓縮檔案(tar jxf uCLinux-dist-20071107.tar.bz2)

(3) 編譯階段:進入uCLinux-Dist目錄後,鍵入make menuconfig選擇目標板 Altera, Nios II Nommu注意:Libc 必須選 (None) 因需要用交叉编譯中的 uClibc而不是使用uCLinux-DistuClibc ,因此在開始編譯Nios IIuClinux核心前,必須將SOPC_Bulider所建立出來的System_0.ptf,加入uClinuxKernel Source中,亦即從XP複製到Vmware 6.0裏的Fedora Core 8作業系統內,此目的可讓Kernel Source解目前Nios II CPU的週邊有那些裝置,而每個裝置的Base Address與裝置是否有IRQ,它的IRQ號碼是多少,標準輸出入裝置為JTAG-UARTRS-232,還有要使用那個Memory做為執行uClinux的記憶體裝置等。

        實現本作品之Nios II uClinux Kernel軟體開發架構圖,如圖4.27所示。觀察圖4.27可知,圖中有一個System_0.ptf檔,這是從Quartus II ProjectSOPC_Bulider中建立出來的,因此必須把它複製到Vmware 6.0裏的Fedora Core 8作業系統內的jy使用者帳戶之目錄中之uCLinux-dist目錄內。而支援Nios II CPU的跨平台編譯器,通常來自nios2gcc-200712.7.tar.bz2,可將它解壓縮到『/opt/nios2/bin』目錄內,並在編譯Kernel時會用到nios2-linux-uclibc-gcc,而核心的部份,需透過gitlynx之版本控制軟體與網頁內容Parsing工具,以負責Patch核心原始碼。

 

4.27 Nios II uClinux Kernel軟體開發架構圖

4.3.2 嵌入式作業系統移植至Nios II平台之DE2 Board

Vmware Linux 裏所做出來的uClinux Kernel 2.6核心之 zImage 映像檔案複製到Windows XP作業系統中,可透過以下2種方法達成:

(1)USB隨身碟的方式複製。

(2) Vmware Linux vs_ftp,再利用Windows XP執行ftp Client連到Vmware Linux 去把zImage抓回Windows XP,並Nios II Command Shell下執行

#> nios2-download -g zImage(下載zImage file DE2板子的SDRAM)。

#>nios2-terminal (透過JTAG-UART控制Nios II 開始執行SDRAM上的uClinux)

這一連串之uClinux下載與執行過程,請參考圖4.284.32。圖4.32的紅色框框代表DM9000A乙太網路成功驅動起來了。

 

4.28 執行Nios II Command Shell

 

4.29下載zImageDE2板子

 

4.30 zImage下載完成

4.31 啟動Nios II-Terminal

 

4.32 DM9000A乙太網路成功在uClinux驅動起來

4.3.3 感測器接收之應用程式開發流程圖

本作品所開發的DE2_Multi_Sensor 應用程式,執行在uClinux作業系統中的User Space層,主要有主動切換頻率來接收各種無線感測節點(溫度、濕度、瓦斯、地震與照度)回傳的感測值與控制GSM Modem,當偵測到緊急狀況發生時,可用短簡訊通報等2個主要功能。針對DE2_Multi_Sensor 應用程式之第1個功能,所對應的硬體為使用SOPC_Builder提供的PIO元件,實現SPI(Serial Peripheral Interface)介面控制所需要的4I/O,分別為MOSIMISOSCKCSN訊號。這將可讓CC1100無線模組,透過我們自行製作的無線感測模組轉接電路板與DE2平台的FPGA連接,並接收無線感測節點回傳的感測值,包含溫度、濕度、震度與瓦斯等感測值,並針對每一種感測項目輸出到獨立的檔案,再讓檔案內記錄目前溫度(度數)、濕度(Percent)、是否有感測到地震或瓦斯等訊息,而這些檔案通常可提供Web ServerCGI程式讀取之用,並顯示在監督者的Web Browser上,以供監督者在網頁上,能實現即時監看的重要功用。

針對DE2_Multi_Sensor 應用程式第2個功能,即讀取CGI程式所建立的Phone.out檔案,在這檔案有鍵入監督者的手機號碼,而該手機號碼可由監督者提供,並透過網頁設定管理者的手機號碼產生出來。當有緊急狀況發生時,例如溫度超過40度就發出簡訊給監督者所提供之手機,當溫度降回至40度後,將會再發一封簡訊給監督者所提供之手機,以告知緊急狀況解除。發送簡訊之時機,在超過臨界點以上或以下之瞬間,程式設有判斷機制,僅會發出一封簡訊,而不會在溫度超過40度連續發送簡訊,導致過度浪費通訊費用之情況。而DE2_Multi_Sensor 應用程式,必須能控制UART介面與外部的GSM Modem溝通,且使用AT CommandGSM Modem來送出簡訊,並讓監控平台與電信(Telecom)網路連接,如圖4.33所示

 

4.33 DE2_Multi_Sensor程式實現3網連接

4.3.4 Web Server HTML網頁架構

本作品所設計之HTML網頁設計架構,如圖4.34所示。觀察圖4.34可知,在首頁設計部份有密碼驗證功能,如圖4.35,唯有密碼輸入正確才可以進入網頁內的多功能選單畫面。

在多功能選單畫面下(4.36)可切換到監控溫度(4.37),瓦斯、地震、人體紅外線偵測與手機號碼設定之網頁,監控網頁的部份會以每5秒就自動更新網頁內容,即溫度,瓦斯、地震、人體紅外線偵測的監控值,而感測的數值則來自Sensor.out檔案,而Sensor.out的內容又來自DE2_Multi_Sensor 應用程式。

關於手機號碼設定網頁可用來讓監控員輸入監督者的手機號碼,如圖4.38所示,並且透過CGI程式接收到手機號之後,在uClinux的檔案系統內產生一個Phone.out的檔案,讓檔案內容為手機號碼,當有緊急狀況發生時,DE2_Mulit_Sensor程式,將會讀取Phone.out檔案內的手機號碼,並結合成AT Command與要發送的簡訊內容,然後發出緊急短簡訊通報給監督者,並將網頁顯示在緊急畫面的頁面,如圖4.39所示。另外網頁亦設計留言版,以供監督者在網站上留言或記錄,如圖4.40所示

 

4.34 WSN網頁設計架構圖

 

4.35 首頁輸入監督者密碼

 

4.36 網頁的多功能選單

 

4.37 溫度顯示網頁

 

4.38 發送緊急短簡訊的手機號碼設定

 

4.39 緊急突發狀況之網頁

 

4.40 留言板網頁

本作品目前未實現之感測器,尚有濕度監控、照度監控與瓦斯監控等多功能選單的監控畫面,這些監控畫面皆使用CGI程式,不斷的讀取DE2_Mulit_Sensor程式所輸出的感測檔案內容,並以HTML的格式輸出在Web Browser的畫面上,且以5秒做一次感測值的更新。

4.3.5 CGI 程式流程圖

本作品的CGI程式,皆以C語言來撰寫,並透過跨平台編譯器nios2-linux-gcc來進行編譯。開發CGI程式可讓BOA Web Server能與 DE2_Multi_Sensor 溝通,而溝通的方式是透過uClinuxFilesystem來達成。由DE2_Multi_Sensor產生Sensor.out文字檔案與手機號CGI程式,產生Phone.out文字檔案,關於二者之間的介面程式,如圖4.41所示。

 

4.41 CGI 程式之輸出入介面

本作品開發2CGI程式,接著詳細說明這2CGI程式之功能如下:

(1) 監控回報CGI程式:負責將DE2_Multi_Sensor程式所輸出的溫度、濕度、照度與瓦斯等感測檔案,名稱為Sensor.out,並將檔案內容讀出,再轉換成HTML語言,直接輸出到監督者的網頁上,以讓監督者的網頁,能夠每5秒更新一次感測畫面。

(2) 手機號碼CGI程式:負責將監督者在網頁上所設定的手機號碼,輸出到uClinux 檔案系統中的檔案,名稱為Phone.out,其目的是要讓DE2_Multi_Sensor程式將檔案內的手機號碼讀出,並轉成AT Command的撥號碼,當WSN端有偵測到緊急狀況時,就可將AT Command 送往GSM Modem,作為緊急短簡訊送出給監督者。

本作品之CGI的片段程式aa.cCGI Makefile內容,分別如圖4.42與圖4.43所示。

 

4.42 CGI的片段程式(aa.c)

 

4.43 CGI Makefile內容

4.4實作結果

本作品之實作部分可分為無線感測網路、電信網路與有線網際網路等3大區塊,而這3個網路皆已經連接至友晶科技所開發之DE2平台,如圖4.44所示。接著詳細說明這3大區塊之主要功能如下:

(1)無線感測網路的部份:用來接收各式感測器回傳回來的感測值,分別為光感測器、人體紅外線感測器、瓦斯感測器、溫度感測器、濕度感測器與地震感測器等數據。

(2)、電信網路的部份:透過DE2平台UART介面與外部的GSM/GPRS Modem連接,再連上電信網路,而GSM Modem放置一張SIM Card,並連上電信業者的基地台。當監督者出門在外,只要有隨身攜帶著手機,當有緊急狀況發生時,便能接收到DE2平台WSN監控平台偵測的異常狀況時所緊急通報的簡訊。

(3)、有線網際網路:透過DE2平台DM9000A 乙太網路介面連上外面的Internet,只要監督者在有網際網路服務的地方,即可透過3G手機、筆電或桌上型電腦上網,開啟網頁Browser連接到監控平台的Web Server,即可得知無線感測網路的各個感測節點所回傳的感測值。

 

4.44 DE2平台與三網連接之架構圖

當在網頁設定一個緊急電話設定的網頁,以輸入監督者的手機號碼,如圖4.45所示。

 

4.45 溫度發射端與溫度接收端(Nios II + CC1100

        當偵測溫到度異常時,例如溫度超過40度時,網頁便自動顯示出溫度過高的背景圖示,並將溫度值顯示於網頁上,如圖4.46所顯示44.8度。

 

4.46 網頁自動顯示出溫度過高的圖示

        當發生溫度過高之緊急情況,DE2平台便透過GSM Modem發送出短簡訊到監督者的手機,如圖4.47所示。

 

4.47 當溫度過高時傳送簡訊至監督者的手機

4.5結論

本參賽作品是在ALTERA Cyclone II FPGA內,結合Hardware/Software Co-Design Co-Verification,實現有線/無線網路的通訊能力,整合在TerAsicDE2平台上,以打造出具有三網合一之無線感測網路監控平台。

Hardware設計部份,本作品打造出Nios II CPU 平台在Avalon Bus上掛載了27IP,其中有一個Avalon Slave IP為自行用Verilog HDL設計出來,將它取名成GSM_UART_RX它是負責接收GSM Modem所發出的UART Packet,作為接收訊息之用,以取代傳統ALTERA 提供的UART無法接收一個封包內有連續Bytes傳送過來的缺點,它內部有100BytesShift Register,當收完一個Packet後,Nios II CPU,便可去FIFO中取出資料來解讀來電ID或簡訊內容。

Hardware無線感測模組轉接電路板設計部份,此電路板為自行開發的套件,目的可讓TerAsic DE2平台的GPIO介面能與無線感測模組連接。無線感測模組轉接電路板,除連接無線CC1100模組之外,尚有UARTUSBSL811HST)、DM9000ALCD_TEXT、時鐘ICLED等元件,可供將來擴充更多功能時使用。

ALTERA Cyclone II FPGA內部Software設計部份,成功移植uClinux Kernel 2.6作業系統,並驅動DM9000A乙太網路,讓整個平台在uClinux Kernel 2.6作業系統環境中,能夠連上Internet,本作品亦陸續架設監控功能的Web Server、自行開發CGI程式與DE2_Multi_Sensor應用程式,並成功的將整個三網合一無線感測網路監控功能建立起來。最後將目前本作品所完成項目歸納如下:

Ø          完成三網合一之無線感測網路監控平台。

Ø          完成快速GSM_UART_RXAvalon Slave IPVerilog HDL設計,負責接收GSM Modem所發出的UART Packet,做為解讀來電顯示電話號碼與短簡訊。

Ø          完成近端監控所需的語音播報,透過DE2Audio Codec之輸出。

Ø          完成製作無線感測模組轉接電路板,將DE2 BoardCC1100模組連接起來。

Ø          完成CC1100模組與DE2 BoardFPGA/CPLDRF收發封包。

Ø          完成uClinux Kernel 2.6 PortingNios II CPU平台。

Ø          完成具有監控無線感測網路所需之CC1100模組的SPI介面的uClinux C應用程式(DE2_Multi_Sensor)

Ø          完成具有控制GSM Modem之無線電信網路,來發送短簡訊的uClinux C應用程式(DE2_Multi_Sensor)

Ø          完成BOA Web Server提供網際網路監控。

Ø          完成Web Server的網頁設計,本網頁可提供以下之功能:

n          需透過網頁輸入監督者的密碼才可登入。

n          可透過網頁設定監督者的手機號碼,供緊急發簡訊用:

n          可在網頁上顯示出溫度、濕度、瓦斯煙霧、地震位移、太陽能照度與人體紅外線等無線感測值。

n          留言板

Ø          完成CGI程式連接Web Server與應用程式(DE2_Multi_Sensor)

Ø          完成溫度、濕度、瓦斯煙霧、地震位移、太陽能照度與人體紅外線等6種無線感測節點。

 

(Revision: 6 / 2010-09-15 16:32:15)

5. 設計方法 (Final Project Paper)

5. 設計方法

        本作品的設計方法,主要分成SOPC系統設計Verilog硬體描述語言uClinix中開發的C程式語言等進行詳細解說,本作品目前之實作內容詳述如下。

章節目錄

5.1 SOPC系統設計... 3

5.1.1 三網合一無線感測網路SOPC系統架構設計... 3

5.1.2 SPI介面溝通CC1100晶片之uClinux C程式開發... 4

5.1.3 有線網際網路Verilog IP設計... 7

5.1.4 電信網路通訊Verilog IP設計... 10

5.1.5 GSM Modem 溝通之AT CommanduClinux C程式開發... 21

5.1.6 三網合一之uClinux C應用程式開發... 23

5.1.7 uClinux 網頁伺服器CGI 程式開發... 28

5.1.8 Web Server HTML網頁設計... 34

5.1.9 WSN監控平台Audio Codec效輸出Verilog Code程式... 37

5.1.10 WSN監控平台Audio Codec音效輸出uClinux C程式... 43

5.2結論... 48


    

5.1 Nios II &三網合一SOPC設計架構圖... 3

5.2 PIO Input 1bit模組介面... 4

5.3 SPI操作函式SpiTxRxByte() 5

5.4無線感測網路通訊架構與協定... 6

5.5有線乙太網路硬體架構... 7

5.6 DM9000A Verilog IP 的輸出入Port宣告... 8

5.7 DM9000A Verilog IP內部電路描述... 9

5.8無線電信網路架構... 10

5.9電信網路IPVerilog Code. 20

5.10 uClinux下發送短簡訊的AT CommandC Code. 23

5.11 DE2_Multi_Sensor程式實現3網連接... 24

5.12 DE2_Multi_Sensor程式uClinux C Code. 27

5.13 CGI 程式之輸出入介面... 28

5.14 監控回報的CGI程式... 30

5.15產生手機號碼的CGI程式... 34

5.16 WSN網頁設計架構圖... 34

5.17 首頁輸入監督者密碼... 35

5.18 網頁的多功能選單... 35

5.19 溫度顯示網頁... 35

5.20 發送緊急短簡訊的手機號碼設定... 36

5.21 緊急突發狀況之網頁... 36

5.22 留言板網頁... 37

5.23 AUDIO_DAC_FIFO Verilog程式修改... 41

5.24 WM8731的音效取樣率暫存器設定值... 41

5.25 WM8731的音效取樣率暫存器R8. 42

5.26 WM8731的音效設定I2C介面Verilog程式... 42

5.27 uClinux C程式之SD_CARD修改... 48

5.28 無線感測模組轉接電路板成品... 49


5.1 SOPC系統設計

5.1.1 三網合一無線感測網路SOPC系統架構設計

本作品所實現之雙Nios II &三網合一無線感測網路(WSN)SOPC系統架構圖,如圖5.1所示。觀察圖5.1可知,在Avalon Bus下的IPNios II CPUSDRAM ControllerFlash Controller的組合,這個組合為執行嵌入式作業系統與WSN的監控程式,而Avalon Bus下的cc1100_csncc1100_mosicc1100_misocc1100_sckcc1100_GDO為無線感測網路,連接cc1100晶片所需的SPI介面,透過SOPC_Builder所提供的PIO,以達成SPI BusCC1100晶片溝通,並掛在Avalon Bus上。有線網際網路採用DM9000A控制IP,如圖5.1的藍色框框部份,即利用DM9000A控制IP來與FPGA外部的DM9000A乙太網路晶片讀寫暫存器與傳輸資料,以實現溝通有線網際網路之用途。無線電信網路部份,本作品自行開發Verilog CodeGSM Modem溝通用的UART IP,如圖5.1的紅色框框部份(my_gsm_uart),以實現電信網路的連接。

此外,Nios II CPU_1所連接的IP為SD_CARD 所需之IP(SD_DAT SD_CMD SD_CLK)與Audio_DAC達成音樂播放之功能。

 

5.1 Nios II & 三網合一SOPC設計架構圖

為實現本作品達到獨立性運作(Stand Alone)之功能,Cyclone II FPGA的內部硬體架構規劃,包含Nios II CPU的架構,透過「*.pof」檔燒錄到連接Cyclone FPGA外部的EPCS EEPROM中;而軟體部份,Nios II CPU需執行的uClinux Image程式之zImage檔,並結合Boot Loader燒錄到DE2板子上的外部Flash Memory,因此只要打開電FPGA內部的Nios II CPU,即可從Flash Memory啟動uClinux作業系統, uClinux作業系統開機完成後,即可依序啟動Web ServerCGI程式與DE2_Multi_Sensor程式,以將整個WSN監控系統Run起來。

5.1.2 SPI介面溝通CC1100晶片之uClinux C程式開發

本作品主要在ATERA Cyclone FPGA內打造出SOPC System , 並加入GPIO (General Purpose I/O),以實現出SPI Bus來與無線模組溝通(CC1100晶片)溝通,而這5SPI訊號分別為:

1.          cc1100_sck

2.          cc1100_csn

3.          cc1100_mosi

4.          cc1100_miso

5.          cc1100_GDO0

由於cc1100_sckcc1100_csncc1100_mosi,皆屬於Output Pin之屬性,而cc1100_misocc1100_GDO0屬於Input Pin 之屬性。接著詳細說明這些Output PinInput PinAvalon Bus位址定義與結合到uClinux的應用程式之方法如下:

(1)     首先在uClinux的應用程式專案中,寫一個rf_cc1100.h之程式碼,如圖5. 2所示,在該標頭檔中定義這5個訊號的BASE_Address,其目的是為給rf_cc1100.c使用。

 

5.2 PIO Input 1bit 模組介面

(2)     SPI 操作的副程式:撰寫一個rf_cc1100.c程式碼,如圖5.3所示,在該程式檔中寫一個函式叫做SpiTxRxByte(),在該函式中有一個做8次的for迴圈,目的為伴隨CC1100_SCK_BASEHi-Lo變化,以控制8bits (1Byte的資料),並由CC1100_MOSI_BASEGPIO腳打出,再接收8bits (1Byte的資料),從CC1100_MISO_BASEGPIO腳進來。如圖5. 3所示。

 

5.3 SPI操作函式SpiTxRxByte()

觀察圖5.3可知,在121137行設計一個可做8次之for迴圈,其中dat為輸入變數。若欲透過CC1100_MOSI_BASE腳輸出,則在圖5.3123128行,便可進行Parallel In Serial Out (PISO)的動作,且以MSB優先輸出的方式來進行,因在123行的if裏,可將dat & 0x80做運算,目的為可取出datMSB位元,若運算所得之結果為1,便可將CC1100_MOSI_BASE腳輸出1。若125行為0,便會將CC1100_MOSI_BASE腳輸出0(如第127)。接著在圖5.3130行,將CC1100_SCK_BASE設成1,以製造出SPI Clock的上昇緣,由於監控平台是屬於SPI介面的Master端要負責,以製造出SPI Clock

當將1bit打出去後,便要進行1bit的接收,並透過讀取CC1100_MISO_BASEGPIO腳,若在圖5.3的第134CC1100_MISO_BASE讀進來之值為1,便會將temp++,否則temp不需要加1,這樣就算接收1bit進來。接著在135行,將CC1100_SCK_BASE設成0,以製造出SPI Clock的下降緣。依照上述之方法,依序做完8for迴圈後,便可將temp回傳給當初呼叫函式SpiTxRxByte()的地方。

本作品之無線感測網路通訊架構與協定,如圖5.4所示,在圖5.4A.1部份為監控平台初始化CC1100無線模組的暫存器,包含設定無線調變方式(2-FSK)、無線工作中心頻率(433MHz)、資料傳輸速率(128kbps)與啟動硬體CRC檢查封包的基本設定,而A.2部份為監控平台操作中的狀態,這部分可將無線CC1100晶片的工作狀態Strobe設定為RX 模式,若偵測到封包接收進來,再使用Burst Read的方式將封包從CC1100RX FIFO中取出,接著再進行解讀封包內容的動作,例如解出溫度值。

 

5.4無線感測網路通訊架構與協定

當感測節點在固定的Channel中不斷的發出封包時,監控平台便可不斷接收封包。若監控平台同時監控溫度、濕度、照度、震度與瓦斯等多個感測節點,由於這些感測節點,需有各自的無線模組,且使用不同的Channel,方可讓這些感測節點,不會在同一個頻率下操作而相互影響,因此這些感測節點與監控平台端的關係,皆以星狀方式排列。本作品所設計之感測節點,所使用的每個Channel,皆間隔為3MHz,因此只要改變監控平台的Channel Register,即可用來監控不同的感測器。

5.1.3 有線網際網路Verilog IP設計

在本作品之硬體部份,加入由Verilog 所撰寫的DM9000A IP,並放在Avalon Bus上,由於Avalon BusAvalon Slave IP,因此可利用CPU Based方式來操作,讓CPU可透過Verilog 所寫的DM9000A IP,以讀寫DM9000A的暫存器或收發封包,因此該IP類似一個Bridge,以負責Avalon Bus介面與DM9000A 的介面,如圖5.5所示。觀察圖5.5可知,當外部DM9000A網路晶片接收到封包時,將會中斷Nios II CPU,而Nios II CPU可透過DM9000A Avalon SLAVE IP,將乙太網路封包從DM9000A CHIP RX FIFO取出,並透過SDRAM Avalon SLAVE放到SDRAM去,當有封包要發送時,Nios II CPU可透過SDRAM Avalon SLAVE取出SDRAM內的封包,經過DM9000A Avalon SLAVE IP送到DM9000A ChipTX FIFO去,並將封包發送出去。一般而言,控制封包收送之讀、寫暫存器與上層的IP/TCP層溝通,需要透過驅動程式來完成。

 

5.5有線乙太網路硬體架構

本作品之軟體部份,即uClinux內的Protocol Stack層,須將DM9000A Verilog IP放入SOPC 系統架構中,其實它為一個界於Avalon BusDM9000A Bus間之Bridge,當CPU想要讀寫DM9000A的內部暫存器,只要將欲讀/寫的位址與Read/Write控制訊號下達到DM9000A Verilog IP即可,即DM9000A Verilog IP的硬體邏輯電路,會轉換成DM9000A Bus的時序控制訊號,以發送給FPGA外部的DM9000A晶片,如圖5.6所示。觀察圖5.6可知,DM9000A Verilog IP 的輸出與輸入Port宣告,分成Host SideDM9000A SideHost Side可連接Avalon Bus介面,而DM9000A Side可與外部DM9000A CHIP相連的部份。

 

5.6 DM9000A Verilog IP 的輸出入Port宣告

本作品在DM9000A Verilog IP電路實作部份,如圖5.7所示。在圖5.7之第51ENET_DATA16位元的資料雙向埠,可用來與DM9000A16位元的資料埠連接,ENET_DATA16位元三態閘,當ENET_WR_NLow訊號時,表示要寫資料進入DM9000A晶片,此時就會將TMP_DATA傳送到ENET_DATA資料埠,否則當ENET_WR_NHigh訊號時,表示要從DM9000A晶片讀取資料,此時ENET_DATA資料埠,必須設定為High Impedance

在圖5.76773行,則將oDATAENET_DATA連接,oDATAAvalon Bus的讀取資料滙流排。oINTENET_INT連接,oINTAvalon Bus的中斷訊號。TEM_DATAiDATA連接,iDATAAvalon Bus的輸出資料滙流排。ENET_CMDiCMD連接,iCMDAvalon Bus Address Bit 2,可用來指定讀寫DM9000A晶片的暫存器的位址或資料。ENET_CS_NiCS_N連接,做為DM9000A晶片的晶片選擇訊號,即ENET_RD_NiRD_N連接,做DM9000A晶片的讀取訊號,而ENET_WR_NiWR_N連接,可做為DM9000A晶片的寫入訊號。

 

5.7 DM9000A Verilog IP內部電路描述

5.1.4 電信網路通訊Verilog IP設計

本作品實作一個GSM_UART_RX硬體電路,以接收GSM Modem傳過來的資料,如圖5. 8所示。由於採用Verilog硬體描述語言開發,嵌入深度為100Bytes Shift Register所構成的FIFO,而FIFO的輸入部份,可用來存放己經解出GSM Modem傳過來的UART數據(單位:1Byte)。由於GSM Modem皆以Packet Type來傳送,而傳統的UART內部只有一組8位元的Register,若遇到Packet Type,可能會產生來不及接收之問題,這將會導致後面進來的資料,覆蓋掉前面未處理資料之問題。為克服上述之問題,本作品自行開發GSM_UART_RX,即Shift Registers的內容,可經由內嵌的Avalon Slave位址解碼後,再由Nios II CPU讀出,以供寫入Firmware來處理。本作品之IP設計的主要目的,為擷取出當有人打電話或發簡訊到的三網合一無線感測網路監控平台時,能夠將來電之電話號碼與簡訊內容解讀出來。

 

5.8 無線電信網路架構

本作品亦自行開發設計的Verilog Code,如圖5.9所示,在圖5.9my_gsm_uart模組裏,宣告100個暫存器從uart_d0 uart_d99如圖5.9的第37~136行,每個暂存器皆為8位元,正好用來存放1個字元,亦即簡訊收下來的內容或來電顯示電話號碼。由於本作品之硬體有100Bytes的容量,這足以儲存這些資訊,而由於GSM Modem發出來的訊號為Continue Data Type,且具連續傳送資料之功能。舉例來說,Byte AByte BGSM Modem連續發出來,而Byte AUART Stop Bit,緊密連接下一個Byte BUART Start bit,中間便不會任何Delay的情況發生。若使用傳統SOPC Builder所提供的Avalon UART,將會出現資料Overwrite的現象,即當Nios 2 CPU來不及讀取UART Rx Register裏的Byte A資料時,便馬上被後面來的Byte B資料覆蓋掉。由於上述之原因,本作品自行設計一個IP,以便能順利將GSM Modem所發出的連續UART資料,全部接收下來,並存到硬體的100Register中,因此這個IP非常重要的,亦即在這個IP開發出來後,本作品便可實現來電顯示電話號碼與解讀收到簡訊的所有內容。

     1         // synthesis translate_off
     2         `timescale 1ns / 100ps
     3         // synthesis translate_on
     4         module my_gsm_uart (
     5                          // Host Side:
     6                           address,
     7                           chipselect,
     8                           clk,
     9                           reset_n,
    10                           write_n,
    11                           writedata,
    12                           readdata,
    13                           read,
    14        
    15                          // GSM Modem UART Side:
    16                           uart_in,
    17                                       f
    18                        );
    19        
    20          output           f;
    21          input            uart_in;
    22          output [ 7: 0] readdata;
    23          input   [ 15: 0] address;
    24          input            chipselect;
    25          input            read;
    26          input            clk;
    27          input            reset_n;
    28          input            write_n;
    29          input   [ 7: 0] writedata;
    30        
    31          reg     [ 7: 0] readdata;
    32          reg     [ 7: 0] data_out0;
    33          reg     [ 7: 0] data_out1;
    34          reg     [ 7: 0] data_out2;
    35          reg     [ 7: 0] uart_wraddress;
    36          
    37          reg     [ 7: 0]     uart_d0,
    38                                uart_d1,
    39                                                       uart_d2,
    40                                                       uart_d3,
    41                                                       uart_d4,
    42                                                       uart_d5,
    43                                                       uart_d6,
    44                                                       uart_d7,
    45                                                       uart_d8,
    46                                                       uart_d9,
    47                                                       uart_d10,
    48                                                       uart_d11,
    49                                                       uart_d12,
    50                                                       uart_d13,
    51                                                       uart_d14,
    52                                                       uart_d15,
    53                                                       uart_d16,
    54                                                       uart_d17,
    55                                                       uart_d18,
    56                                                       uart_d19,
    57                                                       uart_d20,
    58                                uart_d21,
    59                                                       uart_d22,
    60                                                       uart_d23,
    61                                                       uart_d24,
    62                                                       uart_d25,
    63                                                       uart_d26,
    64                                                       uart_d27,
    65                                                       uart_d28,
    66                                                       uart_d29,
    67                                                       uart_d30,
    68                                                       uart_d31,
    69                                                       uart_d32,
    70                                                       uart_d33,
    71                                                       uart_d34,
    72                                                       uart_d35,
    73                                                       uart_d36,
    74                                                       uart_d37,
    75                                                       uart_d38,
    76                                                       uart_d39,
    77                                                       uart_d40,                                             
    78                                                       uart_d41,
    79                                                       uart_d42,
    80                                                       uart_d43,
    81                                                       uart_d44,
    82                                                       uart_d45,
    83                                                       uart_d46,
    84                                                       uart_d47,
    85                                                       uart_d48,
    86                                                       uart_d49,
    87                                                       uart_d50,
    88                                                       uart_d51,
    89                                                       uart_d52,
    90                                                       uart_d53,
    91                                                       uart_d54,
    92                                                       uart_d55,
    93                                                       uart_d56,
    94                                                       uart_d57,
    95                                                       uart_d58,
    96                                                       uart_d59,
    97                                                       uart_d60,
    98                                                       uart_d61,
    99                                                       uart_d62,
   100                                                       uart_d63,
   101                                                       uart_d64,
   102                                                       uart_d65,
   103                                                       uart_d66,
   104                                                       uart_d67,
   105                                                       uart_d68,
   106                                                       uart_d69,
   107                                                       uart_d70,
   108                                                       uart_d71,
   109                                                       uart_d72,
   110                                                       uart_d73,
   111                                                       uart_d74,
   112                                                       uart_d75,
   113                                                       uart_d76,
   114                                                       uart_d77,
   115                                                       uart_d78,
   116                                                       uart_d79,
   117                                uart_d80,
   118                                                       uart_d81,
   119                                                       uart_d82,
   120                                                       uart_d83,
   121                                                       uart_d84,
   122                                                       uart_d85,
   123                                                       uart_d86,
   124                                                       uart_d87,
   125                                                       uart_d88,
   126                                                       uart_d89,
   127                                uart_d90,
   128                                                       uart_d91,
   129                                                       uart_d92,
   130                                                       uart_d93,
   131                                                       uart_d94,
   132                                                       uart_d95,
   133                                                       uart_d96,
   134                                                       uart_d97,
   135                                                       uart_d98,
   136                                                       uart_d99;                                             
   137          
   138          //---------------step motor-----------------
   139        
   140          reg              clk_scan;
   141          reg              f;           //256Hz
   142          
   143          reg     [ 5:0]    delay;
   144          reg     [26:0]   hz_counter;
   145          reg     [15:0]   d_ff;
   146        
   147          wire             phase,direction;
   148          wire    [15:0]   comp;
   149          wire    [ 7:0] uart_data_out2,read_for_host; //UART RX
   150          wire    UART_rx_empty;
   151          
   152          assign phase = data_out0[1];
   153          assign clear_uart_data_regs = data_out0[0];
   154          assign comp = {data_out2,data_out1};
   155          
   156          always @(address)
   157                case (address)
   158                                         16'h00: readdata <= uart_d0;
   159                                         16'h04: readdata <= uart_d1;
   160                                         16'h08: readdata <= uart_d2;
   161                                         16'h0c: readdata <= uart_d3;
   162                            16'h10: readdata <= uart_d4;
   163                                         16'h14: readdata <= uart_d5;
   164                                         16'h18: readdata <= uart_d6;
   165                                         16'h1c: readdata <= uart_d7;
   166                            16'h20: readdata <= uart_d8;
   167                                         16'h24: readdata <= uart_d9;
   168                                         16'h28: readdata <= uart_d10;
   169                                         16'h2c: readdata <= uart_d11;
   170                                         16'h30: readdata <= uart_d12;
   171                                         16'h34: readdata <= uart_d13;
   172                                         16'h38: readdata <= uart_d14;
   173                                         16'h3c: readdata <= uart_d15;
   174                                         16'h40: readdata <= uart_d16;
   175                                         16'h44: readdata <= uart_d17;
   176                                         16'h48: readdata <= uart_d18;
   177                                         16'h4c: readdata <= uart_d19;
   178                            16'h50: readdata <= uart_d20;
   179                                         16'h54: readdata <= uart_d21;
   180                                         16'h58: readdata <= uart_d22;
   181                                         16'h5c: readdata <= uart_d23;
   182                            16'h60: readdata <= uart_d24;
   183                                         16'h64: readdata <= uart_d25;
   184                                         16'h68: readdata <= uart_d26;
   185                                         16'h6c: readdata <= uart_d27;
   186                                         16'h70: readdata <= uart_d28;
   187                                         16'h74: readdata <= uart_d29;
   188                                         16'h78: readdata <= uart_d30;
   189                                         16'h7c: readdata <= uart_d31;
   190                            16'h80: readdata <= uart_d32;
   191                                         16'h84: readdata <= uart_d33;
   192                                         16'h88: readdata <= uart_d34;
   193                                         16'h8c: readdata <= uart_d35;
   194                                         16'h90: readdata <= uart_d36;
   195                                         16'h94: readdata <= uart_d37;
   196                                         16'h98: readdata <= uart_d38;
   197                                         16'h9c: readdata <= uart_d39;
   198                                         16'ha0: readdata <= uart_d40;
   199                                         16'ha4: readdata <= uart_d41;
   200                                         16'ha8: readdata <= uart_d42;
   201                                         16'hac: readdata <= uart_d43;
   202                                         16'hb0: readdata <= uart_d44;
   203                                         16'hb4: readdata <= uart_d45;
   204                                         16'hb8: readdata <= uart_d46;
   205                                         16'hbc: readdata <= uart_d47;
   206                                         16'hc0: readdata <= uart_d48;
   207                                         16'hc4: readdata <= uart_d49;
   208                                         16'hc8: readdata <= uart_d50;
   209                                         16'hcc: readdata <= uart_d51;
   210                                         16'hd0: readdata <= uart_d52;
   211                                         16'hd4: readdata <= uart_d53;
   212                                         16'hd8: readdata <= uart_d54;
   213                                         16'hdc: readdata <= uart_d55;
   214                                         16'he0: readdata <= uart_d55;
   215                                         16'he4: readdata <= uart_d56;
   216                                         16'he8: readdata <= uart_d57;
   217                                         16'hec: readdata <= uart_d58;
   218                                         16'hf0: readdata <= uart_d59;
   219                                         16'hf4: readdata <= uart_d60;
   220                                         16'hf8: readdata <= uart_d61;
   221                                         16'hfc: readdata <= uart_d62;
   222                                         16'h100: readdata <= uart_d63;
   223                                         16'h104: readdata <= uart_d64;
   224                                         16'h108: readdata <= uart_d65;
   225                                         16'h10c: readdata <= uart_d66;
   226                                         16'h110: readdata <= uart_d67;
   227                                         16'h114: readdata <= uart_d68;
   228                                         16'h118: readdata <= uart_d69;
   229                                         16'h11c: readdata <= uart_d70;
   230                                         16'h120: readdata <= uart_d71;
   231                                         16'h124: readdata <= uart_d72;
   232                                         16'h128: readdata <= uart_d73;
   233                                         16'h12c: readdata <= uart_d74;
   234                                         16'h130: readdata <= uart_d75;
   235                                         16'h134: readdata <= uart_d76;
   236                                         16'h138: readdata <= uart_d77;
   237                                         16'h13c: readdata <= uart_d78;
   238                                         16'h140: readdata <= uart_d79;
   239                                         16'h144: readdata <= uart_d80;
   240                                         16'h148: readdata <= uart_d81;
   241                                         16'h14c: readdata <= uart_d82;
   242                                         16'h150: readdata <= uart_d83;
   243                                         16'h154: readdata <= uart_d84;
   244                                         16'h158: readdata <= uart_d85;
   245                                         16'h15c: readdata <= uart_d86;
   246                                         16'h160: readdata <= uart_d87;
   247                                         16'h164: readdata <= uart_d88;
   248                                         16'h168: readdata <= uart_d89;
   249                                         16'h16c: readdata <= uart_d90;
   250                                         16'h170: readdata <= uart_d91;
   251                                         16'h174: readdata <= uart_d92;
   252                                         16'h178: readdata <= uart_d93;
   253                                         16'h17c: readdata <= uart_d94;
   254                                         16'h180: readdata <= uart_d95;
   255                                         16'h184: readdata <= uart_d96;
   256                                         16'h188: readdata <= uart_d97;
   257                                         16'h18c: readdata <= uart_d98;
   258                                         16'h190: readdata <= uart_d99;
   259                                         16'h194: readdata <= uart_wraddress;
   260                              
   261                default: readdata <= read_for_host;
   262             endcase
   263            
   264          //s1, which is an e_avalon_slave
   265          always @(posedge clk or negedge reset_n)
   266             begin
   267               if (reset_n == 0)
   268                   data_out0 <= 0;
   269               else if (chipselect && ~write_n && (address == 0))
   270                   data_out0 <= writedata[7 : 0];
   271             end
   272        
   273          //speed_low_byte register
   274          always @(posedge clk or negedge reset_n)
   275             begin
   276               if (reset_n == 0)
   277                   data_out1 <= 0;
   278               else if (chipselect && ~write_n && (address == 4))
   279                   data_out1 <= writedata[7 : 0];
   280             end
   281            
   282            //speed_high_byte register
   283          always @(posedge clk or negedge reset_n)
   284             begin
   285               if (reset_n == 0)
   286                   data_out2 <= 0;
   287               else if (chipselect && ~write_n && (address == 8))
   288                   data_out2 <= writedata[7 : 0];
   289             end
   290            
   291          always @(posedge clk or negedge reset_n)
   292             begin
   293                if (!reset_n)
   294                  begin
   295                     f <= 0;
   296                     hz_counter <= 0;
   297                 
   298                  end
   299                  else if (hz_counter>=(25)) //107// 100000000 / 868 = 115200, 100MHz -->115200Hz
   300                    begin          
   301                      f <= ~f;                 
   302                      hz_counter <= 0;
   303                    end
   304                  else
   305                    begin
   306                      f <= f;
   307                      hz_counter <= hz_counter + 1;
   308                    end 
   309             end
   310                    uart uu1(
   311        
   312                      .rxclk(f),
   313                      .uld_rx_data(1'b1),
   314                      .rx_data(uart_data_out2),
   315                      .rx_enable(1'b1),
   316                      .rx_in(uart_in),
   317                      .rx_empty(UART_rx_empty)
   318                   );
   319        
   320         always @(posedge f)
   321         begin 
   322                if(clear_uart_data_regs)
   323                       begin
   324                            uart_d0<=0;
   325                            uart_d1<=0;
   326                                                       uart_d2<=0;
   327                                                       uart_d3<=0;
   328                                                       uart_d4<=0;
   329                                                       uart_d5<=0;
   330                                                       uart_d6<=0;
   331                                                       uart_d7<=0;
   332                                                       uart_d8<=0;
   333                                                       uart_d9<=0;
   334                                                       uart_d10<=0;
   335                                                       uart_d11<=0;
   336                                                       uart_d12<=0;
   337                                                       uart_d13<=0;
   338                                                       uart_d14<=0;
   339                                                       uart_d15<=0;
   340                                                       uart_d16<=0;
   341                                                       uart_d17<=0;
   342                                                       uart_d18<=0;
   343                                                       uart_d19<=0;
   344                                                      uart_d20<=0;
   345                                    uart_d21<=0;
   346                                                       uart_d22<=0;
   347                                                       uart_d23<=0;
   348                                                       uart_d24<=0;
   349                                                       uart_d25<=0;
   350                                                       uart_d26<=0;
   351                                                       uart_d27<=0;
   352                                                       uart_d28<=0;
   353                                                       uart_d29<=0;
   354                                                       uart_d30<=0;
   355                                                       uart_d31<=0;
   356                                                       uart_d32<=0;
   357                                                       uart_d33<=0;
   358                                                       uart_d34<=0;
   359                                                       uart_d35<=0;
   360                                                       uart_d36<=0;
   361                                                       uart_d37<=0;
   362                                                       uart_d38<=0;
   363                                                       uart_d39<=0;
   364                                                       uart_d40<=0;
   365                                    uart_d50<=0;
   366                                    uart_d51<=0;
   367                                                       uart_d52<=0;
   368                                                       uart_d53<=0;
   369                                                       uart_d54<=0;
   370                                                       uart_d55<=0;
   371                                                       uart_d56<=0;
   372                                                       uart_d57<=0;
   373                                                       uart_d58<=0;
   374                                                       uart_d59<=0;
   375                                                       uart_d60<=0;
   376                                                       uart_d61<=0;
   377                                                       uart_d62<=0;
   378                                                       uart_d63<=0;
   379                                                       uart_d64<=0;
   380                                                       uart_d65<=0;
   381                                                       uart_d66<=0;
   382                                                       uart_d67<=0;
   383                                                       uart_d68<=0;
   384                                                       uart_d69<=0;
   385                                                       uart_d70<=0;
   386                                    uart_d71<=0;
   387                                                       uart_d72<=0;
   388                                                       uart_d73<=0;
   389                                                       uart_d74<=0;
   390                                                       uart_d75<=0;
   391                                                       uart_d76<=0;
   392                                                       uart_d77<=0;
   393                                                       uart_d78<=0;
   394                                                       uart_d79<=0;
   395                                                       uart_d80<=0;
   396                                                       uart_d81<=0;
   397                                                       uart_d82<=0;
   398                                                       uart_d83<=0;
   399                                                       uart_d84<=0;
   400                                                       uart_d85<=0;
   401                                                       uart_d86<=0;
   402                                                       uart_d87<=0;
   403                                                       uart_d88<=0;
   404                                                       uart_d89<=0;
   405                                                       uart_d90<=0;
   406                                    uart_d91<=0;
   407                                                       uart_d92<=0;
   408                                                       uart_d93<=0;
   409                                                       uart_d94<=0;
   410                                                       uart_d95<=0;
   411                                                       uart_d96<=0;
   412                                                       uart_d97<=0;
   413                                                       uart_d98<=0;
   414                                                       uart_d99<=0;                                       
   415                      
   416                       end
   417                else if(!UART_rx_empty)
   418                       begin
   419                                    uart_d0<=uart_data_out2;
   420                                    uart_d1<=uart_d0;
   421                                                       uart_d2<=uart_d1;
   422                                                       uart_d3<=uart_d2;
   423                                                       uart_d4<=uart_d3;
   424                                                       uart_d5<=uart_d4;
   425                                                       uart_d6<=uart_d5;
   426                                                       uart_d7<=uart_d6;
   427                                                       uart_d8<=uart_d7;
   428                                                       uart_d9<=uart_d8;
   429                                                       uart_d10<=uart_d9;
   430                                                       uart_d11<=uart_d10;
   431                                                       uart_d12<=uart_d11;
   432                                                       uart_d13<=uart_d12;
   433                                                       uart_d14<=uart_d13;
   434                                                       uart_d15<=uart_d14;
   435                                                       uart_d16<=uart_d15;
   436                                                       uart_d17<=uart_d16;
   437                                                       uart_d18<=uart_d17;
   438                                                       uart_d19<=uart_d18;
   439                                                       uart_d20<=uart_d19;
   440                                                       uart_d21<=uart_d20;
   441                                                       uart_d22<=uart_d21;
   442                                                       uart_d23<=uart_d22;
   443                                                       uart_d24<=uart_d23;
   444                                                       uart_d25<=uart_d24;
   445                                                       uart_d26<=uart_d25;
   446                                                       uart_d27<=uart_d26;
   447                                                       uart_d28<=uart_d27;
   448                                                       uart_d29<=uart_d28;
   449                                                       uart_d30<=uart_d29;
   450                                                       uart_d31<=uart_d30;
   451                                                       uart_d32<=uart_d31;
   452                                                       uart_d33<=uart_d32;
   453                                                       uart_d34<=uart_d33;
   454                                                       uart_d35<=uart_d34;
   455                                                       uart_d36<=uart_d35;
   456                                                       uart_d37<=uart_d36;
   457                                                       uart_d38<=uart_d37;
   458                                                       uart_d39<=uart_d38;
   459                                                       uart_d40<=uart_d39;   
   460                                                       uart_d41<=uart_d40;
   461                                                       uart_d42<=uart_d41;
   462                                                       uart_d43<=uart_d42;
   463                                                       uart_d44<=uart_d43;
   464                                                       uart_d45<=uart_d44;
   465                                                       uart_d46<=uart_d45;
   466                                                       uart_d47<=uart_d46;
   467                                                       uart_d48<=uart_d47;
   468                                                       uart_d49<=uart_d48;
   469                                                       uart_d50<=uart_d49;
   470                                                       uart_d51<=uart_d50;
   471                                                       uart_d52<=uart_d51;
   472                                                       uart_d53<=uart_d52;
   473                                                       uart_d54<=uart_d53;
   474                                                       uart_d55<=uart_d54;
   475                                                       uart_d56<=uart_d55;
   476                                                       uart_d57<=uart_d56;
   477                                                       uart_d58<=uart_d57;
   478                                                       uart_d59<=uart_d58;
   479                                                       uart_d60<=uart_d59;
   480                                                       uart_d61<=uart_d60;
   481                                                       uart_d62<=uart_d61;
   482                                                       uart_d63<=uart_d62;
   483                                                       uart_d64<=uart_d63;
   484                                                       uart_d65<=uart_d64;
   485                                                       uart_d66<=uart_d65;
   486                                                       uart_d67<=uart_d66;
   487                                                       uart_d68<=uart_d67;
   488                                                       uart_d69<=uart_d68;
   489                                                       uart_d70<=uart_d69;
   490                                                       uart_d71<=uart_d70;
   491                                                       uart_d72<=uart_d71;
   492                                                       uart_d73<=uart_d72;
   493                                                       uart_d74<=uart_d73;
   494                                                       uart_d75<=uart_d74;
   495                                                       uart_d76<=uart_d75;
   496                                                       uart_d77<=uart_d76;
   497                                                       uart_d78<=uart_d77;
   498                                                       uart_d79<=uart_d78;
   499                                                       uart_d80<=uart_d79;
   500                                    uart_d81<=uart_d80;
   501                                                       uart_d82<=uart_d81;
   502                                                       uart_d83<=uart_d82;
   503                                                       uart_d84<=uart_d83;
   504                                                       uart_d85<=uart_d84;
   505                                                       uart_d86<=uart_d85;
   506                                                       uart_d87<=uart_d86;
   507                                                       uart_d88<=uart_d87;
   508                                                       uart_d89<=uart_d88;
   509                                                       uart_d90<=uart_d89;
   510                                                       uart_d91<=uart_d90;
   511                                                       uart_d92<=uart_d91;
   512                                                       uart_d93<=uart_d92;
   513                                                       uart_d94<=uart_d93;
   514                                                       uart_d95<=uart_d94;
   515                                                       uart_d96<=uart_d95;
   516                                                       uart_d97<=uart_d96;
   517                                                       uart_d98<=uart_d97;
   518                                                       uart_d99<=uart_d98;
   519                               end
   520                                                      
   521         end                                       
   522        
   523         always @(posedge f)
   524         begin 
   525            if (!reset_n || clear_uart_data_regs)
   526                uart_wraddress <=0;         
   527            else if (!UART_rx_empty)
   528                uart_wraddress <= uart_wraddress + 1;         
   529            else
   530                       uart_wraddress <= uart_wraddress;
   531        
   532         end
   533        
   534         endmodule
   535         //-----------------------------------------------------
   536         // Design Name : uart
   537         // File Name   : uart.v
   538         // Function    : Simple UART
   539         // Coder       : Deepak Kumar Tala
   540         //-----------------------------------------------------
   541         module uart (
   542         reset          ,
   543         txclk          ,
   544         ld_tx_data     ,
   545         tx_data        ,
   546         tx_enable      ,
   547         tx_out         ,
   548         tx_empty       ,
   549         rxclk          ,
   550         uld_rx_data    ,
   551         rx_data        ,
   552         rx_enable      ,
   553         rx_in          ,
   554         rx_empty
   555         );
   556         // Port declarations
   557         input        reset          ;
   558         input        txclk          ;
   559         input        ld_tx_data     ;
   560         input [7:0] tx_data        ;
   561         input        tx_enable      ;
   562         output       tx_out         ;
   563         output       tx_empty       ;
   564         input        rxclk          ;
   565         input        uld_rx_data    ;
   566         output [7:0] rx_data        ;
   567         input        rx_enable      ;
   568         input        rx_in          ;
   569         output       rx_empty       ;
   570        
   571         // Internal Variables
   572         reg [7:0]    tx_reg         ;
   573         reg          tx_empty       ;
   574         reg          tx_over_run    ;
   575         reg [3:0]    tx_cnt         ;
   576         reg          tx_out         ;
   577         reg [7:0]    rx_reg         ;
   578         reg [7:0]    rx_data        ;
   579         reg [3:0]    rx_sample_cnt ;
   580         reg [3:0]    rx_cnt         ;
   581         reg          rx_frame_err   ;
   582         reg          rx_over_run    ;
   583         reg          rx_empty       ;
   584         reg          rx_d1          ;
   585         reg          rx_d2          ;
   586         reg          rx_busy        ;
   587        
   588         // UART RX Logic
   589         always @ (posedge rxclk or posedge reset)
   590         if (reset) begin
   591         rx_reg        <= 0;
   592         rx_data       <= 0;
   593         rx_sample_cnt <= 0;
   594         rx_cnt        <= 0;
   595         rx_frame_err <= 0;
   596         rx_over_run   <= 0;
   597         //rx_empty      <= 1;
   598         rx_d1         <= 1;
   599         rx_d2         <= 1;
   600         rx_busy       <= 0;
   601         end else begin
   602         // Synchronize the asynch signal
   603         rx_d1 <= rx_in;
   604         rx_d2 <= rx_d1;
   605         // Uload the rx data
   606         if (uld_rx_data) begin
   607             rx_data <= rx_reg;
   608             //rx_empty <= 1;
   609         end
   610         // Receive data only when rx is enabled
   611         if (rx_enable) begin
   612             // Check if just received start of frame
   613             if (!rx_busy && !rx_d2) begin
   614               rx_busy       <= 1;
   615               rx_sample_cnt <= 1;
   616               rx_cnt        <= 0;
   617             end
   618             // Start of frame detected, Proceed with rest of data
   619             if (rx_busy) begin
   620                rx_sample_cnt <= rx_sample_cnt + 1;
   621                // Logic to sample at middle of data
   622                if (rx_sample_cnt == 7) begin
   623                   if ((rx_d2 == 1) && (rx_cnt == 0)) begin
   624                     rx_busy <= 0;
   625                   end else begin
   626                     rx_cnt <= rx_cnt + 1;
   627                     // Start storing the rx data
   628                     if (rx_cnt > 0 && rx_cnt < 9) begin
   629                       rx_reg[rx_cnt - 1] <= rx_d2;
   630                     end
   631                     if (rx_cnt == 9) begin
   632                        rx_busy <= 0;
   633                        // Check if End of frame received correctly
   634                                           
   635                        if (rx_d2 == 0) begin
   636                          rx_frame_err <= 1;
   637                        end else begin
   638                         // rx_empty     <= 0;
   639                          rx_frame_err <= 0;
   640                          // Check if last rx data was not unloaded,
   641                          rx_over_run <= (rx_empty) ? 0 : 1;
   642                        end
   643                                           
   644                     end
   645                                        
   646                   end
   647                end
   648             end
   649         end
   650         if (!rx_enable) begin
   651             rx_busy <= 0;
   652         end
   653         end
   654        
   655         always @ (posedge rxclk or posedge reset)
   656         begin
   657         if(reset)
   658                    rx_empty     <= 1;         
   659         else if (rx_cnt == 10 && !rx_d2)
   660             rx_empty     <= 0;
   661         else
   662             rx_empty     <= 1; 
   663         end
   664        
   665        
   666         // UART TX Logic
   667         always @ (posedge txclk or posedge reset)
   668         if (reset) begin
   669         tx_reg        <= 0;
   670         tx_empty      <= 1;
   671         tx_over_run   <= 0;
   672         tx_out        <= 1;
   673         tx_cnt        <= 0;
   674         end else begin
   675            if (ld_tx_data) begin
   676               if (!tx_empty) begin
   677                 tx_over_run <= 0;
   678               end else begin
   679                 tx_reg   <= tx_data;
   680                 tx_empty <= 0;
   681               end
   682            end
   683            if (tx_enable && !tx_empty) begin
   684              tx_cnt <= tx_cnt + 1;
   685              if (tx_cnt == 0) begin
   686                tx_out <= 0;
   687              end
   688              if (tx_cnt > 0 && tx_cnt < 9) begin
   689                 tx_out <= tx_reg[tx_cnt -1];
   690              end
   691              if (tx_cnt == 9) begin
   692                tx_out <= 1;
   693                tx_cnt <= 0;
   694                tx_empty <= 1;
   695              end
   696            end
   697            if (!tx_enable) begin
   698              tx_cnt <= 0;
   699            end
   700         end
   701        
   702         endmodule

5.9 無線電信網路IPVerilog Code

在圖5.9的第419~518行,將100Registers串起來,即可變成一組Shift Register,而這100Registers,亦可設計為提供Nios2 CPU來讀取的介面。由於每個Register皆有自己獨立的偏移位址(如圖5.9158 259),且彼此之間的位址皆相差4。此外,本作品亦寫一個Avalon Slave IP,提供被Nios2 CPU讀取資料的介面,如圖5.96~13行與156~262行的Always Block 裏,以判別Address的值來決定哪一組Registers通過Readdata訊號,而readdata訊號可連接Avalon Bus,供Nios2 CPU讀取的訊號最後將設計好的無線電信網路IP,掛載到Avalon Bus上,以供Nios2 CPU可讀到這100Registers,以比對來電顯示電話號碼是否為系統管理者打來的電話,或為系統管理者所發出來的簡訊內容。

UART RX的電路設計解UART Protocol 的設計部份,在圖5.9541~702行,其內部宣告一個暫存器名稱為rx_cnt,而在圖5.9的第580行,此暫存器會從0數到9,總共10個位元,其中UART的起始位元與終止位元各佔用2個位元,而資料位元為8個位元,因此狀態機電路將會依照rx_cnt,便可知道資料是否接收完成。當rx_cnt數到1~8時代表為資料位元,就存入圖5.9的第629rx_reg暫存器中;當數到9時,代表目前的資料為終止位元,在圖5.9的第606~607行,可將rx_reg暫存器的內容輸出到rx_data之輸出埠上,最後在圖5.9的第658~659行,依照rx_cnt數到10,將會產生一個負脈波rx_empty<= 0,它為一個寫入訊號,告知圖5.9的第317417行,將收到的這筆UART資料入到由100Registers所組成的Shift Register中。

在圖5.9291~309行,可產生與GSM Modem溝通所用的Baud Rate 115200bps所需的Clock,並由Avalon系統滙流排的100Mhz 除頻下來,再透過一個hz_counter計數器,當它數到25時,就會將f做反相,以製造出115,200Hz的方波,並且將hz_counter歸零,而這個方波用f來表示,接著將Instance uart模組稱為uu1(5.9的第310),且給它的工作Clockf(5.9的第312),因此當uart接收電路uu1,才能正常的接收GSM Modem所發出的資料。

5.1.5 GSM Modem 溝通之AT CommanduClinux C程式開發

有關GSM Modem協定處理方面,通常需針對AT Command 做介紹,由於GSM Modem是透過UART傳送AT Command,以控制GSM Modem所傳送之數據。本作品使用DE2 平台,並連接自己設計的無線感測模組轉接電路板的UART介面上連接GSM Modem。由於Nios II CPUuClinux中執行的DE2_Multi_Sensor應用程式,通常透過UART介面,向GSM Modem發送AT 指令集與緊急回報之簡訊內容。通常收發短簡訊,可分為Block模式、Text模式與PDU 模式等3種模式,而本作品採用為Text模式。

利用AT Command所發出簡訊,可分為以下4個步驟:

步驟1.          ATCMGF1

步驟2.          ATCMGS"0919179306"

步驟3.          This is Sort Message Content FOR WSN TEST

步驟4.          ^z

其中步驟1是用來設定要發簡訊為Text模式,而步驟2可用來指定接收簡訊的手機號碼,步驟3則用來輸入簡訊內容,步驟4輸入^z字元,代表簡訊內容輸入完畢。在步驟123的結束時,需記得加入0x0d字元。

接著介紹撰寫發送緊急短簡訊的uClinux下的C程式碼,如圖5.10所示。首先執行fpfopen("/phone.out","r"),將電話號碼的內容取出,而該檔案phone.outCGI程式,根據網頁設定緊急電話號碼時所產生,因此這一系列都有相互關聯性。若將phone.out的電話號碼取出並放到msg12[]陣列中,而簡訊內容存放在msg13[]msg14[]陣列中,再透過for(i0;istrlen(msg1);i++)迴圈來初始化GSM Modem,以設定要發簡訊的格式Text模式,最後透過for(i0;istrlen(msg12);i++)迴圈來設定接收簡訊的手機號碼。當送出ATCMGS"手機號碼"GSM Modem時,可透過for(i0;istrlen( (key == 0) ? msg13 : msg14 );i++)迴圈來發送簡訊的內容。若key變數為0時,將發送簡訊內容為"Remote WSN Temperature node is higher than 40 degree !!!!",否則將發送簡訊內容為"Remote WSN Temperature node is lower than 40 degree !!!!"

#include<stdio.h>
#include <string.h>
#include <unistd.h>
#define UART_IO * (unsigned int *) (0x80681000 + 4)
int main_sms(int key)
{
     char msg1[]="AT+CMGF=1";
     char msg12[]="AT+CMGS=\"0930217028\"";
     char msg13[]="Remote WSN Temperature node is higher than 40 degree !!!!";
     char msg14[]="Remote WSN Temperature node is lower than 40 degree !!!!";
 
     int i;
     char mystr [20];
     FILE *fp;
 
       fp = fopen("/phone.out","r");
       if(fp!=NULL){
            fgets(mystr,20,fp);
            printf("phone.out = %s\n\r",mystr);
 
            for(i=0;i<strlen(mystr);i++)
                msg12[9+i] = mystr[i];
 
            printf("msg12 = %s\n\r",msg12);
            fclose(fp);
          }
       else
            printf("can not found phone.out\n");
             
     for(i=0;i<strlen(msg1);i++)
     {              
               UART_IO = msg1[i];
               usleep(1000);              
     }                        
     UART_IO = 0x0d;  
     usleep(1000);           
     for(i=0;i<strlen(msg12);i++)
     {                   
               UART_IO = msg12[i];
               usleep(1000);
     }
     UART_IO = 0x0d;
     usleep(1000); 
     for(i=0;i<strlen( (key == 0) ? msg13 : msg14 );i++)
     {              
               if(key == 0) 
                   UART_IO = msg13[i];
               else
                   UART_IO = msg14[i];
               usleep(1000);
     }
     UART_IO = 0x1a; //ctrl-z
     usleep(1000);
     return 1;
}

5.10 uClinux下發送短簡訊的AT CommandC Code

5.1.6 三網合一之uClinux C應用程式開發

本作品所開發的DE2_Multi_Sensor 應用程式,將執行在uClinux作業系統中的User Space層,主要可主動切換頻率來接收各種無線感測節點(溫度、濕度、瓦斯、地震與照度)回傳的感測值與控制GSM Modem。當感測器偵測到緊急狀況發生時,可用短簡訊通報等2個主要功能。針對DE2_Multi_Sensor 應用程式之第1個功能,所對應的硬體為使用SOPC_Builder提供的PIO元件,實現SPI(Serial Peripheral Interface)介面控制所需要的4I/O,分別為MOSIMISOSCKCSN訊號。這將讓CC1100無線模組,透過自行製作的無線感測模組轉接電路板與DE2平台的FPGA連接,並接收無線感測節點回傳的感測值,包含溫度、濕度、震度與瓦斯等感測值,並針對每一種感測項目輸出到獨立的檔案,再讓檔案內記錄目前溫度(度數)、濕度(Percent)、是否有感測到地震或瓦斯等訊息,而這些檔案通常可提供Web ServerCGI程式讀取之用,並顯示在監督者的Web Browser上,以供監督者在網頁上能即時監看的重要功能。

針對DE2_Multi_Sensor 應用程式第2個功能,即讀取CGI程式所建立的Phone.out檔案,在這檔案有鍵入監督者的手機號碼,而該手機號碼可由監督者提供,並透過網頁設定管理者的手機號碼產生出來。當有緊急狀況發生時,例如溫度超過40度,便會發出簡訊給監督者所提供之手機,當溫度降回至40度後,將會再發一封簡訊給監督者所提供之手機,以告知緊急狀況解除。發送簡訊之時機,在超過臨界點以上或以下之瞬間,程式設有判斷機制,僅會發出一封簡訊,而不會在溫度超過40度連續發送簡訊,導致過度浪費通訊費用之情況。而DE2_Multi_Sensor 應用程式,必須能控制UART介面與外部的GSM Modem溝通,且使用AT CommandGSM Modem來送出簡訊,並讓監控平台與電信(Telecom)網路連接,如圖5.11所示

 

5.11 DE2_Multi_Sensor程式實現3網連接

本作品之DE2_Multi_Sensor C程式架構,如圖5.12所示。觀察圖5.12可知,遠端感測器與DE2之無線監控平台溝通的方式,可透過呼叫main_cc1100()來達成;而DE2WSN監控平台與GSM Modem溝通的方式,可透過呼叫main_sms()來達成,而中間的while (1) 為無窮迴圈,可實現WSN 感測節點的封包接收,包含溫度、光度、震度與瓦斯,並可作頻率的切換。當感測節點偵測到異常情形發生時,就發送短簡訊給系統管理員,透過呼叫圖5.12中的main_sms()函式,以判讀是否有打電話與簡訊進來。當程式呼叫change_channel_by_sw (4)//setting channel 0x0a,可將Channel 設定在0x0a,以用來偵測瓦斯感測器的感測情形。

#include <stdio.h>
#include <unistd.h>
#include "rf_cc1100.h"
#include "lcd.h"
 
int main_gsm(int kky);
int main_sms(int key);
int main()
{
 unsigned char k,j=3,sms_sended;
 unsigned char RxBuf[8]={0},lcd_text[16],lcd_no_rx[]="lcd_no_rx       ";
 unsigned char leng,gas_status ;
 unsigned int qw,counter,temperature,switch_key,last_switch_key,a,b,c,d;
 float quantization;
 unsigned short qq;
 int cc,kky;
 FILE *fp,*fp_gas,*fp_Earthquake;
  
 fp=fopen("/jj.out","w+");
 fp_gas=fopen("/gas.out","w+");
 fp_Earthquake=fopen("/Earthquake.out","w+");
 LCD_Test();
    
 CC1100_CSN_BASE = 0;
 usleep(1000); //1ms
 CC1100_CSN_BASE = 1;
 k = main_cc1100(0x10, 0x00,0x03);
 printf("RF CC1100's Current Channel=%x, j=%x\n\r",k,j); 
 printf("We Are demostrating temp sensor RX on NIOS II Platform\n\r");
 RxBuf[0] = halSpiReadReg(CCxxx0_FREQ2);
 printf("CC1100_FREQ2=%x\n\r",RxBuf[0]);
 RxBuf[1] = halSpiReadReg(CCxxx0_FREQ1);
 printf("CC1100_FREQ1=%x\n\r",RxBuf[1]);
 RxBuf[2] = halSpiReadReg(CCxxx0_FREQ0);
 printf("CC1100_FREQ0=%x\n\r",RxBuf[2]);
 printf("Press Any Key to continue ... get remote temp sensor\n\r");
 last_switch_key = SW;
 change_channel_by_sw (last_switch_key);   //setting channel depend on SW
 k = getchar();
 counter=0;
 sms_sended = 0;
 kky=1;
 while(1)
 {
    main_gsm(kky);
    kky=2;
    switch_key = SW;
    if(switch_key != last_switch_key)
     {
       printf("You change SW \n\r");
       change_channel_by_sw (switch_key);    //setting channel depend on SW      
       last_switch_key = switch_key;
     }
    
////////////////////////////////////////////////////////////////////////////////////
    change_channel_by_sw (2);    //setting channel 0x10
    for(qw=0;qw<0x1100;qw++)
    {
    leng = 4; // set 4 bytes to receive
    if(halRfReceivePacket(RxBuf,&leng)) //Receive packet with 4 Bytes
    {
        sprintf(lcd_text,"%x%x.%x Degrees",RxBuf[2],RxBuf[1],RxBuf[0]);
    if(RxBuf[2]!=9)
     { 
          if(RxBuf[2] >=4 && !sms_sended)
             {
                        sms_sended = 1;
                        cc = main_sms(0);
             }
 
          if(RxBuf[2] < 4 && sms_sended)
             {
                        sms_sended = 0;
                        cc = main_sms(1);
             }
 
    temperature = (RxBuf[2] << 12 | RxBuf[1] << 8 | RxBuf[0] << 4 | 0xC );
    c = (a<<16 | temperature);
    SEVEN_SEG_LED = c;
    quantization = RxBuf[2]*10 + RxBuf[1];
    quantization *= 0.16;    //quantizaion = quantizaion * 0.16
    qq = 0;
    for(j=1;j<=(unsigned char)quantization;j++)
    {
              qq <<= 1;
              qq = qq | 0x01;
    }
          RED_LED_BASE = qq >> 8;
          GREEN_LED_BASE = qq & 0xff;
          fprintf(fp,"%x%x.%x",RxBuf[2],RxBuf[1],RxBuf[0]);
          rewind(fp);
     }
          counter=0;
         }
    else
         {
        counter++;
        if(counter>=0x0fff)
             {
              counter=0;
              temperature=0xFFFF;
              c = (a<<16 | temperature);
              SEVEN_SEG_LED = c ;    // SEG_LED   SHOW
              GREEN_LED_BASE = 0x00;         // GREEN_LED SHOW
              RED_LED_BASE = 0x00;           // RED_LED   SHOW
              switch_key = SW;
             }
          }
    }
   ///////////////////////////////////////////////////////////////////
     change_channel_by_sw (4);    //setting channel 0x0a
     for(qw=0;qw<0x1100;qw++)
     {
     leng = 4; // set 4 bytes to receive
     if(halRfReceivePacket(RxBuf,&leng)) //Receive packet with 4 Bytes
         {
    
        if(RxBuf[0] == 0x09)
           sprintf(lcd_text,"NO GAS = %x      ",RxBuf[0]);
        else if(RxBuf[0] == 0x01)
           sprintf(lcd_text,"GAS Detected = %x",RxBuf[0]);
        else
           sprintf(lcd_text,"GAS Unknow   = %x",RxBuf[0]);
          
        if(gas_status != RxBuf[0])
           {
          gas_status = RxBuf[0];
          LCD_Line1();
          // Show Text to LCD
          LCD_Show_Text(lcd_text);  
        
          fprintf(fp_gas,"%x",RxBuf[0]);
          rewind(fp_gas);
         
           }
        counter=0;
     }
    else
         {
        counter++;
        if(counter>=0x0fff)
             {
          
           sprintf(lcd_text,"NO GAS Sensor   ");
           LCD_Line1();
           // Show Text to LCD
           LCD_Show_Text(lcd_text);  
           fprintf(fp_gas,"0");
           rewind(fp_gas);
           counter=0;
           gas_status = 0;
             }
         }
     
    }
/////////////////////////////////////////////////////////////////////////////////// 
    change_channel_by_sw (8);    //setting channel 0x1a
    for(qw=0;qw<0x1100;qw++)
     {
    leng = 4; // set 4 bytes to receive
    if(halRfReceivePacket(RxBuf,&leng)) //Receive packet with 4 Bytes
       {           
         a=RxBuf[0];
         b=(a<<16 | temperature);
         SEVEN_SEG_LED = b;
         fprintf(fp_Earthquake,"%x",RxBuf[0]);
         rewind(fp_Earthquake);
         counter=0;
       }
    else
       {
        counter++;
        if(counter>=0x0fff)
            {
           RxBuf[0]=0x0;
           a=RxBuf[0];        
           d = (a<<16|temperature);
           SEVEN_SEG_LED = d;
           fprintf(fp_Earthquake,"0");
           rewind(fp_Earthquake);
           counter=0;
        }
        }    
     
      }
 }//end of while(1)
 fclose(fp);
 fclose(fp_gas);
 fclose(fp_Earthquake);
 return 0;
}

5.12 DE2_Multi_Sensor程式uClinux C Code

觀察5.12可知,當程式呼叫change_channel_by_sw (8)//setting channel 0x1a,可將channel 設定在0x1a,以用來偵測地震感測器的感測情形。當程式呼叫change_channel_by_sw (2)//setting channel 0x10,可將channel 設定在0x10,以用來偵測溫度感測器的感測情形。當溫度大於40度時,就會呼叫sms_main(0)發送溫度高於40度的簡訊給系統管理員,並將sms_sended = 1,讓程式不會一直重複發送簡訊;等溫度降回低於40度時,再呼叫sms_main(1)發送溫度低於40度的簡訊給系統管理員,並將sms_sended = 0,讓程式不會一直重複發送簡訊,即可實現溫度大於40度時,僅發1次簡訊,當低於40度時再發1次簡訊之功能。

5.1.7 uClinux 網頁伺服器CGI 程式開發

本作品的CGI程式,皆以C語言來撰寫,並透過跨平台編譯器nios2-linux-gcc來進行編譯。自行開發CGI程式,可讓BOA Web Server能與 DE2_Multi_Sensor溝通,且溝通的方式可透過uClinuxFilesystem來達成。由DE2_Multi_Sensor產生Sensor.out文字檔案與手機號CGI程式,以產生Phone.out文字檔案,關於這兩者之間的介面程式,如圖5.13所示。

 

5.13 CGI程式之輸出入介面

本作品開發2CGI程式,接著詳細說明這2CGI程式之功能如下:

(1) 監控回報CGI程式:負責將DE2_Multi_Sensor程式所輸出的溫度、濕度、照度與瓦斯等感測檔案,名稱為Sensor.out,並將檔案內容讀出,再轉換成HTML語言,直接輸出到監督者的網頁上,以讓監督者的網頁,能夠每5秒更新一次感測畫面。

(2) 手機號碼CGI程式:負責將監督者在網頁上所設定的手機號碼,輸出到uClinux 檔案系統中的檔案,名稱為Phone.out,其目的是讓DE2_Multi_Sensor程式將檔案內的手機號碼讀出,並轉成AT Command的撥號碼,當WSN端有偵測到緊急狀況時,就可將AT Command 送往GSM Modem,作為緊急短簡訊送出給監督者。

本作品之監控回報CGI的程式,如圖5.14所示。在圖5.14中,可看到呼叫fopen,以開啟sensor.out檔案,然後開啟一個HTML視窗,並透過strtoul(),將字串轉成長整數,並顯示在新的HTML視窗中,以顯示出溫度值或地震與瓦斯的圖示。而一個可產生手機號碼的CGI程式,如圖5.15所示。觀察圖5.15可知,透過fopen()函式開啟phone.out檔,當搭配參數為w+;若檔案已存在,則清除掉之前phone.out檔的內容,並寫入新的手機號碼到phone.out檔;若檔案不存在,則直接建立新的phone.out檔,並寫入新的手機號碼。

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "cgl.h"  
int main (int argc, char **argv)
{
        char *csPtr;
        char mystr [100];
        unsigned long ulTemp=0 ;
        FILE *fp;
  
        fp=fopen("/sensor.out","r");
        if(cgl_init()==-1)
        {
                fprintf(stderr,"cgl_init error\n");
                cgl_freeall();
                exit(1);
        }
        csPtr = cgl_getvalue("userinput"); //獲得使用者輸入值
        fprintf(stderr,"csPtr=%s\n",csPtr);
        ulTemp = strtoul(csPtr,NULL,0); //將字串轉成Unsigned long Integer
        //產生一新網頁,顯示使用者於網頁中輸入的數值
        cgl_html_header();
        cgl_html_begin("");
        if(strcmp(csPtr , "1256") == 0)
        {
           if (fp==NULL)
              printf("/SENSOR.OUT OPEN FAILED");
           else
           {
                fgets(mystr,100,fp);
                //printf("mystr=%s",mystr);
                printf("Please Input Mobile Phone Number");
                printf("<br>");         
           }
           fclose(fp);
           printf("<form method=\"POST\" action=\"/cgi-bin/get_temp.cgi\">");
           printf("<input type=\"text\" name=\"userinput\" value=\"\">");
           printf("<input type=\"submit\">");
           printf("<input type=\"reset\">");
           printf("</form>");
        }
        else
        {
            printf("password failed <br>",csPtr);
                             printf("<a href=\"/pass.htm\">重新輸入密碼</a>",csPtr);
        }
        return 0;
}

5.14 監控回報的CGI程式

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h> //strcmp()
#include "cgl.h"   //下載的cgl include檔案
int main (int argc, char **argv)
{
        char *csPtr;
        char mystr [100];
        unsigned long ulTemp=0 ;
        FILE *fp,*phone_file;
  
 
        if(cgl_init()==-1)
        {
                fprintf(stderr,"cgl_init error\n");
                cgl_freeall();
                exit(1);
        }
        csPtr = cgl_getvalue("userinput"); //獲得使用者輸入值
        fprintf(stderr,"csPtr=%s\n",csPtr);
        if(csPtr!=NULL)
        {
          phone_file=fopen("/phone.out","w+");
          fprintf(phone_file,"%s",csPtr);
          fclose(phone_file);
         }
         cgl_html_header();
 
printf("<html>\n<head>");
printf("\n<meta http-equiv=\"refresh\" content=\"5;url=\"\"/>");
printf("<meta http-equiv=\"Content-Language\" content=\"zh-tw\">");
printf("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\">");
printf("<title>try</title>");
printf("<style>");
printf("<!--");
printf(".ms-simple1-main { border-left-style: none; border-right-style: none; ");
printf("               border-top: 1.5pt solid green; border-bottom: 1.5pt solid green }");
printf(".ms-simple1-tl { border-left-style: none; border-right-style: none; border-top-style: none; ");
printf("               border-bottom: .75pt solid green }");
printf(".ms-simple1-left { border-style: none }");
printf(".ms-simple1-top { border-left-style: none; border-right-style: none; border-top-style: none; ");
printf("               border-bottom: .75pt solid green }");
printf(".ms-simple1-even { border-style: none }");
printf("-->");
printf("</style>");
printf("\n</head>");
fp=fopen("/jj.out","r");
fgets(mystr,100,fp);
if ( mystr[0] > '3' )
printf("<body background=\"/img/hot.jpg\" style=\"background-attachment: fixed\">");
else
{
printf("<body background=\"/img/water.jpg\" style=\"background-attachment: fixed\">");
}
printf("<div align=\"center\">");
if ( mystr[0] > '3' )
{
printf("<img border=\"0\" src=\"/img/welcome_hot.gif\" width=\"236\" height=\"56\">");
 
}
else
{
printf("<img border=\"0\" src=\"/img/welcome_water.gif\" width=\"236\" height=\"56\">");
 
}
printf("          <table border=\"1\" width=\"622\" height=\"187\">");
printf("                    <tr>");
printf("                               <td height=\"2\" width=\"612\"></td>");
printf("                    </tr>");
printf("                    <tr>");
printf("                               <td height=\"156\" width=\"612\"> ");
 
 
if (fp==NULL)
       printf("/JJ.OUT OPEN FAILED");
else
{
 
if ( mystr[0] > '3' )
printf("<b><font color=\"#FF0000\" size=\"6\">\"Temperature=%s Degree\"</font></b>",mystr);
else
{
printf("<b><font color=\"#0000FF\" size=\"6\">\"Temperature=%s Degree\"</font></b>",mystr);
}
                printf("<br>");
 
printf("<p align=\"center\">");
                switch (mystr[0]){
                     case '0' :
                         printf("<img border=\"0\" src=\"/img/number_0.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '1' :
                         printf("<img border=\"0\" src=\"/img/number_1.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '2' :
                         printf("<img border=\"0\" src=\"/img/number_2.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '3' :
                         printf("<img border=\"0\" src=\"/img/number_3.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '4' :
                         printf("<img border=\"0\" src=\"/img/number_4.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '5' :
                         printf("<img border=\"0\" src=\"/img/number_5.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '6' :
                         printf("<img border=\"0\" src=\"/img/number_6.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '7' :
                         printf("<img border=\"0\" src=\"/img/number_7.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '8' :
                         printf("<img border=\"0\" src=\"/img/number_8.gif\" width=\"57\" height=\"56\">");
                         break;
                     case '9' :
                         printf("<img border=\"0\" src=\"/img/number_9.gif\" width=\"57\" height=\"56\">");
                         break;
                     default :
                         printf("<img border=\"0\" src=\"/img/13.gif\" width=\"57\" height=\"56\">");
                         break;
                     }
                switch (mystr[1]){
                     case '0' :
                         printf("<img border=\"0\" src=\"/img/number_0.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>");                              
                         break;
                     case '1' :
                         printf("<img border=\"0\" src=\"/img/number_1.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     case '2' :
                         printf("<img border=\"0\" src=\"/img/number_2.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
 
                     case '3' :
                         printf("<img border=\"0\" src=\"/img/number_3.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     case '4' :
                         printf("<img border=\"0\" src=\"/img/number_4.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     case '5' :
                         printf("<img border=\"0\" src=\"/img/number_5.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     case '6' :
                         printf("<img border=\"0\" src=\"/img/number_6.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     case '7' :
                         printf("<img border=\"0\" src=\"/img/number_7.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     case '8' :
                         printf("<img border=\"0\" src=\"/img/number_8.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     case '9' :
                         printf("<img border=\"0\" src=\"/img/number_9.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     default :
                         printf("<img border=\"0\" src=\"/img/13.gif\" width=\"57\" height=\"56\">");
printf("<b><font color=\"#0000FF\" size=\"6\">.</font></b>"); 
                         break;
                     }
                switch (mystr[3]){
                     case '0' :
                         printf("<img border=\"0\" src=\"/img/number_0.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '1' :
                         printf("<img border=\"0\" src=\"/img/number_1.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '2' :
                         printf("<img border=\"0\" src=\"/img/number_2.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '3' :
                         printf("<img border=\"0\" src=\"/img/number_3.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '4' :
                         printf("<img border=\"0\" src=\"/img/number_4.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '5' :
                         printf("<img border=\"0\" src=\"/img/number_5.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '6' :
                         printf("<img border=\"0\" src=\"/img/number_6.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '7' :
                         printf("<img border=\"0\" src=\"/img/number_7.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '8' :
                         printf("<img border=\"0\" src=\"/img/number_8.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     case '9' :
                         printf("<img border=\"0\" src=\"/img/number_9.gif\" width=\"57\" height=\"56\">");
                     if ( mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     default :
                         printf("<img border=\"0\" src=\"/img/13.gif\" width=\"57\" height=\"56\">");
                     if (  mystr[0] > '3' )
                         printf("<img border=\"0\" src=\"/img/phone_0007.gif\" width=\"30\" height=\"30\">");
                                   else
{
printf("");
}
                         break;
                     }
                         printf("<br>");
}
printf("</td>");
printf("                    </tr>");
printf("                    <tr>");
printf("                               <td height=\"9\" width=\"612\"></td>");
printf("                    </tr>");
printf("          </table>");
printf("</div>");
printf("<p><a target=\"_top\" href=\"/cgi-bin/aa_nopass.cgi\">");
if ( mystr[0] > '3' )
printf("<img border=\"0\" src=\"/img/hot_3.gif\" width=\"860\" height=\"26\"><img border=\"0\" src=\"/img/hot_2.gif\" width=\"100\" height=\"120\"></p>");
else
{
printf("<img border=\"0\" src=\"/img/water_3.gif\" width=\"860\" height=\"26\"><img border=\"0\" src=\"/img/water_2.gif\" width=\"100\" height=\"120\"></p>");
}
cgl_html_begin("");
printf("</body>"); 
printf("</html>"); 
 
fclose(fp);
return 0;
}

5.15 產生手機號碼的CGI程式

5.1.8 Web Server HTML網頁設計

本作品所設計之HTML網頁設計架構,如圖5.16所示。觀察圖5.16可知,在首頁設計部份有密碼驗證功能,如圖5.17,唯有密碼輸入正確密碼,方可進入網頁內的多功能選單畫面。在圖5.18之多功能選單畫面下,可切換到圖5.19之監控溫度、瓦斯、地震、人體紅外線偵測與手機號碼設定之網頁。監控網頁會以每5秒就自動更新網頁內容,即溫度、瓦斯、地震與人體紅外線偵測的監控值,這些感測的數值來自Sensor.out檔案,而Sensor.out的內容來自DE2_Multi_Sensor 應用程式接收到各個無線感測節點的溫度、瓦斯、地震、人體紅外線數值,將它輸出到Sensor.out檔案中。

 

5.16 WSN網頁設計架構圖

 

5.17 首頁輸入監督者密碼


 

5.18 網頁的多功能選單

 

5.19 溫度顯示網頁

手機號碼設定網頁,可用來讓監控員輸入監督者的手機號碼,如圖5.20所示。透過CGI程式接收到手機號後,在uClinux的檔案系統內產生一個Phone.out的檔案,讓檔案內容為手機號碼。當有緊急狀況發生時,DE2_Mulit_Sensor程式會讀取Phone.out檔案內的手機號碼,並結合成AT Command與欲發送的簡訊內容,再發出緊急短簡訊通報給監督者,並將網頁顯示在緊急畫面的頁面,如圖5.21所示。另網頁亦設計了留言版,以供監督者在網站上留言或記錄,如圖5.22所示。

 

5.20 發送緊急短簡訊的手機號碼設定

 

5.21 緊急突發狀況之網頁

 

5.22 留言板網頁

5.1.9 WSN監控平台Audio Codec音效輸出Verilog Code程式

在監控平台近端監控部份,本作品加入音效播放的功能,透過DE2所提供的WM8731 Audio Codec晶片進行音效輸出,而音樂的來源則放在SD Card上。當偵測到溫度過高時(例如無線感測節點的溫度大於40度),就會播放一段音樂(例如:張惠妹火),做為近端的音效提示之應用,類似警報器的作用。

本作品在修改音效播放的Verilog Code之實現方法,可在uClinux作業系統裏面要放48KHz/16bitStereo音樂,必須由Nios2 CPU一直在搬音樂樣本,當從SD Card至播放音樂的ADC FIFO,就佔用接近89成的CPU使用率,而監控平台同時亦須執行作業系統、DM000A乙太網路、網頁伺服器與連接電信網路等事情需同時進行,這將導致播放音樂的品質大打折扣與造成三網合一的監控功能執行速度變慢之問題。

為克服上述之問題,可由降低音樂的解析度來著手,讓它從48KHz/16bitsStereo音樂降為8KHz/16bits,由於整整小了6倍,將會使音樂量減少,使Nios2可空出許多時間來進行WSN的監控工作,因此必須修改底層電路驅動音效晶片的I2S介面的Bit Clock(音樂樣本的位元Colock)與RL Clock(左右聲道Colock),將它們降到適合8KHz播放的速度,而對應在Verilog的地方,就在AUDIO_DAC_FIFO.v。本作品加大計數器的暫存器容量,讓計數器能夠計數到足夠大的累加值來做Bit ClockRL Clock的反相。若不加大計數器的容量,將會導致永遠無法累加至期待的值,導致一直再循環溢位,那就無法製造出適合8KHz Bit ClockRL Clock

在圖5.23程式碼之第35行,進行了BCK_DIV個計數暫存器的容量修改,其中BCK_DIV從原先的[3:0]改為[5:0],因在第61行的BCLK_DIV必須判別REF_CLK/(SAMPLE_RATE×DATA_WIDTH*CHANNEL_NUM×2) 1而這個值換算成SAMPLE_RATE8000時,將為[18432000/(8000×16×2×2)] 1 = 35,因此必須用6位元的計數暫存器,才能容納的下35這個值,且原先的[3:0],僅能表示到0~15,而無法數到35,因此需將BCK_DIV從原先的[3:0]改為[5:0]

在圖5.23程式碼36行,進行LRCK_1X_DIV個計數暫存器的容量修改,其中LRCK_1X_DIV從原先的[8:0]改為[10:0],因第84行的LRCK_1X_DIV主要用來判別REF_CLK/(SAMPLE_RATE×2) 1而這個值換算成SAMPLE_RATE8000時,將為[18432000/(8000×2)] 1 = 1151,因此必須用11位元的計數暫存器,才能容納1151值,且原先的[8:0],僅能表示到0~511,而無法數到1151,因此須將LRCK_1X_DIV從原先的[8:0]改為[10:0]

在圖5.23程式碼之第37行,進行了LRCK_2X_DIV個計數暫存器的容量修改,其中LRCK_2X_DIV從原先的[7:0]改為[9:0],因第92行的LRCK_2X_DIV必須判別REF_CLK/(SAMPLE_RATE×4)1而這個值換算成SAMPLE_RATE8000時,將為[18432000/(8000×4)]1 = 575,因此必須用10位元的計數暫存器,才能容納下575之值,且原先的[7:0],僅能表示到0~255,而無法數到575,因此須將LRCK_2X_DIV從原先的[7:0]改為[9:0]

     1         module AUDIO_DAC_FIFO (       //         FIFO Side
     2                                                                         iDATA,iWR,iWR_CLK,
     3                                                                         oDATA,
     4                                                                         //         Audio Side
     5                                                                         oAUD_BCK,
     6                                                                          oAUD_DATA,
     7                                                                         oAUD_LRCK,
     8                                                                         oAUD_XCK,
     9                                                                         //         Control Signals
    10                                                                   iCLK_18_4,
    11                                                                         iRST_N         );                                       
    12        
    13         parameter      REF_CLK                           =         18432000;    //         18.432          MHz
    14         parameter      SAMPLE_RATE                 =         48000;                     //         48                 KHz
    15         parameter      DATA_WIDTH                   =         16;                           //         16                 Bits
    16         parameter      CHANNEL_NUM              =         2;                             //         Dual Channel
    17        
    18         //         FIFO Side
    19         input   [DATA_WIDTH-1:0]          iDATA;
    20         input                                                        iWR;
    21         input                                                        iWR_CLK;
    22         output [DATA_WIDTH-1:0]          oDATA;
    23         wire    [DATA_WIDTH-1:0]          mDATA;
    24         reg                                                                      mDATA_RD;
    25         //         Audio Side
    26         output oAUD_DATA;
    27         output oAUD_LRCK;
    28         output oAUD_BCK;
    29         output oAUD_XCK;
    30         reg                 oAUD_BCK;
    31         //         Control Signals
    32         input   iCLK_18_4;
    33         input   iRST_N;
    34         //         Internal Registers and Wires
    35         reg                 [5:0]   BCK_DIV;       //[3:0]
    36         reg                 [10:0] LRCK_1X_DIV;   //[8:0]
    37         reg                 [9:0]   LRCK_2X_DIV;   //[7:0]
    38         reg                 [3:0]   SEL_Cont;
    39         ////////////////////////////////////
    40         reg                 [DATA_WIDTH-1:0]          DATA_Out;
    41         reg                 [DATA_WIDTH-1:0]          DATA_Out_Tmp;
    42         reg                                                                      LRCK_1X;
    43         reg                                                                      LRCK_2X;
    44        
    45         FIFO_16_256         u0       (         .data(iDATA),.wrreq(iWR),
    46                                                                         .rdreq(mDATA_RD),.rdclk(iCLK_18_4),
    47                                                                         .wrclk(iWR_CLK),.aclr(~iRST_N),
    48                                                                         .q(mDATA),.wrfull(oDATA[0]));
    49         assign oAUD_XCK =         ~iCLK_18_4;
    50        
    51         ////////////         AUD_BCK Generator         //////////////
    52         always@(posedge iCLK_18_4 or negedge iRST_N)
    53         begin
    54                    if(!iRST_N)
    55                    begin
    56                               BCK_DIV                <=       0;
    57                               oAUD_BCK <=       0;
    58                    end
    59                    else
    60                    begin
    61                               if(BCK_DIV >= REF_CLK/(SAMPLE_RATE*DATA_WIDTH*CHANNEL_NUM*2)-1 )
    62                               begin
    63                                         BCK_DIV                <=       0;
    64                                         oAUD_BCK <=       ~oAUD_BCK;
    65                               end
    66                               else
    67                               BCK_DIV                <=       BCK_DIV+1;
    68                    end
    69         end
    70         //////////////////////////////////////////////////
    71         ////////////         AUD_LRCK Generator       //////////////
    72         always@(posedge iCLK_18_4 or negedge iRST_N)
    73         begin
    74                    if(!iRST_N)
    75                    begin
    76                               LRCK_1X_DIV       <=       0;
    77                               LRCK_2X_DIV       <=       0;
    78                               LRCK_1X                <=       0;
    79                               LRCK_2X                <=       0;
    80                    end
    81                    else
    82                    begin
    83                               //         LRCK 1X
    84                               if(LRCK_1X_DIV >= REF_CLK/(SAMPLE_RATE*2)-1 )
    85                               begin
    86                                         LRCK_1X_DIV       <=       0;
    87                                         LRCK_1X     <=       ~LRCK_1X;
    88                               end
    89                               else
    90                               LRCK_1X_DIV                  <=       LRCK_1X_DIV+1;
    91                               //         LRCK 2X
    92                               if(LRCK_2X_DIV >= REF_CLK/(SAMPLE_RATE*4)-1 )
    93                               begin
    94                                         LRCK_2X_DIV       <=       0;
    95                                         LRCK_2X     <=       ~LRCK_2X;
    96                               end
    97                               else
    98                               LRCK_2X_DIV                  <=       LRCK_2X_DIV+1;            
    99                    end
   100         end
   101         assign oAUD_LRCK          =         LRCK_1X;
   102         //////////////////////////////////////////////////
   103         ////////// Read Signal Generator        //////////////
   104         always@(posedge iCLK_18_4 or negedge iRST_N)
   105         begin
   106                    if(!iRST_N)
   107                    begin
   108                               mDATA_RD <=       0;
   109                    end
   110                    else
   111                    begin
   112                               if(LRCK_1X_DIV == REF_CLK/(SAMPLE_RATE*2)-1 )
   113                               mDATA_RD <=       1;
   114                               else
   115                               mDATA_RD <=       0;
   116                    end
   117         end
   118         //////////////////////////////////////////////////
   119         //////////////        DATA Latch         //////////////////
   120         always@(posedge iCLK_18_4 or negedge iRST_N)
   121         begin
   122                    if(!iRST_N)
   123                    DATA_Out_Tmp     <=       0;
   124                    else
   125                    begin
   126                               if(LRCK_2X_DIV == REF_CLK/(SAMPLE_RATE*4)-1 )
   127                               DATA_Out_Tmp     <=       mDATA;
   128                    end
   129         end
   130         always@(posedge iCLK_18_4 or negedge iRST_N)
   131         begin
   132                    if(!iRST_N)
   133                    DATA_Out    <=       0;
   134                    else
   135                    begin
   136                               if(LRCK_2X_DIV == REF_CLK/(SAMPLE_RATE*4)-3 )
   137                               DATA_Out    <=       DATA_Out_Tmp;
   138                    end
   139         end
   140         //////////////////////////////////////////////////
   141         ////////// 16 Bits PISO MSB First      //////////////
   142         always@(negedge oAUD_BCK or negedge iRST_N)
   143         begin
   144                    if(!iRST_N)
   145                    SEL_Cont     <=       0;
   146                    else
   147                    SEL_Cont     <=       SEL_Cont+1;
   148         end
   149         assign oAUD_DATA          =         DATA_Out[~SEL_Cont];
   150         //////////////////////////////////////////////////
   151        
   152         endmodule

5.23 AUDIO_DAC_FIFO Verilog程式修改

        另外,驅動音效晶片WM8731的暫存器亦須跟著修改,即須修改在I2C介面驅動的Verilog code,將原先設定的48KHz D/A的頻率,改配置成8KHz的速度,因此研究WM8731的暫存器的Spec,以說明修改暫存器內容,將原先對應48KHz的暫存器對應製圖5.24的紅色框框處,置換成藍色框框的值,亦即設定SR3SR20BOSRSR1SR01,而這些值則對應到圖5.25的綠色框框處之R8暫存器內容,必須設定成0x100E

 

5.24 WM8731的音效取樣率暫存器設定值

 

5.25 WM8731的音效取樣率暫存器R8

        R8暫存器的內容設定成0x100EVerilog程式碼,位於I2C_AV_Config.v裏,在圖5.26程式碼的第124行,在SAMPLE_CTRLCase中,把LUT_DATA<=16’h100E,並將原先第123行的48KHz註解掉。

 

5.26 WM8731的音效設定I2C介面Verilog程式

5.1.10 WSN監控平台Audio Codec音效輸出uClinux C程式

uClinux C程式部份,本作品所加入的音效播放功能,亦須進行程式修改,由於Nios II IDE環境下,所提供的巨集如IOWR()IORD()uClinuxC不提供IOWR()IORD()函式,因此須自行用指標的方式來存取FPGAGPIO控制腳,這些GPIO的控制腳(SD_CMDSD_CLKSD_DAT)是連接SD_CARD,因此必須在uClinuxC程式裏,想辦法控制到這些GPIO才可讀到SD_CARD內的音樂資料。若將原先在Nios II IDE環境下的宣告全部註解掉,如圖5.27的第17~32行,再加入指標控制GPIO的宣告,如圖5.27的第7~15行。

SD_CARD的實際數據讀取部份,如圖5.27的第60Ncr()函式,這將第62行取代成63行、65行取代成66行、67行取代成68行、69行取代成70行與71行取代成72行。同理,在圖5.27的第75Ncc()函式,亦可採相同的作法,一律將取代成我們撰寫的巨集呼叫方式。

在圖5.27的第87SD_card_init(void)函式,亦要進行置換巨集動作,亦即將93行取代成9497行取代成98101行取代成102106行取代成107108行取代成109。在圖5.27的第177SD_read_lba()函式內,同樣要進行置換巨集動作,亦即將197行取代成198199行取代成200217行取代成218226行取代成227228行取代成229

在圖5.27的第236response_R ()函式內,亦要進行置換巨集動作,亦即將242行取代成243244行取代成245246行取代成247263行取代成264270行取代成271 272行取代成273。在圖5.27299send_cmd ()函式內,亦要進行置換巨集動作,亦即將303行取代成304 310行取代成311316行取代成317319行取代成320331行取代成332 334行取代成335337行取代成338341行取代成342。以上為修改uClinux C程式有關音樂播放時,讀取SD_CARD的部份。

     1         #ifndef   __SD_Card_H__
     2         #define   __SD_Card_H__
     3         //-------------------------------------------------------------------------
     4         // #include <altera_avalon_pio_regs.h>
     5         // SD Card Set I/O Direction
     6        
     7         #define SD_CMD_IN_2 * (unsigned long * ) (SD_CMD_BASE + 0x80000000 + 4)
     8         #define SD_CMD_OUT_2 * (unsigned long * ) (SD_CMD_BASE + 0x80000000 + 4)
     9         #define SD_CLK_BASE_2 * ( unsigned long *) (SD_CLK_BASE + 0x80000000 )
    10         #define SD_CMD_HIGH_2 * ( unsigned long *) (SD_CMD_BASE + 0x80000000 )
    11      #define SD_DAT_IN_2   * ( unsigned long *) (SD_DAT_BASE + 0x80000000 + 4)
    12         #define SD_DAT_LOW_2 * ( unsigned long *) (SD_DAT_BASE + 0x80000000)
    13        
    14         #define SD_TEST_DAT_2 * ( unsigned int *) (SD_DAT_BASE + 0x80000000)
    15         #define SD_TEST_CMD_2 * ( unsigned int *) (SD_CMD_BASE + 0x80000000)
    16        
    17         /*
    18         #define SD_CMD_IN   IOWR(SD_CMD_BASE, 1, 0)
    19         #define SD_CMD_OUT IOWR(SD_CMD_BASE, 1, 1)
    20         #define SD_DAT_IN   IOWR(SD_DAT_BASE, 1, 0)
    21         #define SD_DAT_OUT IOWR(SD_DAT_BASE, 1, 1)
    22         // SD Card Output High/Low
    23         #define SD_CMD_LOW IOWR(SD_CMD_BASE, 0, 0)
    24         #define SD_CMD_HIGH IOWR(SD_CMD_BASE, 0, 1)
    25         #define SD_DAT_LOW IOWR(SD_DAT_BASE, 0, 0)
    26         #define SD_DAT_HIGH IOWR(SD_DAT_BASE, 0, 1)
    27         #define SD_CLK_LOW IOWR(SD_CLK_BASE, 0, 0)
    28         #define SD_CLK_HIGH IOWR(SD_CLK_BASE, 0, 1)
    29         // SD Card Input Read
    30         #define SD_TEST_CMD IORD(SD_CMD_BASE, 0)
    31         #define SD_TEST_DAT IORD(SD_DAT_BASE, 0)
    32         */
    33         //-------------------------------------------------------------------------
    34         #define BYTE    unsigned char
    35         #define UINT16 unsigned int
    36         #define UINT32 unsigned long
    37         //-------------------------------------------------------------------------
    38         void Ncc(void);
    39         BYTE response_R(BYTE);
    40         BYTE send_cmd(BYTE *);
    41         BYTE SD_read_lba(BYTE *,UINT32,UINT32);
    42         BYTE SD_card_init(void);
    43         //-------------------------------------------------------------------------
    44         BYTE read_status;
    45         BYTE response_buffer[20];
    46         BYTE RCA[2];
    47         BYTE cmd_buffer[5];
    48         const BYTE cmd0[5]   = {0x40,0x00,0x00,0x00,0x00};
    49         const BYTE cmd55[5] = {0x77,0x00,0x00,0x00,0x00};
    50         const BYTE cmd2[5]   = {0x42,0x00,0x00,0x00,0x00};
    51         const BYTE cmd3[5]   = {0x43,0x00,0x00,0x00,0x00};
    52         const BYTE cmd7[5]   = {0x47,0x00,0x00,0x00,0x00};
    53         const BYTE cmd9[5]   = {0x49,0x00,0x00,0x00,0x00};
    54         const BYTE cmd16[5] = {0x50,0x00,0x00,0x02,0x00};
    55         const BYTE cmd17[5] = {0x51,0x00,0x00,0x00,0x00};
    56         const BYTE acmd6[5] = {0x46,0x00,0x00,0x00,0x02};
    57         const BYTE acmd41[5] = {0x69,0x0f,0xf0,0x00,0x00};
    58         const BYTE acmd51[5] = {0x73,0x00,0x00,0x00,0x00};
    59         //-------------------------------------------------------------------------
    60         void Ncr(void)
    61         {
    62          //SD_CMD_IN;
    63          SD_CMD_IN_2 = 0;
    64          
    65         // SD_CLK_LOW;
    66          SD_CLK_BASE_2 = 0;
    67         // SD_CLK_HIGH;
    68            SD_CLK_BASE_2 = 1;
    69         // SD_CLK_LOW;
    70          SD_CLK_BASE_2 = 0;
    71         // SD_CLK_HIGH;
    72            SD_CLK_BASE_2 = 0;
    73         }
    74         //-------------------------------------------------------------------------
    75         void Ncc(void)
    76         {
    77          int i;
    78          for(i=0;i<8;i++)
    79          {
    80         //    SD_CLK_LOW;
    81             SD_CLK_BASE_2 = 0;
    82         //    SD_CLK_HIGH;
    83             SD_CLK_BASE_2 = 1;
    84          }
    85         }
    86         //-------------------------------------------------------------------------
    87         BYTE SD_card_init(void)
    88         {
    89        
    90             BYTE x,y;
    91            
    92        
    93             //SD_CMD_OUT;
    94             SD_CMD_OUT_2 = 1;
    95            
    96            
    97             //SD_DAT_IN;
    98             SD_DAT_IN_2 = 0;
    99        
   100            
   101             //SD_CLK_HIGH;
   102             SD_CLK_BASE_2 = 1;
   103        
   104        
   105            
   106             //SD_CMD_HIGH;
   107             SD_CMD_HIGH_2 = 1; //will failed
   108             //SD_DAT_LOW;
   109             SD_DAT_LOW_2 = 0;
   110             read_status=0;
   111             for(x=0;x<40;x++)
   112             Ncr();
   113             for(x=0;x<5;x++)
   114             cmd_buffer[x]=cmd0[x];
   115             y = send_cmd(cmd_buffer);
   116             do
   117             {
   118               for(x=0;x<40;x++);
   119               Ncc();
   120               for(x=0;x<5;x++)
   121               cmd_buffer[x]=cmd55[x];
   122               y = send_cmd(cmd_buffer);
   123               Ncr();
   124               if(response_R(1)>1) //response too long or crc error
   125               return 1;
   126               Ncc();
   127               for(x=0;x<5;x++)
   128               cmd_buffer[x]=acmd41[x];
   129               y = send_cmd(cmd_buffer);
   130               Ncr();     
   131             } while(response_R(3)==1);
   132             Ncc();
   133             for(x=0;x<5;x++)
   134             cmd_buffer[x]=cmd2[x];
   135             y = send_cmd(cmd_buffer);
   136             Ncr();
   137             if(response_R(2)>1)
   138             return 1;
   139             Ncc();
   140             for(x=0;x<5;x++)
   141             cmd_buffer[x]=cmd3[x];
   142             y = send_cmd(cmd_buffer);
   143             Ncr();
   144             if(response_R(6)>1)
   145             return 1;        
   146             RCA[0]=response_buffer[1];
   147             RCA[1]=response_buffer[2];
   148             Ncc();
   149             for(x=0;x<5;x++)
   150             cmd_buffer[x]=cmd9[x];
   151             cmd_buffer[1] = RCA[0];
   152             cmd_buffer[2] = RCA[1]; 
   153             y = send_cmd(cmd_buffer);
   154             Ncr();
   155             if(response_R(2)>1)
   156             return 1;
   157             Ncc();
   158             for(x=0;x<5;x++)
   159             cmd_buffer[x]=cmd7[x];
   160             cmd_buffer[1] = RCA[0];
   161             cmd_buffer[2] = RCA[1];
   162             y = send_cmd(cmd_buffer);
   163             Ncr();
   164             if(response_R(1)>1)
   165             return 1;
   166             Ncc();
   167             for(x=0;x<5;x++)
   168             cmd_buffer[x]=cmd16[x];
   169             y = send_cmd(cmd_buffer); 
   170             Ncr();
   171             if(response_R(1)>1)
   172             return 1;
   173             read_status =1; //sd card ready
   174             return 0;
   175         }
   176         //-------------------------------------------------------------------------
   177         BYTE SD_read_lba(BYTE *buff,UINT32 lba,UINT32 seccnt)
   178         {
   179          BYTE c=0;
   180          UINT32 i,j;
   181          lba+=101;
   182          for(j=0;j<seccnt;j++)
   183          {
   184             {
   185               Ncc();
   186               cmd_buffer[0] = cmd17[0];
   187               cmd_buffer[1] = (lba>>15)&0xff;
   188               cmd_buffer[2] = (lba>>7)&0xff;
   189               cmd_buffer[3] = (lba<<1)&0xff;
   190               cmd_buffer[4] = 0;
   191               lba++;
   192               send_cmd(cmd_buffer);
   193               Ncr();
   194             }
   195             while(1)
   196             {
   197               //SD_CLK_LOW;
   198               SD_CLK_BASE_2 = 0;
   199               //SD_CLK_HIGH;
   200               SD_CLK_BASE_2 = 1;
   201        
   202               // if(!(SD_TEST_DAT))
   203               if(!(SD_TEST_DAT_2/*SD_TEST_DAT*/))//failed
   204           
   205               break;
   206             }
   207             for(i=0;i<512;i++)
   208             {
   209               BYTE j;
   210               for(j=0;j<8;j++)
   211               {
   212                 //SD_CLK_LOW;
   213                 SD_CLK_BASE_2 = 0;
   214                 //SD_CLK_HIGH;
   215                  SD_CLK_BASE_2 = 1;
   216                 c <<= 1;
   217                 //if(SD_TEST_DAT)
   218                 if(!(SD_TEST_DAT_2/*SD_TEST_DAT*/))//failed
   219                 c |= 0x01;
   220               }
   221               *buff=c;
   222               buff++;
   223             }
   224             for(i=0; i<16; i++)
   225             {
   226                 //SD_CLK_LOW;
   227                 SD_CLK_BASE_2 = 0;
   228                 //SD_CLK_HIGH;
   229                  SD_CLK_BASE_2 = 1; //compiler optimize , music will failed
   230             }
   231          }
   232          read_status = 1; //SD data next in
   233          return 0;
   234         }
   235         //-------------------------------------------------------------------------
   236         BYTE response_R(BYTE s)
   237         {
   238          BYTE a=0,b=0,c=0,r=0,crc=0;
   239          BYTE i,j=6,k;
   240          while(1)
   241          {
   242             //SD_CLK_LOW;
   243             SD_CLK_BASE_2 = 0;
   244             //SD_CLK_HIGH;
   245             SD_CLK_BASE_2 = 1; //compiler optimize , music will failed
   246             //if(!(SD_TEST_CMD))
   247             if(!(SD_TEST_CMD_2)) //SD_TEST_CMD_2 failed
   248             break;
   249             if(crc++ >100)
   250             return 2;
   251          }
   252          crc =0;
   253          if(s == 2)
   254          j = 17;
   255        
   256          for(k=0; k<j; k++)
   257          {
   258             c = 0;
   259             if(k > 0)                      //for crc culcar
   260             b = response_buffer[k-1];   
   261             for(i=0; i<8; i++)
   262             {
   263               //SD_CLK_LOW;
   264               SD_CLK_BASE_2 = 0;
   265               if(a > 0)
   266               c <<= 1;
   267               else
   268               i++;
   269               a++;
   270               //SD_CLK_HIGH;
   271               SD_CLK_BASE_2 = 1;
   272               //if(SD_TEST_CMD)
   273               if(SD_TEST_CMD_2)
   274               c |= 0x01;
   275               if(k > 0)
   276               {
   277                 crc <<= 1;
   278                 if((crc ^ b) & 0x80)
   279                 crc ^= 0x09;
   280                 b <<= 1;
   281                 crc &= 0x7f;
   282               }
   283             }
   284             if(s==3)
   285             {
   286               if( k==1 &&(!(c&0x80)))
   287               r=1;
   288             }
   289             response_buffer[k] = c;
   290          }
   291          if(s==1 || s==6)
   292          {
   293             if(c != ((crc<<1)+1))
   294             r=2;
   295          }
   296          return r;
   297         }
   298         //-------------------------------------------------------------------------
   299         BYTE send_cmd(BYTE *in)
   300         {
   301          int i,j;
   302          BYTE b,crc=0;
   303          //SD_CMD_OUT;
   304          SD_CMD_OUT_2 = 1;
   305          for(i=0; i < 5; i++)
   306          {
   307             b = in[i];
   308             for(j=0; j<8; j++)
   309             {
   310               //SD_CLK_LOW;
   311               SD_CLK_BASE_2 = 0;
   312               if(b&0x80)
   313               //SD_CMD_HIGH;
   314               SD_CMD_HIGH_2 = 1;
   315               else
   316               //SD_CMD_LOW;
   317               SD_CMD_HIGH_2 = 0;
   318               crc <<= 1;
   319               //SD_CLK_HIGH;
   320              SD_CLK_BASE_2 = 1;
   321               if((crc ^ b) & 0x80)
   322               crc ^= 0x09;
   323               b<<=1;
   324             }
   325             crc &= 0x7f;
   326          } 
   327          crc =((crc<<1)|0x01);
   328          b = crc;
   329          for(j=0; j<8; j++)
   330          {
   331            // SD_CLK_LOW;
   332             SD_CLK_BASE_2 = 0;
   333             if(crc&0x80)
   334             //SD_CMD_HIGH;
   335             SD_CMD_HIGH_2 = 1;
   336             else
   337             //SD_CMD_LOW;
   338             SD_CMD_HIGH_2 = 0;
   339            
   340               
   341             //SD_CLK_HIGH;
   342             SD_CLK_BASE_2 = 1;
   343             crc<<=1;
   344          }   
   345          return b;  
   346         }
   347         //-------------------------------------------------------------------------
   348         #endif

5.27 uClinux C程式之SD_CARD修改

5.2結論

本參賽作品是在ALTERA Cyclone II FPGA EP2C35F672C6內,結合Hardware/Software Co-Design Co-Verification,實現有線/無線網路的通訊能力,並整合在TerAsicDE2平台上,以打造出具有三網合一之無線感測網路監控平台。

Hardware設計部份,本作品打造出Nios II CPU 平台在Avalon Bus上掛載了27IPs,其中有一個Avalon Slave IP為自行用Verilog HDL設計出來,將它取名成GSM_UART_RX它是負責接收GSM Modem所發出的UART Packet,作為接收訊息之用,以取代傳統ALTERA 提供的UART無法接收一個封包內有連續Bytes傳送過來的缺點,它內部有100BytesShift Register,當收完一個Packet後,Nios II CPU,便可去FIFO中取出資料來解讀來電ID或簡訊內容。

Hardware無線感測模組轉接電路板設計部份,此電路板為自行開發的套件,而無線感測模組轉接電路板成品,如圖5.28所示,此電路板可實現TerAsic DE2平台的GPIO介面能與無線感測模組連接之功能。圖5.28之無線感測模組轉接電路板,除可連接無線CC1100模組與GSM ModemUART介面外,亦可透過自行設計的電路板來連接,而LCD_TEXT的部份,可用來顯示來電之電話號碼或收到簡訊的簡訊內容。此外還設計USBSL811HST)、DM9000A、時鐘ICLED等元件,可供將來擴充更多功能時使用。

 

5.28 無線感測模組轉接電路板成品

ALTERA Cyclone II FPGA內部Software設計部份,成功移植uClinux Kernel 2.6作業系統,並驅動DM9000A乙太網路,讓整個平台在uClinux Kernel 2.6作業系統環境中,能夠連上Internet。本作品亦陸續架設監控功能的Web Server、自行開發CGI程式與DE2_Multi_Sensor應用程式,並成功的將整個三網合一無線感測網路監控功能建立起來。最後將目前本作品所完成項目歸納如下:

Ø完成雙核心 Nios II 設計無線感測網路+電信網路+網際網路之三網合一與音樂播放之無線感測網路監控平台。

Ø完成快速GSM_UART_RXAvalon Slave IPVerilog HDL設計,負責接收GSM Modem所發出的UART Packet,以做為解讀來電顯示電話號碼與短簡訊。

Ø完成系統管理員用自己的手機打電話到DE2監控平台,監控平台會自動掛斷電話並比對來電號碼是否為系統管理員的手機;如果是,就立即傳送簡訊至系統管理員的手機回報目前感測狀況,否則不做任何回應。

Ø完成系統管理員用自己的手機發送簡訊到DE2監控平台,控制WSN節點的風扇啟動或停止,監控平台比對簡訊的來電號碼是系統管理員的手機,就立即控制WSN節點的風扇啟動或停止,否則不做任何回應

Ø完成當監控平台偵測到異常情形(溫度飆高),直接發送緊急通報簡訊到系統管理員的手機

Ø完成近端監控所需的語音播報,透過DE2Audio Codec之聲音輸出。

Ø完成製作無線感測模組轉接電路板,將DE2 BoardCC1100模組連接起來。

Ø完成CC1100模組與DE2 BoardFPGARF收發封包。

Ø完成uClinux Kernel 2.6 PortingNios II CPU平台。

Ø完成具有監控無線感測網路所需之CC1100模組的SPI介面的uClinux C應用程式(DE2_Multi_Sensor)

Ø完成具有控制GSM Modem之無線電信網路,來發送短簡訊的uClinux C應用程式(DE2_Multi_Sensor)

Ø完成BOA Web Server提供網際網路監控。

Ø完成Web Server的網頁設計,本網頁可提供以下之功能:

n需透過網頁輸入監督者的密碼才可登入。

n可透過網頁設定監督者的手機號碼,供緊急發簡訊用。

n可在網頁上顯示出溫度、濕度、瓦斯煙霧、地震位移、太陽能照度與人體紅外線等無線感測值。

n留言板。

Ø完成CGI程式連接Web Server與應用程式(DE2_Multi_Sensor)

Ø完成溫度、濕度、瓦斯煙霧、地震位移、太陽能照度與人體紅外線與風扇控制等7種無線感測節點。

(Revision: 33 / 2010-09-15 14:39:26)

6. 設計特點 (Preliminary Paper)

6 設計特點

本無線感測網路監控平台主要的特點,在於監督者可透過跨網監控的方式來監控無線感測網路,例如透過有線網際網路的Web BrowserInternet的方式或透過手機走電信網路的方式,而不用監督者一直待在監控平台旁邊來進行監測工作。本作品之最大功效,可大幅的提供監督者的自由度,即在任何有網際網路或電信網路的地方,都可監控無線感測網路,以達到跨網監控的功能。

本作品展現出無線感測網路監控平台,可將接收放置於在家中感測器的資訊,監督者在台灣或全世界任何地方,皆可透過網際網路與電信網路來監控家中的無線感測網路情形,如圖1所示。本作品之建置,不需花上太多成本,僅需要將TearAsic DE2平台放在家中,並開啟無線感測網路管理功能,在連上網路與GSM Modem,再裝上預付卡後,即使可出門在外,亦可達到跨網監控家中任何感測狀況的功能。

 

1 具跨網監控功能之家庭WSN監控示意圖

綜合以上之說明,歸納本作品之主要特點如下:

Ø          特點1

假設監控平台放在家中,以監控家裏的溫度、廚房瓦斯、光度與震度等訊息,當監督者人不在家中,而在公司、學校或至國、內外出差,只要在有Internet服務的地方,監督者可透過電腦連上Internet ,在開啟瀏覽器後,即可連接到家中監控平台的Web Server,以得知無線感測網路的實況,包括家中溫度、瓦斯、光度、人體紅外線等各種感測數據,均可顯示在網頁上,並以每51次進行動態更新。由於目前3GWiMAX 4G皆已Ready,監督者亦可透過3GWiMAX 4G的智慧型手機或筆電上網,亦可連接到家中監控平台的Web Server,以得知無線感測網路的實況。

Ø          特點2

當監督者不在無線感測監控平台旁邊,而有攜帶手機在身上時,只要在戶外有手機基地台服務的地方,當無線感測網路偵測到家裏異常情形,便可收到由WSN監控平台所發出的短簡訊,以提供緊急通報監督者之功能。若監督者想要透過手機了解目前家中的無線感測平台之狀態,亦可打一通電話回到家中的監控平台的GSM Modem,在掛斷電話(不需接通)後,此時家中監控平台會比對來電顯示,若手機號碼與監督者相同,便會發送目前感測到的數據與狀態,透過簡訊回報給監督者。

當監督者想要透過手機控制家中的無線感測節點時,例如控制家電On/Off,亦可透過手機發送簡訊,簡訊內容包含密碼與欲控制家電的控制命令。在監控平台收到簡訊後,比對來電號碼與簡訊內的密碼符合後,便可接受控制家電的控制命令,再透過無線感測網路去執行家電On/OffSensor On/Off之動作。由於手機為目前相當普遍之行動通訊裝置,具可隨使用者移動到任何地方(具移動性)之優點,因此透過手機來監控WSN為本專題的設計特點之一。

 

 

(Revision: 5 / 2010-09-15 16:36:10)

7. 總結 (Final Project Paper)

 很榮幸有機會參加由友晶科技所舉辦的[2010 Altera 亞洲創新設計大賽],在這次比賽中讓我們有機會學到更多的軟、硬體協同設計技巧與相關知識與克服問題的能力。在WSN監控平台之實作方面,可分為硬體與軟體兩大部份,在硬體部份使用CYCLONE II FPGA為架構內嵌Nios 2 CPU與自行設計接收GSM Modem的硬體加速IP;而在軟體部份執行嵌入式uCLinux作業系統並開發HTMLCGIC程式,以掌控整個三網合一之無線感測網路監控平台。

本作品利用Verilog HDL設計一個硬體加速IP掛載在Avalon Bus下,用來接收GSM Modem傳過來的封包,以取代NIOS IIUART無法接收Packet Type 的缺點,實現來電顯示電話號碼與接收簡訊內容,並提供系統管理者發送簡訊給監控平台DE2,以控制WSN所接收之感測節點。若要隨時知道監控情形,亦可打一通電話回到監控平台,監控平台會自動Hand Off並比對來電號碼,若來電號碼與系統管理者相同,則立即發送簡訊,並夾帶目前WSN監控的狀況到系統管理者的手機。此外,在DE2板子上,自行嵌入一個uClinux作業系統,讓監控系統的功能更強大,利用DE2板子本身的網路晶片DM9000A上網。本作品在uClinux內成功驅動DM9000A的驅動設式、架設Web Server、撰寫CGI程式與開發三網合一的應用程式,讓系統管理者使用Web Browser,只要在全世界任何有Internet地方,就可連上監控平台DE2,進行監控WSN之感測節點之現況

本系統能夠24小時不停的監控,並提供跨網監控的功能,亦即可實現透過網際網路與電信網路來與感測網路連接,讓監控者雖然人不在無線感測網路內,只要有網際網路(Internet)與電信網路(Telecom)存在的地方,皆可得知目前感測網路的情況,以下達適當的操作指令。希望本系統能協助廠商切入無線監控領域,讓電子資通訊技術打入農、漁、園藝、家庭與工廠自動化之領域,這或許將成為下一個台灣奇蹟。

最後感謝友晶科技舉辦這場ALTERA FPGA設計比賽,讓我們從中學習到不少FPGA內部的軟硬體設計,也感謝老師在材料費與設計電路板方面的協助;讓我們在研發實作的過程當中無後顧之憂,順利實現具三網合一之WSN監控平台之設計。

 

(Draft / 2010-08-31 20:46:13)