Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
kalbun
PostPosted: Oct 03, 2011 - 06:53 PM
Hangaround


Joined: Mar 02, 2010
Posts: 103
Location: Firenze, Italy

This short tutorial describes how to configure a MODBUS server (FREEMODBUS more exactly) to run on USB instead than a standard serial port.
I suppose only a few persons will find this document useful, but why not to share nonetheless? Wink

Note: this is not a FMB or LUFA tutorial. So I suppose you are already able to setup both.

Goal: implement a MODBUS server that can be accessed via USB
Software needed:
- FREEMODBUS (FMB) for the MODBUS server
- LUFA for the USB driver, configured as CDC device
- Part Pack from Atmel if you use the latest XMega with USB

Problem: FMB expects an ISR to signal when there is a new character in the RX buffer, It also expects another ISR that tells when the TX buffer is empty, to start sending next char.
With USB you don't have these ISRs, and you have to find a different strategy.

As FMB documentation suggests, start from the BARE implementation and change these files:
- portserial.c for serial initialisation and interface
- portevent.c for the inter-function event exchange

Step 1: set portserial.c

a) the first function to implement is
Code:
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )

The function takes two parameters to enable/disable RX and TX respectively. FMB enables TX only when it really needs to transmit, so when the function is called to enable TX, we also "simulate" the TX buffer empty signal.
Code:
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
if (xTxEnable)
  pxMBFrameCBTransmitterEmpty(  );
}


b) now implement the callback function that FMB calls to put a byte in the serial port.
Code:
BOOL xMBPortSerialPutByte( CHAR ucByte )
{
CDC_Device_SendByte(&VirtualSerial_CDC_Interface,ucByte);
xMBPortEventPost(EV_FRAME_SENT);
return TRUE;
}

Note that, after putting a byte in the USB buffer, we also send a FRAME_SENT event. This is used, again, to "simulate" the TX buffer empty signal. We will use it later.

c) finally, define the function called to get a byte from the serial port:

Code:
BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
   if (CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface) > 0)
   {
      int16_t data = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
      if (data >= 0)
       *pucByte = (uint8_t)(data);
     return TRUE;
   }
   return FALSE;
}


Not much to say here. Instead of reading from serial we read from USB.

Step 2: changes to portevent.c

The file defined in the BARE port is almost ok. There is only one change to do. You remember that, after sending a byte to USB, we also put a FRAME_SENT event in the FMB queue. It's now time to handle this event:

Code:
BOOL xMBPortEventGet( eMBEventType * eEvent )
{
    BOOL            xEventHappened = FALSE;

    if( xEventInQueue )
    {
        *eEvent = eQueuedEvent;
        xEventInQueue = FALSE;
        xEventHappened = TRUE;
         // add these two lines
         if (eQueuedEvent == EV_FRAME_SENT)
           pxMBFrameCBTransmitterEmpty(  );
    }
    return xEventHappened;
}

This will retrigger another fake "TX buffer empty" event and force FMB to send a new byte, if available.

Step 3: implement USB task

You need to periodically call LUFA functions CDC_Device_USBTask() and USB_USBTask() from inside a task or a timer ISR. Short before/after these functions are invoked, check if new bytes are received and in case, inform FMB:

Code:
   if (CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface) > 0)
        pxMBFrameCBByteReceived( );
    CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
    USB_USBTask();


That's all. When you call eMBInit(), you can pass any value for serial speed.

These modifications were tested with ASCII MODBUS on a MEGA16U4 and are working well. As soon as the new hardware is ready, I will test on XMEGA64A3U but no big surprises are expected.

If you find mistakes or parts that are not clear, please let me know.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
js
PostPosted: Oct 03, 2011 - 11:33 PM
10k+ Postman


Joined: Mar 28, 2001
Posts: 20387
Location: Sydney, Australia (Gum trees, Koalas and Kangaroos, No Edelweiss)

Quote:
Goal: implement a MODBUS server that can be accessed via USB
So how do you connect, say 32, MODBUS devices on the same USB line? Smile

_________________
John Samperi
Ampertronics Pty. Ltd.
www.ampertronics.com.au
* Electronic Design * Custom Products * Contract Assembly
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
kalbun
PostPosted: Oct 04, 2011 - 08:43 AM
Hangaround


Joined: Mar 02, 2010
Posts: 103
Location: Firenze, Italy

js wrote:
Quote:
Goal: implement a MODBUS server that can be accessed via USB
So how do you connect, say 32, MODBUS devices on the same USB line? Smile


1) put all USB ports in parallel

2) turn power on

2) open the window so the smoke coming from your boards/PCs can go out. If you see fire, call for professional help Twisted Evil
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits