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