Porting for RTU/ASCII

The first steps should always be to create a new directory for the port. The recommended layout is to create a top level directory, e.g. demo/PLATFORM which hold the application and project files. In addition a subdirectory port should be created for the port specific files.
demo/PLATFORM/Makefile
demo/PLATFORM/main.c
demo/PLATFORM/port/portserial.c
demo/PLATFORM/port/porttimer.c
demo/PLATFORM/port/portother.c
demo/PLATFORM/port/port.h
You can use demo/BARE as a starting point. Simply copy the directory and rename it to a name of your choice.

Platform specifics (port.h)

You should first check the file port.h and check the if the examples are already suitable for your platform. You must at least define the macros for enabling ENTER_CRITICAL_SECTION and disabling EXIT_CRITICAL_SECTION interrupts.

Implementation of the timer functions (porttimer.c)

The Modbus protocol stacks needs a timer to detect the end of the frame. The timers should have a resolution of half the time of a serial character. For example for 38400 baud the character time is approx. 280us assuming 11bits for a single character. The smallest timeout used by the protocol stack is 3.5 times the character timeout.

You should start by implementing the function xMBPortTimersInit( USHORT usTim1Timerout50us ) and vMBPortTimersEnable( ). Test the function with the following sample code:

xMBPortTimersInit( 20 );
vMBPortTimersEnable( );
for( ;; );
Place a breakpoint or toggle an LED in the interrupt handler which calls pxMBPortCBTimerExpired. The ISR should occur approx. 1ms after the call to vMBPortTimersEnable(). You should also check that vMBPortTimersDisable( ) works as expected.
Note:
If you use Modbus ASCII the timers are in the range of seconds because the timeouts are much larger there. Make sure you can handle a value of 20000 for usTim1Timerout50us which corresponds to an one second timeout. See mbconfig.h for the value of the timeout defined by MB_ASCII_TIMEOUT_SEC.

Porting for RTU/ASCII

The serial porting layer must be capable of initializing the UART, disabling and enabling the receiver and transmitter components as well as performing callbacks if a character has been received or can be transmitted. You should start by implementing xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) and vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ). In addition you need to create two interrupt service routines for you communication devices. It is usually simpler to start with the receive interrupt.

Create an interrupt handler for the receive interrupt, set a breakpoint there and check if xMBPortSerialGetByte( CHAR * pucByte ) correctly returns the character. This can be tested by the following code:

/* Initialize COM device 0 with 38400 baud, 8 data bits and no parity. */
if( xMBPortSerialInit( 0, 38400, 8, MB_PAR_NONE ) == FALSE )
{
  fprintf(stderr, "error: com init failed");
}
else
{
  /* Enable the receiver. */ 
  vMBPortSerialEnable( TRUE, FALSE );
  /* Now block. Any character received should cause an interrupt now. */
  for( ;; );
}
And your serial character received ISR should look like:
static void prvvUARTTxReadyISR( void )
{
    CHAR cByte;
    ( void )xMBPortSerialGetByte( &cByte );
    /* Now cByte should contain the character received. */
}

Next you should check that the transmitter part is actually working as expected. Open a terminal program and simply call xMBPortSerialPutByte( 'a' ) in the transmit buffer empty ISR. If you use the sample code from below exactly 10 characters should be received.

/* Initialize COM device 0 with 38400 baud, 8 data bits and no parity. */
if( xMBPortSerialInit( 0, 38400, 8, MB_PAR_NONE ) == FALSE )
{
  fprintf(stderr, "error: com init failed");
}
else
{
  /* Enable the transmitter. */ 
  vMBPortSerialEnable( FALSE, TRUE );
  /* Now block. Any character received should cause an interrupt now. */
  for( ;; );
}
And you serial transmit buffer empty ISR should look like:
static unsigned int uiCnt = 0;

void prvvUARTTxReadyISR( void )
{
    if( uiCnt++ < 10 )
    {
        ( void )xMBPortSerialPutByte( 'a' );
    }
    else
    {
        vMBPortSerialEnable( FALSE, FALSE );
    }
}

If you are sure everything works correctly change the interrupt routines back to the examples shown in portserial.c

Implementing the event queue (portevent.c)

If you are not using an operating system the port is already finished and the demo application should work as expected. If you in the luck of having an operating system usage of the FreeModbus protocol stack differs in the following way:

In addition the serial and timer interrupt function must be modified. Whenever the protocol handler callback functions pxMBFrameCBByteReceived, pxMBFrameCBTransmitterEmpty and pxMBPortCBTimerExpired return TRUE a context switch should be made after exiting the ISR because an event has been posted to the queue. Forgetting to do this will result in slow performance of the protocol stack.


Automatically generated by Doxygen 1.4.2 on 13 Sep 2018.