前面我們已經(jīng)詳細(xì)講解過Modbus協(xié)議棧的開發(fā)過程,并且利用協(xié)議棧封裝了Modbus RTU主站和從站,Modbus TCP服務(wù)器與客戶端,Modbus ASCII主站與從站應(yīng)用。但在使用過程中,我們發(fā)現(xiàn)一些使用不便和受限的地方,所以我們就想要更新一下協(xié)議棧,主要是應(yīng)用站的封裝。
1 、存在的局限性
在原有的協(xié)議棧中,我們所封裝的Modbus RTU主站是一個(gè)特定的主站,即它只是一個(gè)主站。在通常的應(yīng)用中不會(huì)有什么問題,但在有些應(yīng)用場(chǎng)合就會(huì)顯現(xiàn)出它的局限性。
首先,作為一個(gè)特定的主站,帶多個(gè)從站時(shí),寫從站的處理變的非常復(fù)雜,需要分辨不同的從站,不同的變量。當(dāng)有多個(gè)端口時(shí),還需要分辨不同的端口。
其次,作為一個(gè)特定的主站,帶多個(gè)從站時(shí),讀從站的處理,即使是不同的端口也不能分辨相同站地址的從站。因?yàn)橥恢髡镜慕馕龊瘮?shù)是同一個(gè),所以即使在不同端口也很難分辨,除非在解析前傳遞端口信息,這其實(shí)是將多余的信息傳遞到協(xié)議棧。這樣做不但程序不夠明晰也缺乏一般性。
最后,將所有的Modbus從站通訊都作為唯一的一個(gè)特定從站來處理,使得各部分混雜在一起,程序結(jié)構(gòu)很不清晰,對(duì)象也不明確。
2 、更新設(shè)計(jì)
考慮到前述的局限性,我們將主站及其帶訪問的從站定義為通用的對(duì)象,而當(dāng)我們?cè)诰唧w應(yīng)用中使用時(shí),再將其特例化為特定的主站和從站對(duì)象。
首先我們來考慮主站,原則上我們規(guī)劃的每一個(gè)主站對(duì)象對(duì)應(yīng)我們?cè)O(shè)備上的一個(gè)端口,那么在同一端口下,也就是在一個(gè)特定主站下,我們可以定義多個(gè)地址不同的從站,但不同端口下不受影響。如下圖所示:
從上圖中我們可以發(fā)現(xiàn),我們的目的就是讓協(xié)議棧支持,多主站和多從站,并且在不同主站下,從站的地址重復(fù)不受影響。接下來我們還需要考慮從站對(duì)象。主站對(duì)從站的操作無非兩類:讀從站信息和寫從站信息。
對(duì)于讀從站信息來說,主站需要發(fā)送請(qǐng)求命令,等待從站返回響應(yīng)信息,然后主站解析收到的信息并更新對(duì)應(yīng)的參數(shù)值。有兩點(diǎn)需要我們考慮,第一返回的響應(yīng)消息是沒有對(duì)應(yīng)的寄存器地址的,所以要想在解析的時(shí)候定位寄存器就必須知道發(fā)送的命令,為了便于分辨我們將命令存放在從站對(duì)象中。第二在解析響應(yīng)時(shí),如果兩條命令的響應(yīng)類似是沒法分辨的,所以我們還需要記住上一條命令是什么。也存儲(chǔ)于從站對(duì)象中。
而對(duì)于寫從站操作,無論寫的要求來自于哪里,對(duì)于協(xié)議棧來說肯定是其它的數(shù)據(jù)處理進(jìn)程發(fā)過來的,所接到要求后我們需要記錄是哪一個(gè)主站管理的哪一個(gè)從站的哪些參數(shù)。對(duì)于主站我們不需要分辨,因?yàn)槊總€(gè)主站都是獨(dú)立的處理進(jìn)程,但是對(duì)于從站和參數(shù)我們就需要分辨。每一個(gè)主站可以帶的站地址為0到255,但0和255已有定義,所以實(shí)際是1到254個(gè)。所以我們使用一個(gè)256位的變量,每位對(duì)應(yīng)站號(hào)來標(biāo)志其是否有需要寫的請(qǐng)求。記錄于主站,具體如下:
而每個(gè)從站的寫參數(shù)請(qǐng)求標(biāo)志則存儲(chǔ)于各個(gè)從站對(duì)象,因?yàn)椴煌膹恼究赡苡泻艽髤^(qū)別,存儲(chǔ)于各個(gè)從站更加靈活。
3 、如何實(shí)現(xiàn)
我們已經(jīng)設(shè)計(jì)了我們的更新,但具體如何實(shí)現(xiàn)它呢?我們主要從以下幾個(gè)方面來實(shí)現(xiàn)它。第一,實(shí)現(xiàn)主站對(duì)象類型和從站對(duì)象類型。第二,主站對(duì)象的實(shí)例化及從站對(duì)象的實(shí)例化。第三,讀從站的主站操作過程。第四,寫從站的主站操作過程。接下來我們將一一描述之。
3.1 、定義對(duì)象類型
在Modbus RTU協(xié)議棧的封裝中,我們需要定義主站對(duì)象和從站對(duì)象,自然也需要定義這兩種類型。至于其功能前述已經(jīng)描述過。
首先我們來定義本地主站的類型,其成員包括:一個(gè)uint32_t的寫從站標(biāo)志數(shù)組;從站數(shù)量字段;從站順序字段;本主站所管理的從站列表;4個(gè)數(shù)據(jù)更新函數(shù)指針。具體定義如下:
1 /* 定義本地RTU主站對(duì)象類型 */
2 typedef struct LocalRTUMasterType{
3 uint32_t flagWriteSlave[8]; //寫一個(gè)站控制標(biāo)志位,最多256個(gè)站,與站地址對(duì)應(yīng)。
4 uint16_t slaveNumber; //從站列表中從站的數(shù)量
5 uint16_t readOrder; //當(dāng)前從站在從站列表中的位置
6 RTUAccessedSlaveType *pSlave; //從站列表
7 UpdateCoilStatusType pUpdateCoilStatus; //更新線圈量函數(shù)
8 UpdateInputStatusType pUpdateInputStatus; //更新輸入狀態(tài)量函數(shù)
9 UpdateHoldingRegisterType pUpdateHoldingRegister; //更新保持寄存器量函數(shù)
10 UpdateInputResgisterType pUpdateInputResgister; //更新輸入寄存器量函數(shù)
11 }RTULocalMasterType;
關(guān)于主站對(duì)象類型,在前面的更新設(shè)計(jì)中已經(jīng)講的很清楚了,只有兩個(gè)需要說明一下。第一,從站列表是用來記錄本主站所管理的從站對(duì)象。第二,readOrder字段表示為當(dāng)前訪問從站在列表中的位置,而slaveNumber是從站對(duì)象的數(shù)量,即列表的長(zhǎng)度。具體如下圖所示:
還需要定義從站對(duì)象,此從站對(duì)象只是便于主站而用于表示真實(shí)的從站。主站的從站列表中就是此對(duì)象。具體結(jié)構(gòu)如下:
1 /* 定義被訪問RTU從站對(duì)象類型 */
2 typedef struct AccessedRTUSlaveType{
3 uint8_t stationAddress; //站地址
4 uint8_t cmdOrder; //當(dāng)前命令在命令列表中的位置
5 uint16_t commandNumber; //命令列表中命令的總數(shù)
6 uint8_t (*pReadCommand)[8]; //讀命令列表
7 uint8_t *pLastCommand; //上一次發(fā)送的命令
8 uint32_t flagPresetCoil; //預(yù)置線圈控制標(biāo)志位
9 uint32_t flagPresetReg; //預(yù)置寄存器控制標(biāo)志位
10 }RTUAccessedSlaveType;
關(guān)于從站對(duì)象有兩個(gè)字段需要說一下,就是flagPresetCoil和flagPresetReg字段。這兩個(gè)字段用來表示對(duì)線圈和保持寄存器的寫請(qǐng)求。
3.2 、實(shí)例化對(duì)象
我們定義了主站即從站對(duì)象類型,我們?cè)谑褂脮r(shí)就需要實(shí)例化這些對(duì)象。一般來說一個(gè)硬件端口我們將其實(shí)例化為一個(gè)主站對(duì)象。
RTULocalMasterType hgraMaster;
/ 初始化RTU主站對(duì)象 /
InitializeRTUMasterObject(&hgraMaster,2,hgraSlave,NULL,NULL,NULL,NULL);
而一個(gè)主站對(duì)象會(huì)管理1到254個(gè)從站對(duì)象,所以我們可以將多個(gè)從站對(duì)象實(shí)例組成數(shù)組,并將其賦予主站管理。
RTUAccessedSlaveType hgraSlave[]={{1,0,2,slave1ReadCommand,NULL,0x00,0x00},{2,0,2,slave2ReadCommand,NULL,0x00,0x00}};
所以,根據(jù)主站和從站實(shí)例化的條件,我們需要先實(shí)例化從站對(duì)象才能完整實(shí)例化主站對(duì)象。在主站的初始化中,我們這里將4的數(shù)據(jù)處理函數(shù)指針初始化為NULL,有一個(gè)默認(rèn)的處理函數(shù)會(huì)復(fù)制給它,該函數(shù)是上一版本的延續(xù),在簡(jiǎn)單應(yīng)用時(shí)簡(jiǎn)化操作。從站的上一個(gè)發(fā)送的命令指針也被賦值為NULL,因?yàn)槌跏紩r(shí)還沒有命令發(fā)送。
3.3 、讀從站操作
讀從站操作原理上與以前的版本是一樣的。按照一定的順序給從站發(fā)送命令再對(duì)收到的消息進(jìn)行解析。我們對(duì)主站及其所管理的從站進(jìn)行了定義,將發(fā)送命令保存于從站對(duì)象,將從站列表保存于主站對(duì)象,所以我們需要對(duì)解析函數(shù)進(jìn)行修改。
1 /*解析收到的服務(wù)器相應(yīng)信息*/
2 /*uint8_t *recievedMessage,接收到的消息列表*/
3 /*uint8_t *command,發(fā)送的讀操作命令,若為NULL則在命令列表中查找*/
4 void ParsingSlaveRespondMessage(RTULocalMasterType *master,uint8_t *recievedMessage,uint8_t *command)
5 {
6 int i=0;
7 int j=0;
8 uint16_t startAddress;
9 uint16_t quantity;
10 uint8_t *cmd=NULL;
11
12 /*如果不是讀操作的反回信息不需要處理*/
13 if(recievedMessage[1]>0x04)
14 {
15 return;
16 }
17
18 /*判斷功能碼是否有誤*/
19 FunctionCode fuctionCode=(FunctionCode)recievedMessage[1];
20 if (CheckFunctionCode(fuctionCode) != MB_OK)
21 {
22 return;
23 }
24
25 /*校驗(yàn)接收到的信息是否有錯(cuò)*/
26 uint16_t byteCount=recievedMessage[2];
27 bool chechMessageNoError=CheckRTUMessageIntegrity(recievedMessage,byteCount+5);
28 if(!chechMessageNoError)
29 {
30 return;
31 }
32
33 if((command==NULL)||(!CheckMessageAgreeWithCommand(recievedMessage,command)))
34 {
35 while(islaveNumber)
36 {
37 if(master->pSlave[i].stationAddress==recievedMessage[0])
38 {
39 break;
40 }
41 i++;
42 }
43
44 if(i>=master->slaveNumber)
45 {
46 return;
47 }
48
49 if((master->pSlave[i].pLastCommand==NULL)||(!CheckMessageAgreeWithCommand(recievedMessage,master->pSlave[i].pLastCommand)))
50 {
51 j=FindCommandForRecievedMessage(recievedMessage,master->pSlave[i].pReadCommand,master->pSlave[i].commandNumber);
52
53 if(j<0)
54 {
55 return;
56 }
57
58 cmd=master->pSlave[i].pReadCommand[j];
59 }
60 else
61 {
62 cmd=master->pSlave[i].pLastCommand;
63 }
64 }
65 else
66 {
67 cmd=command;
68 }
69
70 startAddress=(uint16_t)cmd[2];
71 startAddress=(startAddress<<8)+(uint16_t)cmd[3];
72 quantity=(uint16_t)cmd[4];
73 quantity=(quantity<<8)+(uint16_t)cmd[5];
74
75 if((fuctionCode>=ReadCoilStatus)&&(fuctionCode<=ReadInputRegister))
76 {
77 HandleSlaveRespond[fuctionCode-1](master,recievedMessage,startAddress,quantity);
78 }
79 }
解析函數(shù)的主要部分是在檢查接收到的消息是否是合法的Modbus RTU消息。檢查沒問題則調(diào)用協(xié)議站解析。而最后調(diào)用的數(shù)據(jù)處理函數(shù)則是我們需要在具體應(yīng)用中編寫。在前面主站初始化時(shí),回調(diào)函數(shù)我們初始化為NULL,實(shí)際在協(xié)議棧中有弱化的函數(shù)定義,需要針對(duì)具體的寄存器和變量地址實(shí)現(xiàn)操作。
3.4 、寫從站操作
寫從站操作則是在其它進(jìn)程請(qǐng)求后,我們標(biāo)識(shí)需要寫的對(duì)象再統(tǒng)一處理。對(duì)具體哪個(gè)從站的寫標(biāo)識(shí)存于主站實(shí)例。而該從站的哪些變量需要寫則記錄在從站實(shí)例中。
所以在進(jìn)程檢測(cè)到需要寫一個(gè)從站時(shí)則置位對(duì)應(yīng)的位,即改變flagWriteSlave中的對(duì)應(yīng)位。而需要寫該站的哪些變量則標(biāo)記flagPresetCoil和flagPresetReg的對(duì)應(yīng)位。修改這些標(biāo)識(shí)都在其它請(qǐng)求更改的進(jìn)程中實(shí)現(xiàn),而具體的寫操作則在本主站進(jìn)程中,檢測(cè)到標(biāo)志位的變化統(tǒng)一執(zhí)行。
這部分不修改協(xié)議棧的代碼,因?yàn)楦髡炯案髯兞慷贾劣诰唧w對(duì)象相關(guān)聯(lián),所以在具體的應(yīng)用中修改。
4 、回歸驗(yàn)證
為了驗(yàn)證我們前面的更新設(shè)計(jì)是符合要求的,我們?cè)O(shè)計(jì)一個(gè)難度較高的實(shí)驗(yàn)系統(tǒng)。這一實(shí)驗(yàn)系統(tǒng)包括Modbus網(wǎng)關(guān),上位Modbus主站以及下位的Modbus從站。我們所要實(shí)現(xiàn)的是Modbus網(wǎng)關(guān)部分,其具體結(jié)構(gòu)圖設(shè)計(jì)如下:
從上圖我們知道,該Modbus網(wǎng)關(guān)需要實(shí)現(xiàn)一個(gè)Modbus從站用于和上位的通訊;需要實(shí)現(xiàn)兩個(gè)Modbus主站用于和下位的通訊。
在這個(gè)實(shí)驗(yàn)中,讀操作沒有什么需要說的,只需要發(fā)送命令,解析返回消息即可。所以我們重點(diǎn)描述一下寫操作,為了方便操作,在需要寫的連續(xù)段,我們只要找到第一個(gè)請(qǐng)求寫的位置后,就將后續(xù)連續(xù)可寫數(shù)據(jù)一次性寫入。修改寫標(biāo)志位的代碼如下:
1 /* 寫從站寄存器控制 */
2 static void WriteSlaveRegisterControll(uint16_t startAddress,uint16_t endAddress)
3 {
4 if((12<=startAddress)&&(startAddress<=71)&&(12<=endAddress)&&(endAddress<=71))
5 {
6 ModifyWriteRTUSlaveEnableFlag(&hgraMaster,hgraMaster.pSlave[0].stationAddress,true);
7
8 if((startAddress<=12)&&(13<=endAddress))
9 {
10 hgraMaster.pSlave[0].flagPresetReg|=0x01;
11 }
12 if((startAddress<=14)&&(15<=endAddress))
13 {
14 hgraMaster.pSlave[0].flagPresetReg|=0x02;
15 }
16 if((startAddress<=16)&&(17<=endAddress))
17 {
18 hgraMaster.pSlave[0].flagPresetReg|=0x04;
19 }
20 if((startAddress<=18)&&(19<=endAddress))
21 {
22 hgraMaster.pSlave[0].flagPresetReg|=0x08;
23 }
24 if((startAddress<=20)&&(21<=endAddress))
25 {
26 hgraMaster.pSlave[0].flagPresetReg|=0x10;
27 }
28 if((startAddress<=22)&&(23<=endAddress))
29 {
30 hgraMaster.pSlave[0].flagPresetReg|=0x20;
31 }
32 if((startAddress<=24)&&(25<=endAddress))
33 {
34 hgraMaster.pSlave[0].flagPresetReg|=0x40;
35 }
36 if((startAddress<=26)&&(27<=endAddress))
37 {
38 hgraMaster.pSlave[0].flagPresetReg|=0x80;
39 }
40
41 if((startAddress<=32)&&(32<=endAddress))
42 {
43 hgraMaster.pSlave[0].flagPresetReg|=0x100;
44 }
45 if((startAddress<=33)&&(33<=endAddress))
46 {
47 hgraMaster.pSlave[0].flagPresetReg|=0x200;
48 }
49 if((startAddress<=34)&&(34<=endAddress))
50 {
51 hgraMaster.pSlave[0].flagPresetReg|=0x400;
52 }
53 if((startAddress<=35)&&(35<=endAddress))
54 {
55 hgraMaster.pSlave[0].flagPresetReg|=0x800;
56 }
57 if((startAddress<=36)&&(36<=endAddress))
58 {
59 hgraMaster.pSlave[0].flagPresetReg|=0x1000;
60 }
61 if((startAddress<=37)&&(37<=endAddress))
62 {
63 hgraMaster.pSlave[0].flagPresetReg|=0x2000;
64 }
65 if((startAddress<=38)&&(38<=endAddress))
66 {
67 hgraMaster.pSlave[0].flagPresetReg|=0x4000;
68 }
69 if((startAddress<=39)&&(39<=endAddress))
70 {
71 hgraMaster.pSlave[0].flagPresetReg|=0x8000;
72 }
73 if((startAddress<=40)&&(40<=endAddress))
74 {
75 hgraMaster.pSlave[0].flagPresetReg|=0x10000;
76 }
77 if((startAddress<=41)&&(41<=endAddress))
78 {
79 hgraMaster.pSlave[0].flagPresetReg|=0x20000;
80 }
81 if((startAddress<=42)&&(42<=endAddress))
82 {
83 hgraMaster.pSlave[0].flagPresetReg|=0x40000;
84 }
85 if((startAddress<=43)&&(43<=endAddress))
86 {
87 hgraMaster.pSlave[0].flagPresetReg|=0x80000;
88 }
89 if((startAddress<=44)&&(44<=endAddress))
90 {
91 hgraMaster.pSlave[0].flagPresetReg|=0x100000;
92 }
93 if((startAddress<=45)&&(45<=endAddress))
94 {
95 hgraMaster.pSlave[0].flagPresetReg|=0x200000;
96 }
97 if((startAddress<=46)&&(46<=endAddress))
98 {
99 hgraMaster.pSlave[0].flagPresetReg|=0x400000;
100 }
101 if((startAddress<=47)&&(47<=endAddress))
102 {
103 hgraMaster.pSlave[0].flagPresetReg|=0x800000;
104 }
105
106 if((startAddress<=52)&&(55<=endAddress))
107 {
108 hgraMaster.pSlave[0].flagPresetReg|=0x1000000;
109 }
110 if((startAddress<=56)&&(59<=endAddress))
111 {
112 hgraMaster.pSlave[0].flagPresetReg|=0x2000000;
113 }
114 if((startAddress<=60)&&(63<=endAddress))
115 {
116 hgraMaster.pSlave[0].flagPresetReg|=0x4000000;
117 }
118 if((startAddress<=64)&&(67<=endAddress))
119 {
120 hgraMaster.pSlave[0].flagPresetReg|=0x8000000;
121 }
122 }
123
124 if((72<=startAddress)&&(startAddress<=131)&&(72<=endAddress)&&(endAddress<=131))
125 {
126 ModifyWriteRTUSlaveEnableFlag(&hgraMaster,hgraMaster.pSlave[1].stationAddress,true);
127
128 if((startAddress<=72)&&(73<=endAddress))
129 {
130 hgraMaster.pSlave[1].flagPresetReg|=0x01;
131 }
132 if((startAddress<=74)&&(75<=endAddress))
133 {
134 hgraMaster.pSlave[1].flagPresetReg|=0x02;
135 }
136 if((startAddress<=76)&&(77<=endAddress))
137 {
138 hgraMaster.pSlave[1].flagPresetReg|=0x04;
139 }
140 if((startAddress<=78)&&(79<=endAddress))
141 {
142 hgraMaster.pSlave[1].flagPresetReg|=0x08;
143 }
144 if((startAddress<=80)&&(81<=endAddress))
145 {
146 hgraMaster.pSlave[1].flagPresetReg|=0x10;
147 }
148 if((startAddress<=82)&&(83<=endAddress))
149 {
150 hgraMaster.pSlave[1].flagPresetReg|=0x20;
151 }
152 if((startAddress<=84)&&(85<=endAddress))
153 {
154 hgraMaster.pSlave[1].flagPresetReg|=0x40;
155 }
156 if((startAddress<=86)&&(87<=endAddress))
157 {
158 hgraMaster.pSlave[1].flagPresetReg|=0x80;
159 }
160
161 if((startAddress<=92)&&(92<=endAddress))
162 {
163 hgraMaster.pSlave[1].flagPresetReg|=0x100;
164 }
165 if((startAddress<=93)&&(93<=endAddress))
166 {
167 hgraMaster.pSlave[1].flagPresetReg|=0x200;
168 }
169 if((startAddress<=94)&&(94<=endAddress))
170 {
171 hgraMaster.pSlave[1].flagPresetReg|=0x400;
172 }
173 if((startAddress<=95)&&(95<=endAddress))
174 {
175 hgraMaster.pSlave[1].flagPresetReg|=0x800;
176 }
177 if((startAddress<=96)&&(96<=endAddress))
178 {
179 hgraMaster.pSlave[1].flagPresetReg|=0x1000;
180 }
181 if((startAddress<=97)&&(97<=endAddress))
182 {
183 hgraMaster.pSlave[1].flagPresetReg|=0x2000;
184 }
185 if((startAddress<=98)&&(98<=endAddress))
186 {
187 hgraMaster.pSlave[1].flagPresetReg|=0x4000;
188 }
189 if((startAddress<=99)&&(99<=endAddress))
190 {
191 hgraMaster.pSlave[1].flagPresetReg|=0x8000;
192 }
193 if((startAddress<=100)&&(100<=endAddress))
194 {
195 hgraMaster.pSlave[1].flagPresetReg|=0x10000;
196 }
197 if((startAddress<=101)&&(101<=endAddress))
198 {
199 hgraMaster.pSlave[1].flagPresetReg|=0x20000;
200 }
201 if((startAddress<=102)&&(102<=endAddress))
202 {
203 hgraMaster.pSlave[1].flagPresetReg|=0x40000;
204 }
205 if((startAddress<=103)&&(103<=endAddress))
206 {
207 hgraMaster.pSlave[1].flagPresetReg|=0x80000;
208 }
209 if((startAddress<=104)&&(104<=endAddress))
210 {
211 hgraMaster.pSlave[1].flagPresetReg|=0x100000;
212 }
213 if((startAddress<=105)&&(105<=endAddress))
214 {
215 hgraMaster.pSlave[1].flagPresetReg|=0x200000;
216 }
217 if((startAddress<=106)&&(106<=endAddress))
218 {
219 hgraMaster.pSlave[1].flagPresetReg|=0x400000;
220 }
221 if((startAddress<=107)&&(107<=endAddress))
222 {
223 hgraMaster.pSlave[1].flagPresetReg|=0x800000;
224 }
225
226 if((startAddress<=112)&&(115<=endAddress))
227 {
228 hgraMaster.pSlave[1].flagPresetReg|=0x1000000;
229 }
230 if((startAddress<=116)&&(119<=endAddress))
231 {
232 hgraMaster.pSlave[1].flagPresetReg|=0x2000000;
233 }
234 if((startAddress<=120)&&(123<=endAddress))
235 {
236 hgraMaster.pSlave[1].flagPresetReg|=0x4000000;
237 }
238 if((startAddress<=124)&&(127<=endAddress))
239 {
240 hgraMaster.pSlave[1].flagPresetReg|=0x8000000;
241 }
242 }
243
244 if((132<=startAddress)&&(startAddress<=191)&&(131<=endAddress)&&(endAddress<=191))
245 {
246 ModifyWriteRTUSlaveEnableFlag(&hgpjMaster,hgpjMaster.pSlave[0].stationAddress,true);
247 }
248
249 if((192<=startAddress)&&(startAddress<=251)&&(192<=endAddress)&&(endAddress<=251))
250 {
251 ModifyWriteRTUSlaveEnableFlag(&hgpjMaster,hgpjMaster.pSlave[1].stationAddress,true);
252 }
253 }
然后在主站對(duì)象的進(jìn)程中檢測(cè)標(biāo)志位,根據(jù)標(biāo)志位的狀態(tài)來實(shí)現(xiàn)操作,具體的操作代碼很簡(jiǎn)單,且不具普遍性,在此不貼出。
5 、幾點(diǎn)注意
雖然我們對(duì)主站對(duì)象和從站對(duì)象進(jìn)行了封裝,但我們?cè)谑褂脮r(shí)人需要注意一些問題。
(1)、4個(gè)回調(diào)函數(shù)的定義,這4個(gè)回調(diào)函數(shù)用于處理從粘返回的信息,對(duì)應(yīng)Modbus定義的四種數(shù)據(jù),需要根據(jù)主站對(duì)象管理的從站情況來實(shí)現(xiàn)。
(2)、對(duì)于寫操作標(biāo)識(shí)符,一般都是在請(qǐng)求進(jìn)程置位,在主站對(duì)象所在的進(jìn)程檢測(cè)并操作,然后復(fù)位。
告之: 源代碼可上Github下載:https://github.com/foxclever/Modbus
-
MODBUS
+關(guān)注
關(guān)注
28文章
2008瀏覽量
78968 -
ASCII
+關(guān)注
關(guān)注
5文章
172瀏覽量
35673 -
RTU
+關(guān)注
關(guān)注
0文章
432瀏覽量
29270 -
協(xié)議棧
+關(guān)注
關(guān)注
2文章
145瀏覽量
34013
發(fā)布評(píng)論請(qǐng)先 登錄
開關(guān)量轉(zhuǎn)485、485轉(zhuǎn)開關(guān)量
Modbus TCP轉(zhuǎn)Modbus RTU的實(shí)現(xiàn)
中斷不能調(diào)printf的原因
中斷不能調(diào)printf的原因
Modbus RTU實(shí)現(xiàn)工控機(jī)與智能儀表通信
扒一扒中斷為什么不能調(diào)printf

扒一扒中斷為什么不能調(diào)printf

扒一扒中斷為什么不能調(diào)printf

扒一扒中斷為什么不能調(diào)printf?

扒一扒中斷為什么不能調(diào)printf

西門子SMARTP通過MODBUS RTU實(shí)現(xiàn)一主多從的步驟

Profibus DP主站轉(zhuǎn)Modbus RTU協(xié)議網(wǎng)關(guān)(Modbus RTU轉(zhuǎn)Profibus DP)

EtherCAT從站轉(zhuǎn)Modbus RTU總線協(xié)議轉(zhuǎn)換網(wǎng)關(guān)配置詳情

Profibus DP從站轉(zhuǎn)Modbus RTU主站總線協(xié)議轉(zhuǎn)換網(wǎng)關(guān)配置詳情

EtherCAT轉(zhuǎn)Modbus-RTU協(xié)議網(wǎng)關(guān)(Modbus-RTU轉(zhuǎn)EtherCAT)

評(píng)論