Trying to decide to use Interval timer or polling in my main loop...

Status
Not open for further replies.

KurtE

Senior Member+
Currently I am playing around with my Teensy Bioloid Servo controller code and I am migrating the servo interpolation code from the host code into the Servo controller code...

Basically every N milliseconds (example 20), the code walks through the different servo locations and adds (or subtracts) a delta to the current position and then we output a SYNC_WRITE command over the serial port(1mbs). Example case is 3dof hexapod with maybe a pan/tilt. So 20 servos. If all servos are moving, the SYNC_WRITE command would be something like:

0xff 0xff <id> <length> <SYNC_WRITE> <2 bytes per servo><id 0><new pos 0 L> <new pos 0 H> ... <id servo 20> <new pos 20 L><new pos 20 H><checksum>.

So talking about output of 20*3+7 or 67 bytes...

Right now in my Teensy code base I am trying to decide if this will work within an Interval Timer, as I believe that the interval timer runs as an ISR. In the majority of cases, my packets will be under the default size for Serial1 tx buffer, and as such the Serial1.write calls will simply copy the data into the buffer. However not sure what happens if I fill the output buffer within the Interval timer ISR...

So I will probably answer my own question. Should I instead just do this in the main loop? Simply check to see if the appropriate time has expired and call off to do the interpolation. My guess is it is safer to go this route.

Note: My main hexapod currently only has Pan servo (removed tilt), so the actual SYNC_WRITE would be 64 bytes if all servos are moving.

Thoughts?

Thanks
Kurt
 
Well I'm absolutely no expert on this, but I think you are right in keeping the ISR as short as possible. Just set a global flag and do the serial communication in the main loop:
if (syncFlag==1) whatever();
If your sync commands get longer or you change platforms to a µC with a smaller buffer you don't have to rewrite your code.
 
In the majority of cases, my packets will be under the default size for Serial1 tx buffer, and as such the Serial1.write calls will simply copy the data into the buffer. However not sure what happens if I fill the output buffer within the Interval timer ISR...

If you use Serial1.write() for more data than will fit into the buffer memory, it will wait until the buffer has space. Normally the Serial1 interrupt has a higher priority, so its interrupt will (probably) be able to work if your IntervalTimer interrupt is the normal priority. If you call Serial1.write() from within an interrupt that's at the same or higher priority and try to write too much, the write() code tries to detect this condition and manipulate the hardware directly with a busy loop which waits. Either way, Serial1.write() wait until all the data fits into the buffer.
 
There's an old technique that might be useful here. Pseudo code here:

* Define an "I'm already interpolating" count (IAIC !), set to 0

ISR entry:
* Interrupts remain disabled
* Do what you need to do
* Increment your IAIC.
* If it's now greater than 2, reset it to 2. You're not keeping up with
your interpolations. If this were comms handling I'd suggest letting
the count go higher than 2, but you can afford to miss an interpolation
* If it's now 2, return from the interrupt (This interrupt interrupted
your interpolation)
* Unmask interrupts (so the ISR can happen again) and enter your interpolation code

Interpolation Code (operates at a priority level just below the ISRs)
* Do your interpolation
* Mask interrupts
* Decrement the IAIC
* If the IAIC is now zero, return from the original interrupt
* Unmask interrupts and loop, doing another interpolation
Note: Your interpolation code:
- Will run as an ISR, but will be interrupt-able by subsequent interrupts
- Will use a bit extra stack space
- Must not make any calls that might already be in progress from the loop().
- If your interpolation code can't get caught up, loop() processing will never resume.
- Shouldn't try to give the cpu back to the loop() if it's waiting for
something. If the tx queue is full, just spin on it.

I hope this makes sense! Historically it's a great way for a microprocesssor
to handle bursty processing loads.
 
I was just looking at the 3.2 specs and see "16 DMA channels". If the uart you're using can be driven by a dma channel your problems are solved. Just construct the msg and start the DMA. DMA has always been my friend! Further research is needed, but you shouldn't need to be worrying about Tx buffer sizes.

And, given the amount of ram that's available, making the tx buffer(s) bigger is not unreasonable...

[Further research results]
1) The Uart Control Register 5's have TDMAS & RDMAS bits that are used into enable/disable Tx/Rx DMA. So we know UART DMA exists on the CPU.
2) DMA channels must know the length of the Tx and Rx frame. Since we construct the frame, and we know how many bytes to expect back...
3) It seems we can arrange to have an interrupt only when the Tx completes, and another only when the Rx completes.

:) I am officially happy! This is a VERY nice processor!
 
Last edited:
Yeah, DMA works great when you have large, fixed size messages.

For small messages, it tends to be quite a lot of extra steps. For variable length messages, where something like a newline or carriage return indicates the end of a line, it's usually not so useful. You generally need to know the number of bytes ahead of time to make really effective use of the DMA channels.
 
Thanks guys,

The messages are sort-of random in size, they have a specific format: <ff> <ff> <id><len>....<chksum>
In the case where I am handling background interpolation, I can know calculate the actual output message size ahead of time and in theory I can guess how many bytes will come back (unless error).

For example: I know that with a SYNC_WRITE packet, I Will not receive a return packet.
If I do a WRITE packet, I will receive a fixed size return status packet, Unless the ID is not found (no packet) or the Servo has it's response setting saying I wont send you a response...

A Read packet: With a valid request, you can calculate the size of the return packet since the request has starting register number and count, BUT if error, you may get the small status packet. Or again if ID is not found or if ID has response setting set to 0, then no return packet.

For now I may punt and try it with normal Serial
 
I'm with you! The manual is pretty specific that you need to know the I/O length.

In Kurt's hexapod application (mine as well, but with different goals), he's using a 1Mb data channel, sending & receiving a fairly short async message 30 times a second. The interrupt rate is brutal - 100k bytes/second (I haven't looked at fifo depths or possible of fifo range-interrupts yet). The message lengths (Tx & Rx) are known. It occurred to me that if we DMA both sides of the exchange we'd be getting roughly 60 interrupts a second. Maybe 120 if interval timers are used to handle send/receive timing requirements.

I've also been thinking about this approach with serial I/O as well. For the 1st few bytes you could just use the Tx complete interrupts. As the application fills the Tx send buffer you could switch over to dma-driven data transfers. I'm not sure it's worth the coding effort considering the low data rate.. And for the Rx side I don't see any way to use DMA.
 
Actually with Serial1 and Serial 2 you have a nice 8 entry fifo Queue, so I believe it cuts down on interrupts a reasonable amount. I actually try to run it a 2+Mb rate and maybe 100 frames per second.

At some point I may try hacking a little more on the Serial objects to try to do some of the stuff we did for the standard Arduino code and recently with T2 code, where the Serial.write function first tests to see if the queue is empty and there is room in the FIFO (or output register on other Serial ports) and output it directly, instead of putting it into the queue and enabling the TIE interupt...

But to do so may need to make sure to handle some possible race conditions. i.e. properly handling the busy setting as well as the Transmitter direction IO pin if appropriate.
 
Status
Not open for further replies.
Back
Top