Netduino home hardware projects downloads community

Jump to content


Photo

SparkFun Inventor's Kit / CIRC-05 | 8 More LEDs - 74HC595 Shift Register


  • Please log in to reply
18 replies to this topic

#1 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 26 April 2012 - 02:37 AM

Thanks to the efforts of .:oomlout:. and their Netduino Experimentation Kit, and our community member ItsDan on the Netduino Wiki, I was able to get the 74HC595 Shift Register example to work on my Netduino Go.

There were a couple of hurdles that made this a little more involved than adding references to the Netduino Go, the Shield Base and it's digital pins. More specifically it has to do with the way the current Shield Base realease and .NETMF 4.2 QFE1 are creating dependent objects out of order when trying to create them at the static class level. A quick solution is to pull those objects locally into the Main method however we lose global access to them. This is problematic for this example where in several of the methods found in this project require global access to those objects. A simple way to get around this problem was to pass the clock, data, and latch objects as arguments in the updateLEDs and changeLED methods. This isn't the prettiest solution but it works. Any other suggestions would be greatly welcomed. Hopefully the static declarations will be fixed in a future update.

For wiring and original code please consult the Wiki tutorial found here:
SparkFun Inventor's Kit / CIRC-05 | 8 More LEDs - 74HC595 Shift Register

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoGo;
using NetduinoGo;

namespace NetduinoGo_ShieldBaseShiftRegister
{
    public class Program            
    {
        static ShieldBase shieldBase = new ShieldBase(GoSockets.Socket5);

        //As of .NetMF 4.2 QFE1 we cannot create these as static objects without throwing an error.
        //static OutputPort data = new OutputPort(shieldBase.Pins.GPIO_PIN_D2, false);  //Set pin 2 as an output and our data line
        //static OutputPort clock = new OutputPort(shieldBase.Pins.GPIO_PIN_D3, false); //Set pin 3 as an output and our clock line
        //static OutputPort latch = new OutputPort(shieldBase.Pins.GPIO_PIN_D4, false); //Set pin 4 as an output and our latch line
        
        public static void Main()   
        {
            OutputPort data = new OutputPort(shieldBase.Pins.GPIO_PIN_D2, false);  //Set pin 2 as an output and our data line
            OutputPort clock = new OutputPort(shieldBase.Pins.GPIO_PIN_D3, false); //Set pin 3 as an output and our clock line
            OutputPort latch = new OutputPort(shieldBase.Pins.GPIO_PIN_D4, false); //Set pin 4 as an output and our latch line
                        
            int delayTime = 100; //the number of milliseconds to delay between LED updates

            while (true)  
            {
                
                for (int i = 0; i < 256; i++)
                {
                    updateLEDs(i, data, clock, latch);    //display the variable i on the LEDs in binary form
                    Thread.Sleep(delayTime);              //wait delayTime milliseconds
                }


                //Uncomment this code, and comment out the previous for loop to cause the lights to light up one after another and then off 
                
                //for (int i = 0; i < 8; i++)
                //{
                //    changeLED(i, true, data, clock, latch);
                //    Thread.Sleep(delayTime);
                //}
                //for (int i = 0; i < 8; i++)
                //{
                //    changeLED(i, false, data, clock, latch);
                //    Thread.Sleep(delayTime);
                //} 
            }
        }

        /// <summary>
        /// sends the LED states set in value to the 74HC595 sequence
        /// </summary>
        /// <param name="value">the 8 bit number to be displayed in binary on the LEDs</param>
        /// <param name="data">The OutputPort data </param>
        /// <param name="clock">The OutputPort clock </param>
        /// <param name="latch">The OutputPort latch </param>
        static void updateLEDs(int value, OutputPort data, OutputPort clock, OutputPort latch)
        {
            latch.Write(false);          //Pulls the chips latch low

            for (int i = 0; i < 8; i++)  //Will repeat 8 times (once for each bit)
            {
                int bit = value & 0x80;  //We use a "bitmask" to select only the eighth
                //bit in our number (the one we are addressing this time through
                value = value << 1;      //we move our number up one bit value so next time bit 7 will be
                //bit 8 and we will do our math on it
                if (bit == 128)
                {
                    data.Write(true);    //if bit 8 is set then set our data pin high
                }
                else
                {
                    data.Write(false);   //if bit 8 is unset then set the data pin low
                }

                clock.Write(true);      //the next three lines pulse the clock pin      
                Thread.Sleep(1);
                clock.Write(false);
            }
            latch.Write(true);          //pulls the latch high shifting our data into being displayed
        }

        //These are used in the bitwise math that we use to change individual LEDs
        //For more details http://en.wikipedia.org/wiki/Bitwise_operation
        static int[] bits = { 1, 2, 4, 8, 16, 32, 64, 128 };
        static int[] masks = { 254, 253, 251, 247, 239, 223, 191, 127 };
        static int ledState = 0;
        ///<summary>
        /// changeLED(int led, int state) - changes an individual LED 
        /// <param name="led">the LED we are addressing</param>
        /// <param name="state">whether that LED is to be on or off</param>
        /// <param name="data">The OutputPort data </param>
        /// <param name="clock">The OutputPort clock </param>
        /// <param name="latch">The OutputPort latch </param>
        ///</summary>    
        public static void changeLED(int led, bool state, OutputPort data, OutputPort clock, OutputPort latch)
        {
            ledState = ledState & masks[led];               //clears ledState of the bit we are addressing
            if (state) { ledState = ledState | bits[led]; } //if the bit is on we will add it to ledState
            updateLEDs(ledState, data, clock, latch);       //send the new LED state to the shift register
        }

    }                               
}



#2 BlackLamb

BlackLamb

    Member

  • Members
  • PipPip
  • 12 posts

Posted 26 April 2012 - 04:21 AM

I handled the static initialization issue by by declaring the static objects at the class level but not initializing them. Thin my first lines in my main initialize them all.

public class program
{
        static ShieldBase shieldBase = new ShieldBase(GoSockets.Socket5);

        static OutputPort data;
        static OutputPort clock;
        static OutputPort latch;
        
        public static void Main()   
        {
            shieldBase = new ShieldBase(GoSockets.Socket5);
            data = new OutputPort(shieldBase.Pins.GPIO_PIN_D2, false);  //Set pin 2 as an output and our data line
            clock = new OutputPort(shieldBase.Pins.GPIO_PIN_D3, false); //Set pin 3 as an output and our clock line
            latch = new OutputPort(shieldBase.Pins.GPIO_PIN_D4, false); //Set pin 4 as an output and our latch line
        }
}


#3 Fabian Nunez

Fabian Nunez

    Member

  • Members
  • PipPip
  • 10 posts

Posted 26 April 2012 - 04:35 AM

The reason you're having problems is that .net doesn't guarantee the initialization order of static fields in a class. If you need them initialized in a particular way (eg they depend on each other), you have to write a static constructor for the class. This is just a method that has the same name as the class, just like a regular constructor, but has the 'static' qualifier in front of it. This static constructor will be called sometime between the module being loaded and the first time the class is used.

public class program
{
        static ShieldBase shieldBase;
        static OutputPort data;
        static OutputPort clock;
        static OutputPort latch;
        
        static program()
        {
            shieldBase = new ShieldBase(GoSockets.Socket5);
            data = new OutputPort(shieldBase.Pins.GPIO_PIN_D2, false);  //Set pin 2 as an output and our data line
            clock = new OutputPort(shieldBase.Pins.GPIO_PIN_D3, false); //Set pin 3 as an output and our clock line
            latch = new OutputPort(shieldBase.Pins.GPIO_PIN_D4, false); //Set pin 4 as an output and our latch line
        }

        public static void Main()   
        {
           // everything is already initialized when you enter this function
        }
}


#4 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7142 posts
  • LocationNew York, NY

Posted 26 April 2012 - 04:44 AM

Very cool, Gutworks. Glad to see more and more samples running on Shield Base. If you have a video, please let us know...we'd be happy to Tweet and help show it off for you. BTW, Fabian--thanks for the notes on static constructors and initialization order. That is one of those behaviors in .NET (which is even more subtle in NETMF) that is good to understand. Chris

#5 Fabian Nunez

Fabian Nunez

    Member

  • Members
  • PipPip
  • 10 posts

Posted 26 April 2012 - 04:52 AM

NP. Always glad to share my obscure .net knowledge :)

#6 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 26 April 2012 - 01:03 PM

BlackLamb and Fabien, thank you so much for the perfect explanations and code samples. That is awesome and the exact sort of thing I was wishing to see by posting this. I will post an update to the code later today once I get back to my office. Chris, I would post a video if my web camera wasn't 10+ years old. I think my daughter's barbie cam has a better image. Hmmm, maybe...nevermind :)

#7 BlackLamb

BlackLamb

    Member

  • Members
  • PipPip
  • 12 posts

Posted 26 April 2012 - 04:23 PM

The static constructor was new to me I'm going to switch all my programs to using that instead now. I like the idea of it being cleaner and separate from main.

#8 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 26 April 2012 - 05:47 PM

The reason you're having problems is that .net doesn't guarantee the initialization order of static fields in a class. If you need them initialized in a particular way (eg they depend on each other), you have to write a static constructor for the class. This is just a method that has the same name as the class, just like a regular constructor, but has the 'static' qualifier in front of it. This static constructor will be called sometime between the module being loaded and the first time the class is used.

public class program
{
        static ShieldBase shieldBase;
        static OutputPort data;
        static OutputPort clock;
        static OutputPort latch;
        
        static program()
        {
            shieldBase = new ShieldBase(GoSockets.Socket5);
            data = new OutputPort(shieldBase.Pins.GPIO_PIN_D2, false);  //Set pin 2 as an output and our data line
            clock = new OutputPort(shieldBase.Pins.GPIO_PIN_D3, false); //Set pin 3 as an output and our clock line
            latch = new OutputPort(shieldBase.Pins.GPIO_PIN_D4, false); //Set pin 4 as an output and our latch line
        }

        public static void Main()   
        {
           // everything is already initialized when you enter this function
        }
}

Hi Fabian,

I had a chance to try the example you have above and I am now getting the error:

An unhandled exception of type 'System.Exception' occurred in NetduinoGo.ShieldBase.dll


Any ideas what I may be doing wrong? I have also attached a screenshot of the error.

Thanks,
Steve

Attached Files



#9 EricMeyer

EricMeyer

    Advanced Member

  • Members
  • PipPipPip
  • 35 posts
  • LocationAllen, Texas

Posted 26 April 2012 - 07:08 PM

It is possible that the initialization is still a problem when it is in the static constructor. I'm betting it is being done before something in the NetduinoGO initialization has completed. It may not be safe to call into any of the NetduinoGO runtime code until Man is called. Just a guess. -Eric

#10 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 26 April 2012 - 07:15 PM

Hi,
the static contructor is a solution but I prefer a diffrent approach. My main program always looks like this:
using System.Threading;

namespace Netduino.Extender
{
	public class Program
	{
		private static ImportantController _ImportantController;
		
		public static void Main()
		{
			_ImportantController = new ImportantController();
			Thread.Sleep(Timeout.Infinite);
		}
	}
}
Now I'm done with all the static stuff. The real party starts in my "ImportantController" (in real life the class name is not "ImportantController" of course).
using ...;

namespace Netduino.Extender.Heating
{
	public class ImportantController 
	{
		private readonly Lcd _Lcd;
		private readonly SwitchModule _SwitchModule;
		
		public ImportantController()
		{
			ILcdTransferProvider lcdTransportProvider = new Shifter74Hc595LcdTransferProvider(...);
			_Lcd = new Lcd(lcdTransportProvider, ...);
			_SwitchModule = new SwitchModule(...);
			_SwitchModule.ItemChanged += SwitchModuleItemChanged;
		}
		...
		void SwitchModuleItemChanged()
		{
			_Lcd.Write();
			...
		}
	}
}

In my opinion the advantage is that only one class (the "ImportantController") must be declared static. Inside this class you can decide if the objects should be instanciated more than once or if it should be static for some reason. Also if you want to refactor your code you don't have to remove all "static" modifiers. For example if you build a class with a field to count something and you need this class a second time the field shouldn't be static because your counter would count the "somethings" of all your instances.
Maybe the code in my example is a little bit desktop style programming but I don't know a reason against my approach. If there is one I would be happy to know it.

Sorry for the short and confused explanation but I've cought a cold and my brain is alread in bed sleeping :-)

Regards
Guido

#11 Fabian Nunez

Fabian Nunez

    Member

  • Members
  • PipPip
  • 10 posts

Posted 26 April 2012 - 09:24 PM

Static objects are always tricky, especially if you don't own all the modules as is the case here. What is probably happening is that your static initializer is being called before those of the other classes you're calling. I think the best fix is to use gbreder's solution.

#12 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 27 April 2012 - 09:01 PM

I finally had a breakthrough with all of your suggestions and ItsDan's suggestion of the Singleton method. I'm not sure if I'm following the exact guidelines but it seems to work. Any input would be greatly welcomed.

I would also add that these methodologies can temporarily inhibit the beginner folk, like myself, from using some of the basic Netduino samples. Hopefully this process can and will be updated in future revisions, and perhaps this code can help others.

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoGo;
using NetduinoGo;

namespace NetduinoGo_ShieldBaseShiftRegisterVer2
{
    public class Program            
    {
        static ShieldBaseController shieldBaseController; 
        
        public static void Main()   
        {
            shieldBaseController = ShieldBaseController.GetInstance();
            var data = shieldBaseController.Data;

            int delayTime = 100; //the number of milliseconds to delay between LED updates

            while (true)  
            {
                
                for (int i = 0; i < 256; i++)
                {
                    updateLEDs(i);           //display the variable i on the LEDs in binary form
                    Thread.Sleep(delayTime); //wait delayTime milliseconds
                }


                //Uncomment this code, and comment out the previous for loop to cause the lights to light up one after another and then off 
                
                //for (int i = 0; i < 8; i++)
                //{
                //    changeLED(i, true);
                //    Thread.Sleep(delayTime);
                //}
                //for (int i = 0; i < 8; i++)
                //{
                //    changeLED(i, false, data, clock, latch);
                //    Thread.Sleep(delayTime);
                //} 
            }
        }

        /// <summary>
        /// sends the LED states set in value to the 74HC595 sequence
        /// </summary>
        /// <param name="value">the 8 bit number to be displayed in binary on the LEDs</param>
        /// <param name="data">The OutputPort data </param>
        /// <param name="clock">The OutputPort clock </param>
        /// <param name="latch">The OutputPort latch </param>
        static void updateLEDs(int value)
        {
            shieldBaseController.Latch.Write(false);  //Pulls the chips latch low

            for (int i = 0; i < 8; i++)  //Will repeat 8 times (once for each bit)
            {
                int bit = value & 0x80;  //We use a "bitmask" to select only the eighth
                //bit in our number (the one we are addressing this time through
                value = value << 1;      //we move our number up one bit value so next time bit 7 will be
                //bit 8 and we will do our math on it
                if (bit == 128)
                {
                    shieldBaseController.Data.Write(true);    //if bit 8 is set then set our data pin high
                }
                else
                {
                    shieldBaseController.Data.Write(false);   //if bit 8 is unset then set the data pin low
                }

                shieldBaseController.Clock.Write(true);      //the next three lines pulse the clock pin      
                Thread.Sleep(1);
                shieldBaseController.Clock.Write(false);
            }
            shieldBaseController.Latch.Write(true);          //pulls the latch high shifting our data into being displayed
        }

        //These are used in the bitwise math that we use to change individual LEDs
        //For more details http://en.wikipedia.org/wiki/Bitwise_operation
        static int[] bits = { 1, 2, 4, 8, 16, 32, 64, 128 };
        static int[] masks = { 254, 253, 251, 247, 239, 223, 191, 127 };
        static int ledState = 0;
        ///<summary>
        /// changeLED(int led, int state) - changes an individual LED 
        /// <param name="led">the LED we are addressing</param>
        /// <param name="state">whether that LED is to be on or off</param>
        /// <param name="data">The OutputPort data </param>
        /// <param name="clock">The OutputPort clock </param>
        /// <param name="latch">The OutputPort latch </param>
        ///</summary>    
        public static void changeLED(int led, bool state)
        {
            ledState = ledState & masks[led];               //clears ledState of the bit we are addressing
            if (state) { ledState = ledState | bits[led]; } //if the bit is on we will add it to ledState
            updateLEDs(ledState);                           //send the new LED state to the shift register
        }


        ///<summary>
        /// ShieldBaseController - a 'Singelton' class http://msdn.microsoft.com/en-us/library/ff650316.aspx
        ///</summary> 
        public class ShieldBaseController
        {
            private static ShieldBaseController instance;
            private static object syncRoot = new Object();
            private ShieldBase shieldBase;
            private OutputPort data;
            private OutputPort clock;
            private OutputPort latch;

            private ShieldBaseController()
            {
                shieldBase = new ShieldBase(GoSockets.Socket5);
                data = new OutputPort(shieldBase.Pins.GPIO_PIN_D2, false);  //Set pin 2 as an output and our data line
                clock = new OutputPort(shieldBase.Pins.GPIO_PIN_D3, false); //Set pin 3 as an output and our clock line
                latch = new OutputPort(shieldBase.Pins.GPIO_PIN_D4, false); //Set pin 4 as an output and our latch line
            }
            
            public static ShieldBaseController GetInstance()
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                            instance = new ShieldBaseController();
                    }
                }
                return instance;
            }
            
            public OutputPort Data
            {
                get { return data; }
            }

            public OutputPort Latch
            {
                get { return latch; }
            }

            public OutputPort Clock
            {
                get { return clock; }
            }
        }

    }                               
}



#13 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 27 April 2012 - 10:55 PM

Hi Gutworks,
a singleton can work until you add your second shield base. If you will never ever do this you code is fine.

A singelton if often used if the following is true:
  • Your class can only be instantiated once, there may not be a second instance of the class (like I2C bus on the classic Neduino).
  • Your class will be used in multiple other classes in your code.
  • You have no idea which code will call your class when. And so you have no idea who has to call the contructor.
Without the singleton pattern you would have to build the instance yourself (call the contructor) and give it to every class which may need it. This can get very ugly...

The use of an normal contructor (as stated in my earlier post) would suffice because your program calls "GetInstanc" once, your singleton will be used in only one class and you know when to call the constructor.

It isn't my intention to be rude and I'm absolutly thrilled that you have coded a beautiful singleton pattern. But simpler may be better.

Regards
Guido

#14 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 27 April 2012 - 11:43 PM

It isn't my intention to be rude and I'm absolutly thrilled that you have coded a beautiful singleton pattern. But simpler may be better.

Guido, if your intention was to be rude, then you really would have done a poor job :) We Canadians have much thicker skin than that. In fact you have been quite the opposite. These responses have been great and are really forcing me to stretch my current, albeit, rusty developer skills.

I tried to follow your example but failed to get my code to work. In trying to research and find solutions it lead me to the Singleton method, which I thought closely resembled your example. My noobness really shines through when I say I don't really know the major differences between the two examples. In my reading I had missed the point about the Singleton only being able to be instantiated once, even though I had tried to make it thread safe.

So this is where I admit defeat, and ask for more help. Is is possible to provide some specific examples on how to alter my existing code so that it conforms to best practices? Again, I really appreciate all the help thus far from everyone and it has been a fantastic learning experience. I won't tell you how long it's taken me to get to this point out of sheer embarrassment :)

Thanks,
Steve

#15 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 28 April 2012 - 09:26 AM

Hi,
I'm happy to help. I've mode some major and some minor changes. All major changes are listed below. The whole result was zipped and attached. But be aware that my Netduino GO hasn't arrived yet. So the code is completely untested.

  • The "controller" isn't nested anymore. A nested type is only used if it is related to its parent in some way.
  • The method "UpdateLeds" was moved inside "ShieldBaseController" because it was accessing only OutputPorts from it.
  • All output ports are now private.
  • The statement "bits[led]" and its "bits" array was replaced by a bit shift operation "1 << led".
  • The "mask[led]" was also expressed as (inverted) bit shift "~(1<<led)". For example 1<<4 would result in 16 (or 0x10) and the "~" will invert every bit in the byte so you will get 0xEF or 239. Less manual calculations and less code results in less errors :-)
  • Some minor renaming and other unimportant stuff

There is still room for improvement. You could change your SPI bit bang mode to native SPI from the Netduino. I've attached a class for the 74HC595 from my classic Netduino as example. You can also provide the parameter "GoSockets.Socket5" for your "new ShieldBase()" through the constructor of the class.

Regards
Guido

#16 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 28 April 2012 - 09:30 AM

The whole result was zipped and attached.

Forgot to press the "Attach This File" button ;)

Attached Files



#17 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 28 April 2012 - 04:20 PM

I'm happy to help. I've mode some major and some minor changes.

Guido, this is perfect. The code runs perfectly with the Netduino Go Shield Base. I'm taking some time today to go over the code and study the changes so that I can implicitly understand them. This is exactly what I was looking for and needed. Previously I got so stuck into the existing code and it's framework and I didn't think about pulling the other methods inside of the controller and creating new classes. After managing web developers for a few years, I'm having to learn this stuff all over again after placing my own coding skills on a shelve for too long.

Thanks a ton,
Steve

#18 Limpygnome

Limpygnome

    New Member

  • Members
  • Pip
  • 7 posts

Posted 06 May 2012 - 06:47 PM

How would you get two shift registers working?

#19 gbreder

gbreder

    Advanced Member

  • Members
  • PipPipPip
  • 53 posts
  • LocationGermany

Posted 09 May 2012 - 08:23 PM

How would you get two shift registers working?

You would put the second shift register also on SPI1 and define a different latch pin (aka slave select / device select).

If it is a 74HC595 shifter you can use something like this:
public class Shifter74Hc595Extender
{
	public Shifter74Hc595Extender(SPI.SPI_module spiBus, Cpu.Pin latchPin)
	{
		var spiConfig = new SPI.Configuration(
			latchPin, //latchPin (aka SlaveSelect),
			false, // active state
			0, // setup time
			0, // hold time 
			false, // clock idle state
			true, // clock edge
			1000, // clock rate
			spiBus);

		_Spi = new SPI(spiConfig);	
	}

	public void WriteData(byte data)
	{
		_WriteBuf[0] = data;
		_Spi.Write(_WriteBuf);
	}
}
I hope this helps...

Regards
Guido




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

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