Monday, July 21, 2014

ATtiny85 Arduino part 1

I was inspired by the Paperduino Tiny project on Instructables and the Digispark board from Digistump to make my own Arduino board based on the Atmel ATtiny85 chip. The Paperduino project might be too hard for high school students with limited soldering and engineering experience but we can make our own circuit boards in the classroom.

I used the USBtinyISP AVR programmer to load the micronucleus boot loader onto a couple of ATtiny85 chips purchased from Abra Electronics.
NOTE: The High fuse should set to 0xdd. The instructions here indicate that burning with or without RSTDSBL can be tried but the chip burned with RSTDSBL (hfuse:w:0x5d:m) did not function afterwards and AVRDUDE (see below) does not recognize it any more.
AVRDUDE is a DOS based program that comes as a part of WINAVR. The DOS commands are rather long so I copied them into notepad and saved them as .bat files. To run the DOS command just put the .bat file into the same directory as the .hex file and type the name of the .bat file in from DOS in that directory. The complete command also needs to be modified to reflect the ASP used.
avrdude -c usbtiny -p t85 -U flash:w:micronucleus-1.06-upgrade.hex -U lfuse:w:0xe1:m -U hfuse:w:0xdd:m -U efuse:w:0xfe:m
Here is the breadboarded Paperduino circuit.




The circuit board to the right contains the circuitry necessary to connect the USBTINY AVR to the ATtiny85 chip on the bottom half and a USB breakout at the top. USB sockets do not plug in to breadboards so some kind of adaptor was required.

I used Fritzing to lay out the circuit board. From Fritzing I export an etchable svg file which I bring into Corel Draw so that I can make the traces thicker and add text.





I like the way the Paperduino brings out the ATtiny85 pins to a female header so I did the same thing below the chip on my board. 22 gauge hookup wire can be used to connect to a breadboard.

The Digispark Arduino IDE package contains the driver installation. The red LED is on Digital pin 1 so the Blink program with the output set to pin 1 is all that is needed to verify operation. Change the Programmer to Digispark and the Board to Digispark (Tiny Core). You start with the board unplugged and after hitting Upload the IDE tells you to plug in the board. The breadboard version worked once and the circuit board a couple of times. Most of the time I end up with a USB Not Recognized warning from Windows. The computer sees it as an unknown device. Despite this the code has loaded on three occasions. I suspect something is going on in Windows.

Despite having problems with the Windows driver it did work a couple of times and my red LED is happily blinking away as I write this. I think of Blink as a kind of Hello World on my own home-made ATtiny85 based Arduino board. Nice!

Tuesday, July 1, 2014

8 x 8 LED matrix using 4017 and Arduino


  This years grade 12 Computer Engineering summative  challenge asked the students to control an 8 x 8 LED matrix using a 4017 decade counter to "scan" the columns and an Arduino to "sink" the rows. Examine the schematic below and you will see that control of any particular LED in the matrix is a matter of making the 4017 pin high and the Arduino pin low. The 4017 chip counts from one to ten and then resets but setting pin 15 high also causes a reset. Connecting pin 9 to 15 causes the ninth count to reset the 4017 back to 1. Our decade counter ends up counting one to eight. Clock pulses on pin 14 make the 4017 count up. The fastest possible digitalWrite(2, HIGH); digitalWrite(2, LOW) cycle from the Arduino is enough for the 4017 to respond.
   The usual logic for LED control is to use the Arduino pins to "source" the LED - to go high or "on". An examination of most Logic and microprocessor circuitry specifications will show that these circuits have a higher capacity to "sink" current. That means you need to connect the LED anode to power and the cathode to the Arduino pin. Making the pin low is the same as ground so a zero on the pin will cause the LED to glow. This is a reversal in our usual logic where one = on and zero = off. In this case we need to keep the Arduino pins high and send them low to turn on the LED.

Parts
Arduino UNO
Breadboard
4017
BL-M07C881 - 8x8 LED matrix
8 x 300 ohm resistors (I used an 8 x 470 ohm DIP)
jumper wires (I cut most to length to keep it neat)

Here are the pin-outs for the LED matrix:


STEP 1: Wiring

Matrix             4017
5 -----------------> 3
6 -----------------> 2
7 -----------------> 4
8 -----------------> 7
13 ---------------> 6
14 ---------------> 5
15 ---------------> 1
16 ---------------> 10

Matrix             Arduino via a current limiting resistor.
1 -----------------> 6
2 -----------------> 7
3 -----------------> 8
4 -----------------> 9
12 ---------------> 10
11 ---------------> 11
10 ---------------> 12
9 -----------------> 13

Don't forget to wire Arduino pin 2 to 4017 pin 14 for a clk input and pin 3 on the Arduino to to pin 6 on the 4017 to detect a count of 8. Wire 5V from the Arduino to pin 16 on the 4017 and tie the ground to 4017 pins 8 and 13.



Step 2 Testing

   Use Blink but change the output pin to 2. Disconnect the wires going to the Arduino pins 6 to 13 . Blink will pulse the 4017 slowly. Connect any one of the 8 disconnected leads to ground. The LEDs on that row should light up one after another. If you reduce the delay the LEDs will light quicker and quicker. A very short delay will make it seem as though the entire row is lit. POV or Persistence of Vision makes it seem as though all 8 LEDs are on at the same time when really only one is on at any given time. You will use this later to minimize the current draw on your circuit.

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */

 
// Pin 2 is our 4017 clock.
int clk = 2;

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(clk, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(clk, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(clk, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second
}

Step 3 Programming

In the checkmark example below I used an array to 'map' which LEDs I wanted to come on.

/*
 Test 4
 Test 4 Checkmark
 */
int del = 0; // clk width
int chk = 0; // check for reset signal
int row = 1; // row
int ct = 0; //array counter
int pattern[] = {0,0,0,0,0,0,0,1,
                       0,0,0,0,0,0,1,0,
                       0,0,0,0,0,1,0,0,
                       1,0,0,0,1,0,0,0,
                       0,1,0,1,0,0,0,0,
                       0,0,1,0,0,0,0,0,
                       0,0,0,0,0,0,0,0,
                       0,0,0,0,0,0,0,0};
int sensorPin = 3;
void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  // initialize the digital pins as an output.
  pinMode(2, OUTPUT); //clk out
  pinMode(3, INPUT); // reset sense
  for (int i = 6; i <=13; i++){
    pinMode(i, OUTPUT);  
  }
  for (row = 6; row <=13; row++){
    digitalWrite(row, HIGH);  
  }
  reset();
} // setup

void loop() {
    for (row = 6; row<=13; row++){
      for(int i=1; i<=8; i++){
        if(pattern[ct]==1){    
        digitalWrite(row, 0);
        pulse();
        delay(2);
        digitalWrite(row, 1);
        }
        else{
          pulse();
        }
        ct++;
    }
  }
ct=0;
}

void pulse(){
      digitalWrite(2, HIGH);
      delay(del);            
      digitalWrite(2, LOW);  
      delay(del);  
      // 8 pulses for 8 rows
}

void reset (){
  for (int i = 1; i<=9; i++){
    pulse();
    Serial.println(i);
    chk = digitalRead(sensorPin);
    Serial.println(chk);
    if (chk==1){  
      return;
    } // if
  } // for
} // function

If I were to do this again I would reverse the zeros and ones in pattern[] and then instead of using an if statement to check for a 1 I could have simply written the contents of the array out.
for(int i=1; i<=8; i++){  
        digitalWrite(row, pattern(ct));
        pulse();
        delay(2);
        digitalWrite(row, 1);
        }
       ct++;
etc.

Another interesting experiment would be using Arduino Port D (pins 0 to 7) to control all 8 rows with one command. I usually avoid using pins 0 and 1 but in this case it seems sensible to use them.

Step 4 Challenge

The challenge that earned top marks was to make a set of initials scroll across the matrix. In the example below I used a 2 dimensional array to 'map' the LEDs in a way that would make shifting them easier.
/*
 Test 5
 Scrolling Checkmark
 Implements 2 dimensional array
 */
int del = 0; // clk width
int chk = 0; // check for reset signal
int row = 1; // row
int ct = 0; //array counter

int pattern[8][16] = {
  {0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1},
  {0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0},
  {0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0},
  {1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0},
  {0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0},
  {0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
int ab=0;
int sensorPin = 3;
void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  // initialize the digital pins as an output.
  pinMode(2, OUTPUT); //clk out
  pinMode(3, INPUT); // reset sense
  for (int i = 6; i <=13; i++){
    pinMode(i, OUTPUT);  
  }
  for (row = 6; row <=13; row++){
    digitalWrite(row, HIGH);  
  }
  reset();
} // setup

void loop() {
  for(ct=0; ct<8 animation="" croll="" ct="" p="">    for (int draw = 0; draw<5 down="" draw="" p="" slow="">      for(int row=0; row<8 p="" row="">        for(int col=0; col<8 col="" p="">          if(pattern[row][col+ct]==1){    
            digitalWrite(row+6, 0);
            pulse();
            delay(2);
            digitalWrite(row+6, 1);
          }
          else{
            pulse();
          }
        }//col
      } //row
    }//draw
  }// ct
} // main loop

void pulse(){
  digitalWrite(2, HIGH);
  delay(del);            
  digitalWrite(2, LOW);  
  delay(del);  
  // 8 pulses for 8 rows
}
/* The reset function is called once during setup.
 It pulses the 4017 until it detects an output from
 pin 6, indicating that the count is at 8 and the next pulse
 will be column 1. */
void reset (){
  for (int i = 1; i<=9; i++){
    pulse();
    Serial.println(i);
    chk = digitalRead(sensorPin);
    Serial.println(chk);
    if (chk==1){   // 4017 pin 6 is high
      return; // No need to continue
    } // if
  } // for
} // function

The array is twice as wide as the matrix to allow a frame 'buffer'. There are probably better ways to manipulate the array to do this.