Arduinoesque overriding of core functionality

Status
Not open for further replies.
As I said before I don't want to promote this by making it easy to use until it's been tested. Using the example in the README should be sufficient to start using it. For example:

Code:
#define DMX_REDE 30
struct RDMINIT rdmData {
  "TeensyDMX v0.1",
  "Teensyduino",
  1,  // Device ID
  "DMX Node",
  1,  // The DMX footprint
  0,  // The DMX startAddress - only used for RDM
  0,  // Additional commands length for RDM
  0   // Definition of additional commands
};
TeensyDmx Dmx(Serial2, &rdmData, DMX_REDE);

void setup() {
  Dmx.setMode(TeensyDmx::Mode::DMX_IN);
}

void loop() {
  Dmx.loop();

  if (Dmx.newFrame()) {
    volatile const uint8_t *buf = Dmx.getBuffer();
    // Do something with the buffer
  }
}
 
Hi All,
Sorry to have been absent from this thread...honestly, it was a bit of a fire-and-forget. I'm glad (and more than a little humbled) that folks have taken this on, and I'm fine with the repackaging and licensing. I am far from a software guy, more like a hardware guy who got frustrated with the lack of a DMX receive library and wrote some hack code. Next time I'll wrap some more words around it like "Works fine with ETC Express 48/96 serial number XXXX, your mileage *will* vary." I have no doubt that there are DMX implementations that break it, and I apologize if that's caused anyone heartburn. I seem to remember being particularly nervous about minimal-length MBB (mark before break) or break periods, and it sounds like that's been an issue. I also don't claim to have employed sound coding practices, so Jim and Chris' efforts to clean up my mess are especially appreciated.

For what it's worth, I've used this code to drive LED pixel strings in the theater for a number of shows, and am looking forward to trying out some of the updates. The theaters I work in all run the same lighting console, an ETC Express, so I'm afraid I can't offer much in the way of interesting DMX implementations to test against. Also, for my Christmas lights this past year I put together a new controller that I'm going to start using in the theater and I'll gladly share details on. It uses standard Ethernet cabling (RJ45-terminated cat 5/5e/6) to wire the LED strings back to the controller, running +24VDC and balanced (RS-422) WS2811 protocol over the wire. I've successfully tested runs of 200 feet from the controller to the head of the string, with good integrity on the WS2811 signal.
 
Last edited:
All you should need is a RS-485 chip wired for receive-only mode.

The 8 pin ones almost all use the same pinout. Pin 1 is the output, which you'd connect to RX1 on Teensy. Pin 2 is RE', which you'd connect to ground to enable the receiver. Pins 3 and 4 are for the transmitter. Connect pin 3 to ground so the transmitter is disabled. Pin 4 can be left unconnected. Pins 5 and 8 are for power, and pins 6 and 7 are the signal lines for the DMX.

That's not a schematic... but hopefully it gives you enough info to wire up any of those 8-pin RS485 chips.
 
Here's a section of the design where I'm using it:
teensy-dmx.png
 
Hello again Paul (and thank you again !!)

I finished my board, but when try to compile, I have the error : error: 'IRQ_UART0_ERROR' was not declared in this scope

Teensyduino is set as serial, do you have an idea ?

there's the code :
Code:
/* DmxReceiver DmxTest.ino - DMX Test
   Copyright (c) 2014 Jim Paris

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.

   Required Connections
   --------------------
   pin 0: DMX in (from RS485 transceiver)

   This test prints out DMX data to USB serial every 1 second.
   The on-board LED is toggled every time a DMX frame is received.
*/

#include <DmxReceiver.h>

DmxReceiver dmx;
IntervalTimer dmxTimer;

void dmxTimerISR(void)
  {
  dmx.bufferService();
  }

void setup() 
  {
  /* USB serial */
  Serial.begin(115200);

  /* DMX */
  dmx.begin();

  /* Use a timer to service DMX buffers every 1ms */
  dmxTimer.begin(dmxTimerISR, 1000);

  pinMode(LED_BUILTIN, OUTPUT);
  }

int led = 0;
elapsedMillis elapsed;
void loop()
  {
  /* Toggle LED on every new frame */
  if (dmx.newFrame())
    {
    led = !led;
    digitalWrite(LED_BUILTIN, led);
    }

  /* Dump DMX data every second */
  if (elapsed > 1000) 
    {
    elapsed -= 1000;
    Serial.printf("DMX frameCount=%d", dmx.frameCount());

    /* Display all nonzero DMX values */
    for (int i = 0; i < 512; i++) 
      {
      uint8_t v = dmx.getDimmer(i);
      if (v)
        {
        Serial.printf(" %d:%d", i, v);
        }
      }
    Serial.printf("\n");
    }
  }
 
Hi there,

New try, I see that a file named "mk20dx128.h" was called in the jim's library, but the teensy LC has a mkl26z64 :(
Does it means that the library cannot work with Teensy LC ?

I also tried to add these lines in the dmxReceiver.cpp
Code:
#define IRQ_UART0_LON   15
#define IRQ_UART0_STATUS  16
#define IRQ_UART0_ERROR   17
#define IRQ_UART1_STATUS  18
the code now seems valid, but the board doesn't seems to be alive (the first line of my setup is not runned);
 
It looks like the MKL26Z64 doesn't separate the UART0 interrupts like the MK20DX128 did, so properly catching the BREAK condition (frame error) will require modifications to the Teensy core code. Which is really the right way to do it anyway. I just don't know what the best interface is to expose it -- a weakly linked callback?
 
It looks like the MKL26Z64 doesn't separate the UART0 interrupts like the MK20DX128 did, so properly catching the BREAK condition (frame error) will require modifications to the Teensy core code. Which is really the right way to do it anyway. I just don't know what the best interface is to expose it -- a weakly linked callback?

Ok I understand absolutely nothing :confused: (And I'm a little scared when I see "modifications to the Teensy core code" :D)
I don't understand why dmx cannot be readed like a serial data (yes yes, I'm noob...)
 
Hi Jim,

So I am a bit new to all this too, and was wondering if it would be possible (for me) to adjust the library you modified, so that I could use serial 3 on a Teensy 3.1? this is because I made a bit of a mistake and had a PCB made for a project that includes DMX receiving, and wired the MAX 485 to use the Serial 3 Port on my teensy, and obviously now, it doesn't receive nothing :s could you guide me on where to change this? I have looked on the .cpp and .h files and I see UART0 in a lot of places, I guess I have to change all of that to UART3... ??? any guideance would be greatly appreciated.

cheers,
 
Hi Jim,

So I am a bit new to all this too, and was wondering if it would be possible (for me) to adjust the library you modified, so that I could use serial 3 on a Teensy 3.1? this is because I made a bit of a mistake and had a PCB made for a project that includes DMX receiving, and wired the MAX 485 to use the Serial 3 Port on my teensy, and obviously now, it doesn't receive nothing :s could you guide me on where to change this? I have looked on the .cpp and .h files and I see UART0 in a lot of places, I guess I have to change all of that to UART3... ??? any guideance would be greatly appreciated.

cheers,

I'd start by changing HardwareSerial() to HardwareSerial3(), then change all the UART0 to UART2 (UART2 is serial 3). Might work!
 
I'd start by changing HardwareSerial() to HardwareSerial3(), then change all the UART0 to UART2 (UART2 is serial 3). Might work!

Hey Jim,

Thanks for the quick response, So I tried that, changed the HardwareSerial() to HardwareSerial3() (I also tried ...serial2()) and everywere I could see UART0, I changed to UART2, but it still wont work :(
I did try with no modification and using Serial 1 and it works like a charm, so my wiring is correct.

There is 2 lines in your code that have a UART0 but if I change that, it wont complie, telling me that those expressions have not been defined, these are:


if (UART2_S1 & UART_S1_FE)

and

UART2_C3 |= UART_C3_FEIE;

as I mentioned before, my programming skills are very basic, so I dont really understand what those expressions actually mean :confused:

anyways, here is my modified code, I dont know if you have the patience and will to give it a read and tell me where am I messing up.

Thank you very much again for the help.

Code:
/* DmxReceiver - DMX Receiver for Teensy 3
   Copyright (c) 2014 Jim Paris
   Copyright (c) 2014 Ward

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

#include "mk20dx128.h"
#include "DmxReceiver_CrisEdit.h"
#include "HardwareSerial.h"

#define DMX_BUFFER_SIZE 513

#ifndef UART_C3_FEIE
#define UART_C3_FEIE    (uint8_t)0x02   // Framing Error Interrupt Enable
#endif

static volatile uint8_t dmxBuffer1[DMX_BUFFER_SIZE];
static volatile uint8_t dmxBuffer2[DMX_BUFFER_SIZE];
static volatile uint8_t *activeBuffer;
static volatile uint8_t *inactiveBuffer;
static volatile uint16_t dmxBufferIndex;
static volatile unsigned int frameCount=0;
static volatile bool newFrame=false;
HardwareSerial Uart = HardwareSerial2();

void DmxReceiver::begin(void)
{
        // UART Initialization
        Uart.begin(250000);

        // Fire UART0 receive interrupt immediately after each byte received
        UART2_RWFIFO = 1;

        // Set error IRQ priority lower than that of the status IRQ,
        // so that the status IRQ receives any leftover bytes before
        // we detect and trigger a new frame.
        NVIC_SET_PRIORITY(IRQ_UART2_ERROR,
                          NVIC_GET_PRIORITY(IRQ_UART2_STATUS) + 1);

        // Enable UART0 interrupt on frame error and enable IRQ
        UART2_C3 |= UART_C3_FEIE;
        NVIC_ENABLE_IRQ(IRQ_UART2_ERROR);

        activeBuffer = dmxBuffer1;
        inactiveBuffer = dmxBuffer2;
}

void DmxReceiver::end(void)
{
        Uart.end();
        NVIC_DISABLE_IRQ(IRQ_UART2_ERROR);
}

void DmxReceiver::fill(uint8_t v)
{
        __disable_irq();
        dmxBuffer1[0] = 0;
        memset((void *)(dmxBuffer1 + 1), v, DMX_BUFFER_SIZE - 1);
        dmxBuffer2[0] = 0;
        memset((void *)(dmxBuffer2 + 1), v, DMX_BUFFER_SIZE - 1);
        __enable_irq();
}

void DmxReceiver::clear(void)
{
        fill(0);
}

unsigned int DmxReceiver::frameCount(void)
{
        return ::frameCount;
}

uint8_t DmxReceiver::getDimmer(uint16_t d)
{
        return inactiveBuffer[d];
}

int DmxReceiver::bufferService (void)
{
        __disable_irq(); //Prevents conflicts with the UART0 error ISR
        int available=Uart.available();
        int retval=available;
        while (available--)
        {
                activeBuffer[dmxBufferIndex]=Uart.read();
                if (dmxBufferIndex<(DMX_BUFFER_SIZE-1)) dmxBufferIndex++;
        }
        __enable_irq();
        return retval;
}

bool DmxReceiver::newFrame(void)
{
        if (::newFrame)
        {
                ::newFrame=false;
                return true;
        }
        return false;
}

// UART0 will throw a frame error on the DMX break pulse.  That's our
// cue to switch buffers and reset the index to zero
void uart2_error_isr(void)
{
        // On break, uart0_status_isr() will probably have already
        // fired and read the data buffer, clearing the framing error.
        // If for some reason it hasn't, make sure we consume the 0x00
        // byte that was received.
        if (UART2_S1 & UART_S1_FE)
                (void) UART2_D;

        // Ensure we've processed all the data that may still be sitting
        // in software buffers.
        DmxReceiver::bufferService();

        // Update frame count and swap buffers
        ::frameCount++;
        dmxBufferIndex = 0;
        if (activeBuffer == dmxBuffer1)
        {
                activeBuffer = dmxBuffer2;
                inactiveBuffer = dmxBuffer1;
        }
        else
        {
                activeBuffer = dmxBuffer1;
                inactiveBuffer = dmxBuffer2;
        }
        ::newFrame=true;
}
 
There is 2 lines in your code that have a UART0 but if I change that, it wont complie, telling me that those expressions have not been defined
That's strange. With Arduino 1.0.5 + Teensyduino 1.18, and with Arduino 1.6.4 + Teensyduino 1.23, your code compiles just fine (with those lines). Maybe try upgrading to the latest Teensyduino?

Besides that, I don't have any ideas, sorry. There may be differences in the way the UART0 and UART2 hardware is implemented in the chip that affects how we're using it here, but I don't have a test setup at the moment where I can look into that further.
 
Yeah, the code "as is" compiles fine, but if I change

if (UART2_S1 & UART_S1_FE)

to

if (UART2_S1 & UART2_S1_FE)

is when it wont compile... :(

well, thanks a lot for the help man, I don't know if Paul could have a little bit more insight as on what could be done, cause your library is awesome, and it would be sweet if it could be used with any of the 3 UART ports the Teensy 3.1 has.

Cheers
 
Breaks detected via framing errors do indeed return a zero in the data byte, however because DMX also requires knowledge of non-zero bytes from framing errors—for being able to detect bad data and throwing the frame away—a better solution might be to register some framing error handler that includes the data byte. But this feels like too special a case and not a flexible enough case for doing proper break detection by adding this error handler to the API. (In reference to Paul's question about an API addition.)

I'll also add that without using an extra pin to do RX signal edge detection, it's hard to detect longer breaks. The best I've been able to do is detect BREAK plus Mark after Break (MAB) time, together but not separate. In other words, if we just use the serial port, all we know is when the start of a break (having a zero data byte) occurred by subtracting one character time, and when the MAB ended by detecting a byte received and also subtracting one character time.

See the TeensyDMX project for some example code for all this. (Sorry for all the #defines. I'm attempting to avoid duplicate code for 7 serial ports.) Note that it uses its own ISRs and not the built-in serial API.
 
Yeah, the code "as is" compiles fine, but if I change

if (UART2_S1 & UART_S1_FE)

to

if (UART2_S1 & UART2_S1_FE)

is when it wont compile... :(
...

From a quick glance at the kinetis.h header it looks like this is just a constant used to check the indicated flag::
Code:
#define UART_S1_FE		0x02			//  Framing Error Flag

So the first line that compiles should be the correct one to check that flag where UART[#]_S1 just refers to // UART Status Register [#]
 
the uart register of the first field is different per UART port, however, the second field is common among all UART registers.

UART_S1_FE is valid for all status registers of UARTs, theyre all shifted the same way down the register map
 
Status
Not open for further replies.
Back
Top