Differential DMX output from OLIMEXINO85 (ATTiny85)

Hi there,

I got one of these cracking little boards the other day…
http://www.ebay.co.uk/itm/271422174902?ssPageName=STRK:MEWNX:IT&_trksid=p3984.m1439.l2649
… and downloaded the arduino IDE adaption for it, it ran perfectly first time

THIS IS SUCH AN EXCELLENT (and cheap) ‘MUST-HAVE’ BIT OF KIT !!!

I instantly started to write/adapt some software to run on it and see what it can do, so I wrote/adapted a DMX driver I had so that it will create differential output. This means you can connect the two outputs DIRECTLY to a DMX receiving light and it should run (therefore NO Max485 or SN76175 RS485 interface chip required). I only tested this one one DMX node so I dont know the AtTiny’s current capabilities for driving several nodes at length.

I tested this as part of a bigger program and it works just fine.
If you use a SPI programmed ATtiny45/85 instead you may need to adjust the timings for 4us

// OLIMEXINO85 ATTINY85 + micronucleus bootloader - 16mhz version only
// Differential DMX Transmitter
//
// Digispark/Arduino IDE needs to be set to the following
// Tools >> Board >> Digispark 16mhz (NO USB) tiny core
// Tools >> programmer >> Digispark
//
// OLIMEXINO must be programmed with nothing else connected or the USB bootloader will fail

#include <avr/pgmspace.h>
#include <WProgram.h>
#include "pins_arduino.h";

#define DMX_OUT_POS 2 // pin 6
#define DMX_OUT_NEG 1 // pin 5

// ATTINY45/85 pinout //
//
//[RESET]     RESET     1[     ]8  VCC
//            ADC3 PB3<<2[     ]7>>PB2 [SCK]  (DMX OUT +) 
//            ADC2 PB4>>3[     ]6>>PB1 [MISO] (DMX OUT -) 
// GND        GND       4[     ]5>>PBO [MOSI] 
//

unsigned long dmx_monitor; 
int port_to_output[] = { NOT_A_PORT, NOT_A_PORT, _SFR_IO_ADDR(PORTB) };
volatile uint8_t *portNumberPOS, *portNumberNEG;
uint8_t pinMaskPOS, pinMaskNEG, dmx_count;

void setup()
{
  pinMode(DMX_OUT_POS,OUTPUT);
  digitalWrite(DMX_OUT_POS, HIGH); // normal output is high for a zero
  pinMode(DMX_OUT_NEG,OUTPUT);
  digitalWrite(DMX_OUT_POS, LOW);
  portNumberPOS = portOutputRegister(digitalPinToPort(DMX_OUT_POS));
  pinMaskPOS = digitalPinToBitMask(DMX_OUT_POS);
  portNumberNEG = portOutputRegister(digitalPinToPort(DMX_OUT_NEG));
  pinMaskNEG = digitalPinToBitMask(DMX_OUT_NEG);
}

void loop()
{
  if (millis() - dmx_monitor > 100) { 
    dmx_monitor = millis(); 
    dump_dmx(); 
  } // every 50ms (@16mhz) send dmx + inc value
}

void dump_dmx(void) {
  cli(); 
  *portNumberPOS &= ~pinMaskPOS; // LOW 
  *portNumberNEG |= pinMaskNEG;  // high
  delayMicroseconds(200); // 100us break (92us min)   
  *portNumberPOS |= pinMaskPOS; // HIGH 
  *portNumberNEG &= ~pinMaskNEG;  // low
  fourUSdelay16mhz(); // 12us mark after break 
  fourUSdelay16mhz(); 
  fourUSdelay16mhz(); 
  shiftDmxOutDiff(0); // send zero as first byte    
  dmx_count++;
  for (uint8_t i=0; i<20; i++) {
    shiftDmxOutDiff(dmx_count);
    shiftDmxOutDiff(255-dmx_count);
    shiftDmxOutDiff((dmx_count + 128) % 256);
  }    
  sei();  
}

void shiftDmxOutDiff(int data){
  *portNumberPOS &= ~pinMaskPOS; //  LOW
  *portNumberNEG |= pinMaskNEG;  // high
  fourUSdelay16mhz(); // start bit LOW 4us
  for (uint8_t e=0; e<8; e++){
    if (data & 01){                 
      *portNumberPOS |= pinMaskPOS; // HIGH
      *portNumberNEG &= ~pinMaskNEG; // low
    }
    else { 
      *portNumberPOS &= ~pinMaskPOS; // LOW
      *portNumberNEG |= pinMaskNEG; // high
    }
    fourUSdelay16mhz(); // 8 data bits 4us each
    data >>= 1;
  }
  *portNumberPOS |= pinMaskPOS; // HIGH
  *portNumberNEG &= ~pinMaskNEG; // low
    fourUSdelay16mhz(); // 2 stop bits 4us each
    fourUSdelay16mhz();
// enter idle with output HIGH, ready for next BREAK(low)
}

void fourUSdelay16mhz(void) { 
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n"); 
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n");
}

Regards Bob

Hello Bob!

Hey there Jonny ! glad you found the forum !! :-)))

I have tried to change this code so that it runs off an ISR every 4usec,
I was guided to Gammon Forum : Electronics : Microprocessors : Interrupts which basically tells me that calling an ISR takes 42 clock cycles, and 4usec is 44 clock cycles, that leaves 2 clock cycles to do any work !!!