Author Topic: DMX project  (Read 7471 times)

pstamand

  • Newbie
  • *
  • Posts: 4
DMX project
« on: October 06, 2013, 02:22:31 pm »
Hello,

Erik thanks for a great board!

I'm working on a project and hit a block.  I just dont yet understand enough about the arduino environment to figure it out.

Im working on a DMX lighting controller as a volunteer effort for a local youth theatre group.

Its a PS2 mouse based controller with a couple slide potentiometers.

Here's a little background on where I'm at:

breadboard
wiring
ps2 mouse
SN75176BP  (rs486 chip for the DMX output)

The DMX circuit, PS2 interface to the input pins, and the poteniometer are all working well.

Aside from smoothing the slide values which I think needs a good enough bit level even at the suggested 10K range, might still be some interference , the breadboard wiring loops might still be picking up noise.  All that I'll get sorted out.

I started out on a rev3 Arduino UNO and all the various parts worked well separately and tested.

I got to the point of working out all the PS2 init code and got all the X Y X movements and slider values to send solid value to Serial.print.  Aside from the smoothing that either 12 bits resolution will fix or better physical analog input.

At this point I'd tested the DMX layer by sending solid DMX data from the UNO to another DMX USB receiver and was seeing output.

Now I needed to put all the parts together...

loop() locks up so I decided it was time to grab the Digix and test the resolution at 12 bit... Helped!  Not perfect but much better...

but when I but all the code together and have the PS2 , analog sliders, serial.prints and DMX output all together, loop consistently locks up on the second loop.


At this point as many of you more experienced Arduino developers surely know....
None of the libraries that expected specific boards worked


I'm deep into trying to just force the library into the future  trying to learning enough about the various boards to help myself through __SAM3XA__  __AVR_ATmega...__ details to see if I can get the code to compile

Brute force to jump start what I suspect is going to be a long learning curve, but I'll get there...

I want to thank erik for a quick tip __SAM3XA__ the old libraries many arent Due aware

the DmxMaster library no only not being Due aware also blocks interrupts...  DMX timing is very critical and the library calls cli() sei() which werent defined with the board type set to DigiX,  I hopefully took care of that by simply changing code to NoInterrupt() and interrupt()


I had to commment out :

//#include <avr/io.h>
//#include <util/delay.h>

I'm still trying to find them.  I installed 1.5.4, so maybe they are only in the old version of the arduino IDE...


my C isnt that string but is going to get better by spring time,  I have a good time line , this volunteer project isnt urgent.
My electronics isn't strong, but good enough to get by and google my why through to making progress.

At the moment I have a controller and code issue.

I enabled the check boxes to show the compile and error details.  Not sre if there are others but this seems to output enough to post

I suspect __SAM3XA__ is going to need to make an appearance in the code along with what I'm going to have to learn about the chipset changes...


So, where do I begin?

Thanks,
Paul




C:\Program Files\Arduino/hardware/tools/g++_arm_none_eabi/bin/arm-none-eabi-g++ -c -g -Os -w -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -Dprintf=iprintf -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=154 -DARDUINO_SAM_DIGIX -DARDUINO_ARCH_SAM -D__SAM3X8E__ -mthumb -DUSB_PID=0x078A -DUSB_VID=0x16D0 -DUSBCON -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/libsam -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/CMSIS/CMSIS/Include/ -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/CMSIS/Device/ATMEL/ -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\cores\digix -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\variants\digix -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\PS2 -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster C:\DOCUME~1\RENSSE~1\LOCALS~1\Temp\build6414767374750799333.tmp\DMXFollowSpotController.cpp -o C:\DOCUME~1\RENSSE~1\LOCALS~1\Temp\build6414767374750799333.tmp\DMXFollowSpotController.cpp.o
C:\Program Files\Arduino/hardware/tools/g++_arm_none_eabi/bin/arm-none-eabi-g++ -c -g -Os -w -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -Dprintf=iprintf -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=154 -DARDUINO_SAM_DIGIX -DARDUINO_ARCH_SAM -D__SAM3X8E__ -mthumb -DUSB_PID=0x078A -DUSB_VID=0x16D0 -DUSBCON -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/libsam -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/CMSIS/CMSIS/Include/ -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/CMSIS/Device/ATMEL/ -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\cores\digix -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\variants\digix -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\PS2 -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\PS2\utility C:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\PS2\ps2.cpp -o C:\DOCUME~1\RENSSE~1\LOCALS~1\Temp\build6414767374750799333.tmp\PS2\ps2.cpp.o
C:\Program Files\Arduino/hardware/tools/g++_arm_none_eabi/bin/arm-none-eabi-g++ -c -g -Os -w -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -Dprintf=iprintf -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=154 -DARDUINO_SAM_DIGIX -DARDUINO_ARCH_SAM -D__SAM3X8E__ -mthumb -DUSB_PID=0x078A -DUSB_VID=0x16D0 -DUSBCON -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/libsam -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/CMSIS/CMSIS/Include/ -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\system/CMSIS/Device/ATMEL/ -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\cores\digix -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\hardware\ardunio\sam\variants\digix -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\PS2 -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster -IC:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster\utility C:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster\DmxMaster.cpp -o C:\DOCUME~1\RENSSE~1\LOCALS~1\Temp\build6414767374750799333.tmp\DmxMaster\DmxMaster.cpp.o
C:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster\DmxMaster.cpp:150: error: macro "ISR" passed 2 arguments, but takes just 1
C:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster\DmxMaster.cpp: In function 'void dmxBegin()':
C:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster\DmxMaster.cpp:64: error: cannot convert 'volatile RwReg*' to 'volatile uint8_t*' in assignment
C:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster\DmxMaster.cpp: At global scope:
C:\Documents and Settings\Rensselaer Student\My Documents\Arduino\libraries\DmxMaster\DmxMaster.cpp:152: error: expected constructor, destructor, or type conversion before '{' token


***DmxMaster.h***
/**
* DmxMaster - A simple interface to DMX.
*
* Copyright (c) 2008-2009 Peter Knight, Tinker.it! All rights reserved.
*/

#ifndef DmxMaster_h
#define DmxMaster_h

#include <inttypes.h>

#if RAMEND <= 0x4FF
#define DMX_SIZE 128
#else
#define DMX_SIZE 512
#endif

class DmxMasterClass
{
  public:
    void maxChannel(int);
    void write(int, uint8_t);
    void usePin(uint8_t);
};
extern DmxMasterClass DmxMaster;

#endif



***DmxMaster.cpp***
/**
* DmxMaster - A simple interface to DMX.
*
* Copyright (c) 2008-2009 Peter Knight, Tinker.it! All rights reserved.
*/
//#include <avr/io.h>
#include <avr/interrupt.h>
//#include <util/delay.h>
#include "pins_arduino.h"

#include "Arduino.h"
#include "DmxMaster.h"

/** dmxBuffer contains a software copy of all the DMX channels.
*/
volatile uint8_t dmxBuffer[DMX_SIZE];
static uint16_t dmxMax = 16; /* Default to sending the first 16 channels */
static uint8_t dmxStarted = 0;
static uint16_t dmxState = 0;

static volatile uint8_t *dmxPort;
static uint8_t dmxBit = 0;
static uint8_t dmxPin = 3; // Defaults to output on pin 3 to support Tinker.it! DMX shield

void dmxBegin();
void dmxEnd();
void dmxSendByte(volatile uint8_t);
void dmxWrite(int,uint8_t);
void dmxMaxChannel(int);

/* TIMER2 has a different register mapping on the ATmega8.
* The modern chips (168, 328P, 1280) use identical mappings.
*/
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega1280__)
#define TIMER2_INTERRUPT_ENABLE() TIMSK2 |= _BV(TOIE2)
#define TIMER2_INTERRUPT_DISABLE() TIMSK2 &= ~_BV(TOIE2)
#elif defined(__AVR_ATmega32U4__)
#define TIMER2_INTERRUPT_ENABLE() TIMSK3 |= _BV(TOIE3)
#define TIMER2_INTERRUPT_DISABLE() TIMSK3 &= ~_BV(TOIE3)
#elif defined(__AVR_ATmega8__)
#define TIMER2_INTERRUPT_ENABLE() TIMSK |= _BV(TOIE2)
#define TIMER2_INTERRUPT_DISABLE() TIMSK &= ~_BV(TOIE2)
#else
#define TIMER2_INTERRUPT_ENABLE()
#define TIMER2_INTERRUPT_DISABLE()
/* Produce an appropriate message to aid error reporting on nonstandard
* platforms such as Teensy.
*/
#warning "DmxMaster does not support this CPU"
#endif


/** Initialise the DMX engine
*/
void dmxBegin()
{
  dmxStarted = 1;
#ifdef __AVR_ATmega32U4__
  TCCR3A = _BV(WGM30);
  TCCR3B = _BV(CS31) | _BV(CS30);
#endif

  // Set up port pointers for interrupt routine
  dmxPort = portOutputRegister(digitalPinToPort(dmxPin));
  dmxBit = digitalPinToBitMask(dmxPin);
  // Set DMX pin to output
  pinMode(dmxPin,OUTPUT);

  // Initialise DMX frame interrupt
  //
  // Presume Arduino has already set Timer2 to 64 prescaler,
  // Phase correct PWM mode
  // So the overflow triggers every 64*510 clock cycles
  // Which is 510 DMX bit periods at 16MHz,
  // 255 DMX bit periods at 8MHz,
  // 637 DMX bit periods at 20MHz
  TIMER2_INTERRUPT_ENABLE();
}

/** Stop the DMX engine
* Turns off the DMX interrupt routine
*/
void dmxEnd()
{
  TIMER2_INTERRUPT_DISABLE();
  dmxStarted = 0;
  dmxMax = 0;
}

/** Transmit a complete DMX byte
* We have no serial port for DMX, so everything is timed using an exact
* number of instruction cycles.
*
* Really suggest you don't touch this function.
*/
void dmxSendByte(volatile uint8_t value)
{
  uint8_t bitCount, delCount;
  __asm__ volatile (
    "cli\n"
    "ld __tmp_reg__,%a[dmxPort]\n"
    "and __tmp_reg__,%[outMask]\n"
    "st %a[dmxPort],__tmp_reg__\n"
    "ldi %[bitCount],11\n" // 11 bit intervals per transmitted byte
    "rjmp bitLoop%=\n" // Delay 2 clock cycles.
  "bitLoop%=:\n"\
    "ldi %[delCount],%[delCountVal]\n"
  "delLoop%=:\n"
    "nop\n"
    "dec %[delCount]\n"
    "brne delLoop%=\n"
    "ld __tmp_reg__,%a[dmxPort]\n"
    "and __tmp_reg__,%[outMask]\n"
    "sec\n"
    "ror %[value]\n"
    "brcc sendzero%=\n"
    "or __tmp_reg__,%[outBit]\n"
  "sendzero%=:\n"
    "st %a[dmxPort],__tmp_reg__\n"
    "dec %[bitCount]\n"
    "brne bitLoop%=\n"
    "sei\n"
    :
      [bitCount] "=&d" (bitCount),
      [delCount] "=&d" (delCount)
    :
      [dmxPort] "e" (dmxPort),
      [outMask] "r" (~dmxBit),
      [outBit] "r" (dmxBit),
      [delCountVal] "M" (F_CPU/1000000-3),
      [value] "r" (value)
  );
}

/** DmxMaster interrupt routine
* Transmit a chunk of DMX signal every timer overflow event.
*
* The full DMX transmission takes too long, but some aspects of DMX timing
* are flexible. This routine chunks the DMX signal, only sending as much as
* it's time budget will allow.
*
* This interrupt routine runs with interrupts enabled most of the time.
* With extremely heavy interrupt loads, it could conceivably interrupt its
* own routine, so the TIMER2 interrupt is disabled for the duration of
* the service routine.
*/
#ifdef __AVR_ATmega32U4__
ISR(TIMER3_OVF_vect,ISR_NOBLOCK)
#else
ISR(TIMER2_OVF_vect,ISR_NOBLOCK)
#endif
{

  // Prevent this interrupt running recursively
  TIMER2_INTERRUPT_DISABLE();

  uint16_t bitsLeft = F_CPU / 31372; // DMX Bit periods per timer tick
  bitsLeft >>=2; // 25% CPU usage
  while (1) {
    if (dmxState == 0) {
      // Next thing to send is reset pulse and start code
      // which takes 35 bit periods
      uint8_t i;
      if (bitsLeft < 35) break;
      bitsLeft-=35;
      *dmxPort &= ~dmxBit;
      for (i=0; i<11; i++) _delay_us(8);
      *dmxPort |= dmxBit;
      _delay_us(8);
      dmxSendByte(0);
    } else {
      // Now send a channel which takes 11 bit periods
      if (bitsLeft < 11) break;
      bitsLeft-=11;
      dmxSendByte(dmxBuffer[dmxState-1]);
    }
    // Successfully completed that stage - move state machine forward
    dmxState++;
    if (dmxState > dmxMax) {
      dmxState = 0; // Send next frame
      break;
    }
  }
 
  // Enable interrupts for the next transmission chunk
  TIMER2_INTERRUPT_ENABLE();
}

void dmxWrite(int channel, uint8_t value) {
  if (!dmxStarted) dmxBegin();
  if ((channel > 0) && (channel <= DMX_SIZE)) {
    if (value<0) value=0;
    if (value>255) value=255;
    dmxMax = max((unsigned)channel, dmxMax);
    dmxBuffer[channel-1] = value;
  }
}

void dmxMaxChannel(int channel) {
  if (channel <=0) {
    // End DMX transmission
    dmxEnd();
    dmxMax = 0;
  } else {
    dmxMax = min(channel, DMX_SIZE);
    if (!dmxStarted) dmxBegin();
  }
}


/* C++ wrapper */


/** Set output pin
* @param pin Output digital pin to use
*/
void DmxMasterClass::usePin(uint8_t pin) {
  dmxPin = pin;
  if (dmxStarted && (pin != dmxPin)) {
    dmxEnd();
    dmxBegin();
  }
}

/** Set DMX maximum channel
* @param channel The highest DMX channel to use
*/
void DmxMasterClass::maxChannel(int channel) {
  dmxMaxChannel(channel);
}

/** Write to a DMX channel
* @param address DMX address in the range 1 - 512
*/
void DmxMasterClass::write(int address, uint8_t value)
{
dmxWrite(address, value);
}
DmxMasterClass DmxMaster;


pstamand

  • Newbie
  • *
  • Posts: 4
Re: DMX project
« Reply #1 on: October 06, 2013, 02:30:32 pm »
ignore this part...  DMX timing is very critical and the library calls cli() sei() which werent defined with the board type set to DigiX,  I hopefully took care of that by simply changing code to NoInterrupt() and interrupt()

Looking back over the post cli and sei are in the lib_dmx which wasnt any better so I switched back to DmxMaster.


Thanks,
Paul

pstamand

  • Newbie
  • *
  • Posts: 4
Re: DMX project
« Reply #2 on: October 06, 2013, 05:33:17 pm »
As I continue to read its become clear I've stepped fully in the ISR conflict issue with the HardwareSerial.

So how do I get debugging info and the DMX library?

If I have to build a test circuit and not use the builtin serial that's fine.  It will be a pain to work through but that will be progress.

I'm doing a lot of Serial.print to make sure all the analog inputs are operating correctly.

Is there a good reference anyone can point on similar conflicts?

Thanks,
Paul

« Last Edit: October 06, 2013, 05:35:41 pm by pstamand »

digistump

  • Administrator
  • Hero Member
  • *****
  • Posts: 1465
Re: DMX project
« Reply #3 on: October 06, 2013, 10:35:55 pm »
Serial.print doesn't tie up any hardware serial ports - because it is just a virtual USB serial


The following serial "ports" are present on the DigiX:
Serial (on the Due known as SerialUSB) - virtual USB CDC port
Serial0 (on the Due known as Serial) - port on pins 0 and 1
Serial1 - pins 14 and 15
Serial2 - pins 16 and 17
Serial3 - pins 18 and 19

pstamand

  • Newbie
  • *
  • Posts: 4
Re: DMX project
« Reply #4 on: October 07, 2013, 05:51:37 am »
As I look further I don't think its the serial port itself that's an issue.  I think its the ISR routines that conflict between HardwareSerial and the DMX library. 

I need to dig into the Hardware Serial code to see if I can spot similar code.

I picked up
Exploring Arduino: Tools and Techniques for Engineering Wizardry
by Jeremy Blum

which has a good section on interrupts and it explains a similar problem with the builtin Arduino HardwareSerial causing Interrupt issues with other libraries.