diff --git a/DmxSimplePlus.cpp b/DmxSimplePlus.cpp deleted file mode 100644 index 403c12d..0000000 --- a/DmxSimplePlus.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/** - * 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 "DmxSimplePlus.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__) || 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 << WGM20); - TCCR2B = (1 << CS22); // 64 prescaler -#elif defined(__AVR_ATmega32U4__) - TCCR4B = (1 << CS42) | (1 << CS41) | (1 << CS40); -#elif defined(CORE_TEENSY) && defined(__arm__) - DMXtimer.begin(DMXinterrupt, 2000); -#endif - TIMER2_INTERRUPT_ENABLE(); -} - -/** Stop the DMX engine - * Turns off the DMX interrupt routine - */ -void dmxEnd() { - TIMER2_INTERRUPT_DISABLE(); -#if defined(CORE_TEENSY) && defined(__arm__) - DMXtimer.end(); -#endif - 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. - */ -#if defined(__AVR__) -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)); -} -#elif defined(__arm__) -void dmxSendByte(uint8_t value) { - uint32_t begin, target; - uint8_t mask; - - noInterrupts(); - ARM_DEMCR |= ARM_DEMCR_TRCENA; - ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; - begin = ARM_DWT_CYCCNT; - *dmxPort = 0; - target = F_CPU / 250000; - while (ARM_DWT_CYCCNT - begin < target) - ; // wait, start bit - for (mask = 1; mask; mask <<= 1) { - *dmxPort = (value & mask) ? 1 : 0; - target += (F_CPU / 250000); - while (ARM_DWT_CYCCNT - begin < target) - ; // wait, data bits - } - *dmxPort = 1; - target += (F_CPU / 125000); - while (ARM_DWT_CYCCNT - begin < target) - ; // wait, 2 stops bits - interrupts(); -} -#endif - -/** DmxSimple 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. - */ -ISR(ISR_NAME, ISR_NOBLOCK) { - - // Prevent this interrupt running recursively - TIMER2_INTERRUPT_DISABLE(); - - uint16_t bitsLeft = BITS_PER_TIMER_TICK; // 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++) 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 DmxSimplePlusClass::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 DmxSimplePlusClass::maxChannel(int channel) { - dmxMaxChannel(channel); -} - -/** Write to a DMX channel - * @param address DMX address in the range 1 - 512 - */ -void DmxSimplePlusClass::write(int address, uint8_t value) { - dmxWrite(address, value); -} - -uint8_t DmxSimplePlusClass::read(int channel) { - if (channel > 0 && channel <= dmxMax) - return (dmxBuffer[channel]); - else - return (0); -} -DmxSimplePlusClass DmxSimple; diff --git a/DmxSimplePlus.h b/DmxSimplePlus.h deleted file mode 100644 index deea267..0000000 --- a/DmxSimplePlus.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * DmxSimple - A simple interface to DMX. - * - * Copyright (c) 2008-2009 Peter Knight, Tinker.it! All rights reserved. - */ - -#pragma once - -#include - -#if RAMEND <= 0x4FF -#define DMX_SIZE 128 -#else -#define DMX_SIZE 512 -#endif - -class DmxSimplePlusClass { -public: - void maxChannel(int); - void write(int, uint8_t); - void usePin(uint8_t); - uint8_t read(int channel); -}; -extern DmxSimplePlusClass DmxSimplePlus; diff --git a/RiTCh_Lightshow.ino b/RiTCh_Lightshow.ino index ad86a9b..4b8dff6 100644 --- a/RiTCh_Lightshow.ino +++ b/RiTCh_Lightshow.ino @@ -14,7 +14,7 @@ #include #include -#include "DmxSimplePlus.h" +#include "DmxSimple.h" #include #include "looper.h" @@ -35,6 +35,11 @@ #define MAX_DMX_LEVEL 255 #define MAX_DMX_SPOTS 2 +#define MIN_DMX_FADE_TIME 1 +#define MAX_DMX_FADE_TIME 5 +#define MIN_DMX_HOLD_TIME 3 +#define MAX_DMX_HOLD_TIME 15 + // Arduino pins #define POTI1_PIN A1 #define POTI2_PIN A2 @@ -55,19 +60,15 @@ uint32_t button_time[4] = { 0, 0, 0, 0 }; uint8_t poti_level[4] = { 0, 0, 0, 0 }; uint8_t brightness = LED_NORMAL_BRIGHTNESS; -enum { - DMX_RGB, - DMX_RGBW -}; - typedef struct { - uint8_t dmx_type = DMX_RGB; + uint8_t dmx_type = 4; uint16_t address = 0; uint16_t steps = 0; float diff[4] = { 0.0, 0.0, 0.0, 0.0 }; + float values[4] = { 0.0, 0.0, 0.0, 0.0 }; } dmx_spot; -dmx_spot spot[2]; +dmx_spot spot[MAX_DMX_SPOTS]; // schedular looper sched; @@ -86,17 +87,21 @@ void button_check(void) { uint16_t b = button(i); if (b > BUTTON_LONG_PRESS && !bitRead(button_state, i)) { - // long press +// long press +#ifdef DEBUG Serial.print(F("Button[")); Serial.print(i + 1, DEC); Serial.println(F("]: long")); +#endif button_time[i] = 0; do_button_long(i); } else if (b > MIN_TIME_SWITCH_PRESSED && !bitRead(button_state, i)) { - // short press +// short press +#ifdef DEBUG Serial.print(F("Button[")); Serial.print(i + 1, DEC); Serial.println(F("]: short")); +#endif button_time[i] = 0; do_button_short(i); } @@ -111,10 +116,12 @@ void level_check(void) { poti_lvl = map(analogRead(poti_pin_by_number(i)), 15, 1023, 0, 255); if (poti_lvl != poti_level[i]) { poti_level[i] = poti_lvl; +#ifdef DEBUG Serial.print(F("Poti[")); Serial.print(i + 1, DEC); Serial.print(F("]: ")); Serial.println(poti_level[i]); +#endif do_level(i); } } @@ -126,12 +133,57 @@ void show_led(void) { void worker(void) { for (uint8_t s = 0; s < MAX_DMX_SPOTS; s++) { + uint8_t diff_counter = 0; + if (spot[s].steps == 0) { - // start hold timer + for (uint8_t i = 0; i < spot[s].dmx_type; i++) { + if (spot[s].diff[i] != 0.0) { + diff_counter++; + spot[s].diff[i] = 0.0; + } + } + if (diff_counter != 0) { + // start hold timer + spot[s].steps = random(MIN_DMX_HOLD_TIME, MAX_DMX_HOLD_TIME) * 1000 / WORKER_SCHED; +#ifdef DEBUG + Serial.print(F("Spot ")); + Serial.print(s, DEC); + Serial.print(F(" holding for ")); + Serial.print(spot[s].steps * WORKER_SCHED / 1000, DEC); + Serial.println(F(" seconds")); +#endif + } else { + // new random values + spot[s].steps = random(MIN_DMX_FADE_TIME, MAX_DMX_FADE_TIME) * 1000 / WORKER_SCHED; + for (uint8_t i = 0; i < spot[s].dmx_type; i++) + spot[s].diff[i] = (random(0, 255) - spot[s].values[i]) / spot[s].steps; +#ifdef DEBUG + Serial.print(F("Spot ")); + Serial.print(s, DEC); + Serial.print(F(" changing color for ")); + Serial.print(spot[s].steps * WORKER_SCHED / 1000, DEC); + Serial.println(F(" seconds")); +#endif + } } else { - for (uint8_t i = 0; i < 3; i++) - DmxSimplePlus.write(spot[s].address + i, uint8_t(float(DmxSimplePlus.read(spot[s].address + i)) + spot[s].diff[i] + 0.5)); + for (uint8_t i = 0; i < spot[s].dmx_type; i++) { + spot[s].values[i] += spot[s].diff[i]; + DmxSimple.write(spot[s].address + i, uint8_t(spot[s].values[i] + 0.5)); +#ifdef DEBUG + Serial.print(F("Spot ")); + Serial.print(s, DEC); + Serial.print(F(" color ")); + Serial.print(i, DEC); + Serial.print(F(" step ")); + Serial.print(spot[s].steps, DEC); + Serial.print(F(" diff ")); + Serial.print(spot[s].diff[i], 4); + Serial.print(F(" value ")); + Serial.println(spot[s].values[i], 4); +#endif + } } + spot[s].steps--; } } @@ -139,19 +191,25 @@ void worker(void) { // HELPER FUNCTIONS //-------------------------------------------------------------------------------- void do_level(uint8_t p) { - Serial.print("LEVEL "); +#ifdef DEBUG + Serial.print(F("LEVEL ")); Serial.println(p); +#endif - DmxSimplePlus.write(1 + p, poti_level[p]); + DmxSimple.write(1 + p, poti_level[p]); } void do_button_long(uint8_t b) { - Serial.print("LONG "); +#ifdef DEBUG + Serial.print(F("LONG ")); +#endif Serial.println(b); } void do_button_short(uint8_t b) { - Serial.print("SHORT "); +#ifdef DEBUG + Serial.print(F("SHORT ")); +#endif Serial.println(b); } @@ -220,15 +278,18 @@ uint8_t poti_pin_by_number(byte n) { // SYSTEM //-------------------------------------------------------------------------------- void setup() { +#ifdef DEBUG Serial.begin(9600); +#endif #ifdef __AVR_ATmega32U4__ while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } #endif - - Serial.println(""); +#ifdef DEBUG + Serial.println(F("")); +#endif mySoftwareSerial.begin(9600); @@ -251,19 +312,24 @@ void setup() { pinMode(LED_PIN, OUTPUT); // setup DMX - DmxSimplePlus.usePin(DMX_PIN); - DmxSimplePlus.maxChannel(DMX_MAX_CHANNEL); + DmxSimple.usePin(DMX_PIN); + DmxSimple.maxChannel(DMX_MAX_CHANNEL); // DMX setup spot[0].address = 1; + spot[1].address = 5; - // setup audio card +// setup audio card +#ifdef DEBUG Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)")); +#endif for (uint8_t n = 0; n < 3; n++) { if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3. +#ifdef DEBUG Serial.print(F("Unable use DFPlayer: ")); Serial.println(n, DEC); +#endif for (uint8_t i = 0; i < 3; i++) { analogWrite(LED_PIN, LED_PLAY_BRIGHTNESS); delay(100); @@ -271,7 +337,9 @@ void setup() { delay(100); } } else { +#ifdef DEBUG Serial.println(F("DFPlayer Mini online.")); +#endif myDFPlayer.setTimeOut(500); //Set serial communictaion time out 500ms break; } @@ -284,7 +352,9 @@ void setup() { sched.addJob(level_check, LEVEL_CHECK_SCHED); sched.addJob(worker, WORKER_SCHED); sched.addJob(show_led, LED_SCHED); - Serial.println(""); +#ifdef DEBUG + Serial.println(F("")); +#endif } void loop() {