Oop. Yeah, of course that makes sense. Thanks.
Oop. Yeah, of course that makes sense. Thanks.
I incorporated your idea, Henrik, and all is well. I could have sworn this was working earlier this year when I last touched it but perhaps if using the same chip and saving too often to EEPROM means it starts to take longer to do the WRITE and hence only then really missed out on the IOC's. I thought it also incremented MotorRPM by 1 but I can't prove that now. In the end, it works with using a ISR counter and only on the 4th one does it increment MotorRPM.
I upped the TimeToSave value to 50000 - seems like a good compromise.
Should I be setting Old_Bits to New_Bits in the 'DoneRotEnc' label for the first 3 of 4 interrupts related to an IOC?
Code:' *************************************************************** ' [IOC - interrupt handler] ' *************************************************************** RotEncAdjust: New_Bits = PORTA & (%00000011) IF New_Bits = Old_Bits Then DoneRotEnc ' Increment ISR counter (since quad encoder throws 4 interrupts ' for every single detent), only want to update MotorRPM when counter ' reaches 4 (i.e. a single detent 'click') ISRCounter = ISRCounter + 1 IF ISRCounter < 4 THEN DoneRotEnc RotEncDir = New_Bits.1 ^ Old_Bits.0 IF RotEncDir = 1 Then ; CW rotation - increase speed but only to a max of 'MaxDuty' IF MotorRPM < MaxDuty then MotorRPM = MotorRPM + 1 Else ' CCW rotation - decrease speed to a min of 0 IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1 EndIF ValueDirty = 1 ; Set dirty flag ISRCounter = 0 ; Reset ISR entry counter TimeToSave = 50000 ; Set value to begin countdown to EEPROM save DoneRotEnc: Old_Bits = New_Bits IOCAF.0 = 0 ' Clear interrupt flags IOCAF.1 = 0 @ INT_RETURN
Hi,
I'm afraid I don't quite understand. A quadrature signal consists of four states (00-10-11-01), from any one state there are TWO valid states which it can "transition" to, one in either direction. If you don't keep track of Old_Bits it doesn't know in which state it was and will get confused, count in the wrong direction etc - just as it did when you had the delay in there. Are you trying to keep the MotorRPM from incrementing by 4 for each click?
Why don't you simply have an EncoderCount variable which is the one you save to EEPROM, and then you do MotorRPM = EncoderCount / 4. Also, instead of triggering on both the rising and falling edges you could trig on only the rising (or falling) which would give you two counts per click instead of four.
/Henrik.
But if the interrupts are meant to fire on either a rising or falling edge, then going from '00' to '10' has only one edge (A rises from 0 to 1; B stays the same). The 00 to 10 state change represents one detent, right?
I got this ISR code from another post (http://www.picbasic.co.uk/forum/show...5396#post25396) and it seemed to work back then (incrementing MotorRPM by 1)
Last edited by RossWaddell; - 17th March 2013 at 14:23.
Hi,
Yes of course, one edge.But if the interrupts are meant to fire on either a rising or falling edge, then going from '00' to '10' has only one edge (A rises from 0 to 1; B stays the same).
It depends on the encoder used but most mechanical type encoders I've come across has a full quadrature cycle per detent. Here's cutout from the datasheet for a EN12-series encoder from TT Electronics:The 00 to 10 state change represents one detent, right?
As you can see, going from one detent to the next results in a full quadrature cycle or four edges. So if the IOC is setup to trig the on the rising and falling edge of both the A and the B input then you'll have four interrupts per detent so the MotorRPM variable will increase by 4.
When it "seemed to work back then" was that with this very same encoder or have you changed it? Do you have a modelnumber or datasheet for the encoder you're using?
/Henrik.
Last edited by HenrikOlsson; - 17th March 2013 at 15:13.
Quite possibly it was using an earlier rotary encoder. I switched to a CUI ACZ11 (datasheet) one because I needed the button. Perhaps the Grayhill one I was using before (datasheet) showed the single-increment-per-detent. The CUI datasheet doesn't explicitly state it goes through a full quadrature cycle but one of the diagrams seems to infer that.
Again, thanks for your patience, Henrik. Your explanation makes sense to me now (I hadn't realized that different mechanical encoders behave differently. Should have checked that, of course).
EDIT: I just replaced the CUI encoder with the Grayhill one on my breadboard and guess what - it increments by 1. Mystery solved. Sadly, there doesn't seem to be a cheap Grayhill encoder with a push button so I'll just have to add comments in case I ever switch encoders again to do this test (or read the data sheet more carefully).
Last edited by RossWaddell; - 17th March 2013 at 18:39.
I have to admit, Ioannis, the code was grabbed from an example here and implemented on my side using a Grayhill encoder which only triggered the interrupt once per detent. Works perfectly with that device, With the CUI ACZ11, I added the check on the ISRCounter but it **does** work - both CW & CCW rotations are captured correctly.
I tried CCW rotation again last night and it does seem to get confused every once in a while - either decrements by 2 or sometimes even increments. Doesn't happen often enough to be too much of a problem, but I would like to make the code as efficient as possible.
Not sure what else I can do in the ISR, though. If I only need one edge to check the rotation direction then I could do as Henrik suggested and turn off 2 of the edge interrupts; that way, it would only hit the ISR twice. I could do the RotEncDir determination on the first interrupt then set a flag to ignore the 2nd interrupt, then reset those after the 2nd.
I stil cannot see how this piece of code can be true:
If you follow what Henrik has posted on that pulse train diagram at #21, you will see that after the above XOR, the IF RotEncDir = 1 statement will always fail and the ELSE will be executed.Code:RotEncDir = New_Bits.1 ^ Old_Bits.0 IF RotEncDir = 1 Then
Ioannis
Bookmarks