Table of Contents

Arduino IDE with AVR Dragon and ATtiny13

This page describes how to add the AVR Dragon as programmer to the Arduino IDE, compile for ATtiny13 and program as such the ATtiny13 directly from within the Arduino IDE.

Add ATTINY13 hardware package

As described here, copy paste the following url

https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json

Into Arduino IDE at: File → Preferences → Additional Boards Manager URLs:

Then click on OK.

Then select the newly added Board via: Tools → Board → Boards Manager…

Search for MicroCore and click on Install.

Now a new entry will be listed at: Tools → Board → MicroCore → ATtiny13

Select this controller.

Add AVR Dragon ISP

To make it possible to program directly with the AVR Dragon from within the Arduino IDE the ATtiny13, search for the following file:

$HOME/.arduino15/packages/MicroCore/hardware/avr/1.0.6/programmers.txt

And add the following lines at the bottom:

dragonisp.name=Dragon ISP
dragonisp.communication=usb
dragonisp.protocol=dragon_isp
dragonisp.program.protocol=dragon_isp
dragonisp.program.tool=avrdude
dragonisp.program.extra_params= -p attiny13 -c dragon_isp -P usb

Now the AVR Dragon can be selected via: Tools → Programmer → Dragon ISP

Getting started

Follow the link to the connection diagram at the official github page getting-started-with-microcore will show how to connect the ATtiny13 to the AVR Dragon.

Start with burning the bootloader from within arduino. (Tools → Burn Bootloader)

Then add following blink code:

void setup() {
 // initialize pin 4 (ATtiny leg 3) as an output.
 pinMode(4, OUTPUT);
}
void loop() {
 digitalWrite(4, HIGH);   // turn the LED on (HIGH is the voltage level)
 delay(1000);             // wait for a second
 digitalWrite(4, LOW);    // turn the LED off by making the voltage LOW
 delay(1000);             // wait for a second
} 

And compile, program and observe that pin 3 alternates between 0v and 5v in a 1s interval

/*
 * Output test
 */
 
/*
 * pinout:
 * https://camo.githubusercontent.com/b621cdf0625c6a9e961bb3ac5c94c46198f7585d/687474703a2f2f692e696d6775722e636f6d2f4a7362677550562e6a7067
 */
 
#define P1_PB5 5
#define P2_PB3 3
#define P3_PB4 4
#define P5_PB0 0
#define P6_PB1 1
#define P7_PB2 2
 
// the setup function runs once when you press reset or power the board
void setup() {
  pinMode(P2_PB3, OUTPUT);
  pinMode(P5_PB0, OUTPUT);
  pinMode(P6_PB1, OUTPUT);
  pinMode(P7_PB2, OUTPUT);
}
 
// the loop function runs over and over again forever
void loop() {
  for (int i=0; i <= 255; i++) {
    digitalWrite(P2_PB3, (i & 1) ? HIGH : LOW);
    digitalWrite(P5_PB0, (i & 2) ? HIGH : LOW);
    digitalWrite(P6_PB1, (i & 4) ? HIGH : LOW);
    digitalWrite(P7_PB2, (i & 8) ? HIGH : LOW);
    delay(500);
  }
}

Now you can easy verify if all pins are correctly controlled: Pin 2 should show every 0.5 second a change, Pin 5 should show every second, pin 6 every 2 seconds and pin 7 every 4 seconds.

Burst Controller

/**
 * ESD burst control
 * Controllersoftware for switching output pins in a sequence
 * based on a trigger from one input pin.
 * 
 * The output pins control 3 high voltage relays and one
 * trigger output for measurement purposes.
 * 
 * Input pin uses gpio and timer based interrupt to handle 
 * debouncing and signal edge processing. This simplifies
 * processing a great deal.
 * 
 * Hardware is based on the ATTINY13 8-bit microcontroller.
 * Development was done with Arduino IDE, extended with the
 * MicroCore, installed via: 
 * Arduino Preferences -> Additional Boards Manager URLs
 *   https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json
 * And then selected via Tools -> Board -> MicroCore -> ATtiny13
 * The device is programmed using ISP programming with the AVR Dragon.
 * From within Arduino the ATtiny13 can then be programmed with the Upload
 * button. The selected programmer is the DragonISP.
 * 
 * You need to edit the programmers.txt file in Arduino:
 *   $HOME/.arduino15/packages/MicroCore/hardware/avr/1.0.6/programmers.txt
 * 
 * See here for details:
 *   https://www.auditeon.com/projects:arduino_with_dragon_attiny13
 * 
 * For pinout, see following page:
 * https://github.com/MCUdude/MicroCore
 *
 * License: MIT
 * Author: Marc Nijdam 10-10-2020
 */
 
 
#define ARRAY_SIZE(x)  (sizeof(x) / sizeof(x[0]))
 
#define P1_PB5_NC 5
#define P2_PB3_TRIGOUT 3
#define P3_PB4_TOGGLESW 4
#define P5_PB0_CHARGE 0
#define P6_PB1_DSCHRG 1
#define P7_PB2_ALLOFF 2
 
#define STOP 255
 
/**
 * pin name and active state
 */
const struct {
  const uint8_t pin; // pin name
  const uint8_t das; // default active state
} io[] = {
  [0] = {P7_PB2_ALLOFF ,1}, // bit 0, active high
  [1] = {P2_PB3_TRIGOUT,1}, // bit 1, active high
  [2] = {P6_PB1_DSCHRG ,0}, // bit 2, active low
  [3] = {P5_PB0_CHARGE ,0}, // bit 3, active low
  [4] = {P1_PB5_NC ,1}      // reset pin
 
};
 
/**
 *  ____> charge -------------> (i.e. on high, switch closes)
 * | ___> discharge ----------> (i.e. on high, switch closes)
 * || __> trigout + pulldown -> (i.e. on high, trigger goes up.)
 * ||| _> all off ------------> (i.e. on high, it connects output to gnd)
 * ||||
 * 0000
 *
 * 0: not active 
 * 1: active
 */
const struct {
  const uint8_t bits; // bitpattern for gpio output pins
  const uint16_t dur; // duration in ms
} patterns[] = {
  {0b0000,  100}, /*  charge   */
  {0b0000,  100}, /*     !     */
  {0b1000,    0}, /*    \ /    */
  {STOP, 0},      /*   _____   */
  {0b0101,    0}, /*           */
  {0b0110, 5000}, /*    / \    */
  {0b0010,    0}, /*     !     */
  {0b0000,  500}, /*     !     */
  {0b0000,  100}, /* discharge */
};
 
/* counter which points to index of array */
volatile uint8_t processing;
volatile uint8_t pat_idx;
volatile uint8_t mode; // 0: charge, 1: discharge
 
void setup() {
  /* initialize digital in- and output pins */
  pinMode(P3_PB4_TOGGLESW, INPUT_PULLUP);
  for (uint8_t i=0; i < ARRAY_SIZE(io); i++) {
    pinMode(io[i].pin, OUTPUT);
  }
 
  mode = digitalRead(P3_PB4_TOGGLESW); // switch: 0=charging, 1=discharging
 
  /* initialize flag, 0: processing not initiated, 1: processing */
  processing = 0;
 
  /* enable interrupt from PB4 */
  setup_gpio_interrupt(P3_PB4_TOGGLESW);
  enable_pcint_interrupt();
 
  /* enable interrupt from timer for semi-automatic with 1x 0.12ms debounce */
  setup_timer_interrupt(1);
 
  /* enable all interrupts */
  sei();
}
 
// the loop function runs over and over again forever
void loop() {
  if (!processing) {
    processing = 1;
    process_sequence(mode);
  }
}
 
void process_sequence(uint8_t md) {
  pat_idx = md ? ARRAY_SIZE(patterns) - 1 : 0;
  for (;;) {
    set_bits(patterns[pat_idx].bits);
    /* wait ms duration, unless interrupted by switch */
    if (wait_ms(patterns[pat_idx].dur)) return;
    pat_idx += 1 - 2*md;
    if (patterns[pat_idx].bits == STOP) return;
  }
}
 
/**
 * Set bit pattern onto gpio output
 * take into account active state
 */
void set_bits(uint8_t bits) {
  for (uint8_t i=0; i < ARRAY_SIZE(io); i++) {
    digitalWrite(io[i].pin, !io[i].das ^ (( (1 << i) & bits) ? HIGH : LOW));
  }
}
 
uint8_t wait_ms(uint16_t d) {
  unsigned long ts = millis() + d;
  for (;;) {
    if (millis() >= ts) return 0;
    if (!processing) return 1;
    delay(1);
  }
}
 
void setup_gpio_interrupt(uint8_t port) {
  pinMode(port, INPUT_PULLUP);
  MCUCR |=  (1<<ISC00); // Trigger INT0 on rising edge
  MCUCR &= ~(1<<ISC01); // Trigger INT1 on rising edge
  PCMSK |= (1<<PCINT4); // pin change mask: listen to portb, pin PB4
}
 
/**
 * enable PCINT interrupt
 */
void enable_pcint_interrupt(void) {
  GIMSK |= (1<<PCIE);
}
 
/**
 * disable PCINT interrupt
 */
void disable_pcint_interrupt(void) {
  GIMSK &= ~(1<<PCIE);
}
 
/**
 * @brief setup timer interrupt
 * @param duration in unit of +/- 0.125ms
 */
void setup_timer_interrupt(uint8_t d) {
  TCCR0A |= _BV(WGM01); // set timer counter mode to CTC
  TCCR0B |= _BV(CS02)|_BV(CS00); // set prescaler to 1024 (CLK=1200000Hz/1024/256=4.57Hz, 0.22s)
  OCR0A = d; // set Timer's counter max value
}
 
void enable_timer_interrupt(void) {
  TIMSK0 |= _BV(OCIE0A); // enable Timer CTC interrupt
}
 
void disable_timer_interrupt(void) {
  TIMSK0 &= ~_BV(OCIE0A); // disable Timer CTC interrupt
}
 
ISR(PCINT0_vect) // Interrupt on Int0 vector
{
  disable_pcint_interrupt();
  /* prevent false interrupt from switch, only allow toggle */
  if (mode != digitalRead(P3_PB4_TOGGLESW)) {
    mode = digitalRead(P3_PB4_TOGGLESW);
    processing = 0;
  }
  /* use timer interrupt for debouncing */
  enable_timer_interrupt();  
}
 
ISR(TIM0_COMPA_vect) // Interrupt on Timer0 vector
{
  disable_timer_interrupt();
  enable_pcint_interrupt();
}