This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/EK-TM4C123GXL: ISR Clear QEI_INTDIR problem

Part Number: EK-TM4C123GXL

Tool/software: Code Composer Studio

Hello Esteemed Helpers,

I have tried to reduce my problem to the minimal functionality which still exhibits the problem. There is a 2000 point quadrature rotary encoder triggering an ISR with QEI_INTDIR. The minimalist code is below:

void QEIInterruptHandler(void)

//   =========================

{

//   QEIIntClear(QEI0_BASE,QEI_INTDIR);

   if (stepLogic == 0)

   {

       PULSE_LOW;

       stepLogic = 1;

   }

   else

   {

       PULSE_HIGH;

       stepLogic = 0;

   }

}

When the encoder is stationary, the ISR is triggered at a very fast rate once an interrupt is sent  (by twitching the encoder spindle) as shown in the scope cap. But iI f the comment on the Clear event is removed nothing happens even when the encoder is set in motion at 5 RPM (Or to be more accurate the pulse goes high and stays there). I must be missing something about clearing interrupt events. Thank you.

  • Hi William,

      Yes, you must clear the interrupt INTDIR flag in your interrupt handler. When the direction is detected, it sets the INTDIR flag high in the QEIRIS register. When the INTDIR is high, it becomes an interrupt request to the processor. Once in the interrupt ISR, you need to clear the INTDIR. If you don't clear the INTIDIR bit, the interrupt request remains pending. This means that once you exit the ISR, it will immediately enter the ISR again since the interrupt is still pending. I think the encoder must have detected a direction change event. When you said the encoder is stationary, it may be stationary from a human eye. There could be a very tiny movement detected. This is what I suspect. Somehow, it started with a very first interrupt. As the INTDIR is not clear, it keeps generating interrupt after it exists even though there is no noticeable movement on the inputs. With all said, your description of the problem seems to be the opposite of what I expected. You said with a 5RPM inputs there is no interrupt at all. Can you read the INTDIR bit and see if it is pending when it's 5RPM?

      Please also check the ERROR bit in the QEISTAT register. Do you have any errors?

      

  • Greetings poster William & friend/vendor Charles,

    While (far) from 'esteemed helpers' - crack young staff & I have a rather different take.    As our method has often (bordering upon 'always') succeeded - it may offer value.

    • while it is good that you adopt such compressed description - it is far more "normal/customary" that the Encoder's actual output signals are 'scope-capped' &  displayed.    (or at minimum - interleaved w/your software driven GPIO)
    • such quad encoders generate "DIR" via the "edge-timing" between Enc. Phase A & B.   It is our experience that this method (i.e. detection of the encoder's edge-timing relationship) proves far more robust & successful.    (note: your 'difficulty' is not unique when SW generated outputs are employed...)
    • running your motor at several, reasonably stable speeds (say 10, 20, 50 RPM) while presenting the "3-Channel Scope Caps" - achieved at each speed - should prove productive & confirming to (both) poster & 'helpers.'     (3-Channels: 2 Phases & Index, s'il vous plaît)
    • Ground should be solid between encoder & MCU - low value (serial) resistors in line w/each encoder provide "protection" for the MCU
    • Helpers (even esteemed ones) are "unaware" of the:
      • encoder's cable length
      • noise produced by the motor drive
      • proximity of noise-generators to the encoder's signal path
      • motor's type (i.e. Brushed, BLDC, Stepper, Inductive etc.) each may present unique challenges - complicating (helper) analysis

    Vendor's API provides (proven) results for calculating motor's Speed & Direction - yet assumes "normal" motor operation - not that observed thru, "Spindle Twitching."

    Sometimes it makes most sense to "Remain w/the herd" - - we are told that (both) predators & odd tech results often arrive when one "trail-blazes."

  • Hello Charles,

    I understand the explanation of the interrupt re-firing itself if you don’t clear it which presumably is what is happening and explains the endless pulses with no apparent movement of the encoder. Having said that, you are right that that a small perturbation on the bench for example or spindle twitching triggers the interrupt to start the process.

    But I am puzzled why clearing it seems to stop any further processing in the handler whereas not clearing it lets the handler execute the code but maybe I shouldn’t be.

    I’m afraid I’m getting into new territory as usual so could you tell me how to read the bits in the register please. I will also follow the suggestions of c1_mobile but only have two channels on the scope so can’t do the Index simultaneously.

  • Hi William,

      In the ISR, I will suggest you first call the QEIIntStatus() to obtain the current interrupt status. Base on the interrupt status, you will clear the flags and handle them per your application. In the current ISR, you immediately clear the INTDIR. How can you be sure that you are not getting interrupt due to other events such as QEI_INTERROR? Suppose the QEI detects some type of error and the interrupt is getting generated. It will keep generating the interrupt as you never clear the error flag and you are handling the error interrupt as if an INTDIR interrupt.  If this is the case, this is how the stepLogic keeps toggling between 0 and 1. 

  • Thanks for your advice Charles. I need to get a bit of clear thinking going here – I think I’m confusing myself with too many head – bench collisions. I’ll get back when I am sure of what’s going on.

  • If my group may - you are (far) outside of "KISS!"     (i.e. Starting w/high detail - rather than a systematic "Build" from the basics.)     Is it not true that you've "Held your QEI Mastery hostage" to a crystal clear understanding of a (pardon) not too 'key/critical interrupt?'     Is that best for your learning & advancement?    Should it (not) be noted?  

    Much can be gleaned from observing the scope under "normal/proper" motor running conditions - not (helped) by a twitch!    (Never/ever have staff, multiple clients nor I "started" via that (twitch) method...)

    By briefly running the motor - and then 'Breaking (break-pointing) upon the API's various "Points of Interest" - much of the "Encoder-API" interaction can be highlighted - vastly aiding understanding & confidence.    (Twitching ... Not so much...)

    As Charles noted - there are multiple "interrupts" from a variety of sources.    Most always these are "high details" - and should be engaged only, "AFTER the basics are exercised, observed & mastered..."

    In (still) further support of this, "Less is MORE" approach - these Key QEI Functions (w/in the API) operate independently from interrupts:   (they may operate in concert w/interrupts - yet that is not mandated)

    • QEI GetDirection()
    • QEI GetPosition()
    • QEI GetVelocity()
  • Hello cb1_mobile,

    Thank you again for you sharing your great experience for the benefit of novices such as myself. The scope caps show the two phases as there are only two channels on this scope. The velocity and position were generated correctly (as evidenced by using UARTprintf ) but maybe these signals (the quality of which you have alluded to before) are not generating INTDIR interrupts (the only interrupt enabled btw) successfully which would account for why the expected code is never executed. If this question was started at a high level it’s because it was working in a poll mode but then it was moved to be interrupt driven.

  • Thank you - appreciated.    Know that the MCU's API was created by a clever & well-practiced team - and almost always should be your "First Order of MCU interaction."

    William Herschel said:
    If this question was started at a high level it’s because it was working in a poll mode but then it was moved to be interrupt driven.

    Never could my team have known this - brevity has its limits - and most always - forces "Greater effort from 'taxed helpers.'"   

    Might you assist by describing why the API's "QEI Get Direction()" function did not satisfy?    What specifically was the interrupt's role - and was the API (really) incapable of and/or ineffectual in 'meeting your need?' 

    We are "under the gun" time-wise - yet if you can "describe your intent" - we may be able to assist you in exploiting the API.   (we will have to respond after today's, 'Final Test & Packing/shipping' - which demands 'all hands' till 19:30 or so this evening...

    BTW - when only 2 scope channels are available - provided you specify the motor's direction during the scope-cap - Phase A & Index should prove insightful.   (An expanded time-base would best accommodate the index signal  during the, "2000:1 Phase vs. Index" pulse generation.    Or ... as we often do ... you may impose a "Divide by" stage on Phase A - (i.e. a divide by 100) so that the Index signal occurs at/around every 20 Phase pulses...    (it is also possible to logically combine both Phase signals (externally - although some scopes enable this) - so that (even) a 2 channel scope provides a "FULL Encoder Display.")

  • I am sorry that the scant scenario hid much of the detail. I have been criticised for erring on the side of brevity, but know how tiring verbosity can be when no new information is revealed. To be honest the last thing I want to do is plough through 100 lines of somebody else’s code so I try not to burden others. You ask what my need is – well it is simply to read the encoder and synchronise a stepper motor position after appropriate gearing of the signal (to suit metric thread pitches). My thinking was to get 2000 interrupts per revolution and send a ‘geared’ signal to the 6400 stepper per interrupt. For example, when the gearing is 1:1, for every 5 steps of the encoder 16 steps have to be sent to the stepper spaced out over the same time. This seemed to work more or less in the polled method but I didn’t want to miss a position (which I couldn’t recover from) which is why I went for interrupts. I may have missed a trick here but that was my thinking. I am sure you have implemented many similar projects so if you think there is a better way of doing it I would obviously accept your long experience as guidance.

  • Thank you - shipping (here) continues so we'll have to respond (pardon) in a "chopped" fashion.    (i.e. more 'chopped' than my (usual) chopped fashion...)

    William Herschel said:
    plough through 100 lines of somebody else’s code

    Yet that (100 code lines) was (not) sought - the revelation that you (earlier) "Succeeded via QEI Polling" - would have been most helpful...   Your narratives are quite good - the "Description of your methods & objectives" - highlighting 'Results' - proves a most 'reasonable' compromise" - does it not?    (Not always do posters choose the most direct and/or effective Solution Paths...)

    William Herschel said:
    My thinking was to get 2000 interrupts per revolution and send a ‘geared’ signal to the 6400 stepper per interrupt.    For example, when the gearing is 1:1, for every 5 steps of the encoder 16 steps have to be sent to the stepper spaced out over the same time.

    Your "objective" now is far more clearly presented & leads to multiple, "Follow on questions."    (never/ever possible w/out your creation of so clear a narrative ... (even) when minus those pesky "100 code lines"...)

    It is likely best to identify (and even attempt to agree upon) key facts:

    • Stepper Motor in question is expected to produce either a 1.8° or 0.9° "rotation per step" - is this correct?   
    • The 1.8° stepper requires 200 "Full Steps" per revolution.    The 0.9° stepper requires (double) that number.
    • You've chosen a 2000 pulse/revolution encoder.   That's 5x the resolution of the 0.9° stepper.   (or 10x the resolution of the 1/8° one.)
    • It appears that you intend to provide, "5 micro-steps" - with which to generate "One Full Step."   (thus 5 micro-steps * 400 full-steps/revolution = 2000 micro-steps and 2000 (resulting) encoder counts)

    Might you agree/dispute and/or comment upon, "Each of these points?"    (Please - let's hold to these points at this stage - unless 'new facts' greatly simplify/clarify.)

    William Herschel said:
    For example, when the gearing is 1:1, for every 5 steps of the encoder 16 steps have to be sent to the stepper spaced out over the same time.

    • Your (new) mention of the "6400 stepper" does (not) compute!   Are you suggesting that your motor provides, "Beyond the 400 full-steps/revolution" - which we earlier theorized?
    • Also the "5:16 Encoder/Stepper" relationship fails to compute!    Realize that, "No motor spec" has been provided - and that (some) justification for 'conclusions' prove helpful
    • The encoder can only respond to the motor's rotation - which is (fully) "Under your control."    You appear to (somehow) expect the "Encoder Outputs to LEAD the Stepper Motor's 'Stepped Drive' - kindly assist our understanding of this point.   (your language, "Every 5 steps of the encoder ... 16 steps have to be sent to the stepper" forms the basis for our conclusion...)

    In addition - it is believed that your following "methods" prove "unusual/non-standard:"    (Not that there's anything inherently 'wrong' w/that...)

    • generating an interrupt upon "each/every" encoder count   (even the API resists that - but for a 'Phase Error' - far too demanding - in my group's opinion...)
    • seeking to interrupt upon a "Change in motor direction"    (it is our experience that when, "Driving to a Position" - most always - we avoid, "Position Over-Shooting" - preventing motor's need to reverse/(Retreat!)

    Note that the API supports my group's classifying your methods as "unusual" via, "The QEI module generates interrupts when the index pulse is detected, when the velocity timer expires, when the encoder direction changes, and when a phase signal error is detected."

  • Hi Charles.

    I added this code to the handler: (Only QEI_INTDIR is enabled by the way).

       Status = QEIIntStatus(QEI0_BASE,false);

       if(Status & QEI_INTDIR)

       {

           UARTprintf("In Handler %X\n",Status);

           QEIIntClear(QEI0_BASE,QEI_INTDIR);

       }

    While the encoder is ‘stationary’, it occasionally prints the message ‘In Handler 6’. (When the encoder is shaken momentarily). The sporadic waveforms of Phase A (blue) and the motor pulses when this happens are shown below.

    However, when the encoder is rotating there seems to be no motor pulses at all as below.

     

    I really don't know if this tells you anything useful.

  • Hi William,

      Before anything, I think you need to move the QEIIntClear to the earlier part of your ISR. I will suggest you clear the flag right after you call QEIIntStatus. I just wanted to rule out the interrupt re-entry due to the write-buffer reason as described in the below. 

  • Hi Charles,

    I did that but there is no perceptible difference.

  • Just a wild thought - I am assuming this interrupt fires every time the encoder moves forwards - or does it only do it when it changes from frowards to backwards?

  • Looking at the diagram from the datasheet. The interrupt should be generated when there is a change from forwards to backwards. While in either forwards or backwards motion, the dir is unchanged and there is no interrupt. Highlighted in yellow is when the interrupt is generated,

    Can you play with the noise filter and see if it makes a difference?

  • Well I have been labouring under a misconception. I was looking for an interrupt that would occur for every step of the encoder but I don’t think any of the QEI interrupts can do that.

  • I see. Now it kinds of connecting the dots. When your encoder is rotating in a forward motion, the direction is the same and hence no interrupt. That was what you reported in your capture. When you twitch the encoder a bit (from stationary to some movement), it creates a direction change. This direction change generates the interrupt. 

    Let me know if I can close the thread if it is clear to you now. 

  • Yes thank you Charles. Thank you for working through it with me.  (Don't mention twitching by the way).

  • I hope you got your shipment out successfully. It’s good to see the fruits of one’s hard work.

    I will endeavour to answer your questions as soon as I can. In the meantime, you might get a flavour for what I am doing from Mr Clough.

    https://www.youtube.com/watch?v=FTs9GygRQ-U

    He has turned his idea into a commercial product but I am building it from scratch because I can incorporate new ideas more easily as time goes by.

  • Young team seeks to note that, "Even while tasked w/extensive Testing, Packing & Shipping - they were (first) to identify your, "Improperly chosen interrupt source."

    Their post last evening (27 Jan, 20:20 CST) revealed:

    cb1_mobile said:

    In addition - it is believed that your following "methods" prove "unusual/non-standard:"    (Not that there's anything inherently 'wrong' w/that...)

    • generating an interrupt upon "each/every" encoder count   (even the API resists that - but for a 'Phase Error' - far too demanding - in my group's opinion...)
    • seeking to interrupt upon a "Change in motor direction"    (it is our experience that when, "Driving to a Position" - most always - we avoid, "Position Over-Shooting" - preventing motor's need to reverse/(Retreat!)

    While the 'Green Ink' is flowing - should not their their "predictive & insightful post" be properly rewarded?      Such dedication demands recognition - and heightens their drive to, "Provide similar timely (and detailed) assistance into the future."     (the majority of your issues which they identified remain 'w/out comment' - it is expected that future posts will revisit those...)

  • Hello cb1_mobile,

    Thank you for your comments. I appreciate the effort that you and your staff make to answer some probably fairly inept questions for newcomers. If I had understood the significance of their remarks about the type of interrupt I would have recognised the cause of my problem and marked it as such but I didn’t. As I said to Charles I was unaware that the INT_DIR interrupt only occurred on change of direction. It would require one step forwards followed by one step back to achieve my objective which obviously would achieve nothing so if I’d known that I wouldn’t have tried it.

    Perhaps I should have made it clearer that the Microstep driver I am using sends 6400 pulses per revolution. If 5 steps of the encoder requires 16 steps of the stepper (Driver) it means send 5 groups of signal to the Driver over this interval viz. 3,3 4,3,3 = 16. i.e. the first encoder pulse sends 3 steps, the second 3 etc. The stepper will lag the encoder of course but by the order of a microseconds which will mean the deviation of the position of the shaft attached to the stepper from its true angular position will be milli-arc seconds which is good enough for this application. The scope cap shows the 5 groups of Driver signals (3,3,4,3,3) against the 5 encoder signals.

    When you say generating an interrupt every encoder signal is too demanding, I’m not sure what this means. I have set an interrupt on a pin to trigger a once per step interrupt and the scope signals show clean signals up to several hundred RPM. Thanks again to you and your staff. This would be virtually impossible for me without your assistance. If this is becoming too time consuming please feel free to cease.

  • There remains a deep gulf between "your & our understanding" of the "Joint encoder-stepper motor's operation!"

    This quote - which you expressed several times earlier - greatly disturbs!

    William Herschel said:
    The stepper will lag the encoder of course

    Pardon - but if we've "Graduated from Twitching" your quote (above) cannot prove true!    The encoder (any encoder - btw) responds to the motor's angular rotation - thus it proves, "Beyond my group's ability - to recognize (and/or accept), 'How the stepper can (ever/possibly) 'Lag the Encoder!"   

    Again - the role of the encoder is to, "Track & report Shaft Rotation" - minus that shaft rotation - no meaningful (changed or new) encoder data is possible!    (some encoders enable a 'Position Read' at any time - but that's outside of your, "Stepper lagging the Encoder" (pardon) [unacceptable to my group's] belief!

    For the sake of "completeness" you (may) be suggesting that the encoder's "projected" new position may be known in advance of the arrival of the stepper motor's drive signals.    Yet that belief must be detailed/clarified to be (reasonably) accepted by others - and we find such detail/clarity absent...

    Our group's belief - developed over > 10 years of successful encoder design/delivery/usage (delivered to several U.S. Federal Laboratories) reveals that it proves, 'Far more 'normal/customary' to note the "Motion Stage's" (*) initial position and then drive the motor(s) progressively toward the new position.   Rarely is this done (but for the final 'fine-position' adjustment) via "Single Encoder Pulses."

    *  Motion Stage usually refers to 2 or more, motor-driven, orthogonal axes which enable the (reasonably) precise positioning of a travelling 'payload.'    Encoders have historically assisted this effort - yet "latest/greatest" such 'stages' (linear or rotary) have now graduated to "Linear Scales" (optical or magnetic) which enable improved Positioning: Speed, Accuracy & Precision!

  • I am sorry to hear of this deep gulf. I wonder if it can be closed by referring to your statement - The encoder (any encoder - btw) responds to the motor's angular rotation. Correct me if I’m wrong, but you may have got the impressions that the encoder is driven by the stepper motor. If so, this is not right. The encoder is driven by a different motor. The encoder on motor A informs the stepper motor B what to do. Since motor B can only be told what to do after motor A has made its move, there will always be some lag. I do hope this is the cause of the gulf.

  • Staff & this reporter are (almost) "shocked" by this latest, "Fact introduction!"

    William Herschel said:
    you may have got the impressions that the encoder is driven by the stepper motor.

    Indeed that is true - and it is suspected that 99.9% of those following this thread - would have garnered that same impression.    It is 'normal/customary' for an encoder to attach to the (referenced) motor's shaft - and thus provide, "Positional feedback."

    Totally new to your series of postings here now arrives:

    William Herschel said:
    The encoder is driven by a different motor.    The encoder on motor A informs the stepper motor B what to do.

    Should not this "Level of Fine Detail" have been (somehow) earlier presented - (even) highlighted?    This was totally unexpected - and if presented - would have likely led to (other) vastly different, design aspects/considerations.

    We've just (now) been told: (for the very first time)

    • there exist not one - but two motors!
    • the encoder described herein is (not) monitoring the (only) motor this thread chose to disclose!

    Pardon - but "Might the "withhold" of these (necessary) facts"  lead to, "Krazy-Making?"   

    The indirection of the approach (just now described) introduces multiple, potential, entirely new, "Error Sources" and (almost surely) demands a "High Degree" of "Motor, Load & Drive Current/Waveform MATCH & Control" to effectively succeed.      ("KISS" has rightly, "fled the scene" - as we do now as well....)