Warning: Use of undefined constant PREG_PATTERN_VALID_LANGUAGE - assumed 'PREG_PATTERN_VALID_LANGUAGE' (this will throw an Error in a future version of PHP) in
/var/www/kucjica/emperors-wiki/inc/parser/xhtml.php on line
633
Warning: preg_replace(): Delimiter must not be alphanumeric or backslash in
/var/www/kucjica/emperors-wiki/inc/parser/xhtml.php on line
633
DmxSimple.cpp
/**
* DmxSimple - A simple interface to DMX.
*
* Copyright (c) 2008-2009 Peter Knight, Tinker.it! All rights reserved.
*/
/**
* Some alterations by Andreas Rothenwänder
*
* * added support for AT90USB162, AT90USB646 & AT90USB1286 (e.g. Teensy++ board - suggested by Paul Stoffregen)
* * write : added return value - returns now previous setting
* * usePin : minor bug corrected
* * modulate: new function - relative value change of given channel (returns new value)
* * getValue: new function - returns current value of given channel
*
* alterations commented as // -->
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#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;
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);
uint8_t dmxWrite(int,uint8_t);
void dmxMaxChannel(int);
uint8_t dmxModulate(int, int);
uint8_t dmxGetValue(int);
/* TIMER2 has a different register mapping on the ATmega8.
* The modern chips (168, 328P, 1280) use identical mappings.
*/
// --> added "|| defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)"
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega1280__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define TIMER2_INTERRUPT_ENABLE() TIMSK2 |= _BV(TOIE2)
#define TIMER2_INTERRUPT_DISABLE() TIMSK2 &= ~_BV(TOIE2)
#elif defined(__AVR_ATmega8__)
#define TIMER2_INTERRUPT_ENABLE() TIMSK |= _BV(TOIE2)
#define TIMER2_INTERRUPT_DISABLE() TIMSK &= ~_BV(TOIE2)
// --> -----------------------------
// --> added section for AT90USB162 (this CPU has only one timer - can cause problems)
#elif defined(__AVR_AT90USB162__) // added
#define TIMER2_INTERRUPT_ENABLE() TIMSK0 |= _BV(TOIE0)
#define TIMER2_INTERRUPT_DISABLE() TIMSK0 &= ~_BV(TOIE0)
// --> -----------------------------
#elif defined (__AVR_ATmega32U4__) // leonardo
#define TIMER2_INTERRUPT_ENABLE() TIMSK4 |= _BV(TOIE4)
#define TIMER2_INTERRUPT_DISABLE() TIMSK4 &= ~_BV(TOIE4)
#else
#define TIMER2_INTERRUPT_ENABLE()
#define TIMER2_INTERRUPT_DISABLE()
/* Produce an appropriate message to aid error reporting on nonstandard
* platforms such as Teensy.
*/
#warning "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
// --> -----------------------------
// --> added section for AT90USB646/1286
#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
TCCR2A = (1<<WGM20);
TCCR2B = (1<<CS22); // 64 prescaler
#elif defined (__AVR_ATmega32U4__) //leonardo
// comme c'est un timer 10 bits, on mets le prescaler à 16
// TCCR4A =
TCCR4B = (1<<CS42) | (1<<CS40);
#endif
// --> -----------------------------
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)
);
}
/** 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.
*/
#if defined (__AVR_ATmega32U4__)
ISR(TIMER4_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();
}
uint8_t dmxWrite(int channel, uint8_t value) { // --> uint8_t replaces void as return value
uint8_t oldValue = 0; // --> buffer for return value
if (!dmxStarted) dmxBegin();
if ((channel > 0) && (channel <= DMX_SIZE)) {
dmxMax = max((unsigned)channel, dmxMax);
oldValue = dmxBuffer[channel-1]; // --> remember previous value
dmxBuffer[channel-1] = value;
}
return oldValue; // --> return previous value
}
void dmxMaxChannel(int channel) {
if (channel <=0) {
// End DMX transmission
dmxEnd();
dmxMax = 0;
} else {
dmxMax = min(channel, DMX_SIZE);
if (!dmxStarted) dmxBegin();
}
}
// --> new function
uint8_t dmxModulate(int channel, int offset)
{
if (!dmxStarted) dmxBegin();
if ((channel > 0) && (channel <= DMX_SIZE)) {
dmxMax = max((unsigned)channel, dmxMax);
return (dmxBuffer[channel-1] = max(min(offset + dmxBuffer[channel-1], 255), 0));
}
return 0;
}
// --> new function
uint8_t dmxGetValue(int channel)
{
if ((channel > 0) && (channel <= DMX_SIZE))
return (dmxBuffer[channel-1]);
else
return 0;
}
/* C++ wrapper */
/** Set output pin
* @param pin Output digital pin to use
*/
void DmxSimpleClass::usePin(uint8_t pin) {
bool restartRequired = dmxStarted;
// --> original version never set pin
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
*/
uint8_t DmxSimpleClass::write(int address, uint8_t value) // --> uint8_t replaces void as return value
{
return dmxWrite(address, value); // --> return previous value
}
// --> new function
uint8_t DmxSimpleClass::modulate(int channel, int offset)
{
return dmxModulate(channel, offset);
}
// --> new function
uint8_t DmxSimpleClass::getValue(int channel)
{
return dmxGetValue(channel);
}
DmxSimpleClass DmxSimple;