AnsweredAssumed Answered

RTU Modbus for the Kinetis KL25Z?

Question asked by juanm on Jul 17, 2015
Latest reply on May 6, 2017 by kiger Zhang

Hi. I'm wondering if there's available a library implementing Modbus for the KL25Z? I'm trying to port the freemodbus library but I just can't get it to work. I'm using an Arduino shield with a MAX485

and this interface for comunicating with the PC:

http://www.atac.com.tw/product/5-serial-usb/5-uc485.HTML

 

 

I tried the UART and timer functions without the modbus stack and they work fine

http://www.freemodbus.org/index.php?idx=5

http://sourceforge.net/projects/freemodbus.berlios/files/

This are the functions that I have ported:

 

 

portserial.c:

 

void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts.
     * If xTxENable enable transmitter empty interrupts.
     */
//UART0_C1 |= UART_C1_LOOPS_MASK;
    /* Compute control register value */
    // TIE=0,TCIE=0,RIE=0,ILIE=0,TE=1,RE=1,RWU=0,SBK=0
    UART_C2_REG(UART0_BASE_PTR) |= UART_C2_TE_MASK | UART_C2_RE_MASK ;
    /* Enable the requested interrupts */
    if( xRxEnable )
     UART_C2_REG(UART0_BASE_PTR) |= UART_C2_RIE_MASK;
    if( xTxEnable )
     UART_C2_REG(UART0_BASE_PTR) |= UART_C2_TIE_MASK;
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
 uint16_t sbr;
 uint16_t temp;
    /* Calculate baud settings */
    sbr = (uint16_t)((48000*1000)/(ulBaudRate * 16));
    /* Save off the current value of the UARTx_BDH except for the SBR field */
    temp = UART_BDH_REG(UART0_BASE_PTR) & ~(UART_BDH_SBR(0x1F));
    UART_BDH_REG(UART0_BASE_PTR) = temp |  UART_BDH_SBR(((sbr & 0x1F00) >> 8));
    UART_BDL_REG(UART0_BASE_PTR) = (uint8_t)(sbr & UART_BDL_SBR_MASK);
    /* Configure the serial port */
//      SCI1C1 = 0x00;          // LOOPS=0,SCISWAI=0,RSRC=0,M=0,ILT=0,PE=0,PT=0
    if( ucDataBits == 8 && eParity != MB_PAR_NONE )
     UART_C1_REG(UART0_BASE_PTR) |= UART_C1_M_MASK;
    if( eParity != MB_PAR_NONE )
     UART_C1_REG(UART0_BASE_PTR) |= UART_C1_PE_MASK;
    if( eParity == MB_PAR_ODD || ( eParity == MB_PAR_NONE && ucDataBits == 7 ) )
     UART_C1_REG(UART0_BASE_PTR) |= UART_C1_PT_MASK;
enable_irq(12);
    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. */
    /* Wait until space is available in the FIFO */
    while(!(UART_S1_REG(UART0_BASE_PTR) & UART_S1_TDRE_MASK));
    /* Send the character */
    UART_D_REG(UART0_BASE_PTR) = (uint8_t)ucByte;
    UART_C2_REG(UART0_BASE_PTR) |= UART_C2_TIE_MASK ;
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
    BOOL            parityOK;
    /* Wait until character has been received */
    while (!(UART_S1_REG(UART0_BASE_PTR) & UART_S1_RDRF_MASK));
    if (UART0_S1 & UART_S1_PF_MASK)
    {
     parityOK = 0;
    }
    if (!(UART0_S1 & UART_S1_PF_MASK))
    {
     parityOK = 1;
    }
    //parityOK = !SCI1S1_PF;
    if (UART0_C1 & UART_C1_M_MASK)
        *pucByte = UART0_C3;      // for coherent 9 bit reads of the receiver buffer
    *pucByte = UART0_D;

    if (!(UART0_C1 & UART_C1_M_MASK))
        if( UART_C1_PE(UART0_BASE_PTR) || ( !UART_C1_PE(UART0_BASE_PTR) && UART_C1_PT(UART0_BASE_PTR) ) )
            *pucByte &= 0x7F;
   UART0_C2 |= UART_C2_RIE_MASK;
    return parityOK;            // return TRUE if Parity Error Flag was not set
}
/* 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.
 */
/*interrupt VectorNumber_Vsci1tx void
prvvUARTTxReadyISR( void )
{
    SCI1C2_TIE = FALSE;
    ( void )pxMBFrameCBTransmitterEmpty(  );
}*/
/* 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.
 */
/*interrupt VectorNumber_Vsci1rx void
prvvUARTRxISR( void )
{
    SCI1C2_RIE = FALSE;
    ( void )pxMBFrameCBByteReceived(  );
}*/
void UART0_IRQHandler (void)
{
  char c = 0;
  CHAR cByte;
  if (UART0_S1 & UART_S1_RDRF_MASK) //Si recibio...
  {
      
   //UART0_C2 &= ~UART_C2_RIE_MASK;
    ( void )pxMBFrameCBByteReceived(  );
    if ((UART0_S1&UART_S1_TDRE_MASK)||(UART0_S1&UART_S1_TC_MASK)) //Si transmitio...
    {

     UART0_C2 &= ~UART_C2_TIE_MASK;
     ( void )pxMBFrameCBTransmitterEmpty(  );
    }
  }
}

 

port.h:

#include "MKL25Z4.h"
/* Tne include fila inttypes.h is not available for HCS08, so we use our own definitions instead */
//#include <inttypes.h>
typedef signed char        int8_t;
typedef unsigned char uint8_t;
typedef signed short int         int16_t;
typedef unsigned short int uint16_t;
typedef signed long int        int32_t;
typedef unsigned long int uint32_t;

/* Michael Barrs verification code */
#pragma MESSAGE ERROR DISABLE C1135
static union
{
    char            int8_t_incorrect[sizeof( int8_t ) == 1];
    char            uint8_t_incorrect[sizeof( uint8_t ) == 1];
    char            int16_t_incorrect[sizeof( int16_t ) == 2];
    char            uint16_t_incorrect[sizeof( uint16_t ) == 2];
    char            int32_t_incorrect[sizeof( int32_t ) == 4];
    char            uint32_t_incorrect[sizeof( uint32_t ) == 4];
};

#define INLINE
#define PR_BEGIN_EXTERN_C       extern "C" {
#define PR_END_EXTERN_C         }
#define memcpy                  (void) memcpy   // gets wid of a warning in mbfuncother.c
#define ENTER_CRITICAL_SECTION( )   __asm(" CPSID i");
#define EXIT_CRITICAL_SECTION( )    __asm__("cpsie   i");
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
/* The bus clock is used for computing timer and baud rate register values */
#define BM_BUS_CLOCK   24000000 // Hz
#endif

 

porttimer.c:

/* ----------------------- Persistent variable ------------------------------*/
static USHORT   usDelta;
/* ----------------------- Start implementation -----------------------------*/
USHORT
usMBMulDiv( USHORT a, USHORT b, USHORT c )
{
    ULONG           x;
    x = a;
    x *= b;
    x /= c;
    return ( USHORT ) x;
}
/* Initialize Timer 1 as free running, and Channel 0 as output compare (with no pin)
 */
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
    /* We assume 24000000 Hz bus clock and let the timer run at 1/16 of this frequency, ie. 1500000 Hz */
    ( void )assert( BM_BUS_CLOCK == 24000000 );
    TPM1_SC = TPM_SC_CMOD(1)| TPM_SC_PS(7);  //CLK dividido por 128 (24 MHz/128 = 187500 ) Para 50us necesito mod 9
    //TPM1SC = 0x0F;              // TOF=0,TOIE=0,CPWMS=0,CLKS=0:1,PS=1:1:1
 TPM1_C1SC = TPM_CnSC_MSA_MASK;
    //TPM1C0SC = 0x10;            // CH0F=0,CH0IE=0,MS0=0:1,ELS0=0:0;??=0:0
    /* Compute numer of timer tics corresponding to the given timeout periode */
    usDelta = usMBMulDiv( usTim1Timerout50us, 900, 100 ); 
    return TRUE;
}
/* Set the output compare register value and enable timer interrupt
 */
INLINE void
vMBPortTimersEnable(  )
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */

    TPM1_C1V = usDelta;
    
    if (TPM1_C1SC & TPM_CnSC_CHF_MASK)
     TPM1_C1SC &= ~TPM_CnSC_CHF_MASK; //TPM1C0SC_CH0F = FALSE;
    TPM1_C1SC |= TPM_CnSC_CHIE_MASK; //TPM1C0SC_CH0IE = TRUE;
    enable_irq (18); //Habilito la interrupcion de TPM1
}
/* Disable timer interrupts
 */
INLINE void
vMBPortTimersDisable(  )
{
 TPM1_C1SC &= ~TPM_CnSC_CHIE_MASK; //TPM1C0SC_CH0IE = FALSE;
}
/* Handle the interrupt and disable further timeouts
 */

TPM1_IRQHandler(void)
{
 TPM1_C1SC |= TPM_CnSC_CHF_MASK;   //Clear interrupt flag (CHF = 1);
 TPM1_C1SC &= ~TPM_CnSC_CHIE_MASK; //TPM1C0SC_CH0IE = FALSE
 ( void )pxMBPortCBTimerExpired(  );
}

Outcomes