Modbus協(xié)議及C語(yǔ)言開(kāi)發(fā)1.4_第1頁(yè)
Modbus協(xié)議及C語(yǔ)言開(kāi)發(fā)1.4_第2頁(yè)
Modbus協(xié)議及C語(yǔ)言開(kāi)發(fā)1.4_第3頁(yè)
Modbus協(xié)議及C語(yǔ)言開(kāi)發(fā)1.4_第4頁(yè)
Modbus協(xié)議及C語(yǔ)言開(kāi)發(fā)1.4_第5頁(yè)
已閱讀5頁(yè),還剩28頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、Modbus協(xié)議上海維度信息技術(shù)有限公司張偉Modbus是什么?Modbus是一種串行通信協(xié)議,是Modicon于1979年,為使用可編程邏輯控制器(PLC)而發(fā)表的。MODBUS是工業(yè)領(lǐng)域通信協(xié)議的業(yè)界標(biāo)準(zhǔn),并且現(xiàn)在是工業(yè)電子設(shè)備之間相當(dāng)常用的連接方式為什么使用Modbus?Modbus比其他通信協(xié)議使用的更廣泛的主要原因有: 公開(kāi)發(fā)表并且無(wú)版稅要求 相對(duì)容易的工業(yè)網(wǎng)絡(luò)部署 對(duì)供應(yīng)商來(lái)說(shuō),修改移動(dòng)原生的位元或字節(jié)沒(méi)有很多限制Modbus允許多個(gè)設(shè)備連接在同一個(gè)網(wǎng)絡(luò)上進(jìn)行通信,舉個(gè)例子,一個(gè)由測(cè)量溫度和濕度的裝置,并且將結(jié)果發(fā)送給計(jì)算機(jī)。在數(shù)據(jù)采集與監(jiān)視控制系統(tǒng)(SCADA)中,Modbus通

2、常用來(lái)連接監(jiān)控計(jì)算機(jī)和Remote Terminal Unit (RTU)。Modbus的版本(1) 串行連接(RS232,RS485需要Linux內(nèi)核2.6.28以上):Modbus RTU:是一種緊湊的,采用二進(jìn)制表示數(shù)據(jù)的方式Modbus ASCII:是一種人類(lèi)可讀的,冗長(zhǎng)的表示方式這兩個(gè)變種都使用串行通訊(serial communication)方式。RTU格式后續(xù)的命令數(shù)據(jù)帶有循環(huán)冗余校驗(yàn)的校驗(yàn)和;而ASCII格式采用縱向冗余校驗(yàn)的校驗(yàn)和。被配置為RTU變種的節(jié)點(diǎn)不會(huì)和設(shè)置為ASCII變種的節(jié)點(diǎn)通信,反之亦然。Modbus的版本(2) Modbus TCP/IP對(duì)于通過(guò)TCP/IP

3、(例如以太網(wǎng))的連接,存在多個(gè)Modbus/TCP變種,這種方式不需要校驗(yàn)和的計(jì)算。Modbus ASCII 幀格式在ASCII模式中,幀以冒號(hào)”:”(ASCII碼 3A)表示信息的開(kāi)始;以回車(chē)換行”CRLF”(ASCII碼 0D和OA)表示信息結(jié)束。有的控制器(584、984A/B/C)可以接受沒(méi)有結(jié)束字符串”CRLF”,此時(shí)出現(xiàn)1s的時(shí)間間隔,控制器也因?yàn)槭钦V袛嚅_(kāi)始開(kāi)始地址地址功能功能數(shù)據(jù)數(shù)據(jù)LRC校驗(yàn)校驗(yàn)結(jié)束結(jié)束1個(gè)字符2個(gè)字符2個(gè)字符N個(gè)字符2個(gè)字符2個(gè)字符Modbus RTU 幀格式(1) RTU模式中,信息開(kāi)始至少需要有3.5個(gè)字符的靜止時(shí)間(依據(jù)通訊的波特率,很容易計(jì)算這個(gè)時(shí)

4、間)。接著,第一個(gè)區(qū)的數(shù)據(jù)為設(shè)備地址。 各區(qū)允許發(fā)送的字符均為16進(jìn)制的0-9,A-F。 網(wǎng)絡(luò)上的設(shè)備連續(xù)監(jiān)測(cè)網(wǎng)絡(luò)上的信息,包括靜止時(shí)間。當(dāng)接收到第一個(gè)地址數(shù)據(jù)時(shí),每臺(tái)設(shè)備立即對(duì)他解碼,以決定是否是自己的地址。 發(fā)送完最后一個(gè)字符后,也有3.5個(gè)字符的靜止時(shí)間,然后才能發(fā)送一個(gè)新的消息。 整個(gè)信息必須連續(xù)發(fā)送。如果發(fā)送幀信息的時(shí)間間隔大于1.5個(gè)字符的靜止時(shí)間時(shí),則接收設(shè)備刷新不完整的信息,并假定是下一個(gè)地址數(shù)據(jù)。 一個(gè)信息發(fā)送后,立即發(fā)送一個(gè)新信息( 沒(méi)有3.5個(gè)字符的靜止時(shí)間),信息會(huì)產(chǎn)生合并,但CRC校驗(yàn)碼無(wú)效,從而產(chǎn)生錯(cuò)誤。Modbus RTU 幀格式(2)開(kāi)始開(kāi)始地址地址功能功能數(shù)據(jù)

5、數(shù)據(jù)CRC校驗(yàn)校驗(yàn)結(jié)束結(jié)束T1+T2+T3+T41 byte1 byteN個(gè)byte2個(gè)byteT1+T2+T3+T4Modbus設(shè)備和通訊(1)Modbus協(xié)議是一個(gè) master/slave 架構(gòu)的協(xié)議。有一個(gè)節(jié)點(diǎn)是 master 節(jié)點(diǎn),其他使用Modbus協(xié)議參與通信的節(jié)點(diǎn)是 slave 節(jié)點(diǎn)。每一個(gè) slave 設(shè)備都有一個(gè)唯一的地址。在串行和MB+網(wǎng)絡(luò)中,只有被指定為主節(jié)點(diǎn)的節(jié)點(diǎn)可以啟動(dòng)一個(gè)命令(在以太網(wǎng)上,任何一個(gè)設(shè)備都能發(fā)送一個(gè)Modbus命令,但 是通常也只有一個(gè)主節(jié)點(diǎn)設(shè)備啟動(dòng)指令)。Modbus設(shè)備和通訊(2)一個(gè)ModBus命令包含了打算執(zhí)行的設(shè)備的Modbus地址。所有設(shè)

6、備都會(huì)收到命令,但只有指定位置的設(shè)備會(huì)執(zhí)行及回應(yīng)指令(地址 0例外,指定地址 0 的指令是廣播指令,所有收到指令的設(shè)備都會(huì)執(zhí)行,不過(guò)不回應(yīng)指令)。所有的Modbus命令包含了檢查碼,以確定到達(dá)的命令沒(méi)有被破壞?;镜?ModBus命令能指令一個(gè)RTU改變它的寄存器的某個(gè)值,控制或者讀取一個(gè)I/O端口,以及指揮設(shè)備回送一個(gè)或者多個(gè)其寄存器中的數(shù)據(jù)。Modbus大數(shù)據(jù)中的應(yīng)用 嵌入式工控板的運(yùn)行軟件主要包括:數(shù)據(jù)采集、Modbus TCP Proxy和TCP/IP、和服務(wù)器通訊三大塊。數(shù)據(jù)采集軟件采用定時(shí)觸發(fā)機(jī)制編寫(xiě)。定時(shí)采集進(jìn)程將根據(jù)上位機(jī)派發(fā)的配置文件的配置信息采集實(shí)時(shí)數(shù)據(jù)上傳至上位機(jī);如果網(wǎng)

7、絡(luò)暫時(shí)故障,需要本地緩存實(shí)時(shí)采集的數(shù)據(jù),在通訊故障排除后實(shí)現(xiàn)斷點(diǎn)續(xù)傳;Modbus TCP Proxy代理處理相應(yīng)服務(wù)器發(fā)來(lái)的設(shè)備查詢(xún)請(qǐng)求并返回處理結(jié)果;服務(wù)器通訊模塊將按照實(shí)時(shí)/響應(yīng)數(shù)據(jù)(根據(jù)配置文件)向上位服務(wù)器發(fā)送信息。Modbus大數(shù)據(jù)數(shù)據(jù)采集模塊 開(kāi)源庫(kù):libmodbus3.1.x 參考代碼QModMaster:主站發(fā)出命令,等待從站返回?cái)?shù)據(jù);監(jiān)控總線(xiàn)數(shù)據(jù)http:/ to Modbus/RTU 代理(非libmodbus庫(kù))http:/ Big-Endian就是高位字節(jié)排放在內(nèi)存的低地址端,低位字節(jié)排放在內(nèi)存的高地址端 Little-Endian就是低位字節(jié)排放在內(nèi)存的低地址端,

8、高位字節(jié)排放在內(nèi)存的高地址端舉例(Hex):12 34 56 78 大端:低地址 - 高地址0 x12 | 0 x34 | 0 x56 | 0 x78(字符串的存儲(chǔ)模式類(lèi)似) 小端:低地址 - 高地址0 x78 | 0 x56 | 0 x34 | 0 x12大端和小端模式的內(nèi)存定義-16位16bit寬的數(shù)0 x1234在Little-endian模式(以及Big-endian模式)CPU內(nèi)存中的存放方式(假設(shè)從地址0 x4000開(kāi)始存放)為:內(nèi)存地址小端模式存放內(nèi)容大端模式存放內(nèi)容0 x40000 x340 x120 x40010 x120 x34大端和小端模式的內(nèi)存定義-32位32bit寬的

9、數(shù)0 x12345678在Little-endian模式以及Big-endian模式)CPU內(nèi)存中的存放方式(假設(shè)從地址0 x4000開(kāi)始存放)為:內(nèi)存地址小端模式存放內(nèi)容大端模式存放內(nèi)容0 x40000 x780 x120 x40010 x560 x340 x40020 x340 x560 x40030 x120 x78大端和小端模式的優(yōu)缺點(diǎn)一般操作系統(tǒng)都是小端,而通訊協(xié)議是大端的 小端模式 :強(qiáng)制轉(zhuǎn)換數(shù)據(jù)不需要調(diào)整字節(jié)內(nèi)容,1、2、4字節(jié)的存儲(chǔ)方式一樣 大端模式 :符號(hào)位的判定固定為第一個(gè)字節(jié),容易判斷正負(fù)數(shù)組在大端小端情況下的存儲(chǔ)以u(píng)nsigned int value = 0 x1234

10、5678為例,分別看看在兩種字節(jié)序下其存儲(chǔ)情況,我們可以用unsigned char buf4來(lái)表示value:Big-Endian: 低地址存放高位,如下:高地址 - buf3 (0 x78) - 低位 buf2 (0 x56) buf1 (0 x34) buf0 (0 x12) - 高位 - 低地址Little-Endian: 低地址存放低位,如下:高地址 - buf3 (0 x12) - 高位 buf2 (0 x34) buf1 (0 x56) buf0 (0 x78) - 低位 -低地址如何判斷機(jī)器的字節(jié)序(1)BOOLIsBigEndian()inta=0 x1234;charb=*

11、(char*)&a;/通過(guò)將通過(guò)將int強(qiáng)制類(lèi)型轉(zhuǎn)換成強(qiáng)制類(lèi)型轉(zhuǎn)換成char單字節(jié),通過(guò)判斷起始存儲(chǔ)位置。即等于單字節(jié),通過(guò)判斷起始存儲(chǔ)位置。即等于取取b等于等于a的低地址部分的低地址部分if(b=0 x12)returnTRUE;returnFALSE;如何判斷機(jī)器的字節(jié)序(2)聯(lián)合體union的存放順序是所有成員都從低地址開(kāi)始存放,利用該特性可以輕松地獲得了CPU對(duì)內(nèi)存采用Little-endian還是Big-endian模式讀寫(xiě):BOOLIsBigEndian()unionNUMinta;charb;num;num.a=0 x1234;if(num.b=0 x12)returnT

12、RUE;returnFALSEC語(yǔ)言浮點(diǎn)的計(jì)算(1)32位CPU四個(gè)字節(jié)表示浮點(diǎn)值:#includeintmain(void)floata=0.0f;unsignedchar*b=(unsignedchar*)&a;intc4;inti=0;/scanf(%x%x%x%x,c+1,c,c+3,c+2);scanf(%x%x%x%x,c,c+1,c+2,c+3);for(i=0;i4;i+)bi=(unsignedchar)ci;printf(%f,a);return0;輸入輸入a45a42f6的結(jié)果是:的結(jié)果是:123.320999C語(yǔ)言浮點(diǎn)的計(jì)算(2)16位CPU二個(gè)字節(jié)表示浮點(diǎn)值:

13、#includeintmain(void)floata=0.0f;unsignedchar*b=(unsignedchar*)&a;intc2;inti=0;scanf(%x%x,c,c+1);for(i=0;i2;i+)bi=(unsignedchar)ci;printf(%f,a);return0;C語(yǔ)言:strcpy()和memcpy()strcpy()函數(shù)只能拷貝字符串。strcpy()函數(shù)將源字符串的每個(gè)字節(jié)拷貝到目錄字符串中,當(dāng)遇到字符串末尾的null字符(0)時(shí),它會(huì)刪去該字符,并結(jié)束拷貝。memcpy()函數(shù)可以拷貝任意類(lèi)型的數(shù)據(jù)。因?yàn)椴⒉皇撬械臄?shù)據(jù)都以null字符結(jié)

14、束,所以你要為memcpy()函數(shù)指定要拷貝的字節(jié)數(shù)。在拷貝字符串時(shí),通常都使用strcpy()函數(shù);在拷貝其它數(shù)據(jù)(例如結(jié)構(gòu))時(shí),通常都使用memcpy()函數(shù)。以下是一個(gè)使用strcpy()函數(shù)和memcpy()函數(shù)的例子:C語(yǔ)言:strcpy()/memcpy()舉例#include#includetypedefstructcust-strintid;charlast_name20;charfirst_namel5;CUSTREC;voidmain(void)char*src_string=Thisisthesourcestring;chardest_string50;CUSTRECsr

15、c_cust;CUSTRECdest_cust;/*Copysrc_stringintodest-string.strcpy(dest_string,src_string);printf(Done!dest_stringis:%sn,dest_string);/*First,intializethesrc_custdatamembers.*/src_cust.id=1;strcpy(src_cust.last_name,“Elton);strcpy(src_cust.first_name,“Zhang);/*Now,Usethememcpy()functiontocopythesrc-cust

16、structuretothedest_custstructure.memcpy(&dest_cust,&src_cust,sizeof(CUSTREC);printf(Done!Ijustcopiedcustomernumber#%d(%s%s).,dest_cust.id,dest_cust.first_name,dest_cust.last_name);C語(yǔ)言:printf()/fprintf()/sprintf()printf、sprintf與fprintf 的用法區(qū)分: printf 是和標(biāo)準(zhǔn)輸出文件(stdout)關(guān)聯(lián)的。 fprintf是用于文件操作的,fprint

17、f是格式化輸出到一個(gè)stream,通常是到文件。 sprintf是格式化輸出到一個(gè)字符串。C語(yǔ)言:printf()/fprintf()/sprintf()舉例(1) while (1) / 等待客戶(hù)端請(qǐng)求 zmq_msg_t request; zmq_msg_init (&request); zmq_recv (responder, &request, 0); printf (收到 Hellon); zmq_msg_close (&request); / 做些“處理” sleep (1); / 返回應(yīng)答 zmq_msg_t reply; zmq_msg_init_siz

18、e (&reply, 5); memcpy (zmq_msg_data (&reply), World, 5); zmq_send (responder, &reply, 0); zmq_msg_close (&reply); C語(yǔ)言:printf()/fprintf()/sprintf()舉例(2) while (1) / 生成數(shù)據(jù) int zipcode, temperature, relhumidity; zipcode = randof (100000); temperature = randof (215) - 80; relhumidity = ra

19、ndof (50) + 10; / 向所有訂閱者發(fā)送消息 char update 20; sprintf (update, %05d %d %d, zipcode, temperature, relhumidity); s_send (publisher, update); C語(yǔ)言:printf()/fprintf()/sprintf()舉例(3) FILE *stream; void main( void ) int i = 10; double fp = 1.5; char s = this is a string; char c = n; stream = fopen( fprintf.

20、out, w ); fprintf( stream, %s%c, s, c ); fprintf( stream, %dn, i ); fprintf( stream, %fn, fp ); fclose( stream ); system( type fprintf.out ); C語(yǔ)言:scanf()/fscan()/sscanf() fscanf()會(huì)自參數(shù)stream的文件流中讀取字符串,再根據(jù)參數(shù)format字符串來(lái)轉(zhuǎn)換并格式化數(shù)據(jù) scanf()會(huì)將輸入的數(shù)據(jù)根據(jù)參數(shù)format字符串來(lái)轉(zhuǎn)換并格式化數(shù)據(jù) sscanf()會(huì)將參數(shù)str的字符串根據(jù)參數(shù)format字符串來(lái)轉(zhuǎn)換并格式化

21、數(shù)據(jù)。格式轉(zhuǎn)換形式請(qǐng)參考scanf()。轉(zhuǎn)換后的結(jié)果存于對(duì)應(yīng)的參數(shù)內(nèi)。C語(yǔ)言: scanf()/fscan()/sscanf()舉例(1) int main(void) int i; float fp; char c, s81; printf(Enter an integer, a real number, a character and a string : n); if (scanf(%d %f %c %s, &i, &fp, &c, s) != 4) printf(Not all of the fields were assignedn); else printf

22、(integer = %dn, i); printf(real number = %fn, fp); printf(character = %cn, c); printf(string = %sn,s); C語(yǔ)言: scanf()/fscan()/sscanf()舉例(2) int main(void) char *tokenstring = 15 12 14; int i; float fp; char sSIZE; char c; /* Input various data */ printf(No. of conversions=%dn, sscanf(tokenstring, %s %c%d%f, s, &c, &i, &fp); /* If there were no space between %s and %c, */ /* sscanf would read the first char

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論