Source Code File:"portserial.c"

/*
 * 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: portserial.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
 */

#include "port.h"

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


/* ----------------------- static variables ---------------------------------*/
static eMBParity  eCurrParity;

/* ----------------------- functions ---------------------------------*/
UCHAR CalcParity( UCHAR ucData );

/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
    if(xRxEnable == TRUE)
    {
        set_bit(rcsta, CREN);
        set_bit(pie1, RCIE);
        //485 status determined by xTxEnable state
    }
    else
    {
        clear_bit(pie1, RCIE);
        clear_bit(rcsta, CREN);
        //485 status determined by xTxEnable state
    }
    if(xTxEnable == TRUE)
    {
        set_bit(pie1, TXIE);
        TRANSMIT_ON_485();
        //Allow time for bus to turn around.
        delay_10us(1);
    }
    else
    {
        clear_bit(pie1, TXIE);
        //Bad way to do this, but wait for TX complete
        while(txsta.TRMT == 0);
        RECEIVE_ON_485();
    }
    //Enable peripheral and global ints
     set_bit(intcon, GIEL);
//   set_bit(intcon, GIEH);
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    BOOL ucReturn;
    
    /* prevent compiler warning. */
    (void)ucPORT;
    /* Only supporting RTU so always 8 data bits */
    (void)ucDataBits;
    
    set_bit(rcsta, SPEN);
    //Int priority low
    clear_bit(ipr1, RCIP);
    clear_bit(ipr1, TXIP);
    
    // Make sure serial pins are NOT digital outputs
    set_bit(trisb, 1);
    set_bit(trisb, 4);
    
    // Turn off A/D for serial pins
    set_bit(adcon1, 5);
    set_bit(adcon1, 6);
    
    baudctl = 0x08;  //Set BRG16 bit
    //Set 16 bit baudrate count
    spbrg = (4000000/(16*ulBaudRate)) - 1;
    //Save parity since it is generated programatically
    eCurrParity = eParity;
    
    //Since RTU only, for tx parity will be on 9th data bit or
    //  9th data bit set to 0 to be 2nd stop bit
    set_bit(txsta, TX9);
    set_bit(rcsta, RX9);
    if(eParity == MB_PAR_NONE)
    {
        //Do not receive 9th bit in case sender doesn't have 2 stop bits.
        clear_bit(rcsta, RX9);
        //Clear 9th data bit for case of no parity to do 2 stop bits.
        clear_bit(txsta, TX9D);
    }
    //Leave transmit enabled
    set_bit(txsta, TXEN);
    

    vMBPortSerialEnable( FALSE, FALSE );
    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. */
     
    /* We need to generate TX parity, if enabled */
    switch(eCurrParity)
    {
        case MB_PAR_EVEN:
            if(CalcParity(ucByte) == 0 )
            {
                clear_bit(txsta, TX9D);
            }
            else
            {
                set_bit(txsta, TX9D);
            }
            break;
        case MB_PAR_ODD:
            if(CalcParity(ucByte) == 0 )
            {
                set_bit(txsta, TX9D);
            }
            else
            {
                clear_bit(txsta, TX9D);
            }
            break;
            
    }
    //Write the data byte
     txreg = ucByte;    
    
    
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack
     */
    *pucByte = rcreg;
    return TRUE;
}


void  vMBPortClose( void )
{
}


/******************************************************************************
 * Name:        CalcParity
 * Description: Calculates parity of ucData
 * Uses: 
 *     
 * Returns: Parity of ucData in bit 0 of returned UCHAR: 0 = even, 1 = odd
 *                
 * NOTE: For TX of 9 bit Even Parity, send the bit returned here in the 9th bit.
 *  For TX of 9 bit Odd Parity, send the complement of the returned bit in the 9th bit.
 *  For RX of 9 Bit Even Parity, parity is good if returned bit matches 9th RX bit.     
 *  For RX of 9 bit Odd Parity, parity is good if returned bit is complement of 9th RX bit.
 *****************************************************************************/
UCHAR CalcParity( UCHAR ucData )
{
    asm
    { 
        movf   _ucData, W
        ;8-bit parity
        ;This routine will leave the parity of ucData in _ucData.0 
        ;while blenderizing most of the rest of _ucData
        swapf   _ucData, W  ;x =     abcdefgh  w =     efghabcd
        xorwf   _ucData, F  ;x =     abcdefgh  w =     efghabcd
        rrcf    _ucData, W  ;x =     abcdefgh  w =     -abcdefg
        xorwf   _ucData, F  ;x =     abcdefgh  w =     -abcdefg
    
        ; at this point, the parity for half the bits
        ; (a, b, e, and f) is in bit 2 of X, and the
        ; parity for the other half (bits c, d, g, and h)
        ; is in bit 0 of X.
    
        btfsc   _ucData, 2  ; if the parity of (a,b,e,f) is 0,
                        ; then the parity of (a,b,c,d,e,f,g,h)
                        ; is equal to the parity of (c,d,g,h)...
                        ; which is already in bit 0, so skip ahead.
    
    
        incf    _ucData, F  ; otherwise, the parity of (a,b,e,f) is 1,
                        ; so the parity of (a,b,c,d,e,f,g,h) is
                        ; NOT equal to the parity of (c,d,g,h).
                        ; invert bit 0.
    
        ; at this point, bit 0 of _ucData contains the parity of
        ; (a,b,c,d,e,f,g,h).
    }
    return ucData & 0x01;
}    

http://www.sourceboost.com

Copyright © 2006 SourceBoost Technologies