Meandering Arduino Notes


You Did Blink, So Now, Son of Blink

In this very simple, very boring project where you learn something invaluable, how to protect your Arduino ports from an over-current condition, i.e. blowing them up. When you look at a schematic of the Arduino Uno/Duemilanove, you'll see that pin 13 is connected to an an on-board LED (the one that blinks when you run Blink), but there is another component in series with the LED, a 1K ohm resistor that is used to limit current. Why? Well, this is the official explanation:


Pins configured as OUTPUT with pinMode() are said to be in a low-impedance state. This means that they can provide a substantial amount of current to other circuits. Atmega pins can source (provide positive current) or sink (provide negative current) up to 40 mA (milliamps) of current to other devices/circuits. This is enough current to brightly light up an LED (don't forget the series resistor), or run many sensors, for example, but not enough current to run most relays, solenoids, or motors.

Short circuits on Arduino pins, or attempting to run high current devices from them, can damage or destroy the output transistors in the pin, or damage the entire Atmega chip. Often this will result in a "dead" pin in the microcontroller but the remaining chip will still function adequately. For this reason it is a good idea to connect OUTPUT pins to other devices with 470Ω or 1k resistors, unless maximum current draw from the pins is required for a particular application.


There are internal 20K ohm pull up resistors on the digital ports, and these are activated in the pinmode INPUT (pimode(pin#,INPUT) state when the pin is set HIGH (digitalwrite(pin#, HIGH). So, we are going to run two programs, one using the internal resistor to dimly light an LED and then we'll turn everything off and set up an LED with a proper series resistor (more about that soon) and set the pinmode to OUTPUT and digitalwrite to HIGH (giving us +5 volts at 40 ma maximum).

But, This Is Boring...

First let's look what happens if we use the internal resistor so that we'll recognize the symptoms when our circuits get more complex. Realize that you might damage your board if you don't follow the directions verbatim.


  1. Turn off the Arduino (pull power – if used - and the USB connection, which incidentally also supplies power, although with limited current capabilities (officially about 100 ma total, with a possibility of 500 ma depending on your system).

  2. Connect a wire from digital port 8 (see layout here) to the long lead of the LED (anode). Connect the LED shorter lead to a 'momentary on' type (normally open, closes when pushed) pushbutton switch. Connect the other end of the pushbutton to the arduino ground pin.  The switch is just insurance.

  3. Load the listed code (Program 1a).

  4. Press the pushbutton.


/* You Did Blink, So Now, Son of Blink

Program 1a

a very boring program

http://sites.google.com/site/measuringstuff/

*/

const byte myPin=8;

void setup()

{

// These two statements set myPin at 5 volts with

// a 20K resistor

// This limits currrent to 0.25 ma! A very tiny current!

pinMode(myPin,INPUT);

digitalWrite(myPin,HIGH);

}

void loop()

{

}

Did you see a dimly lit LED like the documentation suggests? I certainly didn't, and here's why. Five volts divided by 20000 ohms gives all of .00025 amps (.25 ma). That's not enough to light any LEDs I have lying around.

Now replace the pushbutton with a resistor in the range of 250 ohms to a 1000 ohms. One thousand ohms (brown black red) provides 5 ma of current which is the rated current for many small common LEDs (these are NOT the high intensity type). Load the following code.

/* You Did Blink, So Now, Son of Blink

Program 1b

http://sites.google.com/site/measuringstuff/

*/

const byte myPin=8;

void setup()

{

// These two statements set myPin at 5 volts with

// no current limiting resistor

// do not exceed 40 ma per pin!

// always use 100 ohms or higher current limiting reistor!

pinMode(myPin,OUTPUT);

digitalWrite(myPin,HIGH);

}

void loop()

{

digitalWrite(myPin,LOW);

delay(1000);

digitalWrite(myPin,HIGH);

delay(1000);

}

You now have your very own blinking LED, and you've learned to never, ever, to use a current limiting resistor lower than 100 ohms when working with the arduino. Does the schematic show 47 ohms? Don't do it!


Working With Displays

Displaying a result is sometimes the whole point of a project, and the Arduino does this by sending data to the IDE serial monitor or to a local display using parallel or serial processing. Perhaps the best way to show this is with a set of examples. First we'll look at sending data back to the serial monitor by solving a mystery.

Using the IDE Serial Monitor 

We just got done examining the crucial current limits associated with digital ports, but there is another digital port mystery to be examined.  At what voltage level does a digital pin flip from LOW to HIGH and from HIGH to LOW?  Believe it or not, sometimes that is an important fact to know!  To find the facts of the situation, we'll send a one Hertz sinusoidal signal, alternating from 0 to 5 volts, to our digital friend, pin 8.  In the mean time, we'll be checking constantly for a state change while recording voltage with our hard working analog buddy, pin 0. We'll send the results to, you guessed it, the serial monitor.  But why?  Well, in this case we want a listing, or a record of events, and that is what the IDE serial monitor does best.  If we displayed to an LCD, there might be, at most, four display lines, but the IDE monitor provides a listing or logging capability.

The frequency generator is set to 1 Hz, and the ground is tied to the Arduino ground.  The alternating 5 volt lead is tied to pins 0 and 8 through a 10K resistor.  Here is the input:

 

By the way, this came from a Parallax USB oscope running under Unbuntu Linux in wine.  The Arduino code is:



/*
Finding the turn on turn off voltage for a digital pi
Requires:
1 Hz 0 to 5 volt input signal
pin 8 is the tested digital pin
pin 0 is the analog monitor
*/

const byte digitalPin = 8;
const byte analogPin = 0;
unsigned long theTime=0;
int aVolts=0;
boolean dStatus=0;
const float refVolts=5.09;
int i=0;
int k=0;

// The setup() method runs once, when the sketch starts
void setup() 
{
// initialize the digital pin as an output:
Serial.begin(57600);
pinMode(digitalPin, INPUT);
}

// the loop() method runs over and over again,
// as long as the Arduino has power
void loop()
{
// get time
Serial.print(millis());
Serial.print(",");
// get voltage
Serial.print(analogRead(analogPin));
Serial.print(",");
// check digital status
Serial.println(digitalRead(digitalPin));
}

Here is a snippet of the data collected in the serial monitor window:

50520,598,1
50522,590,1
50525,584,1
50527,577,1
50529,569,1
50532,561,1
50534,556,1
50536,548,1
50538,541,1
50541,533,1
50543,527,1
50545,520,1
50547,513,1
50550,506,1
50552,499,1
50554,492,0
50557,485,0
50560,479,0
50562,471,0
50564,463,0
50567,457,0

And a Freemat script to evaluate the data:

% freemat script to find the average high low toggle voltages

% read in our comma separted file of time,raw voltage,high/low sign

T=csvread('/home/lowell64/arduino/data/highlow.csv');

% break the matrix into arrays

t=T(:,1);

v=T(:,2);

b=T(:,3);

%find the length of the arrays

s=size(t);

% graph the data

plot(t./1000,v);

xlabel('Seconds');

ylabel('Analog Output (0-1023)');

printf('Data array length is %d elements\n',s(1));

toggle1=b(1);

hi=1;

li=1;

for i=2:s(1)

toggle2 = b(i);

if (toggle2 ~= toggle1)

if (toggle2 == 1)

highv(hi)=v(i);

hi=hi+1;

else

lowv(li)=v(i);

li=li+1;

end

end

toggle1=toggle2;

end

refVolts=5.09/1023;

nh=size(highv);

nl=size(lowv);

highvolts=mean(highv)*refVolts;

printf('Average low to high voltage: %5.2f\n',highvolts);

printf('using %d averaged values\n',nh(2));

lowvolts=mean(lowv)*refVolts;

printf('Average high to low voltage: %5.2f\n',lowvolts);

printf('using %d averaged values\n',nl(2));


Which gives us:

Data array length is 22736 elements
Average low to high voltage:  2.42
using 51 averaged values
Average high to low voltage:  2.41
using 51 averaged values

Your mileage may vary!

Hooking Up An LCD Display (Parallel Mode)

There are two ways (okay, really many ways) to hook up an LCD screen to your Arduino.  The least expensive way is 'parallel' mode.  Unfortunately it takes a bunch of pins, but it works very well.  You don't need my ramblings on this one because  LadyAda has a fantastic tutorial.  

As you can see from the tutorial, it takes a lot of wires and six digital pins.  It's a mess to implement. This is ripe territory for a tiny breadboard solution (coming soon!).

Hooking Up An LCD Screen (Serial Mode)

LCD displays can be run using another small microcontroller that translates the serial bus activity.  This means one pin gets used on the Arduino, a serial Tx pin.  Of course you also need +5 volts (some are set for 3.3 volts) and ground, so there are three wires leading to the display.  Sparkfun has a ton of these devices including a backpack device for that LCD screen that is already in your kit.  There are other options that work as well, but Sparkfun serial has enough of a following that  several support libraries have been written, including my favorite from Moco's site (his library displays floats).  Remember, if you are using a Mega, then we're talking a single line to pin 18, 16, or 14 which are addressed as Serial1.print, Serial2.print, and Serial3.print respectively, without the need of libraries!  The Mega is great, and getting cheaper (haven't done business with site - be careful).
 

Interrupts and Momentary Pushbutton Switches

Quite often you want to use a momentary pushbutton switch to get the Arduino to do some task, and to further complicate the situation, you'll be asking the Arduino to do what you want it to do, while it is busy doing something else.

That's where interrupts come in handy.

The Arduino watches for changes in the state of interrupts while it does it normal chores (this is probably a good reason to turn interrupts off when not needed, use 'noInterrupts()') . So, I can press a pushbutton, thus pulling the interrupt pin LOW, and then the Arduino will happily go off and do whatever is supposed to be done according to the interrupt instruction.

Very cool, except I couldn't get it to work.

I would press the button, and sometimes something would happen, but often times not.  So, I learned about switch chatter and processing speed.  I was doing a state toggle, so of course, it became a game of chance to actually change the state.  All it takes is a simple while loop to capture the down state (LOW) of the pushbutton, and a slight delay to allow for contact chatter.  The code is shown below along with a layout pictorial.

/*
chkhighlow
*/

// define pins
const byte dpin3=3;
const byte dpin3int=1;
const byte ledpin=13;

// define globals
boolean toggleInt3=false;

void setup()   
{
pinMode(ledpin,OUTPUT);
digitalWrite(ledpin,LOW);
// method to sense a momentary switch connected to ground  
pinMode(dpin3,INPUT);
digitalWrite(dpin3,HIGH);
attachInterrupt(dpin3int,toggleInt3Routine,LOW);
}
void loop()                     
{
  if (toggleInt3) digitalWrite(ledpin,HIGH);
  else digitalWrite(ledpin,LOW);
}

void toggleInt3Routine()
{
  toggleInt3=!toggleInt3;
  while (digitalRead(dpin3)==LOW)
  {
      delay(1);
  }
  delay(1);
}

  
















So, the interrupt routine (int 1 is associated with pin3) calls a subroutine called 'toggleInt3Routine' when the pushbutton is pressed, but YOU, as mere human, are slow, so we have to capture the act in a while loop until the switch is released, otherwise the system toggles repeatedly.  That is the secret.  The delay is included to help with switch chatter (make sure contact is firmly made or not made).

Also note that simplicity of the pushbutton pulldown circuit.  By programming these two lines,

pinMode(dpin3,INPUT);
digitalWrite(dpin3,HIGH);

you are setting the internal pulldown resistor and placing the pin in a high state. You press the pushbutton to bring it low. The alternative is to use your own pulldown resistor as shown below, but this is much simpler. Here is the official explanation from the Arduino site:

 
Often it is useful to steer an input pin to a known state if no input is present. This can be done by adding a pullup resistor (to +5V), or a pulldown resistor (resistor to ground) on the input, with 10K being a common value.There are also convenient 20K pullup resistors built into the Atmega chip that can be accessed from software. These built-in pullup resistors are accessed in the following manner.
pinMode(pin, INPUT);           // set pin to input
digitalWrite(pin, HIGH);       // turn on pullup resistors

Note that the pullup resistors provide enough current to dimly light an LED connected to a pin that has been configured as an input. If LED's in a project seem to be working, but very dimly, this is likely what is going on, and the programmer has forgotten to use pinMode() to set the pins to outputs.

Note also that the pullup resistors are controlled by the same registers (internal chip memory locations) that control whether a pin is HIGH or LOW. Consequently a pin that is configured to have pullup resistors turned on when the pin is an INPUT, will have the pin configured as HIGH if the pin is then swtiched to an OUTPUT with pinMode(). This works in the other direction as well, and an output pin that is left in a HIGH state will have the pullup resistors set if switched to an input with pinMode().


Setting the internal pull-up is so much simpler than building this circuit:


The Arduino Duemilanove only has two interrupts. The Mega has four. They are very handy things.

AnalogReference, a Cautionary Tale


Alright, so if you go to the analogReference reference page, it says:

If you're using an external reference voltage (applied to the AREF pin), you must set the analog reference to EXTERNAL before calling analogRead(). Otherwise, you will short together the active reference voltage (internally generated) and the AREF pin, possibly damaging the microcontroller on your Arduino board.

Alternatively, you can connect the external reference voltage to the AREF pin through a 5K resistor, allowing you to switch between external and internal reference voltages. Note that the resistor will alter the voltage that gets used as the reference because there is an internal 32K resistor on the AREF pin. The two act as a voltage divider, so, for example, 2.5V applied through the resistor will yield 2.5 * 32 / (32 + 5) = ~2.2V at the AREF pin.

Well, that's cool.  I'm logging some specialized task that varies from 0 to 2.5 volts, so I tie the 3.3 volt output to the reference pin using a 10K resistor, and now when I set analogReference to EXTERNAL, I get maximum resolution since, 3.3*32/(32+10)=2.51 volts.  And, by golly, it matches up fairly well.  Also, when I go back to DEFAULT, I have no problems.  But, and you knew it was coming, watch out for INTERNAL!  It does not act like normal.  Here's the code and the output.  The input to pin 7 is set precisely to 1 volt.

int rawVolts;

void setup()

{

  Serial.begin(9600);

  //------------------------------------

  Serial.println("Using default reference");

  analogReference(DEFAULT);

  delay(2);

  for(int j=0;j<10;j++)

  {

    rawVolts=analogRead(7);

    Serial.println(rawVolts,DEC);

  }

  float f=5.08*(float)rawVolts/1023.0;

  Serial.print(" --> ");

  Serial.print(f,3);

  Serial.println("  volts");

  //------------------------------------

  Serial.println("Using external reference");

  analogReference(EXTERNAL);

  delay(2);

  for(int j=0;j<10;j++)

  {

    rawVolts=analogRead(7);

    Serial.println(rawVolts,DEC);

  }

  f=2.51*(float)rawVolts/1023.0;

  Serial.print(" --> ");

  Serial.print(f,3);

  Serial.println("  volts");

  //------------------------------------

  Serial.println("Using internal reference");

  analogReference(INTERNAL);

  delay(2);

  for(int j=0;j<10;j++)

  {

    rawVolts=analogRead(7);

    Serial.println(rawVolts,DEC);

  }

  f=1.1*(float)rawVolts/1023.0;

  Serial.print(" --> ");

  Serial.print(f,3);

  Serial.println("  volts");

}


void loop()

{

}

Using default reference

213

213

214

212

213

213

212

212

213

212

 --> 1.053  volts

Using external reference

200

392

397

397

396

396

397

396

396

397

 --> 0.974  volts

Using internal reference

395

398

397

397

397

398

396

396

397

396

 --> 0.426  volts


Also note the first two reading inside each loop.  They are always screwed up.  Doesn't matter whether you change the analogReference and do a couple of reads outside the loop, the first two readings inside the loop will be messed up.  Just something to think about. 


 


Comments