Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Multiple Serials Ports in Threads - Best Practices


Best Answer Homey, 07 April 2014 - 09:20 PM

Ok another update.
 
1.  SPI issues seem to have been solved with (a) pull down resistors on CLK, MISO, MOSI and ( B) use of at Digital pin on the ND+2 as the Chip Select (or slave select - depending on your vocabulary).  Here was the final setup that worked.

YEIDeviceConfig = new SPI.Configuration(
Pins.GPIO_PIN_D9, //Chip Select Pin
false, //chip select active state low
6, //chip select setup time 0us
6, //chip select hold time 0us
false, //clock idle state
true, //clock Edge
2000, //clock rate in KHz (Netduino Wiki said 250Khz is about 6 us delay between writes, YEI wants 5 us delay min between writes)
SPI_Devices.SPI1); //SPI Module (must use the Secret Labs one here not Microsoft)
 

Regarding serial ports, I have used some of the suggestions here (many thanks for all the input) and I seem to be getting reliable data now from a Adafruit ultimate GPS and at ConnectBlue OBS419 Bluetooth Module.

 

Here is the solution file for a Adafruit Ultimate GPS using the OnDataReceived event concepts in this thread.

 

http://www.saffprod....are/GPSTest.zip

 

I have also been running my unit using UDP broadcasting and a TCP messaging system now for over three hours networking the device data to a central location (with another app reading the data).

 

Im gonna mark this thread closed as the main points seem to now be solved with these suggestions and the new firmware 4.3.1 / MF.

 

Yay!   Homey

 

PS.  Ill get to testing CTS/RTS and post results in another thread.  Thanks again for an active forum!

Go to the full post


  • Please log in to reply
28 replies to this topic

#21 lifanek

lifanek

    Member

  • Members
  • PipPip
  • 23 posts
  • LocationKatowice, Poland

Posted 05 March 2014 - 02:56 PM

I have also noticed event firing with 0 bytes to read - just added if (bytestoread < 1) do nothing.



#22 Homey

Homey

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts

Posted 09 March 2014 - 02:47 AM

I see firmware 4.3 is out. Im gonna try to upgrade to it and see if it fixes at least some of the issues I have.

more later! Homey

#23 nhale

nhale

    Advanced Member

  • Members
  • PipPipPip
  • 64 posts
  • LocationHeidelberg, Germany

Posted 24 March 2014 - 09:38 PM

I think we are talking about a similar problem here.

I also have issues with reading from the serial port on N2(+) devices

http://forums.netdui...e-v431/?p=57040



#24 Homey

Homey

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts

Posted 24 March 2014 - 10:27 PM

I feel your pain. I have updated to 4.3.1 and VS2012 and none of the issues I have been able to test (serial ports, SPI) have been resolved by it. I am currently using a logic analyzer to debug these issues here and hopefully will have some tech news to give to chris and secret labs team. Im down to just a Program.cs, no threads, no network .... pretty much a bare bones setup (which of course is useless for my application - but useful for my logic analyzer) :)

What I have seen so far on SPI tho is what looks like an extra transient pulse on SPI CLK right after each 8 pulses that seems to be interpreted by SPI devices as another pulse ... putting theSPI comms out of sync.

I haven't tried seeing if the Networking hang up issues have been resolved with new firmware ... I plan to get to that when I get through just getting the basic communications working reliably. Its a shame the ND+2 doesn't work for me out of the box tho.

#25 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 25 March 2014 - 07:22 AM

Hi Homey,

What I have seen so far on SPI tho is what looks like an extra transient pulse on SPI CLK right after each 8 pulses that seems to be interpreted by SPI devices as another pulse ... putting theSPI comms out of sync.

If you can provide a quick code repro and logic analyzer trace for that, we'd love to see what's going on in your setup. We spent a lot of time analyzing SPI (making sure that all modes were working properly) in the 4.3.1 release.

Please note that we do enable/disable the SPI feature on the STM32--so it's possible that what you're seeing is the clock pin return to its default mode _between_ transaction. If you're using SPI_SS, everything should be working properly on your device. But if you have a setup where you've removed SPI_SS...and the re-initialization between transactions is problematic...we'll need to see if there's a way to restructure NETMF a bit to deal with that. The framework wasn't necessarily designed to balance selection-less SPI as a priority scenario vs. reuse of SPI pins as other features when SPI is not in use, etc. But it's a pretty malleable framework so we can explore :)

Chris

#26 Homey

Homey

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts

Posted 25 March 2014 - 12:30 PM

Hi Chris

Here is a logic trace of the extra transients. Im still not completely convinced my issues are not my code tho but im pretty sure the trace is accurate on what the ND+2 is putting out.

What is SPI_SS? I think I posted my SPI code earlier in this thread but if not my set is:

//set the config and open the SPI port
YEIDeviceConfig = new SPI.Configuration(
Cpu.Pin.GPIO_NONE, //Chip Select Pin
false, //chip select active state low
0, //chip select setup time 0us
0, //chip select hold time 0us
false, //clock idle state
true, //clock Edge
2000, //clock rate in KHz (forums said http://forums.netdui...ues#entry51999)
SPI_Devices.SPI1); //SPI Module (must use the Secret Labs one here not Microsoft)

In my configuration I only have one spi device on the bus so I don't actually use a ND pin for CS (Chip Select). I have the CS pin tied to ground on the SPI slave. You can see the SPI device MISO transition from low to high on the transient. What has been difficult is the SPI device stops responding correctly after about 10 minutes (responds with garbage) so it actually works for while with this transient in there. After this 10 minutes the SPI device seems out of sync with the proper sequences of writereads for the unit. I see this phenomena with and without pull downs on the SCLK and MOSI lines.

Link to Logic Trace here

http://www.saffprod....03_F6TRIG 4.png


My Code is here:

http://www.saffprod....ages/Program.cs

#27 Fahrice

Fahrice

    New Member

  • Members
  • Pip
  • 2 posts

Posted 26 March 2014 - 04:09 AM

Hi Homey,

 

I've been looking over the code you've posted in this thread for a bit now trying to understand what you're trying to do.  I'd like to offer up some help with regard to the serial port issue.  Basically, I've had weird/interesting/inconsistent results when trying to build up a system to communicate via a serial port as well, but I've finally squashed the last bug and got it working very nicely.  I collected various ideas and bits and pieces of code from several different examples I've seen and put it all together into something that is clean, concise, and effective.

 

Here are some key points about the setup I've ended up with:

 

1) I'm not using multiple threads, so it's simple.

 

2) It's FAST.  I'm using this for serial communications using SPP over bluetooth, and the data transfer is nearly instantaneous.

 

3) My buffer and bufferLength variables are class variables, not local variables - as you'll see by the "this" prefix in the code below.  The bufferLength variable stores a value indicating the number of bytes in the buffer at any given time.  I keep them as class variables so their content persists through multiple DataReceived events fired by SerialPort.

 

4) I use the DataReceived event on SerialPort for one thing and one thing only:  receiving the data, storing it in the buffer, and keeping bufferLength updated.  After those things happen, I fire an event to notify anyone who cares that there is new data in the buffer.

 

Here is the code that handles SerialPort's DataReceived event:

private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
    this.VerifyOpenPort();
            
    lock (this.locker)
    {
        int bytesReceived = this.serialPort.Read(this.buffer, this.bufferLength, this.serialPort.BytesToRead);

        if (bytesReceived > 0)
        {
            this.bufferLength += bytesReceived;

            if (this.bufferLength >= BufferMax)
                throw new Exception("Buffer overflow.");

            this.RaiseDataReceived();
        }
    }
}

Now here is a bit of code I picked up from another user on these forums, although it is modified.  This allows an outside caller to try to read a line of text from the buffer.  If there isn't a full line (meaning a CR/LF character isn't in the byte array), an empty string is returned.  This method is in the same class as the OnDataReceived method above.:

public string ReadLine()
{
    string line = string.Empty;

    lock (this.locker)
    {
        for (int i = 0; i < this.bufferLength; i++)
        {
            if (this.buffer[i] == '\r' || this.buffer[i] == '\n')
            {
                this.buffer[i] = 0;

                line += new string(Encoding.UTF8.GetChars(this.buffer, 0, i));

                if (line != null && line != string.Empty)
                    Debug.Print("Line read: {" + line + "}");
                
                this.bufferLength = this.bufferLength - i - 1;

                Array.Copy(this.buffer, i + 1, this.buffer, 0, this.bufferLength);

                break;
            }
        }
    }

    return line;
}

Now for your purposes, you could modify this to return byte[] and grab the first 72 bytes from the buffer and return them - OR, if there aren't 72 bytes in the buffer yet, return null.  Here's what that might look like:

public byte[] ReadBytes(int quantity)
{
    byte[] bytes = new byte[quantity];

    lock (this.locker)
    {
        if (this.bufferLength >= quantity)
        {
            // Copy X number of bytes from class buffer into local buffer
            Array.Copy(this.buffer, 0, bytes, 0, quantity);

            // Update bufferLength
            this.bufferLength -= quantity;

            // Move bytes remaining in buffer to the beginning of the buffer
            Array.Copy(this.buffer, quantity, this.buffer, 0, this.bufferLength);
        }
        else
        {
            bytes = null;
        }
    }

    return bytes;
}

The example above will allow you to request any number of bytes from the buffer - 72 included. :-)

 

Now, up above in the first code block, you see:  this.RaiseDataReceived();

 

This method simply fires the DataReceived event on the containing class (*NOT* SerialPort) if there are any listeners.  Here's the code for it:

// This raises the event
private void RaiseDataReceived()
{
    var handler = this.DataReceived;
    if (handler != null)
    {
        handler();
    }
}

// This is the event
public event DataReceivedEventHandler DataReceived;

// And this is the definition of that DataReceivedEventHandler delegate
// This does not have to be in the same class as everything else I've
// shared - in fact, it shouldn't be in ANY class - just the same namespace.
public delegate void DataReceivedEventHandler();

Now, this is how the "ReadLine" method is used in conjunction with the DataReceived event to process the info in the buffer:

// Wire up a handler for the DataReceived event on an instance of the class 
// that contains all of the code above
this.myInstance.DataReceived += this.OnDataReceived;

// Here's the code for the handler
private void OnDataReceived()
{
    string message = this.myInstance.ReadLine();

    while (message != null && message != string.Empty)
    {
        Console.WriteLine("Received message: {0}", message);

        message = this.myInstance.ReadLine();
    }
}

And here is a sample implementation you could use to read X number of bytes rather than strings:

private void OnDataReceived()
{
    byte[] bytes = this.myInstance.ReadBytes(72);

    while (bytes != null)
    {
        /////////////////////////////////////
        // Do something here with your bytes!
        /////////////////////////////////////

        // Then try to read in another set of bytes
        bytes = this.myInstance.ReadBytes(72);
    }
}

Hopefully this all makes sense.  The idea is that you have a buffer that stores data that is loaded into it by the DataReceived event on the SerialPort instance.  Every time it fires and the data is buffered, it fires another event that tells your message processing code to use the ReadLine/ReadBytes method to pull the data out of the buffer.  The two sides of the buffer (the side that writes & the side that reads) operate completely independently of each other.

 

The bug I had was in my consumer "OnDataReceived" method where I call ReadLine.  I was only calling ReadLine one time each time the DataReceived event fired.  Well, the processing of that line wouldn't be completed and more data was already coming into the buffer faster than it was being processed, and eventually a buffer overflow would occur.  Now, every time new data arrives, ReadLine will be called repeatedly until the buffer is emptied.

 

Finally, I have one more thing that I wanted to suggest you try.  I did some playing around, and I added the following code to my DataRecieved handler for the SerialPort - and it worked.  You mentioned before "how do I know how long to Thread.Sleep for to let the buffer fill up with 72 bytes?"  Well, this will solve that and might be the simplest thing for you:

private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
    this.VerifyOpenPort();

    while (this.serialPort.BytesToRead < 72)
    {
        Thread.Sleep(1); // Just enough to not consume *every* cycle of the processor
    }
            
    lock (this.locker)
    {
        int bytesReceived = this.serialPort.Read(this.buffer, this.bufferLength, this.serialPort.BytesToRead);

        if (bytesReceived > 0)
        {
            this.bufferLength += bytesReceived;

            if (this.bufferLength >= BufferMax)
                throw new Exception("Buffer overflow.");

            this.RaiseDataReceived();
        }
    }
}

Let me know if you have any questions or if you found this useful.

 

Thanks!

 

Fahrice



#28 Homey

Homey

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts

Posted 02 April 2014 - 04:32 PM

Thanks Fahrice, ill give some of these ideas a try when I get the hardware back.  It in a lab right now getting Logic data on issues with SPI.

 

Chris, any updates on SPI issues im seeing based on this thread (aka SPI_SS and Scope Results)?  Look up :)

 

Homey



#29 Homey

Homey

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts

Posted 07 April 2014 - 09:20 PM   Best Answer

Ok another update.
 
1.  SPI issues seem to have been solved with (a) pull down resistors on CLK, MISO, MOSI and ( B) use of at Digital pin on the ND+2 as the Chip Select (or slave select - depending on your vocabulary).  Here was the final setup that worked.

YEIDeviceConfig = new SPI.Configuration(
Pins.GPIO_PIN_D9, //Chip Select Pin
false, //chip select active state low
6, //chip select setup time 0us
6, //chip select hold time 0us
false, //clock idle state
true, //clock Edge
2000, //clock rate in KHz (Netduino Wiki said 250Khz is about 6 us delay between writes, YEI wants 5 us delay min between writes)
SPI_Devices.SPI1); //SPI Module (must use the Secret Labs one here not Microsoft)
 

Regarding serial ports, I have used some of the suggestions here (many thanks for all the input) and I seem to be getting reliable data now from a Adafruit ultimate GPS and at ConnectBlue OBS419 Bluetooth Module.

 

Here is the solution file for a Adafruit Ultimate GPS using the OnDataReceived event concepts in this thread.

 

http://www.saffprod....are/GPSTest.zip

 

I have also been running my unit using UDP broadcasting and a TCP messaging system now for over three hours networking the device data to a central location (with another app reading the data).

 

Im gonna mark this thread closed as the main points seem to now be solved with these suggestions and the new firmware 4.3.1 / MF.

 

Yay!   Homey

 

PS.  Ill get to testing CTS/RTS and post results in another thread.  Thanks again for an active forum!






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.