Heavy stuff... Please don«t fall asleep; interrupt-on change discussion at the end... ;) I use to build quadrature decode systems in another way than I«ve seen you talking about in the telescope control discussion. As I think it works very well and can be nicely implemented in PICs, I decided to share it with You guys. The description below is based upon a system I built to position a large machine in an industry, and I have added some new details to try building a "perfect" system. I have never seen any other system using this method. Please tell me what you think about it! --- CABLE DRIVE To send the pulses over long distance, use two differential line drivers with matched impedance into one cable of two shielded twisted pairs. (Maybe a third one for the zero position signal) RECIEVING CIRQUITRY Equally for both A and B channels: The recievers also has cable matched impedance. They each feed a RC delay and schmidt-trigger. The time constant based om max interrupt process time (below). The schmidt-triggers then connected to a XOR gate, which other input was >from a microcontroller (MC6805K1) output pin. The outputs from the XOR gates directly to the microcontroller. The pins generate a interrupt on high level. (On the 6805K1 I used an extra external gate to generate a combined input to INT input pin from the two XOR gates) DECODE PROCESS The microcontroller acknowledges the last position by setting the outputs to the XOR gates to the same. When one channel change, the XOR output goes high, and the controller executes the interrupt routine. The interrupt routine read the pins in to internal register. Then immediately outputs the new state it computes easily since it is just XOR of the input and current output. It checks if both XOR inputs were high; if so then error (double step (or 2*n) since last decode.) Then check if no one is changed! This happens if the routine is called by mistake (program error) heavy noise, hardware error etc. If no error then compute what move has happened, and just increment/decrement a buffer counter register, checking also for overrun of buffer. It detects both positive and negative transistion on both channels so it gives the highest possible resolution: four steps per encoder cycle (slot). If any pin still high then run again. (return from interrupt only when everyting done, to save overhead time; at very high count rate this routine will be computing two steps per interrupt, (since it acknowledges the new state early (see above)), and the system is still working. The main program then transfer counts from the buffer counter to the main counter and process. Thanks to the buffer counter the system accepts very high count frequency, like when the machine is vibrating and the decoder quickly moves forvard and backwards. Using minimum process time. --- The system seem very reliable and routine is fast. Could be even faster using a PIC! (Or an SX!!) So, for an up-coming project I plan to use a PIC. As a lot of you probably have realized the cirquit with the XOR described above is similar to the Interrupt-on-change feature of some PICs. INTERRUPT-ON-CHANGE Nice, but there are one potentially nasty problem with that, though... Read in Microchip 1996 PIC14000 preliminary datasheet DS40122B: " 10.6.3 PORTC INTERRUPT ON CHANGE " -snip- " Note: If a change on the I/O pin should occur when the read operation is being executed (start of the Q2 cycle), then the RCIF interrupt flag may not be set. " ... And we have a high frequency of potential misses, and one miss is too much! Conclusion: ¤ Only the service routine may read (or use read-manipulate-restore) port C. ¤ Other routines may execute clean writes to port C (not BSF, INCF etc) This will do it, if we design the input filters and schmidt-triggers to never output too short pulses (same length positive or negative): In order to guarantee that the routine called by the interrupt-on-change always read the input before it changes next time. But I would like also to use the other inputs with interrupt on change. I«ts OK, You may think: just let the routine process the other pins too. NO!! Because we have no time guarantee! Example: One signal change so the interrupt routine is called and read port C. A pin that changes while that routine is reading port C, _may_ neither be read that time, nor set the pending bit! - Begin bad idea Maybe we say: OK don«t use the other interrupt on change pins, but use the ones without interrupt as normal inputs? For other routines to read port C, they may disable interrupt and call the decode routine in order to get it to read in data from Port C? If inputs has changed when the decode routine read the new quadrature data then it will execute on it. If the routine detects no change in quadrature when having read port C it shall exit. - End bad idea What happens if the decode routine is called like above, without any change on quadrature inputs have occourred yet, but the quadrature input(s) change while reading the port?? BAD thing: old value is processed (no change) and the port change interrupt pending flag is not set until input changes another time! Then the encoder will either have moved two steps or back to none relative the last processed position. This is a fault! Conclusion: ¤ Only the two quadrature inputs should generate interrupt on change interrupts (configure other pins as outputs or I2C to disable their interrupt capability) ¤ Never call this routine unless a change has ocurred (pending flag set) The processor may never be busy (like doing other interrupts, or processing a routine that temporarily disables interrupt) for longer time than needed to jump to the decode routine and having read the port C. One remedy would be that the routines that work in interrupt disabled mode often enough check the interrupt on change pending flag and calls the quadrature decode routine when needed. Hum... Lets try to create a simple circuit work like the one I made with the MC6805 and external gates ! METHOD TO _AT_ANY_TIME_ CALL THE QUADRATURE DECODE ROUTINE IN ORDER TO READ PORT C If we make sure that the compare register on port C interrupt-on-change logic always gets loaded with the value that the routine has as input, there will be no problems. METHOD TO LOAD THE READ DATA INTO INPUT COMPARE REGISTER First we add resistors in series with the input pins on port C, as we will (very shortly) set them as outputs! (R=approx 1k - 10k ohm.) For schematic on Port C see: Microchip 1996 PIC14000 preliminary datasheet DS40122B FIGURE 5-3 and 5-4 The compare latch is the most lowest in the schematics. Read and write port C routine: 1 Read port C to PORTC_known register in RAM (reads input pins) * Mask bits in output pin positions using OR (optionally, see (*) below) * AND with PORTC_out (optionally, see (*) below) 2 Write Port C with the result (writes to the output latch) 3 Set all pins as outputs ("input" pins will win over outer cirquitry because of the resistors) 4 NOP (Needed to stabilize output, I read Microchip recommended it when write-reading ports) 5 Read port C (Reads pins = output latch = known_state into compare register (as well as W)) 6 Set input pins as inputs (those we added resistors to) 7 Clear port C interrupt-on-change pending flag (see (7) below) WOW! Now we have the read data in PORTC_known register, and guaranteed _the_same_ in the compare register. As the routine and compare register see the world the same way we will have no "scizzofrenia" problems about however pins are changed or not. (*) Optionally, if some of the pins are outputs, this routine can take care of writing the data to them too. Any other routine may write a byte directly to port C if it is not interrupting between the read and write of the routine above, of course. But never use read or read-modify-write! HOWEVER: If there should be a temporary glitch on one output pin as it is read by the routine above, it will then be recritten with the value of that glitch! Not likely to occour, but to be safe, include the lines with * and write to port C by setting PORTC_out register and call the routine above. (7) If inputs changed during the first read of port C, the pending flag will be set even when we tried to clear it. So if changes occourred then or before end of this routine, it will be processed when this routine before exit test to run again if the flag then is set. Now we can call the port C read routine anytime; with or without interrupt-on-change request! And we can have asynchronous interrupt-on-change inputs processed correctly! Beware: ¤ The only routine reading from the port must be the port C read routine described above. ¤ After that routine is processed _all_ routines that handles change on port C signals _must_ be processed before next read! ¤ Good idea is to check if interrupt-on-change pending bit is set, directl y when the routines to handle them are done, and in that case go for it again! ¤ No routine may read port C or even use BSF or other read-modify-write instructions on port C!) They may make a clean byte-write, but se (*) above! ¤ To output to pins in port C in a safe way, include the lines with * and write to port C by setting the bits as desired in PORTC_out register and call the routine above. ¤ A routine that wish to know a state of a pin on port C which is not a interrupt on change pin, may call the port C read routine (which then executes all interrupt on change routines in case there happened to be new data for them). Then the calling routine reads the input in the PORTC_known register. This seem to be a watertight solution. Anyone who see a doubt, please tell! So, we can enable interrupt on change on all pins, and have the position zero signal on one of them, and then we can guarantee zero position is interpreted quickly and without a puls on quadrature signals. - Puh... - it«s in the middle of the night now... - Anyone have comments on the quadrature decoding above? Have I understood the interrupt-on-change functions correctly? In all this text I might also have made other mistakes... /Morgan Morgan Olsson, MORGANS REGLERTEKNIK, Sweden, ph: +46 (0)414 70741; fax 70331 -