Refs #789. Merged branch with Modbus RTU support back into the trunk.

git-svn-id: https://svn.code.sf.net/p/openblt/code/trunk@1126 5dc33758-31d5-4daf-9ae8-b24bf3d40d73
This commit is contained in:
Frank Voorburg 2023-11-30 11:07:17 +00:00
parent fb81509207
commit 073e3223a4
315 changed files with 254638 additions and 80 deletions

Binary file not shown.

Binary file not shown.

View File

@ -495,6 +495,7 @@ static void DisplayProgramUsage(void)
printf(" xcp (default) -> XCP version 1.0.\n");
printf(" -t=[name] Name of the communication transport layer:\n");
printf(" xcp_rs232 (default) -> XCP on RS232.\n");
printf(" xcp_mbrtu -> XCP on Modbus RTU.\n");
printf(" xcp_can -> XCP on CAN.\n");
printf(" xcp_usb -> XCP on USB.\n");
printf(" xcp_net -> XCP on TCP/IP.\n");
@ -523,6 +524,22 @@ static void DisplayProgramUsage(void)
printf(" second, as a 32-bit value (Default = 57600).\n");
printf(" Supported values: 9600, 19200, 38400, 57600, 115200.\n");
printf("\n");
printf("XCP on Modbus RTU settings (xcp_mbrtu):\n");
printf(" -d=[name] Name of the communication device. For example COM1 or\n");
printf(" /dev/ttyUSB0 (Mandatory).\n");
printf(" -b=[value] The communication speed, a.k.a baudrate in bits per\n");
printf(" second, as a 32-bit value (Default = 57600).\n");
printf(" Supported values: 9600, 19200, 38400, 57600, 115200.\n");
printf(" -pa=[value] The UART parity bit configuration as a 8-bit value.\n");
printf(" (Default = 2).\n");
printf(" Supported values: 0 (none), 1 (odd), 2 (even).\n");
printf(" -sb=[value] The number of UART stopbits as a 8-bit value.\n");
printf(" (Default = 1).\n");
printf(" Supported values: 1, 2.\n");
printf(" -da=[value] Destination address, i.e. the node ID of the receiver,\n");
printf(" as a 8-bit value (Default = 1).\n");
printf(" Supported values: between 1 and 247.\n");
printf("\n");
printf("XCP on CAN settings (xcp_can):\n");
printf(" -d=[name] Name of the CAN device (Mandatory). On Linux this is\n");
printf(" the name of the SocketCAN network interface, such as\n");
@ -649,6 +666,9 @@ static void DisplayTransportInfo(uint32_t transportType, void const * transportS
case BLT_TRANSPORT_XCP_V10_RS232:
printf("XCP on RS232\n");
break;
case BLT_TRANSPORT_XCP_V10_MBRTU:
printf("XCP on Modbus RTU\n");
break;
case BLT_TRANSPORT_XCP_V10_CAN:
printf("XCP on CAN\n");
break;
@ -695,6 +715,52 @@ static void DisplayTransportInfo(uint32_t transportType, void const * transportS
}
break;
}
case BLT_TRANSPORT_XCP_V10_MBRTU:
{
/* Check settings pointer. */
assert(transportSettings);
if (transportSettings == NULL) /*lint !e774 */
{
/* No valid settings present. */
printf(" -> Invalid settings specified\n");
}
else
{
tBltTransportSettingsXcpV10MbRtu * xcpMbRtuSettings =
(tBltTransportSettingsXcpV10MbRtu *)transportSettings;
/* Output the settings to the user. */
printf(" -> Device: ");
if (xcpMbRtuSettings->portName != NULL)
{
printf("%s\n", xcpMbRtuSettings->portName);
}
else
{
printf("Unknown\n");
}
/* Build parity string. */
char parityStr[5] = "";
switch (xcpMbRtuSettings->parity)
{
case 0:
strcat(parityStr, "None");
break;
case 1:
strcat(parityStr, "Odd");
break;
case 2:
default:
strcat(parityStr, "Even");
break;
}
printf(" -> Baudrate: %u bit/sec\n", xcpMbRtuSettings->baudrate);
printf(" -> Parity: %s\n", parityStr);
printf(" -> Stopbits: %hhu\n", xcpMbRtuSettings->stopbits);
printf(" -> Destination address: %hhu\n", xcpMbRtuSettings->destinationAddr);
}
break;
}
case BLT_TRANSPORT_XCP_V10_CAN:
{
/* Check settings pointer. */
@ -1064,7 +1130,8 @@ static uint32_t ExtractTransportTypeFromCommandLine(int argc, char const * const
{ .name = "xcp_rs232", .value = BLT_TRANSPORT_XCP_V10_RS232 },
{ .name = "xcp_can", .value = BLT_TRANSPORT_XCP_V10_CAN },
{ .name = "xcp_usb", .value = BLT_TRANSPORT_XCP_V10_USB },
{ .name = "xcp_net", .value = BLT_TRANSPORT_XCP_V10_NET }
{ .name = "xcp_net", .value = BLT_TRANSPORT_XCP_V10_NET },
{ .name = "xcp_mbrtu", .value = BLT_TRANSPORT_XCP_V10_MBRTU }
};
/* Set the default transport type in case nothing was specified on the command line. */
@ -1176,6 +1243,88 @@ static void * ExtractTransportSettingsFromCommandLine(int argc,
}
}
break;
/* -------------------------- XCP on Modbus RTU -------------------------------- */
case BLT_TRANSPORT_XCP_V10_MBRTU:
/* The following transport layer specific command line parameters are supported:
* -d=[name] -> Device name: /dev/ttyUSB0, COM1, etc.
* -b=[value] -> Baudrate in bits per second.
* -pa=[value] -> Parity (0 for none, 1 for odd, 2 for even).
* -sb=[value] -> Stopbits (1 for one, 2 for two stopbits).
* -da=[value] -> Destination address.
*/
/* Allocate memory for storing the settings and check the result. */
result = malloc(sizeof(tBltTransportSettingsXcpV10MbRtu));
assert(result != NULL);
if (result != NULL) /*lint !e774 */
{
/* Create typed pointer for easy reading. */
tBltTransportSettingsXcpV10MbRtu * mbRtuSettings =
(tBltTransportSettingsXcpV10MbRtu *)result;
/* Set default values. */
mbRtuSettings->portName = NULL;
mbRtuSettings->baudrate = 57600;
mbRtuSettings->parity = 2;
mbRtuSettings->stopbits = 1;
mbRtuSettings->destinationAddr = 1;
/* Loop through all the command line parameters, just skip the 1st one because
* this is the name of the program, which we are not interested in.
*/
for (paramIdx = 1; paramIdx < argc; paramIdx++)
{
/* Is this the -d=[name] parameter? */
if ( (strstr(argv[paramIdx], "-d=") != NULL) &&
(strlen(argv[paramIdx]) > 3) )
{
/* Store the pointer to the device name. */
mbRtuSettings->portName = &argv[paramIdx][3];
/* Continue with next loop iteration. */
continue;
}
/* Is this the -b=[value] parameter? */
if ( (strstr(argv[paramIdx], "-b=") != NULL) &&
(strlen(argv[paramIdx]) > 3) )
{
/* Extract the baudrate value. */
sscanf(&argv[paramIdx][3], "%u", &(mbRtuSettings->baudrate));
/* Continue with next loop iteration. */
continue;
}
/* Is this the -pa=[value] parameter? */
if ((strstr(argv[paramIdx], "-pa=") != NULL) &&
(strlen(argv[paramIdx]) > 4))
{
/* Extract the parity value. */
static uint8_t tempParity;
sscanf(&argv[paramIdx][4], "%hhu", &tempParity);
mbRtuSettings->parity = tempParity;
/* Continue with next loop iteration. */
continue;
}
/* Is this the -sb=[value] parameter? */
if ((strstr(argv[paramIdx], "-sb=") != NULL) &&
(strlen(argv[paramIdx]) > 4))
{
/* Extract the parity value. */
static uint8_t tempStopbits;
sscanf(&argv[paramIdx][4], "%hhu", &tempStopbits);
mbRtuSettings->stopbits = tempStopbits;
/* Continue with next loop iteration. */
continue;
}
/* Is this the -da=[value] parameter? */
if ( (strstr(argv[paramIdx], "-da=") != NULL) &&
(strlen(argv[paramIdx]) > 4) )
{
/* Extract the destination address value. */
static uint8_t tempDestinationAddr;
sscanf(&argv[paramIdx][4], "%hhu", &tempDestinationAddr);
mbRtuSettings->destinationAddr = tempDestinationAddr;
/* Continue with next loop iteration. */
continue;
}
}
}
break;
/* -------------------------- XCP on CAN --------------------------------------- */
case BLT_TRANSPORT_XCP_V10_CAN:
/* The following transport layer specific command line parameters are supported:

View File

@ -160,6 +160,12 @@ namespace OpenBLT
/// </summary>
private const UInt32 TRANSPORT_XCP_V10_NET = 3;
/// <summary>
/// Transport layer for the XCP v1.0 protocol that uses Modbus RTU for data
/// exchange.
/// </summary>
private const UInt32 TRANSPORT_XCP_V10_MBRTU = 4;
/// <summary>
/// Structure layout of the XCP version 1.0 session settings.
/// </summary>
@ -360,6 +366,58 @@ namespace OpenBLT
public IntPtr address;
public UInt16 port;
}
/// <summary>
/// Structure layout of the XCP version 1.0 Modbus RTU transport layer settings.
/// </summary>
/// <remarks>
/// The portName field is platform dependent. On Linux based systems this should be
/// the filename of the tty-device, such as "/dev/tty0". On Windows based systems
/// it should be the name of the COM-port, such as "COM1".
/// </remarks>
public struct TransportSettingsXcpV10MbRtu
{
/// <summary>
/// Communication port name such as /dev/tty0.
/// </summary>
public String portName;
/// <summary>
/// Communication speed in bits/sec.
/// </summary>
public UInt32 baudrate;
/// <summary>
/// Parity (0 for none, 1 for odd, 2 for even).
/// </summary>
public Byte parity;
/// <summary>
/// Stopbits (1 for one, 2 for two stopbits).
/// </summary>
public Byte stopbits;
/// <summary>
/// Destination address (receiver node ID).
/// </summary>
public Byte destinationAddr;
}
/// <summary>
/// Unmanaged structure layout of the XCP version 1.0 Modbus RTU transport layer settings.
/// </summary>
/// <remarks>
/// Only used internally when calling the API function inside the DLL.
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
private struct TransportSettingsXcpV10MbRtuUnmanaged
{
public IntPtr portName;
public UInt32 baudrate;
public Byte parity;
public Byte stopbits;
public Byte destinationAddr;
}
[DllImport(LIBNAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern void BltSessionInit(UInt32 sessionType, IntPtr sessionSettings, UInt32 transportType, IntPtr transportSettings);
@ -673,6 +731,90 @@ namespace OpenBLT
Marshal.FreeHGlobal(sessionSettingsUnmanaged.seedKeyFile);
}
}
/// <summary>
/// Initializes the firmware update session for the XCP v1.0 communication
/// protocol and Modbus RTU as the transport layer. This function is typically
/// called once at the start of the firmware update.
/// </summary>
/// <param name="sessionSettings">XCP V1.0 protocol settings</param>
/// <param name="transportSettings">Modbus RTU transport layer settings</param>
/// <example>
/// <code>
/// OpenBLT.Lib.Session.SessionSettingsXcpV10 sessionSettings;
/// sessionSettings.timeoutT1 = 1000;
/// sessionSettings.timeoutT3 = 2000;
/// sessionSettings.timeoutT4 = 10000;
/// sessionSettings.timeoutT5 = 1000;
/// sessionSettings.timeoutT6 = 50;
/// sessionSettings.timeoutT7 = 2000;
/// sessionSettings.seedKeyFile = "";
/// sessionSettings.connectMode = 0;
///
/// OpenBLT.Lib.Session.TransportSettingsXcpV10MbRtu transportSettings;
/// transportSettings.portName = "COM8";
/// transportSettings.baudrate = 57600;
/// transportSettings.parity = 2;
/// transportSettings.stopbits = 1;
/// transportSettings.destinationAddr = 1;
///
/// OpenBLT.Lib.Session.Init(sessionSettings, transportSettings);
/// </code>
/// </example>
public static void Init(SessionSettingsXcpV10 sessionSettings, TransportSettingsXcpV10MbRtu transportSettings)
{
// Copy the managed session settings to an unmanaged structure.
SessionSettingsXcpV10Unmanaged sessionSettingsUnmanaged;
sessionSettingsUnmanaged.timeoutT1 = sessionSettings.timeoutT1;
sessionSettingsUnmanaged.timeoutT3 = sessionSettings.timeoutT3;
sessionSettingsUnmanaged.timeoutT4 = sessionSettings.timeoutT4;
sessionSettingsUnmanaged.timeoutT5 = sessionSettings.timeoutT5;
sessionSettingsUnmanaged.timeoutT6 = sessionSettings.timeoutT6;
sessionSettingsUnmanaged.timeoutT7 = sessionSettings.timeoutT7;
// Convert string to unmanged string.
sessionSettingsUnmanaged.seedKeyFile = (IntPtr)Marshal.StringToHGlobalAnsi(sessionSettings.seedKeyFile);
sessionSettingsUnmanaged.connectMode = sessionSettings.connectMode;
// Copy the managed transport settings to an unmanaged structure.
TransportSettingsXcpV10MbRtuUnmanaged transportSettingsUnmanaged;
// Convert string to unmanaged string.
transportSettingsUnmanaged.portName = (IntPtr)Marshal.StringToHGlobalAnsi(transportSettings.portName);
transportSettingsUnmanaged.baudrate = transportSettings.baudrate;
transportSettingsUnmanaged.parity = transportSettings.parity;
transportSettingsUnmanaged.stopbits = transportSettings.stopbits;
transportSettingsUnmanaged.destinationAddr = transportSettings.destinationAddr;
// The structures are now formatted to be converted to unmanaged memory. Start by allocating
// memory on the heap for this.
IntPtr sessionSettingsUnmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf(sessionSettingsUnmanaged));
IntPtr transportSettingsUnmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf(transportSettingsUnmanaged));
// Assert the heap allocations.
Debug.Assert(sessionSettingsUnmanaged.seedKeyFile != IntPtr.Zero);
Debug.Assert(transportSettingsUnmanaged.portName != IntPtr.Zero);
Debug.Assert(sessionSettingsUnmanagedPtr != IntPtr.Zero);
Debug.Assert(transportSettingsUnmanagedPtr != IntPtr.Zero);
// Only continue if all the heap allocations were successful.
if ((sessionSettingsUnmanaged.seedKeyFile != IntPtr.Zero) &&
(transportSettingsUnmanaged.portName != IntPtr.Zero) &&
(sessionSettingsUnmanagedPtr != IntPtr.Zero) &&
(transportSettingsUnmanagedPtr != IntPtr.Zero))
{
// Copy the structures to unmanaged memory.
Marshal.StructureToPtr(sessionSettingsUnmanaged, sessionSettingsUnmanagedPtr, false);
Marshal.StructureToPtr(transportSettingsUnmanaged, transportSettingsUnmanagedPtr, false);
// Call the API function inside the DLL.
BltSessionInit(SESSION_XCP_V10, sessionSettingsUnmanagedPtr, TRANSPORT_XCP_V10_MBRTU, transportSettingsUnmanagedPtr);
// Free memory allocated on the heap.
Marshal.FreeHGlobal(transportSettingsUnmanagedPtr);
Marshal.FreeHGlobal(sessionSettingsUnmanagedPtr);
Marshal.FreeHGlobal(transportSettingsUnmanaged.portName);
Marshal.FreeHGlobal(sessionSettingsUnmanaged.seedKeyFile);
}
}
[DllImport(LIBNAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern void BltSessionTerminate();

View File

@ -77,6 +77,9 @@ const
BLT_TRANSPORT_XCP_V10_USB: LongWord = 2;
// Transport layer for the XCP v1.0 protocol that uses TCP/IP for data exchange.
BLT_TRANSPORT_XCP_V10_NET: LongWord = 3;
// Transport layer for the XCP v1.0 protocol that uses Modbus RTU communication for
// data exchange.
BLT_TRANSPORT_XCP_V10_MBRTU: LongWord = 4;
type
@ -114,6 +117,15 @@ type
port: Word; // TCP port to use.
end;
// Structure layout of the XCP version 1.0 Modbus RTU transport layer settings.
tBltTransportSettingsXcpV10MbRtu = record
portName: PAnsiChar; // Communication port name such as /dev/tty0.
baudrate: LongWord; // Communication speed in bits/sec.
parity: Byte; // Parity (0 for none, 1 for odd, 2 for even).
stopbits: Byte; // Stopbits (1 for one, 2 for two stopbits).
destinationAddr: Byte; // Destination address (receiver node ID).
end;
procedure BltSessionInit(sessionType: LongWord;
sessionSettings: Pointer;

View File

@ -52,10 +52,12 @@ from openblt.lib import BLT_TRANSPORT_XCP_V10_RS232
from openblt.lib import BLT_TRANSPORT_XCP_V10_CAN
from openblt.lib import BLT_TRANSPORT_XCP_V10_USB
from openblt.lib import BLT_TRANSPORT_XCP_V10_NET
from openblt.lib import BLT_TRANSPORT_XCP_V10_MBRTU
from openblt.lib import BltSessionSettingsXcpV10
from openblt.lib import BltTransportSettingsXcpV10Rs232
from openblt.lib import BltTransportSettingsXcpV10Can
from openblt.lib import BltTransportSettingsXcpV10Net
from openblt.lib import BltTransportSettingsXcpV10MbRtu
from openblt.lib import session_init
from openblt.lib import session_terminate
from openblt.lib import session_start

View File

@ -816,6 +816,7 @@ BLT_TRANSPORT_XCP_V10_RS232 = 0
BLT_TRANSPORT_XCP_V10_CAN = 1
BLT_TRANSPORT_XCP_V10_USB = 2
BLT_TRANSPORT_XCP_V10_NET = 3
BLT_TRANSPORT_XCP_V10_MBRTU = 4
# ***************************************************************************************
@ -901,6 +902,25 @@ class BltTransportSettingsXcpV10Net:
self.port = 1000 # TCP port to use.
class BltTransportSettingsXcpV10MbRtu:
"""
Class with the layout of the XCP version 1.0 Modbus RTU transport layer settings. The
portName field is platform dependent. On Linux based systems this should be the
filename of the tty-device, such as "/dev/tty0". On Windows based systems it should
be the name of the COM-port, such as "COM1".
"""
def __init__(self):
"""
Class constructor.
"""
# Set default values for instance variables.
self.portName = '' # Communication port name such as /dev/tty0.
self.baudrate = 57600 # Communication speed in bits/sec.
self.parity = 2 # Parity (0 for none, 1 for odd, 2 for even).
self.stopbits = 1 # Stopbits (1 for one, 2 for two stopbits).
self.destinationAddr = 1 # Destination address (receiver node ID).
# ***************************************************************************************
# Functions
# ***************************************************************************************
@ -979,6 +999,16 @@ def session_init(session_type, session_settings, transport_type, transport_setti
_fields_ = [('address', ctypes.c_char_p),
('port', ctypes.c_uint16)]
class struct_t_blt_transport_settings_xcp_v10_mbrtu(ctypes.Structure):
"""
C-types structure for mapping to BltTransportSettingsXcpV10MbRtu
"""
_fields_ = [('portName', ctypes.c_char_p),
('baudrate', ctypes.c_uint32),
('parity', ctypes.c_uint8),
('stopbits', ctypes.c_uint8),
('destinationAddr', ctypes.c_uint8)]
# Convert session settings to the correct c-types structure.
session_settings_struct = None
if session_type == BLT_SESSION_XCP_V10:
@ -1021,6 +1051,14 @@ def session_init(session_type, session_settings, transport_type, transport_setti
ctypes.c_char_p(transport_settings.address.encode('utf-8'))
transport_settings_struct.port = \
ctypes.c_uint16(transport_settings.port)
elif transport_type == BLT_TRANSPORT_XCP_V10_MBRTU:
transport_settings_struct = struct_t_blt_transport_settings_xcp_v10_mbrtu()
transport_settings_struct.portName = \
ctypes.c_char_p(transport_settings.portName.encode('utf-8'))
transport_settings_struct.baudrate = ctypes.c_uint32(transport_settings.baudrate)
transport_settings_struct.parity = ctypes.c_uint8(transport_settings.parity)
transport_settings_struct.stopbits = ctypes.c_uint8(transport_settings.stopbits)
transport_settings_struct.destinationAddr = ctypes.c_uint8(transport_settings.destinationAddr)
# Check if the shared library function could be imported.
if BltSessionInit is not None:

View File

@ -39,6 +39,7 @@
#include "session.h" /* Communication session module */
#include "xcploader.h" /* XCP loader module */
#include "xcptpuart.h" /* XCP UART transport layer */
#include "xcptpmbrtu.h" /* XCP Modbus RTU transport layer */
#include "xcptpcan.h" /* XCP CAN transport layer */
#include "xcptpusb.h" /* XCP USB transport layer */
#include "xcptpnet.h" /* XCP TCP/IP transport layer */
@ -119,6 +120,7 @@ LIBOPENBLT_EXPORT void BltSessionInit(uint32_t sessionType,
*/
assert(sessionType == BLT_SESSION_XCP_V10);
assert( (transportType == BLT_TRANSPORT_XCP_V10_RS232) || \
(transportType == BLT_TRANSPORT_XCP_V10_MBRTU) || \
(transportType == BLT_TRANSPORT_XCP_V10_CAN) || \
(transportType == BLT_TRANSPORT_XCP_V10_USB) || \
(transportType == BLT_TRANSPORT_XCP_V10_NET) );
@ -236,6 +238,35 @@ LIBOPENBLT_EXPORT void BltSessionInit(uint32_t sessionType,
xcpLoaderSettings.transport = XcpTpNetGetTransport();
}
}
else if (transportType == BLT_TRANSPORT_XCP_V10_MBRTU)
{
/* Verify transportSettings parameters because the XCP Modbus RTU transport layer
* requires them.
*/
assert(transportSettings != NULL);
/* Only continue if the transportSettings parameter is valid. */
if (transportSettings != NULL) /*lint !e774 */
{
/* Cast transport settings to the correct type. */
tBltTransportSettingsXcpV10MbRtu * bltTransportSettingsXcpV10MbRtuPtr;
bltTransportSettingsXcpV10MbRtuPtr =
(tBltTransportSettingsXcpV10MbRtu * )transportSettings;
/* Convert transport settings to the format supported by the XCP Modbus RTU
* transport layer. It was made static to make sure it doesn't get out of scope
* when used in xcpLoaderSettings.
*/
static tXcpTpMbRtuSettings xcpTpMbRtuSettings;
xcpTpMbRtuSettings.baudrate = bltTransportSettingsXcpV10MbRtuPtr->baudrate;
xcpTpMbRtuSettings.portname = bltTransportSettingsXcpV10MbRtuPtr->portName;
xcpTpMbRtuSettings.parity = bltTransportSettingsXcpV10MbRtuPtr->parity;
xcpTpMbRtuSettings.stopbits = bltTransportSettingsXcpV10MbRtuPtr->stopbits;
xcpTpMbRtuSettings.destinationAddr = bltTransportSettingsXcpV10MbRtuPtr->destinationAddr;
/* Store transport layer settings in the XCP loader settings. */
xcpLoaderSettings.transportSettings = &xcpTpMbRtuSettings;
/* Link the transport layer to the XCP loader settings. */
xcpLoaderSettings.transport = XcpTpMbRtuGetTransport();
}
}
/* Perform actual session initialization. */
SessionInit(XcpLoaderGetProtocol(), &xcpLoaderSettings);
}

View File

@ -113,6 +113,11 @@ LIBOPENBLT_EXPORT char const * BltVersionGetString(void);
*/
#define BLT_TRANSPORT_XCP_V10_NET ((uint32_t)3u)
/** \brief Transport layer for the XCP v1.0 protocol that uses Modbus RTU
* communication for data exchange.
*/
#define BLT_TRANSPORT_XCP_V10_MBRTU ((uint32_t)4u)
/****************************************************************************************
* Type definitions
@ -177,6 +182,20 @@ typedef struct t_blt_transport_settings_xcp_v10_net
uint16_t port; /**< TCP port to use. */
} tBltTransportSettingsXcpV10Net;
/** \brief Structure layout of the XCP version 1.0 Modbus RTU transport layer settings.
* The portName field is platform dependent. On Linux based systems this should
* be the filename of the tty-device, such as "/dev/tty0". On Windows based
* systems it should be the name of the COM-port, such as "COM1".
*/
typedef struct t_blt_transport_settings_xcp_v10_mbrtu
{
char const * portName; /**< Communication port name such as /dev/tty0. */
uint32_t baudrate; /**< Communication speed in bits/sec. */
uint8_t parity; /**< Parity (0 for none, 1 for odd, 2 for even). */
uint8_t stopbits; /**< Stopbits (1 for one, 2 for two stopbits). */
uint8_t destinationAddr; /**< Destination address (receiver node ID). */
} tBltTransportSettingsXcpV10MbRtu;
/****************************************************************************************
* Function prototypes

View File

@ -93,14 +93,16 @@ void SerialPortTerminate(void)
/************************************************************************************//**
** \brief Opens the connection with the serial port configured as 8,N,1 and no flow
** control.
** \brief Opens the connection with the serial port.
** \param portname The name of the serial port to open, i.e. /dev/ttyUSB0.
** \param baudrate The desired communication speed.
** \param parity The desired parity configuration.
** \param stopbits The desired stop bits configuration.
** \return True if successful, false otherwise.
**
****************************************************************************************/
bool SerialPortOpen(char const * portname, tSerialPortBaudrate baudrate)
bool SerialPortOpen(char const* portname, tSerialPortBaudrate baudrate,
tSerialPortParity parity, tSerialPortStopbits stopbits)
{
bool result = false;
struct termios options = { 0 };
@ -168,8 +170,28 @@ bool SerialPortOpen(char const * portname, tSerialPortBaudrate baudrate)
options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* Output modes - clear giving: no post processing such as NL to CR+NL. */
options.c_oflag &= ~(OPOST);
/* Control modes - set 8 bit chars */
/* Control modes - reset databits and parity configuration. */
options.c_cflag &= ~(CSIZE | PARENB | PARODD);
/* Control modes - set 8 bit chars. */
options.c_cflag |= (CS8);
/* Control modes - set stop bits. Default is 1 stop bit. */
if (stopbits == SERIALPORT_STOPBITS2)
{
options.c_cflag |= (CSTOPB);
}
/* Control modes - set parity. Default is no parity. */
if (parity == SERIALPORT_PARITY_ODD)
{
options.c_cflag |= (PARENB | PARODD); /* Odd parity for I/O.*/
options.c_iflag |= (INPCK); /* Enable input parity check. */
}
else if (parity == SERIALPORT_PARITY_EVEN)
{
options.c_cflag |= (PARENB); /* Even parity for I/O.*/
options.c_iflag |= (INPCK); /* Enable input parity check. */
}
/* Local modes - clear giving: echoing off, canonical off (no erase with
* backspace, ^U,...), no extended functions, no signal chars (^Z,^C).
*/

View File

@ -80,14 +80,16 @@ void SerialPortTerminate(void)
/************************************************************************************//**
** \brief Opens the connection with the serial port configured as 8,N,1 and no flow
** control.
** \brief Opens the connection with the serial port.
** \param portname The name of the serial port to open, i.e. COM4.
** \param baudrate The desired communication speed.
** \param parity The desired parity configuration.
** \param stopbits The desired stop bits configuration.
** \return True if successful, false otherwise.
**
****************************************************************************************/
bool SerialPortOpen(char const * portname, tSerialPortBaudrate baudrate)
bool SerialPortOpen(char const* portname, tSerialPortBaudrate baudrate,
tSerialPortParity parity, tSerialPortStopbits stopbits)
{
bool result = false;
COMMTIMEOUTS timeouts = { 0 };
@ -132,8 +134,34 @@ bool SerialPortOpen(char const * portname, tSerialPortBaudrate baudrate)
{
dcbSerialParams.BaudRate = SerialConvertBaudrate(baudrate);
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if (stopbits == SERIALPORT_STOPBITS1)
{
dcbSerialParams.StopBits = ONESTOPBIT;
}
else
{
dcbSerialParams.StopBits = TWOSTOPBITS;
}
dcbSerialParams.ErrorChar = '\0';
if (parity == SERIALPORT_PARITY_NONE)
{
dcbSerialParams.Parity = NOPARITY;
dcbSerialParams.fParity = FALSE;
dcbSerialParams.fErrorChar = FALSE;
}
else if (parity == SERIALPORT_PARITY_ODD)
{
dcbSerialParams.Parity = ODDPARITY;
dcbSerialParams.fParity = TRUE;
dcbSerialParams.fErrorChar = TRUE;
}
else
{
dcbSerialParams.Parity = EVENPARITY;
dcbSerialParams.fParity = TRUE;
dcbSerialParams.fErrorChar = TRUE;
}
dcbSerialParams.fOutX = FALSE;
dcbSerialParams.fInX = FALSE;
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;

View File

@ -50,13 +50,29 @@ typedef enum
SERIALPORT_BR115200 = 4 /**< 115200 bits/sec */
} tSerialPortBaudrate;
/** \brief Enumeration of the supported parities. */
typedef enum
{
SERIALPORT_PARITY_NONE = 0, /**< no parity bit */
SERIALPORT_PARITY_ODD = 1, /**< odd parity bit */
SERIALPORT_PARITY_EVEN = 2 /**< even parity bit */
} tSerialPortParity;
/** \brief Enumeration of the supported stop bits. */
typedef enum
{
SERIALPORT_STOPBITS1 = 0, /**< 1 stop bit */
SERIALPORT_STOPBITS2 = 1 /**< 2 stop bits */
} tSerialPortStopbits;
/****************************************************************************************
* Function prototypes
****************************************************************************************/
void SerialPortInit(void);
void SerialPortTerminate(void);
bool SerialPortOpen(char const * portname, tSerialPortBaudrate baudrate);
bool SerialPortOpen(char const * portname, tSerialPortBaudrate baudrate,
tSerialPortParity parity, tSerialPortStopbits stopbits);
void SerialPortClose(void);
bool SerialPortWrite(uint8_t const * data, uint32_t length);
bool SerialPortRead(uint8_t * data, uint32_t length);

View File

@ -0,0 +1,635 @@
/************************************************************************************//**
* \file xcptpmbrtu.c
* \brief XCP Modbus RTU transport layer source file.
* \ingroup XcpTpMbRtu
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2022 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include <assert.h> /* for assertions */
#include <stdint.h> /* for standard integer types */
#include <stddef.h> /* for NULL declaration */
#include <stdbool.h> /* for boolean type */
#include <stdlib.h> /* for standard library */
#include <string.h> /* for string library */
#include "session.h" /* Communication session module */
#include "xcploader.h" /* XCP loader module */
#include "xcptpmbrtu.h" /* XCP UART transport layer */
#include "util.h" /* Utility module */
#include "serialport.h" /* Serial port module */
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief This module embeds the bootloader's XCP communication packets inside Modbus
* RTU communication packets. As this is a non-standard functionality of Modbus,
* a user-defined function code is used to extend the Modbus functionality for
* the purpose of embedded XCP packets. User-defined function codes are allowed,
* as long as they are in the range 65..72 or 100..110. By default, the user-
* defined function code 109 is selected. Note that some other Modbus device on
* the network might also already use this. In this case set this macro to
* another unique user-defined function code.
*/
#define XCP_TP_MBRTU_FCT_CODE_USER_XCP (109u)
/****************************************************************************************
* Function prototypes
****************************************************************************************/
static void XcpTpMbRtuInit(void const * settings);
static void XcpTpMbRtuTerminate(void);
static bool XcpTpMbRtuConnect(void);
static void XcpTpMbRtuDisconnect(void);
static bool XcpTpMbRtuSendPacket(tXcpTransportPacket const * txPacket,
tXcpTransportPacket * rxPacket, uint16_t timeout);
static uint16_t XcpTpMbRtuCrcCalculate(uint8_t const * data, uint16_t len);
/****************************************************************************************
* Local constant declarations
****************************************************************************************/
/** \brief XCP transport layer structure filled with Modbus RTU specifics. */
static const tXcpTransport mbRtuTransport =
{
XcpTpMbRtuInit,
XcpTpMbRtuTerminate,
XcpTpMbRtuConnect,
XcpTpMbRtuDisconnect,
XcpTpMbRtuSendPacket
};
/****************************************************************************************
* Local data declarations
****************************************************************************************/
/** \brief The settings to use in this transport layer. */
static tXcpTpMbRtuSettings tpMbRtuSettings;
/** \brief Modbus RTU 3.5 character time in milliseconds. In the baudrate is > 19200
* bits/sec the T3_5 can be fixed at 1750 us. This is 2 ms when rounded to
* milliseconds. Use this as the initial value and adjust later on in case of a
* baudrate <= 19200 bps. Make sure to add 1 ms to adjust for millisecond
* resolution inaccuracy.
*/
static uint32_t tpMbRtuT3_5Ms = 2 + 1;
/***********************************************************************************//**
** \brief Obtains a pointer to the transport layer structure, so that it can be
** linked to the XCP protocol module.
** \return Pointer to transport layer structure.
**
****************************************************************************************/
tXcpTransport const * XcpTpMbRtuGetTransport(void)
{
return &mbRtuTransport;
} /*** end of XcpTpMbRtuGetTransport ***/
/************************************************************************************//**
** \brief Initializes the transport layer.
** \param settings Pointer to settings structure.
** \return None.
**
****************************************************************************************/
static void XcpTpMbRtuInit(void const * settings)
{
char * mbRtuPortName;
/* Reset transport layer settings. */
tpMbRtuSettings.portname = NULL;
tpMbRtuSettings.baudrate = 0;
tpMbRtuSettings.stopbits = 1; /* Default 1 stopbit according to Modbus protocol. */
tpMbRtuSettings.parity = 2; /* Default even parity according to Modbus protocol. */
tpMbRtuSettings.destinationAddr = 1;
/* Check parameters. */
assert(settings != NULL);
/* Only continue with valid parameters. */
if (settings != NULL) /*lint !e774 */
{
/* Shallow copy the transport layer settings for layer usage. */
tpMbRtuSettings = *((tXcpTpMbRtuSettings *)settings);
/* The portname is a pointer and it is not guaranteed that it stays valid so we need
* to deep copy this one. note the +1 for '\0' in malloc.
*/
assert(((tXcpTpMbRtuSettings *)settings)->portname != NULL);
if (((tXcpTpMbRtuSettings *)settings)->portname != NULL) /*lint !e774 */
{
mbRtuPortName = malloc(strlen(((tXcpTpMbRtuSettings *)settings)->portname) + 1);
assert(mbRtuPortName != NULL);
if (mbRtuPortName != NULL) /*lint !e774 */
{
strcpy(mbRtuPortName, ((tXcpTpMbRtuSettings *)settings)->portname);
tpMbRtuSettings.portname = mbRtuPortName;
}
}
}
/* Initialize the serial port. */
SerialPortInit();
/* Recalculate the 3.5 character time, if needed. */
if (tpMbRtuSettings.baudrate <= 19200)
{
/* T3_5 [ms] = 3.5 * Tchar = 3.5 * 11 * 1000 / baudrate = 38500 / baudrate.
* Make sure to do integer round up though. Make sure to add 1 ms to adjust for
* millisecond resolution inaccuracy.
*/
tpMbRtuT3_5Ms = ((38500 + (tpMbRtuSettings.baudrate - 1)) / tpMbRtuSettings.baudrate) + 1;
}
} /*** end of XcpTpMbRtuInit ***/
/************************************************************************************//**
** \brief Terminates the transport layer.
**
****************************************************************************************/
static void XcpTpMbRtuTerminate(void)
{
/* Terminate the serial port. */
SerialPortTerminate();
/* Release memory that was allocated for storing the port name. */
if (tpMbRtuSettings.portname != NULL)
{
free((char *)tpMbRtuSettings.portname);
}
/* Reset transport layer settings. */
tpMbRtuSettings.portname = NULL;
tpMbRtuSettings.baudrate = 0;
tpMbRtuSettings.stopbits = 1; /* Default 1 stopbit according to Modbus protocol. */
tpMbRtuSettings.parity = 2; /* Default even parity according to Modbus protocol. */
tpMbRtuSettings.destinationAddr = 1;
} /*** end of XcpTpUartTerminate ***/
/************************************************************************************//**
** \brief Connects to the transport layer.
** \return True is connected, false otherwise.
**
****************************************************************************************/
static bool XcpTpMbRtuConnect(void)
{
bool result = false;
bool baudrateSupported;
tSerialPortBaudrate baudrate;
uint32_t idleTimeoutTime;
uint32_t waitTimeoutTime;
uint32_t currentTime;
uint8_t rxByte = 0;
tSerialPortParity parity;
tSerialPortStopbits stopbits;
/* Check if the specified baudrate is supported by the serial port driver. */
baudrateSupported = (tpMbRtuSettings.baudrate == 9600) ||
(tpMbRtuSettings.baudrate == 19200) ||
(tpMbRtuSettings.baudrate == 38400) ||
(tpMbRtuSettings.baudrate == 57600) ||
(tpMbRtuSettings.baudrate == 115200);
/* Check transport layer settings. */
assert(tpMbRtuSettings.portname != NULL);
assert(baudrateSupported);
assert(tpMbRtuSettings.parity <= 2);
assert((tpMbRtuSettings.stopbits >= 1) && (tpMbRtuSettings.stopbits <= 2));
assert((tpMbRtuSettings.destinationAddr >= 1) && (tpMbRtuSettings.destinationAddr <= 247));
/* Only continue if the transport layer settings are valid. */
if ( (tpMbRtuSettings.portname != NULL) && (baudrateSupported) &&
(tpMbRtuSettings.parity <= 2) &&
((tpMbRtuSettings.stopbits >= 1) && (tpMbRtuSettings.stopbits <= 2)) &&
((tpMbRtuSettings.destinationAddr >= 1) && (tpMbRtuSettings.destinationAddr <= 247)) ) /*lint !e774 */
{
/* Convert the numeric baudrate to the one supported by the serial port driver. */
switch (tpMbRtuSettings.baudrate)
{
case 115200:
baudrate = SERIALPORT_BR115200;
break;
case 57600:
baudrate = SERIALPORT_BR57600;
break;
case 38400:
baudrate = SERIALPORT_BR38400;
break;
case 19200:
baudrate = SERIALPORT_BR19200;
break;
case 9600:
default:
baudrate = SERIALPORT_BR9600;
break;
}
/* Set parity value. */
switch (tpMbRtuSettings.parity)
{
case 0:
parity = SERIALPORT_PARITY_NONE;
break;
case 1:
parity = SERIALPORT_PARITY_ODD;
break;
case 2:
default:
parity = SERIALPORT_PARITY_EVEN;
break;
}
/* Set stopbits value. */
switch (tpMbRtuSettings.stopbits)
{
case 1:
default:
stopbits = SERIALPORT_STOPBITS1;
break;
case 2:
stopbits = SERIALPORT_STOPBITS2;
break;
}
/* Connect to the serial port. */
result = SerialPortOpen(tpMbRtuSettings.portname, baudrate, parity, stopbits);
}
/* Only continue if successfully connected to the serial port. */
if (result)
{
/* On Modbus RTU you need to guarantee that there is T3_5 idle time before starting
* a new packet transmission or reception. Therefore we need to wait for an idle
* state on the communication line here.
*/
currentTime = UtilTimeGetSystemTimeMs();
idleTimeoutTime = currentTime + tpMbRtuT3_5Ms;
/* Add a second timeout to make sure this logic doesn't hang, in case the serial
* line is flooded with data for some weird unexpected reason. It really shouldn't
* take more a few hundred milliseconds for the idle line to occur.
*/
waitTimeoutTime = currentTime + 500;
/* Poll for T3_5 timeout, but restart when a character was received. */
do
{
/* Get the current system time. */
currentTime = UtilTimeGetSystemTimeMs();
/* Check if a byte was received. */
if (SerialPortRead(&rxByte, 1))
{
/* Transmission line not idle, so restart the 3.5 character timeout. */
idleTimeoutTime = currentTime + tpMbRtuT3_5Ms;
}
/* Check if an overall timeout occurred. */
if (currentTime >= waitTimeoutTime)
{
/* Could not detect an idle communication line. Flag the error, disconnected
* the serial port again and stop the loop.
*/
result = false;
SerialPortClose();
break;
}
}
while (currentTime < idleTimeoutTime);
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpTpMbRtuConnect ***/
/************************************************************************************//**
** \brief Disconnects from the transport layer.
**
****************************************************************************************/
static void XcpTpMbRtuDisconnect(void)
{
/* Disconnect from the serial port. */
SerialPortClose();
} /*** end of XcpTpMbRtuDisconnect ***/
/************************************************************************************//**
** \brief Transmits an XCP packet on the transport layer and attempts to receive the
** response packet within the specified timeout.
** \param txPacket Pointer to the packet to transmit.
** \param rxPacket Pointer where the received packet info is stored.
** \param timeout Maximum time in milliseconds to wait for the reception of the
** response packet.
** \return True is successful and a response packet was received, false otherwise.
**
****************************************************************************************/
static bool XcpTpMbRtuSendPacket(tXcpTransportPacket const * txPacket,
tXcpTransportPacket * rxPacket, uint16_t timeout)
{
bool result = false;
uint32_t idleTimeoutTime;
uint32_t waitTimeoutTime = 0;
uint32_t currentTime;
uint16_t byteIdx;
uint16_t checksumCalculated;
uint16_t checksumReceived;
uint16_t rxModbusPacketLen = 0;
/* static to lower the stack load. +4 because of Modbus RTU packet overhead for
* slave address, function code and 16-bit CRC.
*/
static uint8_t mbRtuBuffer[XCPLOADER_PACKET_SIZE_MAX + 5];
/* On Modbus RTU, there must always be a T3_5 time separation between packets. Note
* that an idle line detection was already performed, right after connecting to
* the serial port.
*
* This bootloader uses XCP packets embedded in Modbus RTU packets. XCP is used in a
* request / response manner, where no new request is sent before either a response
* was received or a timeout occurred, waiting for the response to come it.
*
* Theoretically both scenarios already guarantee the T3_5 idle time on the
* communication link. However, the detection of the T3_5 end-of-packet time on a
* non real-time OS such as Windows and Linux proves a bit troublesome. To get
* this reliable, you would have to extend this time significantly (50 - 100 ms),
* because the standard T3_5 is sometimes already detected between bytes of
* the packet itself. Simple because the SerialPort driver doesn't give you a
* real-time timestamp of a received byte.
*
* For this reason, this module does not implement the T3_5 end-of-packet time event.
* Instead if uses length information embedded in the XCP on Modbus RTU packet to
* determine if the packet was fully received.
*
* Long story short: Before a new packet transmission, the T3_5 time should be added
* to guarantee the T3_5 separation time between the last successful packet reception
* and the next transmission.
*/
/* Check parameters. */
assert(txPacket != NULL);
assert(rxPacket != NULL);
/* Only continue with valid parameters. */
if ((txPacket != NULL) && (rxPacket != NULL)) /*lint !e774 */
{
/* Set result value to okay and only change it from now on if an error occurred. */
result = true;
/* ------------ Guarantee idle time ---------------------------------------------- */
/* Poll for T3_5 timeout. */
idleTimeoutTime = UtilTimeGetSystemTimeMs() + tpMbRtuT3_5Ms;
do
{
/* Get the current system time. */
currentTime = UtilTimeGetSystemTimeMs();
}
while (currentTime < idleTimeoutTime);
/* ------------ Packet transmission ---------------------------------------------- */
/* Construct the Modbus RTU packet. Start by adding the slave address. */
mbRtuBuffer[0] = tpMbRtuSettings.destinationAddr;
/* Add the user-defined function code for embedding XCP packets. */
mbRtuBuffer[1] = XCP_TP_MBRTU_FCT_CODE_USER_XCP;
/* add the XCP packet length. */
mbRtuBuffer[2] = txPacket->len;
/* Copy the XCP packet data. */
for (byteIdx = 0; byteIdx < txPacket->len; byteIdx++)
{
mbRtuBuffer[byteIdx + 3] = txPacket->data[byteIdx];
}
/* Calculate the checksum for the packet, including slave address, function code and
* extra XCP length.
*/
checksumCalculated = XcpTpMbRtuCrcCalculate(&mbRtuBuffer[0], txPacket->len + 3);
/* add the checksum at the end of the packet */
mbRtuBuffer[txPacket->len + 3] = (uint8_t)(checksumCalculated & 0xff);
mbRtuBuffer[txPacket->len + 4] = (uint8_t)(checksumCalculated >> 8);
/* Transmit the packet. */
if (!SerialPortWrite(&mbRtuBuffer[0], txPacket->len + 5))
{
result = false;
}
/* ------------ Packet reception ------------------------------------------------- */
/* Only continue if the transmission was successful. */
if (result)
{
/* Determine timeout time for the response packet. */
waitTimeoutTime = UtilTimeGetSystemTimeMs() + timeout;
/* Reset the indexer used for storing the received data. */
rxModbusPacketLen = 0;
/* Poll with timeout detection to receive the header of the XCP on Modbus RTU
* response packet. This is everything up to the actual XCP packet data. The
* header includes the slave address, function code and extra XCP length.
*/
while ((UtilTimeGetSystemTimeMs() < waitTimeoutTime) && (rxModbusPacketLen < 3))
{
/* Attempt to receive a new byte. */
if (SerialPortRead(&mbRtuBuffer[rxModbusPacketLen], 1))
{
/* Continue with the next byte. */
rxModbusPacketLen++;
}
}
/* Check if the entire packet header was received. */
if (rxModbusPacketLen != 3)
{
/* Packet header not completely received, so a timeout must have occured. */
result = false;
}
}
/* Only continue if a complete packet header was received. */
if (result)
{
/* Validate the header and make sure the buffer is large enough to store the entire packet. */
if ( (mbRtuBuffer[0] != tpMbRtuSettings.destinationAddr) ||
(mbRtuBuffer[1] != XCP_TP_MBRTU_FCT_CODE_USER_XCP) ||
(mbRtuBuffer[2] == 0) ||
(((uint16_t)mbRtuBuffer[2] + 5U) > (sizeof(mbRtuBuffer) / sizeof(mbRtuBuffer[0]))) )
{
/* The header is either invalid or it is not an XCP response packet for us. */
result = false;
}
}
/* Only continue with reception if a valid valid packet header was received. */
if (result)
{
/* Sanity check. */
assert(rxModbusPacketLen == 3);
/* Store the XCP packet length since we know that the packet header is valid. */
rxPacket->len = mbRtuBuffer[2];
/* Poll with timeout detection to receive the remainder of the response
* packet. This is the XCP packet data and 16-byte CRC.
*/
while ((UtilTimeGetSystemTimeMs() < waitTimeoutTime) && (rxModbusPacketLen < (3 + rxPacket->len + 2)))
{
/* Attempt to receive a new byte. */
if (SerialPortRead(&mbRtuBuffer[rxModbusPacketLen], 1))
{
/* Continue with the next byte. */
rxModbusPacketLen++;
}
}
/* Check if the entire packet remainder was received. */
if (rxModbusPacketLen != (3 + rxPacket->len + 2))
{
/* packet remainder not completely received, so a timeout must have occured. */
result = false;
}
}
/* Now validate the complete packet. */
if (result)
{
/* Calculate the packet checksum. */
checksumCalculated = XcpTpMbRtuCrcCalculate(&mbRtuBuffer[0], rxModbusPacketLen - 2);
/* Extract the checksum received with the packet. */
checksumReceived = mbRtuBuffer[rxModbusPacketLen - 2] | (mbRtuBuffer[rxModbusPacketLen - 1] << 8);
/* Check if the CRC is correct. */
if (checksumCalculated != checksumReceived)
{
result = false;
}
}
/* The packet was for us, is valid and contains an XCP response packet. As a final
* step, copy the XCP packet data.
*/
if (result)
{
for (byteIdx = 0; byteIdx < rxPacket->len; byteIdx++)
{
rxPacket->data[byteIdx] = mbRtuBuffer[byteIdx + 3];
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpTpMbRtuSendPacket ***/
/****************************************************************************************
* C R C G E N E R A T I O N
****************************************************************************************/
/****************************************************************************************
* Local constant declarations
****************************************************************************************/
/** \brief Table of CRC values for high-order byte. */
static uint8_t CRCHi[] =
{
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
/** \brief Table of CRC values for low-order byte. */
static uint8_t CRCLo[] =
{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA