TITLE=This code is property of Siemens Industry, Inc. Network 1 // // If this is the first scan then we assume that the modbus master is not yet active. // // Modbus Master // Version: 2.0 LD SM0.1 // if (first scan == TRUE) MOVB 0, VB655 // show master not configured Network 2 // Assume no errors... // LD SM0.0 MOVB 0, LB10 // assume no errors S L9.0, 1 // show done = TRUE Network 3 // Check to see if this is a call to disable Modbus after having it enabled at some point. I check to see if modbus was previously active because I do not want to mess up freeport for the user if he is disabling modbus so that he can use the port for something else, maybe like dialing a modem. // // If the mode is disabled and modbus was not previously active then just return. // LDN L0.0 // if (Modbus == disabled) LPS // AB= VB655, 16#69 // if (Modbus was previously active) LPS // clear modbus state MOVB 0, VB654 // show modbus not active MOVB 0, VB655 // detach transmit complete AB= LB6, 0 // DTCH 9 // ... MOVB 0, SMB87 // RCV VB402, 0 // disable RCV (if operating) MOVB 0, SMB30 // disable freeport LPP AB= LB6, 1 DTCH 26 MOVB 0, SMB187 // RCV VB402, 1 // MOVB 0, SMB130 // disable freeport LPP CRET Network 4 // Save the accumulators so that we do not disturb the user values. // // Put in a Fill or Block Move instruction so that the memory used by the Modbus memory shows up as used in the cross reference. The instructions do not have to execute, they just need to be present. // LD SM0.0 MOVD AC0, LD11 // save accumulators MOVD AC1, LD15 // ... NOT BMB VB402, VB402, 250 // make buffers show in cross reference Network 5 // If modbus is enabled (mMode must be '1' because we would have already returned if it was not) and has previously been initialized (mModbusActive == 0x69) then go to the active state handler. // LDB= VB655, 16#69 // if (Master is active) JMP 100 // goto active handler Network 6 // Initialize Modbus... // // Modbus is enabled but has not been initialized. We know it has not been initialized because mModbusActive is not set to the 'active' status value (0x69). If mModbusActive was set to active, then we would have jumped in the previous network. // // Check the baud rate. // // Modbus RTU always uses 8 data bits. The parity and baudrate are variable. The modbus messages are delineated by a quiet line for at least 3.5 byte times which at 9600 baud is 4 milliseconds. Setup a character timeout value based on the baudrate for this quiet line time. // // Check for 115.2 KBaud // LDD= LD1, +115200 // if (baudrate == 115200) MOVB 16#19, AC0 // setup 115200 and 8 data bits MOVW +1, AC1 // set intercharacter timeout Network 7 // // Check for 57.6 KBaud // LDD= LD1, +57600 // else if (baudrate == 57600) MOVB 16#1D, AC0 // setup 57600 and 8 data bits MOVW +1, AC1 // set intercharacter timeout Network 8 // // Check for 38.4 KBaud // LDD= LD1, +38400 // else if (baudrate == 38.4K) MOVB 16#1, AC0 // setup 38.4K and 8 data bits MOVW +1, AC1 // set intercharacter timeout Network 9 // Check for 19.2KBaud // LDD= LD1, +19200 // else if (baudrate == 19.2K) MOVB 16#05, AC0 // setup 19.2K and 8 data bits MOVW +2, AC1 // set intercharacter timeout Network 10 // // Set default for 9600 baud // LDD= LD1, +9600 // else if (baudrate == 9.6K) MOVB 16#09, AC0 // setup 9600 and 8 data bits MOVW +4, AC1 // set intercharacter timeout Network 11 // // Check for 4800 Baud // LDD= LD1, +4800 // else if (baudrate == 4800) MOVB 16#0D, AC0 // setup 4800 and 8 data bits MOVW +9, AC1 // set intercharacter timeout Network 12 // // Check for 2400 Baud // LDD= LD1, +2400 // else if (baudrate == 2400) MOVB 16#11, AC0 // setup 2400 and 8 data bits MOVW +17, AC1 // set intercharacter timeout Network 13 // Check for 1200 Baud // LDD= LD1, +1200 // else if (baudrate == 1200) MOVB 16#15, AC0 // setup 1200 and 8 data bits MOVW +33, AC1 // set intercharacter timeout Network 14 // Show an error if we did not find a valid baudrate. // LDB= AC0, 0 // if we did not find a valid baudrate MOVB 2, LB10 // show a baudrate error JMP 255 // and abort Network 15 // The valid port values are: // // 0 = port 0 // 1 = port 1 // LDB> LB6, 1 // if illegal port setup MOVB 9, LB10 // show a port setup error JMP 255 // and abort NOT MOVB LB6, VB687 Network 16 // Check if port 1 is installed on this CPU LDB= LB6, 1 ATCH INT4, 26 AENO JMP 99 // If no ENO error then port 1 is available Network 17 LDB= LB6, 1 // if port 1 is missing MOVB 10, LB10 // show a port missing error DTCH 26 // and abort JMP 255 Network 18 LBL 99 Network 19 // If odd parity... // LDB= LB5, 1 // if (ODD parity) ORB 16#C0, AC0 // set ODD parity Network 20 // If even parity... // LDB= LB5, 2 // else if (EVEN parity) ORB 16#40, AC0 // set EVEN parity Network 21 // Check the timeout value. This is pretty much wide open except for negative values. // LDW<= LW7, 0 // if (Timeout <= 0) MOVB 3, LB10 // show a timeout setup error JMP 255 // return Network 22 // Set up the other modbus variables and the RCV box parameters. // // NOTE: The Modbus buffer size has been hard coded to be 250 bytes! This will be overridden during each RCV as we // will compute the size of the response and use the max buffer size to terminate the message. // // NOTE: The mModbusMax value is used for Modbus functions 15 and 16 (multibyte writes) to be sure that the data does // not overrun the buffer. mModbusMax should be set to 10 bytes less than the Modbus buffer size. // // NOTE: The intercharacter timeout for the RCV box has been hardcoded to be 100 mSec. This should be long enough // for any modem but not too long in case of errors. If this needs to be modified, the user can change this time by // changing the value in mModbusCharTimeout (milliseconds). // // NOTE: The number of retries has been set to two. The user can modify this behaviour by changing the value in the // mModbusRetries. // // NOTE: The default operation of the Modbus master is to utilize Modbus functions 5 and 6 for single bit/word writes. // Setting the option flag mModbusForceMulti will force the Modbus master to utilize function 15 and 16 for // single bit writes. This option is available if the Modbus slave does not support functions 5 and 6. // LD SM0.0 MOVB 16#69, VB655 // show modbus active MOVB 0, VB654 // reset comm state machine MOVB 0, VB656 // clear error result S V652.0, 1 // show instance is is done MOVW AC1, VW662 // set transmit delay time (mSec) MOVW LW7, VW660 // copy Rx timeout to background variable MOVB 2, VB659 // default number of retries MOVW 100, VW666 // default intercharacter timeout to 100 mSec R V653.0, 1 // default option to force functions 15 and 16 MOVW 240, VW664 // set max data size (buffer size - 10) Network 23 LD SM0.0 LPS AB= LB6, 0 // MOVB AC0, SMB30 // set freeport baudrate and parity MOVB 0, SMB86 // clear RCV box status MOVB 2#11000100, SMB87 // start character, intercharacter timeout MOVW +0, SMW90 // idle line time = 0 MOVW VW666, SMW92 // set intercharacter timeout MOVB 250, SMB94 // set max rx buffer size ATCH INT4, 9 // interrupt when transmit complete (MBUS_TX) LRD AB= LB6, 1 // enable user interrupts MOVB AC0, SMB130 // return MOVB 0, SMB186 // set freeport baudrate and parity MOVB 2#11000100, SMB187 // clear RCV box status MOVW +0, SMW190 // start character, intercharacter timeout MOVW VW666, SMW192 // idle line time = 0 MOVB 250, SMB194 // set intercharacter timeout ATCH INT4, 26 // set max rx buffer size LPP // interrupt when transmit complete (MBUS_TX) ENI JMP 255 Network 24 // Active handler.... // // We come here after Modbus has been initialized to check on the status of the master. We do all the checking for timeouts and complete (all the polled operations) in this routine. We do these operations here because this routine is supposed to be called every scan. // // We could do these things in the MBUS_MSG routine, but the user could disable that routine after the message has been initialized. If the user stops calling MBUS_MSG then the state machine for the Modbus master would stall and we would never complete the operation, but worse yet, we would lock up the MBUS_MSG for all other calls. By placing all the code to advance the state machine in this routine, we are assured that the state machine will progress and we will eventually complete the current message and free the resources. // // The mModbusState has the following values: // // 0 = idle // 1 = waiting to transmit // 2 = waiting for tx complete // 3 = waiting for first rx char // 4 = waiting for rx complete // 5 = rx complete // LBL 100 Network 25 // Check to see if the user has changed the mModbusCharTimeout value. If yes, then we need to reload SMW92 with the updated value.; // LDB= LB6, 0 AW<> VW666, SMW92 // if (intercharacter timeout has changed) MOVW VW666, SMW92 // set new intercharacter timeout Network 26 // Check to see if the user has changed the mModbusCharTimeout value. If yes, then we need to reload SMW92 with the updated value.; // LDB= LB6, 1 AW<> VW666, SMW192 // if (intercharacter timeout has changed) MOVW VW666, SMW192 // set new intercharacter timeout Network 27 // Modbus State 0 -- Idle // Modbus State 2 -- Waiting for transmit complete // // If the state is idle or waiting for the XMT to complete then there is nothing to do but return. // // We do not have to worry about the state when waiting for transmit complete (state = 2) because if we get to state 2 then we will finish the transmit and transition to state 3 because the transmit complete is interrupt driven. // LDB= VB654, 0 // if (state == idle) or OB= VB654, 2 // (state == waiting for transmit complete) JMP 255 // nothing to do so return Network 28 // Modbus State 1 -- Waiting to transmit // // Get the number of milliseconds since we started the idle line timeout. If the idle line timeout has completed then start the transmit. // LDB= VB654, 1 // if (state == waiting to transmit) LPS CITIM VD670, AC0 // calculate delta time in AC0 AW>= AC0, VW662 // if (delta time >= transmit delay time) LPS // ModbusState = transmitting MOVB 2, VB654 // transmit request AB= LB6, 0 XMT VB402, 0 // return LPP AB= LB6, 1 XMT VB402, 1 LPP JMP 255 Network 29 // Modbus state 3 -- waiting for receive to start // // We are in this state if we have finished transmitting the request but have not yet received the first character of the response. This is a timed state. If we time out here then we assume that the slave did not see the request and we show an error. // // If the receive is already complete (RCV box is complete), we just need to set the state to Rx complete. // // If we have received at least character then we move to the Receiving state to wait for the rest of the characters. // // If we are still waiting for the first character of the response, we need to watch the time to make sure there is some response and the slave is not just dead. The time was captured in the transmit interrupt and store in mModbusTimer. Call CITIM to calculate the number of milliseconds since the timer was started (when the transmit was complete). // // If we timeout then abort the receive by showing the receive is complete. MBUS_MSG will handle the display of the error and any retries that we need to do. // LDB= VB654, 3 // if (state == waiting to Rx) LPS // ... LDB= LB6, 0 // if (RCV box is complete) AB<> SMB86, 0 // set state to Rx complete LDB= LB6, 1 AB<> SMB186, 0 // return OLD ALD // else if (Rx count != 0) MOVB 5, VB654 // set state to Receiving AENO JMP 255 // return LRD // else LPS // calculate delta time into AC0 AB<> VB402, 0 MOVB 4, VB654 // if (delta time >= receive timeout) AENO // disable RCV box JMP 255 // ... LPP // ... CITIM VD670, AC0 // show receive complete LRD AW>= AC0, VW660 // return LPS AB= LB6, 0 R SM87.7, 1 RCV VB402, 0 S SM87.7, 1 LRD AB= LB6, 1 R SM187.7, 1 RCV VB402, 1 S SM187.7, 1 LPP MOVB 5, VB654 LPP JMP 255 Network 30 // Modbus State 4...waiting for receive complete // // If the RCV box is still executing then do not change the status and just return. // // If the RCV box shows the Rx is complete, set the state to Rx complete and return. // LDB= VB654, 4 // if (state == waiting for Rx complete) and LDB= LB6, 0 // (the RCV box is complete) AB<> SMB86, 0 // set state to Rx Complete LDB= LB6, 1 // skip to the return AB<> SMB186, 0 OLD ALD MOVB 5, VB654 JMP 255 Network 31 // Modbus State 5 = receive complete // // If the state is Rx complete and we are here again (we set the Rx complete state on the last scan) then it means that the user has (probably) turned off the input to the MBUS_MSG box that started this message. If this is the case, we just want to throw the response away and reset the state machine to the idle state so a new message can be started. // LDB>= VB654, 5 // if (state == Rx complete) MOVB 0, VB654 // state state to idle Network 32 // Done... LBL 255 Network 33 LD SM0.0 MOVD LD11, AC0 // show done MOVD LD15, AC1 // restore accumulators