Teensy(duino) support

296 views
Skip to first unread message

Scruff

unread,
Sep 3, 2009, 8:42:22 AM9/3/09
to DmxSimple
In DmxSimple.cpp (v3) is explicitly stated :

/* Produce an appropriate message to aid error reporting on
nonstandard
* platforms such as Teensy.
*/
#warning "DmxSimple does not support this CPU"

Is there a solid technical reason for this or is it just not supported
yet? Or just not going to be?
What if I'd like to extend your DmxSimple-Lib in order to support
Teensy or Teensy++?
How could that be done and what would need to be taken into
consideration?

Thanks for any feedback

Scruff.R

Peter Knight

unread,
Sep 3, 2009, 10:13:01 AM9/3/09
to DmxSimple
The Teensyduino is a completely different processor. The assumptions
DmxSimple makes are not valid on that platform. So many things break
that I would prefer to trap the error and return a more helpful
message.

Right now, I fail to see the value in porting, testing and long term
support
of Teensy. An Arduino or compatible is cheap, and supports off the
shelf
interface hardware. I know the Teensy has some advantages over Arduino
but that works both ways. And if you're doing a DMX project, buy or
build the
boards for which there is already proven tested software support.

I am open to persuasion though - if you can give me a convincing use
case that would show the value of porting DmxSimple, I am happy to
oblige. It would be even more convincing if you can provide me with
tested working diffs of the library and a single Teensy board to do
future
QA testing on.


Peter

Paul Stoffregen

unread,
Sep 3, 2009, 11:08:10 AM9/3/09
to dmxs...@googlegroups.com, Scruff
Hi, Paul here, the author of Teensyduino.

I reviewed the DmxSimple code briefly, though I don't actually have any
DMX hardware to test. But I believe it should be possible to make this
work on the Teensy++. Things would be harder on the Teensy, since it
has limited RAM and lacks Timer2.


As a first attempt, here are 3 simple things to tweak:


1: In DmxSimple.cpp at line 34, add checks for the Teensy++, like this:

/* 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_AT90USB646__) || defined(__AVR_AT90USB1286__)

The Teensy uses a modern chip with the same register names as the 168,
328p, etc.


2: In DmxSimple.cpp at line 71, add this to set the Timer 2 prescaler:

// 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
#endif
TIMER2_INTERRUPT_ENABLE();
}


3: Inside the Teensy core, which is in hardware/cores/teensy_serial,
copy the file pins_teensy.h to pins_arduino.h. DmxSimple.cpp expects to
include this header for macros like digitalPinToBitMask.

It seems I was overly cautious trying to avoid infringing on the Arduino
trademark when I decided to rename this file. In future versions of
Teensyduino I will restore the name to "pins_arduino.h" for libraries
that need pointers to the direct I/O registers. For now, please just
copy pins_teensy.h to pins_arduino.h.


Hope this makes it work. :-)


-Paul

Peter Knight

unread,
Sep 3, 2009, 12:04:36 PM9/3/09
to DmxSimple
Thanks for the great information Paul.

I've had a quick check through the Teensy data sheet - and yes, it
should be capable of supporting DmxSimple with the right
modifications. DmxSimple will need one timer interrupt - and Timer1 is
available. RAM is a problem, but DmxSimple supports smaller channel
buffers on low RAM platforms (the '168) already - see "DMX_SIZE".

For the Teensy++, fewer changes are needed - and everything Paul says
looks sensible.


So -

Is there a significant demand for DmxSimple on the Teensy/++?
Who is going to code and test it?
Long term, how can we do QA testing on Teensy for the future?


Peter

Scruff

unread,
Sep 5, 2009, 4:14:07 PM9/5/09
to DmxSimple
Hi Peter, hi Paul,

thanks for your quick responses and now I've been able to test Pauls
suggestions with a Teensy++.
It seems to work well, the only thing I had problems with, was to
change the dmxPin using the usePin function.
My setup code looked like this

int dmxPin = 12; // Teensy++ Port C2

void setup() // run once, when the sketch starts
{
DmxSimple.maxChannel(6); // only 6 channels on our device
DmxSimple.usePin(dmxPin);
}

But after this DmxSimple still used Port D3 (default pin 3 in
DmxSimple).
I looked into DmxSimple.cpp and for me it seems that in

void DmxSimpleClass::usePin(uint8_t pin) {
dmxPin = pin;
if (dmxStarted && (pin != dmxPin)) {
dmxEnd();
dmxBegin();
}
}

the second part of the if statement "(pin != dmxPin)" never can become
true since dmxPin is already set to pin in the preceding line, and for
that reason the new pin was never used.
After altering this code to

void DmxSimpleClass::usePin(uint8_t pin) {
bool restartRequired = dmxStarted;

if (restartRequired)
dmxEnd();

dmxPin = pin;

if (restartRequired)
dmxBegin();
}

things seem to work propperly now.

Thanks for you help to get my project running with DmxSimple and Teensy
++!
Sorry I can't say if there is or will be significant demand for
DmxSimple on Teensy, I think Paul (as the father of Teensy/Teensy++
and TeensyDuino) would be the only one to be able to estimate this.
But for Teensy++ I think things are pretty much the same as for the
bigger Arduino-CPUs.

Andy

PS:
The main reason for me to use Teensy rather than Arduino is the
possibility to create USB HIDs and so no driver installation is
required.
This does not play any role in this project though, since the device
will be running standalone. But I happened to have several Teensy(++)
laying around but no Arduinos ;-)

Peter Knight

unread,
Sep 5, 2009, 4:53:54 PM9/5/09
to DmxSimple
Brilliant work Scruff! Thanks particularly for spotting and fixing a
bug that has until now gone unreported.

I'm currently in the middle of another project ( http://twitpic.com/g463o
), but after I've got that tidied up, I'll roll those fixes into
DmxSimple, and look at future support.

Have fun with DMX. I'd love to see what you come up with!


Peter

Scruff

unread,
Sep 5, 2009, 8:41:26 PM9/5/09
to DmxSimple
My pleasure ;-)

I hope everything is going to work as I'd like it to.

My intention is to fix a Bluetooth module to my now working
(selfpowered) dmxTeensy++ to remote control my DMX devices via up to
seven Bluetooth enabled devices (e.g. notebook, PC, PDA, ...)
If everything goes to plan, I'd be happy to let you know.

Studying your code a bit more closely I came accross some statements
in you dmxWrite function. I might be awfully wrong, but is it possible
that there is a risk that in some rare circumstances not all channels
might be controlled correctly?

void dmxWrite(int channel, uint8_t value) {
if (!dmxStarted) dmxBegin();
if ((channel > 0) && (channel <= DMX_SIZE)) { // minor:
Why not check against dmxMax?
if (value<0) value=0; // minor:
How could (byte < 0) ever be true?
if (value>255) value=255; // minor:
How could (byte > 255) ever be true?
dmxMax = max((unsigned)channel, dmxMax); // possible
risk: Why set dmxMax at every dmxWrite attempt? There is a dedicated
function for that!
If
programm sets channels not in ascending order, higher channels might
not get set propperly!
dmxBuffer[channel-1] = value;
}
}


Since the ISR checks

dmxState++;
if (dmxState > dmxMax) {
dmxState = 0; // Send next frame
break;
}

the last channel number passed to dmxWrite will be the max channel the
ISR will ever run up to - or am I terribly wrong here? (such a thing
has happend to me before, you know ;-)

I have no comprehension yet of how frequently the ISR is interrupting
the program loop. If that is often enough, that between two dmxWrite
calls at least one full frame can be sent then this problem might
never occure.

But in case of a program like

cli(); // for some strange
reasons no interrupts allowed
dmxWrite(userEnteredChannel, userValue); // completely
arbitrary values don't allow to guess the order in which channels are
passed
dmxWrite(someCalculatedChannel, calcValue);
dmxWrire(randomChannel, rndValue);
sei(); // what will happen
after enabeling the interrupts?

or when channels are set within an ISR (when an interrupt occures the
global interrupt flag is cleared automatically), that problem might be
quite difficult to track down. Sometimes things work, sometimes
not?!?!?

I hope this was no complete nonsense and might help to further improve
you great work - that asm code and its down to single cycle timing is
cool stuff!


Keep it up

Andy

Peter Knight

unread,
Sep 6, 2009, 7:10:37 AM9/6/09
to DmxSimple
Thanks for the extensive code review. Much appreciated. Comments below

On Sep 6, 1:41 am, Scruff <scruf...@sbg.at> wrote:
>
> void dmxWrite(int channel, uint8_t value) {
>   if (!dmxStarted) dmxBegin();
>   if ((channel > 0) && (channel <= DMX_SIZE)) {           // minor:
> Why not check against dmxMax?
>     if (value<0) value=0;                                 // minor:
> How could (byte < 0) ever be true?
>     if (value>255) value=255;                             // minor:
> How could (byte > 255) ever be true?
>     dmxMax = max((unsigned)channel, dmxMax);              // possible
> risk: Why set dmxMax at every dmxWrite attempt? There is a dedicated
> function for that!
>                                                                   If
> programm sets channels not in ascending order, higher channels might
> not get set propperly!
>     dmxBuffer[channel-1] = value;
>   }
>
> }

Two issues here.
1) I'm trying to encourage bounds checking, but I appreciate the 0 to
255 check is pointless. I'm expecting GCC to optimise those down to
nothing. I don't want people to see no bounds checking and presume its
not there - when actually it is implicit in the uint8_t parameter.
2) The idea with DmxSimple is to provide an extremely simple API. You
can do almost anything you will ever need to do with just
DmxSimple.write(). That does have a cost - every call checks the
DmxSimple initialisation flag, dmxMax is grown as channels are
written. But those costs are pretty insignificant in time and ROM, and
the one-call API keeps things very beginner friendly. A lot of the
standard Arduino libraries do similar things.

dmxBuffer is always set to the maximum number of channels. All dmxMax
does is trade off fewer transmitted channels for a higher update
frequency.

> Since the ISR checks
>
>     dmxState++;
>     if (dmxState > dmxMax) {
>       dmxState = 0; // Send next frame
>       break;
>     }
>
> the last channel number passed to dmxWrite will be the max channel the
> ISR will ever run up to - or am I terribly wrong here? (such a thing
> has happend to me before, you know ;-)

You've missed the max function: dmxMax = max((unsigned)channel,
dmxMax);
So dmxMax will grow until it finds the highest write() channel, then
it will stay there. It might send a short frame or two along the way,
but the DMX standard permits abbreviated frames.

> I have no comprehension yet of how frequently the ISR is interrupting
> the program loop. If that is often enough, that between two dmxWrite
> calls at least one full frame can be sent then this problem might
> never occure.

DmxSimple uses timeslots. There isn't enough time between interrupts
to send a complete DMX frame, so the ISR is given a time 'budget' to
spend. It will send as much as it can while remaining under budget. It
will only stop after a break or channel stop bits, as DMX allows these
timings to be much more flexible than at other times.

I used to send the frame in one single chunk - but splitting it up
like this is much less troublesome to the foreground process, and is
friendly to other interrupt users.

> But in case of a program like
>
>   cli();                                        // for some strange
> reasons no interrupts allowed
>   dmxWrite(userEnteredChannel, userValue);       // completely
> arbitrary values don't allow to guess the order in which channels are
> passed

dmxMax is set to userValue

>   dmxWrite(someCalculatedChannel, calcValue);

dmxMax is set to maximum of (userValue, calcValue)

>   dmxWrire(randomChannel, rndValue);

dmxMax is set to maximum of (userValue, calcValue, rndValue)

>   sei();                                      // what will happen
> after enabeling the interrupts?

dmxMax has the correct value - the highest channel used in the frame.
dmxMax is only ever written by the foreground, and only ever read by
the ISR. There is potentially a problem - where dmxMax is updated from
0x5 to 0x101 - where the interrupt occurs between the low and the high
byte write. But bear in mind that short frames and over-long frames
are both handled sensibly by DMX, and the worst that can happen is
data output is delayed until the following frame.

I hope that clears things up.


Peter

Scruff

unread,
Sep 6, 2009, 9:12:06 AM9/6/09
to DmxSimple
Thanks Peter,

for that prompt clarification!
In deed I obviously have missed the correct meaning of the max()-
function.

Sorry for that ;-)

Andy
Reply all
Reply to author
Forward
0 new messages