DT-Ints latency and other interrupt conciderations


Closed Thread
Page 1 of 2 12 LastLast
Results 1 to 40 of 59
  1. #1
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520

    Default DT-Ints latency and other interrupt conciderations

    Hi everybody,
    (I appologise in advance for this lenghty post but I'll try to give as much info as I can up front).

    I'm working on a DC servomotor drive using my incPID routine and DT-Ints on an 18F2431 running at 20Mhz. The input to the drive is step and direction signals, the step-pulse is connected to INT2 and trips a high priority interrupt, the direction signal to PortB.1. Originally I had the following code in the INT2 ISR:
    Code:
    DoStep:
    If PortB.1 = 1 THEN         'Direction signal connected to PortB.1
     Setpoint = Setpoint + 1    'Setpoint is a 32bit variable
    ELSE
     Setpoint = Setpoint - 1
    ENDIF
    This worked fine and I measured the execution time of ISR to 2-2.4uS depending on if the add or subtract code is executed - not to shabby. The problem was that it took 29uS from the rising edge of the step-pulse to the point where the ISR starts to execute. During testing I found that the code tracked the step-input up to 15kHz but above that it started to miss pulses. In practice 10kHz proved to be the limit, above that too much time was taken away from the PID-routine (which is triggered as a low priority interrupt by TMR2 at 1220Hz) resulting in an unstable servo loop.

    So, with my VERY limited ASM skills I set out to change the DoStep ISR from PBP to ASM. I decided to not do the 32bit operations in the ISR and instead went to an 8bit "buffer variable". This not only made it possible for me to code the ISR but it'll also allow me to add features like electronic gearing etc in the future. The ASM ISR now looks like this:
    Code:
    ASM
    DoStep
       BSF PORTB,6        ;For timing purposes
       BTFSS PORTB,1      ;Check direction signal
       INCF _StepBuffer   ;Increase
       BTFSC PORTB,1      ;Check direction signal
       DECF _StepBuffer   ;Decrease
       BCF PORTB,6
       INT_RETURN
    ENDASM
    This alone dropped the latency from 29uS to 8.4uS and the ISR execution time to 1uS.

    Is there anything more that can be done to...
    A) Lower the latency further and...
    B) improve my pathetic attempt at ASM coding above?


    In the servo-loop ISR I then have the following code:
    Code:
    INTCON3.4 = 0                'Temporarily disable INT2 interrupt
    TempStepBuffer = StepBuffer  'How many steps has arrived?
    StepBuffer= 0                'Reset buffer
    INTCON3.4 = 1                'Re-enable INT2 interrupt
    The new target position is then calculated by adding or subtracting the ABS value of TempStepBuffer from the current target position.

    Now I have reliable operation up to ~20kHz step frequency but above that it occasionally misses pulses. It can run at 25-30kHz for several minutes without missing a pulse but all of a sudden it does. The pulsetrain is generated by software on a PC so depending on the actual frequency the jitter can be more or less severe which may explain the occasional lost pulse. Never the less, I'd like it to perform better than this.... (100kHz preferably)

    Apart from the DoStep ISR there's one more high priority interrupt (IC2QEIF_INT) which is fired when the 16 bit QEI counter rolls over. That ISR is still in PBP and looks like this:
    Code:
    RollOver:
    IF QEICON.5 = 1 then
      Position.Word1 = Position.Word1 + 1    'Position is a LONG
    ELSE
      Position.Word1 = Position.Word1 - 1
    ENDIF
    @  INT_RETURN
    Can some kind person more knowlagable about ASM than me please show me how the RollOver ISR above could look in ASM?

    How many cycles does it take for DT-Ints to "get out of" an ASM ISR?

    Any other ideas what I can do to increase the performance? (Except going to 40MHz which will be done).

    Again, sorry for the long post and many questions.

    Thanks!
    /Henrik.

  2. #2
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Hi Henrik,

    It does take a lot of time to save/restore all of PBP's System variables.
    So, PBP type interrupt handlers are not too good at high frequencies.

    Fortunately, both of your interrupt routines aren't doing anything that requires System variables. And you can just change their "Type" to ASM, without actually writing them in ASM.

    And your ASM routine would only count 8-bits, so I think it might be a bit limiting.

    IF statements with "=", and simple addition/subtraction doesn't use PBP's system vars, so most interrupts that only need to count something can usually be written as PBP, then used as ASM.

    HTH,
    DT

  3. #3
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi Darrel,
    Many thanks, it does help a lot!

    So, can I assume that it takes roughly the same time to "get out of" an ASM interupt as it takes to enter it, in this case 8.4uS? Then, theoretically, the max frequency would be 1/(2*8.4+1uS) = 56kHz - if all it had to do was servicing the INT2 interrupt - which unfortunately still isn't good enough :-( Even going to 40Mhz just barely gets me over the 100kHz target with not much time left to do anything.

    I know the 8bit buffer variable may "look" limiting but because it is "read" by the servo-loop, which is currently run at 1220Hz, it means the actual limit would be ~155kHz - if I can get that I'd be happy. The only concern I have with this approach is that I need to disable the INT2 interrupt while reading and resetting the buffer. But if I'm not mistaken if an interrupt occurs during this time it will get "latched" and serviced as soon as I re-enable it again.

    Thanks again Darrel! I'll give it a go during the day and see what I can get.

    /Henrik.

    PS. I have a comercially available drive that I bought which also uses the 18F2431, it has a claimed step-frequency of 400kHz - I wonder how they do it....

  4. #4
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Well let's see what we can do here ...

    Since it's an 18F, with "qualified" ASM handlers ...
    You can remove the remaining overhead by removing DT_INTS all together, and using an ASM ISR, written in PBP of course.

    Something like this ...
    Code:
    DEFINE INTHAND _ISR
    DEFINE NO_CLRWDT 1
    
    ;---------------------------------------------------------------------------
    ISR:
      IF INTCON3.1 THEN             ' IF INT2 triggered
    DoStep:
        If PortB.1 = 1 THEN         'Direction signal connected to PortB.1
          Setpoint = Setpoint + 1    'Setpoint is a 32bit variable
        ELSE
          Setpoint = Setpoint - 1
        ENDIF
        INTCON3.1 = 0               ' clear the int flag
      ENDIF
        
    RollOver:
      IF PIR3.2 = 1 THEN            ' if IC2QEIF triggered
        IF QEICON.5 = 1 then
          Position.Word1 = Position.Word1 + 1    'Position is a LONG
        ELSE
          Position.Word1 = Position.Word1 - 1
        ENDIF
        PIR3.2 = 0                  ' clear the int flag
      ENDIF
    @   RETFIE FAST                 ; return from interrupt with shadow regs
    Setpoint is handled as a LONG, but if you can use a BYTE sized result, you can change it to Setpoint.byte0 to reduce it by a few instructions.

    The NO_CLRWDT drops a couple more instructions, but make sure the WDT is OFF.

    Compiled, it looks like this, including both handlers ...
    Code:
    _ISR
            btfss   INTCON3, 001h
            goto    _RollOver
    _DoStep
            btfss   PORTB, 001h
            goto    L00003
            incf    _Setpoint, F
            clrf    WREG
            addwfc  (_Setpoint) + 1, F
            addwfc  (_Setpoint) + 2, F
            addwfc  (_Setpoint) + 3, F
            goto    L00004
    L00003
            decf    _Setpoint, F
            clrf    WREG
            subwfb  (_Setpoint) + 1, F
            subwfb  (_Setpoint) + 2, F
            subwfb  (_Setpoint) + 3, F
    L00004
            bcf     INTCON3, 001h
    _RollOver
            btfss   PIR3,  002h
            goto    L00005
            btfss   QEICON, 005h
            goto    L00007
            incf    _Position??WORD1, F
            movlw   0
            addwfc  (_Position??WORD1) + 1, F
            goto    L00008
    L00007
            decf    _Position??WORD1, F
            movlw   0
            subwfb  (_Position??WORD1) + 1, F
    L00008
            bcf     PIR3,   002h
    L00005
        RETFIE FAST                 ; return from interrupt with shadow regs
    Hope that gets it closer.
    DT

  5. #5
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Thanks Darrel,
    Now I'm a bit confused.... Will this allow me to still use DT-Ints for the low-priority interrupts? The actual servo-loop code is, as I said earlier, triggered as a low priority interrupt by TMR2 and I also have a second low priority interrupt for handling USART comms (which isn't used during the tests I've made so far).

    Does the RETFIE FAST automatically restore the STATUS, W etc registers or is that not needed due to the "shadow regs"? As you can see I really suck at the low level stuff...

    Before seeing your message I tried changing the RollOver ISR to type ASM but it didn't make much difference. Up to 20kHz it works fine, above that it starts to fall apart and I can't really see why.

    Your assistance is much apprecited, thanks!

    /Henrik.

  6. #6
    Join Date
    Feb 2005
    Location
    Kolkata-India
    Posts
    563


    Did you find this post helpful? Yes | No

    Default Shadow Fight !!

    Hi,

    Darrel correct me if I am wrong, I am a bit rusty here.

    The shadow registers are not memory mapped but are actually hardware registers, a single level deep hardware stack that can save your context (W,Status, BSR). Optional during <b>call, something s , </b> or a <b>return, something s , </b> and automatic when entering interrupt. You decide whether you want to restore them by Retfie Fast . So when a low priority interrupt (0x0018h) is interrupted by a high priority one, the context saved while entering the low priority is lost. Thus it works best if you are using only HP Ints or using it for only HP Ints.
    Regards

    Sougata

  7. #7
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi again,
    I'll start by quoting myself:
    Before seeing your message I tried changing the RollOver ISR to type ASM but it didn't make much difference. Up to 20kHz it works fine, above that it starts to fall apart and I can't really see why.
    Correction/clarification somewhere between 17.5-18kHz something happens which makes the TMR2 LP interrupt frequency start to drop. I guess the HP priority interrupts are stealing so much time that the TMR2 interrupts "runs over itself".

    At 32kHz the high priority DoStep ISR still tracks the step pulses correctly but now the TMR2 ISR is only run at ~550Hz instead of 1220Hz which can be both heard and felt from the motor. Going higher than this (I tried 33kHz) completely unstabilizes the servo-loop so I don't know how high the INT2 interrupt will actually track the pulses.

    /Henrik.

  8. #8
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    I think that both INT2 and TMR2 interrupts should be HIGH priority.
    The USART can be LOW priority, since servicing it is not really time critical.
    But no, you won't be able to use DT_INTS for just the Low Priority.

    Sougata is correct about the shadow registers...
    They are only good for High Priority interrupts.
    For Low priority ints you have to save W, STATUS and BSR in the ISR.

    What are you doing with the USART handler?
    Stuffing the bytes in a buffer?
    <br>
    DT

  9. #9
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi,
    At the moment I'm not doing anything with the USART interrupt - it's never been fired during these tests. But I'll need to use it for the "front end" later on - if I get there - and the is to stuff data into a buffer and signal the main program when a LF or CR arrives.

    Just so we're all on the same page, here are the current interupts:
    INT2_INT - High priority ISR in ASM
    IC2QEIF_INT - High priority ISR in PBP but "declared" as ASM in the INT_LIST macro
    TMR2_INT - Low priroity ISR in PBP
    RX_INT - Low priotity ISR in PBP


    I originally had the TMR2 interrupt as high priority but then I didn't get more than a few kHz before it started to miss pulses. The DoServo routine that is run as the TMR2 ISR is quite "heavy" since it does all the math including the PID routine:
    Code:
    DoServo:                                    'Interrupt handler starts here.
    PortB.5 = 1                                 'Used for timing purposes
    GetPosition:
    'Since the position counter isn't double buffered we can get to situations
    'where the lowbyte overflows between readings of the high and low byte. This
    'is prevented by reading the high byte two times and compare the two readings.
    'If they are the same then we're fine, if not re-read the registers.
      
        PosHigh = POSCNTH                       'Get high byte of counter
        PosLow = POSCNTL                        'Get low byte of counter
        PosTemp = POSCNTH                       'Get high byte again
        
        If PosHigh - PosTemp = 0 then Goto Done 'Compare, if equal we're done.
        
        PosHigh = POSCNTH                       'If not, get values again.
        PosLow = POSCNTL
    
    Done:  
    'The high word of the Position variable is handled by the RollOver ISR that gets
    'tripped by IC2QEIF when POSCNT = MAXCNT.
        
        Position.Word0 = POSHIGH * 256 + PosLow 'Put high and lowbyte together.
        Speed = Position - oldPosition          'Calculate current motor speed.
    
        INTCON3.4 = 0                           'Temporarily disable INT2 interrupt
        TempStepBuffer = StepBuffer
        StepBuffer = 0
        INTCON3.4 = 1                           'Re-enable INT2 interrupt.
    
        If TempStepBuffer.7 = 1 then            'Step buffer is negative
         Setpoint = Setpoint - ABS (TempStepBuffer)
        Else
         SetPoint = Setpoint + TempStepBuffer
        Endif
        pid_Error = Setpoint - Position          'Calculate error....
        
    'Now we have our error-signal in the variable that the PID-routine expects
    'so we call the PID-filter with a GOSUB. The filter will respond with the
    'output in variable pid_out.
    
        Gosub PID                               'and send to PID filter
    ' (PID filter execution time measured to ~190uS worst case @20Mhz)
     
        If pid_out.15 = 0 then                  'If the output from the pid-filter
           Direction = 1                        'is positive we want to drive the
           Duty = pid_Out                       'motor forward.
        Else
           Direction = 0                        'If it's negative we want to drive
           Duty = ABS (pid_Out)                 'it backwards.
        Endif
    
        CCP1CON.5 = Duty.1                      'Set the two LSB's of the dutycycle-
        CCP1CON.4 = Duty.0                      'register. Then shift them out and
        CCPR1L = Duty >> 2                      'set the remainig 8 bits.
        
        oldPosition = Position                  'Store position for next interrupt.
    
    ISRCount = ISRCount + 1
    PortB.5 = 0
    
    @  INT_RETURN                               ;ISR is complete, return from here.
    The actual PID code isn't shown here, I've measured the execution time of the complete DoServo ISR and it seems to vary alot, from 160uS all the way up to 400uS.

    So, I guess I'll need to stick with DT-Ints since re-writing the DoServo routine including the PID code in ASM would take me years. Question is what else, if anything, can be improved to increase the speed. The thing is that I was hoping to add more code to the DoServo routine but I guess that may not be a good idea.

    Thanks alot guys!

    /Henrik.

  10. #10
    Join Date
    Feb 2005
    Location
    Kolkata-India
    Posts
    563


    Did you find this post helpful? Yes | No

    Default Any possibilities of checking INTs within INTs

    Henrik,

    I wonder if there could be any hardware optimizations however I would just like to remind you:

    Even if an Interrupt is Enabled (IE bit), the Flag (IF bit) always gets set. So it might just be possible to check within an interrupt that if other sources triggered. This enables you to pre-service that interrupt without a return then re-entry. This can save a lot of cycles with the penalty of a bit more code space.
    Regards

    Sougata

  11. #11
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    I may be continuing to miss the point, but ...

    I still think TMR2 should be a high priority interrupt.
    Except, it should be ASM, and ONLY collect the data required to calculate the error.

    The PID and motor control should be running in the main loop, using the error info from the interrupt.

    If the PID happens to miss an update, it won't matter that much because the error will still be updated the next time. And it's not like the PID loop can cancel the error on each update. It takes time. But at least the actual error measurements will be consistent at much higher frequencies.

    Obviously, the "I" time period won't be constant, but I think they'll be close enough that you won't notice a difference. IMHO
    Then the PID loop can run at it's maximum rate, while the interrupts run at their maximum rate, without interfering with each other.
    <br>
    DT

  12. #12
    Join Date
    Feb 2005
    Location
    Kolkata-India
    Posts
    563


    Did you find this post helpful? Yes | No

    Default Agree to that

    Quote Originally Posted by Darrel Taylor View Post
    I may be continuing to miss the point, but ...

    I still think TMR2 should be a high priority interrupt.
    Except, it should be ASM, and ONLY collect the data required to calculate the error.

    The PID and motor control should be running in the main loop, using the error info from the interrupt.

    <br>
    I did not realize that the PID routine was running within the ISR. Henrik is it necessary ?? A better way could be accumulating error in the back (ISR) while processing error in main loop. I did tried your PID loop once but never closely worked on it. So excuse my ignorance if I am talking something analogue (illogical!!)
    Regards

    Sougata

  13. #13
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi,
    Yes the PID is currently run as part of the 1220Hz TMR2 ISR because I've always been taught that digital PID systems needs to be run at constant intervals and I can definetly see why that is neccessary.

    You are correct that the important thing is to sample the data at precise intervals - if that's not done the effect will be the same as constantly changing the gains up and down. Or it will "look like" the speed of the motor isn't constant even though it actually is because the number of encoder counts per update is changing.

    On the other hand, if the output of the filter isn't updated at constant intervals the motor doesn't have the same time to respond to input which is also the same as changing the gains but it might not be as bad as not sampling regularly. So, this is essentially why the PID routine is run inside the TMR2 ISR.

    However, it's definitely worth a try to move it to the main routine and then have the TMR2 ISR signaling the main routine that new data is available, I'll try that! That would mean that the TMR2 ISR wouldn't (hopefully) contain any code that requires the PBP system variables so I'll be able to change its type to ASM although it's coded in PBP.


    As a side note, I'm wondering what in the DoServo code it is that takes so much time. The PID code alone was measured to 190uS worsy case and it contains far more math than the rest of the DoServo code. Yet the complete DoServo routine sometimes takes 400uS, without it being interrupted. The 190uS was measured when compiled with PBP though, not PBPL but the PID code doesn't contain ANY operations on LONGS, does it matter?

    Sorry, now I'm asking queastion that my scope can answer.... Thank you both!

    /Henrik.

  14. #14
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    I believe that motor will not feel the difference ifthe PID routine is interrupted for a small fraction of time (in the order of microseconds).

    The inertia is large enough even for small motors.

    As Darrel always says, keep the ISR as fast as possible and return to your main routine for the processing. That way your PID will still run fast.

    Ioannis

  15. #15
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi,
    I agree, generally. BUT the "few uS" quickly add up... The servo code, including the PID, is supposed to run at 1220Hz and currently takes up to 400uS from "input to output". Let's say we're stepping at 50kHz and each interrupt takes 20uS. This means that during the 400uS it takes the servo code to execute there will be 20 interrupts, each taking 18uS for a total of another 360uS - the servo code now takes 760uS from "input to output".

    However, the numbers will be the same no matter "where" the servo code is run - it's just a matter of finding which works the best. One benefit of moving it to the main routine is that it makes it at least possible for me to have the ISRs in "real" ASM which will help bring the latency down further. Don't know how I'll handle the USART though.... I love DT-Ints.... :-/

    Thanks!
    /Henrik.

  16. #16
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi,
    Still messing around with this.... I changed the TMR2 interrupt to high priority and its type (as "declared" in the INT-List) to ASM but kept the code in PBP and moved the PID and PWM code to the main routine. The INT2 ISR now executes in 14uS. A semaphore signals the main routine to run the PID etc.

    It works but there's no noticable increase in performance. At 10kHz the motor moves smoothly and silently, at 22.5kHz it's a lot more nervous and you can hear it kind of "ticking".

    So I stopped playing with that for a while and instead tried to find what it is that eats up the CPU cycles in the servo-code and not surprisingly the PID-code is the biggest hog. What I find strange though is that when it's compiled with PBP 2.6 it runs in 40-125uS but when compiled with PBPL it takes 80-325uS to execute - quite a difference.

    Why does it take more than twice the number of cycles to execute when compiled with PBPL? The PID code itself doesn't even use any longs.

    Thanks!
    /Henrik.

  17. #17
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    With PBPL, all internal System and T? variables are LONGs.

    All multiplications and divisions are done as Longs, even if they are byte vars.
    Most things that get put in a System var with a CALL to the library are done as a Long.

    Code:
    IF ByteVar1 >= ByteVar2 THEN             ; <-- Long 
    IF ByteVar1 = ByteVar2 THEN              ; <-- Byte
    WordVar = WordVar /* Const               ; <-- Long
    ByteVar1 = ByteVar1 + Const              ; <-- Byte
    ByteVar1 = ByteVar1 + (ByteVar2 >> 2)    ; <-- Long
    ByteVar1 = ByteVar1 + (ByteVar2 >> 1)    ; <-- Byte
    Having 32-bit vars is nice, ... unless you need speed.

    Is there a particular reason you need PBPL?
    Maybe we can cut that down to word sized math so you can go back to PBPW.
    <br>
    Last edited by Darrel Taylor; - 1st January 2010 at 21:26. Reason: added
    DT

  18. #18
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Thanks Darrel,
    That explains it for sure - but it kind of sucks.... :-/

    Basically, the Position and the Setpoint variables are the only longs and the reason for PBPL.

    The setpoint is calculated by adding or subtracting the number of steps that has arrived since the last servoloop. For the position I get the lower 16bits from the hardware QEI counter and the upper 16bits by keeping track of the number of over/underflows with the RollOver ISR.

    Man, I'm painting myself into a corner here....

    BTW, how come these two...
    Code:
    ByteVar1 = ByteVar1 + (ByteVar2 >> 2)    ; <-- Long
    ByteVar1 = ByteVar1 + (ByteVar2 >> 1)    ; <-- Byte
    ...are treated different?

    Thanks for all the help, I appreciate it!

    /Henrik.

  19. #19
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by HenrikOlsson View Post
    Basically, the Position and the Setpoint variables are the only longs and the reason for PBPL.

    The setpoint is calculated by adding or subtracting the number of steps that has arrived since the last servoloop. For the position I get the lower 16bits from the hardware QEI counter and the upper 16bits by keeping track of the number of over/underflows with the RollOver ISR.
    If that's all there is ... it's a piece of cake for PBPW.

    Man, I'm painting myself into a corner here....
    I think not.

    BTW, how come these two...
    Code:
    ByteVar1 = ByteVar1 + (ByteVar2 >> 2)    ; <-- Long
    ByteVar1 = ByteVar1 + (ByteVar2 >> 1)    ; <-- Byte
    ...are treated different?
    A single shift right compiles to a
    Code:
        BCF STATUS,C
        RRF _ByteVar2,F
    or, RRNCF for an 18F.

    But for 2 or more shifts, it puts the value in a system reg, saves the number of bits to shift, then calls a library routine to actually do the shift. Therefore ... LONG.

    Let me try to figure out the PBPW way. (should be easy)
    I think all the info is already in this thread.
    Anything missing that might help?
    <br>
    DT

  20. #20
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Thanks Darrel,

    At the Piclist I found some signed 32bit math routines. I copied the add, subtract and the needed support routines (addba and negateb) as add and subtract is the only 32bit ops I need (at the moment at least).

    After some trial and error I managed to get these working on the 18F2431 by replacing the skpnc with btfsc, STATUS, C and clrc with bcf STATUS, C. I also made it so that the REGA, REGB and MTEMP variables are declared as PBP variables and then preceeded by an underscore in the actual ASM code. I saved it as a separate file and INCLUDE it in the program together with DT-Ints and inc_PID.

    I've replaced my LONG-ops in the DoServo code with calls to these routines, it compiles with PBPW and as far as I can currently see it runs correctly. I haven't tried to apply any step-pulses to see what it does "in real life" but I used TMR1 to time the DoServo routine (including the PID) and worst case I've seen so far is 207uS. ~100% increase in performance compared to when compiled with PBPL.

    I've also sent various variables (like the 4 bytes 'building' the 32bit position variable and the pid_Error variable) to the PC and it all looks to be correct. I need to test this much much more but what I'm currently worried about is if there are some bank-switching or RAM issues that I need to take into consideration with these math routines.

    I consider it pure luck that I've got it working this far and I'm afraid once I'll start to add more variables and code I'll break it - and I'll have no clue why.

    /Henrik.
    Last edited by HenrikOlsson; - 3rd January 2010 at 00:17.

  21. #21
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Great minds think alike ....
    I had a similar idea, although started with different code from the net.

    I guess it's good you got yours working, because I'm still struggling to convert it to 18F's.

    I will continue anyway, because it's really cool.
    It will be extremely useful for everyone else too.

    And if you have problems with yours, it might fill the gap.

    Best regards,
    DT

  22. #22
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Many thanks Darrel,
    Looking forward to see what you come up with as it will most definitely be better than my hacks.

    Generally speaking, when using 18F devices and declaring variables in PBP but using them in ASM by preceeding them with an _underscore. Is there ANYTHING I should be doing in the ASM code to make SURE it'll work? When lookinh at ASM code I keep seeing BANKSEL, PAGESEL etc but don't know if it applies to 18F and/or when the variable is declared by PBP. I see in the datasheet for the 18F2431 (that I'm currently using) that the RAM is divided into banks and this is what I'm worried about.

    As I said, I consider it pure luck that I've gotten this far - that and the fact that the forum has been unaccessable during the whole day which has kind of forced me to read up and do some trial and error - a good thing! ;-)

    Anyway, it's almost 2AM here, time for some sleep I guess.

    /Henrik.
    Last edited by HenrikOlsson; - 3rd January 2010 at 01:03.

  23. #23
    Join Date
    Feb 2008
    Location
    Michigan, USA
    Posts
    231


    Did you find this post helpful? Yes | No

    Default Excited lurker

    I have been watching with great interest.

    This Thread and PID-filter routine (2nd try) (page 1) PID-filter routine (2nd try) (page 2) are very exciting.
    I have a need for a small servo driver and I just can't bring myself to spending $115 for another Gecko 320. They are wonderful, but this app is just a small servo and doesn't justify it. The UHU looks perfect, but I really don't care to jump off of the PIC ship.
    I'm afraid that all I can offer is my encouragement.

    Thanks
    Bo

  24. #24
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Henrik,

    Well, my intent was to solve your problem ...
    But I'm afraid I may have just muddied the waters even more.

    I do believe this will help a great deal though ... fingers crossed.

    N-Bit_Math.pbp
    http://www.picbasic.co.uk/forum/showthread.php?t=12433

    Best regards,
    DT

  25. #25
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi,
    Today I've had somewhat of a breakthru :-)

    I went back for another try of having the servo code in the main routine but this time I took out DT-Ints all together. Currently there are three interrupts but only one of the ISRs (DoStep) is actually ASM. The other two are PBP but apparently they aren't using any system variables so I seem to get away with it - for now.....

    The latency for the DoStep ISR is now down to 2uS compared to 8.5uS before (and 29 from the beginning). This made it possible to get a step-rate of at least 60kHz without noticable problems - and this is still with 20Mhz osciallator. I'm still using the 32bit add & subtract routines I mentioned earlier but I plan to try N-Bit_Math once I get the next hurdle/challenge sorted:


    With the servo/PID code now running in the main loop I'm trying to figure out how and where I should add the "front end". The front end needs to be able to send and recieve commands thru the USART as it'll be used to setup the various PID parameters, get current position and status etc.

    Previously, when the servo/PID code was run as a low priority interrupt, I had some basic HSEROUT statements in the main routine sending out current position etc at intervals to help me debug the system. But now I had to take those out as they would otherwise hold off the execution of the servo/PID code.

    My guess is that HSEROUT/IN is out of the question, or perhaps I could use them by creating my own serial buffers and then HSEROUT (and/or IN) one character per pass thru the main loop or something like that.

    Any ideas on a good way to approach this?

    Thanks!
    /Henrik.

  26. #26
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    Either send or receive characters one by one using buffer and interrupt.

    I know you are counting the usecs here but do not see how else can have serial communication. (When will PBP support 24F series??)

    If it is not necessary to send or receive while running the PID routine, then maybe a button can branch to the setup routine, do what you want to do, then return to main program.

    Ioannis

  27. #27
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Ioannis,
    It'll have to be some kind of interrupt driven buffer routine for both sending and receiving, I think. I'd like to be able to send the current error while the system is running so the following error can be watched in "real time".

    I'm currently looking at Microchips AN744 which seems to do pretty much what I need. "All" I have to do is figure out how it works and what I need to do get it working "in" PBP.

    /Henrik.

  28. #28
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default

    my two cents worth,

    im getting 55us on the doservo code. is that possible? im using an 18f4431 @ 20mhz (couldnt get 40mhz going)

    the scope is a 2211 tektronix. i thought there was something wrong with it, but its working perfect, and upto calibration.

    i have the I_term function every 4th call, hence the long 73us on-time after every three loops on the main int code

    here's an image of the scope screen.

    all interupts are dt_interupts, in pbc.

    thanks for all the ideas.
    Attached Images Attached Images  
    NAG CON WIFE!
    WIFE VAR MOOD

  29. #29
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Thats nice, I'm glad you got it working! Are you running the DoServo code as part of an Interrupt service routine? What are you using as "input"?

    The long execution time (up to 400uS) I mentioned earlier was when the code was compiled with PBPL, when compiled with PBP it runs a lot faster. With PBP I've timed the PID code alone to as low as 40uS so your 55uS for the complete doServo seems plausible. The fact that it works points in that direction as well ;-)

    The problem starts to appear when you:
    1) Need 32 bit math for the position and setpoint variables (or anything else). Switching to PBPL makes the execution time of almost all arithemtic operations go WAY up. Without switching to PBPL you have to resort to ASM for the 32bit variables and math.

    2) Using step- and direction as the input and DT-Ints with PBP interrupt handler for counting the step pulses. This pretty much kills it above a few kHz due the latency caused by saving and restoring the PBP system variables.

    At least that's what I've found this past week. If you have yours running with step and direction input I'd love to hear what kind of steprate you can achieve.

    Thanks!
    /Henrik.

    EDIT: Wait a minute, the "period time" is 55uS (ie. the servo code is run at 18kHz), the execution time is probably closer to 40uS or so. Are you really trying to run the servocode at 18kHz?
    Last edited by HenrikOlsson; - 5th January 2010 at 22:48.

  30. #30
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default

    thanks for the prompt reply.
    right now im fighting with the step/dir int routine, which is not showing any sign of life. so im assuming that step/dir is not working.

    whole routine is in the interrupt, (just as you had it)except the lcdout, which is the only command in the main routine.

    yes, that is 18khz according to the scope. is that not good?

    the only motor connection is the encoder and im using leds to show hpwm output. saves a lot on h-bridges and smoke from explosions.
    will eventually move to the power control module once everything is figured out.
    NAG CON WIFE!
    WIFE VAR MOOD

  31. #31
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Running the PID-loop at 18kHz is probably overkill, for motorcontrol somewhere between 1000 and 2500Hz seems to be common depending on inertia of the motor etc but YMMV.

  32. #32
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by HenrikOlsson View Post
    Ioannis,
    It'll have to be some kind of interrupt driven buffer routine for both sending and receiving, I think. I'd like to be able to send the current error while the system is running so the following error can be watched in "real time".

    I'm currently looking at Microchips AN744 which seems to do pretty much what I need. "All" I have to do is figure out how it works and what I need to do get it working "in" PBP.

    /Henrik.
    Checking the RCIF/TXIF or RC1IF/TX1IF (for the 18Fseries) flags I think is enough to see if the UART is ready to send or has received a byte.

    Of course the checks and byte read/write to the UART are not free and need your precious time each.

    Maybe you have to move to a higher grade PIC like the 24F or even on the 32bit ones but then you also have to say good bye to PBP.

    Ioannis

  33. #33
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Ioannis,
    Nah, it should be doable with the 18F series - I'm almost there now. Like I said earlier I have a comercially available drive that is based on the 18F2431 and it works fine, step-rate specified to 400kHz (not verified) and USB interface thru some FTDI-chip.

    I've got the code from AN744 working "inside" a very simple PBP program - 5 lines of PBP code and 200 lines of ASM. Now I only need to figure out HOW it works so I get it to do what I need. I may opt for interrupt driven RX and polled TX though.

    Anyway, I think I'll start a separate thread for that...

    Thanks!
    /Henrik.

  34. #34
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    The AN I think is 774 not 744. The driver you have might be based on pure assembly. What clock does it have?

    Ioannis

  35. #35
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Of course, you're right! I've mentioned it twice now and managed to get it wrong both times.

    Obviously I don't have access to the source code for the drive but I was told by the developer that he was using MPLAB+BOOSTC (that probably means ASM for the time critical stuff and C for the rest). I don't know what oscillator speed it uses but I suspect it's 10*4MHz.

    /Henrik.

  36. #36
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    It makes sense to be so. Are you clocking at this speed too?

    Ioannis

  37. #37
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    The protoype, on which I managed to get 60kHz+ yesterday, is still running on 20Mhz which makes me believe I should be able to reach my target of 100Khz even after adding the "front end" and some other features.

    But right now these serial routines is making me go nuts....

  38. #38
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    Well, if you go from 20 to 40 MHz then your nice 60KHz is made with no pain 120KHz. That is more than 20% of your target, leaving you with a margin for the serial communication.

    Why not try this first, to ease the pain?

    Ioannis

  39. #39
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default one more question for Mr Henrik;

    once again, thanks for the wonderful pid filter. im still troubleshooting, and my main problem is this;
    Code:
     MAIN:                                             
         IF POSITION < SETPOINT THEN                  ;decreasing the setpoint                
         pid_Error = setpoint - position            ;calculate error
         ENDIF
         IF POSITION > SETPOINT THEN                  ;increasing the setpoint
         PID_ERROR = 65535-(POSITION - SETPOINT)    ;calculate error                                                              
         endif         
         gosub pid                                  ;take error to pid filter  
         DIRECTION = pid_out.15                     ;determine direction
         PID_TEMP = ABS PID_OUT
         DUTY = PID_TEMP                            ;load duty cycle from pid_out
         select case DIRECTION                      ;direction bit to control    
         case 0                                     ;  hpwm 1 for forward or hpwm2     
         GOSUB FORWARD                              ;  for reverse to corresponding
         case 1                                     ;  h-bridge circuit 
         GOSUB BACKWARD                             ;
         END SELECT
         LCDOUT $FE, $80, "POS=" ,DEC5 POSITION, " SP=" ,DEC5 SETPOINT; temporary
         LCDOUT $FE, $C0, "DT=" ,DEC5 DUTY, " ERR=" ,DEC5 PID_ERROR   ; temporary                 
         goto Main
    my calculated pid_error jumps from 65534 to 00001 , skipping two numbers; 65535, and 00000. i have the recommended position calculation,
    Code:
     position = 256*poscnth + poscntl
    this runs from a dt_interrupt (thanks Darrel Taylor). i tried to change setpoint and position variables to byte, instead of word, but pid filter works on word variables, and all calculations get messed up. any solutions? this has been my biggest headache on this project, for the last three weeks. and any help from anyone will be highly appreciated. thanks in advance.
    NAG CON WIFE!
    WIFE VAR MOOD

  40. #40
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi,
    It "skips" 0 because when Position=Setpoint (ie. Error should be 0) neither of your < and > evaluations are true so it won't even calculate the error. Why it skips 65535 I haven't figured out though. Can you explain why you have those two comparison statements?

    All you really should need to do is pid_Error = Position - Setpoint or Setpoint - Position if you prefer it the other way around, it doesn't matter as far as the PID routine is concerned.

    Let's say pid_Error = Position - Setpoint (all three are 16bit variables):
    If Position is 10000 and setpoint is 9000 the error will be calculated to 1000
    If Position is 9000 and setpoint is 10000 the error will be calculated to 64536 (-1000)
    If Position is 1 and setpoint is 0 the error will be calculated to 1
    If Position is 0 and setpoint is 1 the error will be calculated to 65535 (-1)

    Just pass it to the PID-filter, it figures out the sign of the error.

    /Henrik.
    Last edited by HenrikOlsson; - 7th January 2010 at 19:52.

Members who have read this thread : 1

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts