8/15/2009

要去大陸了之複習(1)

24號即將要飛去深圳...來公司剛滿一個月就被派出去...還要負責3件case...

還滿無言的...hychen笑的很開心!!!!

總而言之,這次是個機會,但是真的實在是有點趕....

信用卡來不及辦...台胞證要落地簽證...沒出過國的我只有一個人去!!!!

超級的坎恪不安的啦...(還要去一個月....= =)

負責的案子相當陌生,除了BT外...山寨機的wifi porting...android porting等等

簡直要逼出我的潛能呀呀呀呀!!!!!!!!!

以下先來複習之前學習過的東東吧...

-------------------------------------------------------

PC通常一上電,第一個執行的是BIOS,它負責的事有:

1.所有硬體週邊初始化
2.遮罩所有的中斷
3.重新分配記憶體資源
4.引導到MBR開機磁區,MBR存放著作業系統的開機程序,此程序會跳到作業系統的初始化入口

其實bootload做的事跟BIOS差不多,但通常BIOS還有負責一些廠商的firmware設定等等其它作用,這些都是廠商的機密,而bootload是沒有像BIOS有這麼多功能的.

bootload是常駐在快閃記憶體(flash)或者是ROM的低階元件,當power on時,bootload會馬上取得控制權.

它主要是設計來執行低階的初始化作業 啟動影像檔載入及系統診斷,也可以包括target board的低階自我測試的程式,像是記憶體和輸出/輸入的測試.

最後,bootload裡會包含一小段程式,它會載入和移轉控制權到其它的程式,而這程式通常都是作業系統.

而這些不是今天的重點...我先把目標放在核心初始化...未來再來複習其它東西.

在不同的處理器架構,都會產出許多的共通檔案,其中有個叫vmlinux的二進位檔,而這個vmlinux就是核心的本尊了.

這vmlinux相當大,它可不可以直接放在嵌入式系統裡?答案是可以的,像是PowerPC架構和U-BOOT開機載入程序建置的平台,通常是可以直接啟動vmlinux影像檔的.(這我不太清楚好壞了...一般來說核心影像檔都是以壓縮的格式儲)

然而其它架構(像是ARM)和開機載入程序的組合,就需要建置適合的內文來整合其它的共用程序.

註:最常見的開機載入程序(bootload)就是U-Boot了,bootload跟硬體架構幾乎是密不可分的,所以不可能會有一個bootload可以適合多種平台架構(未來好像很難講!?)

什麼叫適合的內文?當開機載入程序取得控制權,並沒有準備好執行環境,直到開機載入程序初如化處理器和相關硬體之後才會有.

也就是說,當開機載入程序到執行作業系統的第一支程式(../init/main.c)時,如果沒有建立適合的內文及環境,是沒辦法執行main.c的.

因為開機載入程序取得控制權時是沒有任何堆壘或是堆壘指標,所以這時只要執行一個簡單的C程式,都會造成處理器當機,因為C的任何變數是儲存在stack上的.

這時你就知道為何要做成uImage檔了吧?會有u這個字,是因為U-BOOT的關係,而這個image檔,就是為了建立適合的環境以及低階公用常式,以便在特定架構中啟動核心.

影像檔是很複雜的,而且又有組合語言的關係,令我非常的難以理解...所以大家只要知道為何要做成影像檔就好.

其實真正的開機載入程序是有二層,其中開機載入程序在啟動時控制版子,它完全不依靠核心;而第二層bootstrap loader的主要功能像是介於開機載入程序及核心中間.

也就是說,bootstrap loader的責任是執行解壓縮和重新定位核心影像檔,以便提供核心在運作時有適合的環境.

像常看到成功載入核心時的一段訊息:

Uncompressing Linux...................done, booting the kernel.

這就是bootstrap loader成功解壓縮核心之後的訊息.

另外我要提到一個重點:

在移到核心時,所有的處理器暫存器所指向的邏輯位址,都是實體位址,可是當處理器暫存器以及核心的資料結構初始化之後,馬上就會受到處理器打開的MMU(記憶體管理單元)的影響,突然間,處理器所有能見到的位址空間,都變成了以虛擬位址定址所取代.

這也是為什麼寫driver時,通常都會利用ioremap或其它技巧來轉換實體位址的關係了(除非處理器沒有MMU的架構...之前常被搞死...終於知道原因了...T_T)

核心初始化的第一步,就是執行../init/main.c裡面的start_kernelo()的function,在這裡核心開始發展它的勢力!!!

其中,它呼叫了一個setup_arch(),很清楚的,它會辨識特定的處理器,然後提供一個機制來呼叫(setup_processor)特定處理器的初始化常式,可以在../arch/arm/kernel/setup.c看到.

其實核心初始化非常複雜,越瞭解下去就越煩...其中還有什麼核心命令列巨集等等....但我工作只是porting,深入瞭解對目前的我來說就未必有用,說不定還能因此鬱悶吐血身亡....所以點一點能止渴就好.

8/12/2009

Google Android

今天為什麼要提到android呢?原因在於hychen可能會把android BT丟給我處理,所以趁還未開始動之前,只好先加班看文件稍微瞭解一下android的面目了...(不知道會不會又把WIFI丟給我...真是畜生!!)

Linux Kernel提供了一個與硬體溝通的平台,但是在Linux Kernel上面執行的程式就不一定跟它有關係了,加上一些自由軟體...像是基本函式庫、工具、圖形界面、應用程式等等,把這些拉歷拉雜的東西整合起來,就統稱為「Linux」。

但為何又有人叫「Linux」為「GNU/Linux」?原因在於kernel跟硬體溝通所用到的系統元件幾乎來自於GUN,也就是自由軟體基本會。

所以這邊要注意的是,Android雖號稱是用Linux kernel運作的,但它跟「GNU/Linux」是不一樣的,因為許多「GUN/Linux」有的東西,在android是看不到的(看的到就不會有那麼多大廠為它背書了...)

而常看到的GPL,它的全名是GNU General Public Licens,它主要目的就是為了確保智慧財產權能夠公開流傳,而基於此創作的延伸,也都必須要採用這個版權宣告。

而重點來了,Android採用Linux kernel,想當然爾也要照規矩來,但是!Android是商業應用,如果這麼做還商業個屁呀!

所以,某人非常的聰明,他在Linux kernel後面開了一個「後門」!

什麼意思呢?kernel要跟硬體溝通,要如何溝通呢?當然就是靠廠商提供的驅動程式啦,所以驅動程式是位於「kernel space」,一般的理解是這樣沒錯。

可是Android卻把驅動程式移到「user space」!完完全全避開了GPL版權的問題!

接著就利用Kernel的「後門」,讓本來不能控制硬體的「user space」也可以去控制了,所以某人就勉為其難依照GPL的規則也公佈了這段「後門程式」

android的application layer是用Java寫的,根本原本的Java寫法並沒什麼不同,因為我不瞭解Java...所以我只好再去偷別人的文章來瞭解一下了...

Java分為三部份:

第一個是「Java語言」,這我就不提了。

第二個是「Java虛擬機器」又稱JVM(Java virtual machine),這是幹什麼的勒?主要是針對 不同的平台架構然後透過JVM的指令能轉換成其它指令,因為不同的CPU其指令一定不同。所以這樣我們只要把程式編譯成JVM看的懂就可以囉!

第三個是「Java函式庫」,Java的library裡頭提供的工具,是有一定的標準,所以不管是在Windows還是Linux,寫法是完全一樣的。

用Java語言寫,呼叫Java函式庫,用編譯器編成指令集,再交給JVM就大功告成了!

所以編譯好的程式,只要使用者有裝JVM跟Java library就可以在各個不同的平台上RUN了!從這裡看的出來,為什麼Java那麼適合商業應用,因為它完全不用公開程式碼!

而Android也有虛擬機,只是它跟JVM不一樣,它叫作Dalvik,而它認識的指令集則是dex。

其中dalvik提供了一個工具叫dx,它是可以把JVM的指令翻成dex,這樣dalvik就知道要如何執行了。

而dalvik是需要linux kernel幫它處理一些事,所以它是綁在Linux上的。


好,我上述所講的大部份都參照下面,我只是為了加深印像及挑部份重點:

android(1)

andorid(2)

8/11/2009

compile glibc的問題

本來要繼續試BT的東西,結果主管突然丟一塊山寨機給我叫我幫忙把環境架起來...

因為之前員工離職,所以有些東西用到一半就沒追下去...但聽說FW跟SW都己經是好的= =

但我光用開發工具的環境就搞的頭大= =

完全摸不著頭緒....而且又好像是Win的東西...根本就一頭霧水...

聽說之前好像是有sample可以看...現在正在努力中...所以只好偷懶一下發一篇之前在編glibc時遇到的錯誤,先筆記下來。

checking for growing stack pointer... configure: error: in `/project1/tmp/dickyjob/bluetooth/glib-2.20.0':

在編譯glibc遇到下列的錯誤:

configure: error: cannot run test program while cross compiling

意思大約是指configure無法在目標機運行測試程序,所以沒辦法自動檢查變量變數。

所以要靠手動去指定,並且寫入cache文件中。

進入configure裡,注意到abstract socket namespace在configure中查找abstract socket可以看到類似這樣的結構:

echo "$as_me:$LINENO: checking abstract socket namespace" >&5
echo $ECHO_N "checking abstract socket namespace... $ECHO_C" >&6
if test "${ac_cv_have_abstract_sockets+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6

其中ac_cv_have_abstract_sockets是我們要查找的變量,所以改成:

echo ac_cv_have_abstract_sockets=yes>arm-linux.cache
./configure --host=arm-linux --cache-file=arm-linux.cache

OK!這樣就行了,只是glibc還滿多要手動的...所以我直接寫到script,看哪個不能檢查就一一寫進去。

8/08/2009

BT porting 總整理

星期四終於完成最後的步驟:成功的可以用藍牙耳機聽音樂 用手機藍牙傳輸檔案...

為了怕日後忘記,先在這做個統一整理

別浪費颱風假...嗄嗄嗄....

藍牙移稙

藍牙術語表

在實作過程中,參考了這篇文章.

---------------------------------------------------

使用TI的platform,搭配自家開發的無線模組來測試BT是否能正常運作.

利用TI所提供的kernel(2.6.14) toolchain(arm-none-gunereal-linux) bluez(藍牙協議棧)等等來架構應用環境完成BT功能.

首先利用TI提供的toolchain,先對bluez做交叉編譯之後放進BSP裡.

而bluez所提供的庫,工具名稱版本,相關依賴的軟體套件分別為 :

bluez-lib-3.36 //bluez的library
bluez-util-3.36 //bluez的工具
dbus-1.2.1 //用於進程間的通信或進程與內核的通信 DBUS簡介及實例
expat-2.0.1 //一個解析XML的工具(不瞭解為啥要這個...只知道DBUS會用到)
openobex-1.3 //OBEX profile,定義了FTP等等傳輸檔案協定
obexftp-0.22 //OBEX profile的FTP工具
alsa-lib-1.2 //A2DP profile要用到alsa的library

還有TI自行定義的HCILL,所要對kernel及blue-util的patch,編譯過程的錯誤之前的文章己寫過.

由於是利用UART做為interface,所以在對kernel做configure設置時,除了USB外,通通butil-in或做成modules.


這些設置好了之後,進入kernel -> filesystem,把相關bluetooth模組一一載入:

#insmod bluetooth.ko
#insmod hci_uart.ko
#insmod l2cap.ko
#insmod rfcomm.ko
#insmod sco.ko
#insmod hidp.ko

接著調用bluez工具,首先會使用到"hciattach"這個tool,它可以初始化藍牙設備,並且通知核心為該裝置載入藍牙驅動,它的格式如下:

hciattach [-n] [-p] [-b] [-t timeout] [-s initial_speed] [speed] [flow|noflow] [bdaddr]

其中type指的是藍牙裝置類型,ID是指藍牙裝直的名稱,speed即是該裝置的baudrate,所以我要利用TI的UART去鏈結藍牙裝置類型:

#hciattach /dev/ttyS1 texas 115200

由於TI會額外做初始化相關動作,所以baudrate的設置要注意是否準確.

鏈結好了之後,緊接著就可以把藍牙裝置喚醒:

#hciconfig hci0 up

然後查閱藍牙的BDADDR及設備資訊:

#hciconfig -a

若查閱到了,就表示己經完成移稙bluetooth的最重要一步,為了進一步使用它的功能,啟動dbus的服務及hcid的服務:

#dbus-daemon --system
#hcid

都準備好了之後,為了要正常使用一個藍牙設備,必須先對該設備做配對操作,由於3.x版的bluez是利用D-BUS這個API來實現配對機制,所以需要向D-BUS註冊一個agent:

#passkey-agent --default 0000 [bdaddr] &

藍牙設置在做連接時,需要一組配對key,而其中"0000"是PIN Code,那"&"是把這指令丟到背景程式讓它持續執行,後面若不打[bdaddr],表示你所有要連接的藍牙裝置的PIN Code都是這個.

你可以輸入ps來查看所有的process有哪些,其中可以清楚看到dbus的daemon及hcid daemon passkey-agent都在後台執行著.

配對好了之後,就可以開始傳輸檔案了,在此之前,要先查看手機的OBEX serivce是位於哪個channel:

#sdptool browse [bdaddr]

SDP是service discover procotl,它會尋找該裝置所有支持procotol,因為要用OBEX serivce,所以請找OBEX serivce的字樣,如下:

Browsing 00:08:C6:77:A0:6C ...
Service Name: OBEX service route
Service RecHandle: 0x10000
Service Class ID List:
"Dialup Networking" (0x1103)
"Generic Networking" (0x1201)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 1
Profile Descriptor List:
"Dialup Networking" (0x1103)
Version: 0x0100

因為我沒有截圖...所以我是用網路上的資訊copy過來,只要注意紅色部份就可以對OBEX profile下達command:

#obexftp -b [BDADDR] -p [file] -B [channel]

file就是你要傳輸的檔案,channel就是上面那個紅色數字,輸入完之後,若成功的話你手機會馬上顯示"是否要接收[ID]的傳案",並要你輸入配對的key,輸入好了之後,另一方會把這password拿來做比對,確認相同就可以達成通訊協議開始傳輸檔案了.

為了要使用藍牙耳機(headset),要利用dbus直接發送命令來建立device及bond藍牙裝置,相關操作請參閱:

Howto Audio DBUS

接著是一系列用dbus命令來bond藍牙裝置:

start audio service or discover the audio service bus "id" :
#dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez org.bluez.Manager.ActivateService string:audio


create headset:
#dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/audio org.bluez.audio.Manager.CreateHeadset string:[BDADDR]

bond bt device:
#dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/hci0 org.bluez.Adapter.CreateBonding string:[BDADDR]


然後用"aplay"來測試wav檔的音樂(想聽mp3 format,請編譯"mpg123"工具)

#aply -D bluetooth test.wav

輸入完之後,就可以聆聽令人震奮的音樂呀呀呀呀!!!!!!

目前聽音樂的聲音會斷斷續續的...主是要現在的baudrate不足於支撐A2DP profile的傳輸...據說至少要920k bps以上才不會讓封包有延遲現像.


8/04/2009

BT porting (5) - Set up !!!

哇哈哈~~今天下班的時候,BT終於起來了!!!

當它能scan到其它裝置時,高興的都快哭了....

跟TI的人通了20幾封mail,然後在今天下午mike學長帶著我去跟TI的人做電話會議...

經過TI的人確認,BTS是沒有問題的,因為其它家廠商都己經support成功了....只是不知道問題出在哪,但他說,他記得之前也有人碰過這種問題,只是他不太曉得...依稀記得跟size有關。

得到模糊的答案之後,就把主力放在source code上,第一步先檢查TI給的kernel,看裡面的UART source code是不是有問題...

經過1個多小時的追蹤,也確定跟kernel無關,因為UART source code只跟設定baudrate、tty device等相關而己,接著我就把重心放在最不可能的bluez....

但好死不死,我突然發覺,似乎真的是bluez的問題,因為在執行「hciattach」這個bluez tools的時候,為什麼會去download TI的firmware呢?

想到這點,心中百分之百確定TI一定有在bluez動手腳....之前會沒想到是因為bluez幾乎都support各種BT的profile及protocol,實在沒必要畫蛇添足...

果其不然,從「hciattach」這個binary file找到hciattach.c,但進去看的時候,也沒看到有關download BTS等相關code...

正準備要死心的時候,發現到hciattach_ti.c !!!

一點進去,哈,這個source code果然是專門處理BTS的...

在經過40幾分鐘的debug....終於發現,原來TI給的BTS要寫進memory的buff,比這個source code所定義的buff大了許多!!!

嘿嘿....一改完,重新編譯...REBOOT,

用hciattach再去鏈結一次serial裝置....OK !!

---------------------------------------------------

在這段debug的日子裡,為了找出原因....一步一步的確定baudrate、用示波器看TX、RX、CTS、RTS等工作是否正常....到最後找出答案,還滿令人振奮的!!!

或許大家都知道,當要debug一個東西時,都會從源頭找起,不然就是先確認hardware是否能正常工作,等確認ok了,再來開始著手software的問題。

我也是這樣,重編kernel...cross compile等等,懷疑BTS是否能用.....

所以我在這得到一個滿重要的體認:

「當要debug時,務必從printf error message開始往上找起。」

在debug的時候,沒人可以幫你,因為只有自己清楚遇到什麼樣的issue,你問別人說不定還會誤導自己的方向。

除了保持頭腦清晰,重要的是不能慌,再來就是先確定自己遇到什麼問題,然後把你遇到的問題、懷疑那個地方出錯、要如何去debug等等,這些都要再你提問時都要準備好,不然只會越撞越頭大。

不要期望有人給你答案,旁人給你的答案有百分之八十都是錯的,大部份都是經驗談,這點要牢記,除非是同一個問題。

8/03/2009

BT porting (4) - UART

最近因為阿嬤的事,假日沒辦法抽空看些書及寫些心得...常要過去幫忙處理一些身後事及每七天就要回去一趟。

恩,不講這些事,bluetooth的進度還是卡在沒辦法download firmware,讓這顆chip無法初始化及喚醒bluetooth的裝置....

因為是透過UART來傳輸,所以在debug當中,也學到一些UART的東東及如何在linux下寫一個設置serial port的baudrate、位元及flow control,下面是UART的簡介及找到一篇滿詳細的教學(嘿嘿...今天只好偷懶一下...)

我在這提一下CTS跟RTS,除了RX、TX這兩支腳最重要之外,再來就是CTS跟RTS。CTS (clear to send),字面上看起來好像是清除傳輸,其實並不是喔(把它翻成允許發送才是正解!!!),它是接到對方的RTS(request tosend) 的命令之後,CTS這支腳會先看自己本身是否還有其它未傳完的資料或者有等待其它動作,如果沒有,CTS會向對方發出說「我準備好,你可以傳資料過來了」。

所以如果發送方送出RTS訊號 ,若接收方說OK了,那接收方就會發出CTS訊號給發送方,那這時候發送方就會透過TX來發出信號到接收方的RX。(我不太了解硬體動作...就我理解而言= =)

大家常會看到流量控制有Xon/Xoff及硬體,其實設置硬體就是利用CTS跟RTS來做流量控制,Xon/Xoff就是用軟體來做流量控制,因為硬體速度比軟體快,所以CTS/RTS是用於高速傳輸。
-----------------------------------------------------

並行通信是指利用多條數據傳輸線將一個資料的各位同時傳送。特點是傳輸速度快,適用於短距離通信,但要求傳輸速度較高的應用場合。

串行通信是指利用一條傳輸線將資料一位位的順序傳送。特點是通信線路簡單,利用簡單的線纜就可以實現通信,減低成本,適用於遠距離通信,但傳輸速度慢的應用場合。常用的串口有RS-232接口

UART控制器:可以工作在Interrupt(中斷)模式或者DMA(直接內存訪問)模式。據有16字節的FIFO(先入先出寄存器),支持最高波特率可達到230.4Kbps。

UART操作:資料發送、資料接收、產生中斷、產生波特率、Loopback模式、紅外線模式及自動流控制模式。(紅外線模式...用都沒用過= =)

串口設置包括:波特率、起始位數量、數據位數量、停止位數量和流控協議

-------------------------------------------------

串口一、串口二對應設備名依次是「/dev/ttyS0」、「/dev/ttyS1」。

Linux下對串口的讀寫可以使用簡單的「read」、「write」函數完成,不同的是需要對串口的其它參數另作設置。

串口設置主要是設置struct termios結構體成員值:

#include

Struct termio

{

unsigned short c_iflag; /*輸入模式標誌*/

unsigned short c_oflag; /*輸出模式標誌*/

unsigned short c_cflag; /*控制模式標誌*/

unsigned short c_lfag; /*本地模式標誌*/

unsigned short c_line; /*line discipline*/

unsigned short c_cc[NCC]; /*control characters*/

};

通過對c_cflag的賦值,可以設置波特率、字符大小、數據位、停止位、奇偶校驗位和硬件流控等。

設置串口屬性基本流程:

1. 保存原先串口配置

為了安全起見和以後調試程序方便,可先保存原先串口的配置,使用函數tcgetattrfd&oldtio)。該函數得到與fd指向對象的相關參數,並將它們保存於lodtio引用的termios結構中。該函數可以測試配置是否正確、該串口是否可用等。調試成功,函數返回0,失敗,函數返回-1.

if(tcgetattr(fd,&oldtio)!=0)

{

perror(「SetupSerial 1」);

return -1;

}

2. 激活選項有CLOCALCREAD

CLOCALCREAD分別用於本地連接和接受使能,通過位掩碼的方式激活這兩個選項。

Newtio.c_cflag |= CLOCAL | CREAD;

3. 設置波特率

設置波特率的函數主要有cfsetispeedcfsetospeed

cfsetispeed(&newtio,B115200);

cfsetospeed(&newtio,B115200);

一般地用戶需要將輸入輸出函數的波特率設置成一樣的。這幾個函數在成功時返回0,失敗-1

4. 設置字符大小

沒有現成可用函數,需要位掩碼。一般先去除數據位中的位掩碼,再重新按要求設置。

options.c_cflag &= ~CSIZE; /*mask the character size bits*/

options.c_cflag |= CS8;

5. 設置奇偶校驗位

先激活c_cflag中的校驗位使能標誌PARENB和是否要進行偶校驗,同時還要激活c_iflag中的奇偶校驗使能。如使能奇校驗時,代碼如下:

newtio.c_cflag |= PARENB;

newtio.c_cflag |=PARODD;

newtio.c_iflag |= (INPCK | ISTRIP);

而使能偶校驗代碼為:

newtio.c_iflag |= (INPCK | ISTRIP);

newtio.c_cflag |= PARENB;

newtio.c_cflag &= ~PAROOD;

6. 設置停止位

通過激活c_cflag中的CSTOPB而實現的。若停止位為1,則清除CSTOPB,若停止位為0,則激活CSTOPB。下面是停止位為1時的代碼:

newtio.c_cflag &= ~CSTOPB;

7. 設置最少字符和等待時間

在對接收字符和等待時間沒有特別要求的情況下,可以將其設置為0

newtio.c_cc[VTIME] =0;

newtio.c_cc[VMIN]=0;

8. 處理要寫入的引用對象

在串口重新設置之後,在之前要寫入的引用對象要重新處理,可調用函數tcflush(fd,queue_selector)來處理要寫入引用的對象。對於為傳輸的數據,或收到但未讀取的數據,其處理方法取決於queue_selector的值。

Queue_selector可能取值:

TCIFLUSH:刷新收到的數據但不讀

TCOFLUSH:刷新寫入的數據但不傳送

TCIOLFLUSH:同時刷新收到的數據但不讀,並且刷新寫入的數據但不傳送

本例採用一:

tcflush(fd, TCIFLUSH)

9. 激活配置

用到函數tcsetattr:

函數原型:tcsetattr(fd,OPTION,&newtio);

這裡的newtio就是termios類型的變量,OPTION可能的取值如下:

TCSANOW:改變的配置立即生效

TCSADRAIN:改變的配置在所有寫入fd的輸出都結束後生效

TCSAFLUSH:改變的配置自愛所有寫入fd引用對象的輸出都被結束後生效,所有已接受但為讀入的輸入都在改變發生前丟棄。

該函數調用成功返回0,失敗-1.

if((tcsetattr(fd,TCSANOW,&newtio))!=0)

{

perror(「com set error」);

return -1;

}

/*串口配置的完整函數,為了函數的通用性,通常將常用的選項都在函數中列出,可大大方便以後用戶的調試使用*/
int set_opt(int fd,int nSpeed,int nBits,char nEvent,int nStop)
{
struct termios newtio,oldtio;
/*保存測試現有串口參數設置,在這裡如果串口號等出錯,會有相關的出錯信息*/
if(tcgetattr(fd,&oldtio)!=0)
{
perror(「SetupSerial 1」);
return -1;
}
bzero(&newtio,sizeof(newtio));
/*步驟一,設置字符大小*/
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
/*設置停止位*/
switch(nBits)
{
case 7:
newtio.c_cflag |=CS7;
break;
case 8:
newtio.c_cflag |=CS8;
break;
}
/*設置奇偶校驗位*/
switch(nEvent)
{
case 'O'://奇數
newtio.c_cflag |= PARENB;
newtio.c_cflag |=PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E'://偶數
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
case 'N'://無奇偶校驗位
newtio.c_cflag &= ~PARENB;
break;
}
/*設置波特率*/
switch(nSpeed)
{
case 2400:
cfsetispeed(&newtio,B2400);
cfsetospeed(&newtio,B2400);
break;
case 4800:
cfsetispeed(&newtio,B4800);
cfsetospeed(&newtio,B4800);
break;
case 9600:
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
break;
case 115200:
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
break;
case 460800:
cfsetispeed(&newtio,B460800);
cfsetospeed(&newtio,B460800);
break;
default:
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
break;
}
/*設置停止位*/
if(nStop==1)
newtio.c_cflag &= ~CSTOPB;
else if(nStop==2)
newtio.c_cflag |= CSTOPB;
/*設置等待時間和最小接收字符*/
newtio.c_cc[VTIME] =0;
newtio.c_cc[VMIN]=0;
/*處理未接受字符*/
tcflush(fd, TCIFLUSH);
/*激活新配置*/
if((tcsetattr(fd,TCSANOW,&newtio))!=0)

{
perror(「com set error」);
return -1;
}
printf("set done!\n");
return 0;
}