Category: reference

Material from other blogs that I reference here or in my PCFA material.

  • Scary George. (driving a floppy drive stepper with the Arduino/Wiring platform)

    I wanted to quickly demonstrate using a stepper motor from a floppy drive with the Arduino/Wiring platform as a follow up to the weekend’s workshop. By the time we got to the stepper parts I was a bit scattered. So here is an example that I set up yesterday.

    I have a talking George Bush doll from several years ago that was ripped in half (by people watching my punk band (w.busholini.org)) so I went ahead and finished dismantling it. For Halloween I thought his head should turn around Linda Blair style.

    I wired up a ULN2803 Darlington array and a floppy drive stepper motor from the workshop as shown in the diagram above .

    I figured out which wire was the common wire by taking an ohmmeter to the wires on the stepper. Most of the combinations were about 150 except from one of the wires on the end that read 75 ohms. Checking the 75 against all of the other wires I was able to determine that one of the wires was the common one and marked it with a sharpie.

    Then I ran up the arduino (v 17) example program for the stepper library. I modified it so that it just made steps in one direction. When I ran it the motion was really jittery so I checked to make sure that my wiring was good and then rather than rewiring the stepper physically I changed the line in the code from

    Stepper stepper(STEPS, 9, 10, 11, 12);

    to

    Stepper stepper(STEPS, 9, 11, 10,12);

    And Whah La! his head started spinning just like he was possessed by Dick Cheney! I wired the talk button to pin 8 and then added some random delays which gave me the following.

    #include <Stepper.h>
    
    // change this to the number of steps on your motor
    #define STEPS 100
    int relayPin=8;
    int ledPin=13;
    // create an instance of the stepper class, specifying
    // the number of steps of the motor and the pins it's
    // attached to
    Stepper stepper(STEPS, 9, 11, 10,12);
    
    // the previous reading from the analog input
    int previous = 0;
    
    void setup()
    { pinMode(relayPin,OUTPUT);
      pinMode(ledPin,OUTPUT);
      // set the speed of the motor to 30 RPMs
      stepper.setSpeed(100);
    }
    
    void loop()
    {
      // get the sensor value
      int val = analogRead(0);
    
      // move a number of steps equal to the change in the
      // sensor reading
      //stepper.step(val - previous);
    digitalWrite(ledPin, HIGH);
    stepper.step(random(5,90));
    delay(random(60,2000));
    digitalWrite(relayPin, HIGH);
    delay(20);
    digitalWrite(relayPin, LOW);
    digitalWrite(ledPin, LOW);
    stepper.step(-random(15,200));
    delay(random(90,3000));
      // remember the previous value of the sensor
      previous = val;
    }

  • 5 lines about 36 Blinking Lights.

    A while back I started thinking about a way to display stationary bike race results that didn’t require either a projector or a really complicated mechanical assembly. The thing that came to mind was a race tree like at the drag races. I asked Amanda who has been running bike events in portland if she could get me 12 lights. I wound up with pile of Bike Planet lights which required about a weeks worth of surgery.

    From one of our led driving discussions a few years back I wound up with a tube of 74hc595s which I wired up like so.

    While there are examples using bit banging to drive shift registers with the arduino they ignore the built in hardware capabilities of the AVR family.

    Using the built in spi greatly simplifies your code and is remarkably fast. In the code sample below there are 5 bytes which represent the 40 pins from 5 shift registers. The main loop just toggles the bits and sends them out the door.

    byte outbytes[5]={0x55,0x55,0x55,0x55,0x55};
    //uncomment this for a standard arduino
    //#define PIN_SCK          13             // SPI clock (also Arduino LED!)
    //#define PIN_MISO         12             // SPI data input
    //#define PIN_MOSI         11             // SPI data output#define PIN_HEARTBEAT     7           // added LED
    #define PIN_SCK          9           // SPI clock (also Arduino LED!)
    #define PIN_MISO         11            // SPI data input
    #define PIN_MOSI         10          // SPI data output
    #define PIN_SS         8             // SPI slave select
    
    void EnableSPI(void) {
    SPCR |= 1 << SPE;
    }
    
    void DisableSPI(void) {
    SPCR &= ~(1 << SPE);
    }
    
    byte SendRecSPI(byte Dbyte) {             // send one byte, get another in exchange
    SPDR = Dbyte;
    while (! (SPSR & (1 << SPIF))) {
    continue;
    }
    return SPDR;                             // SPIF will be cleared
    }
    
    void RunShiftRegister(void) {
    byte bitBucket;
    int i;
    digitalWrite(PIN_SS,HIGH);
    EnableSPI();                             // turn on the SPI hardware
    for (i=0; i<5; i++) {
    bitBucket = SendRecSPI(outbytes[i]);
    }
    DisableSPI();                             // return to manual control
    digitalWrite(PIN_SS,LOW);
    
    }
    
    void setup() {
    
    pinMode(PIN_SCK,OUTPUT);
    digitalWrite(PIN_SCK,LOW);
    pinMode(PIN_SS,OUTPUT);
    digitalWrite(PIN_SS,HIGH);
    pinMode(PIN_MOSI,OUTPUT);
    digitalWrite(PIN_MOSI,LOW);
    pinMode(PIN_MISO,INPUT);
    digitalWrite(PIN_MISO,HIGH);
    
    SPCR = B01110001;              // Auto SPI: no int, enable, LSB first, master, + edge, leading, f/16
    SPSR = B00000000;              // not double data rate
    
    }
    
    void loop(){
    int i;
    RunShiftRegister();
    for (i=0; i<5; i++){
    outbytes[i]= ~outbytes[i];
    }
    delay(1000);
    };

    Simple fast and easy.

  • The War On Christmas Lights


    War on Christmas Lights from Donald Delmar Davis on Vimeo.

    Last year I cast a series of led arrays for thing-a-day but hadnt wired them up. With this in mind I ordered some hc595s in the last group order but the snow canceled the meeting.

    I have been thinking about using the avr to drive the arrays directly and control them using i2c. I hacked 4 dorkboards by cutting the traces for pins 5-12 on the top of the board and soldering 220 ohm resistors on the other side. This gives a one chip solution which does not require a constant refresh or other attention.

    /*
     Simple 4 node array of leds (8x8).
     */
    #include <Wire.h>
    
    int node=0; /* CHANGE THIS BASED ON POSITION OF ARRAY */
    
    volatile int twi_group_offset=128;
    
    volatile int colpins[]={//12,11,10,9,8,7,6,5};
    5,6,7,8,9,10,11,12};
    volatile int rowpins[]={
    17,16,15,14,13,4,3,2};
    //2,3,4,13,14,15,16,17};
    
    volatile unsigned long int myTics;
    
    unsigned char pixels[]={0x3E, 0x51, 0x49, 0x45, 0x3E,0,0,node,
    0x00, 0x42, 0x7F, 0x40, 0x00,0,1,1,
    0x42, 0x61, 0x51, 0x49, 0x46,0,2,2,
    0x21, 0x41, 0x45, 0x4B, 0x31,0,3,3
    };
    
    void setup()                    // run once, when the sketch starts
    {
    
    	int i;
    	myTics=millis();
    	for(i=0;i<8;i++){
    		pinMode(rowpins[i],OUTPUT);
    		digitalWrite(rowpins[i],LOW);
    		pinMode(colpins[i],OUTPUT);
    		digitalWrite(colpins[i],HIGH);
    	}    // sets the digital pin as output
    	if (node==0) {
    		Wire.begin();
    		Serial.begin(19200);
    		updateSlaveNodes();
    	} else {
    		Wire.begin(node+twi_group_offset);
    		Wire.onReceive(updatePixels); // register event
    	}
    }
    
    void loop()                    //move most of this into a timer loop.
    {
    	int r,c;
    	int lastcolpin=colpins[0];
    	for (c=0;c<8;c++){
    		lastcolpin=colpins[c];
    		for(r=0;r<8;r++){
    			if ((pixels[c]>>r)&0x01){
    				//if ((!r)||((7-c)==r)||(c==r)){
    				digitalWrite(rowpins[r], HIGH);
    			} else {
    				digitalWrite(rowpins[r], LOW);
    			}
    		}
    		digitalWrite(colpins[c], LOW);
    		if (node==0) {
    			updateSlaveNodes();
    		} else {
    			//delayMicroseconds(1500); //
    			delay(3);
    		}
    		digitalWrite(lastcolpin, HIGH);
    	}
    }
    
    void updateSlaveNodes() {
    	int slavenode, row, pixel8;
    	pixel8=8;
    	for (slavenode=1;slavenode<4;slavenode++){
    		Wire.beginTransmission(slavenode+twi_group_offset);
    		for (row=0;row<8;row++) {
    			Wire.send(pixels[pixel8++]);
    		}
    		Wire.endTransmission();
    	};
    }
    
    // function that executes whenever data is received from master
    // this function is registered as an event, see setup()
    void updatePixels(int howMany)
    { int r=0;
    	while(Wire.available()) //
    	{
    		pixels[r++] = Wire.receive(); // receive byte as a character
    	}
    }


    The next step is to move the actual refresh into a timer routine so that the body of code for the main node can focus on content.

    /*------------------------------------------------------------ xmas timer array
     *
     * This is the program for a 4x8x8 led display using a processor for each segment
     * To program the different nodes set the node variable below. Node 0 is the
     * master node which keeps the pixel memory for the remaining nodes.
     *
     * The master node takes its memory and sends updates to each of the other segments
     * via the i2c buss using the Wire library.
     *
     * Each node takes its 8byte array of data and strobes it onto the display using
     * the 8 bit timer2 overflow interrupt.
     *
     * The first 7 characters are loaded into the display memory using the
     * loadCharacters function. The array is then shifted to the left in the main loop
     * with the next character being loaded a collumn at a time.
     *
     * The character data and the bitmap for the 5x7 array are stored in program
     * memory.
     *
     * Since interrupts are used all of the globals are marked as volatile.
     *
     * CopyLeft 2008 Donald Delmar Davis, Tempus Dictum, Inc.
     * This is free software (GPL)
     */
    #include <Wire.h>
    #include <avr/interrupt.h>
    #include <avr/io.h>
    #include <avr/pgmspace.h>
    
    #define FREEK 180
    
    int node=0; /* CHANGE THIS BASED ON POSITION OF ARRAY */
    
    unsigned char PROGMEM Font5x7[] = {
    0x00, 0x00, 0x00, 0x00, 0x00,// (space)
    0x00, 0x00, 0x5F, 0x00, 0x00,// !
    0x00, 0x07, 0x00, 0x07, 0x00,// "
    0x14, 0x7F, 0x14, 0x7F, 0x14,// #
    0x24, 0x2A, 0x7F, 0x2A, 0x12,// $
    0x23, 0x13, 0x08, 0x64, 0x62,// %
    0x36, 0x49, 0x55, 0x22, 0x50,// &
    0x00, 0x05, 0x03, 0x00, 0x00,// '
    0x00, 0x1C, 0x22, 0x41, 0x00,// (
    0x00, 0x41, 0x22, 0x1C, 0x00,// )
    0x08, 0x2A, 0x1C, 0x2A, 0x08,// *
    0x08, 0x08, 0x3E, 0x08, 0x08,// +
    0x00, 0x50, 0x30, 0x00, 0x00,// ,
    0x08, 0x08, 0x08, 0x08, 0x08,// -
    0x00, 0x60, 0x60, 0x00, 0x00,// .
    0x20, 0x10, 0x08, 0x04, 0x02,// /
    0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
    0x00, 0x42, 0x7F, 0x40, 0x00,// 1
    0x42, 0x61, 0x51, 0x49, 0x46,// 2
    0x21, 0x41, 0x45, 0x4B, 0x31,// 3
    0x18, 0x14, 0x12, 0x7F, 0x10,// 4
    0x27, 0x45, 0x45, 0x45, 0x39,// 5
    0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
    0x01, 0x71, 0x09, 0x05, 0x03,// 7
    0x36, 0x49, 0x49, 0x49, 0x36,// 8
    0x06, 0x49, 0x49, 0x29, 0x1E,// 9
    0x00, 0x36, 0x36, 0x00, 0x00,// :
    0x00, 0x56, 0x36, 0x00, 0x00,// ;
    0x00, 0x08, 0x14, 0x22, 0x41,// <
    0x14, 0x14, 0x14, 0x14, 0x14,// =
    0x41, 0x22, 0x14, 0x08, 0x00,// >
    0x02, 0x01, 0x51, 0x09, 0x06,//
    0x32, 0x49, 0x79, 0x41, 0x3E,// @
    0x7E, 0x11, 0x11, 0x11, 0x7E,// A
    0x7F, 0x49, 0x49, 0x49, 0x36,// B
    0x3E, 0x41, 0x41, 0x41, 0x22,// C
    0x7F, 0x41, 0x41, 0x22, 0x1C,// D
    0x7F, 0x49, 0x49, 0x49, 0x41,// E
    0x7F, 0x09, 0x09, 0x01, 0x01,// F
    0x3E, 0x41, 0x41, 0x51, 0x32,// G
    0x7F, 0x08, 0x08, 0x08, 0x7F,// H
    0x00, 0x41, 0x7F, 0x41, 0x00,// I
    0x20, 0x40, 0x41, 0x3F, 0x01,// J
    0x7F, 0x08, 0x14, 0x22, 0x41,// K
    0x7F, 0x40, 0x40, 0x40, 0x40,// L
    0x7F, 0x02, 0x04, 0x02, 0x7F,// M
    0x7F, 0x04, 0x08, 0x10, 0x7F,// N
    0x3E, 0x41, 0x41, 0x41, 0x3E,// O
    0x7F, 0x09, 0x09, 0x09, 0x06,// P
    0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
    0x7F, 0x09, 0x19, 0x29, 0x46,// R
    0x46, 0x49, 0x49, 0x49, 0x31,// S
    0x01, 0x01, 0x7F, 0x01, 0x01,// T
    0x3F, 0x40, 0x40, 0x40, 0x3F,// U
    0x1F, 0x20, 0x40, 0x20, 0x1F,// V
    0x7F, 0x20, 0x18, 0x20, 0x7F,// W
    0x63, 0x14, 0x08, 0x14, 0x63,// X
    0x03, 0x04, 0x78, 0x04, 0x03,// Y
    0x61, 0x51, 0x49, 0x45, 0x43,// Z
    0x00, 0x00, 0x7F, 0x41, 0x41,// [
    0x02, 0x04, 0x08, 0x10, 0x20,// "\"
    0x41, 0x41, 0x7F, 0x00, 0x00,// ]
    0x04, 0x02, 0x01, 0x02, 0x04,// ^
    0x40, 0x40, 0x40, 0x40, 0x40,// _
    0x00, 0x01, 0x02, 0x04, 0x00,// `
    0x20, 0x54, 0x54, 0x54, 0x78,// a
    0x7F, 0x48, 0x44, 0x44, 0x38,// b
    0x38, 0x44, 0x44, 0x44, 0x20,// c
    0x38, 0x44, 0x44, 0x48, 0x7F,// d
    0x38, 0x54, 0x54, 0x54, 0x18,// e
    0x08, 0x7E, 0x09, 0x01, 0x02,// f
    0x08, 0x14, 0x54, 0x54, 0x3C,// g
    0x7F, 0x08, 0x04, 0x04, 0x78,// h
    0x00, 0x44, 0x7D, 0x40, 0x00,// i
    0x20, 0x40, 0x44, 0x3D, 0x00,// j
    0x00, 0x7F, 0x10, 0x28, 0x44,// k
    0x00, 0x41, 0x7F, 0x40, 0x00,// l
    0x7C, 0x04, 0x18, 0x04, 0x78,// m
    0x7C, 0x08, 0x04, 0x04, 0x78,// n
    0x38, 0x44, 0x44, 0x44, 0x38,// o
    0x7C, 0x14, 0x14, 0x14, 0x08,// p
    0x08, 0x14, 0x14, 0x18, 0x7C,// q
    0x7C, 0x08, 0x04, 0x04, 0x08,// r
    0x48, 0x54, 0x54, 0x54, 0x20,// s
    0x04, 0x3F, 0x44, 0x40, 0x20,// t
    0x3C, 0x40, 0x40, 0x20, 0x7C,// u
    0x1C, 0x20, 0x40, 0x20, 0x1C,// v
    0x3C, 0x40, 0x30, 0x40, 0x3C,// w
    0x44, 0x28, 0x10, 0x28, 0x44,// x
    0x0C, 0x50, 0x50, 0x50, 0x3C,// y
    0x44, 0x64, 0x54, 0x4C, 0x44,// z
    0x00, 0x08, 0x36, 0x41, 0x00,// {
    0x00, 0x00, 0x7F, 0x00, 0x00,// |
    0x00, 0x41, 0x36, 0x08, 0x00,// }
    0x08, 0x08, 0x2A, 0x1C, 0x08,// ->
    0x08, 0x1C, 0x2A, 0x08, 0x08 // <-
    };
    
    unsigned char PROGMEM banner[] = "Happy Holidays!!! Merry Christmas!!! May every man with \"Merry Christmas\" on his lips be boiled in his own blood pudding with a steak of holly driven through his heart! Christmas! BAH! HUMBUG!!!";
    
    volatile int twi_group_offset=128;
    volatile int colpins[]={5,6,7,8,9,10,11,12};
    volatile int rowpins[]={17,16,15,14,13,4,3,2};
    
    volatile unsigned long int myTics; //counter for timer interrupt.
    
    volatile unsigned char pixels[]={
    0x3E, 0x51, 0x49, 0x45, 0x3E,0,0,node,
    0x00, 0x42, 0x7F, 0x40, 0x00,0,0,1,
    0x42, 0x61, 0x51, 0x49, 0x46,0,0,2,
    0x21, 0x41, 0x45, 0x4B, 0x31,0,0,3,
    0x00, 0x00, 0x00, 0x00, 0,0,0,0}; //pixel buffer rounded out to another 8 bytes
    
    volatile unsigned int cursorpos=0;
    volatile unsigned int charcursor=0;
    volatile unsigned int currentcol;
    volatile unsigned int charmod;
    volatile unsigned char currentchar;
    volatile unsigned int currentcharoffset;
    
    /*---------------------------------------------------------------------setup()
     * initialize timer2
     * setup the i/o pins (initialized with everything off);
     * Initialize the i2c buss;
     * if master node then load initial characters into display.
     */
    void setup()                    // run once, when the sketch starts
    {
    
        int i;
        currentcol=0;
    
        /*------------ setting up timer two. ----------------*/
        TCCR2A = 0;                           // normal mode
        TCCR2B = 1<<CS22 | 1<<CS21 | 0<<CS20; // clock selection
        TIMSK2 |= 1<<TOIE2;          // enable overflow interupt
        TCNT2=FREEK;                 // adjustment of period
        ASSR=0; // paranoid
        myTics=0;                    // counter for delays
        sei();                       // enable interrupts
    
       for(i=0;i<8;i++){
    		pinMode(rowpins[i],OUTPUT);
    		digitalWrite(rowpins[i],LOW);
    		pinMode(colpins[i],OUTPUT);
    		digitalWrite(colpins[i],HIGH);
        }
    
        if (node==0) {            // master node
    		Wire.begin();
    		Serial.begin(19200);
                    loadCharacters();
    		updateSlaveNodes();
        } else {
    		Wire.begin(node+twi_group_offset);
    		Wire.onReceive(updatePixels); // register event
        }
    }
    
    /*------------------------------------------------------------ISR(TIMER2_OVF_vect)
     * loads the next collumn into the led array
     *
     */
    ISR(TIMER2_OVF_vect) {
    int r;
      digitalWrite(colpins[currentcol], HIGH); //turn off the last column.
    
      if (++currentcol > 7) {
        currentcol=0;
      }
      for(r=0;r<8;r++){
    	if ((pixels[currentcol]>>r)&0x01){
    		digitalWrite(rowpins[r], HIGH);
    	} else {
    		digitalWrite(rowpins[r], LOW);
    	}
      }
      digitalWrite(colpins[currentcol], LOW);
      myTics++;
      TCNT2 = FREEK;
    
    
    };
    
    /*--------------------------------------------------------------loadCharacters()
     * load the initial characters into the pixel buffer
     */
    void loadCharacters(){
      int ch,i,j;
      cursorpos=0;
      for (charcursor=0;charcursor<7;charcursor++) {
        ch=pgm_read_byte_near(banner+charcursor);
        j= (ch-32) & 0x000000ff;
        j = j*5;
       for (charmod=0;charmod<5;charmod++){
          pixels[cursorpos++]=pgm_read_byte_near(Font5x7+j+charmod);
        }
      }
    
    }
    
    /*------------------------------------------------------------updateSlaveNodes()
     *  send the pixel data to the slave nodes.
     */
    
    void updateSlaveNodes() {
      int slavenode, row, pixel8;
    
      if (node==0) {
    	pixel8=8;
    	for (slavenode=1;slavenode<4;slavenode++){
    		Wire.beginTransmission(slavenode+twi_group_offset);
    		for (row=0;row<8;row++) {
    			Wire.send(pixels[pixel8++]);
    		}
    		Wire.endTransmission();
    	};
      }
    }
    /*------------------------------------------------------------------udatePixels()
     * recieve the pixel data from the master node.
     * executes whenever data is received from master
     * this function is registered as an event, see setup()
     */
    void updatePixels(int ignored)
    { int r=0;
    	while(Wire.available()) //
    	{
    		pixels[r++] = Wire.receive(); // receive byte as a character
    	}
    }
    
    /*-------------------------------------------------------------------------loop()
     *
     */
    void loop()
    {int c;
    
     //int head=pixels[0];
     updateSlaveNodes();
    
     for (c=1;c<35;c++) {
       pixels[c-1]=pixels[c];
     }
    
    
      if (charmod==5) {//get next char
        charcursor++;
        charmod=0;
        currentchar=pgm_read_byte_near(banner+charcursor);
        if (currentchar=='\0'){
           currentchar=pgm_read_byte_near(banner);
           charcursor=0;
        }
        currentcharoffset = (currentchar-32) & 0x000000ff;
        currentcharoffset = currentcharoffset*5;
      }
    
      pixels[34]=pgm_read_byte_near(Font5x7+currentcharoffset+charmod);
      charmod++;
    
     while (myTics<90) ; myTics=0L; //wait a few hundred millisecconds
    
    }
    
    
    


    http://www.dorkbotpdx.org/files/xmastimer-081225b.zip