// SPDX-FileCopyrightText: 2013 W.A. van der Meeren // // SPDX-License-Identifier: LGPL-3.0-or-later /* Conceptinetics.cpp - DMX library for Arduino Copyright (c) 2013 W.A. van der Meeren . 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 #include #include #include #include #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(*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<>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 <(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(m_msg.PD)->lbound < m_devid && reinterpret_cast(m_msg.PD)->hbound > m_devid ) { // Discovery messages are responded with data only and no breaks repondDiscUniqueBranch (); } break; case rdm::DiscMute: reinterpret_cast(m_msg.PD)->ctrlField = 0x0; m_msg.PDL = sizeof ( RDM_DiscMuteUnMutePD ); m_rdmStatus.mute = true; break; case rdm::DiscUnMute: reinterpret_cast(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 (m_msg.PD)->DMX512CurrentPersonality = m_Personality; reinterpret_cast (m_msg.PD)->DMX512CurrentPersonality = m_Personalities; m_msg.PDL = sizeof (RDM_DeviceGetPersonality_PD); } else // if ( m_msg.CC == rdm::SetCommand ) { m_Personality = reinterpret_cast (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<>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<>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< -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<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; } }