單片機圖像采集與網(wǎng)絡(luò)傳輸
1.引言
隨著網(wǎng)絡(luò)技術(shù)的發(fā)展和網(wǎng)絡(luò)應(yīng)用的普及,如何充分利用網(wǎng)絡(luò)資源來實現(xiàn)低成本、高可靠的遠(yuǎn)程視頻監(jiān)控,已成為一個技術(shù)熱點。本文介紹一個用單片機與圖像采集模塊接口,嵌入TCP/IP協(xié)議棧,制作“網(wǎng)絡(luò)攝像頭”的方法。本網(wǎng)絡(luò)攝像頭在一個組播式視頻圖像監(jiān)控系統(tǒng)中,只作為組播源向以太網(wǎng)發(fā)送視頻圖像數(shù)據(jù);其它監(jiān)控計算機則作為組播成員接收數(shù)據(jù)。整個視頻圖像發(fā)送和監(jiān)控系統(tǒng)在局域網(wǎng)中使用時,監(jiān)控接收端的PC機只要加入了組播組,不必知道網(wǎng)絡(luò)攝像頭的IP地址和 MAC地址,也不需要兩者的IP地址是在同一網(wǎng)段,均可接收到網(wǎng)絡(luò)攝像頭發(fā)出的圖像數(shù)據(jù),使用起來相當(dāng)方便。
2. 硬件接口電路
網(wǎng)絡(luò)攝像頭的硬件接口電路如圖1所示。該電路采用的單片機是89C52芯片,另擴展32K的外部存儲器,供網(wǎng)絡(luò)和圖像數(shù)據(jù)處理用。
圖1中的DB200是一個產(chǎn)品攝像模塊,它由微型攝像鏡頭、圖像緩存、時序發(fā)生、總線接口等電路構(gòu)成;其外接信號是一個16腳的插座(9 ~ 16腳分別對應(yīng)數(shù)據(jù)線D7 ~ D0,其它為地址、電源和讀寫控制線)。
圖1中,U1、U4和DB200的片選信號由89C52的地址線A14、A15和74HC00的3個與非門提供:A15=0時選通U4;A15=1及A14=0時選通U1;A15=1及A14=1時選通DB200。DB200的第8腳接A13用來選擇其內(nèi)部寄存器。
RTL8019AS有3種工作方式:(1)跳線方式。(2)即插即用方式。(3)免跳線方式。RTL8019AS使用哪種工作方式由第65腳(JP)決定。為減少連線,我們采用跳線方式(把65腳接高電平)。這樣網(wǎng)卡的傳輸介質(zhì)、I/O基地址和中斷號就由74、77、78、79、80、81、82、 84、85等引腳狀態(tài)決定。
RTL8019AS的81、82、84、85(BD0-BD3)腳接低電平,對應(yīng)32個I/O寄存器地址范圍為300H - 31FH;78-80(BD4-BD6)腳接低電平,對應(yīng)中斷號為INT0(IRQ2/9);74(BA14)、77(BD7)腳接低電平,為自動檢測傳輸介質(zhì)方式。
RTL8019AS有20根地址(SA0-SA19)線,將其5、7、8、9、10(SA0-SA4)腳接89C52的A8-A12,將15、 16(SA8、SA9)腳接高電平來確?;刂窞?00H,其余地址線則全部接地。由于RTL8019AS的使能(AEN)信號是由89C52的 A15=1及A14=0時提供,因此我們可得出以下地址關(guān)系:
89C52: A15 A14 A13 A12 A11 A10 A9 A8 A7………A0
8019: SA4 SA3 SA2 SA1 SA0 …………
2進制數(shù): 1 0 0 0 0 0 0 0 0000 0000 16進制數(shù): 0X8000
2進制數(shù): 1 0 0 1 1 1 1 1 0000 0000 16進制數(shù): 0X9F00
可見,如果89C52輸出地址0X8000至0X9F00,均可選中RTL8019AS。由于RTL8019AS的SA9和SA8恒接高電平,當(dāng)89C52 的地址信號由0X8000至0X9F00變動時,會有:SA9 SA8 SA7 SA6 SA5 SA4 SA3 SA2 SA1 SA0 =11 0000 0000 至 11 0001 1111,即對應(yīng)選擇RTL8019AS的I/O寄存器地址300H至31FH。
RTL8019AS的96腳(IOCS16B)接低電平,使其工作在8位總線模式;64腳接低電平,使用非AUI接口;31、32腳接高電平,屏蔽遠(yuǎn)程自舉加載功能;33腳所需復(fù)位信號,由89C52的P1.5提供;29、30腳對應(yīng)接89C52的讀寫腳。
3. 軟件模塊設(shè)計
嵌入式系統(tǒng)一般采用簡化的TCP/IP協(xié)議棧。常用的有IP、ARP、UDP、ICMP、TCP以及HTTP等協(xié)議。為了嘗試實現(xiàn)一個最簡易的嵌入式TCP/IP協(xié)議,我們選用UDP通訊方式。
UDP的通訊方式有3種:點對點、廣播和組播??紤]到點對點通訊需要ARP協(xié)議來取得目標(biāo)節(jié)點的物理地址,我們不用點對點通訊。至于廣播通訊和組播通訊,兩者都不需要ARP協(xié)議。但廣播方式有如下缺陷:(1)廣播數(shù)據(jù)報不能跨過路由器傳播;(2)廣播時本地子網(wǎng)的所有主機都會接收到廣播并作出響應(yīng),既增加了非接收者的開銷,保密性也不好。我們采用的組播方式不存在這些問題,較適合作為網(wǎng)絡(luò)視頻監(jiān)控的信息傳輸。
3.1 主程序工作流程
網(wǎng)絡(luò)攝像頭的圖像采集、打包、發(fā)送的軟件流程如圖2所示,對應(yīng)的主程序源碼見例程1。為了便于接收端正確判斷每幀圖像的開始,主程序在發(fā)送1幀圖像數(shù)據(jù)前,先用Send_lwm( ) 函數(shù)發(fā)送特征字為“lwm”的4字節(jié)長的數(shù)據(jù)包(該函數(shù)從略),然后再發(fā)送圖像數(shù)據(jù)。
void main(void) //(例程1--循環(huán)采集和發(fā)送圖像數(shù)據(jù)的主程序):
{ init_8019( ); // RTLS8019AS初始化。
while(1) // 循環(huán)采集和發(fā)送圖像:
{ img_capture( ); // 采集1幀圖像。
Send_lwm( ); // 發(fā)送圖像開始的特征字“lwm”
Send_img( ); // 發(fā)送1幀圖像數(shù)據(jù)。
}
}
3.2 以太網(wǎng)控制芯片的初始化
RTL8019AS 芯片有32個寄存器地址,映射到4個頁面,每頁有16個寄存器。本系統(tǒng)只用0頁的14個(00-01H,04-0BH,0D-0EH,0F-10H)寄存器。程序先定義reg00-reg10, 然后用初始化函數(shù)init_8019( )對RTL8019AS各寄存器進行配置:
#define XBYTE ((unsigned char volatile xdata *) 0)
#define reg00 XBYTE[0x8000] //對應(yīng)300H A15=1, A14=0,A13=A12=A11=A10=A9=A8=0
………………..
#define reg10 XBYTE[0x9000] //對應(yīng)310H A15=1, A14=0,A13=0,A12=1,A11=A10=A9=A8=0
sbit RST8019 = P1 ^ 5; // RST8019AS的硬件復(fù)位端。
void init_8019(void) // (例程2--RTL8019AS的初始化):
{ UINT C1; for(C1=0;C1<1000;C1++); // 軟件延時,確保芯片進入穩(wěn)定狀態(tài)
RST8019=1; for(C1=0;C1<1000;C1++); // 硬件復(fù)位、延時以確保芯片完全復(fù)位
RST8019=0; for(C1=0;C1<1000;C1++); // 硬件復(fù)位、延時以確保芯片完全復(fù)位
reg00=0x21; // 選擇第0頁寄存器,并使芯片停止收發(fā)和DMA操作reg0e=0xC8; // DCR: 采用普通、8位DMA方式
reg07=0xFF; reg0f=0x00; // 清除和屏蔽所有中斷(本系統(tǒng)未采用中斷)
reg0d=0xE0; // TCR:采用普通發(fā)送模式、允許CRC產(chǎn)生和校驗
}
為了節(jié)省資源,上述對RTLS8019AS的初始化中,凡是與發(fā)送無關(guān)的寄存器都沒有設(shè)置。發(fā)送時所要用的組播地址、物理地址和IP地址,則在打包時再封裝到各協(xié)議層數(shù)據(jù)包的頭部。
3.3 圖像數(shù)據(jù)的采集
負(fù)責(zé)圖像采集的DB200攝像模塊內(nèi)部有數(shù)據(jù)、狀態(tài)、采集控制和地址控制4個寄存器,表1是它們的尋址方式。對各寄存器的操作規(guī)則如下:
(1)寫操作-對采集控制寄存器(CAP_CTRLr)D0位寫1,可啟動采集過程;對地址控制寄存器(CAP_INCr)D0位寫一次1,其圖像緩存的地址就加1。
(2)讀操作-如果狀態(tài)寄存器(CAP_STAUSr)的D0=1,表示可以開始圖像采集過程;D1=1表示已完成1幀圖像采集,可以讀取數(shù)據(jù)寄存器(CAP_DATAr)的圖像數(shù)據(jù)。
DB200工作的地址范圍是:1100 0000 0000 0000 ~ 1110 0000 0000 0000 = 0C00H ~ 0E00H。據(jù)此,我們可寫出如下圖像采集函數(shù)(例程3):
#define CAP_CONTROLr XBYTE[0X0C000] //A15=1=A14, A13=0
#define CAP_STATUSr XBYTE[0X0C000]
void img_capture( ) // (例程3--圖像采集):
{ while(!(CAP_STATUSr & 0x01)); // 檢查DB200是否準(zhǔn)備就緒?
CAP_CONTROLr=0xff; // 啟動采集1幀圖像過程。
while(!(CAP_CONTROLr & 0x02)); // 是否采集完1幀圖像?是就結(jié)束。
}
img_capture( )函數(shù)只完成了1幀圖像數(shù)據(jù)的采集,采集好的數(shù)據(jù)存在DB200的數(shù)據(jù)緩沖區(qū)內(nèi),留待Send_img( )函數(shù)讀取和發(fā)送。Send_img( )是以讀1行圖像數(shù)據(jù)就發(fā)送1行的方式工作。其源碼如下:
#define CAP_INCr XBYTE[0X0E000] //A15=1=A14,A13=1
#define CAP_DATAr XBYTE[0X0E000]
extern UCHAR xdata outbuf[1520];
void Send_img( ) // (例程4--圖像數(shù)據(jù)的讀取和發(fā)送):
{ UINT data Colon, Line ;
for(Line=0;Line<288;Line++) // 288行
{ for(Colon=0;Colon<385;Colon++) // 385 列
{ databuf[Colon]=CAP_DATAr; // 從 db200讀1個像點到databuf。
CAP_INCr=0xff; // db200圖像數(shù)據(jù)緩存地址加1。
}
udp_send(databuf, UDP_PORT, 386); // 封裝并發(fā)送1行圖像數(shù)據(jù)。
}
}
3.4 圖像數(shù)據(jù)的封裝
DB200采集完1幀圖像后,通過數(shù)據(jù)總線傳送給89C52;89C52則將圖像數(shù)據(jù)按TCP/IP協(xié)議封裝成以太網(wǎng)幀(圖3),然后通過數(shù)據(jù)總線送給RTL8019AS;RTL8019AS則將以太網(wǎng)幀經(jīng)RJ45接口送到10M以太網(wǎng)上。
從圖3可以看到,每個以太網(wǎng)幀的最大長度為1518字節(jié),最小為64字節(jié)。其數(shù)據(jù)部分最大為1500字節(jié),最小為46字節(jié)。每個UDP數(shù)據(jù)傳輸前,必須加上 8字節(jié)的UDP頭來構(gòu)成UDP數(shù)據(jù)報;再加上20字節(jié)的IP頭來構(gòu)成IP數(shù)據(jù)報;最后加上14字節(jié)的幀頭來構(gòu)成以太網(wǎng)幀。這就是所謂的數(shù)據(jù)包封裝。為了避免分段操作的麻煩,UDP數(shù)據(jù)報的最大長度應(yīng)為1500 - 28=1472字節(jié)。在實際應(yīng)用中,我們采用每行圖像數(shù)據(jù)(385字節(jié))封裝一個包的方式傳輸數(shù)據(jù)。
void Data_send( UINT src_port, UINT datalen) // (例程5--UDP數(shù)據(jù)報封裝):
{ UDP_HEADER xdata * udp;
udp = (UDP_HEADER xdata *)(outbuf + 34); // 34=14(以太網(wǎng)幀頭長)+ 20(IP報頭長) udp->dest_port = 2001; // 目的端口號
udp->source_port = 20011; // 源端口號
udp->length = 8 + datalen; // UDP包總長= UDP頭長度+數(shù)據(jù)長度
udp->checksum = 0; // 不校驗UDP數(shù)據(jù)報
ip_pack(outbuf, dest_ipaddr, udp->length); // 封裝IP包
}
void ip_pack(UCHAR xdata * outbuf, ULONG dest_ipaddr, UINT datalen)//(例程6-封裝IP包)
{ IP_HEADER xdata * ip; static UINT ip_ident; // datalen為UDP數(shù)據(jù)報總長度
ip = (IP_HEADER xdata *)(outbuf + 14); // 14字節(jié)為以太網(wǎng)幀頭長度。
ip->ver_len = 0x45; ip->type_of_service = 0; // 版本號和服務(wù)類型。
ip->total_length = 20 + datalen; // 數(shù)據(jù)報總長=IP頭長度+數(shù)據(jù)長度
ip->identifier = ip_ident++; // IP數(shù)據(jù)報序列號。
ip->fragment_info = 0; // IP數(shù)據(jù)報不分段。
ip->time_to_live = 32; // 生存時間。
ip->protocol_id = UDP_TYPE; // 協(xié)議類型為UDP=17。
ip->header_cksum = 0; // 校驗和清0。
ip->dest_ipaddr = 0xEA050607L; // 0xEA050607L=234.5.6.7 為目標(biāo)組播地址
ip->source_ipaddr = 0xD224446FL; // 0xD224446FL=210.36.68.111為本地IP地址
ip->header_cksum = ~cksum(outbuf + 14, 20); // 計算20字節(jié)的IP數(shù)據(jù)報頭校驗和
eth_send(outbuf, 34 + datalen); // 封裝以太網(wǎng)幀并發(fā)送。
}
在上述例程5和例程6中,UDP的校驗和是可選的,IP頭的校驗和是必需的。例程5將UDP檢驗和置為0,例程6則用cksum( )函數(shù)來計算IP頭校驗和。本地IP地址可取任意有效值。
3.5 以太網(wǎng)幀的封裝和發(fā)送
從圖3可知,以太網(wǎng)幀封裝就是要將目的MAC地址、源MAC地址和所要傳輸?shù)臄?shù)據(jù)類型放到待發(fā)送的IP數(shù)據(jù)報前(成為以太網(wǎng)幀頭)。因為我們要進行的是組播發(fā)送,所以目的MAC地址也必須是組播的MAC地址;而本地的源MAC地址我們就用01:02:03:04:05:06。
上面說到,“IP組播地址”范圍是224.0.0.0到239.255.255.255。而以太網(wǎng)幀用到的“MAC組播地址”范圍是 01:00:5E:00:00:00-01:00:5E:7F:FF:FF 。MAC組播地址的構(gòu)成方法是:前3個字節(jié)固定用01:00:5E,后3個字節(jié)則用IP組播地址的后3個字節(jié)。所以我們用234.5.6.7作IP組播地址時,對應(yīng)的MAC組播地址就是 01:00:5E:05:06:07。
在下面的程序中,RTL8019AS用查詢方式進行發(fā)送操作。
UCHAR code dest_hwaddr[6] = { 0x01, 0x00, 0x5E, 0x05, 0x06, 0x07}; // 目標(biāo)MAC組播地址
UCHAR code my_hwaddr[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; // 源MAC地址
void eth_send(UCHAR xdata * outbuf, UINT Data_len) // (例程7--以太網(wǎng)幀封裝和發(fā)送):
{ UINT i , send_len; ETH_HEADER xdata * eth; eth = (ETH_HEADER xdata *)outbuf;
for(i=0;i<6;i++)
{ eth->dest_hwaddr[i]= dest_hwaddr[i]; // 裝入目的MAC地址。
eth->source_hwaddr[i]= my_hwaddr[i]; // 裝入源MAC地址。
}
eth->frame_type = IP_PACKET; // 數(shù)據(jù)類型為IP數(shù)據(jù)報
// send_len為實際要發(fā)送的以太網(wǎng)幀長度,它不能小于60字節(jié):
send_len = (Data_len>=60) ? Data_len : 60 ;
reg00=0x22; // 選擇0頁寄存器,啟動芯片。
while( reg00 & 0x04 ); // 原來的數(shù)據(jù)發(fā)送完沒有?完了往下執(zhí)行
reg08=0x00; reg09=0x40; // 設(shè)置發(fā)送緩沖區(qū)開始地址為4000H。
//設(shè)置(RBCR0-1)遠(yuǎn)端DMA傳送數(shù)據(jù)包長度(高、低字節(jié)):
reg0a=(unsigned char)(Data_len); reg0b=(unsigned char)(Data_len>>8);
reg00=0x16; // 設(shè)置遠(yuǎn)端DMA寫,啟動遠(yuǎn)端DMA傳送數(shù)據(jù)到發(fā)送緩沖區(qū)
for(i=0;i<Data_len;i++) reg10=outbuf[i]; // 往RTL8019AS的I/O端口傳送圖像數(shù)據(jù)
reg04=0x40; // 設(shè)置發(fā)送緩沖區(qū)開始地址高字節(jié)
//設(shè)置(TBCR0-1)發(fā)送字節(jié)計數(shù)器的計數(shù)長度(高、低字節(jié)):
reg05=(unsigned char)(send_len); reg06=(unsigned char)(send_len>>8);
reg00=0x26; // 啟動本地DMA操作,向網(wǎng)絡(luò)發(fā)送圖像數(shù)據(jù)。
}
上述程序先設(shè)置好遠(yuǎn)端DMA開始地址(RSAR)和遠(yuǎn)端DMA數(shù)據(jù)字節(jié)數(shù)(RBCR),并設(shè)置遠(yuǎn)端DMA寫( reg00=0x16),就可以把圖像數(shù)據(jù)寫入RTL8019AS的數(shù)據(jù)緩沖區(qū)。給出發(fā)送緩沖區(qū)首地址和數(shù)據(jù)包長度后,啟動發(fā)送命令 (reg00=0x26), RTL8019AS就會按以太網(wǎng)協(xié)議將圖像數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上。
4. 結(jié)束語
TCP/IP協(xié)議較復(fù)雜,嵌入式系統(tǒng)自身的資源又很有限,動輒幾十K的TCP/IP協(xié)議棧,使很多入門者望而卻步。其實要在嵌入式系統(tǒng)實現(xiàn)TCP/IP協(xié)議,只不過就是編制各個符合TCP/IP協(xié)議規(guī)則的軟件模塊罷了。根據(jù)自己系統(tǒng)的具體情況編制專門的功能模塊,也可以實現(xiàn)相當(dāng)簡化的TCP/IP協(xié)議棧。本文在此給出了一個可供借鑒的實例。
參考文獻:
[1] 吳禮發(fā), 網(wǎng)絡(luò)程序設(shè)計教程[M],北京:希望電子出版社,2002.1
[2] RTL8019AS SPECIFICATION[EB/OL],REALTEKSEMI-CONDUCTOR CO., LTD , 2000.8.20
[3] 單片機與TCP/IP網(wǎng)絡(luò)[EB/OL], HTTP:// WWW.LAOGU.COM