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

Help with Ic2 Lockup


  • Please log in to reply
11 replies to this topic

#1 Trey Aughenbaugh

Trey Aughenbaugh

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 07 April 2011 - 02:34 AM

I created a Simple project that uses a few previous projects combined.

I'm using code from this project:
http://forums.netdui...-and-a-pcf8574/
(I did switch to using the Bansky library.)
and from this project:
http://forums.netdui...ch__1#entry7082

All I want to do is have the Date and Time displayed and upon scanning a RFID card, print it on the screen, then go back to the Date/Time. Yes, very simple, just baby steps to a bigger project, but Its not working right.

This works, for a little while. After scanning a few cards, it locks up.
I think I've traced it back to the
lock (_i2cbus) line in the PCF8574P.cs class.

I'm not 100% sure why it locks up.
By Locking up, either the time with just stop updating, or The screen will go blank.
I'm sure there are other instances, just not recalling them all.

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using IDx2Reader;
using System.IO;
using Bansky.SPOT.LCD;
using System.Text;

namespace NetduinoPlusApplication1
{
    public class Program
    {

        private static idReader _idReader;
        static OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);
        //static I2CDevice MyI2CBus = new I2CDevice(null);
        //static I2CDevice.Configuration pcf8574_0 = new I2CDevice.Configuration(0x38, 100);
        
       // private static Timer _lcdTimer;
        private static Thread _lcdThread;

        private static LCD4Bit lcd;

        public static void Main()
        {
            // Create instance of I2C
            I2CDevice I2Cbus = new I2CDevice(new I2CDevice.Configuration(0, 100));

            // Crate instance of expander
            PCF8574P expander = new PCF8574P(I2Cbus,     // I2C bus instance
                                                0x38,       // Address on the bus
                                                true);      // Use big endian

            // Create new LCD instance and use shift register as a transport layer
            //LCD4Bit lcd = new LCD4Bit(expander);
            lcd = new LCD4Bit(expander);
            lcd.Display(true, true, false, false);

            lcd.Clear();



            //_lcdTimer = new Timer(UpdateDisplay, null, 500, 500);
            _lcdThread = new Thread(UpdateDisplay);
            _lcdThread.Start();

            StartRFID();


            Thread.Sleep(Timeout.Infinite);


        }

        private static void StartRFID()
        {
            led.Write(true); // turn on the LED
            Thread.Sleep(50); // sleep for 250ms
            led.Write(false); // turn off the LED
            Thread.Sleep(50); // sleep for 250ms
            led.Write(true); // turn on the LED
            Thread.Sleep(50); // sleep for 250ms
            led.Write(false); // turn off the LED
            Thread.Sleep(50); // sleep for 250ms
            _idReader = new idReader(SerialPorts.COM1, Pins.GPIO_PIN_D4);
            _idReader.RfidEvent += new idReader.RfidEventDelegate(_idReader_RfidEvent);
            _idReader.Start();
        }

        static void _idReader_RfidEvent(object sender, idReader.RfidEventArgs e)
        {
            Debug.Print("Card scanned: " + e.RFID + ", time: " + e.ReadTime);
            //Card_Scanned(e);
 
            //_lcdTimer.Change(Timeout.Infinite, Timeout.Infinite);
            _lcdThread.Suspend();
            Debug.Print("Suspend");
            //lcd.Display(true, false, false, false);
            Debug.Print("LCD");
            lcd.Clear();
            Debug.Print("Clear");
            lcd.Home();
            lcd.Write(FillLine(e.RFID), 0, 16);
            Thread.Sleep(1000);
            Debug.Print("bef resume");
            _lcdThread.Resume();
            //_lcdTimer.Change(500, 500);

        }

        static readonly byte[] _lineBuffer = new byte[16];
        private static byte[] FillLine(string text)
        {
            // fill empty space
            for (int i = 0; i < _lineBuffer.Length; i++)
                _lineBuffer[i] = (byte)' ';

            // write new text
            var bytes = Encoding.UTF8.GetBytes(text);
            bytes.CopyTo(_lineBuffer, 0);

            return _lineBuffer;
        }

        private static void UpdateDisplay()
        {
            while (_lcdThread.IsAlive)
            {
                var dt = DateTime.Now;

                // write time 
                lcd.Home();
                lcd.Write(FillLine(dt.ToString("hh:mm:ss")), 0, 16);

                // write date
                lcd.SetPosition(40);
                lcd.Write(FillLine(dt.ToString("MM/dd/yyyy")), 0, 16);
                Thread.Sleep(500);
            }
        }


    }


}


#2 Trey Aughenbaugh

Trey Aughenbaugh

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 12 April 2011 - 03:51 PM

This is my third attempt to get something so simple to work. I have added some debug statements before, during and after the LOCK "lock (_slaveDevice)" statement for the I2C bus. I always see the statements: before lock:12:15:35 in lock:12:15:35 OUT lock:12:15:35 before lock:12:15:35 in lock:12:15:35 Card scanned: 5423870, time: 01/01/2009 00:15:35 before lock:12:15:35 and never the 'in lock:'(Yes, lots of Debug output). On occasion it may continue and let me scan a few cards, but not lately. This is all I am attempting to do: ~Have an LCD display the Date/Time updating every 500ms. (I have tried 1000ms) ~You scan an RFID card, it Suspends the Date/Time thread long enough to display the card read. ~The Date/Time thread resumes displaying Date/Time again. I added a Thread to blink the onboard LED. Once the I2C bus locks up, the thread for the LED is still working. Why is the I2C bus locking up, can you not have a serial port and I2C bus running on the same board? I have attempted many different techniques. All seem to come down to the same problem. I have tried the MicroCrystalLCD, Blansky's LCD and now this new one. ALL have the same outcome. I have included my solution. It uses the PCF8574P port expander, the I2C bus code by phantomtypist http://forums.netdui...4156#entry4156. The code is mostly messy, got tired of writing "final" versions of code only to end up writing several test version. Hopefully someone can look at this and see the problem. I'm going to try creating a new I2C instance just for the RFID to use to write to the LCD. Which means doing all the setup. This may be enough to cause a reset and prevent lock ups. Maybe I have a bad port expander? --It does the Knight Rider and other tests nicely though. Bad LCD? -- as long as I let it keep displaying the time, it seems to work well. Bad RFID reader? --Other tests where it just does debug.print, works without fail. This is all still in a breadboard. I do have the outputs of the Port expander connected to LEDS along with the LCD display. This gives me a visual output on the fail of the IC2 Bus/port expander. When all is working, the lights are blinking, they all stop on their last state upon lockup. All components seem to work well on their own. I'm really not sure why it is locking up on the LOCK statement. Only 1 device is trying to communicate with the I2C bus at a time anyway. HELP! Thanks, Trey

Attached Files



#3 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 12 April 2011 - 04:52 PM

I'm really not sure why it is locking up on the LOCK statement. Only 1 device is trying to communicate with the I2C bus at a time anyway.

My guess is that _lcdThread.Susped() in _idReader_RfidEvent() may not immediately suspend the thread, so ExampleSensor calls lock up. The problem is not because there is only one device, but because the LCD is accessed simultaneously from multiple places: thread and event handler. Please try replacing Thread.Sleep(1) in _idReader_RfidEvent with Thread.Sleep(0) - zero timeout has a special meaning, it causes the scheduler to switch and execute the other waiting threads (your comment there is not correct).

Personally, I would probably move the display output to the Main and use buffer (queue) with auto-reset event, so the threads would set (enqueue) the text to be displayed and signal the event for the main loop that would wait for the event and then read (dequeue) and process the text.

#4 Trey Aughenbaugh

Trey Aughenbaugh

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 12 April 2011 - 05:31 PM

My guess is that _lcdThread.Susped() in _idReader_RfidEvent() may not immediately suspend the thread, so ExampleSensor calls lock up. The problem is not because there is only one device, but because the LCD is accessed simultaneously from multiple places: thread and event handler. Please try replacing Thread.Sleep(1) in _idReader_RfidEvent with Thread.Sleep(0) - zero timeout has a special meaning, it causes the scheduler to switch and execute the other waiting threads (your comment there is not correct).

Personally, I would probably move the display output to the Main and use buffer (queue) with auto-reset event, so the threads would set (enqueue) the text to be displayed and signal the event for the main loop that would wait for the event and then read (dequeue) and process the text.

Thanks for the Reply CW2.
Adding the Thread.Sleep(0); did not seem to help either.
I even tried
_lcdThread.Suspend();
Thread.Sleep(0);
_lcdThread.Suspend();
Thread.Sleep(1);
Here is where I found the info about Thread.Sleep(1);
Microsoft .NET Micro Framework Tools & Resources > Threading

The problem is the suspended thread is moved to the suspended thread pool, but UpdateScheduler is not called immediately, so that thread will not be suspended until something else causes the update to happen. The thread usually continues to run until its time slice expires. That can take up to 20 milliseconds. As a manual work around, you can force the update by calling Thread.Sleep(1) right after Thread.CurrentThread.Suspend();.


Would you happen to have some link to or example code on proper use of the Auto-Reset events? I will google it as well.
I'm willing to try anything to get this to work.
I came across this interesting snippet of code as well.
Debugging Locks and Deadlocks
Have not looked through it completely, but that was going to be my next test.

Thanks again,
Trey

#5 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 12 April 2011 - 06:14 PM

I'm willing to try anything to get this to work.

Ok then, could you please try using a boolean variable to indicate and check lcdThread state?

static void _idReader_RfidEvent(object sender, idReader.RfidEventArgs e)
{
  _idReader.stop();
  Debug.Print("Card scanned: " + e.RFID + ", time: " + e.ReadTime);
  lcdThreadSuspended = true; // <-- _lcdThread.Suspend();
  // ...
  lcdThreadSuspended = false; // <-- _lcdThread.Resume();
  Thread.Sleep(1);
  Thread.Sleep(100);
  _idReader.Start();
}

private static void UpdateDisplay()
{
  while(true)
  {
    if(!lcdThreadSuspended)
    {
      // ...
    }
  }
}

Here is where I found the info about Thread.Sleep(1);

Thanks, I was not aware about that. I still have some doubt about it, but my knowledge of .NET MF internals is rather limited...

Would you happen to have some link to or example code on proper use of the Auto-Reset events? I will google it as well.

The first answer in this StackOverlow question includes code excerpt that shows implementation of such (concurrent) queue.

#6 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 12 April 2011 - 06:24 PM

I'm willing to try anything to get this to work.

Alternatively, you can avoid locking the I2CBus by using I2CDevice directly.

#7 Trey Aughenbaugh

Trey Aughenbaugh

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 12 April 2011 - 07:11 PM

Ok then, could you please try using a boolean variable to indicate and check lcdThread state?

static void _idReader_RfidEvent(object sender, idReader.RfidEventArgs e)
{
  _idReader.stop();
  Debug.Print("Card scanned: " + e.RFID + ", time: " + e.ReadTime);
  lcdThreadSuspended = true; // <-- _lcdThread.Suspend();
  // ...
  lcdThreadSuspended = false; // <-- _lcdThread.Resume();
  Thread.Sleep(1);
  Thread.Sleep(100);
  _idReader.Start();
}

private static void UpdateDisplay()
{
  while(true)
  {
    if(!lcdThreadSuspended)
    {
      // ...
    }
  }
}

Thanks, I was not aware about that. I still have some doubt about it, but my knowledge of .NET MF internals is rather limited...


The first answer in this StackOverlow question includes code excerpt that shows implementation of such (concurrent) queue.

Oddly enough this fake thread sleeping works better than actually suspending the thread.
Thanks for that. The only thing I noticed, is everything seems to slow down.
Instead of my RFID just popping up on the display, it is slowly written out.
Then the next scan it is ok, then it isn't. I'm looking into it.
I guess it is better than locking up.
I attempted to play with a AutoResetEvent or ManualResetEvent, but oddly enough, that did not seem to help.
It was not a full implementation. I added a MRE.WaitOne to the UpdateDisplay function and a MRE.Reset() at the top of the "static void _idReader_RfidEvent" sub and a MRE.Set() at the end.
I'm not fully understanding the way to do this for both threads, but I'll play with it more.

Thanks for all your help.
I'll post my final solution that seem to be working the best.

#8 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 12 April 2011 - 07:25 PM

Oddly enough this fake thread sleeping works better than actually suspending the thread.
Thanks for that. The only thing I noticed, is everything seems to slow down.

I guess that's caused by while(true) loop in UpdateDisplay() - you should probably uncomment Suspend/Resume calls and bring IsAlive check back, to reduce the overhead; or maybe experiment a little bit with thread priorities.

I'll post my final solution that seem to be working the best.

Fingers crossed Posted Image

#9 Trey Aughenbaugh

Trey Aughenbaugh

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 19 April 2011 - 01:31 PM

I ended up using a ManualResetEvent.
Not sure I like how this works or how I'm implementing it.

        private static void UpdateDisplay()
        {
            while (true)//(!lcdThreadSuspended)
            {
                mre.WaitOne();
               var dt = DateTime.Now;

                // write time 
                _exampleSensor.Home();
               _exampleSensor.Write(FillLine(dt.ToString("hh:mm:ss")), 0, 16);

                // write date
                _exampleSensor.Home();
                  _exampleSensor.SetPosition(40);
                  _exampleSensor.Write(FillLine(dt.ToString("MM/dd/yyyy")), 0, 16);
                Thread.Sleep(400);
            }
        }
Not that this is a huge deal, but the mre.Waitone(); must add some extra CPU cycles.
My Clock no longer updates every 500ms(trying 400). My seconds will jump from like 12:00:37 to 12:00:39.
So on the card reader, I added another event.
When the card is scanned, it calls the mre.Reset(); and after scanning is complete and my little message is displayed, it calls mre.Set();
This seems to have fixed my Lockup on the I2C bus.
This entire article has some great information on threads. Threading in C#
There are still some oddities, but seems the locking up is gone.
The oddity comes with garbage being thrown on the screen once I scan the card.
This does not happen all the time, I'm thinking the UpdateDisplay is in the middle of running during a card scan.
I guess I could create another ManualResetEvent for the card scanner.
        private static void UpdateDisplay()
        {
            while (true)//(!lcdThreadSuspended)
            {
                mre.WaitOne();
mreforCard.Reset();
               var dt = DateTime.Now;

                // write time 
                _exampleSensor.Home();
               _exampleSensor.Write(FillLine(dt.ToString("hh:mm:ss")), 0, 16);

                // write date
                _exampleSensor.Home();
                  _exampleSensor.SetPosition(40);
                  _exampleSensor.Write(FillLine(dt.ToString("MM/dd/yyyy")), 0, 16);
                Thread.Sleep(400);
mreforCard.Set();
            }
        }
So the mreforCard.Reset(); means if I card is scanned it would have a WaitOne until it can continue to process the card.
This just seems so messy.
Any one have a better method?
When you call mre.Reset(), is there a way to know if that event is Reset/waiting to be Set()?
I'm thinking in my _rfidCardDataReceived sub, where it calls the RfidEventMRE(true); to do the MRE.Reset();, if it can wait to see that the MRE is actually made it to the WaitOne() signal.
Hope that makes sense.

Update: In my code where I call the RfidEventMRE(true); I added a Thread.Sleep(400);
Figured that should give enough time for the display to be at the WaitOne signal.
Which it does and I no longer get garbage on the display.

Messy Updated Code attached.

Attached Files



#10 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 20 April 2011 - 02:51 AM

Any one have a better method?

I think the original sin here was to let the display be under the control of two different masters who put a lot of effort into safely handing off control of the display from one to the other. This led to all kinds of uncertainty and messiness. Instead I think it is much simpler if you follow the following principles.
  • Dedicate a thread whose job is display updates
  • Make that thread the sole owner of the display (no one else makes calls to the display library)
  • When someone would like to change the display, they post a message to a queue owned by this thread, and this thread processes the next item in the queue when it is ready
  • When there are no messages in the queue, this thread shows the time

The below class, while not 100% complete, is an example of how to do this:

  public class ControlTheDisplay {
    private readonly ExampleLCD _exampleSensor;
    private readonly Thread myThread;
    private readonly object sync=new object();
    private readonly AutoResetEvent are=new AutoResetEvent(false);
    private readonly Queue items=new Queue();

    public ControlTheDisplay(ExampleLCD exampleSensor) {
      _exampleSensor=exampleSensor;
      myThread=new Thread(Run);
    }

    public void Show(string message) {
      lock(sync) {
        items.Enqueue(message);
        are.Set();
      }
    }

    public void Run() {
      while(true) {
        string nextMessage=null;
        lock(sync) {
          if(items.Count>0) {
            nextMessage=(string)items.Dequeue();
          }
        }

        if(nextMessage==null) {
          var dt = DateTime.Now;
          //no pending messages, so write the time and date
          _exampleSensor.Home();
          _exampleSensor.Write(FillLine(dt.ToString("hh:mm:ss")), 0, 16);

          // write date
          _exampleSensor.Home();
          _exampleSensor.SetPosition(40);
          _exampleSensor.Write(FillLine(dt.ToString("MM/dd/yyyy")), 0, 16);

          //wait up to 1/2 second for a message
          are.WaitOne(500, false);
        } else {
          _exampleSensor.Init();
          _exampleSensor.Display(true, true, false, false);
          Thread.Sleep(10);
          _exampleSensor.Clear();
          Thread.Sleep(1);
          _exampleSensor.Home();
          Thread.Sleep(1);
          _exampleSensor.Write(FillLine(nextMessage), 0, 16);
          Thread.Sleep(1000);
          _exampleSensor.Init();
          _exampleSensor.Display(true, true, false, false);
          _exampleSensor.Clear();

          Thread.Sleep(2*1000); //messages are displayed for two seconds
        }
      }
    }
  }

The way you use it is like this:

display=new ControlTheDisplay(_exampleSensor);

... later, when you want to show a message, perhaps from a different thread or a callback:

display.Show(blah.RFID); //or whatever

I think code like this is a lot easier to analyze. For example, you can easily see that there are no deadlocks and no races that would simultaneously call the display library from two different threads. Also, when a new message arrives, if the time is being displayed, it will be immediately overwritten by the message, but if a previous message is being displayed, that previous message will stay up for the full two seconds.

#11 Trey Aughenbaugh

Trey Aughenbaugh

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 20 April 2011 - 05:37 AM

I think the original sin here was to let the display be under the control of two different masters who put a lot of effort into safely handing off control of the display from one to the other. This led to all kinds of uncertainty and messiness. Instead I think it is much simpler if you follow the following principles.

  • Dedicate a thread whose job is display updates
  • Make that thread the sole owner of the display (no one else makes calls to the display library)
  • When someone would like to change the display, they post a message to a queue owned by this thread, and this thread processes the next item in the queue when it is ready
  • When there are no messages in the queue, this thread shows the time

This is an awesome idea Corey!
I will implement and let you know how well it works.
Thanks for taking the time to look this over for me!
This is very easy to analyze, so simple and exactly what this needs.

Thanks again,
Trey Aughenbaugh

#12 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 20 April 2011 - 01:07 PM

I will implement and let you know how well it works.

Great! Maybe you can post a video of your project when it's working. It sounds fun! By the way, there's a bug or two in the above class (e.g. I forgot to call myThread.Start()) but hopefully nothing serious.




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.