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

ShieldBase Threading


  • Please log in to reply
6 replies to this topic

#1 Kirk K

Kirk K

    New Member

  • Members
  • Pip
  • 7 posts

Posted 27 June 2012 - 04:48 PM

Hi,

I have been testing my Netduino Go with a couple Hitec HS311 servos, a Parallex IR sensor and a Parallax Ultrasonic PING))) distance sensor. I am creating this application to read the pins of the ShieldBase on their own thread then update some values back on the initial thread. This works fine when I create the application and a thread that reads the IR input pin state then fires an event based on the state change of the pin. However, when I create a second thread for the PING)) sensor and run the application I get an exception thrown from the ShieldBase. With this I created a locking object that locks each read or write to the ShieldBase pins. This could have an adversed effect based on how long it takes to release the lock on the object. My question is, how does Netduino Go and the ShieldBase handles multi-threading when it comes to the read/write operation? It seems to me that only one pin can be read or written to at any given operation.

Again, I must admit this is my second programming code and so I ask for your patience and understanding. What I am trying to accomplished is to elimate the need to create a while loop that have all my reads and write operation and use event driven style to does different actions that can happen simultaneously.


Here is my code with thread locking:
namespace Multithread_Robot_Application
{


	//Event signature
	public delegate void ObjectStateChangedHandler(object sender, object state);

	public class Program
	{
		object _Lock = new object();
		public event ObjectStateChangedHandler IRValueChanged;
		//Private member variables
		static InputPort _IRSensor;
		static TristatePort _PingSensor;
		static PWM _Servo1;
		static PWM _Servo2;
		static ShieldBase _ShieldBase;

		/// <summary>
		/// Sets the degree to which servo 1 moves to. value 0.1 - 180
		/// </summary>
		public double Servo1Degree
		{
			set
			{
				_Servo1.Duration = CalculatePulseFromDegrees(value);
			}

		}
		/// <summary>
		/// Sets the degree to which servo 2 moves to. value 0.1 - 180
		/// </summary>
		public double Servo2Degree
		{
			set
			{
				_Servo2.Duration = CalculatePulseFromDegrees(value);
			}
		}


		public static void Main()
		{
			// write your code here
			Program _Application = new Program(); //Create the application instance
			_ShieldBase = new ShieldBase(GoSockets.Socket4); //Initialize shieldbase on Go Socket

			//Register for the Infared value changed
			_Application.IRValueChanged += (obj, state) =>
			{
				Debug.Print("The interrupt value is: " + (bool)state);

				//Simulate a double swing door opening and closing
				if((bool)state)
				{
					//Doors open
					_Application.Servo1Degree = 180.0;
					_Application.Servo2Degree = 0.9;
				}
				else
				{
					//Doors closed 
					_Application.Servo1Degree = 90.0;
					_Application.Servo2Degree = 90.0;
				}
			};

			//Initial components connected to the shieldbase
			_Application.InitializeComponents();

			//Starts a thread to listen to the ping sensor
			Thread t1 = new Thread(_Application.TriggerPingSensor);
			t1.Start();

			//Starts a thread to listen to the IR sensor
			Thread t2 = new Thread(_Application.TriggerIRSensor);
			t2.Start();

			Thread.Sleep(Timeout.Infinite);

		}

		/// <summary>
		/// Initializes all the components connected to the 
		/// </summary>
		private void InitializeComponents()
		{
			_Servo1 = new PWM(_ShieldBase.PWMChannels.PWM_0, 20000, 750, PWM.ScaleFactor.Microseconds, false);
			_Servo2 = new PWM(_ShieldBase.PWMChannels.PWM_1, 20000, 1500, PWM.ScaleFactor.Microseconds, false);
			_Servo1.Start();
			_Servo2.Start();

			//Not sure why I could not initialize these on a digital pin 
			_PingSensor = new TristatePort(_ShieldBase.Pins.GPIO_PIN_A1, false, true, Port.ResistorMode.Disabled);
			_IRSensor = new InputPort(_ShieldBase.Pins.GPIO_PIN_A0, false, Port.ResistorMode.Disabled);
		}




		/// <summary>
		/// Calculates the duration for the degree
		/// </summary>
		/// <param name="degree">double: 0.1 - 180.</param>
		/// <returns>uint  representation</returns>
		private uint CalculatePulseFromDegrees(double degree)
		{

			uint neutralPulse = 1500; //This represent the neutral pulse time (in microsecond) of the servo typically when the servo is at 90 degrees. ; 
			float factor = neutralPulse / 90; //This value represent pulse time when the servo is at 1 degree; 
			uint outPulse = (uint)(factor * degree);
			return outPulse;
		}
		#region Public Methods

		/// <summary>
		/// This reads the state of the port on a Parallax IR Sensor.
		/// </summary>
		public void TriggerIRSensor()
		{
			bool oldValue = false;
			while(true)
			{
				bool ret;
				lock(_Lock)
				{
					ret = _IRSensor.Read();
				}
				if(ret != oldValue)
				{
					oldValue = ret;
					Debug.Print(ret.ToString());

					//Raise event if the Infared value changes
					if(IRValueChanged != null)
						IRValueChanged(this, ret);
				}


				Thread.Sleep(50);
			}
		}

		/// <summary>
		/// Uses a tristate port to send a pulse to the 
		/// </summary>
		public void TriggerPingSensor()
		{
			while(true)
			{
				var val = false;
				lock(_Lock)
				{
					_PingSensor.Active = true;
					_PingSensor.Write(true);
					_PingSensor.Active = false;
				}
				int timeElapsed = 0;
				while(val != true && timeElapsed != 18)
				{
					timeElapsed++;
					lock(_Lock)
					{
						val = _PingSensor.Read();
					}
					if(val)
						Debug.Print(val.ToString() + " @ " + timeElapsed);
					Thread.Sleep(1);
				}
				Thread.Sleep(2);
				Debug.Print(val.ToString());
			}
		}
		#endregion
	}
}

Thanks

#2 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 27 June 2012 - 07:37 PM

With this I created a locking object that locks each read or write to the ShieldBase pins. This could have an adversed effect based on how long it takes to release the lock on the object. My question is, how does Netduino Go and the ShieldBase handles multi-threading when it comes to the read/write operation? It seems to me that only one pin can be read or written to at any given operation.

Again, I must admit this is my second programming code and so I ask for your patience and understanding. What I am trying to accomplished is to elimate the need to create a while loop that have all my reads and write operation and use event driven style to does different actions that can happen simultaneously.

This is sort of what I would expect as whilst the Shieldbase may expose multiple IO channels you are effectively only using one - namely the channel between the GO! and the Shieldbase.

You raise an interesting point though. .NET threading is fixed 20ms timeslices if memory serves. So what happens if you have thread 1 perform a WriteRead operation to the Shieldbase at the end of its timeslice. Is it possible for the framework to switch to Thread 2 before the end of the WriteRead operation or is the WriteRead opertion guaranteed to be atomic? Any NETMF gurus out there with the answer?

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#3 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 27 June 2012 - 07:59 PM

Hi Kirk, Very interesting. We use the Shield Base with multiple threads regularly and haven't seen this issue. But we may not have created enough work to trigger a thread switch :) To confirm -- when you use the lock mechanism then it works fine for you? But when you don't use the lock mechanism, you get exceptions? If that's the case, we can fix that in an update. Virtual IO resources should have the same thread safety as physical IO resources...you shouldn't have to worry about it. Chris

#4 Kirk K

Kirk K

    New Member

  • Members
  • Pip
  • 7 posts

Posted 27 June 2012 - 09:08 PM

Hi Kirk,

Very interesting. We use the Shield Base with multiple threads regularly and haven't seen this issue. But we may not have created enough work to trigger a thread switch :)

To confirm -- when you use the lock mechanism then it works fine for you? But when you don't use the lock mechanism, you get exceptions?

If that's the case, we can fix that in an update. Virtual IO resources should have the same thread safety as physical IO resources...you shouldn't have to worry about it.

Chris


Thanks Guys,

The locking mechanism does work fine as presented in the code. Without it, as soon as a secondary thread starts a read/write operation on another pin while another thread read/write is in process, the ShieldBase throws an exception. I can easily reproduce the exceptions with the above locking mechanism removed.

#5 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 28 June 2012 - 12:03 AM

Hi Kirk,

The locking mechanism does work fine as presented in the code. Without it, as soon as a secondary thread starts a read/write operation on another pin while another thread read/write is in process, the ShieldBase throws an exception. I can easily reproduce the exceptions with the above locking mechanism removed.

If you can create a ~10 line repro, we'd love to test it out. I'll put this on our short list, to address with the UART->SPI update. We'll be updating the C# class drivers anyway, so this is a good issue to work out :)

Chris

#6 Kirk K

Kirk K

    New Member

  • Members
  • Pip
  • 7 posts

Posted 28 June 2012 - 01:45 AM

Hi Kirk,


If you can create a ~10 line repro, we'd love to test it out. I'll put this on our short list, to address with the UART->SPI update. We'll be updating the C# class drivers anyway, so this is a good issue to work out :)

Chris


Here goes
 public class Program
    {
        private static ShieldBase _ShieldBase;
        private static InputPort _Pin1;
        private static InputPort _Pin2;
        public static void Main()
        {
            // write your code here
            _ShieldBase = new ShieldBase(GoSockets.Socket4);
            _Pin1 = new InputPort(_ShieldBase.Pins.GPIO_PIN_A4, false, Port.ResistorMode.Disabled);
            _Pin2 = new InputPort(_ShieldBase.Pins.GPIO_PIN_A5, false, Port.ResistorMode.Disabled);
            Program prog = new Program();
            Thread sec1 = new Thread(prog.ReadPin1);
            sec1.Start();
            prog.ReadPin2();
        }
        public void ReadPin1()
        {
            while(true)
            {
                var ret = _Pin1.Read();
                Thread.Sleep(500);
            }
        }
        public void ReadPin2()
        {
            while(true)
            {
                _Pin2.Read();
                Thread.Sleep(50); 
            }
            
        }

    }


#7 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 28 June 2012 - 02:52 AM

Thanks Kirk!




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.