Differential DMX output from OLIMEXINO85 (ATTiny85)

Started by BobMcNobby, June 08, 2014, 10:08:35 AM

Previous topic - Next topic

BobMcNobby

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");
}

voyager

Hi Bob,
sounds great. Wonderful cute little board, At what Baud rate are you transferring? Would it be possible to also Receive data @ 250kBaud (8Bit,N,1)? As you seem to have a lot of experience with DMX could you give me some help? I would like to receive some serial data and display them.
Thanks
Voy

BobMcNobby

Hi there, I am transferring at 250kbps, unfortunately these little chips dont have a UART/USART, the nearest thing is a USI, but thats more like SPI, absolutely no use for this application. You probably WONTbe able to receive DMX as you would need to be processing bits manually as they arrive at 4us, that would leave very little headroom for other tasks even at 16mhz

BobMcNobby

I have now written a DMX receiver for the ATTiny85, the micro doesnt have a UART so I have had to do it by bit-sniffing.. I have also incorporated a 3 channel PWM (for RGB) which doesnt use the PWM outputs of the micro, it is done manually WITHOUT using any interrupts (which would cause glitches in the DMX receiver

So far the code is just over 900 bytes, so I am going to try and squeeze it into a 1k ATTiny13 !!


BobMcNobby

I shall have a modified source of the DMX transmitter available soon as the one I posted above is not as accurate on timing as first thought.

The DMX receiver I have now adjusted for atTiny13 (<1k) and includes a drier for WS2812 IC leds, a RX_status led and up/down DMX channel change controls. unfortunately I wont be posting the code for this as it has taken me alot of tie to get right and I will be using it for commercial purposes, but I may write a simplified version.

DMX reception @ 9.6mhz was very very difficult, you only get 4us to deal with each bit. Synching and timing breaks and monitoring start/stop bits can be tricky without a UART, but my software can deal with all this at full speed, and pick out any channel in the range and display RGB onto 4 x WS2812 leds, I am very happy with it