Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
QEI.cpp
00001 /** 00002 * @author Aaron Berk 00003 * 00004 * @section LICENSE 00005 * 00006 * Copyright (c) 2010 ARM Limited 00007 * 00008 * Permission is hereby granted, free of charge, to any person obtaining a copy 00009 * of this software and associated documentation files (the "Software"), to deal 00010 * in the Software without restriction, including without limitation the rights 00011 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00012 * copies of the Software, and to permit persons to whom the Software is 00013 * furnished to do so, subject to the following conditions: 00014 * 00015 * The above copyright notice and this permission notice shall be included in 00016 * all copies or substantial portions of the Software. 00017 * 00018 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00019 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00020 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00021 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00022 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00023 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 00024 * THE SOFTWARE. 00025 * 00026 * @section DESCRIPTION 00027 * 00028 * Quadrature Encoder Interface. 00029 * 00030 * A quadrature encoder consists of two code tracks on a disc which are 90 00031 * degrees out of phase. It can be used to determine how far a wheel has 00032 * rotated, relative to a known starting position. 00033 * 00034 * Only one code track changes at a time leading to a more robust system than 00035 * a single track, because any jitter around any edge won't cause a state 00036 * change as the other track will remain constant. 00037 * 00038 * Encoders can be a homebrew affair, consisting of infrared emitters/receivers 00039 * and paper code tracks consisting of alternating black and white sections; 00040 * alternatively, complete disk and PCB emitter/receiver encoder systems can 00041 * be bought, but the interface, regardless of implementation is the same. 00042 * 00043 * +-----+ +-----+ +-----+ 00044 * Channel A | ^ | | | | | 00045 * ---+ ^ +-----+ +-----+ +----- 00046 * ^ ^ 00047 * ^ +-----+ +-----+ +-----+ 00048 * Channel B ^ | | | | | | 00049 * ------+ +-----+ +-----+ +----- 00050 * ^ ^ 00051 * ^ ^ 00052 * 90deg 00053 * 00054 * The interface uses X2 encoding by default which calculates the pulse count 00055 * based on reading the current state after each rising and falling edge of 00056 * channel A. 00057 * 00058 * +-----+ +-----+ +-----+ 00059 * Channel A | | | | | | 00060 * ---+ +-----+ +-----+ +----- 00061 * ^ ^ ^ ^ ^ 00062 * ^ +-----+ ^ +-----+ ^ +-----+ 00063 * Channel B ^ | ^ | ^ | ^ | ^ | | 00064 * ------+ ^ +-----+ ^ +-----+ +-- 00065 * ^ ^ ^ ^ ^ 00066 * ^ ^ ^ ^ ^ 00067 * Pulse count 0 1 2 3 4 5 ... 00068 * 00069 * This interface can also use X4 encoding which calculates the pulse count 00070 * based on reading the current state after each rising and falling edge of 00071 * either channel. 00072 * 00073 * +-----+ +-----+ +-----+ 00074 * Channel A | | | | | | 00075 * ---+ +-----+ +-----+ +----- 00076 * ^ ^ ^ ^ ^ 00077 * ^ +-----+ ^ +-----+ ^ +-----+ 00078 * Channel B ^ | ^ | ^ | ^ | ^ | | 00079 * ------+ ^ +-----+ ^ +-----+ +-- 00080 * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 00081 * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 00082 * Pulse count 0 1 2 3 4 5 6 7 8 9 ... 00083 * 00084 * It defaults 00085 * 00086 * An optional index channel can be used which determines when a full 00087 * revolution has occured. 00088 * 00089 * If a 4 pules per revolution encoder was used, with X4 encoding, 00090 * the following would be observed. 00091 * 00092 * +-----+ +-----+ +-----+ 00093 * Channel A | | | | | | 00094 * ---+ +-----+ +-----+ +----- 00095 * ^ ^ ^ ^ ^ 00096 * ^ +-----+ ^ +-----+ ^ +-----+ 00097 * Channel B ^ | ^ | ^ | ^ | ^ | | 00098 * ------+ ^ +-----+ ^ +-----+ +-- 00099 * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 00100 * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 00101 * ^ ^ ^ +--+ ^ ^ +--+ ^ 00102 * ^ ^ ^ | | ^ ^ | | ^ 00103 * Index ------------+ +--------+ +----------- 00104 * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 00105 * Pulse count 0 1 2 3 4 5 6 7 8 9 ... 00106 * Rev. count 0 1 2 00107 * 00108 * Rotational position in degrees can be calculated by: 00109 * 00110 * (pulse count / X * N) * 360 00111 * 00112 * Where X is the encoding type [e.g. X4 encoding => X=4], and N is the number 00113 * of pulses per revolution. 00114 * 00115 * Linear position can be calculated by: 00116 * 00117 * (pulse count / X * N) * (1 / PPI) 00118 * 00119 * Where X is encoding type [e.g. X4 encoding => X=44], N is the number of 00120 * pulses per revolution, and PPI is pulses per inch, or the equivalent for 00121 * any other unit of displacement. PPI can be calculated by taking the 00122 * circumference of the wheel or encoder disk and dividing it by the number 00123 * of pulses per revolution. 00124 */ 00125 00126 /** 00127 * Includes 00128 */ 00129 #include "QEI.h" 00130 00131 QEI::QEI(PinName channelA, 00132 PinName channelB, 00133 PinName index, 00134 int pulsesPerRev, 00135 Encoding encoding) : channelA_(channelA), channelB_(channelB), 00136 index_(index) { 00137 00138 pulses_ = 0; 00139 revolutions_ = 0; 00140 pulsesPerRev_ = pulsesPerRev; 00141 encoding_ = encoding; 00142 00143 //Workout what the current state is. 00144 int chanA = channelA_.read(); 00145 int chanB = channelB_.read(); 00146 00147 //2-bit state. 00148 currState_ = (chanA << 1) | (chanB); 00149 prevState_ = currState_; 00150 00151 //X2 encoding uses interrupts on only channel A. 00152 //X4 encoding uses interrupts on channel A, 00153 //and on channel B. 00154 channelA_.rise(this, &QEI::encode); 00155 channelA_.fall(this, &QEI::encode); 00156 00157 //If we're using X4 encoding, then attach interrupts to channel B too. 00158 if (encoding == X4_ENCODING) { 00159 channelB_.rise(this, &QEI::encode); 00160 channelB_.fall(this, &QEI::encode); 00161 } 00162 //Index is optional. 00163 if (index != NC) { 00164 index_.rise(this, &QEI::index); 00165 } 00166 00167 } 00168 00169 void QEI::reset(void) { 00170 00171 pulses_ = 0; 00172 revolutions_ = 0; 00173 00174 } 00175 00176 int QEI::getCurrentState(void) { 00177 00178 return currState_; 00179 00180 } 00181 00182 int QEI::getPulses(void) { 00183 00184 return pulses_; 00185 00186 } 00187 00188 int QEI::getRevolutions(void) { 00189 00190 return revolutions_; 00191 00192 } 00193 00194 // +-------------+ 00195 // | X2 Encoding | 00196 // +-------------+ 00197 // 00198 // When observing states two patterns will appear: 00199 // 00200 // Counter clockwise rotation: 00201 // 00202 // 10 -> 01 -> 10 -> 01 -> ... 00203 // 00204 // Clockwise rotation: 00205 // 00206 // 11 -> 00 -> 11 -> 00 -> ... 00207 // 00208 // We consider counter clockwise rotation to be "forward" and 00209 // counter clockwise to be "backward". Therefore pulse count will increase 00210 // during counter clockwise rotation and decrease during clockwise rotation. 00211 // 00212 // +-------------+ 00213 // | X4 Encoding | 00214 // +-------------+ 00215 // 00216 // There are four possible states for a quadrature encoder which correspond to 00217 // 2-bit gray code. 00218 // 00219 // A state change is only valid if of only one bit has changed. 00220 // A state change is invalid if both bits have changed. 00221 // 00222 // Clockwise Rotation -> 00223 // 00224 // 00 01 11 10 00 00225 // 00226 // <- Counter Clockwise Rotation 00227 // 00228 // If we observe any valid state changes going from left to right, we have 00229 // moved one pulse clockwise [we will consider this "backward" or "negative"]. 00230 // 00231 // If we observe any valid state changes going from right to left we have 00232 // moved one pulse counter clockwise [we will consider this "forward" or 00233 // "positive"]. 00234 // 00235 // We might enter an invalid state for a number of reasons which are hard to 00236 // predict - if this is the case, it is generally safe to ignore it, update 00237 // the state and carry on, with the error correcting itself shortly after. 00238 void QEI::encode(void) { 00239 00240 int change = 0; 00241 int chanA = channelA_.read(); 00242 int chanB = channelB_.read(); 00243 00244 //2-bit state. 00245 currState_ = (chanA << 1) | (chanB); 00246 00247 if (encoding_ == X2_ENCODING) { 00248 00249 //11->00->11->00 is counter clockwise rotation or "forward". 00250 if ((prevState_ == 0x3 && currState_ == 0x0) || 00251 (prevState_ == 0x0 && currState_ == 0x3)) { 00252 00253 pulses_++; 00254 00255 } 00256 //10->01->10->01 is clockwise rotation or "backward". 00257 else if ((prevState_ == 0x2 && currState_ == 0x1) || 00258 (prevState_ == 0x1 && currState_ == 0x2)) { 00259 00260 pulses_--; 00261 00262 } 00263 00264 } else if (encoding_ == X4_ENCODING) { 00265 00266 //Entered a new valid state. 00267 if (((currState_ ^ prevState_) != INVALID) && (currState_ != prevState_)) { 00268 //2 bit state. Right hand bit of prev XOR left hand bit of current 00269 //gives 0 if clockwise rotation and 1 if counter clockwise rotation. 00270 change = (prevState_ & PREV_MASK) ^ ((currState_ & CURR_MASK) >> 1); 00271 00272 if (change == 0) { 00273 change = -1; 00274 } 00275 00276 pulses_ -= change; 00277 } 00278 00279 } 00280 00281 prevState_ = currState_; 00282 00283 } 00284 00285 void QEI::index(void) { 00286 00287 revolutions_++; 00288 00289 }
Generated on Thu Jul 14 2022 22:22:17 by
