Shop OBEX P1 Docs P2 Docs Learn Events
Timing a tight pause (<5us) in mainline while running an ISR with serial RX/TX — Parallax Forums

Timing a tight pause (<5us) in mainline while running an ISR with serial RX/TX

ZootZoot Posts: 2,227
edited 2009-01-29 02:50 in General Discussion
I'm hoping perhaps Bean or pjv or Peter Verkaik (or anyone) has comments, suggestions on a rework I'm doing to my own rework of the firmware for the Serial Inkjet Board (see http://forums.parallax.com/showthread.php?p=714455).

The original firmware is pretty much straight up SX/B, with SERIN and SEROUT used for RX/TX -- very "Stamp" style. I reworked that firmware to change the command set, baud, and to hold the serial (open true) line low while printing, so the host could use it as a "busy" signal. All fine.

However, I have printer-bot, and when it finds a piece of paper on the table, it prints on it. Also wonderful. However, what the firmware does not allow for is canceling a print once in progress -- thus if the paper runs out before the 'bot is done printing it prints on the table.

So, I am planning on porting the firmware to a more traditional mainline/isr approach with a state machine for the mainline. This will allow the host to comm. with the module even during printing (so the host can get updates, cancel print in progress, etc). Again, no problem with porting it (it's pretty simple, actually).

The ONLY rub is that there is a portion in the firmware where the two sets of nozzles are fired very close in time to each other -- about 5us apart. In the current firmware (SX28 @ 20MHZ) it's done with a PAUSEUS. Now that won't work here, because of the ISR. Secondly, I can't use a flag in the ISR to mark time that short, because under most circumstances the ISR (if it kicks in during the pause) will take 1-2us.

So, the only possible (kinda simple) approach I've come with is to have the mainline use a dedicated register for the equivalent of a "pauseus" counter. Then, the ISR can decrement this counter AS IT NEEDS TO if a pause is in progress. My idea is if it takes 100 instruction cycles at 20mhz to pause 5us, then if a portion of the ISR consumes 30 cycles, that portion would decrement the dedicated pauseus counter appropriately.

I.E. in pseudo code

Interrupt_Handler:
' bunch of rx code

' if rx code ran and:
' if mainline nozzle pause in progress:
SUB nozPauseCntr, #10 ' or whatever it would need to be depending on cycle count
' and prevent underflow

' bunch of tx code

' if mainline nozzle pause in progress and:
' if tx start code ran:
SUB nozPauseCntr, #30 ' or whatever it would need to be depending on cycle count
' and prevent underflow

' if tx bit code ran:
SUB nozPauseCntr, #20 ' or whatever it would need to be depending on cycle count
' and prevent underflow

'etc

RETURNINT


Main:

  MOV nozPauseCntr, #50
:wait
  DECSZ nozPauseCntr
  JMP :wait





Does that make sense? It's the only way I can think of to get reasonably accurate nozzle timing at those speeds without having to go to an ISR running over 300khz.

I guess I could also insert NOPs and the like to keep the ISR at a consistent time, and adjust the effective frequency of the mainline instructions (in SX/B) but that actually seems more "hacky" to me, esp. given that *most* of the time the nozzle pause will not actually be in use.

The rest of the timing in the app. is all in ms (or longer).

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST

1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php

Comments

  • BeanBean Posts: 8,129
    edited 2009-01-28 12:14
    The problem I see with the method you have above is, what if the nozPauseCntr is 1 after the subtraction ? The next section of code could take 20 or 30 cycles. That would make a pulse WAY overe 5uSec.

    Instead of holding the serial line low while printing, can you leave it high and let the host pull it low to stop printing ?

    The printing module could pull it low briefly when it is done printing to indicate to the host that it is finished.

    That is how I would handle it.

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ·The next time you need a hero don't look up in the sky...Look in the mirror.


    ·
  • ZootZoot Posts: 2,227
    edited 2009-01-28 14:01
    Yes, I see what you mean....

    The idea of the host being able to use the serial line to cancel printing crossed my mind, but:

    - I would still need an ISR or would need to check (manually) during phases of the entire print loop (some of which are I2C transactions) for the pin state change.

    - I wanted to also leverage the ability to fill the 64 byte print buffer for the next print *while the current print is in progress)

    - my host is a BS2, and the chances of missing a pin state change from the module are pretty high, and I wouldn't want to have main execution "waiting" on the print finished signal (that's the lease of it though).

    - I've been thinking about the idea of the host "canceling" a print in progress and immediately starting another while still rolling; this would let the 'bot print dynamic text in "real-time" depending on quickly changing conditions.

    In any case, someone PM'ed me last night suggesting the isrFlag delay trick... that won't work because the minimum delay I would get would be 3.5us or so with an ISR running 307khz, and I'd prefer to run at half that, plus the time could be off from 3.5us depending on when the RTCC rolled over before the flag is cleared/checked. But that got me thinking -- the driver basically runs like this:

    loop through chars
      fetch character
      fetch bitmap for character
      loop through columns of char
         prep hinoz map
         prep lonoz map
         fire hinoz
         pause 3us
         fire lonoz
         pause 100us-25ms (in my case this is never less than 7-15ms)
      next
    next
    
    



    The fire noz code is short, and the delay between whole column firings is relatively long, so I had the idea of clearing/checking an isrFlag right before firing the nozzles -- this would let me know the interrupt *just ran* and at an ISR rate of 153600 would give the mainline approx 6.49us or so to fire the nozzles, pause 3us, fire the other nozzles, before the next interrupt rolls around... that would add -- at most -- 6.5us to the 10ms pause after the column, which is negligible, but leave my 3us pause accurate AND take a minimum hit in the ISR.... e.g.

    FIRE_NOZL nozLo
    \CLR isrFlag ' clear flag
    \JNB isrFlag, $ ' wait till it's set, no longer than 6.49us from now
                        ' then we have ~6.49us of "uninterrupted" time (pun intended)
                       ' to take care of pause; about 120 instructions, which is plenty
       FIRE_NOZL nozLo
       \MOV __PARAM1, #30 ' 3us * 20mhz / 2 instructions
    :waithere
       \DECSZ __PARAM1  ' pause 3us
       \JMP :waithere
       FIRE_NOZL nozHi
    
    



    I *think* something like that would work... the ISR will be very brief -- just serial rx/tx and buffer handling, plus the flag and an ms timing counter.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • pjvpjv Posts: 1,903
    edited 2009-01-29 02:50
    Hi Zoot;

    The way I handle these kind of issues is to have a system that is non-blocking. Specifically, I use a very tiny multi-tasking co-operative RTOS that lets multiple programs run simultaneously. The restriction is that none of those programs may hog the processor, and each must release back to the RTOS quickly. Looping to generate a delay is an absolute no-no; the RTOS creates the delay, and then causes the program to carry on where it left off.

    The typical ticker I use is 1 or 2 uSec interrupt, so that is the resolution on can get. I am just learning SX/B and have yet to try a merge between SX/B and this multi tasking concept as I'm rather busy with a complicated project just now. What I see so far is that any of the standard SX/B commands that involve timing will have to be modified by you (readily accommodated by SX/B it would appear) to not geterate wait loops, but instead release back to the RTOS, and instead let that set the timing for you.

    You could study up on some of the examples listed in my previous SX contest contributions that describe how the RTOS works as well as code examples. It's all in assembler, but I believe you to be no starnger to that.

    Cheers,

    Peter (pjv)
Sign In or Register to comment.