Sunday, 24 April 2016

Arduino Platform - Working with Shift Registers

Note: I originally published this a long time ago on my old website (accidently killed by my ISP) and also on CodeProject

Introduction

Following on from the previous Arduino articles I have written on CodeProject, in this article we shall take a quick look at shift registers. Why? Well, this article is a combination of two smaller articles I had previously written on my own website, and I thought it would be good to share these here, which would extend the articles reach further as well as further extend the Arduino collection of articles on CodeProject. The articles on my website are the 2 most popular of all the articles on there, and going by the analytics data from Google, these are direct search results on the subject.

In this article, we will start of with a single shift register, then increase to dual shift registers and then see what can be done to take things further.


What are Shift Registers, Why Do We Need Them?

Shift registers are an integrated circuit that can be used to change the source data from a serial stream to a parallel stream. They can be used to do other things as well, e.g. serial to serial, parallel to serial, etc. but this is dependant on which type of shift register you are using.
In this article, we are only going to focus on serial to parallel implementation. The image below shows the most basic representation of what is happening, the serial data is clocked into the chip, and the representative bits are placed on the outputs. The outputs are held at the appropriate state until they are changed.

On the Arduino platform, each of the different types of boards, e.g. Duemilanove/Uno or Mega, have differing numbers of inputs and outputs available to use as connections to the real world. By using a shift register, it is possible to extend the available outputs. In the following examples, you will see how first we will control 8 LEDs with just 3 outputs, and then how we will go onto control 16 LEDs with the same 3 outputs. You can of course, go and scale this up as far as your project requires.

Depending on the nature of your project, e.g., one that makes use of a large number of LEDs, you may wish to design your project from the start using shift registers, making it a lot easier to add more in the future, or to save the other IO pins for devices such as switches or motor controllers.


What Hardware is in Use with this Article?

Throughout this example, the hardware being used consists of:

  • Arduino Duemilanove Board
  • Bunch of typical 5V LEDs
  • Suitable resistors for current limiting
  • Shift Register type 74HC595N

As with any electronic project, it is important to take recognition of the limitations of any electronic device, and it is the responsibility of the individual building the circuit to check that they are building within the design limitations of the device(s). Slight variations between manufacturers and suppliers may result in damage to equipment, so always check what exactly you are using. At the end of the day, only you know what you are using and articles such as these are only a guide.

Starting Simple - Single Shift Register

Ok, let's start with a single shift register, and see what is required to get this going using an Arduino. The image below is the schematic used for the initial examples.



As you can see from the schematic, we are picking up +5V and a Ground connection at the Arduino board, Digital IO pins 2,3,4 are being used for the Data, Latch and the Clock. The Shift Register also requires a couple of links to pull the Reset High and the Enable Low to make these examples work. You could, of course, control these pins using the Arduino, but for these simple examples, there is no requirement to do this. A more complex project might want to take full control of the IC, but that would be up to the circuit developer.

For the coding, we will use the started Arduino IDE available for the Arduino website.

This project's code is no different from any other Arduino project and follows the same Initialisation, Setup and Loop Structure. There are no special libraries required to be included or initialised within the code, and standard methods are all that is required to make this work. The basic structure is as follows:
int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

//Add any other variables used by the main program loop here

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    //Main program loop as required to go here
}


Example 1 - Single Shift Register - Binary Counter

The first example will simply create a binary counter, and show this incrementing on the LEDs.
To do this, we must first add a variable to the initialisation section which will store the counter, and then add a standard for loop to do the incrementing in the main program loop method. As this is the first example, I will show the complete program, so you can see where the variables and the loop need to be added.

int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int counter = 0;       //The counter for storing the byte value

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (counter = 0; counter < 256; counter++)
    {
        digitalWrite(latchPin, LOW);          //Pull latch LOW to start sending data
        shiftOut(dataPin, clockPin, MSBFIRST, counter);         //Send the data
        digitalWrite(latchPin, HIGH);         //Pull latch HIGH to stop sending data
        delay(500);
    }
}

As you can see in the main program loop, a simple for loop increments the counter byte. When we are ready to send the new byte value to the shift register, we must first pull the latchPin LOW. We then send out the data to the shift register (in the correct bit orientation). Once the data has been sent, we pull the latchPin HIGH again to signal that we are finished. The delay instruction simply allows us to control how quickly we write out the bytes to the register, in this case, we are waiting 500milliseconds between increments.


Example 2 - Single Shift Register - LED Sweep

No article involving LEDs and shift registers would be complete unless it includes the trusty Knight Rider KITT Light sweep!

To do this, first we need to set up a byte array to store each of the values in the sweep. If you think about what is happening, all we are doing is turning each bit on and off in sequence, so it would be 10000000, 01000000, 00100000, 00010000, etc. etc. These values are then in turn written out to the shift register.
int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int seq[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2};       //The byte sequence

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (int n = 0; n < 14; n++)
    {
        digitalWrite(latchPin, LOW);             //Pull latch LOW to start sending data
        shiftOut(dataPin, clockPin, MSBFIRST, seq[n]);          //Send the data
        digitalWrite(latchPin, HIGH);            //Pull latch HIGH to stop sending data
        delay(75);
    }
}

There you have it, every 75ms the next byte in the sequence is written to the output.

Moving On - Dual Shift Registers

One of the nice features about the Shift Registers is they have a pin called Overflow, and what this basically allows us to do is write more data to the first shift register than it can handle, and it will overflow this excess to this pin. So to extend the chain, we simply connect the first shift registers Overflow to the second shift registers Data pin. This way, if we write out 2 bytes to the first shift register, the first byte is passed onto the second shift register, and the second byte is retained within the first shift register. The schematic below shows how this cascade is connected up, I have also added 3 additional LEDs so we can monitor the DATA, CLOCK and OVERFLOW pins.




For the dual shift register setup, the code is almost identical, the only difference is that we add a second byte array variable and an additional line to write the second byte. To demonstrate this, we will extend the binary counter example from 8 to 16 Bit.

Example 3 - Dual Shift Register - 16Bit Binary Increment

int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int byte1 = 0;         //The counter for storing the byte #1 value
int byte2 = 0;         //The counter for storing the byte #2 value

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (byte2 = 0; byte2 < 256; byte2++)               //Outer Loop
    {
        for (byte1 = 0; byte1 < 256; byte1++)            //Inner Loop
        {
            digitalWrite(latchPin, LOW);           //Pull latch LOW to start sending data
            shiftOut(dataPin, clockPin, MSBFIRST, byte1); //Send the data byte 1
            shiftOut(dataPin, clockPin, MSBFIRST, byte2); //Send the data byte 2
            digitalWrite(latchPin, HIGH);          //Pull latch HIGH to stop sending data
            delay(250);
        }
    }
}

Example 4 - Dual Shift Register - KITT is back!


Just like the TV program, KITT made a re-appearance, but the new version had a dual sweep light on the front of the car. To achieve this, we need two arrays to store the left and right (Byte 1 + 2) values, and again an additional line to write out the second byte.

int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int seq1[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2};  //The array for storing the 
                        // byte #1 value
int seq2[14] = {128,64,32,16,8,4,2,1,2,4,8,16,32,64};  //The array for storing the 
                        // byte #2 value

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (int x = 0; x < 14; x++)         //Array Index
    {
        digitalWrite(latchPin, LOW);            //Pull latch LOW to start sending data
        shiftOut(dataPin, clockPin, MSBFIRST, seq1[x]);         //Send the data byte 1
        shiftOut(dataPin, clockPin, MSBFIRST, seq2[x]);         //Send the data byte 2
        digitalWrite(latchPin, HIGH);           //Pull latch HIGH to stop sending data
        delay(75);
    }
}


Going Further - 3 Registers and Beyond

As you can probably work out, you can continue to add more registers to the chain, by simply linking the OVERFLOW to the DATA as required. You will then need to think about how you want to control and handle the bytes and then write these out in the correct sequence. Of course, you will need to optimise the code to fit within the limitations of the Arduino, but hey, maybe one day you could even build a (no, it is not mine) 8x8x8 Arduino LED Cube or something bigger! I might even get round to having a go at one of these one day...

References




Arduino Platform - Differential Gap Control (Solar Tracker)


Note: I originally published on my old website (which was accidently killed by the ISP) and also published on the CodeProject.

Introduction

This article gives a very simple introduction into writing a differential gap controller on the Arduino platform. I have used a Duemilanove for this example.

This project consists of two inputs which track light levels on the East/West which are then used to move a servo, rotating East/West tracking the strongest light level.

The crude prototype in all its glory......the surplus MSDN discs have found a use at last:

Schematic



How it works

The two light sensors are basic photoresistors, these are mounted at 90' to each other, imagine these are your eyes, if you look straight ahead at a wall, the left eye would point 45' to the left, and the right eye 45' to the right. These sensors are also shielded, so they will see the brightest light levels when pointing directly at the light source. If the light source then moves, a shadow is cast onto the sensor, changing its resistance.

When tracking the sunlight, e.g. for a solar panel, you would want the maximum sunlight intensity, to achieve this, both sensors would therefore need to see the same intensity of light. This is how we determine the input for the gap controller.

Read both of the input sensor values, and do a comparison, 0 difference means they are at the same light level, a -ve error value means the light is brighter to the right, and a +ve error value means the light is brighter to the left.

The servo is then sent up with a position value, and we simply increment or decrement the output on each scan to rotate the platform east and west to find the optimum balance light levels on the sensors again.

In the code, there are also upper and lower limits to prevent damage to the servo by driving hard against its end stops. A deadband value is also established in the code. This effectively means the output will not change, unless the error between the two inputs is greater than a certain value. The deadband prevents jitter and constant twitching of the sensor.

I have also added a very basic 2 point average to help smooth out spikes in the input sensors. In reality, you might want to filter this out further to filter out unwanted noise or spikes.

Using the code

The first part of the code is used to establish the IO Pin allocation, the variables for holding the input readings, the error and the rolling error average. The deadband range is also defined, as well as the upper and lower limits for the servo, and also the initial start point for the servo.

The #include statement makes reference to a prebuilt library for handling servo's on the Arduino. It basically allows a simple value to be written out to the servo object, and then takes care of the Pulse Width Modulation used to set the servo position.

#include <servo.h>

//IO Pins
int pinL = 5;              //Left Sensor IO Pin
int pinR = 4;              //Right Sensor IO Pin
int pinServo = 11;         //Servo PWM pin

int leftValue = 0;         //The left Sensor Value
int rightValue = 0;        //The right Sensor Value
int error =0;              //The Deviation between the 2 sensors
int errorAVG = 0;          //Error Average - Rolling 2 Point

int deadband = 10;         //Range for which to do nothing with output 10 = -10 to +10  
//Servo Stuff
Servo hServo;              //The servo object
int Position = 45;         //Position to write out

int minPos = 5;            //Min Position
int maxPos = 150;          //Max Position

float output = (maxPos - minPos) /2;  //Initial output Position

The next part of the code is the Arduino Setup method. This runs once and is effectively used to initialise anything you want before the main code loop executes. In this example, all I am doing is setting the Servo output to Min, Max and MidPoint for 5 Seconds each, to allow any positioning of the hardware on my desk for testing. The Serial statements just pump messages out the serial port and can be monitored on the PC.

void setup()
{
Serial.begin(9600); 

hServo.attach(pinServo);

//Set Servo to Centre for Alignment Purpose
Serial.println("Moving Servo to Minimum Position");
hServo.write(minPos);
delay(5000);
Serial.println("Moving Servo to Maximum Position");
hServo.write(maxPos);
delay(5000);
Serial.println("Moving Servo to Mid-Point");
hServo.write(output);
delay(5000);
Serial.println("Going Live................");
}

The final part of the code is the main loop body, this is the loop that will run continuously until power is switched off or new code is downloaded to the Arduino.

The input values are first read, then some debug info is pumped out to the serial port. The error values are calculated, and the revised new position for the sensor is determined by adding the value returned by getTravel(). The limits are also checked to ensure we do not exceed these.

void loop()
{
  //Input Reading
   leftValue = analogRead(pinL);
   rightValue = analogRead(pinR);

 Serial.print("L = "); Serial.print(leftValue); Serial.print(" | ");
 Serial.print("R = "); Serial.print(rightValue); Serial.print(" | ");
 Serial.print("E = "); Serial.print(error); Serial.print(" | ");
 Serial.print("Eavg = "); Serial.print(errorAVG); 
 Serial.println();

  //Calculate
 error = leftValue - rightValue;
 errorAVG = (errorAVG + error) / 2;
 
 float newOutput = output + getTravel();
 
 if (newOutput > maxPos)
 {
   Serial.println("At Upper Limit");
   newOutput = maxPos;
 }
 else
 { 
   if (newOutput < minPos)
   {
     Serial.println("At Lower Limit");
     newOutput = minPos;
   }
 }
    Serial.println("Writing output");
    
    //Output Writing
    hServo.write(newOutput);
    output = newOutput;
}

I also have a helper method getTravel() which is used to determine if I need to rotate left, rotate right or do nothing (e.g. within deadband) on each scan. It simply returns a +1, -1 or 0 which is then added to the current position before being written out to the servo.

int getTravel()
{
  // -1 = Left; +1 = Right
 
 if (errorAVG < (deadband * -1))
 {
   return 1;
 }
 else 
 {
   if (errorAVG > deadband)
   {
     return -1;
   }
   else
   {
     //Do not move within deadband
     return 0;
   }
 }
}

The working prototype




This is as simple as it gets. Ways that you could enhance this are:
  • Implement an improved noise filtering on the input signals
  • Add some form of PID (Proportional/Integral/Derivative) to the control algorithm
  • Add a second servo and additional sensors for Vertical motion

Arduino Platform - Using Interrupts With the Arduino

What is an interrupt?

Interrupts are a method of signaling to the microprocessor that something has happened. However, you may ask yourself, is that not what happens anyway when you use a digital input etc.? Well quite simply - No.
When you use the likes of a digital input, you will typically read its value by issuing an instruction, then act on the read value by using some form of logic, i.e. the code is polling for a value. Depending on the complexity of your routines, and the duration of the state change on the input it is quite possible to not see the change on the input occur at all.
By using an interrupt, the code is literally interrupted, and forced to branch off and do some other execution. i.e. the state change on the input is not 'missed'. So interrupts are like a hardware trigger.

What Benefit Are They?
Interrupts can help solve timing issues which may occur within your code/hardware setup. You may have had timing issues already and just not understood what happened. How many times have you said to yourself, "Why didn't that fire?" or "It worked last time I ran it. What is different."

Getting Down To Business
I am using the Arduino Duemilanove Board for this example, and will be using the Release 18 of the development IDE. This can be downloaded directly from Arduino.cc.
If you are using a different type of Arduino Board, you will need to check out the specs to see which pins are which, as the different types of board can have different allocations and numbers of interrupts/digital pins/analog pins etc.
On the Duemilanove, there are 2 hardware interrupts available. These are located on Digital Pins 2 and 3. In this demo, we will use Pin 2, and also use Digital Pin 4 as an output to control an LED. The schematic for this is shown below;


Standard Digital Input and Output - No Interrupts
If you set up the Arduino as per the schematic and upload the code below to the microprocessor. Here you read the value of an input, do a conditional comparison, run some lengthy routine and repeat.
This will given unpredictable outputs on the LED due to the lengthy process being at an undetermined point in relation to when the input button is triggered. Sometimes the LED will change state immediately, other times nothing happens, and then sometimes you need to hold the button for a while for the state changed to be recognised.
int pbIn = 2;          // Digital input on pin 2
int ledOut = 4;        // The output LED pin
int state = LOW;       // The input state
 
void setup()
{                
  // Set up the digital Pin 2 to an Input and Pin 4 to an Output
  pinMode(pbIn, INPUT); 
  pinMode(ledOut, OUTPUT);
}
 
void loop()                     
{
  state = digitalRead(pbIn);      //Read the button
 
  digitalWrite(ledOut, state);    //write the LED state
 
  //Simulate a long running process or complex task
  for (int i = 0; i < 100; i++)
  {
     // do nothing but waste some time
     delay(10); 
  }
}

Making Use of Interrupts
Now we will use the same schematic diagram and modify the code to make use of hardware interrupts. Now when you upload the code, the LED changes state whenever the button is pressed. Even though the code is still running the same long delay in the main loop.
int pbIn = 0;                  // Interrupt 0 is on DIGITAL PIN 2!
int ledOut = 4;                // The output LED pin
volatile int state = LOW;      // The input state toggle
 
void setup()
{                
  // Set up the digital pin 2 to an Interrupt and Pin 4 to an Output
  pinMode(ledOut, OUTPUT);
 
  //Attach the interrupt to the input pin and monitor for ANY Change 
  attachInterrupt(pbIn, stateChange, CHANGE);
}
 
void loop()                     
{
  //Simulate a long running process or complex task
  for (int i = 0; i < 100; i++)
  {
    // do nothing but waste some time
    delay(10); 
  }
}
 
void stateChange()
{
  state = !state;
  digitalWrite(ledOut, state);  
}
The volatile keyword is added to the state variable, this causes the compiler to use RAM instead of a storage register. This is done because the storage register can be temporarily inaccurate if they are being modified by areas other than the main thread. In the Arduino, this relates to code being triggered by interrupts.

The attachInterrupt(param1, param2, param3) requires 3 parameters, these are;
param1 = Which interrupt to listen for. This is the Interrupt Number not the Digital In number
param2 = Which code function to call, this must be a method that takes no parameters and returns no value.
param3 = Which condition to watch for.

The Arduino can listen for 4 types of condition changes. These are;
LOW = The input is at a LOW state
RISING = The input state changes from LOW to HIGH
FALLING = the input state changes from HIGH to LOW
CHANGE = The input state changed from HIGH to LOW or LOW to HIGH, i.e. has changed its state

ReAssigning Interrupts
Interrupts can be changed at any point by using the attachInterrupt() method. As soon as this is done, any previously assigned interrupt on the associated pin is removed.

Starting / Stopping Interrupts
The Arduino also has the ability to temporarily ignore all the interrupts. You may want to do this if you have some sensitive code that must be executed without interruption. In this case you would issue a noInterrupts() call. Once your sensitive code block has completed, interrupts can be restarted by calling interrupts().

Removing Interrupts
Interrupts can also be removed by using the detachInterrupt(interrupt_number) method.

So, that is it, a quick basic introduction to hardware Interrupts on the Arduino platform. Now, you just need to see how they fit in with your projects, and how you can make use of them.

Sunday, 19 July 2015

It all went wrong............

Hi,

Thanks for visiting.

Unfortunately, this is not the normal Dave-Auld.net website. There has been a service interruption.

This is a temporary home. Hopefully a very temporary home.

The original website is currently dead following a virtual machine failure at my service provider. I am still working with them to get content back that they managed to clone from the virtual hard disk while performing investigations to try and recover the virtual machine which they managed to bork while applying updates in the early hours of 17th June.

This may take some time, as I am also limited to what I can do from afar, and really need to get home to get free unrestricted access to the internet.

In the meantime you may find some of my mumblings at the following locations;

My Qatar adverture blog can be found at:
http://qatar-is-calling.blogspot.co.uk/

My articles at CodeProject can be found at:
http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=557325

I only became aware of the failure when the Googlebot emailed to tell me it failed to access the site to perform a regular index. I became curious and sure enough I couldn't see the site either. I tried to remote desktop onto the server, but again, no connection. Tried to gain access to a web control panel to reboot the machine, no joy.

Calling in the big guns at the server support, they also failed to gain access to the machine and elevated the support request within their internal processes. The guru's failed to recover the situation and pronounced the server dead. At that is where we still are.......