112 lines
6.0 KiB
Plaintext
112 lines
6.0 KiB
Plaintext
/*! \page sec_porting_serial 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. <tt>demo/PLATFORM</tt> which hold the application and project files. In addition a subdirectory <tt>port</tt> should be created for the port specific files.
|
|
\verbatim
|
|
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
|
|
\endverbatim
|
|
You can use <tt>demo/BARE</tt> as a starting point. Simply copy the directory and rename it to a name of your choice.
|
|
|
|
\section sec_porting_types Platform specifics (port.h)
|
|
You should first check the file <tt>port.h</tt> and check the if the examples are already suitable for your platform. You must at least define the macros for enabling <code>ENTER_CRITICAL_SECTION</code> and disabling <code>EXIT_CRITICAL_SECTION</code> interrupts.
|
|
|
|
\section sec_porting_timers 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.
|
|
<p>You should start by implementing the function <code>xMBPortTimersInit( USHORT usTim1Timerout50us )</code> and <code>vMBPortTimersEnable( )</code>. Test the function with the following sample code:
|
|
\code
|
|
xMBPortTimersInit( 20 );
|
|
vMBPortTimersEnable( );
|
|
for( ;; );
|
|
\endcode
|
|
Place a breakpoint or toggle an LED in the interrupt handler which calls <code>pxMBPortCBTimerExpired</code>. The ISR should occur approx. 1ms after the call to <code>vMBPortTimersEnable()</code>. You should also check that <code>vMBPortTimersDisable( )</code> works as expected.
|
|
</p>
|
|
\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 <code>usTim1Timerout50us</code> which corresponds to an one second timeout. See <tt>mbconfig.h</tt> for the value of the timeout defined by <code>MB_ASCII_TIMEOUT_SEC</code>.
|
|
|
|
\section sec_porting_serial Implementation of the serial functions (portserial.c)
|
|
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 <code>xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )</code> and <code>vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )</code>.
|
|
In addition you need to create two interrupt service routines for you communication devices. It is usually simpler to start with the receive interrupt.
|
|
<p>Create an interrupt handler for the receive interrupt, set a breakpoint there and check if <code>xMBPortSerialGetByte( CHAR * pucByte )</code> correctly returns the character. This can be tested by the following code:
|
|
\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( ;; );
|
|
}
|
|
\endcode
|
|
And your serial character received ISR should look like:
|
|
\code
|
|
static void prvvUARTTxReadyISR( void )
|
|
{
|
|
CHAR cByte;
|
|
( void )xMBPortSerialGetByte( &cByte );
|
|
/* Now cByte should contain the character received. */
|
|
}
|
|
\endcode
|
|
</p>
|
|
<p>
|
|
Next you should check that the transmitter part is actually working as expected. Open a terminal program and simply call <code>xMBPortSerialPutByte( 'a' )</code> in the transmit buffer empty ISR. If you use the sample code from below exactly 10 characters should be received.
|
|
\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 transmitter. */
|
|
vMBPortSerialEnable( FALSE, TRUE );
|
|
/* Now block. Any character received should cause an interrupt now. */
|
|
for( ;; );
|
|
}
|
|
\endcode
|
|
And you serial transmit buffer empty ISR should look like:
|
|
\code
|
|
static unsigned int uiCnt = 0;
|
|
|
|
void prvvUARTTxReadyISR( void )
|
|
{
|
|
if( uiCnt++ < 10 )
|
|
{
|
|
( void )xMBPortSerialPutByte( 'a' );
|
|
}
|
|
else
|
|
{
|
|
vMBPortSerialEnable( FALSE, FALSE );
|
|
}
|
|
}
|
|
\endcode
|
|
</p>
|
|
<p>
|
|
If you are sure everything works correctly change the interrupt routines back to the examples shown in <tt>portserial.c</tt>
|
|
</p>
|
|
|
|
\section sec_porting_event 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:
|
|
|
|
- Create another task at startup which calls eMBPoll( ) in a loop. This should look like:
|
|
\code
|
|
for( ;; )
|
|
{
|
|
( void )eMBPoll( );
|
|
}
|
|
\endcode
|
|
See the STR71x port for an FreeRTOS example.
|
|
|
|
- Change the function <code>xMBPortEventPost</code> to post an event to a queue. Note that this function will be called from an ISR so check your RTOS documentation for that.
|
|
|
|
- Change the <code>xMBPortEventGet</code> to retrieve an event from that queue. The function <code>eMBPoll</code> periodically calls it. The function should block until an event has been posted to the queue.
|
|
|
|
In addition the serial and timer interrupt function must be modified. Whenever the protocol handler callback functions <code>pxMBFrameCBByteReceived</code>, <code>pxMBFrameCBTransmitterEmpty</code> and <code>pxMBPortCBTimerExpired</code> return <code>TRUE</code> 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.
|
|
*/
|