µC/Modbus Program Flow

This section describes the path taken by messages received and replied to by a Modbus channel. Each channel contains 4 buffers as shown in the figure below along with variables used to manage these buffers.


Figure - µC/Modbus Buffer Management



µC/Modbus-S, ASCII Rx and Tx

It might be useful to follow the code for the description provided below.

MB_CommRxTxISR_Handler() – mb_bsp.c

Characters received on a UART are processed by the MB_CommRxTxISR_Handler()unless the UART has a separate interrupt for Rx and Tx. In this case, the function would be called MB_CommRxISR_Handler(). The received character is extracted from the UART and passed to the MB_RxByte() function for processing.

MB_RxByte() – mb.c

MB_RxByte() determines whether the character received needs to be passed to the ASCII or RTU handler. If ASCII, the character is passed to MB_ASCII_RxByte().

MB_ASCII_RxByte() – mb.c

MB_ASCII_RxByte() places received characters in .RxBuf[]. If the received character is a ‘colon’ character (i.e. ‘:’), we reset the pointer to the beginning of the .RxBuf[] because this signals a new message from a Modbus master. We signal the Rx Task if the character received is a ‘line feed’ (i.e. 0x0A) and the message received is destined for the matching node address of the channel. Signaling of the task is done by calling MB_OS_RxSignal() (mb_os.c).

MB_OS_RxTask() – mb_os.c

All Modbus communications is handled by a single Rx Task called MB_OS_RxTask(). The task waits for a message to be sent to it by MB_ASCII_RxByte(). The message is actually a pointer to the Modbus channel where the message was received from. MB_OS_RxTask() calls MB_RxTask() (mb.c) which in turn calls MBS_RxTask() (mbs_core.c). MBS_RxTask() determines whether the message was an ASCII or RTU message and calls MBS_ASCII_Task() (mbs_core.C) or MBS_RTU_Task() (mbs_core.C), respectively to do the actual processing of the message received.

MBS_ASCII_Task() – mbs_core.c

At this point, we received a message from a Modbus master which was directed to the node address of the channel. However, we don’t know yet whether the message is valid. MBS_ASCII_Task() calls MB_ASCII_Rx() (mb.c) which converts the ASCII frame to a binary format. The converted message is placed in .RxFrameData[].

MBS_ASCII_Task() then calls MB_ASCII_RxCalcLRC() to determine whether the received LRC which is part of the message matches the calculated LRC of the message. Note that the LRC is computed by summing up ALL the ASCII characters in the received message except the colon, LRC and CR/LF and then doing a twos complement. In other words, the LRC consist only of the node address, function code and data sent by the Modbus master.

If we have a valid message, we then call MBS_FCxx_Handler() to parse the received message and formulate a response back to the master.

The response is sent to the master by calling MB_ASCII_Tx().

MBS_FCxx_Handler() – mbs_core.c

This function determines what the master wants by looking at the ‘Function Code’ in the received message. The appropriate Modbus function code handler is called accordingly: MBS_FC??_???(). The response is placed in the .TxFrameData[] buffer in binary format.

MB_ASCII_Tx() – mb.c

This function is called when we need to send a response back to a Modbus master. MB_ASCII_Tx() simply converts the response which was placed in .TxFrameData[] and converts it to ASCII. The converted data is placed in the .TxBuf[].

The LRC of the outgoing frame is calculated by calling MB_ASCII_TxCalcLRC(). Note that the LRC is computed by summing up ALL the ASCII characters to be transmitted except the colon, LRC and CR/LF and then doing a twos complement. In other words, the LRC consist only of the node address, function code and data sent to the Modbus master.

MB_ASCII_Tx() then calls MB_Tx() to setup transmission.

MB_Tx() – mb.c

This function is called to send a message to a Modbus master. Here, we simply point the .TxBufPtr at the beginning of the .TxBuf[] and transmit the first byte by calling MB_TxByte() (mb.c) in order to ‘kick start’ transmission interrupts. Note that in a lot of cases, transmission interrupts occur ONLY after a character has been transmitted.

MB_TxByte() – mb.c

MB_TxByte() in turn calls MB_CommTx1() (mb_bsp.c) which sends a byte to the UART and enables Tx interrupts.

µC/Modbus-S, RTU Rx and Tx

It might be useful to follow the code for the description provided below.

MB_CommRxTxISR_Handler() – mb_bsp.c

Bytes received on a UART are processed by the MB_CommRxTxISR_Handler() unless the UART has a separate interrupt for Rx and Tx. In this case, the function would be called MB_CommRxISR_Handler(). The received byte is extracted from the UART and passed to the MB_RxByte() function for processing.

MB_RxByte() – mb.c

MB_RxByte() determines whether the byte received needs to be passed to the ASCII or RTU handler. If RTU, the byte is passed to MB_RTU_RxByte().

MB_RTU_RxByte() – mb.c

MB_RTU_RxByte() places received bytes in .RxBuf[]. Because in RTU, frames are delimited by time, MB_RTU_RxByte() resets the RTU timer for the channel indicating that we didn’t receive an end of frame yet. The received byte is simply placed in the receive buffer, .RxBuf[]. Signaling of a complete frame is done by timing out on the RTU timer for that channel (See MB_RTU_TmrUpdate() in mb.c).

MB_OS_RxTask() – mb_os.c

All Modbus communications is handled by a single Rx Task called MB_OS_RxTask(). The task waits for a message from the RTU timer handler that indicates that a complete frame has been received. The message is actually a pointer to the Modbus channel where the message was received from. MB_OS_RxTask() calls MB_RxTask() (mb.c) which in turn calls MBS_RxTask() (mbs_core.c). MBS_RxTask() determines whether the message was an ASCII or RTU message and calls MBS_ASCII_Task() (mbs_core.c) or MBS_RTU_Task() (MBS_CORE.C), respectively to do the actual processing of the message received.

MBS_RTU_Task() – mbs_core.c

At this point, we received a message from a Modbus master which was directed to the node address of the channel. However, we don’t know yet whether the message is valid. MBS_RTU_Task() calls MB_RTU_Rx() (mb.c) which copies the frame received from the .RxBuf[] to the .RxFrameData[] buffer.

MBS_RTU_Task() then calls MB_RTU_RxCalcCRC() to determine whether the received CRC which is part of the message matches the calculated CRC of the message. Note that the CRC is computed for ALL the bytes received except for the CRC portion itself. In other words, the CRC consist only of the node address, function code and data sent by the Modbus master.

If we have a valid message, we then call MBS_FCxx_Handler() (mbs_core.C) to parse the received message and formulate a response back to the master.

The response is sent to the master by calling MB_RTU_Tx().

MBS_FCxx_Handler() – mbs_core.c

This function determines what the master wants by looking at the ‘Function Code’ in the received message. The appropriate Modbus function code handler is called accordingly: MBS_FC??_???(). The response is placed in the .TxFrameData[] buffer in binary format.

MB_RTU_Tx() – mb.c

This function is called when we need to send a response back to a Modbus master. MB_RTU_Tx() simply copies the response which was placed in .TxFrameData[] into the .TxBuf[].

The CRC of the outgoing frame is calculated by calling MB_RTU_TxCalcCRC(). Note that the CRC is computed on ALL the bytes to be transmitted except the CRC itself. In other words, the CRC consist only of the node address, function code and data sent to the Modbus master.

MB_RTU_Tx() then calls MB_Tx() to setup transmission.

MB_Tx()

This function is called to send a message to a Modbus master. Here, we simply point the .TxBufPtr at the beginning of the .TxBuf[] and transmit the first byte by calling MB_TxByte() (mb.c) in order to ‘kick start’ transmission interrupts. Note that in a lot of cases, transmission interrupts occur ONLY after a character has been transmitted.

MB_TxByte()

MB_TxByte() in turn calls MB_CommTx1() which sends a byte to the UART and enables Tx interrupts.

µC/Modbus-M, ASCII Rx and Tx

It might be useful to follow the code for the description provided below.

MBM_FC??_????() – mbm_core.c

Your Modbus master application calls one of the MBM_FC??_???() functions (see section 3) to send a command to a slave. This function creates a command frame to send to the Modbus slave which is sent by calling MBM_TxCmd().

MBM_TxCmd() – mbm_core.c

This function determines whether the Master channel is configured for Modbus ASCII or RTU and calls MB_ASCII_Tx() or MB_RTU_Tx() accordingly.

MB_ASCII_Tx() – mb.c

In ASCII mode, this function is called to send the command to a Modbus slave. MB_ASCII_Tx() simply converts the command which was placed in .TxFrameData[] and converts it to ASCII. The converted data is placed in the .TxBuf[].

The LRC of the outgoing frame is calculated by calling MB_ASCII_TxCalcLRC(). Note that the LRC is computed by summing up ALL the ASCII characters to be transmitted except the colon, LRC and CR/LF and then doing a twos complement. In other words, the LRC consist only of the node address, function code and data sent to the Modbus slave.

MB_ASCII_Tx() then calls MB_Tx() to setup transmission.

MB_Tx() – mb.c

This function is called to send a message to a Modbus slave. Here, we simply point the .TxBufPtr at the beginning of the .TxBuf[] and transmit the first byte by calling MB_TxByte() (mb.c) in order to ‘kick start’ transmission interrupts. Note that in a lot of cases, transmission interrupts occur ONLY after a character has been transmitted.

MB_TxByte() – mb.c

MB_TxByte() in turn calls MB_CommTx1() (MB_BSP.C) which sends a byte to the UART and enables Tx interrupts.

MB_OS_Wait() – mb_os.c

When the command is sent, MBM_FC??_???() calls MB_OS_Wait() to wait for a response from the slave but with a timeout. If the response is not received within the specified timeout (see MB_CfgCh()) then we flush the Rx buffer. If a response is received, we call MBM_RxReply() to parse the response.

MB_CommRxTxISR_Handler() – mb_bsp.c

Characters received on a UART are processed by the MB_CommRxTxISR_Handler()unless the UART has a separate interrupt for Rx and Tx. In this case, the function would be called MB_CommRxISR_Handler(). The received character is extracted from the UART and passed to the MB_RxByte() function for processing.

MB_RxByte() – mb.c

MB_RxByte() determines whether the character received needs to be passed to the ASCII or RTU handler. If ASCII, the character is passed to MB_ASCII_RxByte().

MB_ASCII_RxByte() – mb.c

MB_ASCII_RxByte() places received characters in .RxBuf[]. If the received character is a ‘colon’ character (i.e. ‘:’), we reset the pointer to the beginning of the .RxBuf[] because this signals a new message from a Modbus master. We call MB_OS_RxSignal() (mb_os.c) if the character received is a ‘line feed’ (i.e. 0x0A) to indicate that the response was received. This wakes up the task that sent the command to the slave and thus, the MBM_FC??_???() function is resumed (right after the MB_OS_RxWait() call).

MBM_RxReply() – mbm_core.c

MBM_RxReply() determines whether the channel is set for ASCII or RTU and calls MB_ASCII_Rx() or MB_RTU_Rx() to receive the packet.

MB_ASCII_Rx() – mb.c

MB_ASCII_Rx() determines if the packet received contains the proper format and checksum. If we received a valid packet, MB_ASCII_Rx() returns to MBM_RxReply() which in turns returns to the MBM_FC??_???() function.

MBM_FC??_???() – mbm_core.c

MBM_FC??_???() then parses the response and returns the requested information to its caller.

µC/Modbus-M, RTU Rx and Tx

It might be useful to follow the code for the description provided below.

MBM_FC??_????() – mbm_core.c

Your Modbus master application calls one of the MBM_FC??_???() functions (see section 3) to send a command to a slave. This function creates a command frame to send to the Modbus slave which is sent by calling MBM_TxCmd().

MBM_TxCmd() – mbm_core.c

This function determines whether the Master channel is configured for Modbus ASCII or RTU and calls MB_ASCII_Tx() or MB_RTU_Tx() accordingly.

MB_RTU_Tx() – mb.c

This function is called when we need to send a command to a Modbus slave. MB_RTU_Tx() simply copies the command which was placed in .TxFrameData[] into the .TxBuf[].

The CRC of the outgoing frame is calculated by calling MB_RTU_TxCalcCRC(). Note that the CRC is computed on ALL the bytes to be transmitted except the CRC itself. In other words, the CRC consist only of the node address, function code and data sent to the Modbus slave.

MB_RTU_Tx() then calls MB_Tx() to setup transmission.

MB_Tx()

This function is called to send a message to a Modbus slave. Here, we simply point the .TxBufPtr at the beginning of the .TxBuf[] and transmit the first byte by calling MB_TxByte() (mb.c) in order to ‘kick start’ transmission interrupts. Note that in a lot of cases, transmission interrupts occur ONLY after a character has been transmitted.

MB_TxByte()

MB_TxByte() in turn calls MB_CommTx1() which sends a byte to the UART and enables Tx interrupts.

MB_OS_Wait() – mb_os.c

When the command is sent, MBM_FC??_???() calls MB_OS_Wait() to wait for a response from the slave but with a timeout. If the response is not received within the specified timeout (see MB_CfgCh()) then we flush the Rx buffer. If a response is received, we call MBM_RxReply() to parse the response.

MB_CommRxTxISR_Handler() – mb_bsp.c

Characters received on a UART are processed by the MB_CommRxTxISR_Handler()unless the UART has a separate interrupt for Rx and Tx. In this case, the function would be called MB_CommRxISR_Handler(). The received character is extracted from the UART and passed to the MB_RxByte() function for processing.

MB_RxByte() – mb.c

MB_RxByte() determines whether the character received needs to be passed to the ASCII or RTU handler. If RTU, the character is passed to MB_RTU_RxByte().

MB_RTU_RxByte() – mb.c

MB_RTU_RxByte() places received bytes in .RxBuf[]. Because in RTU, frames are delimited by time, MB_RTU_RxByte() resets the RTU timer for the channel indicating that we didn’t receive an end of frame yet. The received byte is simply placed in the receive buffer, .RxBuf[]. Signaling of a complete frame is done by timing out on the RTU timer for that channel (See MB_RTU_TmrUpdate() in mb.c).

MBM_RxReply() – mbm_core.c

MBM_RxReply() determines whether the channel is set for ASCII or RTU and calls MB_ASCII_Rx() or MB_RTU_Rx() to receive the packet.

MB_RTU_Rx() – mb.c

MB_RTU_Rx() determines if the packet received contains the proper format and checksum. If we received a valid packet, MB_RTU_Rx() returns to MBM_RxReply() which in turns returns to the MBM_FC??_???() function.

MBM_FC??_???() – mbm_core.c

MBM_FC??_???() then parses the response and returns the requested information to its caller.