Monday, May 30, 2011

Controlling Your Servos: HS-322HD

You've got two main choices to give any of your projects mobility, servos and dc motors. Technically, you only have dc motors since servos are just a motor with built in gearing and control circuitry, but who asked you anyways? Servos normal have an 180 degree range of rotation, but can also come in a 90 or even 360 degree variety. If you wanted to you can even remove the control circuitry and use them as wheels. With this simple hack your servo becomes a full-rotation continuous motor. If you aren't feeling very adventurous, a lot of stores have started carrying these full rotation servos as well.

Normally, to control a servo with any other micro-controller you would have to cycle a digital output port between high to low for 1 to 2ms depending on the angle you want to set the servo (usually between 0-180 degrees). Depending on the rotational speed of the servo, you would have to determine how long the servo needs to get into position and plan your program accordingly. Sounds like a lot of overhead just to control one little piece of hardware. Thankfully, the Arduino once again has a set of built in functions to take care of most of this for you. You will still need to do a few calculations or trial and error, however, to figure out the delay time.

I visited my local hobby shop, HobbyTown USA, and picked up a standard servo, the HS-322HD. If you're not using the servo for anything important, like in an RC airplane for instance, there's no need to spend more than ten bucks or so on a servo. The HS-322HD would be considered about a medium sized servo and has the following specs.

Picked it up for $9.99 at my local HobbyTown USA
HS-322HD at 6.0 Volts:
  • Speed: 0.15sec/60 degrees
  • Torque: 51oz.in (who uses kg and cm anyways?)
  • Resin Bushings
  • Karbonite Gears, for an extra dollar over the HS-311, they are supposed to be four times stronger than standard resin gears... why not?

Enough chit-chat, let's get this guy spinning.

There are a few things you need to include before you start coding in order to initialize your servo. The first thing is to add the servo library at the begining of your code: #include <Servo.h>. Then, right below your #define statement you need to create a servo object with a Servo myservo statement. You can set the servo object's name to anything you want, you just need to make sure you are consistent at all instances of the object. It's a good idea to also create a variable to keep track of the position of your servo, i.e. the int pos in the code below. Lastly, in your setup() function you need to assign a digital output pin to your servo object via myservo.attach(#). This would be the Arduino pin that you have plugged the servo's yellow data wire into.

Now that our servo is all set-up, we can start coding. Fun!

The servo's position is controlled with the myservo.write(#) function. You can set the servo's angle from 0 to 180 degrees with this write function. Depending on the distance the servo needs to rotate, you might have to include a delay statement to make sure the servo has enough time to set to the correct position, otherwise the servo may try to move faster than it is able to and overheat. Included in the Arduino IDE are example sketches of how to use a servo, File>Examples>Servo.

Expanding upon the Sweep example code by BARRAGAN, I wrote a  program that incorporates the IR sensor from the last blag post.

//  Noah Stahl
//  5/30/2011
//  http://arduinomega.blogspot.com
//  Arduino Mega 2560
//This sketch is used to control a Sharp Long Range IR Sensor mounted on a servo.
//The servo sweeps between 0 to 180 degrees while the IR sensor looks for objects.
//If the IR sensor detects an object within 10 inches of the servo, the servo
//stops moving and the angle of the servo is printed to the Serial Monitor. The
//servo waits until the object is no longer within 10 inches. Once the object has
//passed, the servo goes back to sweeping the area, checking for objects.

#include <Servo.h>

#define sensorIR 15
Servo myservo;  // Creates a servo object
int pos = 0;    // Variable to store the servos angle
volatile float inches;
 
void setup()

{
  myservo.attach(53);  // Assigns data pin to your servo object, must be digital port
  Serial.begin(9600);
}
 
void loop()

{
  search();
}

 
//This function sweeps the servo searching the area for an object within range
void search()

{
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees
  {                                  // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    sensorPrint(analogRead(sensorIR));
    if (inches < 10) follow();
  }
  for(pos = 180; pos>=1; pos -= 1)     // goes from 180 degrees to 0 degrees
  {                               
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    sensorPrint(analogRead(sensorIR));
    if (inches < 10) follow();
  }  
}

 
//This function determines the distance from the servo and prints it to the SMonitor
void sensorPrint(float sensorValue)

{
  inches = 4192.936 * pow(sensorValue,-0.935) - 3.937; //algorithm from last post
  //cm = 10650.08 * pow(sensorValue,-0.935) - 10; //if you use cm instead of in
  Serial.print("Inches: ");
  Serial.println(inches);
}

 
//This function only determines the distance from the servo without printing
void noPrint(float sensorValue)

{
  inches = 4192.936 * pow(sensorValue,-0.935) - 3.937;
//algorithm from last post
  //cm = 10650.08 * pow(sensorValue,-0.935) - 10;
//if you use cm instead of in
}

 
//This function keeps the servo locked on the object until it moves out of range
void follow()

{
  while (inches < 10)

  {
    noPrint(analogRead(sensorIR)); //we want to find the distance but not print it
    Serial.print("Angle: ");       //Current angle of the servo between 0 to 180
    Serial.println(pos);
  }
}


This is what the program above will be controlling
It's an IR sensor hot glued to a servo, hot dog!

By attaching the Sharp Long Range IR sensor from the last blag post to our servo, I created a makeshift proximity detector. The servo sweeps from 0 to 180 degrees while the IR sensor checks for any objects that are within 10 inches of the servo. If an object moves within 10 inches of the servo and is in the IR sensor's line of sight, the servo will stop moving and the angle of the servo is printed to the Serial Monitor. The IR sensor will continue to check the distance of the object and wait until it is no longer within 10 inches of the servo. Once the object has passed, or unfortunately gets closer than 4 inches from the servo... damn blind spot, the servo resumes sweeping the area.

That's some fancy servo action right there I tell you what!

If the servo and IR sensor were mounted to a robot, you could use this code to make sure that your robot doesn't bump into any objects or walls. Once the robot detects that an object is too close, you could have it back up and turn. If you wanted to be even cooler, you could have it sweep the servo one more time after it detects an object and use the IR sensor to determine how far it needs to back up and which way it should turn. By using the IR sensor to see which direction has a further average distance, 0 to 90 degrees vs. 90 to 180 degrees i.e. left vs. right, you can prevent the robot from wasting time backing up and turning into the same wall. Don't always have your robot just blindly back-up and turn right whenever it bumps into something or it may get stuck and freak out. Your robot is only going to be as smart, or stupid, as you program it to be.

Wednesday, May 25, 2011

Long Range Infrared Sensor: GP2Y0A02YK0F

Has it really been over two weeks since I last updated the blag? I for one blame reddit, which has made me far more efficient at wasting time leaving more room for... wasting time.

I've acquired some sensors, motors, and a servo this past week and have been waiting to start plugging them into the Arduino. The servo and motor code seemed straight forward enough so instead I thought I'd first start with one of the cooler looking sensors I bought, the Sharp Long Range Infrared Proximity Sensor -  GP2Y0A02YK0F, courtesy of SparkFun.
I can't be the only one who thinks these look a lot like...
WHOA!
Don't forget your extra wire connectors
The sensors have an odd JST connector, so I'd suggest buying the additional wire adapter (only another $1.50) as well so you don't have to open it up and try to build your own and worry about damaging the sensor. I stripped the ends of the wires and soldered on a single header pin on each for easy wiring.

This long range infrared sensor can calculate the distance from objects up to 150cm away(roughly 5ft). It outputs an analog voltage corresponding to the distance that varies from roughly 2.8V at 15cm to 0.4V at 150cm. This value would be read by the Arduino using the analogRead() function.

Now, while this sensor is quite accurate between the range of 15-150cm (0.5-5ft), it does have two drawbacks:
  • The minimum cutoff for an accurate reading is 15cm, so any reading closer than that will be garbage
  • The voltage output over distance is not linear (more of a power function)

From the Sharp product manual

If your project requires checking for a distance closer than 15cm, there are short and mid-range versions available. If you don't want to sacrifice distance you can always just use more sensors in combination with the long range IR to cover it's "blind spot". Another way to compensate for the minimum distance would be to just place the sensor 6 inches behind the front of your robot...

As for the non-linearity, this just means that we'll have to throw together a nifty little algorithm to convert the sensor's output voltage into distance. Thankfully, Sharp has already taken care of the hard part and provided us with a chart of output values (as seen in the graph above). All we have to do is convert the analog voltages into a 10-bit digital value and determine a best-fit line equation.

Fancy graph drawn up in Excel

By placing the data values into Excel, I got the following graph. The graph most closely resembled a Power function, so the best-fitting line equation was y = 1735 * x ^ (-1.07), with an R^2 value of 0.997. Not bad. You may have noticed that the graph starts at 20 cm instead of 15 cm. I decided to sacrifice an extra 5 cm on the minimum distance in order to have a more accurate algorithm. Since I'll be using the sensor to make sure nothing comes within about 15 cm (6 in) of my robot, this won't be a problem. Also note that the algorithm doesn't take into account the x axis values, so in the equation x = 20 cm would be equivalent to x = 3... just make sure that you take that into consideration if your making an equation this way.

So, what do we do with this equation? Right now it doesn't help us at all since we are reading in y (voltage)   and want to solve for x (distance). We could bust out some scratch paper and a calculator and solve for x, or we could use the All Mighty WolframAlpha.

Is there anything this website can't do? 

So, taking into account the oddities of my graph, our equation to calculate the distance in cm is:
y = 10650.08 * x ^ (-0.935) - 10

For us 'mericans and our imperial units of measurment (10 chains in a furlong?), our algorithm in inches is:
y = 4192.936 * x ^ (-0.935) - 3.937

To use the algorithm in your code, you need to use the pow() function. So, in your Arduino sketch it would look something like this:
inches = 4192.936 * pow(sensorValue,-0.935) - 3.937

In order to test our algorithm, I taped my IR sensor to a tape measure and used a white sheet of printer paper as the test object.

Very rigorous and strict testing standards
 SCIENCE!
I see nothing, nothing!
Here is the code that I wrote up to test the IR sensor:

//  Noah Stahl
//  5/25/2011
//  http://arduinomega.blogspot.com
//  Arduino Mega 2560
//This sketch is used to test the Sharp Long Range Infrared Sensor.
//The sensor output is attached to analog pin 15. Once the distance
//is calculated, it is printed out to the serial monitor.

#define sensorIR 15               //Must be an analog pin
float sensorValue, inches, cm;    //Must be of type float for pow()

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

void loop() {
  sensorValue = analogRead(sensorIR);
  inches = 4192.936 * pow(sensorValue,-0.935) - 3.937;
  //cm = 10650.08 * pow(sensorValue,-0.935) - 10;
  delay(100);
  Serial.print("Inches: ");
  Serial.println(inches);
}


The algorithm works surprisingly well and, without interference, is easily accurate within the inch. The sensor starts at approximately 7.5 inches (19cm) so anything closer than that should be considered garbage data.

The only problem with the sensor is that it is susceptible to noise on the power supply line and needs to be perpendicular to the object for an accurate reading. Being an analog sensor, the output value is going to be dependent on the supply voltage. If you aren't supplying a constant voltage, the numbers are going to fluctuate and if your voltage starts to dip under 5V, you wont get the full 5 ft range. You may need to add an extra 0.1uF cap at the power supply or maybe average every few readings to smooth out the data.

Noise, noise, noise, noise, noise!

If the sensor is not level to the object, your distance is going to be off by a few inches. If you place your sensor on a servo and it's not secured properly, jumbling about (in my case poorly taped to a tape measure), you are going to get random spikes in your data. The sensor is really sensitive, which can be either a good or bad thing depending on your design.

Right now the code doesn't have a cutoff for minimum and maximum distances, but this would be an easy exercise to see if you've been paying any attention. The sensor should be able to read any value inside of roughly 7.5 - 60 inches if you're using this code. Also, if you set your sensor up about 7 inches back from the front of your robot, you can use this code to detect whether an object gets too close without having to worry about the sensor's blind spot. You technically wouldn't need any close ranged sensors if you place it this way to get a basic robot going, but its always good to have more than one sensor to double check your values and in case something manages to sneak in too close. Sensors are the eyes and ears of your robot and you can never have enough eyeballs.

Monday, May 09, 2011

Arduino RGB LED Fun: Groovy!

What better way to learn about Pulse Width Modulation than messing with an Red-Green-Blue LED? Let's create two programs, one that controls the color brightness with potentiometers and another that just cycles through random colors.

By using the analogWrite() function, you can set the brightness of an LED using a PWM wave. The analogWrite function() can set a pin to output anywhere between 0-5 Volts corresponding to a digital value of 0-255. There is also an analogRead() function that is able to convert an analog input voltage of 0-5 Volts into a digital value between 0-1023.

By attaching three potentiometers to three of the Analog In Pins of the Arduino we can control the color output of the RGB LED. Since an RGB LED is essentially three LEDs inside of one, you need to include a current limiting resistor for each pin. According to the data sheet on Sparkfun, the voltages for each pin are 2V Red, 3.2V Green, and 3.2V Blue. All three LEDs draw 20mA. You can calculate the minimum resistor value using:
(Source Voltage - LED Voltage) / LED Current = Resistance

Here is a terrible circuit diagram via LTSPICE:
If I weren't so lazy, I'd draw these out and scan them... maybe next time

Check out that Gray Code

//  Noah Stahl
//  5/9/2011
//  http://arduinomega.blogspot.com
//  Arduino Mega 2560
//This project controls the output of an RGB LED using three potentiometers
//Check the blog for the circuit components and wiring

#define RLED 4        // Must be PWM pins
#define GLED 3
#define BLED 2
#define RPOT 15       //Must be Analog Input Pins
#define GPOT 14
#define BPOT 13

int red_value, green_value, blue_value;

//This function just outputs the RGB values that we read in
void color(){
 analogWrite(RLED, red_value);
 analogWrite(GLED, green_value);
 analogWrite(BLED, blue_value);
}

void setup() {
  pinMode(RLED, OUTPUT);
  pinMode(GLED, OUTPUT);
  pinMode(BLED, OUTPUT);
}

void loop() {
  red_value = analogRead(RPOT) / 4;   // 1024/256 = 4, max output value is 255
  green_value = analogRead(GPOT) / 4;
  blue_value = analogRead(BPOT) / 4;
  color();
}

You can create a makeshift mood light by cycling through the color outputs of an RGB LED. There are only so many color combinations that actually look decent on the RGB LED, but I'm feeling a bit lazy tonight so I'm just going to randomly generate colors. At first I had the code just cycle through the basic eight RGB ON/OFF states, but I thought this would be more fun and a good exercise for my rusty programming skills.

How the algorithm works:
  • Randomly assigns a value from 0-255 for the Red, Green, and Blue LEDs
  • Checks to make sure that all three values are not zero
  • Checks each LED to see if it's already at that value
  • If current value is less than the target value, increase LED brightness
  • If current value is greater than the target value, dim the LED
  • Repeat until chosen values are equal to the LED outputs
//  Noah Stahl
//  5/9/2011
//  http://arduinomega.blogspot.com
//  Arduino Mega 2560
//This project will randomly cycle through an RGB LED
//Circuit uses an RGB common cathode LED attached to PWM pins

#define RLED 4                 // Must be PWM pins
#define GLED 3                
#define BLED 2
#define MAX 255                //Max analog value is 255,

byte RGB[3] = {0,0,0};         //Stores analog LED output values
int  sel[3] = {0,0,0};         //Stores chosen LED brightness
int j;

//Function either dims or brightens RGB depending on sel[] values
void color(byte RGB[], int sel[]){
  while (sel[0]!=RGB[0] || sel[1]!=RGB[1] || sel[2] != RGB[2]){ //loop until LED is equal to chosen values
    for (j=0;j<3;j++){
      if (sel[j]>RGB[j])(RGB[j])++;  //if LED is dimmer than value, brighten LED
      if (sel[j]<RGB[j])(RGB[j])--;  //if LED is brighter than value, dim LED
    }
    analogWrite(RLED, RGB[0]);        //Output analog values to RGB LED
    analogWrite(GLED, RGB[1]);
    analogWrite(BLED, RGB[2]);
    delay(20);                        //slow the dim/brightening process
  }
  delay(1000);                        //wait a sec and enjoy the pretty color :3
}

void setup() {
  pinMode(RLED, OUTPUT);             //R LED annode attached to Pin 4
  pinMode(GLED, OUTPUT);             //G LED annode attached to Pin 3   
  pinMode(BLED, OUTPUT);             //B LED annode attached to Pin 2
  randomSeed(analogRead(0));         //Makes sure that random values are different each run
}

void loop() {
  sel[0] = random(0,MAX+1);      //decide what brightness R will be
  sel[1] = random(0,MAX+1);      //decide what brightness G will be
  sel[2] = random(0,MAX+1);      //decide what brightness B will be
  if(sel[0] || sel[1] || sel[2]) color(RGB,sel); //prevents the LED from turning off
}



The randomSeed() function that you see makes sure that every time the program runs it will generate a different set of random numbers. Technically, the random() function generates psuedo-random numbers and will generate the same sequence of numbers every time you reset the Arduino unless you initialize it with a random input. By using an open floating analog input pin as your reference, you can ensure that your random numbers will be more... random.

My phone does a terrible job of recording the LED color,but does a great job of BURNING MY RETINAS

While this code works, a majority of the colors tend to be white-washed and more towards the light blue side of the color spectrum. If you were feeling adventurous, you could instead create a look-up table for the colors that you wanted to display and randomly cycle through them. This way you could create your own custom "mood light" instead of just a random color generator.

Sunday, May 08, 2011

Setting Interrupts Manually: The Real INT0

Last post I covered how to set External Interrupts using the provided attachInterrupt() function. While convenient, it's important to note that there's always going to be a bit more overhead when using these functions instead of setting the registers yourself. It's also a good exercise to figure out what these functions are doing behind the scenes.

There are eleven external interrupts for the ATmega2560 chip, but the Arduino only allows you to use nine of them and only six of them, INT5:0, can be accessed through attachInterrupt(). These three extra mystery interrupts are the Pin Change Interrupts, referred to as PCI2:0. These three interrupts are set whenever the pins they monitor are toggled, which would be the same function as the CHANGE setting for the six other external interrupts. Each of the PCI interrupts have eight pins they are attached to, PCINT23:0.

The external interrupts and the pins they are wired to:
PCI2:              Arduino:
PCINT23        PIN ANALOG15
PCINT22        PIN ANALOG14
PCINT21        PIN ANALOG13
PCINT20        PIN ANALOG12
PCINT19        PIN ANALOG11
PCINT18        PIN ANALOG10
PCINT17        PIN ANALOG9
PCINT16        PIN ANALOG8
       
PCI1:       
PCINT15        N/A
PCINT14        N/A
PCINT13        N/A
PCINT12        N/A
PCINT11        N/A
PCINT10        PIN 14
PCINT9          PIN 15
PCINT8          PIN 0
       
PCI0:       
PCINT7         PIN 13
PCINT6         PIN 12
PCINT5         PIN 11
PCINT4         PIN 10
PCINT3         PIN 50
PCINT2         PIN 51
PCINT1         PIN 52
PCINT0         PIN 53 

INT7:0
INT7             N/A
INT6             N/A
INT5             PIN 3
INT4             PIN 2
INT3             PIN 18
INT2             PIN 19
INT1             PIN 20
INT0             PIN 21


You may notice that the pins that INT5:0 are attached to are not the same used for attachInterrupt(). This is import to note if you have multiple interrupts since certain interrupts will take priority over one another and can interrupt lower priority interrupts. The lower the interrupt vector, the higher the priority of the interrupt, as seen on the table below.

 
Found on page 105 of ATmega2560 data sheet

Let's take our button program from last post and replace the attachInterrupt() by manually setting the external interrupt control registers.

The trigger mode from attachInterrupt() is instead controlled by the EICRA and EICRB registers. Each INT has two corresponding bits in the registers used to set the mode. You can reference the table below on how to set either LOW, CHANGE, FALLING, or RISING.

 Found on page 114 of ATmega2560 data sheet

To enable any of the INT5:0 interrupts, edit the External Interrupt Mask Register, EIMSK. The INT7:0 enable bits correspond to the eight bits of the register. To enable INT4 for instance, you would set EIMSK = 0x10.

Circuit diagram, again, use your imagination

#include <avr/interrupt.h> 
#include <avr/io.h>

volatile int state = 0;

void setup(){
  pinMode(53, OUTPUT);   //attached to LED annode
  pinMode(2, INPUT);     //
Button input tied to INT4

  EICRB = 0x01;          //INT4, triggered on any edge
  EIMSK = 0x10;          //Enable only INT4
}

void loop(){
}

ISR(INT4_vect) {
  state = !state;
  digitalWrite(53, state);
}

Note that Pin 2 used in the last sketch is now attached to INT4 when we manually set the registers. If we were to use INT0 like last time, we would need to connect our button to Pin 21 instead. The "real" INT0 is attached to Pin 21 and Pin 2 is attached to the "real" INT4. Refer to the previous table at the beginning for reference.

The code for the interrupt vector is just the source of the vector with _vect added to the end of it. For the button code, INT4 is used so the interrupt routine is IRS(INT4_vect). If you look at the interrupt vector table above you can figure out the vectors for any other interrupt. Before, when we used the Timer2 Overflow Interrupt if you look at the table you can see it uses the source TIMER2 OVF. So, the interrupt routine would be ISR(TIMER2_OVF_vect).

Also, you don't have to manually clear the interrupt flag bits at the end of the interrupt service routine as I previously thought. The ATmega automatically clears the flag once the service routine starts.

External Interrupts: Needs More Buttons

Any Arduino project you make is most likely going to be doing a lot of waiting. Instead of constantly polling your sensors for input changes, you can use interrupts to free up your processor so it can do other things without having to worry about missing a signal.

The Arduino IDE provides a function, attachInterrupt(), that can set-up external interrupts for you. The Arduino Mega 2560 has six available external interrupts, INT5:0. The ATmega2560 chip has eight external interrupts total, but the Arduino only connects six of those pins to headers. If you are going to be setting the interrupt registers yourself and choose not to use the provided function, be warned that the interrupt numbers don't necessarily correspond to the same pins listed below.

The External Interrupts are connected to the following Pins when using attachInterrupt():
              Digital Pin
INT5 :        18
INT4 :        19
INT3 :        20
INT2 :        21
INT1 :        3
INT0 :        2

The attachInterrupt() function uses three parameters to customize the interrupt:
  • Interrupt Number: Found in the left column in list above. Attach your sensor to the corresponding pin.
  • Function Name: The function that will be called when the interrupt occurs. This would be your Interrupt Service Routine if you were using a different micro-controller.
  • Trigger Mode: Determines when the interrupt will be triggered. Can be one of four values:  RISING, FALLING, CHANGE, and LOW.
    1. RISING: Triggers the interrupt only when the pin changes from low to high; a rising clock edge
    2. FALLING: Triggers the interrupt only when the pin changes from high to low; a falling clock edge 
    3. CHANGE: Triggers when the pin changes from either low to high or high to low; any clock edge
    4.  LOW: Triggers when the pin is low
The syntax of the function with all of the parameters included is attachInterrupt(interrupt, function, mode). If you wanted to create an external interrupt using pin 2, that triggered on a rising clock edge, and jumped to the function button(), the correct syntax would be attachInterrupt(0, button,  RISING).

Let's apply an interrupt to the following sketch:

int button_state = 0;             // variable for storing button's status

void setup() {
  pinMode(53, OUTPUT);           //Set LED on PIN53 to output  
  pinMode(52, INPUT);            //Set button on PIN52 as input


void loop(){
  button_state = digitalRead(52); //Read state of button
  if (button_state == HIGH) {      //If HIGH, the button has been pressed
    digitalWrite(53, HIGH);       //Turn LED on
  }
  else {                          //If LOW, button is open
    digitalWrite(53, LOW);        //Turn LED off:
  }
}


The sketch controls the state of an LED by polling a tactile button. When the button is pressed, the LED toggles ON/OFF. You can make the program more efficient by replacing the need for polling with an external interrupt. 


 More SCIENCE!

The following code sets up an External Interrupt using the attachInterrupt() function:

volatile int state = 0;

void setup(){
  pinMode(53, OUTPUT);
  attachInterrupt(0, button, CHANGE);
}

void loop(){
}

void button(){
  state = !state;
  digitalWrite(53, state);
}


If you are using a switch(button) for your interrupt trigger, you need to make sure to debounce the input (which I haven't) or the Arduino may read a single button press as multiple presses. The Arduino website offers one solution for a software debounce here. Normally, you could get away with just using a delay after the button press, but the delay() function is disabled inside of interrupt routines.

Saturday, May 07, 2011

Timer2 and Overflow Interrupt: Let's Get Cooking

Anybody can open up the example code and make an LED blink, but do you know how to do this with timers and interrupts? It's important to know your microprocessor fundamentals or you're going to be spending a lot of time on the forums and wading through Google searches.

If you read the previous blog post you might remember the problem I mentioned when using the delay function. Using the function locks up the processor and prevents any other code from running until it is done waiting. One simple way to get around using the delay function is to use timers instead.

There are a six available timers in the Arduino Mega. The tricky part is that they are already tied to specific peripherals and functions. Using a timer for an interrupt will interfere with using the pins it's tied to for PWM. The Arduino website says the Mega2560 has 14 PWM pins available. But, if I read the schematic correctly, it appears there are possibly three more pins PWM ready: Pins 44, 45, and 46. I'll have to test this when I start messing with PWM.

Looking at the Arduino schematic, the Timers are tied to the following Pins:
Timer0:  Pins 4 and 13
Timer1:  Pins 11 and 12
Timer2:  Pins 9 and 10
Timer3:  Pins 2, 3, and 5
Timer4:  Pins 6, 7, and 8
Timer5:  Pins 44, 45, and 46

When using the timers you need to check which pins they are connected to so that you know what functionality you are losing in regards to PWM.

The following program can be used to replace the delay(1000) from the earlier project. By configuring the Timer2 Overflow Interrupt to fire every 1ms, we can increment a counter in the Interrupt Vector and wait until 1000 interrupts have occurred to toggle the LED.


/*=======================================================================================
                                         Noah Stahl
                                          5/7/2011
                                       Arduino Mega 2560

This program will blink an LED on Port 53 once a second using a Timer2 Overflow Interrupt. The timer overflow interrupt fires every 1ms by setting the prescaler to 128 and preloading Timer2's counter with 130. Timer2 is 8-bits so the max number it can count to is 255, this leaves 125 cycles left to count. The Arduino has a clock of 16MHz so:
(1/16E6) * 125 * 128 = 1ms
The interrupt flag will fire every 1ms. Once the interupt fires 1000 times (i.e. one second) the LED on pin 53 will toggle.
=======================================================================================*/
#include <avr/interrupt.h>
#include <avr/io.h>

unsigned int toggle = 0;  //used to keep the state of the LED
unsigned int count = 0;   //used to keep count of how many interrupts were fired

//Timer2 Overflow Interrupt Vector, called every 1ms
ISR(TIMER2_OVF_vect) {
  count++;               //Increments the interrupt counter
  if(count > 999){
    toggle = !toggle;    //toggles the LED state
    count = 0;           //Resets the interrupt counter
  }
  digitalWrite(53,toggle);
  TCNT2 = 130;           //Reset Timer to 130 out of 255
  TIFR2 = 0x00;          //Timer2 INT Flag Reg: Clear Timer Overflow Flag
};

void setup() {
  pinMode(53,OUTPUT);

  //Setup Timer2 to fire every 1ms
  TCCR2B = 0x00;        //Disbale Timer2 while we set it up
  TCNT2  = 130;         //Reset Timer Count to 130 out of 255
  TIFR2  = 0x00;        //Timer2 INT Flag Reg: Clear Timer Overflow Flag
  TIMSK2 = 0x01;        //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
  TCCR2A = 0x00;        //Timer2 Control Reg A: Wave Gen Mode normal
  TCCR2B = 0x05;        //Timer2 Control Reg B: Timer Prescaler set to 128
}

void loop() {
}


A few important things to note about the code:
  • Disable the Timer while you configure your interrupt and timer by setting TCNT2 = 0x00.
  • Timer2 is 8-bits wide, so it can count up to 255. By preloading the counter with 130, TCNT2 = 130, this leaves 125 cycles left to count.
  • (125 cycles) * (128 prescaler) / (16MHz clock speed) = 1ms
  • Clear the Timer2 INT Flag by setting TIFR2 = 0x00.
  • Enable the Timer2 Overflow Interrupt by setting TIMSK2 = 0x01.
  • When your timer and interrupt are configured, you can then set the prescaler which will restart the timer. In this example the prescaler is set to 128 by setting TCCR2B = 0x05. The timer will count 128 clock cycles before it increments its counter.
  • Once inside the Interrupt vector, be sure to reset your timer count (TCNT2 = 130) and clear the interrupt flag (TIFR2 = 0x00) so that you can reuse the timer and interrupt function.
You can use this basic structure as a substitute for the delay() function in any project that has time sensitive signals. By changing the prescaler value and the preloaded count you can set the period at which the interrupt fires to suit your project's needs. Also, if you wanted a larger interrupt period, you can experiment with Timers 1,3,4, and 5 which have a 16-bit width and can count up to 0xFFFF, 65535 decimal. Just be sure to note which pins they are tied to.

Blink with Delay: Let There Be Light!

It is only customary to start one's journey with their Arduino by opening the Blink example code, uploading it to your board, and staring mouth agape at your first beautiful creation.

SCIENCE!

Our first project consists of powering an LED with the Arduino and "blinking" it. The LED is connected to PIN 53 but can easily be moved to any of the other digital output pins. Since we don't want to burn out the LED, it needs a current limiting resistor to be placed in series with the Ground pin located also on the Arduino. The resistor I used for the circuit was 470 Ohms, but that's only because I couldn't find any 220 Ohm resistors. 

 Made with LTspice, use your imagination

You can calculate the minimum resistance value needed for any LED by taking the supply voltage (In this case 5V from the Arduino) and subtracting the voltage drop across the LED. You can find this value by checking the data sheet from the LED manufacturer. If you've got a mystery LED, it is safe to assume it is going to draw about 1.7-2.0 volts. Then, you divide by the typical current drawn by the LED. Once again, if you have a mystery LED, they normally draw about 20mA.

(5V - 1.7V) / 20mA = 165 Ohms

This is the minimum resistance you would want to use for your current limiting resistor. You can, of course, use a greater resistor value. The LED will receive less current though and will be a bit dimmer, but you probably won't notice and this way you don't have to worry about destroying the LED. Also, if you are powering the Arduino off of a battery pack, you may want to use a higher resistance anyways to increase the battery life of your project.

Here is the blink program I wrote based off of the sample code provided in the Arduino IDE. As the blog progresses I'm going to start assuming that you know how the basic functions work and start skimming through explinations. There will always be comments in the code that you can follow along with.


/*=====================================================
Noah Stahl
5/6/2011
Arduino Mega 2560

This program will blink an LED on Pin 53 using
a hard coded delay of 1000ms.
=======================================================*/
unsigned int toggle = 1;      //Keep track of LED state

void setup(){
  pinMode(53, OUTPUT);        //Sets pin 53 as an output
}

void loop(){
  digitalWrite(53, toggle);   //Change LED state
  delay(1000);                //Wait 1000ms
  toggle = !toggle;           //Change toggle to opposite of its current value
}

You can download my updated Blink code from here:


First, Pin 53 is set as an output using the pinMode function in setup. The first number in the parenthesis corresponds to the pin that you want to set. If you don't have an Arduino Mega and don't have a pin 53, just change this number to one of your DIGITAL pins. Once the program moves into the loop, Pin 53's voltage output is set either ON or OFF depending on the value stored in the toggle variable. Next, the delay function is used to wait 1000ms (1 second). If we wanted to speed up the interval between blinks, we would just decrease the value passed into the delay function. And finally, the toggle variable's value is changed to the opposite of its current value.

Once you download, verify, and upload the sketch onto your Arduino you should see your LED blink at a one second interval.

This code, while very simple, is in fact a gigantic resource hog. The problem with using the delay() function is that the Arduino will do just that, wait. While the Arduino is stuck waiting, no other processes can take place. This would be very inconvenient for any project requiring something else to be happening while your Arduino sits there and counts. My next post will teach you how to get around this by using timers and interrupts.

Up Next: Timer2 Overflow Interrupt...

Arduino's IDE: And it begins...

Even if you're programming in hex, there are applications that the micro-controller companies release that allow you to write and program your chip. The IDE program provided for the Arduino utilizes C with some added functions built specifically for the board. If you know how to program in C you know how to write programs for the Arduino.

Let us take a closer look at the Arduino's IDE:

The bare minimum that a program needs to avoid a compiler error are the setup() and loop() functions. Even if you aren't using them, they still need to be placed in the sketch for structure's sake.

 Found by going to File>Examples>Basics>BareMinimum inside of the Arduino IDE

The setup() function is used to initialize pins, registers, variables, and peripherals. It is the first function that is called when the Arduino resets or is powered up, and is only run once. Oddly enough, in all of the example code that I've seen no one actually initializes variables inside of the setup() function even though the Arduino reference suggests it. Most variables are normally declared at the beginning of the program to assure that they are global.

The loop() function replaces the main() function that one would normally see when programming in C. More specifically, it would replace the while loop inside of the main function. As you probably can infer from the name, the loop() function continuously loops until you either place the Arduino into sleep mode or shut it off.

You can still create other functions that would be called from inside of loop(), just place them outside of the loop function as you would when programming in C.


When you are finished with your program or just want to check for any syntax errors, click on the Play (Verify) button to run the compiler.


If your program is error free, you will see a message at the bottom of the IDE letting you know when the compiler is done.


If there are any errors, the program is pretty specific as to what they are and will even highlight the line where the compiler crashed for you. This is a great relief for anyone who has tried to decipher the cryptic error messages in other IDE programs. I'm looking at you MPLAB!



Now you can program your Arduino, assuming you have the USB cable (or a printer to steal it from) by clicking on the upload button.

The best way to learn how to use a new program is to try it out for yourself, so up next... Blinking an LED

Pop That Corn!

I'm sure all of you have been wondering: "When the hell is he going to get to the science? I came here to learn damn it!". Well, hold on to your hats folks because this post is sure to be a doozie.

Today marks a monumental day for the blog. This is going to be the first post in which I actually throw down some Arduino code. And where better to start my adventure than with the Arduino's own version of "Hello World!", making an LED blink? Every great adventure starts with a single step, but thanks to technology we don't have to leave the comfort of our air conditioning to do it.

Edit:
After typing all of this up, I've decided it may be best to split up tonight's work into three different posts so that it is easier to read. This will also make transporting the information into some type of tutorial later a whole lot easier too.

Friday, May 06, 2011

Hello? Are you still there?

I thought I was going to update the blag last night and work on the Arduino. Nope.
Picked up Portal 2 on Amazon for $30 earlier this week and it finally arrived last night right after the Arduino.

So much for that... pulled an all-nighter and finished it sometime around 8am.

Now, back to science.

Thursday, May 05, 2011

Good News Everyone!

My Arduino arrived today, huzzahs all around.


They sure have stepped up their packaging over the past year. The board came in a spiffy little box instead of one of those difficult to open plastic packages, and with a thank you pamphlet and six little stickers. I'll be sure to put one of them on my finished robot somewhere.

It's amazing how small these boards are. The Mega 2560 isn't that much bigger than my old Duemilanove and you can really tell the quality of the board. I guess I've just become so used to the ugly copper boards I've been making with the school's milling machines that anything with screen printing and a solder mask (especially blue) is going to impress me.

Everything you need to get the Arduino up and running can be found on their website with a step-by-step tutorial as well.


If you click on the Learning tab on the website, you can also find at least a hundred tutorials to get you started. And this is where I find myself.

Time to pull out the LEDs and start reading up. Once my insomnia starts to kick in later tonight, I'll be sure to give you all an update.

Noza out!