mirror of https://github.com/rusefi/openblt.git
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:
parent
fb81509207
commit
073e3223a4
Binary file not shown.
Binary file not shown.
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 |