Adafruit_Learning_System_Gu.../DMX_NeoPixels/Conceptinetics.cpp
2024-12-11 09:39:52 -08:00

1233 lines
36 KiB
C++
Executable file

// SPDX-FileCopyrightText: 2013 W.A. van der Meeren <danny@illogic.nl>
//
// SPDX-License-Identifier: LGPL-3.0-or-later
/*
Conceptinetics.cpp - DMX library for Arduino
Copyright (c) 2013 W.A. van der Meeren <danny@illogic.nl>. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
This code has been tested using the following hardware:
- Arduino UNO R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD
- Arduino MEGA2560 R3 using a CTC-DRA-13-1 ISOLATED DMX-RDM SHIELD
*/
#include "pins_arduino.h"
#include "Conceptinetics.h"
#include <inttypes.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#if defined (USE_DMX_SERIAL_0)
#if defined (USART__TXC_vect)
#define USART_TX USART__TXC_vect
#elif defined(USART_TX_vect)
#define USART_TX USART_TX_vect
#elif defined(USART0_TX_vect)
#define USART_TX USART0_TX_vect
#endif
#if defined (USART__RXC_vect)
#define USART_RX USART__RXC_vect
#elif defined(USART_RX_vect)
#define USART_RX USART_RX_vect
#elif defined(USART0_RX_vect)
#define USART_RX USART0_RX_vect
#endif
#if defined UDR
#define DMX_UDR UDR
#elif defined UDR0
#define DMX_UDR UDR0
#endif
#if defined(UBRRH) && defined(UBRRL)
#define DMX_UBRRH UBRRH
#define DMX_UBRRL UBRRL
#elif defined(UBRR0H) && defined(UBRR0L)
#define DMX_UBRRH UBRR0H
#define DMX_UBRRL UBRR0L
#endif
#if defined(UCSRA)
#define DMX_UCSRA UCSRA
#elif defined(UCSR0A)
#define DMX_UCSRA UCSR0A
#endif
#if defined(UCSRB)
#define DMX_UCSRB UCSRB
#elif defined (UCSR0B)
#define DMX_UCSRB UCSR0B
#endif
#if defined(TXEN) && defined(TXCIE)
#define DMX_TXEN TXEN
#define DMX_TXCIE TXCIE
#elif defined(TXEN0) && defined(TXCIE0)
#define DMX_TXEN TXEN0
#define DMX_TXCIE TXCIE0
#endif
#if defined(RXEN) && defined(RXCIE)
#define DMX_RXEN RXEN
#define DMX_RXCIE RXCIE
#elif defined(RXEN0) && defined(RXCIE0)
#define DMX_RXEN RXEN0
#define DMX_RXCIE RXCIE0
#endif
#if defined(FE)
#define DMX_FE FE
#elif defined(FE0)
#define DMX_FE FE0
#endif
#define RX_PIN 0
#define TX_PIN 1
#elif defined (USE_DMX_SERIAL_1)
#define USART_RX USART1_RX_vect
#define USART_TX USART1_TX_vect
#define DMX_UDR UDR1
#define DMX_UBRRH UBRR1H
#define DMX_UBRRL UBRR1L
#define DMX_UCSRA UCSR1A
#define DMX_UCSRB UCSR1B
#define DMX_TXEN TXEN1
#define DMX_TXCIE TXCIE1
#define DMX_RXEN RXEN1
#define DMX_RXCIE RXCIE1
#define DMX_FE FE1
#define RX_PIN 19
#define TX_PIN 18
#elif defined (USE_DMX_SERIAL_2)
#define USART_RX USART2_RX_vect
#define USART_TX USART2_TX_vect
#define DMX_UDR UDR2
#define DMX_UBRRH UBRR2H
#define DMX_UBRRL UBRR2L
#define DMX_UCSRA UCSR2A
#define DMX_UCSRB UCSR2B
#define DMX_TXEN TXEN2
#define DMX_TXCIE TXCIE2
#define DMX_RXEN RXEN2
#define DMX_RXCIE RXCIE2
#define DMX_FE FE2
#define RX_PIN 17
#define TX_PIN 16
#elif defined (USE_DMX_SERIAL_3)
#define USART_RX USART3_RX_vect
#define USART_TX USART3_TX_vect
#define DMX_UDR UDR3
#define DMX_UBRRH UBRR3H
#define DMX_UBRRL UBRR3L
#define DMX_UCSRA UCSR3A
#define DMX_UCSRB UCSR3B
#define DMX_TXEN TXEN3
#define DMX_TXCIE TXCIE3
#define DMX_RXEN RXEN3
#define DMX_RXCIE RXCIE3
#define DMX_FE FE3
#define RX_PIN 14
#define TX_PIN 15
#endif
#define LOWBYTE(v) ((uint8_t) (v))
#define HIGHBYTE(v) ((uint8_t) (((uint16_t) (v)) >> 8))
#define BSWAP_16(x) ( (uint8_t)((x) >> 8) | ((uint8_t)(x)) << 8 )
namespace isr
{
enum isrState
{
Idle,
Break,
DmxBreak,
DmxBreakManual,
DmxStartByte,
DmxRecordData,
DmxTransmitData,
RdmBreak,
RdmStartByte,
RdmRecordData,
RdmTransmitData,
RDMTransmitComplete,
};
enum isrMode
{
Disabled,
Receive,
DMXTransmit,
DMXTransmitManual, /* Manual break... */
RDMTransmit,
RDMTransmitNoInt, /* Setup uart but leave interrupt disabled */
};
};
DMX_Master *__dmx_master;
DMX_Slave *__dmx_slave;
RDM_Responder *__rdm_responder;
int8_t __re_pin; // R/W Pin on shield
isr::isrState __isr_txState; // TX ISR state
isr::isrState __isr_rxState; // RX ISR state
void SetISRMode ( isr::isrMode );
DMX_FrameBuffer::DMX_FrameBuffer ( uint16_t buffer_size )
{
m_refcount = (uint8_t*) malloc ( sizeof ( uint8_t ) );
if ( buffer_size >= DMX_MIN_FRAMESIZE && buffer_size <= DMX_MAX_FRAMESIZE )
{
m_buffer = (uint8_t*) malloc ( buffer_size );
if ( m_buffer != NULL )
{
memset ( (void *)m_buffer, 0x0, buffer_size );
m_bufferSize = buffer_size;
}
else
m_buffer = 0x0;
}
else
m_bufferSize = 0x0;
*m_refcount++;
}
DMX_FrameBuffer::DMX_FrameBuffer ( DMX_FrameBuffer &buffer )
{
// Copy references and make sure the parent object does not dispose our
// buffer when deleted and we are still active
this->m_refcount = buffer.m_refcount;
(*this->m_refcount)++;
this->m_buffer = buffer.m_buffer;
this->m_bufferSize = buffer.m_bufferSize;
}
DMX_FrameBuffer::~DMX_FrameBuffer ( void )
{
// If we are the last object using the
// allocated buffer then free it together
// with the refcounter
if ( --(*m_refcount) == 0 )
{
if ( m_buffer )
free ( m_buffer );
free ( m_refcount );
}
}
uint16_t DMX_FrameBuffer::getBufferSize ( void )
{
return m_bufferSize;
}
uint8_t DMX_FrameBuffer::getSlotValue ( uint16_t index )
{
if (index < m_bufferSize)
return m_buffer[index];
else
return 0x0;
}
void DMX_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value )
{
if ( index < m_bufferSize )
m_buffer[index] = value;
}
void DMX_FrameBuffer::setSlotRange ( uint16_t start, uint16_t end, uint8_t value )
{
if ( start < m_bufferSize && end < m_bufferSize && start < end )
memset ( (void *) &m_buffer[start], value, end-start+1 );
}
void DMX_FrameBuffer::clear ( void )
{
memset ( (void *) m_buffer, 0x0, m_bufferSize );
}
uint8_t &DMX_FrameBuffer::operator[] ( uint16_t index )
{
return m_buffer[index];
}
DMX_Master::DMX_Master ( DMX_FrameBuffer &buffer, int readEnablePin )
: m_frameBuffer ( buffer ),
m_autoBreak ( 1 ) // Autobreak generation is default on
{
setStartCode ( DMX_START_CODE );
__re_pin = readEnablePin;
pinMode ( __re_pin, OUTPUT );
::SetISRMode ( isr::Disabled );
}
DMX_Master::DMX_Master ( uint16_t maxChannel, int readEnablePin )
: m_frameBuffer ( maxChannel + DMX_STARTCODE_SIZE ),
m_autoBreak ( 1 ) // Autobreak generation is default on
{
setStartCode ( DMX_START_CODE );
__re_pin = readEnablePin;
pinMode ( __re_pin, OUTPUT );
::SetISRMode ( isr::Disabled );
}
DMX_Master::~DMX_Master ( void )
{
disable (); // Stop sending
__dmx_master = NULL;
}
DMX_FrameBuffer &DMX_Master::getBuffer ( void )
{
return m_frameBuffer; // Return reference to frame buffer
}
void DMX_Master::setStartCode ( uint8_t value )
{
m_frameBuffer[0] = value; // Set the first byte in our frame buffer
}
void DMX_Master::setChannelValue ( uint16_t channel, uint8_t value )
{
if ( channel > 0 ) // Prevent overwriting the start code
m_frameBuffer.setSlotValue ( channel, value );
}
void DMX_Master::setChannelRange ( uint16_t start, uint16_t end, uint8_t value )
{
if ( start > 0 ) // Prevent overwriting the start code
m_frameBuffer.setSlotRange ( start, end, value );
}
void DMX_Master::enable ( void )
{
__dmx_master = this;
if ( m_autoBreak )
::SetISRMode ( isr::DMXTransmit );
else
::SetISRMode ( isr::DMXTransmitManual );
}
void DMX_Master::disable ( void )
{
::SetISRMode ( isr::Disabled );
__dmx_master = NULL; // No active master
}
void DMX_Master::setAutoBreakMode ( void ) { m_autoBreak = 1; }
void DMX_Master::setManualBreakMode ( void ) { m_autoBreak = 0; }
uint8_t DMX_Master::autoBreakEnabled ( void ) { return m_autoBreak; }
uint8_t DMX_Master::waitingBreak ( void )
{
return ( __isr_txState == isr::DmxBreakManual );
}
void DMX_Master::breakAndContinue ( uint8_t breakLength_us )
{
// Only execute if we are the controlling master object
if ( __dmx_master == this && __isr_txState == isr::DmxBreakManual )
{
pinMode ( TX_PIN, OUTPUT );
digitalWrite ( TX_PIN, LOW ); // Begin BREAK
for (uint8_t bl=0; bl<breakLength_us; bl++)
_delay_us ( 1 );
// Turn TX Pin into Logic HIGH
digitalWrite ( TX_PIN, HIGH ); // END BREAK
__isr_txState = isr::DmxStartByte;
// TX Enable
DMX_UCSRB |= (1<<DMX_TXEN);
_delay_us ( 12 ); // MAB 12µSec
// TX Interupt enable
DMX_UCSRB |= (1<<DMX_TXCIE);
}
}
void (*DMX_Slave::event_onFrameReceived)(unsigned short channelsReceived);
DMX_Slave::DMX_Slave ( DMX_FrameBuffer &buffer, int readEnablePin )
: DMX_FrameBuffer ( buffer ),
m_startAddress ( 1 )
{
__dmx_slave = this;
__re_pin = readEnablePin;
pinMode ( __re_pin, OUTPUT );
::SetISRMode ( isr::Disabled );
}
DMX_Slave::DMX_Slave ( uint16_t nrChannels, int readEnablePin )
: DMX_FrameBuffer ( nrChannels + 1 ),
m_startAddress ( 1 )
{
__dmx_slave = this;
__re_pin = readEnablePin;
pinMode ( __re_pin, OUTPUT );
::SetISRMode ( isr::Disabled );
}
DMX_Slave::~DMX_Slave ( void )
{
disable ();
__dmx_slave = NULL;
}
void DMX_Slave::enable ( void )
{
::SetISRMode ( isr::Receive );
}
void DMX_Slave::disable ( void )
{
::SetISRMode ( isr::Disabled );
}
DMX_FrameBuffer &DMX_Slave::getBuffer ( void )
{
return reinterpret_cast<DMX_FrameBuffer&>(*this);
}
uint8_t DMX_Slave::getChannelValue ( uint16_t channel )
{
return getSlotValue ( channel );
}
uint16_t DMX_Slave::getStartAddress ( void )
{
return m_startAddress;
}
void DMX_Slave::setStartAddress ( uint16_t addr )
{
m_startAddress = addr;
}
void DMX_Slave::onReceiveComplete ( void (*func)(unsigned short) )
{
event_onFrameReceived = func;
}
bool DMX_Slave::processIncoming ( uint8_t val, bool first )
{
static uint16_t idx;
bool rval = false;
if ( first )
{
// We could have received less channels then we
// expected.. but still is a complete frame
if (m_state == dmx::dmxData && event_onFrameReceived)
event_onFrameReceived (idx);
m_state = dmx::dmxStartByte;
}
switch ( m_state )
{
case dmx::dmxStartByte:
setSlotValue ( 0, val ); // Store start code
idx = m_startAddress;
m_state = dmx::dmxWaitStartAddress;
case dmx::dmxWaitStartAddress:
if ( --idx == 0 )
m_state = dmx::dmxData;
break;
case dmx::dmxData:
if ( idx++ < getBufferSize() )
setSlotValue ( idx, val );
else
{
m_state = dmx::dmxFrameReady;
// If a onFrameReceived callback is register...
if (event_onFrameReceived)
event_onFrameReceived (idx-2);
rval = true;
}
break;
}
return rval;
}
uint16_t RDM_FrameBuffer::getBufferSize ( void ) { return sizeof ( m_msg ); }
uint8_t RDM_FrameBuffer::getSlotValue ( uint16_t index )
{
if ( index < sizeof ( m_msg ) )
return m_msg.d[index];
else
return 0x0;
}
void RDM_FrameBuffer::setSlotValue ( uint16_t index, uint8_t value )
{
if ( index < sizeof ( m_msg ) )
m_msg.d[index] = value;
}
void RDM_FrameBuffer::clear ( void )
{
memset ( (void*)m_msg.d, 0x0, sizeof( m_msg ) );
m_state = rdm::rdmUnknown;
}
bool RDM_FrameBuffer::processIncoming ( uint8_t val, bool first )
{
static uint16_t idx;
bool rval = false;
if ( first )
{
m_state = rdm::rdmStartByte;
m_csRecv.checksum = (uint16_t) 0x0000;
idx = 0;
}
// Prevent buffer overflow for large messages
if (idx >= sizeof(m_msg))
return true;
switch ( m_state )
{
case rdm::rdmStartByte:
m_msg.startCode = val;
m_state = rdm::rdmSubStartCode;
break;
case rdm::rdmSubStartCode:
if ( val != 0x01 )
{
rval = true; // Stop processing data
break;
}
m_msg.subStartCode = val;
m_state = rdm::rdmMessageLength;
break;
case rdm::rdmMessageLength:
m_msg.msgLength = val;
m_state = rdm::rdmData;
m_csRecv.checksum = 0xcc + 0x01 + val; // set initial checksum
idx = 3; // buffer index for next byte
break;
case rdm::rdmData:
m_msg.d[idx++] = val;
m_csRecv.checksum += val;
if ( idx >= m_msg.msgLength )
m_state = rdm::rdmChecksumHigh;
break;
case rdm::rdmChecksumHigh:
m_csRecv.csh = val;
m_state = rdm::rdmChecksumLow;
break;
case rdm::rdmChecksumLow:
m_csRecv.csl = val;
if ((m_csRecv.checksum % (uint16_t)0x10000) == m_csRecv.checksum)
{
m_state = rdm::rdmFrameReady;
// valid checksum ... start processing
processFrame ();
}
m_state = rdm::rdmUnknown;
rval = true;
break;
};
return rval;
}
bool RDM_FrameBuffer::fetchOutgoing ( volatile uint8_t *udr, bool first )
{
static uint16_t idx;
static uint16_t cs;
bool rval = false;
if ( first )
{
m_state = rdm::rdmData;
cs = 0x0;
idx = 0;
}
switch ( m_state )
{
case rdm::rdmData:
cs += m_msg.d[idx];
*udr = m_msg.d[idx++];
if ( idx >= m_msg.msgLength )
{
m_state = rdm::rdmChecksumHigh;
}
break;
case rdm::rdmChecksumHigh:
*udr = (cs >> 8);
m_state = rdm::rdmChecksumLow;
break;
case rdm::rdmChecksumLow:
*udr = (cs & 0xff);
m_state = rdm::rdmUnknown;
rval = true;
break;
}
return rval;
}
void (*RDM_Responder::event_onIdentifyDevice)(bool);
void (*RDM_Responder::event_onDeviceLabelChanged)(const char*, uint8_t);
void (*RDM_Responder::event_onDMXStartAddressChanged)(uint16_t);
void (*RDM_Responder::event_onDMXPersonalityChanged)(uint8_t);
//
// slave parameter is only used to ensure a slave object is present before
// initializing the rdm responder class
//
RDM_Responder::RDM_Responder ( uint16_t m, uint8_t d1, uint8_t d2,
uint8_t d3, uint8_t d4, DMX_Slave &slave )
: RDM_FrameBuffer ( ),
m_Personalities (1), // Available personlities
m_Personality (1) // Default personality eq 1.
{
__rdm_responder = this;
m_devid.Initialize ( m, d1, d2, d3, d4 );
// Default software version id = 0x00000000
memset ( (void*)m_SoftwareVersionId, 0x0, 0x4 );
// Rdm responder is disabled by default
m_rdmStatus.enabled = false;
}
RDM_Responder::~RDM_Responder ( void )
{
__rdm_responder = NULL;
}
void RDM_Responder::onIdentifyDevice ( void (*func)(bool) )
{
event_onIdentifyDevice = func;
}
void RDM_Responder::onDeviceLabelChanged ( void (*func) (const char*, uint8_t) )
{
event_onDeviceLabelChanged = func;
}
void RDM_Responder::onDMXStartAddressChanged ( void (*func) (uint16_t) )
{
event_onDMXStartAddressChanged = func;
}
void RDM_Responder::onDMXPersonalityChanged ( void (*func) (uint8_t) )
{
event_onDMXPersonalityChanged = func;
}
void RDM_Responder::setDeviceLabel ( const char *label, size_t len )
{
if ( len > RDM_MAX_DEVICELABEL_LENGTH )
len = RDM_MAX_DEVICELABEL_LENGTH;
memcpy ( (void *)m_deviceLabel, (void *)label, len );
}
#define UID_0 0x12 //ESTA device ID
#define UID_1 0x34
#define UID_2 0x56
#define UID_3 0x88
#define UID_4 0x00
#define UID_5 0x00
#define UID_CS (0xFF *6 +UID_0 +UID_1 +UID_2 +UID_3 +UID_4 +UID_5)
void RDM_Responder::repondDiscUniqueBranch ( void )
{
#if defined(UCSRB)
UCSRB = (1<<TXEN);
#elif defined(UCSR0B)
UCSR0B = (1<<TXEN0);
#endif
uint16_t cs = 0;
uint8_t response[24] =
{
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xaa, // byte 0-7
m_devid.m_id[0] | 0xaa, m_devid.m_id[0] | 0x55, // byte 8, 10 MSB manufacturer
m_devid.m_id[1] | 0xaa, m_devid.m_id[1] | 0x55, // byte 10, 11 LSB manufacturer
m_devid.m_id[2] | 0xaa, m_devid.m_id[2] | 0x55, // byte 12, 13 MSB device
m_devid.m_id[3] | 0xaa, m_devid.m_id[3] | 0x55, // byte 14, 15 .
m_devid.m_id[4] | 0xaa, m_devid.m_id[4] | 0x55, // byte 16, 17 .
m_devid.m_id[5] | 0xaa, m_devid.m_id[5] | 0x55, // byte 18, 19 LSB device
0x0, 0x0, 0x0, 0x0 // Checksum space
};
// Calculate checksum
for ( int i=8; i<20; i++ )
cs += (uint16_t)response [i];
// Write checksum into response
response [20] = (cs>>8) | 0xaa;
response [21] = (cs>>8) | 0x55;
response [22] = (cs&0xff) | 0xaa;
response [23] = (cs&0xff) | 0x55;
// Table 3-2 ANSI_E1-20-2010 <2ms
_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC );
// Fix: 2017, Feb 28: Moved data enable down in order to limit line in marking state time to comply with
// section 3.2.3
// Set shield to transmit mode (turn arround)
digitalWrite ( __re_pin, HIGH );
for ( int i=0; i<24; i++ )
{
// Wait until data register is empty
#if defined (UCSR0A) && defined (UDRE0)
while((UCSR0A & (1 <<UDRE0)) == 0);
#elif defined (UCSRA) && defined (UDRE)
while((UCSRA & (1 <<UDRE)) == 0);
#endif
DMX_UDR = response[i];
}
// Wait until last byte is send
#if defined (UCSR0A) && defined (UDRE0)
//while((UCSR0A & (1 <<UDRE0)) == 0);
// Fix 2017, 28 feb: Test if last byte has been shifted out completely
while (( UCSR0A & (1 <<TXC0) ) == 0 );
#elif defined (UCSRA) && defined (UDRE)
//while((UCSRA & (1 <<UDRE)) == 0);
while (( UCSRA & (1 <<TXC) ) == 0 );
#endif
// TODO:...
// 2017, Feb 28: Removed delay, not required.
// _delay_us (100);
// Restore ISR operations
::SetISRMode ( isr::Receive );
}
void RDM_Responder::populateDeviceInfo ( void )
{
RDM__DeviceInfoPD *pd = reinterpret_cast<RDM__DeviceInfoPD *>(m_msg.PD);
pd->protocolVersionMajor = 0x01;
pd->protocolVersionMinor = 0x00;
pd->deviceModelId = BSWAP_16(m_DeviceModelId);
pd->ProductCategory = BSWAP_16(m_ProductCategory);
memcpy ( (void*)pd->SoftwareVersionId, (void*)m_SoftwareVersionId, 4 );
pd->DMX512FootPrint = BSWAP_16(__dmx_slave->getBufferSize()-1); // eq buffersize-startbyte
pd->DMX512CurrentPersonality = m_Personality;
pd->DMX512NumberPersonalities = m_Personalities;
pd->DMX512StartAddress = BSWAP_16(__dmx_slave->getStartAddress());
pd->SubDeviceCount = 0x0; // Sub devices are not supported by this library
pd->SensorCount = 0x0; // Sensors are not yet supported
m_msg.PDL = sizeof (RDM__DeviceInfoPD);
}
const uint8_t ManufacturerLabel_P[] PROGMEM = "Conceptinetics";
void RDM_Responder::processFrame ( void )
{
// If packet is a general broadcast
if (
m_msg.dstUid.isBroadcast (m_devid.m_id) ||
m_devid == m_msg.dstUid
)
{
// Set default response type
m_msg.portId = rdm::ResponseTypeAck;
switch ( BSWAP_16(m_msg.PID) )
{
case rdm::DiscUniqueBranch:
// Check if we are inside the given unique branch...
if ( !m_rdmStatus.mute &&
reinterpret_cast<RDM_DiscUniqueBranchPD *>(m_msg.PD)->lbound < m_devid &&
reinterpret_cast<RDM_DiscUniqueBranchPD *>(m_msg.PD)->hbound > m_devid )
{
// Discovery messages are responded with data only and no breaks
repondDiscUniqueBranch ();
}
break;
case rdm::DiscMute:
reinterpret_cast<RDM_DiscMuteUnMutePD *>(m_msg.PD)->ctrlField = 0x0;
m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD );
m_rdmStatus.mute = true;
break;
case rdm::DiscUnMute:
reinterpret_cast<RDM_DiscMuteUnMutePD *>(m_msg.PD)->ctrlField = 0x0;
m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD );
m_rdmStatus.mute = false;
break;
case rdm::SupportedParameters:
//
// Temporary solution... this will become dynamic
// in a later version...
//
m_msg.PD[0] = HIGHBYTE(rdm::DmxStartAddress); // MSB
m_msg.PD[1] = LOWBYTE (rdm::DmxStartAddress); // LSB
m_msg.PD[2] = HIGHBYTE(rdm::DmxPersonality);
m_msg.PD[3] = LOWBYTE (rdm::DmxPersonality);
m_msg.PD[4] = HIGHBYTE(rdm::ManufacturerLabel);
m_msg.PD[5] = LOWBYTE (rdm::ManufacturerLabel);
m_msg.PD[6] = HIGHBYTE(rdm::DeviceLabel);
m_msg.PD[7] = LOWBYTE (rdm::DeviceLabel);
m_msg.PDL = 0x6;
break;
// Only for manufacturer specific parameters
// case rdm::ParameterDescription:
// break;
case rdm::DeviceInfo:
if ( m_msg.CC == rdm::GetCommand )
populateDeviceInfo ();
break;
case rdm::DmxStartAddress:
if ( m_msg.CC == rdm::GetCommand )
{
m_msg.PD[0] = HIGHBYTE(__dmx_slave->getStartAddress ());
m_msg.PD[1] = LOWBYTE (__dmx_slave->getStartAddress ());
m_msg.PDL = 0x2;
}
else // if ( m_msg.CC == rdm::SetCommand )
{
__dmx_slave->setStartAddress ( (m_msg.PD[0] << 8) + m_msg.PD[1] );
m_msg.PDL = 0x0;
if ( event_onDMXStartAddressChanged )
event_onDMXStartAddressChanged ( (m_msg.PD[0] << 8) + m_msg.PD[1] );
}
break;
case rdm::DmxPersonality:
if ( m_msg.CC == rdm::GetCommand )
{
reinterpret_cast<RDM_DeviceGetPersonality_PD *>
(m_msg.PD)->DMX512CurrentPersonality = m_Personality;
reinterpret_cast<RDM_DeviceGetPersonality_PD *>
(m_msg.PD)->DMX512CurrentPersonality = m_Personalities;
m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD);
}
else // if ( m_msg.CC == rdm::SetCommand )
{
m_Personality = reinterpret_cast<RDM_DeviceSetPersonality_PD *>
(m_msg.PD)->DMX512Personality;
m_msg.PDL = 0x0;
if ( event_onDMXPersonalityChanged )
event_onDMXPersonalityChanged ( m_Personality );
}
break;
case rdm::IdentifyDevice:
if ( m_msg.CC == rdm::GetCommand )
{
m_msg.PD[0] = (uint8_t)(m_rdmStatus.ident ? 1 : 0);
m_msg.PDL = 0x1;
}
else if ( m_msg.CC == rdm::SetCommand )
{
// Look into first byte to see whether identification
// is turned on or off
m_rdmStatus.ident = m_msg.PD[0] ? true : false;
if ( event_onIdentifyDevice )
event_onIdentifyDevice ( m_rdmStatus.ident );
m_msg.PDL = 0x0;
}
break;
case rdm::ManufacturerLabel:
if ( m_msg.CC == rdm::GetCommand )
{
memcpy_P( (void*)m_msg.PD, ManufacturerLabel_P, sizeof(ManufacturerLabel_P) );
m_msg.PDL = sizeof ( ManufacturerLabel_P );
}
break;
case rdm::DeviceLabel:
if ( m_msg.CC == rdm::GetCommand )
{
memcpy ( m_msg.PD, (void*) m_deviceLabel, 32 );
m_msg.PDL = 32;
}
else if ( m_msg.CC == rdm::SetCommand )
{
memset ( (void*) m_deviceLabel, ' ', 32 );
memcpy ( (void*) m_deviceLabel, m_msg.PD, (m_msg.PDL < 32 ? m_msg.PDL : 32) );
m_msg.PDL = 0;
// Notify application
if ( event_onDeviceLabelChanged )
event_onDeviceLabelChanged ( m_deviceLabel, 32 );
}
break;
default:
// Unknown parameter ID response
m_msg.portId = rdm::ResponseTypeNackReason;
m_msg.PD[0] = rdm::UnknownPid;
m_msg.PD[1] = 0x0;
m_msg.PDL = 0x2;
break;
};
}
//
// Only respond if this this message
// was destined to us only
if ( m_msg.dstUid == m_devid )
{
m_msg.startCode = RDM_START_CODE;
m_msg.subStartCode = 0x01;
m_msg.msgLength = RDM_HDR_LEN + m_msg.PDL;
m_msg.msgCount = 0;
/*
switch ( m_msg.msg.CC )
{
case rdm::DiscoveryCommand:
m_msg.msg.CC = rdm::DiscoveryCommandResponse;
break;
case rdm::GetCommand:
m_msg.msg.CC = rdm::GetCommandResponse;
break;
case rdm::SetCommand:
m_msg.msg.CC = rdm::SetCommandResponse;
break;
}
*/
/* Above replaced by next line */
m_msg.CC++;
m_msg.dstUid.copy ( m_msg.srcUid );
m_msg.srcUid.copy ( m_devid );
//_delay_us ( MIN_RESPONDER_PACKET_SPACING_USEC );
SetISRMode ( isr::RDMTransmit );
}
}
void SetISRMode ( isr::isrMode mode )
{
uint8_t readEnable;
#if defined(USE_DMX_SERIAL_0)
#if defined(UCSRB) && defined (UCSRC)
UCSRC |= (1<<UMSEL)|(3<<UCSZ0)|(1<<USBS);
#elif defined(UCSR0B) && defined (UCSR0C)
UCSR0C |= (3<<UCSZ00)|(1<<USBS0);
#endif
#elif defined(USE_DMX_SERIAL_1)
UCSR1C |= (3<<UCSZ10)|(1<<USBS1);
#elif defined(USE_DMX_SERIAL_2)
UCSR2C |= (3<<UCSZ20)|(1<<USBS2);
#elif defined(USE_DMX_SERIAL_3)
UCSR3C |= (3<<UCSZ30)|(1<<USBS3);
#endif
switch ( mode )
{
case isr::Disabled:
#if defined(UCSRB)
UCSRB = 0x0;
#elif defined(UCSR0B)
UCSR0B = 0x0;
#endif
readEnable = LOW;
break;
case isr::Receive:
DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8);
DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1);
// Prepare before kicking off ISR
//DMX_UDR = 0x0;
__isr_rxState = isr::Idle;
readEnable = LOW;
DMX_UCSRB = (1<<DMX_RXCIE) | (1<<DMX_RXEN);
break;
case isr::DMXTransmit:
DMX_UDR = 0x0;
readEnable = HIGH;
__isr_txState = isr::DmxBreak;
DMX_UCSRB = (1<<DMX_TXEN) | (1<<DMX_TXCIE);
break;
case isr::DMXTransmitManual:
DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8);
DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1);
DMX_UDR = 0x0;
DMX_UCSRB = 0x0;
readEnable = HIGH;
__isr_txState = isr::DmxBreakManual;
break;
case isr::RDMTransmit:
DMX_UCSRA = 0x0;
DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8);
DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1);
DMX_UDR = 0x0;
//DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8);
//DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1);
DMX_UCSRB = (1<<DMX_TXEN) | (1<<DMX_TXCIE);
//DMX_UDR = 0x00;
readEnable = HIGH;
__isr_txState = isr::RdmStartByte;
break;
}
// If read enable pin is assigned
if (__re_pin > -1)
digitalWrite ( __re_pin, readEnable );
}
//
// TX UART (DMX Transmission ISR)
//
ISR (USART_TX)
{
static uint16_t current_slot;
switch ( __isr_txState )
{
case isr::DmxBreak:
DMX_UCSRA = 0x0;
DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8);
DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1);
DMX_UDR = 0x0;
if ( __isr_txState == isr::DmxBreak )
__isr_txState = isr::DmxStartByte;
break;
case isr::DmxStartByte:
DMX_UCSRA = 0x0;
DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8);
DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1);
current_slot = 0;
DMX_UDR = __dmx_master->getBuffer()[ current_slot++ ];
__isr_txState = isr::DmxTransmitData;
break;
case isr::DmxTransmitData:
// NOTE: we always send full frames of 513 bytes, this will bring us
// close to 40 frames / sec with no interslot delays
#ifdef DMX_IBG
_delay_us (DMX_IBG);
#endif
DMX_UDR = __dmx_master->getBuffer().getSlotValue( current_slot++ );
// Send 512 channels
if ( current_slot >= DMX_MAX_FRAMESIZE )
{
if ( __dmx_master->autoBreakEnabled () )
__isr_txState = isr::DmxBreak;
else
SetISRMode ( isr::DMXTransmitManual );
}
break;
/* case isr::RdmBreak:
DMX_UCSRA = 0x0;
DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1)>>8);
DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BREAK_RATE * 8L) / (DMX_BREAK_RATE * 16L) - 1);
DMX_UDR = 0x0;
__isr_txState = isr::RdmStartByte;
break;
*/
case isr::RdmStartByte:
DMX_UCSRA = 0x0;
DMX_UBRRH = (unsigned char)(((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1)>>8);
DMX_UBRRL = (unsigned char) ((F_CPU + DMX_BAUD_RATE * 8L) / (DMX_BAUD_RATE * 16L) - 1);
// Write start byte
__rdm_responder->fetchOutgoing ( &DMX_UDR, true );
__isr_txState = isr::RdmTransmitData;
break;
case isr::RdmTransmitData:
// Write rest of data
if ( __rdm_responder->fetchOutgoing ( &DMX_UDR ) )
__isr_txState = isr::RDMTransmitComplete;
break;
case isr::RDMTransmitComplete:
SetISRMode ( isr::Receive ); // Start waitin for new data
__isr_txState = isr::Idle; // No tx state
break;
}
}
//
// RX UART (DMX Reception ISR)
//
ISR (USART_RX)
{
uint8_t usart_state = DMX_UCSRA;
uint8_t usart_data = DMX_UDR;
//
// Check for framing error and reset if found
// A framing most likely* indicate a break in our ocasion
//
if ( usart_state & (1<<DMX_FE) )
{
DMX_UCSRA &= ~(1<<DMX_FE);
__isr_rxState = isr::Break;
return;
}
switch ( __isr_rxState )
{
case isr::Break:
if ( __dmx_slave && usart_data == DMX_START_CODE )
{
__dmx_slave->processIncoming ( usart_data, true );
__isr_rxState = isr::DmxRecordData;
}
else if ( __rdm_responder &&
usart_data == RDM_START_CODE &&
__rdm_responder->m_rdmStatus.enabled )
{
// __rdm_responder->clear ();
__rdm_responder->processIncoming ( usart_data, true );
__isr_rxState = isr::RdmRecordData;
}
else
{
__isr_rxState = isr::Idle;
}
break;
// Process DMX Data
case isr::DmxRecordData:
if ( __dmx_slave->processIncoming ( usart_data ) )
__isr_rxState = isr::Idle;
break;
// Process RDM Data
case isr::RdmRecordData:
if ( __rdm_responder->processIncoming ( usart_data ) )
__isr_rxState = isr::Idle;
break;
}
}