Get free access to our online edition!

Nuts & Volts Magazine (July 2009)

Smiley’s Workshop 12: AVR Learning Platform Projects

By Joe Pardue    View In Digital Edition  


Smiley's Workshop — AVR C Programming Workshop Series - Part 12

In this and the next few Workshops, we will continue with the ALP (AVR Learning Platform). We will take a look at some things that will quickly get us using more of the components from the Smiley Micros Arduino Projects kit (see below).

FIGURE 1. Layout for Cylon Optometry.

Recap

In Workshop 9, we began using a new development board — the Arduino Duemilanove — recognizing that The Arduino Way (TAW) is a very simple and easy way to begin using microcontrollers. We learned that while TAW uses a C-like language and has an easy-to-use IDE (Integrated Development Environment), it does not IMHO (In My Humble Opinion) provide a clear path to learning the C programming language or the details of the AVR architecture — both of which are our long-term goals for this Workshop series. To help overcome this, we learned how to convert TAW code to work with the more standard (IMHO) Atmel AVR tools: AVRStudio, WinAVR, and AVRDude using A C Way (ACW). We put together the ALP that uses the Arduino Projects kit. This will provide our hardware development system for many Workshops to come.

This time, we will do another communications project, learn to read the voltage across a potentiometer, and then redo the Cylon Optometry code that we first did for the Butterfly back in Workshop 5 for the ALP.

The ASCIITable Example in TAW and ACW

FIGURE 2. ASCII Table in Developer’s Terminal.

You can find the TAW version of the ASCIITable example in the Arduino IDE under the menu File\Sketchbook\Examples\Communication\ASCIITable. In the ACW version, we show TAW code commented out (marked with // to hide it from the compiler) to make it easier to compare the TAW and ACW versions. We will exercise the printf() function and learn that it doesn’t have a binary formatter, so we do without that for now.

Be sure and review Workshops 9 and 10 on converting code from TAW to ACW. From those discussions, you should be able to get AVRStudio to find libACW001 and compile the following code:

// ASCII Table - ACW
// Joe Pardue April 10, 2009
// This program outputs an ASCII Table to
// a PC Terminal.

#include “libACW001.h”

int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}

void setup()
{

  //Serial.begin(9600); //TAW
  serialBegin(9600);

  // prints title with ending line break
  // Serial.println(“ASCII Table ~ Character Map”); //TAW
  printf(“ASCII Table ~ Character Map\n”);
 
  // wait for the long string to be sent
  delay(100);

}

void loop()
{
  // prints value unaltered, first will be ‘!’
  //Serial.print(number, BYTE);    
  printf(“%c”, number);

  // prints value as string in decimal (base 10)
  //Serial.print(“, dec: “);
  //Serial.print(number);          
  printf(“, dec: %u”,number);
 
  // prints value as string in hexadecimal (base 16)
  //Serial.print(“, hex: “);
  //Serial.print(number, HEX);     )
  printf(“, hex: 0x%x”,number);
 
  // prints value as string in octal (base 8)
  //Serial.print(“, oct: “);
  //Serial.print(number, OCT);     
  printf(“, oct: %o”,number);
      
  // also prints ending line break
  printf(“\n”);                   
 
  // if printed last visible character ‘~’ #126 ...

  if(number == 126) {
  // loop forever
   while(1){//true) {
     continue;
    }
  }
  // to the next character
  number++;  

  // allow some time for the Serial data to be sent
  delay(100);  
}

As discussed in earlier Workshops, compile this and upload it using the following two lines in the cmd window to move it to the correct directory and run AVRDude:

Change directories with:
cd \ArduinoToAVRStudio-ASCII Table\default
Upload the code with:
avrdude -p m328p -c avrisp -P com6 -b 57600 -F -U flash:w:ASCIITable.hex

In the Developer’s Terminal, I set the Receive Text window to the font Courier New/10 point so that the characters would align. You may also see that the terminal occasionally adds an extra blank line — a bug in the terminal I’ll get to one of these days. Developer’s Terminal was discussed in Smiley’s Workshop 4: Teaching the Butterfly to Talk. You can download both the Developer’s Terminal and that Workshop from either http://www.nutsvolts.com or www.smileymicros.com.

Analog Input: Reading a Potentiometer

FIGURE 3. Potentiometer Schematic Symbol.
 

Let’s do a couple of projects with the Arduino Projects kit potentiometer shown in Figure 3. The schematic for this project is shown in Figure 4. The layout using the breadboard on the ALP is shown as a drawing in Figure 5.

FIGURE 4. Schematic for Potentiometer with LED.
 
FIGURE 5. ALP’s AnalogInput Layout.

Note that in addition to the potentiometer, we have a 100 Ω resistor in this circuit that prevents us from shorting the power to ground if we set the potentiometer to zero Ω. The analog reading will be for five volts across a resistance for the potentiometer + the resistor from a maximum of 10,100 Ω down to a minimum of 100 Ω. The AVR ADC will measure in 1,024 steps, with the 1,023 value for the full five volts. Since we have the 100 Ω resistor, our lowest ADC value should be about 1% of the full 1,024 range, or roughly 10. In my actual test, the low value was seven, but remember that both the pot and the resistor values have associated errors, so without some sort of external calibration, we will may be off a bit.

I recommend that first you test the hardware using the original AnalogInput from the Arduino IDE example code — the TAW version. BUT change the analog pin from 2 to 0 in the TAW code to match the hardware shown. It doesn’t matter which analog pin you use as long as your code matches the hardware. Once you see that the hardware works — the LED blink rate varies as you turn the potentiometer to the left and right — you can move on to the slightly harder ACW source code version with reasonable confidence that the hardware is correct.

AnalogInput Example Ported from TAW to ACW

// AnalogInput
// mostly ‘borrowed’ from the Arduino example

#include “libACW001.h”

// select the input pin for the potentiometer
int potPin = 0;
// select the pin for the LED
int ledPin = 13;   
// variable to store the value from the sensor
int val = 0;

int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}

void setup()
{
  // declare the ledPin as an OUTPUT
  pinMode(ledPin, OUTPUT);  
}

void loop()
{
  // read the value from the sensor
  val = analogRead(potPin);    
  // turn the ledPin on
  digitalWrite(ledPin, HIGH);  
  // stop the program for some time
  delay(val);                  
  // turn the ledPin off
  digitalWrite(ledPin, LOW);   
  // stop the program for some time
  delay(val);                  
}

Change directories with:
cd \ArduinoToAVRStudio-AnalogInput\default
Upload the code with:
avrdude -p m328p -c avrisp -P com6 -b 57600 -F -U flash:w:AnalogInput.hex

Just as an aside, this was the first time I was actually able to copy code from TAW, change it to ACW, and have it compile and upload with no errors. All the prior examples took some debugging. I tell you this so that you know that getting all this right isn’t easy, even for the guy who is trying to teach it. Chant: ‘Patience Persistence Payoff.’

The Dimmer Example in TAW and ACW

In Workshop 11, we saw how to send out serial data from the AVR to a terminal on the PC, but what about the other direction? In old-fashioned C, you’d use some version of the scanf() function to match the printf(), but we aren’t going to do that. We will use some functions in libACW001.a that replace the TAW serial functions, as discussed in the last Workshop.

Again, I suggest that before you do this ACW you do it in TAW with the dimmer example from File/Sketchbook/Examples/Communication/Dimmer. The following is the ACW version with the original TAW functions commented out:

#include “libACW001.h”

// Use a pin with PWM
int ledPin = 9;

int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}
 
void setup()
{
  // begin the serial communication
  //Serial.begin(9600);
  serialBegin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  byte val;
 
  // check if data has been sent from the computer
  //if (Serial.available()) {
  if (serialAvailable()) {
    // read the most recent byte
    //val = Serial.read();
    val = serialRead();
    // set the brightness of the LED
    analogWrite(ledPin, val);
  }
}

Change directories with:
cd \ArduinoToAVRStudio-Dimmer\default
Upload code with:
avrdude -p m328p -c avrisp -P com6 -b 57600 -F -U flash:w:Dimmer.hex

FIGURE 6. Send HEX Immediate:.
 
FIGURE 7. ACW ReadPot Output.

This program continuously checks to see if there is a byte of data available on the serial port and if so, uses that data to set the LED brightness. You can test it with Developer’s Terminal (we saw how to use it in earlier Workshops). You can use the ‘Send HEX Immediate:’ as shown in Figure 6.

Read a Potentiometer with a Terminal

This project — ReadPot — isn’t taken from an Arduino IDE example, but it is based on Tom Igoe’s example shown in the Arduino Serial.print(data) web page. As usual, I leave in the TAW code commented out to show the TAW versus ACW differences. The output read by Developer’s Terminal should look something like that shown in Figure 7.

ReadPot in ACW

// ReadPot ACW
// Joe Pardue March 24, 2009
// based on Tom Igoe’s example in the
// Arduino Serial.print(data) documentation

#include “libACW001.h”

// variable to hold the analog input value
int analogValue = 0;

int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}
 
void setup()
{
  // begin the serial communication
  //Serial.begin(9600);
  serialBegin(9600);
}

void loop()
{
  // read the analog input on pin 0
  analogValue = analogRead(0);
 
  // print prolog to value
  //Serial.print(“ADC Reading: “);
  printf(“ACW ADC Reading: “);         
 
  // print as an ASCII-encoded decimal
  //Serial.print(analogValue, DEC);
  //Serial.println(); // print a newline
  printf(“%d\n”, analogValue);
 
  // delay 1 second before the next reading:
  delay(1000);
}

Change directories with:
cd \ALP ReadPot\default
Upload code with:
avrdude -p m328p -c avrisp -P com6 -b 57600 -F -U flash:w:ReadPot.hex

Cylon Optometry Redux

FIGURE 8. DIP Switch Drawing and Schematic Symbol.

In Workshop 5, we used an eight-position DIP switch and eight LEDs to demonstrate some principles for using binary numbers in C with the Butterfly. We will do a subset of that project for the ALP. We are forced to use a subset since we can only read five DIP switches if we want to also use the eight LEDs with the Arduino board. We will sacrifice speed control from the original project that used four switches to encode 16 speeds; we will only use one switch for two speeds.

If this statement doesn’t make sense to you — “There are exactly 10 types of people in the world. Those who understand binary and those who don’t.” — then please read Smiley’s Workshop 5. You might also find this useful: Smiley’s Workshop 5 Supplement 1 — Cylon Optometry.pdf (go to Workshop 5). The layout drawing for this project was shown at the beginning of the Workshop in Figure 1; the schematic follows in Figure 9.

FIGURE 9. Schematic for Cylon Optometry.


Scroll Cylon Eyes

This time, we will show the code in both TAW and ACW since there is no example in the Arduino IDE or on its website.

Cylon Eyes TAW:

// CylonEyes TAW
// Joe Pardue April 11, 2009

void setup()
{
 // Init port pins
 DDRB = 0x00; // set port B for input
 DDRD = 0xFF; // set port D for output    
}

void loop()
{
  for(int i = 1; i <= 128; i = i*2)
  {
    PORTD = ~i;
    delay(128);
  }    
            
  for(int i = 128; i > 1; i -= i/2)
  {
    PORTD = ~i;
    delay(128);
  }    
}


Cylon Eyes ACW:

For the ACW version, we drop the ‘~” thus changing the polarity of the LED scroll so we can tell the TAW and ACW versions apart:

// CylonEyes ACW
// Joe Pardue April 11, 2009

#include “libACW001.h”

int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}
 
void setup()
{
 // Init port pins
 DDRB = 0x00; // set port B for input
 DDRD = 0xFF; // set port D for output    
}

void loop()
{
  for(int i = 1; i <= 128; i = i*2)
  {
    PORTD = i;
    delay(128);
  }    
            
  for(int i = 128; i > 1; i -= i/2)
  {
    PORTD = i;
    delay(128);
  }    
}

Change directories with:
cd \ALP CylonEyes\default
Upload code with:
avrdude -p m328p -c avrisp -P com6 -b 57600 -F -U flash:w:CylonEyes.hex

Show DIP Input on LED Output

Before we use the DIP switch to control our Cylon Eyes, let’s make sure we can read its state and show that state on the LEDs:

DIPLEDTAW

// DIPLEDTAW
// Joe Pardue April 11, 2009

void setup()
{
 // Init port pins
 
 DDRB = 0x00; // set port B for input
 PORTB = 0xFF; // set port B pullups

 DDRD = 0xFF; // set port D for output    
}

void loop()
{
  PORTD = PINB;
}

DIPLEDACW

// DIPLEDTAW
// Joe Pardue April 11, 2009

#include “libACW001.h”

int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}

void setup()
{
 // Init port pins
 
 DDRB = 0x00; // set port B for input
 PORTB = 0xFF; // set port B pullups

 DDRD = 0xFF; // set port D for output    
}

void loop()
{
  PORTD = PINB;
}

DIPLEDACW

// DIPLEDTAW
// Joe Pardue April 11, 2009

#include "libACW001.h"

 int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}

void setup()
{
// Init port pins

 DDRB = 0x00; // set port B for input
 PORTB = 0xFF; // set port B pullups

 DDRD = 0xFF; // set port D for output
}

void loop()
{
  PORTD = PINB;
}

Change directories with:
cd \ALP DIPLED\default
Upload code with:
avrdude -p m328p -c avrisp -P com6 -b 57600 -F -U flash:w:DIPLED.hex

Read DIP Switch Positions on Terminal

Now, we have a little problem if we want to read the DIP input, show it on the LEDs, and send it out the serial port. The problem is that the lowest two PORTD pins that drive the rightmost two LEDs are also used to show traffic on the serial port. So, we use the shift operator (see Workshop 5) to move the five lower bits on the DIP switch left three positions so that they will be shown on the leftmost five LEDs:

DIPLEDSerialTAW

// DIPLEDSerialTAW
// Joe Pardue April 11, 2009

int value = 0;

void setup()
{
 // Init port pins
 DDRB = 0x00; // set port B for input
 PORTB = 0xFF; // set port B pullups
 DDRD = 0xFF; // set port D for output    
 
 Serial.begin(9600);

}

void loop()
{
  // load PORTB pins into value
  value = PINB;

  // shift value left 3 positions
  PORTD = (value << 3);
  Serial.print(“value = “);
  Serial.println(value);
  delay(1000);
}

DIPLEDSerialACW

// DIPLEDSerialACW
// Joe Pardue April 11, 2009

#include “libACW001.h”

int value = 0;

int main(void)
{
  init();  // Initialize private stuff
  setup(); // Setup the public stuff
  for (;;) loop(); // Call loop() forever;
  return 0; // You never get here.
}

void setup()
{
 // Init port pins
 DDRB = 0x00; // set port B for input
 PORTB = 0xFF; // set port B pullups
 DDRD = 0xFF; // set port D for output    
 
 //Serial.begin(9600);
 serialBegin(9600);
}

void loop()
{
  // load PORTB pins into value
  value = PINB;

  // shift the value left 3 positions
  PORTD = (value << 3);
  //Serial.print(“value = “);
  //Serial.println(value);
  printf(“value = %d\n”,value);
  delay(1000);
}

Change directories with:
cd \ALP DIPLEDSerial\default
Upload code with:
avrdude -p m328p -c avrisp -P com6 -b 57600 -F -U flash:w:DIPLEDSerial.hex

We have now shown all the components for the Cylon Optometry, but we’ve run out off space, so the final source code that provides the blink patterns is in the Workshop12 Sourcecode.zip file under /ACW/Cylon Optometry ACW/ and /TAW/Cylon Optometery TAW/. Remember, this is a repeat of some concepts from Workshop 5, so you may want to refer back to that if all this seems a bit rushed.  NV


You can find the source code and supplements for this article in Workshop12.zip in the downloads section of the Nuts & Volts website and Smiley Micros website.


The Arduino Projects Kit

The Arduino projects kit is a special hardware pack that will help you get hands on experience as you follow along with this series. It provides components for use with Workshops 9, 10, 11, and many future Workshops. Over time, we'll learn simple ways to use these components, and more importantly, use them to drill down into the deeper concepts of C programming, AVR microcontroller architecture, and embedded systems principles.

The Arduino Projects kit is offered through the Nuts & Volts webstore and Smiley Micros website.

Downloads

July2009_SmileysWorkshop12

Workshop 12 Source Code



Comments