This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C123GH6PM: FreeModbus Implementation

Part Number: TM4C123GH6PM

Hi Everyone! 

I'm trying to implement the Freemodbus stack in my Tm4C123GH6PM  and  i've already make some tests but with no success.

Hope to get some help or advice thank you very much, here are the steps i've done

1.- First, i downloaded the freemodbus-v 1.4.0 folder and make the "changes" in the corresponding port folder, 

port.h


 

/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: port.h,v 1.1 2006/08/22 21:35:13 wolti Exp $
 */

#ifndef _PORT_H
#define _PORT_H
/* ----------------------- Platform includes --------------------------------*/
#include "IncTm4c123.h"
#include <assert.h>
#define	INLINE                      inline
#define PR_BEGIN_EXTERN_C           extern "C" {
#define	PR_END_EXTERN_C             }

#define ENTER_CRITICAL_SECTION( ) ROM_IntMasterDisable()   
#define EXIT_CRITICAL_SECTION( )  ROM_IntMasterEnable()

typedef uint8_t BOOL;

typedef unsigned char UCHAR;
typedef char CHAR;

typedef uint16_t USHORT;
typedef int16_t SHORT;

typedef uint32_t ULONG;
typedef int32_t LONG;

#ifndef TRUE
#define TRUE            1
#endif

#ifndef FALSE
#define FALSE           0
#endif

#endif

So, the .h file IncTm4c123.h is only a header file where i'm putting the corresponding header files to use the peripheral functions, i forgot to mention that i'm using TivaWare 2.1.4.178 and the IDE is IAR 8.0

The constants: ENTER_CRITICAL_SECTION & EXIT_CRITICAL_SECTION as i understand are used or needed to disable the interrupts on the device object so, i disable all the Processor Interrupts 


Following with the porting.dox file, the next file to change is 

porttimer.c

/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "IncTm4c123.h"


/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
//static void prvvTIMERExpiredISR( void );

/* ----------------------- my functions -------------------------------------*/
USHORT usMBMulDiv(USHORT a, USHORT b, USHORT c);

/* ----------------------- Persistent variable ------------------------------*/
USHORT usDelta;
/* ----------------------- Start implementation -----------------------------*/
USHORT usMBMulDiv(USHORT a, USHORT b, USHORT c){
	ULONG x;
	x = a;
	x *= b;
	x /= c;
return ( USHORT ) x;
}

//the time argument usTim1Timerout50us is in multiples of 50us
//so in order to calculate the timer ticks use the function usMBMulDiv 
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{

  ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
  ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_ONE_SHOT);//Full width 32bits and for freemodmus requirements Timer needs to be one-shot
//Define number to load in register  
usDelta = usMBMulDiv(usTim1Timerout50us, 800, 100);
ROM_IntMasterEnable();//enable all processor int
  ROM_IntEnable(INT_TIMER0A);//inc/hw_ints.h header defines
  ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);//select timer source// TIMEOUT interrupt

 
  
  
    return TRUE;
}


void
vMBPortTimersEnable(  )//only starts the timer 
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, usDelta);
ROM_TimerEnable(TIMER0_BASE, TIMER_A);//enable timer
ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
  ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
}

void
vMBPortTimersDisable(  )
{
    /* Disable any pending timers. */
	ROM_TimerDisable(TIMER0_BASE,TIMER_A);		
	ROM_TimerIntDisable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
	ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

}

/* Create an ISR which is called whenever the timer has expired. This function
 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
 * the timer has expired.
 */
void Timer0IntHandler( void )
{

    ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    ROM_TimerIntDisable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    ( void )pxMBPortCBTimerExpired(  );
}

I've test this part with the code sugested:

xMBPortTimersInit(20);
vMBPortTimersEnable();

and it seems that work fine just put to turn-on a LED in the ISR of the Timer0 (Timer0IntHandler(void))


The next one...

portserial.c

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
/*static void prvvUARTTxReadyISR( void );
static void prvvUARTRxISR( void );
*/
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
	/* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
 if( xRxEnable )
    {	
	UARTIntEnable(UART0_BASE,UART_INT_RX);    	

	}else UARTIntDisable(UART0_BASE,UART_INT_RX); 

  if( xTxEnable )
    {
	UARTIntEnable(UART0_BASE,UART_INT_TX);
    }else UARTIntDisable(UART0_BASE,UART_INT_TX); 

}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{	
    (void)ucPORT;
    uint32_t Parity,Bits,StopBits;
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Enable UART0
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Configure GPIO Pins for UART mode.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Select the length and Parity of UART
    //
	
	switch( eParity )
	{
		case MB_PAR_EVEN:
			Parity = UART_CONFIG_PAR_EVEN;
			StopBits = UART_CONFIG_STOP_ONE;
		break;
 		case MB_PAR_ODD:
			Parity = UART_CONFIG_PAR_ODD;
			StopBits = UART_CONFIG_STOP_ONE;
		break;
		case MB_PAR_NONE:
			Parity = UART_CONFIG_PAR_NONE;
			StopBits = UART_CONFIG_STOP_TWO;//Ensure the 11 bits of RTU character format

                break;
	}

	switch( ucDataBits )
	{
		case 8:			
		Bits = UART_CONFIG_WLEN_8;
		break;
		case 7:
		Bits = UART_CONFIG_WLEN_7;
		break;
	}

    //
    // Initialize the UART for console I/O.
    //
  ROM_UARTConfigSetExpClk(UART0_BASE, ROM_SysCtlClockGet(), ulBaudRate,
                            (Bits | StopBits |
                             Parity));
    //Disable the fifo in order to ensure the interrupt is only for a change in RX
    UARTFIFODisable(UART0_BASE);
    //
    // Enable UART & Interrupts
    //    
    ROM_IntEnable(INT_UART0);
   // vMBPortSerialEnable(TRUE,TRUE);
return TRUE;
}

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
	UARTCharPut( UART0_BASE, ucByte );
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )//give an @ to put the new char
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */

    //
    // No FIFO.
    //
    do 
    {
             *pucByte = ROM_UARTCharGet(UART0_BASE);
             
    }while (ROM_UARTCharsAvail(UART0_BASE));

    return TRUE;
}


void UARTIntHandler( void )
{
   	uint32_t ui32status;
 
	ui32status = ROM_UARTIntStatus(UART0_BASE,true);
/* Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 */
	if( ui32status == UART_INT_RX)
	 {      
		 pxMBFrameCBByteReceived(  );
		 ROM_UARTIntClear(UART0_BASE, ui32status);
                
	  }	

/* Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call 
 * xMBPortSerialPutByte( ) to send the character.
 */
	if( ui32status == UART_INT_TX)
	 { 
		pxMBFrameCBTransmitterEmpty(  );
		ROM_UARTIntClear(UART0_BASE, ui32status);
	  }

}

Here i'm not sure if it is ok to disable the FIFO of the UART but i just want to ensure that interrupt only occurs when there is a change in the pin

i only try this part of the code sending a character from my pc and turning on a LED in the uart ISR and it work fine.


Finally the main:

main.c

#include "IncTm4c123.h"
#include <assert.h>
#include "mb.h"
#include "mbport.h"
/* ----------------------- Defines ------------------------------------------*/
#define REG_INPUT_START 1000
#define REG_INPUT_NREGS 4
static unsigned usRegInputStart  = REG_INPUT_START;
static unsigned usRegInputBuf[REG_INPUT_NREGS];
void configure(void);

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
{
    return MB_ENOREG;
}


eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
               eMBRegisterMode eMode )
{
    return MB_ENOREG;
}

eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    return MB_ENOREG;
}
//-----------------------------------------------------------------------------main
int main()
{
  configure();

 eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_NONE);
/* Enable the Modbus Protocol Stack. */
   eMBEnable(  );

    for( ;; )
    {
        ( void )eMBPoll(  );
        

        /* Here we simply count the number of poll cycles. */
        usRegInputBuf[0]++;
    }
}


void configure (void) {
 
  
  ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);// 16 MHz clk
  ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
  ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3 | GPIO_PIN_2 );
  GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3 | GPIO_PIN_2  , 0);
 
}

I only want to work with RTU mode so in mbconfig.h i put a 0 in ASCII and TCP macros, i used the Modpoll Modbus Master Simulator with linux and the parameters were: 

:$./modpoll -m rtu -a 1 -r 1000 -c 4 -t 3 -b 9600 -d 8 -p none /dev/ttyACM0

No answer for the slave :/ i'm really stuck, i'm gonna try do some changes on the fifo maybe it helps but i just want to know if someone has worked with this, And tell me if I'm modifying the files correctly

Thanks and Best regards!

  • Miguel - for a "first ever" post here - you did a "great" job!     Thoughtful, introduced in small, digestible pieces and adequately detailed.     So RARE - and so much BETTER explained than the all too common, "Does NOT Work!"      (like it is our fault that poster has failed...)

    May we "start" from the "end" of your post?

    miguel miranda said:
    No answer for the slave :/ i'm really stuck

    Suspect you meant "from the Slave" - should we not START there?      BTW - I "know of" - yet have never used - Free ModBus.

    Is it not better to "confirm" that your Slave (the 4C123 MCU) is (really) able to "talk" to your "Modpoll Modbus Master Simulator" - using a FAR SIMPLER Implementation?

    Firm/I have achieved (some) success - that primarily due to "KISS" - we always attempt to break the "COMPLEX & LONG" parts of a design into smaller, more fundamental parts - and test those - one at a time!

    In your case - there is (very) much going on - and so your ability to diagnose (as you've discovered) proves very difficult.      We cannot tell if you've ever (successfully) sent from the 4C123 to your Modbus Simulator.    I believe that to be an important test - and should be probably done in the fastest & easiest means possible - that via the vendor's API.     (there are multiple UART examples:  "Examples/Peripherals/UART...")

    Once you succeed in transferring data between the MCU and Modbus - getting the "Modbus Commands" to function should be much easier.

    I would suggest that you (temporarily) hold-off on Modbus - and instead establish two-way communication - via that same UART - between your Modbus Simulator and your 4C123 board.     Note that the UART0 - which you use - is (alone) in using the MCU board's second MCU - to serve as a UART to USB - serial converter.     All other UART channels are 3V3 signal level - ONLY!    (Do NOT connect to RS232 or any voltages greater than 3V3 or transitioning below ground!)

    Are you aware of such a simulator which operates under Windows (ideally Win7 - which we use)?     This would enable (many) here - to repeat your tests - linux is far less popular here - thus will limit the number of persons/groups - able to assist.     (to include my small group)

    Good luck - and again - very nice job...

  • cb1_mobile said:

    Miguel - for a "first ever" post here - you did a "great" job!     Thoughtful, introduced in small, digestible pieces and adequately detailed.     So RARE - and so much BETTER explained than the all too common, "Does NOT Work!"      (like it is our fault that poster has failed...)

    -Thank you for the reply and thanks for the congratulations.

    cb1_mobile said:

    Suspect you meant "from the Slave"

    -Yes i meant that :p

    cb1_mobile said:

    Is it not better to "confirm" that your Slave (the 4C123 MCU) is (really) able to "talk" to your "Modpoll Modbus Master Simulator" - using a FAR SIMPLER Implementation?

    -I did some basic tests, just confirm the correct initialization of timer and UART and it works, also the according interruptions, well in UART only try the RX interrupt. But all the tests that I did were without enabling the Modbus stack, i mean... using somehing like this:

    xMBPortSerialInit (9600UL,0, 8, MB_PAR_NONE);
    vMBPortSerialEnable (TRUE , FALSE);
    xMBPortTimersInit( 200 );
    vMBPortTimersEnable();

    But i'm agree with you, i think that first i need to be sure that interrupt occurs when the first frame arrives from the master and the timer start counting...

    Thanks!

  • Indeed - DO consider "KISS" - always those who, "Attempt EVERYTHING - all JUMBLED Together" find their results lacking - and their test/troubleshooting is UNNECESSARILY EXTENDED AND COMPLICATED!

    Recall the words of astronaut Armstrong, "That's one small "Test" Step (completed by Miguel) - one GIANT step toward project Success!