High Voltage Programmer

I’ve been experimenting with using non-standard pins on an ATtiny85. Using XTAL1 and XTAL2 is simple if you run off the internal oscillator at 8MHz/3.3v but I want to also use the RESET pin as D5/A0.

When you re-purpose the RESET pin, you lose the ability to program using an ISP, so we have to use a high voltage serial programmer (HVSP) which basically applies 12v to the RESET pin to set the fuses.

I redesigned the stripboard from here for use with perfboard and used a 2N2222 transistor as I have plenty of them, as well as a DC barrel jack for the 12v supply.

The hv_rescue.ino sketch which you load onto an Uno is as follows, I modified it slightly to compile nicely without needing the IDE:

// AVR High-voltage Serial Programmer
// Originally created by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
// Inspired by Jeff Keyzer http://mightyohm.com
// Serial Programming routines from ATtiny25/45/85 datasheet

// Desired fuse configuration
#define  HFUSE  0xDF   // Defaults for ATtiny25/45/85
#define  LFUSE  0x62

//#define  HFUSE  0x5F   // reset as gpio5
//#define  LFUSE  0xE2   // 8mhz internal oscillator

// For Attiny13 use
// #define HFUSE 0xFF
// #define LFUSE 0x6A  

#define  RST     13    // Output to level shifter for !RESET from transistor to Pin 1
#define  CLKOUT  12    // Connect to Serial Clock Input (SCI) Pin 2
#define  DATAIN  11    // Connect to Serial Data Output (SDO) Pin 7
#define  INSTOUT 10    // Connect to Serial Instruction Input (SII) Pin 6
#define  DATAOUT  9    // Connect to Serial Data Input (SDI) Pin 5 
#define  VCC      8    // Connect to VCC Pin 8

int inByte = 0;         // incoming serial byte Computer
int inData = 0;         // incoming serial byte AVR

void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("Enter a character to continue");   // send an initial string
    delay(1000);
  }
}

int shiftOut2(uint8_t dataPin, uint8_t dataPin1, uint8_t clockPin, uint8_t bitOrder, byte val, byte val1)
{
	int i;
        int inBits = 0;
        //Wait until DATAIN goes high
        while (!digitalRead(DATAIN));
        
        //Start bit
        digitalWrite(DATAOUT, LOW);
        digitalWrite(INSTOUT, LOW);
        digitalWrite(clockPin, HIGH);
  	digitalWrite(clockPin, LOW);
        
	for (i = 0; i < 8; i++)  {
                
		if (bitOrder == LSBFIRST) {
			digitalWrite(dataPin, !!(val & (1 << i)));
                        digitalWrite(dataPin1, !!(val1 & (1 << i)));
                }
		else {
			digitalWrite(dataPin, !!(val & (1 << (7 - i))));
                        digitalWrite(dataPin1, !!(val1 & (1 << (7 - i))));
                }
                inBits <<=1;
                inBits |= digitalRead(DATAIN);
                digitalWrite(clockPin, HIGH);
		digitalWrite(clockPin, LOW);
                
	}

        
        //End bits
        digitalWrite(DATAOUT, LOW);
        digitalWrite(INSTOUT, LOW);
        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);
        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);
        
        return inBits;
}

void readFuses(){
     //Read lfuse
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x68);
    inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6C);
    Serial.print("lfuse reads as ");
    Serial.println(inData, HEX);
    
    //Read hfuse
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7A);
    inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7E);
    Serial.print("hfuse reads as ");
    Serial.println(inData, HEX);
    
    //Read efuse
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6A);
    inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6E);
    Serial.print("efuse reads as ");
    Serial.println(inData, HEX);
    Serial.println(); 
}

void setup()
{
  // Set up control lines for HV parallel programming
  pinMode(VCC, OUTPUT);
  pinMode(RST, OUTPUT);
  pinMode(DATAOUT, OUTPUT);
  pinMode(INSTOUT, OUTPUT);
  pinMode(CLKOUT, OUTPUT);
  pinMode(DATAIN, OUTPUT);  // configured as input when in programming mode
  
  // Initialize output pins as needed
  digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
  
  // start serial port at 9600 bps:
  Serial.begin(9600);
  
  establishContact();  // send a byte to establish contact until receiver responds 
  
}

void loop()
{
  // if we get a valid byte, run:
  if (Serial.available() > 0) {
    // get incoming byte:
    inByte = Serial.read();
    Serial.println(byte(inByte));
    Serial.println("Entering programming Mode\n");

    // Initialize pins to enter programming mode
    pinMode(DATAIN, OUTPUT);  //Temporary
    digitalWrite(DATAOUT, LOW);
    digitalWrite(INSTOUT, LOW);
    digitalWrite(DATAIN, LOW);
    digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
    
    // Enter High-voltage Serial programming mode
    digitalWrite(VCC, HIGH);  // Apply VCC to start programming process
    delayMicroseconds(20);
    digitalWrite(RST, LOW);   //Turn on 12v
    delayMicroseconds(10);
    pinMode(DATAIN, INPUT);   //Release DATAIN
    delayMicroseconds(300);
    
    //Programming mode
    
    readFuses();
    
    //Write hfuse
    Serial.println("Writing hfuse");
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x40, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, HFUSE, 0x2C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x74);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7C);
    
    //Write lfuse
    Serial.println("Writing lfuse\n");
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x40, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, LFUSE, 0x2C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x64);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6C);

    readFuses();    
    
    Serial.println("Exiting programming Mode\n");
    digitalWrite(CLKOUT, LOW);
    digitalWrite(VCC, LOW);
    digitalWrite(RST, HIGH);   //Turn off 12v
  }
}

I then uploaded the following sketch to the ATtiny85 using make ispload, as you can see its using D5 to blink an LED:

const int led = 5;

void setup()
{
    pinMode(led, OUTPUT);
}

void loop()
{
    digitalWrite(led, HIGH);
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
}

The Makefile is as follows, I used the latest IDE and the ATTinyCore as it supports more chips and I wanted to experiment with something other than the usual arduino-tiny or attiny-master with IDE 1.0.5:

ARDUINO_DIR = $(HOME)/arduino-1.8.5

ISP_PROG       = usbasp
AVRDUDE_OPTS   = -v
ALTERNATE_CORE = ATTinyCore
BOARD_TAG      = attinyx5
BOARD_SUB      = 85
F_CPU          = 8000000L
ISP_HIGH_FUSE  = 0x5F
ISP_LOW_FUSE   = 0xE2

include $(HOME)/Arduino-Makefile/Arduino.mk

The important bit is to then edit the hv_rescue.ino file to set the fuses:

#define  HFUSE  0x5F   // reset as gpio5
#define  LFUSE  0xE2   // 8mhz internal oscillator

Then upload that to the Uno, connect the HVSP board with the ATtiny85 mounted, and press a key to set the fuses. If you don’t edit the script it’ll just reset the fuses to their defaults of 0xDF and 0x62.

The order is important, as you can’t ISP the chip after you’ve set the fuse to disable RESET, so you can’t use make set_fuses for example, you have to use the HVSP to set the fuses, after ISP’ing the sketch. If you want to edit the sketch, you’ll have to reset the fuses, then ISP the sketch and set the fuses back again, so its not something you want to do often – maybe develop using a regular GPIO pin like D2, then when you’re ready switch to D5.

I made a PR to fix that – so you make ispload to upload the sketch, then make set_fuses to set the fuses, after which you need to reset them using the HVSP.

You can then blink six LED’s using an ATTiny85 (the RESET pin has lower power output) with just VCC and GND connected (no crystal etc.) using for example:

#include <avr/io.h>

void setup()
{
    // set d0-d5 as outputs
    DDRB = B11111111;
}

void loop()
{
    // set all high
    PORTB = B11111111;
    delay(500);

    // invert all
    PORTB ^= PORTB;
    delay(500);
}

There are other more complicated sketches around which can auto-detect the chip and prompt for the fuses to set, but they seem to require a button and a 12v DC-DC boost converter, as per the Rescue Shield

LED Strip Controller Redux

I’ve revisited my ATtiny85-based LED strip driver project, and finally got around to soldering it onto veroboard, added a proper switching regulator instead of the awful 7805/capacitor setup, and screw terminals to attach the strip which is much more secure than just pin headers.

It pulls 100mA @ 12v most of the time, topping out at 200mA when on full white constantly, not bad for over a foot of LED strip that I’m testing with.

Tiny LED Strip Driver

I ordered a 5m strip of 5050 RGB analog LED’s – not the digital individually addressable ones, just the ones that the whole strip changes at once.

So I’ve combined my ATtiny85 plus ULN2803 project with my RGB LED project and came up with a tiny LED driver.

Essentially we have a 12V power supply that is tapped off into a 7805 to provide 5V to the ATtiny85 and ULN2803, and the raw 12V powers the LED strip:

I may add something like a trimpot or button to vary the colour/speed/mode later – for instance have it fixed at red, or green or blue, or fade.

I could have used N-channel MOSFETs or NPN transistors to control the LED strip as described here, but the ULN2803 is simpler, tidier and can cope with at least a metre of LED’s, who wants to use discretes?!

The code is unmodified from the RGB LED project, due to the way the ULN2803 and LED strips work, you can just move a couple of wires around and its done. I used the following Makefile to program the ATtiny85 using my usbasp:

ISP_PROG     	   = usbasp
BOARD_TAG          = attiny85at8
ALTERNATE_CORE     = arduino-tiny
ARDUINO_VAR_PATH   = ~/sketches/hardware/arduino-tiny/cores/tiny
ARDUINO_CORE_PATH  = ~/sketches/hardware/arduino-tiny/cores/tiny
AVRDUDE_OPTS       = -v

include /usr/share/arduino/Arduino.mk

I’ve also ordered some 5v/16MHz Sparkfun Pro Micro‘s to add a Boblight setup to my XBMC box.

The Pro Micro’s can be powered over USB and communicate with boblightd over USB (serial) too, so I can just make a breakout board to hold some transistors, take the 5v supply from the Vcc pin on the Pro Micro, and hold a DC barrel jack to feed 12v to the LED strips from a mains adaptor – no need to split the mains feed via a 7805 or UBEC.

It also uses the same ATmega32u4 chip as the Leonardo, so I might finally be able to debug/rewrite the reset code for arduino-mk, probably ditching the perl and going back to python but only using one script instead of 2-3 like before.

P.S. my weather station mk2 lasted a month on 2x AA eneloop batteries, a big improvement on the week I was getting before I added the P-channel MOSFET to cut the power to the peripherals.

Update: I added a pushbutton between pin 2 (D3) and GND to select the mode, and added some static colour/fade/flash functionality to the script:

// init vars
const int REDPIN = 1;   // pin 6
const int GREENPIN = 0; // pin 5
const int BLUEPIN = 4;  // pin 3
const int BUTTON = 3;   // pin 2
int red;
int green;
int blue;
int current_red;
int current_green;
int current_blue;
int previous = LOW;
int counter = 0;
unsigned long last_time = 0;

void doFade(int pin, int &val, int &current_val)
{
    // fade between random colours
    if (val > current_val)
    {
        current_val++;
        analogWrite(pin, current_val);
        delay(10);
    }
    else if (val < current_val)
    {
        current_val--;
        analogWrite(pin, current_val);
        delay(10);
    }
    else
    {
        analogWrite(pin, current_val);
        val = random(255);
    }
}

void fixedColour(int red, int green, int blue)
{
    // set individual leds
    analogWrite(REDPIN, red);
    analogWrite(GREENPIN, green);
    analogWrite(BLUEPIN, blue);
}

void setup()
{
    // set button to input with built-in pull-up
    pinMode(BUTTON, INPUT_PULLUP);

    // setup pin directions
    pinMode(REDPIN, OUTPUT);
    pinMode(GREENPIN, OUTPUT);
    pinMode(BLUEPIN, OUTPUT);

    // read from unconnected pin for entropy
    randomSeed(analogRead(2));
}

void loop()
{
    // take a reading
    int reading = digitalRead(BUTTON);

    // changed state - debounce=200ms
    if (reading == LOW && previous == HIGH && (millis() - last_time > 200))
    {
        // increment counter
        counter++;
        
        // store the time the button was pressed
        last_time = millis();
    }
    
    // store previous reading
    previous = reading;
        
    switch (counter)
    {
        case 0:
            // fade by default
            doFade(REDPIN, red, current_red);
            doFade(GREENPIN, green, current_green);
            doFade(BLUEPIN, blue, current_blue);
            break;
        case 1:
            // red
            fixedColour(255,0,0);
            break;
        case 2:
            // green
            fixedColour(0,255,0);
            break;
        case 3:
            // blue
            fixedColour(0,0,255);
            break;
        case 4:
            // cyan
            fixedColour(0,255,255);
            break;
        case 5:
            // magenta
            fixedColour(255,0,255);
            break;
        case 6:
            // yellow
            fixedColour(255,255,0);
            break;
        case 7:
            // white
            fixedColour(255,255,255);
            break;
        case 8:
            // flash randomly twice a second
            fixedColour(random(255),random(255),random(255));
            delay(500);
            break;
        case 9:
            // reset to fade
            counter = 0;
            break;
    }
}

ATtiny85 driving a ULN2803A

I’ve added a Darlington Array to my gearshift project, so that eventually it will drive 3 relays which in turn will drive 3 solenoids to change gear on the motorbike.

I love the way the ULN2803A works as its so simple – the inputs on the left go to the same outputs on the right, no code, no SPI, just feed it the outputs from the ATtiny.

If you look at the breadboard layout or video you can see that’s I’ve also added a 7805 circuit to bring the 12V bike battery or 9V battery I’m using currently down to 5V for the Arduino and relays. Update: now moved to veroboard: video 2 and veroboard layout.

The code below basically counts up a gear each time the (debounced) button is pressed and stops at 3 and will not do any higher or lower no matter how much you press it. If you hold down the button for 2 seconds it goes back to 0 or neutral. At the moment the 3 LED’s are showing how it works, but will eventually be replaced by relays.

// init vars
const int output1 = 2; // pin7
const int output2 = 1; // pin6
const int output3 = 0; // pin5
const int button  = 3; // pin2
int previous = 0;
int counter = 0;
unsigned long last_time = 0;

void changeGear(int one, int two, int three)
{
    // light led and solenoid
    digitalWrite(output1, one);
    digitalWrite(output2, two);
    digitalWrite(output3, three);
}

void setup()
{
    // set button to input with built-in pull-up
    pinMode(button, INPUT_PULLUP);

    // set led/solenoid pins to outputs
    pinMode(output1, OUTPUT);
    pinMode(output2, OUTPUT);
    pinMode(output3, OUTPUT);
}

void loop()
{
    // take a reading
    int reading = digitalRead(button);

    // changed state - debounce=250ms
    if (reading == LOW && previous == HIGH && millis() - last_time > 250)
    {
        // increment counter
        counter++;

        switch (counter)
        {
            case 1:
                changeGear(1,0,0);
                break;
            case 2:
                changeGear(0,1,0);
                break;
            case 3:
                changeGear(0,0,1);
                break;
        }

        // store the time the button was pressed
        last_time = millis();
    }

    // being held down in 3rd gear - holdtime=3s
    if (reading == LOW && previous == LOW)
    {
        if (millis() - last_time > 3000 && counter >= 3)
        {
            counter = 0;
            changeGear(0,0,0);
        }
    }

    // store previous reading
    previous = reading;
}

RGB LED With ATtiny85 Part 2

I’ve updated my script and removed the pot from my design, so that now it just fades between random colours on its own, using an LDR to tell if its dark enough to turn on or so light that it should turn itself off as you wouldn’t see the LED anyway:

// init vars
const int REDPIN = 1; // 85 leg 6
const int GREENPIN = 0; // 85 leg 5
const int BLUEPIN = 4; // 85 leg 3
const int LDR = 3; // 85 leg 2
const int THRESHOLD = 200;
int last_time;
int red;
int green;
int blue;
int current_red;
int current_green;
int current_blue;

void doFade(int pin, int &val, int &current_val)
{
    // val and current_val are passed as references
    // so modifying them will actually modify e.g.
    // red and current_red without having to return them
    if (val > current_val)
    {
        current_val++;
        analogWrite(pin, current_val);
        delay(10);
    }
    else if (val < current_val)
    {
        current_val--;
        analogWrite(pin, current_val);
        delay(10);
    }
    else
    {
        analogWrite(pin, current_val);
        val = random(255);
    }
}

void setup()
{
    // read from unconnected pin for entropy
    randomSeed(analogRead(2));

    // setup pin directions
    pinMode(REDPIN, OUTPUT);
    pinMode(GREENPIN, OUTPUT);
    pinMode(BLUEPIN, OUTPUT);
    pinMode(LDR, INPUT);
}

void loop()
{
    // fetch current uptime
    int this_time = millis();

    // is ldr reading higher (brighter) than our threshold?
    if (analogRead(LDR) > THRESHOLD)
    {
        // is it more than 5secs since it was last dark?
        if (this_time - last_time > 5000)
        {
            // turn off the led as its not dark
            analogWrite(REDPIN, 0);
            analogWrite(GREENPIN, 0);
            analogWrite(BLUEPIN, 0);
        }
    }
    else
    {
        // call fade function for each colour
        doFade(REDPIN, red, current_red);
        doFade(GREENPIN, green, current_green);
        doFade(BLUEPIN, blue, current_blue);
        
        // reset the counter
        last_time = this_time;
    }
}

I’ve soldered the final design onto veroboard after prototyping on breadboard:

I’ve contributed a few patches to arduino-mk and now you can compile for the ATtiny85 using a Makefile, however it doesn’t support the arduino-tiny core or tiny-core2 only the attiny (HLT) one, so I couldn’t use it for this as I needed the 3 PWM pins which is not supported by attiny.

Update: I seem to have made it work:

ARDMK_DIR          = /usr/share/arduino
ARDMK_PATH         = /usr/bin
AVR_TOOLS_DIR      = /usr
ISP_PORT           = /dev/ttyACM0
BOARD_TAG          = attiny85at8
ALTERNATE_CORE     = arduino-tiny
ARDUINO_VAR_PATH   = /home/user/arduino/hardware/arduino-tiny/cores/tiny
ARDUINO_CORE_PATH  = /home/user/arduino/hardware/arduino-tiny/cores/tiny

include /usr/share/arduino/Arduino.mk