/** * DmxSimple - A simple interface to DMX. * * Copyright (c) 2008-2009 Peter Knight, Tinker.it! All rights reserved. */ // modified to support all Teensy boards #include #include #include #include "pins_arduino.h" #include "Arduino.h" #include "DmxSimple.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; #if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) // Teensy 4.X has 32-bit ports static volatile uint32_t *dmxPort; #else static volatile uint8_t *dmxPort; #endif 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__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) #define TIMER2_INTERRUPT_ENABLE() TIMSK2 |= _BV(TOIE2) #define TIMER2_INTERRUPT_DISABLE() TIMSK2 &= ~_BV(TOIE2) #define ISR_NAME TIMER2_OVF_vect #define BITS_PER_TIMER_TICK (F_CPU / 31372) // older ATMEGA8 has slighly different timer2 #elif defined(__AVR_ATmega8__) #define TIMER2_INTERRUPT_ENABLE() TIMSK |= _BV(TOIE2) #define TIMER2_INTERRUPT_DISABLE() TIMSK &= ~_BV(TOIE2) #define ISR_NAME TIMER2_OVF_vect #define BITS_PER_TIMER_TICK (F_CPU / 31372) // ATMEGA32U4 on Teensy and Leonardo has no timer2, but has timer4 #elif defined (__AVR_ATmega32U4__) #define TIMER2_INTERRUPT_ENABLE() TIMSK4 |= _BV(TOIE4) #define TIMER2_INTERRUPT_DISABLE() TIMSK4 &= ~_BV(TOIE4) #define ISR_NAME TIMER4_OVF_vect #define BITS_PER_TIMER_TICK (F_CPU / 31372) // Teensy 3.0 has IntervalTimer, unrelated to any PWM signals #elif defined(CORE_TEENSY) && defined(__arm__) #define TIMER2_INTERRUPT_ENABLE() #define TIMER2_INTERRUPT_DISABLE() #define ISR_NAME DMXinterrupt #define ISR(function, option) void function(void) #define BITS_PER_TIMER_TICK 500 void DMXinterrupt(void); IntervalTimer DMXtimer; #else #define TIMER2_INTERRUPT_ENABLE() #define TIMER2_INTERRUPT_DISABLE() #define ISR_NAME TIMER2_OVF_vect /* Produce an error (warnings to not print on Arduino's default settings) */ #error "DmxSimple does not support this CPU" #endif /** Initialise the DMX engine */ void dmxBegin() { dmxStarted = 1; // 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 #if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) TCCR2A = (1<>=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++) delayMicroseconds(8); *dmxPort |= dmxBit; delayMicroseconds(12); 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 DmxSimpleClass::usePin(uint8_t pin) { bool restartRequired = dmxStarted; if (restartRequired) dmxEnd(); dmxPin = pin; if (restartRequired) dmxBegin(); } /** Set DMX maximum channel * @param channel The highest DMX channel to use */ void DmxSimpleClass::maxChannel(int channel) { dmxMaxChannel(channel); } /** Write to a DMX channel * @param address DMX address in the range 1 - 512 */ void DmxSimpleClass::write(int address, uint8_t value) { dmxWrite(address, value); } DmxSimpleClass DmxSimple;